2012-10-16 11:25:43 +04:00
/*
* TI Touch Screen / ADC MFD driver
*
* Copyright ( C ) 2012 Texas Instruments Incorporated - http : //www.ti.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.
*
* 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/slab.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <linux/regmap.h>
# include <linux/mfd/core.h>
# include <linux/pm_runtime.h>
2013-01-24 07:45:09 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2013-12-19 19:28:31 +04:00
# include <linux/sched.h>
2012-10-16 11:25:43 +04:00
# include <linux/mfd/ti_am335x_tscadc.h>
static const struct regmap_config tscadc_regmap_config = {
. name = " ti_tscadc " ,
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
} ;
2016-06-08 18:54:34 +03:00
void am335x_tsc_se_set_cache ( struct ti_tscadc_dev * tscadc , u32 val )
2013-01-24 07:45:05 +04:00
{
2013-10-22 18:12:39 +04:00
unsigned long flags ;
2016-06-08 18:54:34 +03:00
spin_lock_irqsave ( & tscadc - > reg_lock , flags ) ;
tscadc - > reg_se_cache | = val ;
if ( tscadc - > adc_waiting )
wake_up ( & tscadc - > reg_se_wait ) ;
else if ( ! tscadc - > adc_in_use )
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_SE , tscadc - > reg_se_cache ) ;
2013-12-19 19:28:31 +04:00
2016-06-08 18:54:34 +03:00
spin_unlock_irqrestore ( & tscadc - > reg_lock , flags ) ;
2013-01-24 07:45:05 +04:00
}
2013-12-19 19:28:29 +04:00
EXPORT_SYMBOL_GPL ( am335x_tsc_se_set_cache ) ;
2016-06-08 18:54:34 +03:00
static void am335x_tscadc_need_adc ( struct ti_tscadc_dev * tscadc )
2013-12-19 19:28:31 +04:00
{
DEFINE_WAIT ( wait ) ;
u32 reg ;
2016-06-08 18:54:35 +03:00
regmap_read ( tscadc - > regmap , REG_ADCFSM , & reg ) ;
2013-12-19 19:28:31 +04:00
if ( reg & SEQ_STATUS ) {
2016-06-08 18:54:34 +03:00
tscadc - > adc_waiting = true ;
prepare_to_wait ( & tscadc - > reg_se_wait , & wait ,
2013-12-19 19:28:31 +04:00
TASK_UNINTERRUPTIBLE ) ;
2016-06-08 18:54:34 +03:00
spin_unlock_irq ( & tscadc - > reg_lock ) ;
2013-12-19 19:28:31 +04:00
schedule ( ) ;
2016-06-08 18:54:34 +03:00
spin_lock_irq ( & tscadc - > reg_lock ) ;
finish_wait ( & tscadc - > reg_se_wait , & wait ) ;
2013-12-19 19:28:31 +04:00
2015-01-07 08:49:36 +03:00
/*
* Sequencer should either be idle or
* busy applying the charge step .
*/
2016-06-08 18:54:35 +03:00
regmap_read ( tscadc - > regmap , REG_ADCFSM , & reg ) ;
2015-01-07 08:49:36 +03:00
WARN_ON ( ( reg & SEQ_STATUS ) & & ! ( reg & CHARGE_STEP ) ) ;
2016-06-08 18:54:34 +03:00
tscadc - > adc_waiting = false ;
2013-12-19 19:28:31 +04:00
}
2016-06-08 18:54:34 +03:00
tscadc - > adc_in_use = true ;
2013-12-19 19:28:31 +04:00
}
2016-06-08 18:54:34 +03:00
void am335x_tsc_se_set_once ( struct ti_tscadc_dev * tscadc , u32 val )
2013-12-19 19:28:31 +04:00
{
2016-06-08 18:54:34 +03:00
spin_lock_irq ( & tscadc - > reg_lock ) ;
am335x_tscadc_need_adc ( tscadc ) ;
2013-12-19 19:28:31 +04:00
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_SE , val ) ;
2016-06-08 18:54:34 +03:00
spin_unlock_irq ( & tscadc - > reg_lock ) ;
2013-12-19 19:28:31 +04:00
}
EXPORT_SYMBOL_GPL ( am335x_tsc_se_set_once ) ;
2016-06-08 18:54:34 +03:00
void am335x_tsc_se_adc_done ( struct ti_tscadc_dev * tscadc )
2013-12-19 19:28:29 +04:00
{
unsigned long flags ;
2016-06-08 18:54:34 +03:00
spin_lock_irqsave ( & tscadc - > reg_lock , flags ) ;
tscadc - > adc_in_use = false ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_SE , tscadc - > reg_se_cache ) ;
2016-06-08 18:54:34 +03:00
spin_unlock_irqrestore ( & tscadc - > reg_lock , flags ) ;
2013-12-19 19:28:29 +04:00
}
2013-12-19 19:28:31 +04:00
EXPORT_SYMBOL_GPL ( am335x_tsc_se_adc_done ) ;
2013-01-24 07:45:05 +04:00
2016-06-08 18:54:34 +03:00
void am335x_tsc_se_clr ( struct ti_tscadc_dev * tscadc , u32 val )
2013-01-24 07:45:05 +04:00
{
2013-10-22 18:12:39 +04:00
unsigned long flags ;
2016-06-08 18:54:34 +03:00
spin_lock_irqsave ( & tscadc - > reg_lock , flags ) ;
tscadc - > reg_se_cache & = ~ val ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_SE , tscadc - > reg_se_cache ) ;
2016-06-08 18:54:34 +03:00
spin_unlock_irqrestore ( & tscadc - > reg_lock , flags ) ;
2013-01-24 07:45:05 +04:00
}
EXPORT_SYMBOL_GPL ( am335x_tsc_se_clr ) ;
2016-06-08 18:54:34 +03:00
static void tscadc_idle_config ( struct ti_tscadc_dev * tscadc )
2012-10-16 11:25:43 +04:00
{
unsigned int idleconfig ;
idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_IDLECONFIG , idleconfig ) ;
2012-10-16 11:25:43 +04:00
}
2012-12-22 03:03:15 +04:00
static int ti_tscadc_probe ( struct platform_device * pdev )
2012-10-16 11:25:43 +04:00
{
struct ti_tscadc_dev * tscadc ;
struct resource * res ;
struct clk * clk ;
2017-10-29 15:58:51 +03:00
struct device_node * node ;
2012-10-16 11:25:44 +04:00
struct mfd_cell * cell ;
2013-05-29 19:39:02 +04:00
struct property * prop ;
const __be32 * cur ;
u32 val ;
2012-10-16 11:25:43 +04:00
int err , ctrl ;
2013-09-24 00:43:29 +04:00
int clock_rate ;
2013-01-24 07:45:09 +04:00
int tsc_wires = 0 , adc_channels = 0 , total_channels ;
2013-05-29 19:39:02 +04:00
int readouts = 0 ;
2012-10-16 11:25:43 +04:00
2013-05-21 19:56:49 +04:00
if ( ! pdev - > dev . of_node ) {
dev_err ( & pdev - > dev , " Could not find valid DT data. \n " ) ;
2012-10-16 11:25:43 +04:00
return - EINVAL ;
}
2013-05-21 19:56:49 +04:00
node = of_get_child_by_name ( pdev - > dev . of_node , " tsc " ) ;
of_property_read_u32 ( node , " ti,wires " , & tsc_wires ) ;
2013-05-29 19:39:02 +04:00
of_property_read_u32 ( node , " ti,coordiante-readouts " , & readouts ) ;
2013-01-24 07:45:09 +04:00
2013-05-21 19:56:49 +04:00
node = of_get_child_by_name ( pdev - > dev . of_node , " adc " ) ;
2013-05-29 19:39:02 +04:00
of_property_for_each_u32 ( node , " ti,adc-channels " , prop , cur , val ) {
adc_channels + + ;
if ( val > 7 ) {
dev_err ( & pdev - > dev , " PIN numbers are 0..7 (not %d) \n " ,
val ) ;
return - EINVAL ;
}
}
2012-10-16 11:25:45 +04:00
total_channels = tsc_wires + adc_channels ;
if ( total_channels > 8 ) {
dev_err ( & pdev - > dev , " Number of i/p channels more than 8 \n " ) ;
return - EINVAL ;
}
2012-10-13 17:37:24 +04:00
if ( total_channels = = 0 ) {
dev_err ( & pdev - > dev , " Need atleast one channel. \n " ) ;
return - EINVAL ;
}
2012-10-16 11:25:44 +04:00
2013-05-29 19:39:02 +04:00
if ( readouts * 2 + 2 + adc_channels > 16 ) {
dev_err ( & pdev - > dev , " Too many step configurations requested \n " ) ;
return - EINVAL ;
}
2012-10-16 11:25:43 +04:00
/* Allocate memory for device */
2016-06-08 18:54:36 +03:00
tscadc = devm_kzalloc ( & pdev - > dev , sizeof ( * tscadc ) , GFP_KERNEL ) ;
2018-03-08 17:15:51 +03:00
if ( ! tscadc )
2012-10-16 11:25:43 +04:00
return - ENOMEM ;
2018-03-08 17:15:51 +03:00
2012-10-16 11:25:43 +04:00
tscadc - > dev = & pdev - > dev ;
2012-11-06 12:09:03 +04:00
err = platform_get_irq ( pdev , 0 ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " no irq ID is specified. \n " ) ;
goto ret ;
} else
tscadc - > irq = err ;
2012-10-16 11:25:43 +04:00
2014-02-12 09:31:49 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
tscadc - > tscadc_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( tscadc - > tscadc_base ) )
return PTR_ERR ( tscadc - > tscadc_base ) ;
2012-10-16 11:25:43 +04:00
2019-11-09 08:06:18 +03:00
tscadc - > tscadc_phys_base = res - > start ;
2016-06-08 18:54:35 +03:00
tscadc - > regmap = devm_regmap_init_mmio ( & pdev - > dev ,
2012-10-16 11:25:43 +04:00
tscadc - > tscadc_base , & tscadc_regmap_config ) ;
2016-06-08 18:54:35 +03:00
if ( IS_ERR ( tscadc - > regmap ) ) {
2012-10-16 11:25:43 +04:00
dev_err ( & pdev - > dev , " regmap init failed \n " ) ;
2016-06-08 18:54:35 +03:00
err = PTR_ERR ( tscadc - > regmap ) ;
2012-11-06 12:09:03 +04:00
goto ret ;
2012-10-16 11:25:43 +04:00
}
2013-01-24 07:45:05 +04:00
spin_lock_init ( & tscadc - > reg_lock ) ;
2013-12-19 19:28:31 +04:00
init_waitqueue_head ( & tscadc - > reg_se_wait ) ;
2012-10-16 11:25:43 +04:00
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_get_sync ( & pdev - > dev ) ;
/*
* The TSC_ADC_Subsystem has 2 clock domains
* OCP_CLK and ADC_CLK .
* The ADC clock is expected to run at target of 3 MHz ,
* and expected to capture 12 - bit data at a rate of 200 KSPS .
* The TSC_ADC_SS controller design assumes the OCP clock is
* at least 6 x faster than the ADC clock .
*/
2018-07-04 07:35:29 +03:00
clk = devm_clk_get ( & pdev - > dev , " adc_tsc_fck " ) ;
2012-10-16 11:25:43 +04:00
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " failed to get TSC fck \n " ) ;
err = PTR_ERR ( clk ) ;
goto err_disable_clk ;
}
clock_rate = clk_get_rate ( clk ) ;
2013-09-24 00:43:29 +04:00
tscadc - > clk_div = clock_rate / ADC_CLK ;
2013-07-20 20:27:35 +04:00
2012-10-16 11:25:43 +04:00
/* TSCADC_CLKDIV needs to be configured to the value minus 1 */
2013-09-24 00:43:29 +04:00
tscadc - > clk_div - - ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_CLKDIV , tscadc - > clk_div ) ;
2012-10-16 11:25:43 +04:00
/* Set the control register bits */
2014-09-04 21:01:57 +04:00
ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_CTRL , ctrl ) ;
2012-10-16 11:25:43 +04:00
/* Set register bits for Idle Config Mode */
2014-09-04 21:01:57 +04:00
if ( tsc_wires > 0 ) {
tscadc - > tsc_wires = tsc_wires ;
if ( tsc_wires = = 5 )
ctrl | = CNTRLREG_5WIRE | CNTRLREG_TSCENB ;
else
ctrl | = CNTRLREG_4WIRE | CNTRLREG_TSCENB ;
2013-07-20 20:27:34 +04:00
tscadc_idle_config ( tscadc ) ;
2014-09-04 21:01:57 +04:00
}
2012-10-16 11:25:43 +04:00
/* Enable the TSC module enable bit */
ctrl | = CNTRLREG_TSCSSENB ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_CTRL , ctrl ) ;
2012-10-16 11:25:43 +04:00
2012-10-13 17:37:24 +04:00
tscadc - > used_cells = 0 ;
tscadc - > tsc_cell = - 1 ;
tscadc - > adc_cell = - 1 ;
2012-10-16 11:25:44 +04:00
/* TSC Cell */
2012-10-13 17:37:24 +04:00
if ( tsc_wires > 0 ) {
tscadc - > tsc_cell = tscadc - > used_cells ;
cell = & tscadc - > cells [ tscadc - > used_cells + + ] ;
2013-05-27 19:08:28 +04:00
cell - > name = " TI-am335x-tsc " ;
2012-10-13 17:37:24 +04:00
cell - > of_compatible = " ti,am3359-tsc " ;
cell - > platform_data = & tscadc ;
cell - > pdata_size = sizeof ( tscadc ) ;
}
2012-10-16 11:25:44 +04:00
2012-10-16 11:25:45 +04:00
/* ADC Cell */
2012-10-13 17:37:24 +04:00
if ( adc_channels > 0 ) {
tscadc - > adc_cell = tscadc - > used_cells ;
cell = & tscadc - > cells [ tscadc - > used_cells + + ] ;
2013-05-27 19:12:52 +04:00
cell - > name = " TI-am335x-adc " ;
2012-10-13 17:37:24 +04:00
cell - > of_compatible = " ti,am3359-adc " ;
cell - > platform_data = & tscadc ;
cell - > pdata_size = sizeof ( tscadc ) ;
}
2012-10-16 11:25:45 +04:00
2018-12-03 11:01:17 +03:00
err = mfd_add_devices ( & pdev - > dev , PLATFORM_DEVID_AUTO ,
tscadc - > cells , tscadc - > used_cells , NULL ,
0 , NULL ) ;
2012-10-16 11:25:43 +04:00
if ( err < 0 )
goto err_disable_clk ;
platform_set_drvdata ( pdev , tscadc ) ;
return 0 ;
err_disable_clk :
pm_runtime_put_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2012-11-06 12:09:03 +04:00
ret :
2012-10-16 11:25:43 +04:00
return err ;
}
2012-12-22 03:03:15 +04:00
static int ti_tscadc_remove ( struct platform_device * pdev )
2012-10-16 11:25:43 +04:00
{
struct ti_tscadc_dev * tscadc = platform_get_drvdata ( pdev ) ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_SE , 0x00 ) ;
2012-10-16 11:25:43 +04:00
pm_runtime_put_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
mfd_remove_devices ( tscadc - > dev ) ;
return 0 ;
}
2018-06-30 13:33:16 +03:00
static int __maybe_unused ti_tscadc_can_wakeup ( struct device * dev , void * data )
{
return device_may_wakeup ( dev ) ;
}
2016-06-08 18:54:32 +03:00
static int __maybe_unused tscadc_suspend ( struct device * dev )
2012-10-16 11:25:43 +04:00
{
2016-06-08 18:54:34 +03:00
struct ti_tscadc_dev * tscadc = dev_get_drvdata ( dev ) ;
2012-10-16 11:25:43 +04:00
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_SE , 0x00 ) ;
2018-06-30 13:33:16 +03:00
if ( device_for_each_child ( dev , NULL , ti_tscadc_can_wakeup ) ) {
u32 ctrl ;
regmap_read ( tscadc - > regmap , REG_CTRL , & ctrl ) ;
ctrl & = ~ ( CNTRLREG_POWERDOWN ) ;
ctrl | = CNTRLREG_TSCSSENB ;
regmap_write ( tscadc - > regmap , REG_CTRL , ctrl ) ;
}
2012-10-16 11:25:43 +04:00
pm_runtime_put_sync ( dev ) ;
return 0 ;
}
2016-06-08 18:54:32 +03:00
static int __maybe_unused tscadc_resume ( struct device * dev )
2012-10-16 11:25:43 +04:00
{
2016-06-08 18:54:34 +03:00
struct ti_tscadc_dev * tscadc = dev_get_drvdata ( dev ) ;
2014-09-04 21:01:57 +04:00
u32 ctrl ;
2012-10-16 11:25:43 +04:00
pm_runtime_get_sync ( dev ) ;
/* context restore */
2013-07-20 20:27:34 +04:00
ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_CTRL , ctrl ) ;
2013-07-20 20:27:34 +04:00
2016-06-08 18:54:34 +03:00
if ( tscadc - > tsc_cell ! = - 1 ) {
if ( tscadc - > tsc_wires = = 5 )
2014-09-04 21:01:57 +04:00
ctrl | = CNTRLREG_5WIRE | CNTRLREG_TSCENB ;
else
ctrl | = CNTRLREG_4WIRE | CNTRLREG_TSCENB ;
2016-06-08 18:54:34 +03:00
tscadc_idle_config ( tscadc ) ;
2014-09-04 21:01:57 +04:00
}
ctrl | = CNTRLREG_TSCSSENB ;
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_CTRL , ctrl ) ;
2012-10-16 11:25:43 +04:00
2016-06-08 18:54:35 +03:00
regmap_write ( tscadc - > regmap , REG_CLKDIV , tscadc - > clk_div ) ;
2013-09-24 00:43:29 +04:00
2012-10-16 11:25:43 +04:00
return 0 ;
}
2016-06-08 18:54:32 +03:00
static SIMPLE_DEV_PM_OPS ( tscadc_pm_ops , tscadc_suspend , tscadc_resume ) ;
2012-10-16 11:25:43 +04:00
2013-01-24 07:45:09 +04:00
static const struct of_device_id ti_tscadc_dt_ids [ ] = {
{ . compatible = " ti,am3359-tscadc " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ti_tscadc_dt_ids ) ;
2012-10-16 11:25:43 +04:00
static struct platform_driver ti_tscadc_driver = {
. driver = {
2013-01-24 07:45:09 +04:00
. name = " ti_am3359-tscadc " ,
2016-06-08 18:54:32 +03:00
. pm = & tscadc_pm_ops ,
2013-10-15 07:48:49 +04:00
. of_match_table = ti_tscadc_dt_ids ,
2012-10-16 11:25:43 +04:00
} ,
. probe = ti_tscadc_probe ,
2012-12-22 03:03:15 +04:00
. remove = ti_tscadc_remove ,
2012-10-16 11:25:43 +04:00
} ;
module_platform_driver ( ti_tscadc_driver ) ;
MODULE_DESCRIPTION ( " TI touchscreen / ADC MFD controller driver " ) ;
MODULE_AUTHOR ( " Rachna Patil <rachna@ti.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;