2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-06-08 16:26:20 -07:00
/*
2016-10-26 15:49:13 -07:00
* OnKey device driver for DA9063 , DA9062 and DA9061 PMICs
2015-06-08 16:26:20 -07:00
* Copyright ( C ) 2015 Dialog Semiconductor Ltd .
*/
2022-02-28 23:25:35 -08:00
# include <linux/devm-helpers.h>
2015-06-08 16:26:20 -07:00
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/input.h>
# include <linux/interrupt.h>
2023-12-20 19:09:05 -08:00
# include <linux/mod_devicetable.h>
2015-06-08 16:26:20 -07:00
# include <linux/platform_device.h>
2023-07-17 12:20:03 -07:00
# include <linux/pm_wakeirq.h>
2023-12-20 19:09:05 -08:00
# include <linux/property.h>
2015-06-08 16:26:20 -07:00
# include <linux/workqueue.h>
# include <linux/regmap.h>
# include <linux/mfd/da9063/core.h>
# include <linux/mfd/da9063/registers.h>
2015-07-27 22:28:40 -07: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-08 16:26:20 -07:00
struct da9063_onkey {
struct delayed_work work ;
struct input_dev * input ;
struct device * dev ;
2015-07-27 22:28:40 -07:00
struct regmap * regmap ;
const struct da906x_chip_config * config ;
char phys [ 32 ] ;
2015-06-08 16:26:20 -07:00
bool key_power ;
} ;
2015-07-27 22:28:40 -07: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 " ,
} ;
2015-06-08 16:26:20 -07:00
static void da9063_poll_on ( struct work_struct * work )
{
2015-07-27 22:28:40 -07:00
struct da9063_onkey * onkey = container_of ( work ,
struct da9063_onkey ,
work . work ) ;
const struct da906x_chip_config * config = onkey - > config ;
2015-06-08 16:26:20 -07:00
unsigned int val ;
int fault_log = 0 ;
bool poll = true ;
int error ;
/* Poll to see when the pin is released */
2015-07-27 22:28:40 -07:00
error = regmap_read ( onkey - > regmap ,
config - > onkey_status ,
& val ) ;
2015-06-08 16:26:20 -07:00
if ( error ) {
dev_err ( onkey - > dev ,
" Failed to read ON status: %d \n " , error ) ;
goto err_poll ;
}
2015-07-27 22:28:40 -07: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-08 16:26:20 -07: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-27 22:28:40 -07:00
error = regmap_read ( onkey - > regmap ,
config - > onkey_fault_log ,
& fault_log ) ;
2015-06-08 16:26:20 -07:00
if ( error ) {
dev_warn ( & onkey - > input - > dev ,
" Cannot read FAULT_LOG: %d \n " , error ) ;
2015-07-27 22:28:40 -07: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-08 16:26:20 -07: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-26 15:49:13 -07:00
" Sending SHUTDOWN to PMIC ... \n " ) ;
2015-07-27 22:28:40 -07:00
error = regmap_write ( onkey - > regmap ,
config - > onkey_shutdown ,
config - > onkey_shutdown_mask ) ;
2015-06-08 16:26:20 -07:00
if ( error )
dev_err ( & onkey - > input - > dev ,
2016-10-26 15:49:13 -07:00
" Cannot SHUTDOWN PMIC: %d \n " ,
2015-06-08 16:26:20 -07: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-27 22:28:40 -07:00
const struct da906x_chip_config * config = onkey - > config ;
2015-06-08 16:26:20 -07:00
unsigned int val ;
int error ;
2015-07-27 22:28:40 -07:00
error = regmap_read ( onkey - > regmap ,
config - > onkey_status ,
& val ) ;
if ( onkey - > key_power & & ! error & & ( val & config - > onkey_nonkey_mask ) ) {
2015-06-08 16:26:20 -07:00
input_report_key ( onkey - > input , KEY_POWER , 1 ) ;
input_sync ( onkey - > input ) ;
schedule_delayed_work ( & onkey - > work , 0 ) ;
2015-12-12 20:43:35 -08:00
dev_dbg ( onkey - > dev , " KEY_POWER long press. \n " ) ;
2015-06-08 16:26:20 -07:00
} else {
2015-12-12 20:43:35 -08:00
input_report_key ( onkey - > input , KEY_POWER , 1 ) ;
2015-06-08 16:26:20 -07:00
input_sync ( onkey - > input ) ;
2015-12-12 20:43:35 -08:00
input_report_key ( onkey - > input , KEY_POWER , 0 ) ;
2015-06-08 16:26:20 -07:00
input_sync ( onkey - > input ) ;
2015-12-12 20:43:35 -08:00
dev_dbg ( onkey - > dev , " KEY_POWER short press. \n " ) ;
2015-06-08 16:26:20 -07:00
}
return IRQ_HANDLED ;
}
static int da9063_onkey_probe ( struct platform_device * pdev )
{
struct da9063_onkey * onkey ;
int error ;
2023-12-13 21:37:15 -08:00
int irq ;
2015-07-27 22:28:40 -07:00
2015-06-08 16:26:20 -07:00
onkey = devm_kzalloc ( & pdev - > dev , sizeof ( struct da9063_onkey ) ,
GFP_KERNEL ) ;
2023-12-13 23:48:38 -08:00
if ( ! onkey )
2015-06-08 16:26:20 -07:00
return - ENOMEM ;
2023-12-13 21:37:15 -08:00
onkey - > config = device_get_match_data ( & pdev - > dev ) ;
if ( ! onkey - > config )
return - ENXIO ;
2015-06-08 16:26:20 -07:00
onkey - > dev = & pdev - > dev ;
2015-07-27 22:28:40 -07:00
onkey - > regmap = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
2023-12-14 22:05:20 -08:00
if ( ! onkey - > regmap )
return dev_err_probe ( & pdev - > dev , - ENXIO ,
" Parent regmap unavailable. \n " ) ;
2015-06-08 16:26:20 -07:00
2023-12-20 19:09:05 -08:00
onkey - > key_power = ! device_property_read_bool ( & pdev - > dev ,
" dlg,disable-key-power " ) ;
2015-06-08 16:26:20 -07:00
onkey - > input = devm_input_allocate_device ( & pdev - > dev ) ;
2023-12-13 23:48:38 -08:00
if ( ! onkey - > input )
2015-06-08 16:26:20 -07:00
return - ENOMEM ;
2015-07-27 22:28:40 -07: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-08 16:26:20 -07:00
2019-09-16 12:45:48 -07:00
input_set_capability ( onkey - > input , EV_KEY , KEY_POWER ) ;
2015-06-08 16:26:20 -07:00
2022-02-28 23:25:35 -08:00
error = devm_delayed_work_autocancel ( & pdev - > dev , & onkey - > work ,
da9063_poll_on ) ;
2023-12-13 23:48:38 -08:00
if ( error )
2015-06-08 16:26:20 -07:00
return error ;
irq = platform_get_irq_byname ( pdev , " ONKEY " ) ;
2019-08-14 10:46:38 -07:00
if ( irq < 0 )
return irq ;
2015-06-08 16:26:20 -07:00
error = devm_request_threaded_irq ( & pdev - > dev , irq ,
NULL , da9063_onkey_irq_handler ,
IRQF_TRIGGER_LOW | IRQF_ONESHOT ,
" ONKEY " , onkey ) ;
2023-12-14 22:05:20 -08:00
if ( error )
return dev_err_probe ( & pdev - > dev , error ,
" Failed to allocate onkey IRQ \n " ) ;
2015-06-08 16:26:20 -07:00
2023-07-17 12:20:03 -07:00
error = dev_pm_set_wake_irq ( & pdev - > dev , irq ) ;
if ( error )
dev_warn ( & pdev - > dev ,
" Failed to set IRQ %d as a wake IRQ: %d \n " ,
irq , error ) ;
else
device_init_wakeup ( & pdev - > dev , true ) ;
2015-06-08 16:26:20 -07:00
error = input_register_device ( onkey - > input ) ;
2023-12-13 23:48:38 -08:00
if ( error )
2015-06-08 16:26:20 -07:00
return error ;
return 0 ;
}
2023-12-13 21:37:15 -08:00
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 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , da9063_compatible_reg_id_table ) ;
2015-06-08 16:26:20 -07:00
static struct platform_driver da9063_onkey_driver = {
. probe = da9063_onkey_probe ,
. driver = {
. name = DA9063_DRVNAME_ONKEY ,
2015-07-27 22:28:40 -07:00
. of_match_table = da9063_compatible_reg_id_table ,
2015-06-08 16:26:20 -07:00
} ,
} ;
module_platform_driver ( da9063_onkey_driver ) ;
MODULE_AUTHOR ( " S Twiss <stwiss.opensource@diasemi.com> " ) ;
2016-10-26 15:49:13 -07:00
MODULE_DESCRIPTION ( " Onkey device driver for Dialog DA9063, DA9062 and DA9061 " ) ;
2015-06-08 16:26:20 -07:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DA9063_DRVNAME_ONKEY ) ;