2009-08-10 08:22:22 +04:00
/*
* Copyright ( c ) 2008 - 2009 Nuvoton technology corporation .
*
* Wan ZongShun < mcuos . com @ gmail . com >
*
* 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 .
*
*/
# 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>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/io.h>
# include <mach/w90p910_keypad.h>
/* Keypad Interface Control Registers */
# define KPI_CONF 0x00
# define KPI_3KCONF 0x04
# define KPI_LPCONF 0x08
# define KPI_STATUS 0x0C
# define IS1KEY (0x01 << 16)
# define INTTR (0x01 << 21)
# define KEY0R (0x0f << 3)
# define KEY0C 0x07
# define DEBOUNCE_BIT 0x08
# define KSIZE0 (0x01 << 16)
# define KSIZE1 (0x01 << 17)
# define KPSEL (0x01 << 19)
# define ENKP (0x01 << 18)
# define KGET_RAW(n) (((n) & KEY0R) >> 3)
# define KGET_COLUMN(n) ((n) & KEY0C)
2009-08-10 08:22:22 +04:00
# define W90P910_MAX_KEY_NUM (8 * 8)
# define W90P910_ROW_SHIFT 3
2009-08-10 08:22:22 +04:00
struct w90p910_keypad {
2009-08-10 08:22:22 +04:00
const struct w90p910_keypad_platform_data * pdata ;
2009-08-10 08:22:22 +04:00
struct clk * clk ;
struct input_dev * input_dev ;
void __iomem * mmio_base ;
int irq ;
2009-08-10 08:22:22 +04:00
unsigned short keymap [ W90P910_MAX_KEY_NUM ] ;
2009-08-10 08:22:22 +04:00
} ;
static void w90p910_keypad_scan_matrix ( struct w90p910_keypad * keypad ,
unsigned int status )
{
2009-08-10 08:22:22 +04:00
struct input_dev * input_dev = keypad - > input_dev ;
unsigned int row = KGET_RAW ( status ) ;
unsigned int col = KGET_COLUMN ( status ) ;
unsigned int code = MATRIX_SCAN_CODE ( row , col , W90P910_ROW_SHIFT ) ;
unsigned int key = keypad - > keymap [ code ] ;
input_event ( input_dev , EV_MSC , MSC_SCAN , code ) ;
input_report_key ( input_dev , key , 1 ) ;
input_sync ( input_dev ) ;
input_event ( input_dev , EV_MSC , MSC_SCAN , code ) ;
input_report_key ( input_dev , key , 0 ) ;
input_sync ( input_dev ) ;
2009-08-10 08:22:22 +04:00
}
static irqreturn_t w90p910_keypad_irq_handler ( int irq , void * dev_id )
{
struct w90p910_keypad * keypad = dev_id ;
unsigned int kstatus , val ;
kstatus = __raw_readl ( keypad - > mmio_base + KPI_STATUS ) ;
val = INTTR | IS1KEY ;
if ( kstatus & val )
w90p910_keypad_scan_matrix ( keypad , kstatus ) ;
return IRQ_HANDLED ;
}
static int w90p910_keypad_open ( struct input_dev * dev )
{
struct w90p910_keypad * keypad = input_get_drvdata ( dev ) ;
2009-08-10 08:22:22 +04:00
const struct w90p910_keypad_platform_data * pdata = keypad - > pdata ;
2009-08-10 08:22:22 +04:00
unsigned int val , config ;
/* Enable unit clock */
clk_enable ( keypad - > clk ) ;
val = __raw_readl ( keypad - > mmio_base + KPI_CONF ) ;
val | = ( KPSEL | ENKP ) ;
val & = ~ ( KSIZE0 | KSIZE1 ) ;
config = pdata - > prescale | ( pdata - > debounce < < DEBOUNCE_BIT ) ;
val | = config ;
__raw_writel ( val , keypad - > mmio_base + KPI_CONF ) ;
return 0 ;
}
static void w90p910_keypad_close ( struct input_dev * dev )
{
struct w90p910_keypad * keypad = input_get_drvdata ( dev ) ;
/* Disable clock unit */
clk_disable ( keypad - > clk ) ;
}
static int __devinit w90p910_keypad_probe ( struct platform_device * pdev )
{
2009-08-10 08:22:22 +04:00
const struct w90p910_keypad_platform_data * pdata =
pdev - > dev . platform_data ;
const struct matrix_keymap_data * keymap_data = pdata - > keymap_data ;
2009-08-10 08:22:22 +04:00
struct w90p910_keypad * keypad ;
struct input_dev * input_dev ;
struct resource * res ;
2009-08-10 08:22:22 +04:00
int irq ;
int error ;
int i ;
2009-08-10 08:22:22 +04:00
2009-08-10 08:22:22 +04:00
if ( ! pdata ) {
2009-08-10 08:22:22 +04:00
dev_err ( & pdev - > dev , " no platform data defined \n " ) ;
2009-08-10 08:22:22 +04:00
return - EINVAL ;
2009-08-10 08:22:22 +04:00
}
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " failed to get keypad irq \n " ) ;
2009-08-10 08:22:22 +04:00
return - ENXIO ;
}
keypad = kzalloc ( sizeof ( struct w90p910_keypad ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! keypad | | ! input_dev ) {
dev_err ( & pdev - > dev , " failed to allocate driver data \n " ) ;
error = - ENOMEM ;
2009-08-10 08:22:22 +04:00
goto failed_free ;
}
2009-08-10 08:22:22 +04:00
keypad - > pdata = pdata ;
keypad - > input_dev = input_dev ;
keypad - > irq = irq ;
2009-08-10 08:22:22 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " failed to get I/O memory \n " ) ;
error = - ENXIO ;
goto failed_free ;
}
res = request_mem_region ( res - > start , resource_size ( res ) , pdev - > name ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " failed to request I/O memory \n " ) ;
error = - EBUSY ;
goto failed_free ;
}
keypad - > mmio_base = ioremap ( res - > start , resource_size ( res ) ) ;
if ( keypad - > mmio_base = = NULL ) {
dev_err ( & pdev - > dev , " failed to remap I/O memory \n " ) ;
error = - ENXIO ;
2009-08-10 08:22:22 +04:00
goto failed_free_res ;
2009-08-10 08:22:22 +04:00
}
keypad - > clk = clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( keypad - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get keypad clock \n " ) ;
error = PTR_ERR ( keypad - > clk ) ;
goto failed_free_io ;
}
/* set multi-function pin for w90p910 kpi. */
mfp_set_groupi ( & pdev - > dev ) ;
input_dev - > name = pdev - > name ;
input_dev - > id . bustype = BUS_HOST ;
input_dev - > open = w90p910_keypad_open ;
input_dev - > close = w90p910_keypad_close ;
input_dev - > dev . parent = & pdev - > dev ;
2009-08-10 08:22:22 +04:00
input_dev - > keycode = keypad - > keymap ;
input_dev - > keycodesize = sizeof ( keypad - > keymap [ 0 ] ) ;
input_dev - > keycodemax = ARRAY_SIZE ( keypad - > keymap ) ;
2009-08-10 08:22:22 +04:00
input_set_drvdata ( input_dev , keypad ) ;
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_REP ) ;
2009-08-10 08:22:22 +04:00
input_set_capability ( input_dev , EV_MSC , MSC_SCAN ) ;
for ( i = 0 ; i < keymap_data - > keymap_size ; i + + ) {
unsigned int key = keymap_data - > keymap [ i ] ;
unsigned int row = KEY_ROW ( key ) ;
unsigned int col = KEY_COL ( key ) ;
unsigned short keycode = KEY_VAL ( key ) ;
unsigned int scancode = MATRIX_SCAN_CODE ( row , col ,
W90P910_ROW_SHIFT ) ;
keypad - > keymap [ scancode ] = keycode ;
__set_bit ( keycode , input_dev - > keybit ) ;
}
__clear_bit ( KEY_RESERVED , input_dev - > keybit ) ;
2009-08-10 08:22:22 +04:00
2009-08-10 08:22:22 +04:00
error = request_irq ( keypad - > irq , w90p910_keypad_irq_handler ,
IRQF_DISABLED , pdev - > name , keypad ) ;
2009-08-10 08:22:22 +04:00
if ( error ) {
dev_err ( & pdev - > dev , " failed to request IRQ \n " ) ;
2009-08-10 08:22:22 +04:00
goto failed_put_clk ;
2009-08-10 08:22:22 +04:00
}
/* Register the input device */
error = input_register_device ( input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to register input device \n " ) ;
goto failed_free_irq ;
}
2009-08-10 08:22:22 +04:00
platform_set_drvdata ( pdev , keypad ) ;
2009-08-10 08:22:22 +04:00
return 0 ;
failed_free_irq :
free_irq ( irq , pdev ) ;
failed_put_clk :
clk_put ( keypad - > clk ) ;
failed_free_io :
iounmap ( keypad - > mmio_base ) ;
2009-08-10 08:22:22 +04:00
failed_free_res :
2009-08-10 08:22:22 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
failed_free :
2009-08-10 08:22:22 +04:00
input_free_device ( input_dev ) ;
2009-08-10 08:22:22 +04:00
kfree ( keypad ) ;
return error ;
}
static int __devexit w90p910_keypad_remove ( struct platform_device * pdev )
{
struct w90p910_keypad * keypad = platform_get_drvdata ( pdev ) ;
struct resource * res ;
free_irq ( keypad - > irq , pdev ) ;
clk_put ( keypad - > clk ) ;
input_unregister_device ( keypad - > input_dev ) ;
iounmap ( keypad - > mmio_base ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
release_mem_region ( res - > start , resource_size ( res ) ) ;
platform_set_drvdata ( pdev , NULL ) ;
kfree ( keypad ) ;
2009-08-10 08:22:22 +04:00
2009-08-10 08:22:22 +04:00
return 0 ;
}
static struct platform_driver w90p910_keypad_driver = {
. probe = w90p910_keypad_probe ,
. remove = __devexit_p ( w90p910_keypad_remove ) ,
. driver = {
. name = " w90p910-keypad " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init w90p910_keypad_init ( void )
{
return platform_driver_register ( & w90p910_keypad_driver ) ;
}
static void __exit w90p910_keypad_exit ( void )
{
platform_driver_unregister ( & w90p910_keypad_driver ) ;
}
module_init ( w90p910_keypad_init ) ;
module_exit ( w90p910_keypad_exit ) ;
MODULE_AUTHOR ( " Wan ZongShun <mcuos.com@gmail.com> " ) ;
MODULE_DESCRIPTION ( " w90p910 keypad driver! " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:w90p910-keypad " ) ;