2015-03-22 07:04:59 +03:00
/*
* Copyright ( C ) 2015 Broadcom Corporation
*
* 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.
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/keyboard.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/of.h>
# include <asm/irq.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <linux/serio.h>
2016-04-06 20:26:27 +03:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2015-03-22 07:04:59 +03:00
# define IPROC_TS_NAME "iproc-ts"
# define PEN_DOWN_STATUS 1
# define PEN_UP_STATUS 0
# define X_MIN 0
# define Y_MIN 0
# define X_MAX 0xFFF
# define Y_MAX 0xFFF
/* Value given by controller for invalid coordinate. */
# define INVALID_COORD 0xFFFFFFFF
/* Register offsets */
# define REGCTL1 0x00
# define REGCTL2 0x04
# define INTERRUPT_THRES 0x08
# define INTERRUPT_MASK 0x0c
# define INTERRUPT_STATUS 0x10
# define CONTROLLER_STATUS 0x14
# define FIFO_DATA 0x18
# define FIFO_DATA_X_Y_MASK 0xFFFF
# define ANALOG_CONTROL 0x1c
# define AUX_DATA 0x20
# define DEBOUNCE_CNTR_STAT 0x24
# define SCAN_CNTR_STAT 0x28
# define REM_CNTR_STAT 0x2c
# define SETTLING_TIMER_STAT 0x30
# define SPARE_REG 0x34
# define SOFT_BYPASS_CONTROL 0x38
# define SOFT_BYPASS_DATA 0x3c
/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */
# define TS_PEN_INTR_MASK BIT(0)
# define TS_FIFO_INTR_MASK BIT(2)
/* Bit values for CONTROLLER_STATUS reg1 */
# define TS_PEN_DOWN BIT(0)
/* Shift values for control reg1 */
# define SCANNING_PERIOD_SHIFT 24
# define DEBOUNCE_TIMEOUT_SHIFT 16
# define SETTLING_TIMEOUT_SHIFT 8
# define TOUCH_TIMEOUT_SHIFT 0
/* Shift values for coordinates from fifo */
# define X_COORD_SHIFT 0
# define Y_COORD_SHIFT 16
/* Bit values for REGCTL2 */
# define TS_CONTROLLER_EN_BIT BIT(16)
# define TS_CONTROLLER_AVGDATA_SHIFT 8
# define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT)
# define TS_CONTROLLER_PWR_LDO BIT(5)
# define TS_CONTROLLER_PWR_ADC BIT(4)
# define TS_CONTROLLER_PWR_BGP BIT(3)
# define TS_CONTROLLER_PWR_TS BIT(2)
# define TS_WIRE_MODE_BIT BIT(1)
# define dbg_reg(dev, priv, reg) \
2016-04-06 20:26:27 +03:00
do { \
u32 val ; \
regmap_read ( priv - > regmap , reg , & val ) ; \
dev_dbg ( dev , " %20s= 0x%08x \n " , # reg , val ) ; \
} while ( 0 )
2015-03-22 07:04:59 +03:00
struct tsc_param {
/* Each step is 1024 us. Valid 1-256 */
u32 scanning_period ;
/* Each step is 512 us. Valid 0-255 */
u32 debounce_timeout ;
/*
* The settling duration ( in ms ) is the amount of time the tsc
* waits to allow the voltage to settle after turning on the
* drivers in detection mode . Valid values : 0 - 11
* 0 = 0.008 ms
* 1 = 0.01 ms
* 2 = 0.02 ms
* 3 = 0.04 ms
* 4 = 0.08 ms
* 5 = 0.16 ms
* 6 = 0.32 ms
* 7 = 0.64 ms
* 8 = 1.28 ms
* 9 = 2.56 ms
* 10 = 5.12 ms
* 11 = 10.24 ms
*/
u32 settling_timeout ;
/* touch timeout in sample counts */
u32 touch_timeout ;
/*
* Number of data samples which are averaged before a final data point
* is placed into the FIFO
*/
u32 average_data ;
/* FIFO threshold */
u32 fifo_threshold ;
/* Optional standard touchscreen properties. */
u32 max_x ;
u32 max_y ;
u32 fuzz_x ;
u32 fuzz_y ;
bool invert_x ;
bool invert_y ;
} ;
struct iproc_ts_priv {
struct platform_device * pdev ;
struct input_dev * idev ;
2016-04-06 20:26:27 +03:00
struct regmap * regmap ;
2015-03-22 07:04:59 +03:00
struct clk * tsc_clk ;
int pen_status ;
struct tsc_param cfg_params ;
} ;
/*
* Set default values the same as hardware reset values
* except for fifo_threshold with is set to 1.
*/
static const struct tsc_param iproc_default_config = {
. scanning_period = 0x5 , /* 1 to 256 */
. debounce_timeout = 0x28 , /* 0 to 255 */
. settling_timeout = 0x7 , /* 0 to 11 */
. touch_timeout = 0xa , /* 0 to 255 */
. average_data = 5 , /* entry 5 = 32 pts */
. fifo_threshold = 1 , /* 0 to 31 */
. max_x = X_MAX ,
. max_y = Y_MAX ,
} ;
static void ts_reg_dump ( struct iproc_ts_priv * priv )
{
struct device * dev = & priv - > pdev - > dev ;
dbg_reg ( dev , priv , REGCTL1 ) ;
dbg_reg ( dev , priv , REGCTL2 ) ;
dbg_reg ( dev , priv , INTERRUPT_THRES ) ;
dbg_reg ( dev , priv , INTERRUPT_MASK ) ;
dbg_reg ( dev , priv , INTERRUPT_STATUS ) ;
dbg_reg ( dev , priv , CONTROLLER_STATUS ) ;
dbg_reg ( dev , priv , FIFO_DATA ) ;
dbg_reg ( dev , priv , ANALOG_CONTROL ) ;
dbg_reg ( dev , priv , AUX_DATA ) ;
dbg_reg ( dev , priv , DEBOUNCE_CNTR_STAT ) ;
dbg_reg ( dev , priv , SCAN_CNTR_STAT ) ;
dbg_reg ( dev , priv , REM_CNTR_STAT ) ;
dbg_reg ( dev , priv , SETTLING_TIMER_STAT ) ;
dbg_reg ( dev , priv , SPARE_REG ) ;
dbg_reg ( dev , priv , SOFT_BYPASS_CONTROL ) ;
dbg_reg ( dev , priv , SOFT_BYPASS_DATA ) ;
}
static irqreturn_t iproc_touchscreen_interrupt ( int irq , void * data )
{
struct platform_device * pdev = data ;
struct iproc_ts_priv * priv = platform_get_drvdata ( pdev ) ;
u32 intr_status ;
u32 raw_coordinate ;
u16 x ;
u16 y ;
int i ;
bool needs_sync = false ;
2016-04-06 20:26:27 +03:00
regmap_read ( priv - > regmap , INTERRUPT_STATUS , & intr_status ) ;
2015-03-22 07:04:59 +03:00
intr_status & = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK ;
if ( intr_status = = 0 )
return IRQ_NONE ;
/* Clear all interrupt status bits, write-1-clear */
2016-04-06 20:26:27 +03:00
regmap_write ( priv - > regmap , INTERRUPT_STATUS , intr_status ) ;
2015-03-22 07:04:59 +03:00
/* Pen up/down */
if ( intr_status & TS_PEN_INTR_MASK ) {
2016-04-06 20:26:27 +03:00
regmap_read ( priv - > regmap , CONTROLLER_STATUS , & priv - > pen_status ) ;
if ( priv - > pen_status & TS_PEN_DOWN )
2015-03-22 07:04:59 +03:00
priv - > pen_status = PEN_DOWN_STATUS ;
else
priv - > pen_status = PEN_UP_STATUS ;
2016-04-06 20:26:27 +03:00
input_report_key ( priv - > idev , BTN_TOUCH , priv - > pen_status ) ;
2015-03-22 07:04:59 +03:00
needs_sync = true ;
dev_dbg ( & priv - > pdev - > dev ,
" pen up-down (%d) \n " , priv - > pen_status ) ;
}
/* coordinates in FIFO exceed the theshold */
if ( intr_status & TS_FIFO_INTR_MASK ) {
for ( i = 0 ; i < priv - > cfg_params . fifo_threshold ; i + + ) {
2016-04-06 20:26:27 +03:00
regmap_read ( priv - > regmap , FIFO_DATA , & raw_coordinate ) ;
2015-03-22 07:04:59 +03:00
if ( raw_coordinate = = INVALID_COORD )
continue ;
/*
* The x and y coordinate are 16 bits each
* with the x in the lower 16 bits and y in the
* upper 16 bits .
*/
x = ( raw_coordinate > > X_COORD_SHIFT ) &
FIFO_DATA_X_Y_MASK ;
y = ( raw_coordinate > > Y_COORD_SHIFT ) &
FIFO_DATA_X_Y_MASK ;
/* We only want to retain the 12 msb of the 16 */
x = ( x > > 4 ) & 0x0FFF ;
y = ( y > > 4 ) & 0x0FFF ;
2016-04-06 20:26:27 +03:00
/* Adjust x y according to LCD tsc mount angle. */
2015-03-22 07:04:59 +03:00
if ( priv - > cfg_params . invert_x )
x = priv - > cfg_params . max_x - x ;
if ( priv - > cfg_params . invert_y )
y = priv - > cfg_params . max_y - y ;
input_report_abs ( priv - > idev , ABS_X , x ) ;
input_report_abs ( priv - > idev , ABS_Y , y ) ;
needs_sync = true ;
dev_dbg ( & priv - > pdev - > dev , " xy (0x%x 0x%x) \n " , x , y ) ;
}
}
if ( needs_sync )
input_sync ( priv - > idev ) ;
return IRQ_HANDLED ;
}
static int iproc_ts_start ( struct input_dev * idev )
{
u32 val ;
2016-04-06 20:26:27 +03:00
u32 mask ;
2015-03-22 07:04:59 +03:00
int error ;
2016-04-06 20:26:27 +03:00
struct iproc_ts_priv * priv = input_get_drvdata ( idev ) ;
2015-03-22 07:04:59 +03:00
/* Enable clock */
error = clk_prepare_enable ( priv - > tsc_clk ) ;
if ( error ) {
dev_err ( & priv - > pdev - > dev , " %s clk_prepare_enable failed %d \n " ,
__func__ , error ) ;
return error ;
}
/*
* Interrupt is generated when :
* FIFO reaches the int_th value , and pen event ( up / down )
*/
val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK ;
2016-04-06 20:26:27 +03:00
regmap_update_bits ( priv - > regmap , INTERRUPT_MASK , val , val ) ;
2015-03-22 07:04:59 +03:00
2016-04-06 20:26:27 +03:00
val = priv - > cfg_params . fifo_threshold ;
regmap_write ( priv - > regmap , INTERRUPT_THRES , val ) ;
2015-03-22 07:04:59 +03:00
/* Initialize control reg1 */
val = 0 ;
val | = priv - > cfg_params . scanning_period < < SCANNING_PERIOD_SHIFT ;
val | = priv - > cfg_params . debounce_timeout < < DEBOUNCE_TIMEOUT_SHIFT ;
val | = priv - > cfg_params . settling_timeout < < SETTLING_TIMEOUT_SHIFT ;
val | = priv - > cfg_params . touch_timeout < < TOUCH_TIMEOUT_SHIFT ;
2016-04-06 20:26:27 +03:00
regmap_write ( priv - > regmap , REGCTL1 , val ) ;
2015-03-22 07:04:59 +03:00
/* Try to clear all interrupt status */
2016-04-06 20:26:27 +03:00
val = TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK ;
regmap_update_bits ( priv - > regmap , INTERRUPT_STATUS , val , val ) ;
2015-03-22 07:04:59 +03:00
/* Initialize control reg2 */
2016-04-06 20:26:27 +03:00
val = TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT ;
2015-03-22 07:04:59 +03:00
val | = priv - > cfg_params . average_data < < TS_CONTROLLER_AVGDATA_SHIFT ;
2016-04-06 20:26:27 +03:00
mask = ( TS_CONTROLLER_AVGDATA_MASK ) ;
mask | = ( TS_CONTROLLER_PWR_LDO | /* PWR up LDO */
2015-03-22 07:04:59 +03:00
TS_CONTROLLER_PWR_ADC | /* PWR up ADC */
TS_CONTROLLER_PWR_BGP | /* PWR up BGP */
TS_CONTROLLER_PWR_TS ) ; /* PWR up TS */
2016-04-06 20:26:27 +03:00
mask | = val ;
regmap_update_bits ( priv - > regmap , REGCTL2 , mask , val ) ;
2015-03-22 07:04:59 +03:00
ts_reg_dump ( priv ) ;
return 0 ;
}
static void iproc_ts_stop ( struct input_dev * dev )
{
u32 val ;
struct iproc_ts_priv * priv = input_get_drvdata ( dev ) ;
2016-04-06 20:26:27 +03:00
/*
* Disable FIFO int_th and pen event ( up / down ) Interrupts only
* as the interrupt mask register is shared between ADC , TS and
* flextimer .
*/
val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK ;
regmap_update_bits ( priv - > regmap , INTERRUPT_MASK , val , 0 ) ;
2015-03-22 07:04:59 +03:00
/* Only power down touch screen controller */
2016-04-06 20:26:27 +03:00
val = TS_CONTROLLER_PWR_TS ;
regmap_update_bits ( priv - > regmap , REGCTL2 , val , val ) ;
2015-03-22 07:04:59 +03:00
clk_disable ( priv - > tsc_clk ) ;
}
static int iproc_get_tsc_config ( struct device * dev , struct iproc_ts_priv * priv )
{
struct device_node * np = dev - > of_node ;
u32 val ;
priv - > cfg_params = iproc_default_config ;
if ( ! np )
return 0 ;
if ( of_property_read_u32 ( np , " scanning_period " , & val ) > = 0 ) {
if ( val < 1 | | val > 256 ) {
dev_err ( dev , " scanning_period (%u) must be [1-256] \n " ,
val ) ;
return - EINVAL ;
}
priv - > cfg_params . scanning_period = val ;
}
if ( of_property_read_u32 ( np , " debounce_timeout " , & val ) > = 0 ) {
if ( val > 255 ) {
dev_err ( dev , " debounce_timeout (%u) must be [0-255] \n " ,
val ) ;
return - EINVAL ;
}
priv - > cfg_params . debounce_timeout = val ;
}
if ( of_property_read_u32 ( np , " settling_timeout " , & val ) > = 0 ) {
if ( val > 11 ) {
dev_err ( dev , " settling_timeout (%u) must be [0-11] \n " ,
val ) ;
return - EINVAL ;
}
priv - > cfg_params . settling_timeout = val ;
}
if ( of_property_read_u32 ( np , " touch_timeout " , & val ) > = 0 ) {
if ( val > 255 ) {
dev_err ( dev , " touch_timeout (%u) must be [0-255] \n " ,
val ) ;
return - EINVAL ;
}
priv - > cfg_params . touch_timeout = val ;
}
if ( of_property_read_u32 ( np , " average_data " , & val ) > = 0 ) {
if ( val > 8 ) {
dev_err ( dev , " average_data (%u) must be [0-8] \n " , val ) ;
return - EINVAL ;
}
priv - > cfg_params . average_data = val ;
}
if ( of_property_read_u32 ( np , " fifo_threshold " , & val ) > = 0 ) {
if ( val > 31 ) {
dev_err ( dev , " fifo_threshold (%u)) must be [0-31] \n " ,
val ) ;
return - EINVAL ;
}
priv - > cfg_params . fifo_threshold = val ;
}
/* Parse optional properties. */
of_property_read_u32 ( np , " touchscreen-size-x " , & priv - > cfg_params . max_x ) ;
of_property_read_u32 ( np , " touchscreen-size-y " , & priv - > cfg_params . max_y ) ;
of_property_read_u32 ( np , " touchscreen-fuzz-x " ,
& priv - > cfg_params . fuzz_x ) ;
of_property_read_u32 ( np , " touchscreen-fuzz-y " ,
& priv - > cfg_params . fuzz_y ) ;
priv - > cfg_params . invert_x =
of_property_read_bool ( np , " touchscreen-inverted-x " ) ;
priv - > cfg_params . invert_y =
of_property_read_bool ( np , " touchscreen-inverted-y " ) ;
return 0 ;
}
static int iproc_ts_probe ( struct platform_device * pdev )
{
struct iproc_ts_priv * priv ;
struct input_dev * idev ;
int irq ;
int error ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2016-04-06 20:26:27 +03:00
/* touchscreen controller memory mapped regs via syscon*/
priv - > regmap = syscon_regmap_lookup_by_phandle ( pdev - > dev . of_node ,
" ts_syscon " ) ;
if ( IS_ERR ( priv - > regmap ) ) {
error = PTR_ERR ( priv - > regmap ) ;
dev_err ( & pdev - > dev , " unable to map I/O memory:%d \n " , error ) ;
2015-03-22 07:04:59 +03:00
return error ;
}
priv - > tsc_clk = devm_clk_get ( & pdev - > dev , " tsc_clk " ) ;
if ( IS_ERR ( priv - > tsc_clk ) ) {
error = PTR_ERR ( priv - > tsc_clk ) ;
dev_err ( & pdev - > dev ,
" failed getting clock tsc_clk: %d \n " , error ) ;
return error ;
}
priv - > pdev = pdev ;
error = iproc_get_tsc_config ( & pdev - > dev , priv ) ;
if ( error ) {
dev_err ( & pdev - > dev , " get_tsc_config failed: %d \n " , error ) ;
return error ;
}
idev = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! idev ) {
dev_err ( & pdev - > dev , " failed to allocate input device \n " ) ;
return - ENOMEM ;
}
priv - > idev = idev ;
priv - > pen_status = PEN_UP_STATUS ;
/* Set input device info */
idev - > name = IPROC_TS_NAME ;
idev - > dev . parent = & pdev - > dev ;
idev - > id . bustype = BUS_HOST ;
idev - > id . vendor = SERIO_UNKNOWN ;
idev - > id . product = 0 ;
idev - > id . version = 0 ;
idev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_ABS ) ;
__set_bit ( BTN_TOUCH , idev - > keybit ) ;
input_set_abs_params ( idev , ABS_X , X_MIN , priv - > cfg_params . max_x ,
priv - > cfg_params . fuzz_x , 0 ) ;
input_set_abs_params ( idev , ABS_Y , Y_MIN , priv - > cfg_params . max_y ,
priv - > cfg_params . fuzz_y , 0 ) ;
idev - > open = iproc_ts_start ;
idev - > close = iproc_ts_stop ;
input_set_drvdata ( idev , priv ) ;
platform_set_drvdata ( pdev , priv ) ;
/* get interrupt */
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " platform_get_irq failed: %d \n " , irq ) ;
return irq ;
}
error = devm_request_irq ( & pdev - > dev , irq ,
iproc_touchscreen_interrupt ,
IRQF_SHARED , IPROC_TS_NAME , pdev ) ;
if ( error )
return error ;
error = input_register_device ( priv - > idev ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" failed to register input device: %d \n " , error ) ;
return error ;
}
return 0 ;
}
static const struct of_device_id iproc_ts_of_match [ ] = {
{ . compatible = " brcm,iproc-touchscreen " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , iproc_ts_of_match ) ;
static struct platform_driver iproc_ts_driver = {
. probe = iproc_ts_probe ,
. driver = {
. name = IPROC_TS_NAME ,
. of_match_table = of_match_ptr ( iproc_ts_of_match ) ,
} ,
} ;
module_platform_driver ( iproc_ts_driver ) ;
MODULE_DESCRIPTION ( " IPROC Touchscreen driver " ) ;
MODULE_AUTHOR ( " Broadcom " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;