2019-05-19 16:51:34 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-06-03 10:38:46 +04:00
/*
* Power key driver for SiRF PrimaII
*
Input: sirfsoc-onkey - update copyright years to 2014
Happy the year of horse, 2014.
,((((^`\
(((( (6 \
,((((( , \
,,,_ ,((((( /"._ ,`,
((((\\ ,... ,(((( / `-.-'
))) ;' `"'"'""(((( (
((( / ((( \
)) | |
(( | . ' |
)) \ _ ' `t ,.')
( | y;- -,-""'"-.\ \/
) / ./ ) / `\ \
|./ ( ( / /'
|| \\ //'|
|| \\ _//'||
|| )) |_/ ||
\_\ |_/ ||
`'" \_\
`'"
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2014-02-14 20:48:21 +04:00
* Copyright ( c ) 2013 - 2014 Cambridge Silicon Radio Limited , a CSR plc group
* company .
2013-06-03 10:38:46 +04:00
*/
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
# include <linux/input.h>
# include <linux/rtc/sirfsoc_rtciobrg.h>
# include <linux/of.h>
2014-02-16 00:30:58 +04:00
# include <linux/workqueue.h>
2013-06-03 10:38:46 +04:00
struct sirfsoc_pwrc_drvdata {
u32 pwrc_base ;
struct input_dev * input ;
2014-02-16 00:30:58 +04:00
struct delayed_work work ;
2013-06-03 10:38:46 +04:00
} ;
# define PWRC_ON_KEY_BIT (1 << 0)
# define PWRC_INT_STATUS 0xc
# define PWRC_INT_MASK 0x10
2014-02-16 00:30:58 +04:00
# define PWRC_PIN_STATUS 0x14
# define PWRC_KEY_DETECT_UP_TIME 20 /* ms*/
static int sirfsoc_pwrc_is_on_key_down ( struct sirfsoc_pwrc_drvdata * pwrcdrv )
{
u32 state = sirfsoc_rtc_iobrg_readl ( pwrcdrv - > pwrc_base +
PWRC_PIN_STATUS ) ;
return ! ( state & PWRC_ON_KEY_BIT ) ; /* ON_KEY is active low */
}
static void sirfsoc_pwrc_report_event ( struct work_struct * work )
{
struct sirfsoc_pwrc_drvdata * pwrcdrv =
container_of ( work , struct sirfsoc_pwrc_drvdata , work . work ) ;
if ( sirfsoc_pwrc_is_on_key_down ( pwrcdrv ) ) {
schedule_delayed_work ( & pwrcdrv - > work ,
msecs_to_jiffies ( PWRC_KEY_DETECT_UP_TIME ) ) ;
} else {
input_event ( pwrcdrv - > input , EV_KEY , KEY_POWER , 0 ) ;
input_sync ( pwrcdrv - > input ) ;
}
}
2013-06-03 10:38:46 +04:00
static irqreturn_t sirfsoc_pwrc_isr ( int irq , void * dev_id )
{
struct sirfsoc_pwrc_drvdata * pwrcdrv = dev_id ;
u32 int_status ;
int_status = sirfsoc_rtc_iobrg_readl ( pwrcdrv - > pwrc_base +
PWRC_INT_STATUS ) ;
sirfsoc_rtc_iobrg_writel ( int_status & ~ PWRC_ON_KEY_BIT ,
pwrcdrv - > pwrc_base + PWRC_INT_STATUS ) ;
2014-02-16 00:30:58 +04:00
input_event ( pwrcdrv - > input , EV_KEY , KEY_POWER , 1 ) ;
2013-06-03 10:38:46 +04:00
input_sync ( pwrcdrv - > input ) ;
2014-02-16 00:30:58 +04:00
schedule_delayed_work ( & pwrcdrv - > work ,
msecs_to_jiffies ( PWRC_KEY_DETECT_UP_TIME ) ) ;
2013-06-03 10:38:46 +04:00
return IRQ_HANDLED ;
}
2014-02-14 11:38:43 +04:00
static void sirfsoc_pwrc_toggle_interrupts ( struct sirfsoc_pwrc_drvdata * pwrcdrv ,
bool enable )
{
u32 int_mask ;
int_mask = sirfsoc_rtc_iobrg_readl ( pwrcdrv - > pwrc_base + PWRC_INT_MASK ) ;
if ( enable )
int_mask | = PWRC_ON_KEY_BIT ;
else
int_mask & = ~ PWRC_ON_KEY_BIT ;
sirfsoc_rtc_iobrg_writel ( int_mask , pwrcdrv - > pwrc_base + PWRC_INT_MASK ) ;
}
static int sirfsoc_pwrc_open ( struct input_dev * input )
{
struct sirfsoc_pwrc_drvdata * pwrcdrv = input_get_drvdata ( input ) ;
sirfsoc_pwrc_toggle_interrupts ( pwrcdrv , true ) ;
return 0 ;
}
static void sirfsoc_pwrc_close ( struct input_dev * input )
{
struct sirfsoc_pwrc_drvdata * pwrcdrv = input_get_drvdata ( input ) ;
sirfsoc_pwrc_toggle_interrupts ( pwrcdrv , false ) ;
2014-02-16 00:30:58 +04:00
cancel_delayed_work_sync ( & pwrcdrv - > work ) ;
2014-02-14 11:38:43 +04:00
}
2013-06-03 10:38:46 +04:00
static const struct of_device_id sirfsoc_pwrc_of_match [ ] = {
{ . compatible = " sirf,prima2-pwrc " } ,
{ } ,
2016-01-28 02:43:32 +03:00
} ;
2013-06-03 10:38:46 +04:00
MODULE_DEVICE_TABLE ( of , sirfsoc_pwrc_of_match ) ;
static int sirfsoc_pwrc_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct sirfsoc_pwrc_drvdata * pwrcdrv ;
int irq ;
int error ;
pwrcdrv = devm_kzalloc ( & pdev - > dev , sizeof ( struct sirfsoc_pwrc_drvdata ) ,
GFP_KERNEL ) ;
if ( ! pwrcdrv ) {
dev_info ( & pdev - > dev , " Not enough memory for the device data \n " ) ;
return - ENOMEM ;
}
/*
2014-02-14 11:38:43 +04:00
* We can ' t use of_iomap because pwrc is not mapped in memory ,
2013-06-03 10:38:46 +04:00
* the so - called base address is only offset in rtciobrg
*/
error = of_property_read_u32 ( np , " reg " , & pwrcdrv - > pwrc_base ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" unable to find base address of pwrc node in dtb \n " ) ;
return error ;
}
pwrcdrv - > input = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! pwrcdrv - > input )
return - ENOMEM ;
pwrcdrv - > input - > name = " sirfsoc pwrckey " ;
pwrcdrv - > input - > phys = " pwrc/input0 " ;
2014-02-16 00:30:58 +04:00
pwrcdrv - > input - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) ;
2014-03-28 11:38:37 +04:00
input_set_capability ( pwrcdrv - > input , EV_KEY , KEY_POWER ) ;
2014-02-16 00:30:58 +04:00
INIT_DELAYED_WORK ( & pwrcdrv - > work , sirfsoc_pwrc_report_event ) ;
2013-06-03 10:38:46 +04:00
2014-02-14 11:38:43 +04:00
pwrcdrv - > input - > open = sirfsoc_pwrc_open ;
pwrcdrv - > input - > close = sirfsoc_pwrc_close ;
input_set_drvdata ( pwrcdrv - > input , pwrcdrv ) ;
/* Make sure the device is quiesced */
sirfsoc_pwrc_toggle_interrupts ( pwrcdrv , false ) ;
2013-06-03 10:38:46 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
error = devm_request_irq ( & pdev - > dev , irq ,
2014-02-14 20:43:05 +04:00
sirfsoc_pwrc_isr , 0 ,
2013-06-03 10:38:46 +04:00
" sirfsoc_pwrc_int " , pwrcdrv ) ;
if ( error ) {
dev_err ( & pdev - > dev , " unable to claim irq %d, error: %d \n " ,
irq , error ) ;
return error ;
}
error = input_register_device ( pwrcdrv - > input ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" unable to register input device, error: %d \n " ,
error ) ;
return error ;
}
2014-02-14 20:45:56 +04:00
dev_set_drvdata ( & pdev - > dev , pwrcdrv ) ;
2013-06-03 10:38:46 +04:00
device_init_wakeup ( & pdev - > dev , 1 ) ;
return 0 ;
}
2014-11-02 10:02:46 +03:00
static int __maybe_unused sirfsoc_pwrc_resume ( struct device * dev )
2013-06-03 10:38:46 +04:00
{
2014-02-14 20:45:56 +04:00
struct sirfsoc_pwrc_drvdata * pwrcdrv = dev_get_drvdata ( dev ) ;
2014-02-14 11:38:43 +04:00
struct input_dev * input = pwrcdrv - > input ;
2013-06-03 10:38:46 +04:00
/*
* Do not mask pwrc interrupt as we want pwrc work as a wakeup source
* if users touch X_ONKEY_B , see arch / arm / mach - prima2 / pm . c
*/
2014-02-14 11:38:43 +04:00
mutex_lock ( & input - > mutex ) ;
if ( input - > users )
sirfsoc_pwrc_toggle_interrupts ( pwrcdrv , true ) ;
mutex_unlock ( & input - > mutex ) ;
2013-06-03 10:38:46 +04:00
return 0 ;
}
2014-02-14 20:45:13 +04:00
static SIMPLE_DEV_PM_OPS ( sirfsoc_pwrc_pm_ops , NULL , sirfsoc_pwrc_resume ) ;
2013-06-03 10:38:46 +04:00
static struct platform_driver sirfsoc_pwrc_driver = {
. probe = sirfsoc_pwrc_probe ,
. driver = {
. name = " sirfsoc-pwrc " ,
. pm = & sirfsoc_pwrc_pm_ops ,
2013-10-06 11:51:28 +04:00
. of_match_table = sirfsoc_pwrc_of_match ,
2013-06-03 10:38:46 +04:00
}
} ;
module_platform_driver ( sirfsoc_pwrc_driver ) ;
2014-07-18 21:05:38 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2013-06-03 10:38:46 +04:00
MODULE_AUTHOR ( " Binghua Duan <Binghua.Duan@csr.com>, Xianglong Du <Xianglong.Du@csr.com> " ) ;
MODULE_DESCRIPTION ( " CSR Prima2 PWRC Driver " ) ;
MODULE_ALIAS ( " platform:sirfsoc-pwrc " ) ;