2011-02-07 22:45:55 +03:00
/*
* Power button driver for Medfield .
*
* Copyright ( C ) 2010 Intel Corp
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* 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 .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/input.h>
2012-01-26 21:40:27 +04:00
# include <linux/mfd/intel_msic.h>
2011-02-07 22:45:55 +03:00
# define DRIVER_NAME "msic_power_btn"
2011-04-06 18:44:37 +04:00
# define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */
2011-02-07 22:45:55 +03:00
2012-01-26 21:40:27 +04:00
/*
* MSIC document ti_datasheet defines the 1 st bit reg 0x21 is used to mask
* power button interrupt
*/
# define MSIC_PWRBTNM (1 << 0)
2011-02-07 22:45:55 +03:00
static irqreturn_t mfld_pb_isr ( int irq , void * dev_id )
{
2011-04-06 18:44:37 +04:00
struct input_dev * input = dev_id ;
2011-02-07 22:45:55 +03:00
int ret ;
u8 pbstat ;
2012-01-26 21:40:27 +04:00
ret = intel_msic_reg_read ( INTEL_MSIC_PBSTATUS , & pbstat ) ;
dev_dbg ( input - > dev . parent , " PB_INT status= %d \n " , pbstat ) ;
2011-04-06 18:44:37 +04:00
if ( ret < 0 ) {
dev_err ( input - > dev . parent , " Read error %d while reading "
" MSIC_PB_STATUS \n " , ret ) ;
} else {
input_event ( input , EV_KEY , KEY_POWER ,
! ( pbstat & MSIC_PB_LEVEL ) ) ;
input_sync ( input ) ;
}
2011-02-07 22:45:55 +03:00
return IRQ_HANDLED ;
}
2012-12-22 01:18:33 +04:00
static int mfld_pb_probe ( struct platform_device * pdev )
2011-02-07 22:45:55 +03:00
{
struct input_dev * input ;
2011-04-06 18:44:37 +04:00
int irq = platform_get_irq ( pdev , 0 ) ;
2011-02-07 22:45:55 +03:00
int error ;
if ( irq < 0 )
return - EINVAL ;
input = input_allocate_device ( ) ;
2011-04-06 18:44:37 +04:00
if ( ! input ) {
dev_err ( & pdev - > dev , " Input device allocation error \n " ) ;
return - ENOMEM ;
2011-02-07 22:45:55 +03: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 ) ;
2012-05-05 01:02:44 +04:00
error = request_threaded_irq ( irq , NULL , mfld_pb_isr , IRQF_NO_SUSPEND ,
2011-04-06 18:44:37 +04:00
DRIVER_NAME , input ) ;
2011-02-07 22:45:55 +03:00
if ( error ) {
2011-04-06 18:44:37 +04:00
dev_err ( & pdev - > dev , " Unable to request irq %d for mfld power "
" button \n " , irq ) ;
goto err_free_input ;
2011-02-07 22:45:55 +03:00
}
error = input_register_device ( input ) ;
if ( error ) {
2011-04-06 18:44:37 +04:00
dev_err ( & pdev - > dev , " Unable to register input dev, error "
" %d \n " , error ) ;
2011-02-07 22:45:55 +03:00
goto err_free_irq ;
}
2011-04-06 18:44:37 +04:00
platform_set_drvdata ( pdev , input ) ;
2012-01-26 21:40:27 +04: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 .
*/
error = intel_msic_reg_update ( INTEL_MSIC_IRQLVL1MSK , 0 , MSIC_PWRBTNM ) ;
if ( error ) {
dev_err ( & pdev - > dev , " Unable to clear power button interrupt, "
" error: %d \n " , error ) ;
goto err_free_irq ;
}
2011-02-07 22:45:55 +03:00
return 0 ;
err_free_irq :
2011-04-06 18:44:37 +04:00
free_irq ( irq , input ) ;
err_free_input :
2011-02-07 22:45:55 +03:00
input_free_device ( input ) ;
return error ;
}
2012-12-22 01:18:33 +04:00
static int mfld_pb_remove ( struct platform_device * pdev )
2011-02-07 22:45:55 +03:00
{
2011-04-06 18:44:37 +04:00
struct input_dev * input = platform_get_drvdata ( pdev ) ;
int irq = platform_get_irq ( pdev , 0 ) ;
2011-02-07 22:45:55 +03:00
2011-04-06 18:44:37 +04:00
free_irq ( irq , input ) ;
input_unregister_device ( input ) ;
2011-02-07 22:45:55 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2011-04-06 18:44:37 +04:00
2011-02-07 22:45:55 +03:00
return 0 ;
}
static struct platform_driver mfld_pb_driver = {
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
} ,
. probe = mfld_pb_probe ,
2012-12-22 01:18:33 +04:00
. remove = mfld_pb_remove ,
2011-02-07 22:45:55 +03:00
} ;
2011-11-26 08:14:37 +04:00
module_platform_driver ( mfld_pb_driver ) ;
2011-02-07 22:45:55 +03:00
MODULE_AUTHOR ( " Hong Liu <hong.liu@intel.com> " ) ;
MODULE_DESCRIPTION ( " Intel Medfield Power Button Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;