2018-09-26 18:27:40 +03:00
// SPDX-License-Identifier: GPL-2.0
2011-02-07 14:45:55 -05:00
/*
2017-01-19 18:39:42 +02:00
* Power button driver for Intel MID platforms .
2011-02-07 14:45:55 -05:00
*
2017-01-19 18:39:49 +02:00
* Copyright ( C ) 2010 , 2017 Intel Corp
*
* Author : Hong Liu < hong . liu @ intel . com >
* Author : Andy Shevchenko < andriy . shevchenko @ linux . intel . com >
2011-02-07 14:45:55 -05:00
*/
# include <linux/input.h>
2017-01-19 18:39:48 +02:00
# include <linux/interrupt.h>
2012-01-26 17:40:27 +00:00
# include <linux/mfd/intel_msic.h>
2017-01-19 18:39:48 +02:00
# include <linux/module.h>
# include <linux/platform_device.h>
2015-09-21 16:47:01 +01:00
# include <linux/pm_wakeirq.h>
2017-01-19 18:39:48 +02:00
# include <linux/slab.h>
2011-02-07 14:45:55 -05:00
2017-01-19 18:39:43 +02:00
# include <asm/cpu_device_id.h>
# include <asm/intel-family.h>
2017-01-19 18:39:46 +02:00
# include <asm/intel_scu_ipc.h>
2017-01-19 18:39:43 +02:00
2011-02-07 14:45:55 -05:00
# define DRIVER_NAME "msic_power_btn"
2011-04-06 17:44:37 +03:00
# define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */
2011-02-07 14:45:55 -05:00
2012-01-26 17:40:27 +00:00
/*
* MSIC document ti_datasheet defines the 1 st bit reg 0x21 is used to mask
* power button interrupt
*/
# define MSIC_PWRBTNM (1 << 0)
2017-01-19 18:39:46 +02:00
/* Intel Tangier */
2017-02-02 19:54:26 +02:00
# define BCOVE_PB_LEVEL (1 << 4) /* 1 - release, 0 - press */
2017-01-19 18:39:46 +02:00
/* Basin Cove PMIC */
# define BCOVE_PBIRQ 0x02
# define BCOVE_IRQLVL1MSK 0x0c
# define BCOVE_PBIRQMASK 0x0d
2017-02-02 19:54:26 +02:00
# define BCOVE_PBSTATUS 0x27
2017-01-19 18:39:46 +02:00
2017-01-19 18:39:43 +02:00
struct mid_pb_ddata {
struct device * dev ;
int irq ;
struct input_dev * input ;
2017-02-02 19:54:28 +02:00
unsigned short mirqlvl1_addr ;
2017-02-02 19:54:26 +02:00
unsigned short pbstat_addr ;
u8 pbstat_mask ;
2017-01-19 18:39:46 +02:00
int ( * setup ) ( struct mid_pb_ddata * ddata ) ;
2017-01-19 18:39:43 +02:00
} ;
2017-02-02 19:54:26 +02:00
static int mid_pbstat ( struct mid_pb_ddata * ddata , int * value )
2011-02-07 14:45:55 -05:00
{
2017-01-19 18:39:43 +02:00
struct input_dev * input = ddata - > input ;
2011-02-07 14:45:55 -05:00
int ret ;
u8 pbstat ;
2017-02-08 19:03:19 +02:00
ret = intel_scu_ipc_ioread8 ( ddata - > pbstat_addr , & pbstat ) ;
2017-01-19 18:39:43 +02:00
if ( ret )
return ret ;
2012-01-26 17:40:27 +00:00
dev_dbg ( input - > dev . parent , " PB_INT status= %d \n " , pbstat ) ;
2017-02-02 19:54:26 +02:00
* value = ! ( pbstat & ddata - > pbstat_mask ) ;
2017-01-19 18:39:43 +02:00
return 0 ;
}
2017-02-02 19:54:28 +02:00
static int mid_irq_ack ( struct mid_pb_ddata * ddata )
2017-01-19 18:39:44 +02:00
{
2017-02-08 19:03:19 +02:00
return intel_scu_ipc_update_register ( ddata - > mirqlvl1_addr , 0 , MSIC_PWRBTNM ) ;
2017-01-19 18:39:46 +02:00
}
static int mrfld_setup ( struct mid_pb_ddata * ddata )
{
/* Unmask the PBIRQ and MPBIRQ on Tangier */
intel_scu_ipc_update_register ( BCOVE_PBIRQ , 0 , MSIC_PWRBTNM ) ;
intel_scu_ipc_update_register ( BCOVE_PBIRQMASK , 0 , MSIC_PWRBTNM ) ;
return 0 ;
}
2017-01-19 18:39:43 +02:00
static irqreturn_t mid_pb_isr ( int irq , void * dev_id )
{
struct mid_pb_ddata * ddata = dev_id ;
struct input_dev * input = ddata - > input ;
2017-02-02 19:54:26 +02:00
int value = 0 ;
2017-01-19 18:39:43 +02:00
int ret ;
2017-02-02 19:54:26 +02:00
ret = mid_pbstat ( ddata , & value ) ;
2011-04-06 17:44:37 +03:00
if ( ret < 0 ) {
2017-01-19 18:39:47 +02:00
dev_err ( input - > dev . parent ,
" Read error %d while reading MSIC_PB_STATUS \n " , ret ) ;
2011-04-06 17:44:37 +03:00
} else {
2017-01-19 18:39:43 +02:00
input_event ( input , EV_KEY , KEY_POWER , value ) ;
2011-04-06 17:44:37 +03:00
input_sync ( input ) ;
}
2011-02-07 14:45:55 -05:00
2017-02-02 19:54:28 +02:00
mid_irq_ack ( ddata ) ;
2011-02-07 14:45:55 -05:00
return IRQ_HANDLED ;
}
2017-08-11 19:50:00 +05:30
static const struct mid_pb_ddata mfld_ddata = {
2017-02-02 19:54:28 +02:00
. mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK ,
2017-02-02 19:54:26 +02:00
. pbstat_addr = INTEL_MSIC_PBSTATUS ,
. pbstat_mask = MSIC_PB_LEVEL ,
2017-01-19 18:39:43 +02:00
} ;
2017-08-11 19:50:00 +05:30
static const struct mid_pb_ddata mrfld_ddata = {
2017-02-02 19:54:28 +02:00
. mirqlvl1_addr = BCOVE_IRQLVL1MSK ,
2017-02-02 19:54:26 +02:00
. pbstat_addr = BCOVE_PBSTATUS ,
. pbstat_mask = BCOVE_PB_LEVEL ,
2017-01-19 18:39:46 +02:00
. setup = mrfld_setup ,
} ;
2017-01-19 18:39:43 +02:00
static const struct x86_cpu_id mid_pb_cpu_ids [ ] = {
2018-11-01 08:42:21 -07:00
INTEL_CPU_FAM6 ( ATOM_SALTWELL_MID , mfld_ddata ) ,
INTEL_CPU_FAM6 ( ATOM_SILVERMONT_MID , mrfld_ddata ) ,
2017-01-19 18:39:43 +02:00
{ }
} ;
2017-01-19 18:39:42 +02:00
static int mid_pb_probe ( struct platform_device * pdev )
2011-02-07 14:45:55 -05:00
{
2017-01-19 18:39:43 +02:00
const struct x86_cpu_id * id ;
struct mid_pb_ddata * ddata ;
2011-02-07 14:45:55 -05:00
struct input_dev * input ;
2011-04-06 17:44:37 +03:00
int irq = platform_get_irq ( pdev , 0 ) ;
2011-02-07 14:45:55 -05:00
int error ;
2017-01-19 18:39:43 +02:00
id = x86_match_cpu ( mid_pb_cpu_ids ) ;
if ( ! id )
return - ENODEV ;
2017-08-09 11:00:54 -05:00
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " Failed to get IRQ: %d \n " , irq ) ;
return irq ;
}
2011-02-07 14:45:55 -05:00
2017-01-19 18:39:41 +02:00
input = devm_input_allocate_device ( & pdev - > dev ) ;
2013-10-23 12:14:52 -07:00
if ( ! input )
2011-04-06 17:44:37 +03:00
return - ENOMEM ;
2011-02-07 14:45:55 -05:00
input - > name = pdev - > name ;
input - > phys = " power-button/input0 " ;
input - > id . bustype = BUS_HOST ;
input - > dev . parent = & pdev - > dev ;
input_set_capability ( input , EV_KEY , KEY_POWER ) ;
2017-01-19 18:39:43 +02:00
ddata = ( struct mid_pb_ddata * ) id - > driver_data ;
if ( ! ddata )
return - ENODATA ;
ddata - > dev = & pdev - > dev ;
ddata - > irq = irq ;
ddata - > input = input ;
2017-01-19 18:39:46 +02:00
if ( ddata - > setup ) {
error = ddata - > setup ( ddata ) ;
if ( error )
return error ;
}
2017-01-19 18:39:42 +02:00
error = devm_request_threaded_irq ( & pdev - > dev , irq , NULL , mid_pb_isr ,
2017-01-19 18:39:43 +02:00
IRQF_ONESHOT , DRIVER_NAME , ddata ) ;
2011-02-07 14:45:55 -05:00
if ( error ) {
2017-01-19 18:39:47 +02:00
dev_err ( & pdev - > dev ,
" Unable to request irq %d for MID power button \n " , irq ) ;
2017-01-19 18:39:41 +02:00
return error ;
2011-02-07 14:45:55 -05:00
}
error = input_register_device ( input ) ;
if ( error ) {
2017-01-19 18:39:47 +02:00
dev_err ( & pdev - > dev ,
" Unable to register input dev, error %d \n " , error ) ;
2017-01-19 18:39:41 +02:00
return error ;
2011-02-07 14:45:55 -05:00
}
2017-01-19 18:39:43 +02:00
platform_set_drvdata ( pdev , ddata ) ;
2012-01-26 17:40:27 +00:00
2017-02-02 19:54:27 +02:00
/*
* SCU firmware might send power button interrupts to IA core before
* kernel boots and doesn ' t get EOI from IA core . The first bit of
* MSIC reg 0x21 is kept masked , and SCU firmware doesn ' t send new
* power interrupt to Android kernel . Unmask the bit when probing
* power button in kernel .
* There is a very narrow race between irq handler and power button
* initialization . The race happens rarely . So we needn ' t worry
* about it .
*/
2017-02-02 19:54:28 +02:00
error = mid_irq_ack ( ddata ) ;
2012-01-26 17:40:27 +00:00
if ( error ) {
2017-01-19 18:39:47 +02:00
dev_err ( & pdev - > dev ,
" Unable to clear power button interrupt, error: %d \n " ,
error ) ;
2017-01-19 18:39:41 +02:00
return error ;
2012-01-26 17:40:27 +00:00
}
2017-01-19 18:39:41 +02:00
device_init_wakeup ( & pdev - > dev , true ) ;
dev_pm_set_wake_irq ( & pdev - > dev , irq ) ;
2011-02-07 14:45:55 -05:00
2017-01-19 18:39:41 +02:00
return 0 ;
2011-02-07 14:45:55 -05:00
}
2017-01-19 18:39:42 +02:00
static int mid_pb_remove ( struct platform_device * pdev )
2011-02-07 14:45:55 -05:00
{
2015-09-21 16:47:01 +01:00
dev_pm_clear_wake_irq ( & pdev - > dev ) ;
device_init_wakeup ( & pdev - > dev , false ) ;
2011-04-06 17:44:37 +03:00
2011-02-07 14:45:55 -05:00
return 0 ;
}
2017-01-19 18:39:42 +02:00
static struct platform_driver mid_pb_driver = {
2011-02-07 14:45:55 -05:00
. driver = {
. name = DRIVER_NAME ,
} ,
2017-01-19 18:39:42 +02:00
. probe = mid_pb_probe ,
. remove = mid_pb_remove ,
2011-02-07 14:45:55 -05:00
} ;
2017-01-19 18:39:42 +02:00
module_platform_driver ( mid_pb_driver ) ;
2011-02-07 14:45:55 -05:00
MODULE_AUTHOR ( " Hong Liu <hong.liu@intel.com> " ) ;
2017-01-19 18:39:42 +02:00
MODULE_DESCRIPTION ( " Intel MID Power Button Driver " ) ;
2011-02-07 14:45:55 -05:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;