2017-04-10 17:49:50 +02:00
/*
* This file is part of STM32 DAC driver
*
* Copyright ( C ) 2017 , STMicroelectronics - All Rights Reserved
* Authors : Amelie Delaunay < amelie . delaunay @ st . com >
* Fabrice Gasnier < fabrice . gasnier @ st . com >
*
* License type : GPLv2
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* 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 , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/bitfield.h>
# include <linux/delay.h>
# include <linux/iio/iio.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include "stm32-dac-core.h"
# define STM32_DAC_CHANNEL_1 1
# define STM32_DAC_CHANNEL_2 2
# define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
/**
* struct stm32_dac - private data of DAC driver
* @ common : reference to DAC common data
*/
struct stm32_dac {
struct stm32_dac_common * common ;
} ;
static int stm32_dac_is_enabled ( struct iio_dev * indio_dev , int channel )
{
struct stm32_dac * dac = iio_priv ( indio_dev ) ;
u32 en , val ;
int ret ;
ret = regmap_read ( dac - > common - > regmap , STM32_DAC_CR , & val ) ;
if ( ret < 0 )
return ret ;
if ( STM32_DAC_IS_CHAN_1 ( channel ) )
en = FIELD_GET ( STM32_DAC_CR_EN1 , val ) ;
else
en = FIELD_GET ( STM32_DAC_CR_EN2 , val ) ;
return ! ! en ;
}
static int stm32_dac_set_enable_state ( struct iio_dev * indio_dev , int ch ,
bool enable )
{
struct stm32_dac * dac = iio_priv ( indio_dev ) ;
u32 msk = STM32_DAC_IS_CHAN_1 ( ch ) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2 ;
u32 en = enable ? msk : 0 ;
int ret ;
ret = regmap_update_bits ( dac - > common - > regmap , STM32_DAC_CR , msk , en ) ;
if ( ret < 0 ) {
dev_err ( & indio_dev - > dev , " %s failed \n " , en ?
" Enable " : " Disable " ) ;
return ret ;
}
/*
* When HFSEL is set , it is not allowed to write the DHRx register
* during 8 clock cycles after the ENx bit is set . It is not allowed
* to make software / hardware trigger during this period either .
*/
if ( en & & dac - > common - > hfsel )
udelay ( 1 ) ;
return 0 ;
}
static int stm32_dac_get_value ( struct stm32_dac * dac , int channel , int * val )
{
int ret ;
if ( STM32_DAC_IS_CHAN_1 ( channel ) )
ret = regmap_read ( dac - > common - > regmap , STM32_DAC_DOR1 , val ) ;
else
ret = regmap_read ( dac - > common - > regmap , STM32_DAC_DOR2 , val ) ;
return ret ? ret : IIO_VAL_INT ;
}
static int stm32_dac_set_value ( struct stm32_dac * dac , int channel , int val )
{
int ret ;
if ( STM32_DAC_IS_CHAN_1 ( channel ) )
ret = regmap_write ( dac - > common - > regmap , STM32_DAC_DHR12R1 , val ) ;
else
ret = regmap_write ( dac - > common - > regmap , STM32_DAC_DHR12R2 , val ) ;
return ret ;
}
static int stm32_dac_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct stm32_dac * dac = iio_priv ( indio_dev ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
return stm32_dac_get_value ( dac , chan - > channel , val ) ;
case IIO_CHAN_INFO_SCALE :
* val = dac - > common - > vref_mv ;
* val2 = chan - > scan_type . realbits ;
return IIO_VAL_FRACTIONAL_LOG2 ;
default :
return - EINVAL ;
}
}
static int stm32_dac_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
{
struct stm32_dac * dac = iio_priv ( indio_dev ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
return stm32_dac_set_value ( dac , chan - > channel , val ) ;
default :
return - EINVAL ;
}
}
static int stm32_dac_debugfs_reg_access ( struct iio_dev * indio_dev ,
unsigned reg , unsigned writeval ,
unsigned * readval )
{
struct stm32_dac * dac = iio_priv ( indio_dev ) ;
if ( ! readval )
return regmap_write ( dac - > common - > regmap , reg , writeval ) ;
else
return regmap_read ( dac - > common - > regmap , reg , readval ) ;
}
static const struct iio_info stm32_dac_iio_info = {
. read_raw = stm32_dac_read_raw ,
. write_raw = stm32_dac_write_raw ,
. debugfs_reg_access = stm32_dac_debugfs_reg_access ,
} ;
static const char * const stm32_dac_powerdown_modes [ ] = {
" three_state " ,
} ;
static int stm32_dac_get_powerdown_mode ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan )
{
return 0 ;
}
static int stm32_dac_set_powerdown_mode ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
unsigned int type )
{
return 0 ;
}
static ssize_t stm32_dac_read_powerdown ( struct iio_dev * indio_dev ,
uintptr_t private ,
const struct iio_chan_spec * chan ,
char * buf )
{
int ret = stm32_dac_is_enabled ( indio_dev , chan - > channel ) ;
if ( ret < 0 )
return ret ;
return sprintf ( buf , " %d \n " , ret ? 0 : 1 ) ;
}
static ssize_t stm32_dac_write_powerdown ( struct iio_dev * indio_dev ,
uintptr_t private ,
const struct iio_chan_spec * chan ,
const char * buf , size_t len )
{
bool powerdown ;
int ret ;
ret = strtobool ( buf , & powerdown ) ;
if ( ret )
return ret ;
ret = stm32_dac_set_enable_state ( indio_dev , chan - > channel , ! powerdown ) ;
if ( ret )
return ret ;
return len ;
}
static const struct iio_enum stm32_dac_powerdown_mode_en = {
. items = stm32_dac_powerdown_modes ,
. num_items = ARRAY_SIZE ( stm32_dac_powerdown_modes ) ,
. get = stm32_dac_get_powerdown_mode ,
. set = stm32_dac_set_powerdown_mode ,
} ;
static const struct iio_chan_spec_ext_info stm32_dac_ext_info [ ] = {
{
. name = " powerdown " ,
. read = stm32_dac_read_powerdown ,
. write = stm32_dac_write_powerdown ,
. shared = IIO_SEPARATE ,
} ,
IIO_ENUM ( " powerdown_mode " , IIO_SEPARATE , & stm32_dac_powerdown_mode_en ) ,
IIO_ENUM_AVAILABLE ( " powerdown_mode " , & stm32_dac_powerdown_mode_en ) ,
{ } ,
} ;
# define STM32_DAC_CHANNEL(chan, name) { \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. output = 1 , \
. channel = chan , \
. info_mask_separate = \
BIT ( IIO_CHAN_INFO_RAW ) | \
BIT ( IIO_CHAN_INFO_SCALE ) , \
/* scan_index is always 0 as num_channels is 1 */ \
. scan_type = { \
. sign = ' u ' , \
. realbits = 12 , \
. storagebits = 16 , \
} , \
. datasheet_name = name , \
. ext_info = stm32_dac_ext_info \
}
static const struct iio_chan_spec stm32_dac_channels [ ] = {
STM32_DAC_CHANNEL ( STM32_DAC_CHANNEL_1 , " out1 " ) ,
STM32_DAC_CHANNEL ( STM32_DAC_CHANNEL_2 , " out2 " ) ,
} ;
static int stm32_dac_chan_of_init ( struct iio_dev * indio_dev )
{
struct device_node * np = indio_dev - > dev . of_node ;
unsigned int i ;
u32 channel ;
int ret ;
ret = of_property_read_u32 ( np , " reg " , & channel ) ;
if ( ret ) {
dev_err ( & indio_dev - > dev , " Failed to read reg property \n " ) ;
return ret ;
}
for ( i = 0 ; i < ARRAY_SIZE ( stm32_dac_channels ) ; i + + ) {
if ( stm32_dac_channels [ i ] . channel = = channel )
break ;
}
if ( i > = ARRAY_SIZE ( stm32_dac_channels ) ) {
2017-07-10 15:23:58 +02:00
dev_err ( & indio_dev - > dev , " Invalid reg property \n " ) ;
2017-04-10 17:49:50 +02:00
return - EINVAL ;
}
indio_dev - > channels = & stm32_dac_channels [ i ] ;
/*
* Expose only one channel here , as they can be used independently ,
* with separate trigger . Then separate IIO devices are instantiated
* to manage this .
*/
indio_dev - > num_channels = 1 ;
return 0 ;
} ;
static int stm32_dac_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct iio_dev * indio_dev ;
struct stm32_dac * dac ;
int ret ;
if ( ! np )
return - ENODEV ;
indio_dev = devm_iio_device_alloc ( & pdev - > dev , sizeof ( * dac ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
platform_set_drvdata ( pdev , indio_dev ) ;
dac = iio_priv ( indio_dev ) ;
dac - > common = dev_get_drvdata ( pdev - > dev . parent ) ;
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 = & stm32_dac_iio_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
ret = stm32_dac_chan_of_init ( indio_dev ) ;
if ( ret < 0 )
return ret ;
return devm_iio_device_register ( & pdev - > dev , indio_dev ) ;
}
static const struct of_device_id stm32_dac_of_match [ ] = {
{ . compatible = " st,stm32-dac " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm32_dac_of_match ) ;
static struct platform_driver stm32_dac_driver = {
. probe = stm32_dac_probe ,
. driver = {
. name = " stm32-dac " ,
. of_match_table = stm32_dac_of_match ,
} ,
} ;
module_platform_driver ( stm32_dac_driver ) ;
MODULE_ALIAS ( " platform:stm32-dac " ) ;
MODULE_AUTHOR ( " Amelie Delaunay <amelie.delaunay@st.com> " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics STM32 DAC driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;