2014-07-24 01:24:00 +04:00
/*
* Rockchip Successive Approximation Register ( SAR ) A / D Converter
* Copyright ( C ) 2014 ROCKCHIP , Inc .
*
* 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 ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; 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/platform_device.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/of.h>
2014-09-16 01:47:00 +04:00
# include <linux/of_device.h>
2014-07-24 01:24:00 +04:00
# include <linux/clk.h>
# include <linux/completion.h>
# include <linux/regulator/consumer.h>
# include <linux/iio/iio.h>
# define SARADC_DATA 0x00
# define SARADC_STAS 0x04
# define SARADC_STAS_BUSY BIT(0)
# define SARADC_CTRL 0x08
# define SARADC_CTRL_IRQ_STATUS BIT(6)
# define SARADC_CTRL_IRQ_ENABLE BIT(5)
# define SARADC_CTRL_POWER_CTRL BIT(3)
# define SARADC_CTRL_CHN_MASK 0x7
# define SARADC_DLY_PU_SOC 0x0c
# define SARADC_DLY_PU_SOC_MASK 0x3f
# define SARADC_TIMEOUT msecs_to_jiffies(100)
2014-09-16 01:47:00 +04:00
struct rockchip_saradc_data {
int num_bits ;
const struct iio_chan_spec * channels ;
int num_channels ;
unsigned long clk_rate ;
} ;
2014-07-24 01:24:00 +04:00
struct rockchip_saradc {
void __iomem * regs ;
struct clk * pclk ;
struct clk * clk ;
struct completion completion ;
struct regulator * vref ;
2014-09-16 01:47:00 +04:00
const struct rockchip_saradc_data * data ;
2014-07-24 01:24:00 +04:00
u16 last_val ;
} ;
static int rockchip_saradc_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct rockchip_saradc * info = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
mutex_lock ( & indio_dev - > mlock ) ;
reinit_completion ( & info - > completion ) ;
/* 8 clock periods as delay between power up and start cmd */
writel_relaxed ( 8 , info - > regs + SARADC_DLY_PU_SOC ) ;
/* Select the channel to be used and trigger conversion */
writel ( SARADC_CTRL_POWER_CTRL
| ( chan - > channel & SARADC_CTRL_CHN_MASK )
| SARADC_CTRL_IRQ_ENABLE ,
info - > regs + SARADC_CTRL ) ;
if ( ! wait_for_completion_timeout ( & info - > completion ,
SARADC_TIMEOUT ) ) {
writel_relaxed ( 0 , info - > regs + SARADC_CTRL ) ;
mutex_unlock ( & indio_dev - > mlock ) ;
return - ETIMEDOUT ;
}
* val = info - > last_val ;
mutex_unlock ( & indio_dev - > mlock ) ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
ret = regulator_get_voltage ( info - > vref ) ;
if ( ret < 0 ) {
dev_err ( & indio_dev - > dev , " failed to get voltage \n " ) ;
return ret ;
}
* val = ret / 1000 ;
2014-09-16 01:47:00 +04:00
* val2 = info - > data - > num_bits ;
2014-07-24 01:24:00 +04:00
return IIO_VAL_FRACTIONAL_LOG2 ;
default :
return - EINVAL ;
}
}
static irqreturn_t rockchip_saradc_isr ( int irq , void * dev_id )
{
struct rockchip_saradc * info = ( struct rockchip_saradc * ) dev_id ;
/* Read value */
info - > last_val = readl_relaxed ( info - > regs + SARADC_DATA ) ;
2014-09-16 01:47:00 +04:00
info - > last_val & = GENMASK ( info - > data - > num_bits - 1 , 0 ) ;
2014-07-24 01:24:00 +04:00
/* Clear irq & power down adc */
writel_relaxed ( 0 , info - > regs + SARADC_CTRL ) ;
complete ( & info - > completion ) ;
return IRQ_HANDLED ;
}
static const struct iio_info rockchip_saradc_iio_info = {
. read_raw = rockchip_saradc_read_raw ,
. driver_module = THIS_MODULE ,
} ;
# define ADC_CHANNEL(_index, _id) { \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = _index , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
. datasheet_name = _id , \
}
static const struct iio_chan_spec rockchip_saradc_iio_channels [ ] = {
ADC_CHANNEL ( 0 , " adc0 " ) ,
ADC_CHANNEL ( 1 , " adc1 " ) ,
ADC_CHANNEL ( 2 , " adc2 " ) ,
} ;
2014-09-16 01:47:00 +04:00
static const struct rockchip_saradc_data saradc_data = {
. num_bits = 10 ,
. channels = rockchip_saradc_iio_channels ,
. num_channels = ARRAY_SIZE ( rockchip_saradc_iio_channels ) ,
. clk_rate = 1000000 ,
} ;
static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels [ ] = {
ADC_CHANNEL ( 0 , " adc0 " ) ,
ADC_CHANNEL ( 1 , " adc1 " ) ,
} ;
static const struct rockchip_saradc_data rk3066_tsadc_data = {
. num_bits = 12 ,
. channels = rockchip_rk3066_tsadc_iio_channels ,
. num_channels = ARRAY_SIZE ( rockchip_rk3066_tsadc_iio_channels ) ,
. clk_rate = 50000 ,
} ;
static const struct of_device_id rockchip_saradc_match [ ] = {
{
. compatible = " rockchip,saradc " ,
. data = & saradc_data ,
} , {
. compatible = " rockchip,rk3066-tsadc " ,
. data = & rk3066_tsadc_data ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , rockchip_saradc_match ) ;
2014-07-24 01:24:00 +04:00
static int rockchip_saradc_probe ( struct platform_device * pdev )
{
struct rockchip_saradc * info = NULL ;
struct device_node * np = pdev - > dev . of_node ;
struct iio_dev * indio_dev = NULL ;
struct resource * mem ;
2014-09-16 01:47:00 +04:00
const struct of_device_id * match ;
2014-07-24 01:24:00 +04:00
int ret ;
int irq ;
if ( ! np )
return - ENODEV ;
indio_dev = devm_iio_device_alloc ( & pdev - > dev , sizeof ( * info ) ) ;
if ( ! indio_dev ) {
dev_err ( & pdev - > dev , " failed allocating iio device \n " ) ;
return - ENOMEM ;
}
info = iio_priv ( indio_dev ) ;
2014-09-16 01:47:00 +04:00
match = of_match_device ( rockchip_saradc_match , & pdev - > dev ) ;
info - > data = match - > data ;
2014-07-24 01:24:00 +04:00
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
info - > regs = devm_ioremap_resource ( & pdev - > dev , mem ) ;
if ( IS_ERR ( info - > regs ) )
return PTR_ERR ( info - > regs ) ;
init_completion ( & info - > completion ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " no irq resource? \n " ) ;
return irq ;
}
ret = devm_request_irq ( & pdev - > dev , irq , rockchip_saradc_isr ,
0 , dev_name ( & pdev - > dev ) , info ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed requesting irq %d \n " , irq ) ;
return ret ;
}
info - > pclk = devm_clk_get ( & pdev - > dev , " apb_pclk " ) ;
if ( IS_ERR ( info - > pclk ) ) {
dev_err ( & pdev - > dev , " failed to get pclk \n " ) ;
return PTR_ERR ( info - > pclk ) ;
}
info - > clk = devm_clk_get ( & pdev - > dev , " saradc " ) ;
if ( IS_ERR ( info - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get adc clock \n " ) ;
return PTR_ERR ( info - > clk ) ;
}
info - > vref = devm_regulator_get ( & pdev - > dev , " vref " ) ;
if ( IS_ERR ( info - > vref ) ) {
dev_err ( & pdev - > dev , " failed to get regulator, %ld \n " ,
PTR_ERR ( info - > vref ) ) ;
return PTR_ERR ( info - > vref ) ;
}
/*
2014-09-16 01:47:00 +04:00
* Use a default value for the converter clock .
2014-07-24 01:24:00 +04:00
* This may become user - configurable in the future .
*/
2014-09-16 01:47:00 +04:00
ret = clk_set_rate ( info - > clk , info - > data - > clk_rate ) ;
2014-07-24 01:24:00 +04:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to set adc clk rate, %d \n " , ret ) ;
return ret ;
}
ret = regulator_enable ( info - > vref ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to enable vref regulator \n " ) ;
return ret ;
}
ret = clk_prepare_enable ( info - > pclk ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to enable pclk \n " ) ;
goto err_reg_voltage ;
}
ret = clk_prepare_enable ( info - > clk ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to enable converter clock \n " ) ;
goto err_pclk ;
}
platform_set_drvdata ( pdev , indio_dev ) ;
indio_dev - > name = dev_name ( & pdev - > dev ) ;
indio_dev - > dev . parent = & pdev - > dev ;
indio_dev - > dev . of_node = pdev - > dev . of_node ;
indio_dev - > info = & rockchip_saradc_iio_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2014-09-16 01:47:00 +04:00
indio_dev - > channels = info - > data - > channels ;
indio_dev - > num_channels = info - > data - > num_channels ;
2014-07-24 01:24:00 +04:00
ret = iio_device_register ( indio_dev ) ;
if ( ret )
goto err_clk ;
return 0 ;
err_clk :
clk_disable_unprepare ( info - > clk ) ;
err_pclk :
clk_disable_unprepare ( info - > pclk ) ;
err_reg_voltage :
regulator_disable ( info - > vref ) ;
return ret ;
}
static int rockchip_saradc_remove ( struct platform_device * pdev )
{
struct iio_dev * indio_dev = platform_get_drvdata ( pdev ) ;
struct rockchip_saradc * info = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
clk_disable_unprepare ( info - > clk ) ;
clk_disable_unprepare ( info - > pclk ) ;
regulator_disable ( info - > vref ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int rockchip_saradc_suspend ( struct device * dev )
{
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
struct rockchip_saradc * info = iio_priv ( indio_dev ) ;
clk_disable_unprepare ( info - > clk ) ;
clk_disable_unprepare ( info - > pclk ) ;
regulator_disable ( info - > vref ) ;
return 0 ;
}
static int rockchip_saradc_resume ( struct device * dev )
{
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
struct rockchip_saradc * info = iio_priv ( indio_dev ) ;
int ret ;
ret = regulator_enable ( info - > vref ) ;
if ( ret )
return ret ;
ret = clk_prepare_enable ( info - > pclk ) ;
if ( ret )
return ret ;
ret = clk_prepare_enable ( info - > clk ) ;
if ( ret )
return ret ;
return ret ;
}
# endif
static SIMPLE_DEV_PM_OPS ( rockchip_saradc_pm_ops ,
rockchip_saradc_suspend , rockchip_saradc_resume ) ;
static struct platform_driver rockchip_saradc_driver = {
. probe = rockchip_saradc_probe ,
. remove = rockchip_saradc_remove ,
. driver = {
. name = " rockchip-saradc " ,
. of_match_table = rockchip_saradc_match ,
. pm = & rockchip_saradc_pm_ops ,
} ,
} ;
module_platform_driver ( rockchip_saradc_driver ) ;