2012-02-14 22:44:56 +04:00
/*
* lpc32xx_adc . c - Support for ADC in LPC32XX
*
* 3 - channel , 10 - bit ADC
*
* Copyright ( C ) 2011 , 2012 Roland Stigge < stigge @ antcom . de >
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/completion.h>
2012-04-21 12:10:16 +04:00
# include <linux/of.h>
2012-02-14 22:44:56 +04:00
2012-04-25 18:54:58 +04:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2012-02-14 22:44:56 +04:00
/*
* LPC32XX registers definitions
*/
2017-02-05 16:06:58 +03:00
# define LPC32XXAD_SELECT(x) ((x) + 0x04)
# define LPC32XXAD_CTRL(x) ((x) + 0x08)
# define LPC32XXAD_VALUE(x) ((x) + 0x48)
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:58 +03:00
/* Bit definitions for LPC32XXAD_SELECT: */
/* constant, always write this value! */
# define LPC32XXAD_REFm 0x00000200
/* constant, always write this value! */
# define LPC32XXAD_REFp 0x00000080
/* multiple of this is the channel number: 0, 1, 2 */
# define LPC32XXAD_IN 0x00000010
/* constant, always write this value! */
# define LPC32XXAD_INTERNAL 0x00000004
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:58 +03:00
/* Bit definitions for LPC32XXAD_CTRL: */
# define LPC32XXAD_STROBE 0x00000002
# define LPC32XXAD_PDN_CTRL 0x00000004
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:58 +03:00
/* Bit definitions for LPC32XXAD_VALUE: */
# define LPC32XXAD_VALUE_MASK 0x000003FF
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:58 +03:00
# define LPC32XXAD_NAME "lpc32xx-adc"
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:59 +03:00
struct lpc32xx_adc_state {
2012-02-14 22:44:56 +04:00
void __iomem * adc_base ;
struct clk * clk ;
struct completion completion ;
u32 value ;
} ;
static int lpc32xx_read_raw ( struct iio_dev * indio_dev ,
2015-10-14 21:14:13 +03:00
struct iio_chan_spec const * chan ,
int * val ,
int * val2 ,
long mask )
2012-02-14 22:44:56 +04:00
{
2017-02-05 16:06:59 +03:00
struct lpc32xx_adc_state * st = iio_priv ( indio_dev ) ;
2017-05-30 14:05:27 +03:00
int ret ;
2012-04-15 20:41:18 +04:00
if ( mask = = IIO_CHAN_INFO_RAW ) {
2012-02-14 22:44:56 +04:00
mutex_lock ( & indio_dev - > mlock ) ;
2017-05-30 14:05:27 +03:00
ret = clk_prepare_enable ( st - > clk ) ;
if ( ret ) {
mutex_unlock ( & indio_dev - > mlock ) ;
return ret ;
}
2012-02-14 22:44:56 +04:00
/* Measurement setup */
2017-02-05 16:06:58 +03:00
__raw_writel ( LPC32XXAD_INTERNAL | ( chan - > address ) |
LPC32XXAD_REFp | LPC32XXAD_REFm ,
2017-02-05 16:06:59 +03:00
LPC32XXAD_SELECT ( st - > adc_base ) ) ;
2012-02-14 22:44:56 +04:00
/* Trigger conversion */
2017-02-05 16:06:58 +03:00
__raw_writel ( LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE ,
2017-02-05 16:06:59 +03:00
LPC32XXAD_CTRL ( st - > adc_base ) ) ;
wait_for_completion ( & st - > completion ) ; /* set by ISR */
clk_disable_unprepare ( st - > clk ) ;
* val = st - > value ;
2012-02-14 22:44:56 +04:00
mutex_unlock ( & indio_dev - > mlock ) ;
return IIO_VAL_INT ;
}
return - EINVAL ;
}
static const struct iio_info lpc32xx_adc_iio_info = {
. read_raw = & lpc32xx_read_raw ,
} ;
2012-04-15 20:41:18 +04:00
# define LPC32XX_ADC_CHANNEL(_index) { \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = _index , \
2013-03-05 01:10:17 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
2017-02-05 16:06:58 +03:00
. address = LPC32XXAD_IN * _index , \
2012-04-15 20:41:18 +04:00
. scan_index = _index , \
2012-02-14 22:44:56 +04:00
}
2012-08-09 11:51:00 +04:00
static const struct iio_chan_spec lpc32xx_adc_iio_channels [ ] = {
2012-02-14 22:44:56 +04:00
LPC32XX_ADC_CHANNEL ( 0 ) ,
LPC32XX_ADC_CHANNEL ( 1 ) ,
LPC32XX_ADC_CHANNEL ( 2 ) ,
} ;
static irqreturn_t lpc32xx_adc_isr ( int irq , void * dev_id )
{
2017-02-05 16:06:59 +03:00
struct lpc32xx_adc_state * st = dev_id ;
2012-02-14 22:44:56 +04:00
/* Read value and clear irq */
2017-02-05 16:06:59 +03:00
st - > value = __raw_readl ( LPC32XXAD_VALUE ( st - > adc_base ) ) &
LPC32XXAD_VALUE_MASK ;
complete ( & st - > completion ) ;
2012-02-14 22:44:56 +04:00
return IRQ_HANDLED ;
}
2012-11-19 22:21:57 +04:00
static int lpc32xx_adc_probe ( struct platform_device * pdev )
2012-02-14 22:44:56 +04:00
{
2017-02-05 16:06:59 +03:00
struct lpc32xx_adc_state * st = NULL ;
2012-02-14 22:44:56 +04:00
struct resource * res ;
int retval = - ENODEV ;
struct iio_dev * iodev = NULL ;
int irq ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " failed to get platform I/O memory \n " ) ;
2015-08-30 11:12:57 +03:00
return - ENXIO ;
2012-02-14 22:44:56 +04:00
}
2017-02-05 16:06:59 +03:00
iodev = devm_iio_device_alloc ( & pdev - > dev , sizeof ( * st ) ) ;
2013-08-31 21:12:00 +04:00
if ( ! iodev )
return - ENOMEM ;
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:59 +03:00
st = iio_priv ( iodev ) ;
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:59 +03:00
st - > adc_base = devm_ioremap ( & pdev - > dev , res - > start ,
resource_size ( res ) ) ;
if ( ! st - > adc_base ) {
2012-02-14 22:44:56 +04:00
dev_err ( & pdev - > dev , " failed mapping memory \n " ) ;
2013-08-31 21:12:00 +04:00
return - EBUSY ;
2012-02-14 22:44:56 +04:00
}
2017-02-05 16:06:59 +03:00
st - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( st - > clk ) ) {
2012-02-14 22:44:56 +04:00
dev_err ( & pdev - > dev , " failed getting clock \n " ) ;
2017-02-05 16:06:59 +03:00
return PTR_ERR ( st - > clk ) ;
2012-02-14 22:44:56 +04:00
}
irq = platform_get_irq ( pdev , 0 ) ;
2013-10-17 00:45:00 +04:00
if ( irq < = 0 ) {
2012-02-14 22:44:56 +04:00
dev_err ( & pdev - > dev , " failed getting interrupt resource \n " ) ;
2015-08-30 11:12:57 +03:00
return - ENXIO ;
2012-02-14 22:44:56 +04:00
}
2013-08-31 21:12:00 +04:00
retval = devm_request_irq ( & pdev - > dev , irq , lpc32xx_adc_isr , 0 ,
2017-02-05 16:06:59 +03:00
LPC32XXAD_NAME , st ) ;
2012-02-14 22:44:56 +04:00
if ( retval < 0 ) {
dev_err ( & pdev - > dev , " failed requesting interrupt \n " ) ;
2013-08-31 21:12:00 +04:00
return retval ;
2012-02-14 22:44:56 +04:00
}
platform_set_drvdata ( pdev , iodev ) ;
2017-02-05 16:06:59 +03:00
init_completion ( & st - > completion ) ;
2012-02-14 22:44:56 +04:00
2017-02-05 16:06:58 +03:00
iodev - > name = LPC32XXAD_NAME ;
2012-02-14 22:44:56 +04:00
iodev - > dev . parent = & pdev - > dev ;
iodev - > info = & lpc32xx_adc_iio_info ;
iodev - > modes = INDIO_DIRECT_MODE ;
iodev - > channels = lpc32xx_adc_iio_channels ;
iodev - > num_channels = ARRAY_SIZE ( lpc32xx_adc_iio_channels ) ;
2013-10-29 15:39:00 +04:00
retval = devm_iio_device_register ( & pdev - > dev , iodev ) ;
2012-02-14 22:44:56 +04:00
if ( retval )
2013-08-31 21:12:00 +04:00
return retval ;
2012-02-14 22:44:56 +04:00
dev_info ( & pdev - > dev , " LPC32XX ADC driver loaded, IRQ %d \n " , irq ) ;
return 0 ;
}
2012-04-21 12:10:16 +04:00
# ifdef CONFIG_OF
static const struct of_device_id lpc32xx_adc_match [ ] = {
{ . compatible = " nxp,lpc3220-adc " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , lpc32xx_adc_match ) ;
# endif
2012-02-14 22:44:56 +04:00
static struct platform_driver lpc32xx_adc_driver = {
. probe = lpc32xx_adc_probe ,
. driver = {
2017-02-05 16:06:58 +03:00
. name = LPC32XXAD_NAME ,
2012-04-21 12:10:16 +04:00
. of_match_table = of_match_ptr ( lpc32xx_adc_match ) ,
2012-02-14 22:44:56 +04:00
} ,
} ;
module_platform_driver ( lpc32xx_adc_driver ) ;
MODULE_AUTHOR ( " Roland Stigge <stigge@antcom.de> " ) ;
MODULE_DESCRIPTION ( " LPC32XX ADC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;