2022-03-05 14:45:40 -08:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2022 MediaTek Inc .
* Author Fengping Yu < fengping . yu @ mediatek . com >
*/
# include <linux/bitops.h>
# include <linux/clk.h>
# include <linux/input/matrix_keypad.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/property.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# define MTK_KPD_NAME "mt6779-keypad"
# define MTK_KPD_MEM 0x0004
# define MTK_KPD_DEBOUNCE 0x0018
# define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0)
# define MTK_KPD_DEBOUNCE_MAX_MS 256
# define MTK_KPD_NUM_MEMS 5
# define MTK_KPD_NUM_BITS 136 /* 4*32+8 MEM5 only use 8 BITS */
struct mt6779_keypad {
struct regmap * regmap ;
struct input_dev * input_dev ;
struct clk * clk ;
void __iomem * base ;
u32 n_rows ;
u32 n_cols ;
DECLARE_BITMAP ( keymap_state , MTK_KPD_NUM_BITS ) ;
} ;
static const struct regmap_config mt6779_keypad_regmap_cfg = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = sizeof ( u32 ) ,
. max_register = 36 ,
} ;
static irqreturn_t mt6779_keypad_irq_handler ( int irq , void * dev_id )
{
struct mt6779_keypad * keypad = dev_id ;
const unsigned short * keycode = keypad - > input_dev - > keycode ;
DECLARE_BITMAP ( new_state , MTK_KPD_NUM_BITS ) ;
DECLARE_BITMAP ( change , MTK_KPD_NUM_BITS ) ;
unsigned int bit_nr ;
unsigned int row , col ;
unsigned int scancode ;
unsigned int row_shift = get_count_order ( keypad - > n_cols ) ;
bool pressed ;
regmap_bulk_read ( keypad - > regmap , MTK_KPD_MEM ,
new_state , MTK_KPD_NUM_MEMS ) ;
bitmap_xor ( change , new_state , keypad - > keymap_state , MTK_KPD_NUM_BITS ) ;
for_each_set_bit ( bit_nr , change , MTK_KPD_NUM_BITS ) {
/*
* Registers are 32 bits , but only bits [ 15 : 0 ] are used to
* indicate key status .
*/
if ( bit_nr % 32 > = 16 )
continue ;
row = bit_nr / 32 ;
col = bit_nr % 32 ;
scancode = MATRIX_SCAN_CODE ( row , col , row_shift ) ;
/* 1: not pressed, 0: pressed */
pressed = ! test_bit ( bit_nr , new_state ) ;
dev_dbg ( & keypad - > input_dev - > dev , " %s " ,
pressed ? " pressed " : " released " ) ;
input_event ( keypad - > input_dev , EV_MSC , MSC_SCAN , scancode ) ;
input_report_key ( keypad - > input_dev , keycode [ scancode ] , pressed ) ;
input_sync ( keypad - > input_dev ) ;
dev_dbg ( & keypad - > input_dev - > dev ,
" report Linux keycode = %d \n " , keycode [ scancode ] ) ;
}
bitmap_copy ( keypad - > keymap_state , new_state , MTK_KPD_NUM_BITS ) ;
return IRQ_HANDLED ;
}
static void mt6779_keypad_clk_disable ( void * data )
{
clk_disable_unprepare ( data ) ;
}
static int mt6779_keypad_pdrv_probe ( struct platform_device * pdev )
{
struct mt6779_keypad * keypad ;
2022-03-14 18:12:21 -07:00
int irq ;
2022-03-05 14:45:40 -08:00
u32 debounce ;
bool wakeup ;
int error ;
keypad = devm_kzalloc ( & pdev - > dev , sizeof ( * keypad ) , GFP_KERNEL ) ;
if ( ! keypad )
return - ENOMEM ;
keypad - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( keypad - > base ) )
return PTR_ERR ( keypad - > base ) ;
keypad - > regmap = devm_regmap_init_mmio ( & pdev - > dev , keypad - > base ,
& mt6779_keypad_regmap_cfg ) ;
if ( IS_ERR ( keypad - > regmap ) ) {
dev_err ( & pdev - > dev ,
" regmap init failed:%pe \n " , keypad - > regmap ) ;
return PTR_ERR ( keypad - > regmap ) ;
}
bitmap_fill ( keypad - > keymap_state , MTK_KPD_NUM_BITS ) ;
keypad - > input_dev = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! keypad - > input_dev ) {
dev_err ( & pdev - > dev , " Failed to allocate input dev \n " ) ;
return - ENOMEM ;
}
keypad - > input_dev - > name = MTK_KPD_NAME ;
keypad - > input_dev - > id . bustype = BUS_HOST ;
error = matrix_keypad_parse_properties ( & pdev - > dev , & keypad - > n_rows ,
& keypad - > n_cols ) ;
if ( error ) {
dev_err ( & pdev - > dev , " Failed to parse keypad params \n " ) ;
return error ;
}
if ( device_property_read_u32 ( & pdev - > dev , " debounce-delay-ms " ,
& debounce ) )
debounce = 16 ;
if ( debounce > MTK_KPD_DEBOUNCE_MAX_MS ) {
dev_err ( & pdev - > dev ,
" Debounce time exceeds the maximum allowed time %dms \n " ,
MTK_KPD_DEBOUNCE_MAX_MS ) ;
return - EINVAL ;
}
wakeup = device_property_read_bool ( & pdev - > dev , " wakeup-source " ) ;
dev_dbg ( & pdev - > dev , " n_row=%d n_col=%d debounce=%d \n " ,
keypad - > n_rows , keypad - > n_cols , debounce ) ;
error = matrix_keypad_build_keymap ( NULL , NULL ,
keypad - > n_rows , keypad - > n_cols ,
NULL , keypad - > input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " Failed to build keymap \n " ) ;
return error ;
}
input_set_capability ( keypad - > input_dev , EV_MSC , MSC_SCAN ) ;
regmap_write ( keypad - > regmap , MTK_KPD_DEBOUNCE ,
( debounce * ( 1 < < 5 ) ) & MTK_KPD_DEBOUNCE_MASK ) ;
keypad - > clk = devm_clk_get ( & pdev - > dev , " kpd " ) ;
if ( IS_ERR ( keypad - > clk ) )
return PTR_ERR ( keypad - > clk ) ;
error = clk_prepare_enable ( keypad - > clk ) ;
if ( error ) {
dev_err ( & pdev - > dev , " cannot prepare/enable keypad clock \n " ) ;
return error ;
}
error = devm_add_action_or_reset ( & pdev - > dev , mt6779_keypad_clk_disable ,
keypad - > clk ) ;
if ( error )
return error ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
error = devm_request_threaded_irq ( & pdev - > dev , irq ,
NULL , mt6779_keypad_irq_handler ,
IRQF_ONESHOT , MTK_KPD_NAME , keypad ) ;
if ( error ) {
dev_err ( & pdev - > dev , " Failed to request IRQ#%d: %d \n " ,
irq , error ) ;
return error ;
}
error = input_register_device ( keypad - > input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " Failed to register device \n " ) ;
return error ;
}
error = device_init_wakeup ( & pdev - > dev , wakeup ) ;
if ( error )
dev_warn ( & pdev - > dev , " device_init_wakeup() failed: %d \n " ,
error ) ;
return 0 ;
}
static const struct of_device_id mt6779_keypad_of_match [ ] = {
{ . compatible = " mediatek,mt6779-keypad " } ,
{ . compatible = " mediatek,mt6873-keypad " } ,
{ /* sentinel */ }
} ;
static struct platform_driver mt6779_keypad_pdrv = {
. probe = mt6779_keypad_pdrv_probe ,
. driver = {
. name = MTK_KPD_NAME ,
. of_match_table = mt6779_keypad_of_match ,
} ,
} ;
module_platform_driver ( mt6779_keypad_pdrv ) ;
MODULE_AUTHOR ( " Mediatek Corporation " ) ;
MODULE_DESCRIPTION ( " MTK Keypad (KPD) Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;