2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-07-13 00:08:34 -07:00
/*
* NXP LPC32xx SoC Key Scan Interface
*
* Authors :
* Kevin Wells < kevin . wells @ nxp . com >
* Roland Stigge < stigge @ antcom . de >
*
* Copyright ( C ) 2010 NXP Semiconductors
* Copyright ( C ) 2012 Roland Stigge
*
* This controller supports square key matrices from 1 x1 up to 8 x8
*/
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/irq.h>
# include <linux/pm.h>
# include <linux/platform_device.h>
# include <linux/input.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/input/matrix_keypad.h>
# define DRV_NAME "lpc32xx_keys"
/*
* Key scanner register offsets
*/
# define LPC32XX_KS_DEB(x) ((x) + 0x00)
# define LPC32XX_KS_STATE_COND(x) ((x) + 0x04)
# define LPC32XX_KS_IRQ(x) ((x) + 0x08)
# define LPC32XX_KS_SCAN_CTL(x) ((x) + 0x0C)
# define LPC32XX_KS_FAST_TST(x) ((x) + 0x10)
# define LPC32XX_KS_MATRIX_DIM(x) ((x) + 0x14) /* 1..8 */
# define LPC32XX_KS_DATA(x, y) ((x) + 0x40 + ((y) << 2))
# define LPC32XX_KSCAN_DEB_NUM_DEB_PASS(n) ((n) & 0xFF)
# define LPC32XX_KSCAN_SCOND_IN_IDLE 0x0
# define LPC32XX_KSCAN_SCOND_IN_SCANONCE 0x1
# define LPC32XX_KSCAN_SCOND_IN_IRQGEN 0x2
# define LPC32XX_KSCAN_SCOND_IN_SCAN_MATRIX 0x3
# define LPC32XX_KSCAN_IRQ_PENDING_CLR 0x1
# define LPC32XX_KSCAN_SCTRL_SCAN_DELAY(n) ((n) & 0xFF)
# define LPC32XX_KSCAN_FTST_FORCESCANONCE 0x1
# define LPC32XX_KSCAN_FTST_USE32K_CLK 0x2
# define LPC32XX_KSCAN_MSEL_SELECT(n) ((n) & 0xF)
struct lpc32xx_kscan_drv {
struct input_dev * input ;
struct clk * clk ;
void __iomem * kscan_base ;
unsigned int irq ;
u32 matrix_sz ; /* Size of matrix in XxY, ie. 3 = 3x3 */
u32 deb_clks ; /* Debounce clocks (based on 32KHz clock) */
u32 scan_delay ; /* Scan delay (based on 32KHz clock) */
unsigned short * keymap ; /* Pointer to key map for the scan matrix */
unsigned int row_shift ;
u8 lastkeystates [ 8 ] ;
} ;
static void lpc32xx_mod_states ( struct lpc32xx_kscan_drv * kscandat , int col )
{
struct input_dev * input = kscandat - > input ;
unsigned row , changed , scancode , keycode ;
u8 key ;
key = readl ( LPC32XX_KS_DATA ( kscandat - > kscan_base , col ) ) ;
changed = key ^ kscandat - > lastkeystates [ col ] ;
kscandat - > lastkeystates [ col ] = key ;
for ( row = 0 ; changed ; row + + , changed > > = 1 ) {
if ( changed & 1 ) {
/* Key state changed, signal an event */
scancode = MATRIX_SCAN_CODE ( row , col ,
kscandat - > row_shift ) ;
keycode = kscandat - > keymap [ scancode ] ;
input_event ( input , EV_MSC , MSC_SCAN , scancode ) ;
input_report_key ( input , keycode , key & ( 1 < < row ) ) ;
}
}
}
static irqreturn_t lpc32xx_kscan_irq ( int irq , void * dev_id )
{
struct lpc32xx_kscan_drv * kscandat = dev_id ;
int i ;
for ( i = 0 ; i < kscandat - > matrix_sz ; i + + )
lpc32xx_mod_states ( kscandat , i ) ;
writel ( 1 , LPC32XX_KS_IRQ ( kscandat - > kscan_base ) ) ;
input_sync ( kscandat - > input ) ;
return IRQ_HANDLED ;
}
static int lpc32xx_kscan_open ( struct input_dev * dev )
{
struct lpc32xx_kscan_drv * kscandat = input_get_drvdata ( dev ) ;
int error ;
error = clk_prepare_enable ( kscandat - > clk ) ;
if ( error )
return error ;
writel ( 1 , LPC32XX_KS_IRQ ( kscandat - > kscan_base ) ) ;
return 0 ;
}
static void lpc32xx_kscan_close ( struct input_dev * dev )
{
struct lpc32xx_kscan_drv * kscandat = input_get_drvdata ( dev ) ;
writel ( 1 , LPC32XX_KS_IRQ ( kscandat - > kscan_base ) ) ;
clk_disable_unprepare ( kscandat - > clk ) ;
}
2012-11-23 21:38:25 -08:00
static int lpc32xx_parse_dt ( struct device * dev ,
2012-07-13 00:08:34 -07:00
struct lpc32xx_kscan_drv * kscandat )
{
struct device_node * np = dev - > of_node ;
u32 rows = 0 , columns = 0 ;
2013-02-25 14:08:40 -08:00
int err ;
2012-07-13 00:08:34 -07:00
2016-11-11 12:43:12 -08:00
err = matrix_keypad_parse_properties ( dev , & rows , & columns ) ;
2013-02-25 14:08:40 -08:00
if ( err )
return err ;
if ( rows ! = columns ) {
dev_err ( dev , " rows and columns must be equal! \n " ) ;
2012-07-13 00:08:34 -07:00
return - EINVAL ;
}
kscandat - > matrix_sz = rows ;
kscandat - > row_shift = get_count_order ( columns ) ;
of_property_read_u32 ( np , " nxp,debounce-delay-ms " , & kscandat - > deb_clks ) ;
of_property_read_u32 ( np , " nxp,scan-delay-ms " , & kscandat - > scan_delay ) ;
if ( ! kscandat - > deb_clks | | ! kscandat - > scan_delay ) {
dev_err ( dev , " debounce or scan delay not specified \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2012-11-23 21:38:25 -08:00
static int lpc32xx_kscan_probe ( struct platform_device * pdev )
2012-07-13 00:08:34 -07:00
{
struct lpc32xx_kscan_drv * kscandat ;
struct input_dev * input ;
struct resource * res ;
size_t keymap_size ;
int error ;
int irq ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " failed to get platform I/O memory \n " ) ;
return - EINVAL ;
}
irq = platform_get_irq ( pdev , 0 ) ;
2019-08-14 10:46:38 -07:00
if ( irq < 0 )
2012-07-13 00:08:34 -07:00
return - EINVAL ;
2014-10-08 11:26:29 -07:00
kscandat = devm_kzalloc ( & pdev - > dev , sizeof ( * kscandat ) ,
GFP_KERNEL ) ;
if ( ! kscandat )
2012-07-13 00:08:34 -07:00
return - ENOMEM ;
error = lpc32xx_parse_dt ( & pdev - > dev , kscandat ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to parse device tree \n " ) ;
2014-10-08 11:26:29 -07:00
return error ;
2012-07-13 00:08:34 -07:00
}
keymap_size = sizeof ( kscandat - > keymap [ 0 ] ) *
( kscandat - > matrix_sz < < kscandat - > row_shift ) ;
2014-10-08 11:26:29 -07:00
kscandat - > keymap = devm_kzalloc ( & pdev - > dev , keymap_size , GFP_KERNEL ) ;
if ( ! kscandat - > keymap )
return - ENOMEM ;
2012-07-13 00:08:34 -07:00
2014-10-08 11:26:29 -07:00
kscandat - > input = input = devm_input_allocate_device ( & pdev - > dev ) ;
2012-07-13 00:08:34 -07:00
if ( ! input ) {
dev_err ( & pdev - > dev , " failed to allocate input device \n " ) ;
2014-10-08 11:26:29 -07:00
return - ENOMEM ;
2012-07-13 00:08:34 -07:00
}
/* Setup key input */
input - > name = pdev - > name ;
input - > phys = " lpc32xx/input0 " ;
input - > id . vendor = 0x0001 ;
input - > id . product = 0x0001 ;
input - > id . version = 0x0100 ;
input - > open = lpc32xx_kscan_open ;
input - > close = lpc32xx_kscan_close ;
input - > dev . parent = & pdev - > dev ;
input_set_capability ( input , EV_MSC , MSC_SCAN ) ;
error = matrix_keypad_build_keymap ( NULL , NULL ,
kscandat - > matrix_sz ,
kscandat - > matrix_sz ,
kscandat - > keymap , kscandat - > input ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to build keymap \n " ) ;
2014-10-08 11:26:29 -07:00
return error ;
2012-07-13 00:08:34 -07:00
}
input_set_drvdata ( kscandat - > input , kscandat ) ;
2014-10-08 11:26:29 -07:00
kscandat - > kscan_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( kscandat - > kscan_base ) )
return PTR_ERR ( kscandat - > kscan_base ) ;
2012-07-13 00:08:34 -07:00
/* Get the key scanner clock */
2014-10-08 11:26:29 -07:00
kscandat - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2012-07-13 00:08:34 -07:00
if ( IS_ERR ( kscandat - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
2014-10-08 11:26:29 -07:00
return PTR_ERR ( kscandat - > clk ) ;
2012-07-13 00:08:34 -07:00
}
/* Configure the key scanner */
error = clk_prepare_enable ( kscandat - > clk ) ;
if ( error )
2014-10-08 11:26:29 -07:00
return error ;
2012-07-13 00:08:34 -07:00
writel ( kscandat - > deb_clks , LPC32XX_KS_DEB ( kscandat - > kscan_base ) ) ;
writel ( kscandat - > scan_delay , LPC32XX_KS_SCAN_CTL ( kscandat - > kscan_base ) ) ;
writel ( LPC32XX_KSCAN_FTST_USE32K_CLK ,
LPC32XX_KS_FAST_TST ( kscandat - > kscan_base ) ) ;
writel ( kscandat - > matrix_sz ,
LPC32XX_KS_MATRIX_DIM ( kscandat - > kscan_base ) ) ;
writel ( 1 , LPC32XX_KS_IRQ ( kscandat - > kscan_base ) ) ;
clk_disable_unprepare ( kscandat - > clk ) ;
2014-10-08 11:26:29 -07:00
error = devm_request_irq ( & pdev - > dev , irq , lpc32xx_kscan_irq , 0 ,
pdev - > name , kscandat ) ;
2012-07-13 00:08:34 -07:00
if ( error ) {
dev_err ( & pdev - > dev , " failed to request irq \n " ) ;
2014-10-08 11:26:29 -07:00
return error ;
2012-07-13 00:08:34 -07:00
}
error = input_register_device ( kscandat - > input ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to register input device \n " ) ;
2014-10-08 11:26:29 -07:00
return error ;
2012-07-13 00:08:34 -07:00
}
platform_set_drvdata ( pdev , kscandat ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int lpc32xx_kscan_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct lpc32xx_kscan_drv * kscandat = platform_get_drvdata ( pdev ) ;
struct input_dev * input = kscandat - > input ;
mutex_lock ( & input - > mutex ) ;
if ( input - > users ) {
/* Clear IRQ and disable clock */
writel ( 1 , LPC32XX_KS_IRQ ( kscandat - > kscan_base ) ) ;
clk_disable_unprepare ( kscandat - > clk ) ;
}
mutex_unlock ( & input - > mutex ) ;
return 0 ;
}
static int lpc32xx_kscan_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct lpc32xx_kscan_drv * kscandat = platform_get_drvdata ( pdev ) ;
struct input_dev * input = kscandat - > input ;
int retval = 0 ;
mutex_lock ( & input - > mutex ) ;
if ( input - > users ) {
/* Enable clock and clear IRQ */
retval = clk_prepare_enable ( kscandat - > clk ) ;
if ( retval = = 0 )
writel ( 1 , LPC32XX_KS_IRQ ( kscandat - > kscan_base ) ) ;
}
mutex_unlock ( & input - > mutex ) ;
return retval ;
}
# endif
static SIMPLE_DEV_PM_OPS ( lpc32xx_kscan_pm_ops , lpc32xx_kscan_suspend ,
lpc32xx_kscan_resume ) ;
static const struct of_device_id lpc32xx_kscan_match [ ] = {
{ . compatible = " nxp,lpc3220-key " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , lpc32xx_kscan_match ) ;
static struct platform_driver lpc32xx_kscan_driver = {
. probe = lpc32xx_kscan_probe ,
. driver = {
. name = DRV_NAME ,
. pm = & lpc32xx_kscan_pm_ops ,
2013-10-06 00:47:35 -07:00
. of_match_table = lpc32xx_kscan_match ,
2012-07-13 00:08:34 -07:00
}
} ;
module_platform_driver ( lpc32xx_kscan_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Kevin Wells <kevin.wells@nxp.com> " ) ;
MODULE_AUTHOR ( " Roland Stigge <stigge@antcom.de> " ) ;
MODULE_DESCRIPTION ( " Key scanner driver for LPC32XX devices " ) ;