2007-03-16 07:58:52 +03:00
/*
2008-01-31 08:56:46 +03:00
* linux / drivers / input / keyboard / pxa27x_keypad . c
2007-03-16 07:58:52 +03:00
*
* Driver for the pxa27x matrix keyboard controller .
*
* Created : Feb 22 , 2007
* Author : Rodolfo Giometti < giometti @ linux . it >
*
* Based on a previous implementations by Kevin O ' Connor
* < kevin_at_koconnor . net > and Alex Osborne < bobofdoom @ gmail . com > and
* on some suggestions by Nicolas Pitre < nico @ cam . org > .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/input.h>
# include <linux/device.h>
# include <linux/platform_device.h>
2007-08-20 13:19:39 +04:00
# include <linux/clk.h>
# include <linux/err.h>
2007-03-16 07:58:52 +03:00
# include <asm/mach-types.h>
# include <asm/mach/arch.h>
# include <asm/mach/map.h>
# include <asm/arch/hardware.h>
# include <asm/arch/pxa-regs.h>
# include <asm/arch/irqs.h>
2008-01-31 08:56:46 +03:00
# include <asm/arch/pxa27x_keypad.h>
2007-03-16 07:58:52 +03:00
2008-01-31 08:56:46 +03:00
# define DRIVER_NAME "pxa27x-keypad"
2007-03-16 07:58:52 +03:00
2008-01-31 08:58:52 +03:00
# define KPC_MKRN(n) ((((n) & 0x7) - 1) << 26) /* matrix key row number */
# define KPC_MKCN(n) ((((n) & 0x7) - 1) << 23) /* matrix key column number */
# define KPC_DKN(n) ((((n) & 0x7) - 1) << 6) /* direct key number */
2008-01-31 08:58:37 +03:00
# define KPAS_MUKP(n) (((n) >> 26) & 0x1f)
# define KPAS_RP(n) (((n) >> 4) & 0xf)
# define KPAS_CP(n) ((n) & 0xf)
2007-03-16 07:58:52 +03:00
2008-01-31 08:58:37 +03:00
# define KPASMKP_MKC_MASK (0xff)
# define MAX_MATRIX_KEY_NUM (8 * 8)
struct pxa27x_keypad {
struct pxa27x_keypad_platform_data * pdata ;
struct clk * clk ;
struct input_dev * input_dev ;
/* matrix key code map */
unsigned int matrix_keycodes [ MAX_MATRIX_KEY_NUM ] ;
/* state row bits of each column scan */
uint32_t matrix_key_state [ MAX_MATRIX_KEY_COLS ] ;
} ;
static void pxa27x_keypad_build_keycode ( struct pxa27x_keypad * keypad )
{
struct pxa27x_keypad_platform_data * pdata = keypad - > pdata ;
struct input_dev * input_dev = keypad - > input_dev ;
unsigned int * key ;
int i ;
key = & pdata - > matrix_key_map [ 0 ] ;
for ( i = 0 ; i < pdata - > matrix_key_map_size ; i + + , key + + ) {
int row = ( ( * key ) > > 28 ) & 0xf ;
int col = ( ( * key ) > > 24 ) & 0xf ;
int code = ( * key ) & 0xffffff ;
keypad - > matrix_keycodes [ ( row < < 3 ) + col ] = code ;
set_bit ( code , input_dev - > keybit ) ;
}
}
static inline unsigned int lookup_matrix_keycode (
struct pxa27x_keypad * keypad , int row , int col )
{
return keypad - > matrix_keycodes [ ( row < < 3 ) + col ] ;
}
static void pxa27x_keypad_scan_matrix ( struct pxa27x_keypad * keypad )
{
struct pxa27x_keypad_platform_data * pdata = keypad - > pdata ;
int row , col , num_keys_pressed = 0 ;
uint32_t new_state [ MAX_MATRIX_KEY_COLS ] ;
uint32_t kpas = KPAS ;
num_keys_pressed = KPAS_MUKP ( kpas ) ;
memset ( new_state , 0 , sizeof ( new_state ) ) ;
if ( num_keys_pressed = = 0 )
goto scan ;
if ( num_keys_pressed = = 1 ) {
col = KPAS_CP ( kpas ) ;
row = KPAS_RP ( kpas ) ;
/* if invalid row/col, treat as no key pressed */
if ( col > = pdata - > matrix_key_cols | |
row > = pdata - > matrix_key_rows )
goto scan ;
new_state [ col ] = ( 1 < < row ) ;
goto scan ;
}
if ( num_keys_pressed > 1 ) {
uint32_t kpasmkp0 = KPASMKP0 ;
uint32_t kpasmkp1 = KPASMKP1 ;
uint32_t kpasmkp2 = KPASMKP2 ;
uint32_t kpasmkp3 = KPASMKP3 ;
new_state [ 0 ] = kpasmkp0 & KPASMKP_MKC_MASK ;
new_state [ 1 ] = ( kpasmkp0 > > 16 ) & KPASMKP_MKC_MASK ;
new_state [ 2 ] = kpasmkp1 & KPASMKP_MKC_MASK ;
new_state [ 3 ] = ( kpasmkp1 > > 16 ) & KPASMKP_MKC_MASK ;
new_state [ 4 ] = kpasmkp2 & KPASMKP_MKC_MASK ;
new_state [ 5 ] = ( kpasmkp2 > > 16 ) & KPASMKP_MKC_MASK ;
new_state [ 6 ] = kpasmkp3 & KPASMKP_MKC_MASK ;
new_state [ 7 ] = ( kpasmkp3 > > 16 ) & KPASMKP_MKC_MASK ;
}
scan :
for ( col = 0 ; col < pdata - > matrix_key_cols ; col + + ) {
uint32_t bits_changed ;
bits_changed = keypad - > matrix_key_state [ col ] ^ new_state [ col ] ;
if ( bits_changed = = 0 )
continue ;
for ( row = 0 ; row < pdata - > matrix_key_rows ; row + + ) {
if ( ( bits_changed & ( 1 < < row ) ) = = 0 )
continue ;
input_report_key ( keypad - > input_dev ,
lookup_matrix_keycode ( keypad , row , col ) ,
new_state [ col ] & ( 1 < < row ) ) ;
}
}
input_sync ( keypad - > input_dev ) ;
memcpy ( keypad - > matrix_key_state , new_state , sizeof ( new_state ) ) ;
}
2007-08-20 13:19:39 +04:00
2008-01-31 08:58:52 +03:00
# define DEFAULT_KPREC (0x007f007f)
2008-01-31 08:56:46 +03:00
static irqreturn_t pxa27x_keypad_irq_handler ( int irq , void * dev_id )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:58:37 +03:00
struct pxa27x_keypad * keypad = dev_id ;
struct input_dev * input_dev = keypad - > input_dev ;
2007-03-16 07:58:52 +03:00
unsigned long kpc = KPC ;
2008-01-31 08:58:37 +03:00
int rel ;
2007-03-16 07:58:52 +03:00
if ( kpc & KPC_DI ) {
unsigned long kpdk = KPDK ;
if ( ! ( kpdk & KPDK_DKP ) ) {
/* better luck next time */
} else if ( kpc & KPC_REE0 ) {
unsigned long kprec = KPREC ;
KPREC = 0x7f ;
if ( kprec & KPREC_OF0 )
rel = ( kprec & 0xff ) + 0x7f ;
else if ( kprec & KPREC_UF0 )
rel = ( kprec & 0xff ) - 0x7f - 0xff ;
else
rel = ( kprec & 0xff ) - 0x7f ;
if ( rel ) {
input_report_rel ( input_dev , REL_WHEEL , rel ) ;
input_sync ( input_dev ) ;
}
}
}
2008-01-31 08:58:37 +03:00
if ( kpc & KPC_MI )
pxa27x_keypad_scan_matrix ( keypad ) ;
2007-03-16 07:58:52 +03:00
return IRQ_HANDLED ;
}
2008-01-31 08:58:52 +03:00
static void pxa27x_keypad_config ( struct pxa27x_keypad * keypad )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:58:52 +03:00
struct pxa27x_keypad_platform_data * pdata = keypad - > pdata ;
unsigned long kpc = 0 ;
2008-01-31 08:58:37 +03:00
2008-01-31 08:58:52 +03:00
/* enable matrix keys with automatic scan */
if ( pdata - > matrix_key_rows & & pdata - > matrix_key_cols ) {
kpc | = KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL ;
kpc | = KPC_MKRN ( pdata - > matrix_key_rows ) |
KPC_MKCN ( pdata - > matrix_key_cols ) ;
}
2007-03-16 07:58:52 +03:00
2008-01-31 08:58:52 +03:00
/* FIXME: hardcoded to enable rotary 0 _only_ */
kpc | = KPC_DKN ( 2 ) | KPC_REE0 | KPC_DI | KPC_DIE ;
2007-03-16 07:58:52 +03:00
2008-01-31 08:58:52 +03:00
KPC = kpc ;
KPREC = DEFAULT_KPREC ;
}
static int pxa27x_keypad_open ( struct input_dev * dev )
{
struct pxa27x_keypad * keypad = input_get_drvdata ( dev ) ;
2007-03-16 07:58:52 +03:00
/* Enable unit clock */
2008-01-31 08:58:37 +03:00
clk_enable ( keypad - > clk ) ;
2008-01-31 08:58:52 +03:00
pxa27x_keypad_config ( keypad ) ;
2007-03-16 07:58:52 +03:00
return 0 ;
}
2008-01-31 08:56:46 +03:00
static void pxa27x_keypad_close ( struct input_dev * dev )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:58:37 +03:00
struct pxa27x_keypad * keypad = input_get_drvdata ( dev ) ;
2007-03-16 07:58:52 +03:00
/* Disable clock unit */
2008-01-31 08:58:37 +03:00
clk_disable ( keypad - > clk ) ;
2007-03-16 07:58:52 +03:00
}
# ifdef CONFIG_PM
2008-01-31 08:56:46 +03:00
static int pxa27x_keypad_suspend ( struct platform_device * pdev , pm_message_t state )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:58:37 +03:00
struct pxa27x_keypad * keypad = platform_get_drvdata ( pdev ) ;
2007-03-16 07:58:52 +03:00
2008-01-31 08:58:52 +03:00
clk_disable ( keypad - > clk ) ;
2007-03-16 07:58:52 +03:00
return 0 ;
}
2008-01-31 08:56:46 +03:00
static int pxa27x_keypad_resume ( struct platform_device * pdev )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:58:37 +03:00
struct pxa27x_keypad * keypad = platform_get_drvdata ( pdev ) ;
struct input_dev * input_dev = keypad - > input_dev ;
2007-03-16 07:58:52 +03:00
mutex_lock ( & input_dev - > mutex ) ;
if ( input_dev - > users ) {
/* Enable unit clock */
2008-01-31 08:58:37 +03:00
clk_enable ( keypad - > clk ) ;
2008-01-31 08:58:52 +03:00
pxa27x_keypad_config ( keypad ) ;
2007-03-16 07:58:52 +03:00
}
mutex_unlock ( & input_dev - > mutex ) ;
return 0 ;
}
# else
2008-01-31 08:56:46 +03:00
# define pxa27x_keypad_suspend NULL
# define pxa27x_keypad_resume NULL
2007-03-16 07:58:52 +03:00
# endif
2008-01-31 08:56:46 +03:00
static int __devinit pxa27x_keypad_probe ( struct platform_device * pdev )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:58:37 +03:00
struct pxa27x_keypad * keypad ;
2007-03-16 07:58:52 +03:00
struct input_dev * input_dev ;
2008-01-31 08:58:52 +03:00
int error ;
2007-03-16 07:58:52 +03:00
2008-01-31 08:58:37 +03:00
keypad = kzalloc ( sizeof ( struct pxa27x_keypad ) , GFP_KERNEL ) ;
if ( keypad = = NULL ) {
dev_err ( & pdev - > dev , " failed to allocate driver data \n " ) ;
return - ENOMEM ;
}
keypad - > pdata = pdev - > dev . platform_data ;
if ( keypad - > pdata = = NULL ) {
dev_err ( & pdev - > dev , " no platform data defined \n " ) ;
error = - EINVAL ;
goto failed_free ;
}
keypad - > clk = clk_get ( & pdev - > dev , " KBDCLK " ) ;
if ( IS_ERR ( keypad - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get keypad clock \n " ) ;
error = PTR_ERR ( keypad - > clk ) ;
goto failed_free ;
2007-08-20 13:19:39 +04:00
}
2007-03-16 07:58:52 +03:00
/* Create and register the input driver. */
input_dev = input_allocate_device ( ) ;
if ( ! input_dev ) {
2008-01-31 08:58:37 +03:00
dev_err ( & pdev - > dev , " failed to allocate input device \n " ) ;
2007-08-20 13:19:39 +04:00
error = - ENOMEM ;
2008-01-31 08:58:37 +03:00
goto failed_put_clk ;
2007-03-16 07:58:52 +03:00
}
input_dev - > name = DRIVER_NAME ;
input_dev - > id . bustype = BUS_HOST ;
2008-01-31 08:56:46 +03:00
input_dev - > open = pxa27x_keypad_open ;
input_dev - > close = pxa27x_keypad_close ;
2007-04-12 09:34:58 +04:00
input_dev - > dev . parent = & pdev - > dev ;
2007-03-16 07:58:52 +03:00
2008-01-31 08:58:37 +03:00
keypad - > input_dev = input_dev ;
input_set_drvdata ( input_dev , keypad ) ;
2007-10-19 10:40:32 +04:00
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_REP ) |
BIT_MASK ( EV_REL ) ;
input_dev - > relbit [ BIT_WORD ( REL_WHEEL ) ] = BIT_MASK ( REL_WHEEL ) ;
2008-01-31 08:58:37 +03:00
pxa27x_keypad_build_keycode ( keypad ) ;
2007-03-16 07:58:52 +03:00
2008-01-31 08:56:46 +03:00
error = request_irq ( IRQ_KEYPAD , pxa27x_keypad_irq_handler , IRQF_DISABLED ,
2008-01-31 08:58:37 +03:00
DRIVER_NAME , keypad ) ;
2007-03-16 07:58:52 +03:00
if ( error ) {
printk ( KERN_ERR " Cannot request keypad IRQ \n " ) ;
goto err_free_dev ;
}
2008-01-31 08:58:37 +03:00
platform_set_drvdata ( pdev , keypad ) ;
2007-03-16 07:58:52 +03:00
/* Register the input device */
error = input_register_device ( input_dev ) ;
if ( error )
goto err_free_irq ;
return 0 ;
err_free_irq :
platform_set_drvdata ( pdev , NULL ) ;
free_irq ( IRQ_KEYPAD , pdev ) ;
err_free_dev :
input_free_device ( input_dev ) ;
2008-01-31 08:58:37 +03:00
failed_put_clk :
clk_put ( keypad - > clk ) ;
failed_free :
kfree ( keypad ) ;
2007-03-16 07:58:52 +03:00
return error ;
}
2008-01-31 08:56:46 +03:00
static int __devexit pxa27x_keypad_remove ( struct platform_device * pdev )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:58:37 +03:00
struct pxa27x_keypad * keypad = platform_get_drvdata ( pdev ) ;
2007-03-16 07:58:52 +03:00
free_irq ( IRQ_KEYPAD , pdev ) ;
2008-01-31 08:58:37 +03:00
clk_disable ( keypad - > clk ) ;
clk_put ( keypad - > clk ) ;
input_unregister_device ( keypad - > input_dev ) ;
platform_set_drvdata ( pdev , NULL ) ;
kfree ( keypad ) ;
2007-03-16 07:58:52 +03:00
return 0 ;
}
2008-01-31 08:56:46 +03:00
static struct platform_driver pxa27x_keypad_driver = {
. probe = pxa27x_keypad_probe ,
. remove = __devexit_p ( pxa27x_keypad_remove ) ,
. suspend = pxa27x_keypad_suspend ,
. resume = pxa27x_keypad_resume ,
2007-03-16 07:58:52 +03:00
. driver = {
. name = DRIVER_NAME ,
} ,
} ;
2008-01-31 08:56:46 +03:00
static int __init pxa27x_keypad_init ( void )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:56:46 +03:00
return platform_driver_register ( & pxa27x_keypad_driver ) ;
2007-03-16 07:58:52 +03:00
}
2008-01-31 08:56:46 +03:00
static void __exit pxa27x_keypad_exit ( void )
2007-03-16 07:58:52 +03:00
{
2008-01-31 08:56:46 +03:00
platform_driver_unregister ( & pxa27x_keypad_driver ) ;
2007-03-16 07:58:52 +03:00
}
2008-01-31 08:56:46 +03:00
module_init ( pxa27x_keypad_init ) ;
module_exit ( pxa27x_keypad_exit ) ;
2007-03-16 07:58:52 +03:00
2008-01-31 08:56:46 +03:00
MODULE_DESCRIPTION ( " PXA27x Keypad Controller Driver " ) ;
2007-03-16 07:58:52 +03:00
MODULE_LICENSE ( " GPL " ) ;