2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-06-09 02:26:20 +03:00
/*
2016-10-27 01:49:13 +03:00
* OnKey device driver for DA9063 , DA9062 and DA9061 PMICs
2015-06-09 02:26:20 +03:00
* Copyright ( C ) 2015 Dialog Semiconductor Ltd .
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/workqueue.h>
# include <linux/regmap.h>
# include <linux/of.h>
# include <linux/mfd/da9063/core.h>
# include <linux/mfd/da9063/registers.h>
2015-07-28 08:28:40 +03:00
# include <linux/mfd/da9062/core.h>
# include <linux/mfd/da9062/registers.h>
struct da906x_chip_config {
/* REGS */
int onkey_status ;
int onkey_pwr_signalling ;
int onkey_fault_log ;
int onkey_shutdown ;
/* MASKS */
int onkey_nonkey_mask ;
int onkey_nonkey_lock_mask ;
int onkey_key_reset_mask ;
int onkey_shutdown_mask ;
/* NAMES */
const char * name ;
} ;
2015-06-09 02:26:20 +03:00
struct da9063_onkey {
struct delayed_work work ;
struct input_dev * input ;
struct device * dev ;
2015-07-28 08:28:40 +03:00
struct regmap * regmap ;
const struct da906x_chip_config * config ;
char phys [ 32 ] ;
2015-06-09 02:26:20 +03:00
bool key_power ;
} ;
2015-07-28 08:28:40 +03:00
static const struct da906x_chip_config da9063_regs = {
/* REGS */
. onkey_status = DA9063_REG_STATUS_A ,
. onkey_pwr_signalling = DA9063_REG_CONTROL_B ,
. onkey_fault_log = DA9063_REG_FAULT_LOG ,
. onkey_shutdown = DA9063_REG_CONTROL_F ,
/* MASKS */
. onkey_nonkey_mask = DA9063_NONKEY ,
. onkey_nonkey_lock_mask = DA9063_NONKEY_LOCK ,
. onkey_key_reset_mask = DA9063_KEY_RESET ,
. onkey_shutdown_mask = DA9063_SHUTDOWN ,
/* NAMES */
. name = DA9063_DRVNAME_ONKEY ,
} ;
static const struct da906x_chip_config da9062_regs = {
/* REGS */
. onkey_status = DA9062AA_STATUS_A ,
. onkey_pwr_signalling = DA9062AA_CONTROL_B ,
. onkey_fault_log = DA9062AA_FAULT_LOG ,
. onkey_shutdown = DA9062AA_CONTROL_F ,
/* MASKS */
. onkey_nonkey_mask = DA9062AA_NONKEY_MASK ,
. onkey_nonkey_lock_mask = DA9062AA_NONKEY_LOCK_MASK ,
. onkey_key_reset_mask = DA9062AA_KEY_RESET_MASK ,
. onkey_shutdown_mask = DA9062AA_SHUTDOWN_MASK ,
/* NAMES */
. name = " da9062-onkey " ,
} ;
static const struct of_device_id da9063_compatible_reg_id_table [ ] = {
{ . compatible = " dlg,da9063-onkey " , . data = & da9063_regs } ,
{ . compatible = " dlg,da9062-onkey " , . data = & da9062_regs } ,
{ } ,
} ;
2016-10-27 02:46:41 +03:00
MODULE_DEVICE_TABLE ( of , da9063_compatible_reg_id_table ) ;
2015-07-28 08:28:40 +03:00
2015-06-09 02:26:20 +03:00
static void da9063_poll_on ( struct work_struct * work )
{
2015-07-28 08:28:40 +03:00
struct da9063_onkey * onkey = container_of ( work ,
struct da9063_onkey ,
work . work ) ;
const struct da906x_chip_config * config = onkey - > config ;
2015-06-09 02:26:20 +03:00
unsigned int val ;
int fault_log = 0 ;
bool poll = true ;
int error ;
/* Poll to see when the pin is released */
2015-07-28 08:28:40 +03:00
error = regmap_read ( onkey - > regmap ,
config - > onkey_status ,
& val ) ;
2015-06-09 02:26:20 +03:00
if ( error ) {
dev_err ( onkey - > dev ,
" Failed to read ON status: %d \n " , error ) ;
goto err_poll ;
}
2015-07-28 08:28:40 +03:00
if ( ! ( val & config - > onkey_nonkey_mask ) ) {
error = regmap_update_bits ( onkey - > regmap ,
config - > onkey_pwr_signalling ,
config - > onkey_nonkey_lock_mask ,
0 ) ;
2015-06-09 02:26:20 +03:00
if ( error ) {
dev_err ( onkey - > dev ,
" Failed to reset the Key Delay %d \n " , error ) ;
goto err_poll ;
}
input_report_key ( onkey - > input , KEY_POWER , 0 ) ;
input_sync ( onkey - > input ) ;
poll = false ;
}
/*
* If the fault log KEY_RESET is detected , then clear it
* and shut down the system .
*/
2015-07-28 08:28:40 +03:00
error = regmap_read ( onkey - > regmap ,
config - > onkey_fault_log ,
& fault_log ) ;
2015-06-09 02:26:20 +03:00
if ( error ) {
dev_warn ( & onkey - > input - > dev ,
" Cannot read FAULT_LOG: %d \n " , error ) ;
2015-07-28 08:28:40 +03:00
} else if ( fault_log & config - > onkey_key_reset_mask ) {
error = regmap_write ( onkey - > regmap ,
config - > onkey_fault_log ,
config - > onkey_key_reset_mask ) ;
2015-06-09 02:26:20 +03:00
if ( error ) {
dev_warn ( & onkey - > input - > dev ,
" Cannot reset KEY_RESET fault log: %d \n " ,
error ) ;
} else {
/* at this point we do any S/W housekeeping
* and then send shutdown command
*/
dev_dbg ( & onkey - > input - > dev ,
2016-10-27 01:49:13 +03:00
" Sending SHUTDOWN to PMIC ... \n " ) ;
2015-07-28 08:28:40 +03:00
error = regmap_write ( onkey - > regmap ,
config - > onkey_shutdown ,
config - > onkey_shutdown_mask ) ;
2015-06-09 02:26:20 +03:00
if ( error )
dev_err ( & onkey - > input - > dev ,
2016-10-27 01:49:13 +03:00
" Cannot SHUTDOWN PMIC: %d \n " ,
2015-06-09 02:26:20 +03:00
error ) ;
}
}
err_poll :
if ( poll )
schedule_delayed_work ( & onkey - > work , msecs_to_jiffies ( 50 ) ) ;
}
static irqreturn_t da9063_onkey_irq_handler ( int irq , void * data )
{
struct da9063_onkey * onkey = data ;
2015-07-28 08:28:40 +03:00
const struct da906x_chip_config * config = onkey - > config ;
2015-06-09 02:26:20 +03:00
unsigned int val ;
int error ;
2015-07-28 08:28:40 +03:00
error = regmap_read ( onkey - > regmap ,
config - > onkey_status ,
& val ) ;
if ( onkey - > key_power & & ! error & & ( val & config - > onkey_nonkey_mask ) ) {
2015-06-09 02:26:20 +03:00
input_report_key ( onkey - > input , KEY_POWER , 1 ) ;
input_sync ( onkey - > input ) ;
schedule_delayed_work ( & onkey - > work , 0 ) ;
2015-12-13 07:43:35 +03:00
dev_dbg ( onkey - > dev , " KEY_POWER long press. \n " ) ;
2015-06-09 02:26:20 +03:00
} else {
2015-12-13 07:43:35 +03:00
input_report_key ( onkey - > input , KEY_POWER , 1 ) ;
2015-06-09 02:26:20 +03:00
input_sync ( onkey - > input ) ;
2015-12-13 07:43:35 +03:00
input_report_key ( onkey - > input , KEY_POWER , 0 ) ;
2015-06-09 02:26:20 +03:00
input_sync ( onkey - > input ) ;
2015-12-13 07:43:35 +03:00
dev_dbg ( onkey - > dev , " KEY_POWER short press. \n " ) ;
2015-06-09 02:26:20 +03:00
}
return IRQ_HANDLED ;
}
static void da9063_cancel_poll ( void * data )
{
struct da9063_onkey * onkey = data ;
cancel_delayed_work_sync ( & onkey - > work ) ;
}
static int da9063_onkey_probe ( struct platform_device * pdev )
{
struct da9063_onkey * onkey ;
2015-07-28 08:28:40 +03:00
const struct of_device_id * match ;
2015-06-09 02:26:20 +03:00
int irq ;
int error ;
2015-07-28 08:28:40 +03:00
match = of_match_node ( da9063_compatible_reg_id_table ,
pdev - > dev . of_node ) ;
if ( ! match )
return - ENXIO ;
2015-06-09 02:26:20 +03:00
onkey = devm_kzalloc ( & pdev - > dev , sizeof ( struct da9063_onkey ) ,
GFP_KERNEL ) ;
if ( ! onkey ) {
dev_err ( & pdev - > dev , " Failed to allocate memory. \n " ) ;
return - ENOMEM ;
}
2015-07-28 08:28:40 +03:00
onkey - > config = match - > data ;
2015-06-09 02:26:20 +03:00
onkey - > dev = & pdev - > dev ;
2015-07-28 08:28:40 +03:00
onkey - > regmap = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
if ( ! onkey - > regmap ) {
dev_err ( & pdev - > dev , " Parent regmap unavailable. \n " ) ;
return - ENXIO ;
}
2015-06-09 02:26:20 +03:00
2019-05-21 07:58:58 +03:00
onkey - > key_power = ! of_property_read_bool ( pdev - > dev . of_node ,
" dlg,disable-key-power " ) ;
2015-06-09 02:26:20 +03:00
onkey - > input = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! onkey - > input ) {
dev_err ( & pdev - > dev , " Failed to allocated input device. \n " ) ;
return - ENOMEM ;
}
2015-07-28 08:28:40 +03:00
onkey - > input - > name = onkey - > config - > name ;
snprintf ( onkey - > phys , sizeof ( onkey - > phys ) , " %s/input0 " ,
onkey - > config - > name ) ;
onkey - > input - > phys = onkey - > phys ;
2015-06-09 02:26:20 +03:00
onkey - > input - > dev . parent = & pdev - > dev ;
2019-09-16 22:45:48 +03:00
input_set_capability ( onkey - > input , EV_KEY , KEY_POWER ) ;
2015-06-09 02:26:20 +03:00
INIT_DELAYED_WORK ( & onkey - > work , da9063_poll_on ) ;
error = devm_add_action ( & pdev - > dev , da9063_cancel_poll , onkey ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" Failed to add cancel poll action: %d \n " ,
error ) ;
return error ;
}
irq = platform_get_irq_byname ( pdev , " ONKEY " ) ;
2019-08-14 20:46:38 +03:00
if ( irq < 0 )
return irq ;
2015-06-09 02:26:20 +03:00
error = devm_request_threaded_irq ( & pdev - > dev , irq ,
NULL , da9063_onkey_irq_handler ,
IRQF_TRIGGER_LOW | IRQF_ONESHOT ,
" ONKEY " , onkey ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" Failed to request IRQ %d: %d \n " , irq , error ) ;
return error ;
}
error = input_register_device ( onkey - > input ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" Failed to register input device: %d \n " , error ) ;
return error ;
}
return 0 ;
}
static struct platform_driver da9063_onkey_driver = {
. probe = da9063_onkey_probe ,
. driver = {
. name = DA9063_DRVNAME_ONKEY ,
2015-07-28 08:28:40 +03:00
. of_match_table = da9063_compatible_reg_id_table ,
2015-06-09 02:26:20 +03:00
} ,
} ;
module_platform_driver ( da9063_onkey_driver ) ;
MODULE_AUTHOR ( " S Twiss <stwiss.opensource@diasemi.com> " ) ;
2016-10-27 01:49:13 +03:00
MODULE_DESCRIPTION ( " Onkey device driver for Dialog DA9063, DA9062 and DA9061 " ) ;
2015-06-09 02:26:20 +03:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DA9063_DRVNAME_ONKEY ) ;