2010-11-19 23:41:19 +03:00
/*
* SPEAr Keyboard Driver
* Based on omap - keypad driver
*
* Copyright ( C ) 2010 ST Microelectronics
2015-05-22 19:58:30 +03:00
* Rajeev Kumar < rajeevkumar . linux @ gmail . com >
2010-11-19 23:41:19 +03:00
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/input.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/module.h>
2012-05-11 09:37:16 +04:00
# include <linux/of.h>
2010-11-19 23:41:19 +03:00
# include <linux/platform_device.h>
# include <linux/pm_wakeup.h>
# include <linux/slab.h>
# include <linux/types.h>
2012-08-24 17:22:23 +04:00
# include <linux/platform_data/keyboard-spear.h>
2010-11-19 23:41:19 +03:00
/* Keyboard Registers */
2012-07-08 05:00:54 +04:00
# define MODE_CTL_REG 0x00
2012-07-08 05:00:36 +04:00
# define STATUS_REG 0x0C
# define DATA_REG 0x10
2010-11-19 23:41:19 +03:00
# define INTR_MASK 0x54
/* Register Values */
2012-05-11 09:37:08 +04:00
# define NUM_ROWS 16
# define NUM_COLS 16
2012-07-08 05:01:07 +04:00
# define MODE_CTL_PCLK_FREQ_SHIFT 9
# define MODE_CTL_PCLK_FREQ_MSK 0x7F
2012-05-11 09:37:08 +04:00
2012-07-08 05:00:54 +04:00
# define MODE_CTL_KEYBOARD (0x2 << 0)
# define MODE_CTL_SCAN_RATE_10 (0x0 << 2)
# define MODE_CTL_SCAN_RATE_20 (0x1 << 2)
# define MODE_CTL_SCAN_RATE_40 (0x2 << 2)
# define MODE_CTL_SCAN_RATE_80 (0x3 << 2)
# define MODE_CTL_KEYNUM_SHIFT 6
# define MODE_CTL_START_SCAN (0x1 << 8)
# define STATUS_DATA_AVAIL (0x1 << 1)
# define DATA_ROW_MASK 0xF0
# define DATA_COLUMN_MASK 0x0F
# define ROW_SHIFT 4
2010-11-19 23:41:19 +03:00
struct spear_kbd {
struct input_dev * input ;
void __iomem * io_base ;
struct clk * clk ;
unsigned int irq ;
2012-02-24 12:51:40 +04:00
unsigned int mode ;
2012-11-09 04:35:27 +04:00
unsigned int suspended_rate ;
2010-11-19 23:41:19 +03:00
unsigned short last_key ;
2012-05-11 09:37:08 +04:00
unsigned short keycodes [ NUM_ROWS * NUM_COLS ] ;
2012-05-11 09:37:16 +04:00
bool rep ;
2012-11-09 04:35:27 +04:00
bool irq_wake_enabled ;
2012-07-13 11:11:10 +04:00
u32 mode_ctl_reg ;
2010-11-19 23:41:19 +03:00
} ;
static irqreturn_t spear_kbd_interrupt ( int irq , void * dev_id )
{
struct spear_kbd * kbd = dev_id ;
struct input_dev * input = kbd - > input ;
unsigned int key ;
2012-07-08 05:00:36 +04:00
u32 sts , val ;
2010-11-19 23:41:19 +03:00
2012-07-08 05:00:36 +04:00
sts = readl_relaxed ( kbd - > io_base + STATUS_REG ) ;
2012-07-08 05:00:54 +04:00
if ( ! ( sts & STATUS_DATA_AVAIL ) )
2010-11-19 23:41:19 +03:00
return IRQ_NONE ;
if ( kbd - > last_key ! = KEY_RESERVED ) {
input_report_key ( input , kbd - > last_key , 0 ) ;
kbd - > last_key = KEY_RESERVED ;
}
/* following reads active (row, col) pair */
2012-07-08 05:00:54 +04:00
val = readl_relaxed ( kbd - > io_base + DATA_REG ) &
( DATA_ROW_MASK | DATA_COLUMN_MASK ) ;
2010-11-19 23:41:19 +03:00
key = kbd - > keycodes [ val ] ;
input_event ( input , EV_MSC , MSC_SCAN , val ) ;
input_report_key ( input , key , 1 ) ;
input_sync ( input ) ;
kbd - > last_key = key ;
/* clear interrupt */
2012-07-08 05:00:36 +04:00
writel_relaxed ( 0 , kbd - > io_base + STATUS_REG ) ;
2010-11-19 23:41:19 +03:00
return IRQ_HANDLED ;
}
static int spear_kbd_open ( struct input_dev * dev )
{
struct spear_kbd * kbd = input_get_drvdata ( dev ) ;
int error ;
2012-07-08 05:00:36 +04:00
u32 val ;
2010-11-19 23:41:19 +03:00
kbd - > last_key = KEY_RESERVED ;
error = clk_enable ( kbd - > clk ) ;
if ( error )
return error ;
2012-07-08 05:01:07 +04:00
/* keyboard rate to be programmed is input clock (in MHz) - 1 */
val = clk_get_rate ( kbd - > clk ) / 1000000 - 1 ;
val = ( val & MODE_CTL_PCLK_FREQ_MSK ) < < MODE_CTL_PCLK_FREQ_SHIFT ;
2010-11-19 23:41:19 +03:00
/* program keyboard */
2012-07-08 05:01:07 +04:00
val = MODE_CTL_SCAN_RATE_80 | MODE_CTL_KEYBOARD | val |
2012-07-08 05:00:54 +04:00
( kbd - > mode < < MODE_CTL_KEYNUM_SHIFT ) ;
writel_relaxed ( val , kbd - > io_base + MODE_CTL_REG ) ;
2012-07-08 05:00:36 +04:00
writel_relaxed ( 1 , kbd - > io_base + STATUS_REG ) ;
2010-11-19 23:41:19 +03:00
/* start key scan */
2012-07-08 05:00:54 +04:00
val = readl_relaxed ( kbd - > io_base + MODE_CTL_REG ) ;
val | = MODE_CTL_START_SCAN ;
writel_relaxed ( val , kbd - > io_base + MODE_CTL_REG ) ;
2010-11-19 23:41:19 +03:00
return 0 ;
}
static void spear_kbd_close ( struct input_dev * dev )
{
struct spear_kbd * kbd = input_get_drvdata ( dev ) ;
2012-07-08 05:00:36 +04:00
u32 val ;
2010-11-19 23:41:19 +03:00
/* stop key scan */
2012-07-08 05:00:54 +04:00
val = readl_relaxed ( kbd - > io_base + MODE_CTL_REG ) ;
val & = ~ MODE_CTL_START_SCAN ;
writel_relaxed ( val , kbd - > io_base + MODE_CTL_REG ) ;
2010-11-19 23:41:19 +03:00
clk_disable ( kbd - > clk ) ;
kbd - > last_key = KEY_RESERVED ;
}
2012-05-11 09:37:16 +04:00
# ifdef CONFIG_OF
2012-11-24 09:38:25 +04:00
static int spear_kbd_parse_dt ( struct platform_device * pdev ,
2012-05-11 09:37:16 +04:00
struct spear_kbd * kbd )
2010-11-19 23:41:19 +03:00
{
2012-05-11 09:37:16 +04:00
struct device_node * np = pdev - > dev . of_node ;
2010-11-19 23:41:19 +03:00
int error ;
2012-07-13 11:11:10 +04:00
u32 val , suspended_rate ;
2010-11-19 23:41:19 +03:00
2012-05-11 09:37:16 +04:00
if ( ! np ) {
dev_err ( & pdev - > dev , " Missing DT data \n " ) ;
2010-11-19 23:41:19 +03:00
return - EINVAL ;
}
2012-05-11 09:37:16 +04:00
if ( of_property_read_bool ( np , " autorepeat " ) )
kbd - > rep = true ;
2012-07-13 11:11:10 +04:00
if ( of_property_read_u32 ( np , " suspended_rate " , & suspended_rate ) )
kbd - > suspended_rate = suspended_rate ;
2012-05-11 09:37:16 +04:00
error = of_property_read_u32 ( np , " st,mode " , & val ) ;
if ( error ) {
dev_err ( & pdev - > dev , " DT: Invalid or missing mode \n " ) ;
return error ;
2010-11-19 23:41:19 +03:00
}
2012-05-11 09:37:16 +04:00
kbd - > mode = val ;
return 0 ;
}
# else
static inline int spear_kbd_parse_dt ( struct platform_device * pdev ,
struct spear_kbd * kbd )
{
return - ENOSYS ;
}
# endif
2012-11-24 09:38:25 +04:00
static int spear_kbd_probe ( struct platform_device * pdev )
2012-05-11 09:37:16 +04:00
{
struct kbd_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
const struct matrix_keymap_data * keymap = pdata ? pdata - > keymap : NULL ;
struct spear_kbd * kbd ;
struct input_dev * input_dev ;
struct resource * res ;
int irq ;
int error ;
2010-11-19 23:41:19 +03:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " not able to get irq for the device \n " ) ;
return irq ;
}
2012-11-09 09:41:24 +04:00
kbd = devm_kzalloc ( & pdev - > dev , sizeof ( * kbd ) , GFP_KERNEL ) ;
if ( ! kbd ) {
dev_err ( & pdev - > dev , " not enough memory for driver data \n " ) ;
return - ENOMEM ;
}
input_dev = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! input_dev ) {
dev_err ( & pdev - > dev , " unable to allocate input device \n " ) ;
return - ENOMEM ;
2010-11-19 23:41:19 +03:00
}
kbd - > input = input_dev ;
kbd - > irq = irq ;
2012-05-11 09:37:16 +04:00
if ( ! pdata ) {
error = spear_kbd_parse_dt ( pdev , kbd ) ;
if ( error )
2012-11-09 09:41:24 +04:00
return error ;
2012-05-11 09:37:16 +04:00
} else {
kbd - > mode = pdata - > mode ;
kbd - > rep = pdata - > rep ;
2012-07-13 11:11:10 +04:00
kbd - > suspended_rate = pdata - > suspended_rate ;
2012-05-11 09:37:16 +04:00
}
2012-02-24 12:51:40 +04:00
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 11:08:17 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 14:09:05 +04:00
kbd - > io_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( kbd - > io_base ) )
return PTR_ERR ( kbd - > io_base ) ;
2010-11-19 23:41:19 +03:00
2012-11-09 09:41:24 +04:00
kbd - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( kbd - > clk ) )
return PTR_ERR ( kbd - > clk ) ;
2010-11-19 23:41:19 +03:00
input_dev - > name = " Spear Keyboard " ;
input_dev - > phys = " keyboard/input0 " ;
input_dev - > id . bustype = BUS_HOST ;
input_dev - > id . vendor = 0x0001 ;
input_dev - > id . product = 0x0001 ;
input_dev - > id . version = 0x0100 ;
input_dev - > open = spear_kbd_open ;
input_dev - > close = spear_kbd_close ;
2012-05-11 09:37:08 +04:00
error = matrix_keypad_build_keymap ( keymap , NULL , NUM_ROWS , NUM_COLS ,
kbd - > keycodes , input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " Failed to build keymap \n " ) ;
2012-11-09 09:41:24 +04:00
return error ;
2012-05-11 09:37:08 +04:00
}
2012-05-11 09:37:16 +04:00
if ( kbd - > rep )
2010-11-19 23:41:19 +03:00
__set_bit ( EV_REP , input_dev - > evbit ) ;
input_set_capability ( input_dev , EV_MSC , MSC_SCAN ) ;
input_set_drvdata ( input_dev , kbd ) ;
2012-11-09 09:41:24 +04:00
error = devm_request_irq ( & pdev - > dev , irq , spear_kbd_interrupt , 0 ,
" keyboard " , kbd ) ;
2010-11-19 23:41:19 +03:00
if ( error ) {
2012-11-09 09:41:24 +04:00
dev_err ( & pdev - > dev , " request_irq failed \n " ) ;
return error ;
2010-11-19 23:41:19 +03:00
}
2012-11-26 20:50:08 +04:00
error = clk_prepare ( kbd - > clk ) ;
if ( error )
return error ;
2010-11-19 23:41:19 +03:00
error = input_register_device ( input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " Unable to register keyboard device \n " ) ;
2012-11-26 20:50:08 +04:00
clk_unprepare ( kbd - > clk ) ;
2012-11-09 09:41:24 +04:00
return error ;
2010-11-19 23:41:19 +03:00
}
device_init_wakeup ( & pdev - > dev , 1 ) ;
platform_set_drvdata ( pdev , kbd ) ;
return 0 ;
}
2012-11-24 09:50:47 +04:00
static int spear_kbd_remove ( struct platform_device * pdev )
2010-11-19 23:41:19 +03:00
{
2012-11-26 20:50:08 +04:00
struct spear_kbd * kbd = platform_get_drvdata ( pdev ) ;
input_unregister_device ( kbd - > input ) ;
clk_unprepare ( kbd - > clk ) ;
2012-07-08 05:00:10 +04:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
2010-11-19 23:41:19 +03:00
return 0 ;
}
# ifdef CONFIG_PM
static int spear_kbd_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct spear_kbd * kbd = platform_get_drvdata ( pdev ) ;
struct input_dev * input_dev = kbd - > input ;
2012-07-13 11:11:10 +04:00
unsigned int rate = 0 , mode_ctl_reg , val ;
2010-11-19 23:41:19 +03:00
mutex_lock ( & input_dev - > mutex ) ;
2012-07-13 11:11:10 +04:00
/* explicitly enable clock as we may program device */
clk_enable ( kbd - > clk ) ;
mode_ctl_reg = readl_relaxed ( kbd - > io_base + MODE_CTL_REG ) ;
2012-07-13 11:11:10 +04:00
if ( device_may_wakeup ( & pdev - > dev ) ) {
2012-11-09 04:35:27 +04:00
if ( ! enable_irq_wake ( kbd - > irq ) )
kbd - > irq_wake_enabled = true ;
2012-07-13 11:11:10 +04:00
/*
* reprogram the keyboard operating frequency as on some
* platform it may change during system suspended
*/
if ( kbd - > suspended_rate )
rate = kbd - > suspended_rate / 1000000 - 1 ;
else
rate = clk_get_rate ( kbd - > clk ) / 1000000 - 1 ;
val = mode_ctl_reg &
~ ( MODE_CTL_PCLK_FREQ_MSK < < MODE_CTL_PCLK_FREQ_SHIFT ) ;
val | = ( rate & MODE_CTL_PCLK_FREQ_MSK )
< < MODE_CTL_PCLK_FREQ_SHIFT ;
writel_relaxed ( val , kbd - > io_base + MODE_CTL_REG ) ;
2012-07-13 11:11:10 +04:00
} else {
2012-07-13 11:11:10 +04:00
if ( input_dev - > users ) {
writel_relaxed ( mode_ctl_reg & ~ MODE_CTL_START_SCAN ,
kbd - > io_base + MODE_CTL_REG ) ;
2012-07-13 11:11:10 +04:00
clk_disable ( kbd - > clk ) ;
2012-07-13 11:11:10 +04:00
}
2012-07-13 11:11:10 +04:00
}
2010-11-19 23:41:19 +03:00
2012-07-13 11:11:10 +04:00
/* store current configuration */
if ( input_dev - > users )
kbd - > mode_ctl_reg = mode_ctl_reg ;
/* restore previous clk state */
clk_disable ( kbd - > clk ) ;
2010-11-19 23:41:19 +03:00
mutex_unlock ( & input_dev - > mutex ) ;
return 0 ;
}
static int spear_kbd_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct spear_kbd * kbd = platform_get_drvdata ( pdev ) ;
struct input_dev * input_dev = kbd - > input ;
mutex_lock ( & input_dev - > mutex ) ;
2012-07-13 11:11:10 +04:00
if ( device_may_wakeup ( & pdev - > dev ) ) {
2012-11-09 04:35:27 +04:00
if ( kbd - > irq_wake_enabled ) {
kbd - > irq_wake_enabled = false ;
disable_irq_wake ( kbd - > irq ) ;
}
2012-07-13 11:11:10 +04:00
} else {
if ( input_dev - > users )
clk_enable ( kbd - > clk ) ;
}
2010-11-19 23:41:19 +03:00
2012-07-13 11:11:10 +04:00
/* restore current configuration */
if ( input_dev - > users )
writel_relaxed ( kbd - > mode_ctl_reg , kbd - > io_base + MODE_CTL_REG ) ;
2010-11-19 23:41:19 +03:00
mutex_unlock ( & input_dev - > mutex ) ;
return 0 ;
}
# endif
2012-02-24 12:51:37 +04:00
static SIMPLE_DEV_PM_OPS ( spear_kbd_pm_ops , spear_kbd_suspend , spear_kbd_resume ) ;
2012-05-11 09:37:16 +04:00
# ifdef CONFIG_OF
static const struct of_device_id spear_kbd_id_table [ ] = {
{ . compatible = " st,spear300-kbd " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , spear_kbd_id_table ) ;
# endif
2010-11-19 23:41:19 +03:00
static struct platform_driver spear_kbd_driver = {
. probe = spear_kbd_probe ,
2012-11-24 09:27:39 +04:00
. remove = spear_kbd_remove ,
2010-11-19 23:41:19 +03:00
. driver = {
. name = " keyboard " ,
. pm = & spear_kbd_pm_ops ,
2012-05-11 09:37:16 +04:00
. of_match_table = of_match_ptr ( spear_kbd_id_table ) ,
2010-11-19 23:41:19 +03:00
} ,
} ;
2011-11-29 23:08:39 +04:00
module_platform_driver ( spear_kbd_driver ) ;
2010-11-19 23:41:19 +03:00
MODULE_AUTHOR ( " Rajeev Kumar " ) ;
MODULE_DESCRIPTION ( " SPEAr Keyboard Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;