2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2013-05-30 13:16:10 -07:00
/*
* Copyright ( C ) 2013 Daniel Tang < tangrs @ tangrs . id . au >
*/
# include <linux/input/matrix_keypad.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/delay.h>
# include <linux/input.h>
# include <linux/slab.h>
# include <linux/clk.h>
# include <linux/module.h>
# include <linux/of.h>
# define KEYPAD_SCAN_MODE 0x00
# define KEYPAD_CNTL 0x04
# define KEYPAD_INT 0x08
# define KEYPAD_INTMSK 0x0C
# define KEYPAD_DATA 0x10
# define KEYPAD_GPIO 0x30
# define KEYPAD_UNKNOWN_INT 0x40
# define KEYPAD_UNKNOWN_INT_STS 0x44
# define KEYPAD_BITMASK_COLS 11
# define KEYPAD_BITMASK_ROWS 8
struct nspire_keypad {
void __iomem * reg_base ;
u32 int_mask ;
struct input_dev * input ;
struct clk * clk ;
struct matrix_keymap_data * keymap ;
int row_shift ;
/* Maximum delay estimated assuming 33MHz APB */
u32 scan_interval ; /* In microseconds (~2000us max) */
u32 row_delay ; /* In microseconds (~500us max) */
u16 state [ KEYPAD_BITMASK_ROWS ] ;
bool active_low ;
} ;
static irqreturn_t nspire_keypad_irq ( int irq , void * dev_id )
{
struct nspire_keypad * keypad = dev_id ;
struct input_dev * input = keypad - > input ;
unsigned short * keymap = input - > keycode ;
unsigned int code ;
int row , col ;
u32 int_sts ;
u16 state [ 8 ] ;
u16 bits , changed ;
int_sts = readl ( keypad - > reg_base + KEYPAD_INT ) & keypad - > int_mask ;
if ( ! int_sts )
return IRQ_NONE ;
memcpy_fromio ( state , keypad - > reg_base + KEYPAD_DATA , sizeof ( state ) ) ;
for ( row = 0 ; row < KEYPAD_BITMASK_ROWS ; row + + ) {
bits = state [ row ] ;
if ( keypad - > active_low )
bits = ~ bits ;
changed = bits ^ keypad - > state [ row ] ;
if ( ! changed )
continue ;
keypad - > state [ row ] = bits ;
for ( col = 0 ; col < KEYPAD_BITMASK_COLS ; col + + ) {
if ( ! ( changed & ( 1U < < col ) ) )
continue ;
code = MATRIX_SCAN_CODE ( row , col , keypad - > row_shift ) ;
input_event ( input , EV_MSC , MSC_SCAN , code ) ;
input_report_key ( input , keymap [ code ] ,
bits & ( 1U < < col ) ) ;
}
}
input_sync ( input ) ;
writel ( 0x3 , keypad - > reg_base + KEYPAD_INT ) ;
return IRQ_HANDLED ;
}
2021-03-23 10:45:55 -07:00
static int nspire_keypad_open ( struct input_dev * input )
2013-05-30 13:16:10 -07:00
{
2021-03-23 10:45:55 -07:00
struct nspire_keypad * keypad = input_get_drvdata ( input ) ;
2013-05-30 13:16:10 -07:00
unsigned long val = 0 , cycles_per_us , delay_cycles , row_delay_cycles ;
2021-03-23 10:45:55 -07:00
int error ;
error = clk_prepare_enable ( keypad - > clk ) ;
if ( error )
return error ;
2013-05-30 13:16:10 -07:00
cycles_per_us = ( clk_get_rate ( keypad - > clk ) / 1000000 ) ;
if ( cycles_per_us = = 0 )
cycles_per_us = 1 ;
delay_cycles = cycles_per_us * keypad - > scan_interval ;
WARN_ON ( delay_cycles > = ( 1 < < 16 ) ) ; /* Overflow */
delay_cycles & = 0xffff ;
row_delay_cycles = cycles_per_us * keypad - > row_delay ;
WARN_ON ( row_delay_cycles > = ( 1 < < 14 ) ) ; /* Overflow */
row_delay_cycles & = 0x3fff ;
val | = 3 < < 0 ; /* Set scan mode to 3 (continuous scan) */
val | = row_delay_cycles < < 2 ; /* Delay between scanning each row */
val | = delay_cycles < < 16 ; /* Delay between scans */
writel ( val , keypad - > reg_base + KEYPAD_SCAN_MODE ) ;
val = ( KEYPAD_BITMASK_ROWS & 0xff ) | ( KEYPAD_BITMASK_COLS & 0xff ) < < 8 ;
writel ( val , keypad - > reg_base + KEYPAD_CNTL ) ;
/* Enable interrupts */
keypad - > int_mask = 1 < < 1 ;
2013-07-06 21:53:26 -07:00
writel ( keypad - > int_mask , keypad - > reg_base + KEYPAD_INTMSK ) ;
2013-05-30 13:16:10 -07:00
return 0 ;
}
static void nspire_keypad_close ( struct input_dev * input )
{
struct nspire_keypad * keypad = input_get_drvdata ( input ) ;
2021-03-23 10:45:55 -07:00
/* Disable interrupts */
writel ( 0 , keypad - > reg_base + KEYPAD_INTMSK ) ;
/* Acknowledge existing interrupts */
writel ( ~ 0 , keypad - > reg_base + KEYPAD_INT ) ;
2013-05-30 13:16:10 -07:00
clk_disable_unprepare ( keypad - > clk ) ;
}
static int nspire_keypad_probe ( struct platform_device * pdev )
{
const struct device_node * of_node = pdev - > dev . of_node ;
struct nspire_keypad * keypad ;
struct input_dev * input ;
struct resource * res ;
int irq ;
int error ;
irq = platform_get_irq ( pdev , 0 ) ;
2019-08-14 10:46:38 -07:00
if ( irq < 0 )
2013-05-30 13:16:10 -07:00
return - EINVAL ;
keypad = devm_kzalloc ( & pdev - > dev , sizeof ( struct nspire_keypad ) ,
GFP_KERNEL ) ;
if ( ! keypad ) {
dev_err ( & pdev - > dev , " failed to allocate keypad memory \n " ) ;
return - ENOMEM ;
}
keypad - > row_shift = get_count_order ( KEYPAD_BITMASK_COLS ) ;
error = of_property_read_u32 ( of_node , " scan-interval " ,
& keypad - > scan_interval ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to get scan-interval \n " ) ;
return error ;
}
error = of_property_read_u32 ( of_node , " row-delay " ,
& keypad - > row_delay ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to get row-delay \n " ) ;
return error ;
}
keypad - > active_low = of_property_read_bool ( of_node , " active-low " ) ;
keypad - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( keypad - > clk ) ) {
dev_err ( & pdev - > dev , " unable to get clock \n " ) ;
return PTR_ERR ( keypad - > clk ) ;
}
Input: keyboard, serio - simplify use of devm_ioremap_resource
Remove unneeded error handling on the result of a call to
platform_get_resource when the value is passed to devm_ioremap_resource.
Move the call to platform_get_resource adjacent to the call to
devm_ioremap_resource to make the connection between them more clear.
A simplified version of the semantic patch that makes this change is as
follows: (http://coccinelle.lip6.fr/)
// <smpl>
@@
expression pdev,res,n,e,e1;
expression ret != 0;
identifier l;
@@
- res = platform_get_resource(pdev, IORESOURCE_MEM, n);
... when != res
- if (res == NULL) { ... \(goto l;\|return ret;\) }
... when != res
+ res = platform_get_resource(pdev, IORESOURCE_MEM, n);
e = devm_ioremap_resource(e1, res);
// </smpl>
Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-08-15 00:08:17 -07:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-05-30 13:16:10 -07:00
keypad - > reg_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
2013-06-26 09:32:50 -07:00
if ( IS_ERR ( keypad - > reg_base ) )
2013-05-30 13:16:10 -07:00
return PTR_ERR ( keypad - > reg_base ) ;
keypad - > input = input = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! input ) {
dev_err ( & pdev - > dev , " failed to allocate input device \n " ) ;
return - ENOMEM ;
}
2021-03-23 10:45:55 -07:00
error = clk_prepare_enable ( keypad - > clk ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to enable clock \n " ) ;
return error ;
}
/* Disable interrupts */
writel ( 0 , keypad - > reg_base + KEYPAD_INTMSK ) ;
/* Acknowledge existing interrupts */
writel ( ~ 0 , keypad - > reg_base + KEYPAD_INT ) ;
/* Disable GPIO interrupts to prevent hanging on touchpad */
/* Possibly used to detect touchpad events */
writel ( 0 , keypad - > reg_base + KEYPAD_UNKNOWN_INT ) ;
/* Acknowledge existing GPIO interrupts */
writel ( ~ 0 , keypad - > reg_base + KEYPAD_UNKNOWN_INT_STS ) ;
clk_disable_unprepare ( keypad - > clk ) ;
2013-05-30 13:16:10 -07:00
input_set_drvdata ( input , keypad ) ;
input - > id . bustype = BUS_HOST ;
input - > name = " nspire-keypad " ;
input - > open = nspire_keypad_open ;
input - > close = nspire_keypad_close ;
__set_bit ( EV_KEY , input - > evbit ) ;
__set_bit ( EV_REP , input - > evbit ) ;
input_set_capability ( input , EV_MSC , MSC_SCAN ) ;
error = matrix_keypad_build_keymap ( NULL , NULL ,
KEYPAD_BITMASK_ROWS ,
KEYPAD_BITMASK_COLS ,
NULL , input ) ;
if ( error ) {
dev_err ( & pdev - > dev , " building keymap failed \n " ) ;
return error ;
}
error = devm_request_irq ( & pdev - > dev , irq , nspire_keypad_irq , 0 ,
" nspire_keypad " , keypad ) ;
if ( error ) {
dev_err ( & pdev - > dev , " allocate irq %d failed \n " , irq ) ;
return error ;
}
error = input_register_device ( input ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" unable to register input device: %d \n " , error ) ;
return error ;
}
dev_dbg ( & pdev - > dev ,
" TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s) \n " ,
res , keypad - > row_delay , keypad - > scan_interval ,
keypad - > active_low ? " , active_low " : " " ) ;
return 0 ;
}
static const struct of_device_id nspire_keypad_dt_match [ ] = {
{ . compatible = " ti,nspire-keypad " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , nspire_keypad_dt_match ) ;
static struct platform_driver nspire_keypad_driver = {
. driver = {
. name = " nspire-keypad " ,
2013-10-06 00:49:32 -07:00
. of_match_table = nspire_keypad_dt_match ,
2013-05-30 13:16:10 -07:00
} ,
. probe = nspire_keypad_probe ,
} ;
module_platform_driver ( nspire_keypad_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " TI-NSPIRE Keypad Driver " ) ;