2017-01-31 10:12:56 +03:00
/*
2017-03-04 11:31:25 +03:00
* ADXL345 3 - Axis Digital Accelerometer IIO core driver
2017-01-31 10:12:56 +03:00
*
* Copyright ( c ) 2017 Eva Rachel Retuya < eraretuya @ gmail . com >
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License . See the file COPYING in the main
* directory of this archive for more details .
*/
# include <linux/module.h>
2017-03-04 11:31:24 +03:00
# include <linux/regmap.h>
2017-01-31 10:12:56 +03:00
# include <linux/iio/iio.h>
2017-03-04 11:31:25 +03:00
# include "adxl345.h"
2017-01-31 10:12:56 +03:00
# define ADXL345_REG_DEVID 0x00
# define ADXL345_REG_POWER_CTL 0x2D
# define ADXL345_REG_DATA_FORMAT 0x31
# define ADXL345_REG_DATAX0 0x32
# define ADXL345_REG_DATAY0 0x34
# define ADXL345_REG_DATAZ0 0x36
# define ADXL345_POWER_CTL_MEASURE BIT(3)
# define ADXL345_POWER_CTL_STANDBY 0x00
# define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
# define ADXL345_DATA_FORMAT_2G 0
# define ADXL345_DATA_FORMAT_4G 1
# define ADXL345_DATA_FORMAT_8G 2
# define ADXL345_DATA_FORMAT_16G 3
# define ADXL345_DEVID 0xE5
/*
* In full - resolution mode , scale factor is maintained at ~ 4 mg / LSB
* in all g ranges .
*
* At + / - 16 g with 13 - bit resolution , scale is computed as :
* ( 16 + 16 ) * 9.81 / ( 2 ^ 13 - 1 ) = 0.0383
*/
static const int adxl345_uscale = 38300 ;
struct adxl345_data {
2017-03-04 11:31:24 +03:00
struct regmap * regmap ;
2017-01-31 10:12:56 +03:00
u8 data_range ;
} ;
# define ADXL345_CHANNEL(reg, axis) { \
. type = IIO_ACCEL , \
. modified = 1 , \
. channel2 = IIO_MOD_ # # axis , \
. address = reg , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
}
static const struct iio_chan_spec adxl345_channels [ ] = {
ADXL345_CHANNEL ( ADXL345_REG_DATAX0 , X ) ,
ADXL345_CHANNEL ( ADXL345_REG_DATAY0 , Y ) ,
ADXL345_CHANNEL ( ADXL345_REG_DATAZ0 , Z ) ,
} ;
static int adxl345_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct adxl345_data * data = iio_priv ( indio_dev ) ;
2017-03-04 11:31:24 +03:00
__le16 regval ;
2017-01-31 10:12:56 +03:00
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
/*
* Data is stored in adjacent registers :
* ADXL345_REG_DATA ( X0 / Y0 / Z0 ) contain the least significant byte
* and ADXL345_REG_DATA ( X0 / Y0 / Z0 ) + 1 the most significant byte
*/
2017-03-04 11:31:24 +03:00
ret = regmap_bulk_read ( data - > regmap , chan - > address , & regval ,
sizeof ( regval ) ) ;
2017-01-31 10:12:56 +03:00
if ( ret < 0 )
return ret ;
2017-03-04 11:31:24 +03:00
* val = sign_extend32 ( le16_to_cpu ( regval ) , 12 ) ;
2017-01-31 10:12:56 +03:00
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
* val = 0 ;
* val2 = adxl345_uscale ;
return IIO_VAL_INT_PLUS_MICRO ;
}
return - EINVAL ;
}
static const struct iio_info adxl345_info = {
. read_raw = adxl345_read_raw ,
} ;
2017-03-04 11:31:25 +03:00
int adxl345_core_probe ( struct device * dev , struct regmap * regmap ,
const char * name )
2017-01-31 10:12:56 +03:00
{
struct adxl345_data * data ;
struct iio_dev * indio_dev ;
2017-03-04 11:31:24 +03:00
u32 regval ;
2017-01-31 10:12:56 +03:00
int ret ;
2017-03-04 11:31:24 +03:00
ret = regmap_read ( regmap , ADXL345_REG_DEVID , & regval ) ;
2017-01-31 10:12:56 +03:00
if ( ret < 0 ) {
2017-03-04 11:31:24 +03:00
dev_err ( dev , " Error reading device ID: %d \n " , ret ) ;
2017-01-31 10:12:56 +03:00
return ret ;
}
2017-03-04 11:31:24 +03:00
if ( regval ! = ADXL345_DEVID ) {
dev_err ( dev , " Invalid device ID: %x, expected %x \n " ,
regval , ADXL345_DEVID ) ;
2017-01-31 10:12:56 +03:00
return - ENODEV ;
}
2017-03-04 11:31:24 +03:00
indio_dev = devm_iio_device_alloc ( dev , sizeof ( * data ) ) ;
2017-01-31 10:12:56 +03:00
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
2017-03-04 11:31:24 +03:00
dev_set_drvdata ( dev , indio_dev ) ;
data - > regmap = regmap ;
2017-01-31 10:12:56 +03:00
/* Enable full-resolution mode */
data - > data_range = ADXL345_DATA_FORMAT_FULL_RES ;
2017-03-04 11:31:24 +03:00
ret = regmap_write ( data - > regmap , ADXL345_REG_DATA_FORMAT ,
data - > data_range ) ;
2017-01-31 10:12:56 +03:00
if ( ret < 0 ) {
2017-03-04 11:31:24 +03:00
dev_err ( dev , " Failed to set data range: %d \n " , ret ) ;
2017-01-31 10:12:56 +03:00
return ret ;
}
2017-03-04 11:31:24 +03:00
indio_dev - > dev . parent = dev ;
2017-03-04 11:31:25 +03:00
indio_dev - > name = name ;
2017-01-31 10:12:56 +03:00
indio_dev - > info = & adxl345_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = adxl345_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( adxl345_channels ) ;
/* Enable measurement mode */
2017-03-04 11:31:24 +03:00
ret = regmap_write ( data - > regmap , ADXL345_REG_POWER_CTL ,
ADXL345_POWER_CTL_MEASURE ) ;
2017-01-31 10:12:56 +03:00
if ( ret < 0 ) {
2017-03-04 11:31:24 +03:00
dev_err ( dev , " Failed to enable measurement mode: %d \n " , ret ) ;
2017-01-31 10:12:56 +03:00
return ret ;
}
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 ) {
2017-03-04 11:31:24 +03:00
dev_err ( dev , " iio_device_register failed: %d \n " , ret ) ;
regmap_write ( data - > regmap , ADXL345_REG_POWER_CTL ,
ADXL345_POWER_CTL_STANDBY ) ;
2017-01-31 10:12:56 +03:00
}
return ret ;
}
2017-03-04 11:31:25 +03:00
EXPORT_SYMBOL_GPL ( adxl345_core_probe ) ;
2017-01-31 10:12:56 +03:00
2017-03-04 11:31:25 +03:00
int adxl345_core_remove ( struct device * dev )
2017-01-31 10:12:56 +03:00
{
2017-03-04 11:31:25 +03:00
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
2017-01-31 10:12:56 +03:00
struct adxl345_data * data = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
2017-03-04 11:31:24 +03:00
return regmap_write ( data - > regmap , ADXL345_REG_POWER_CTL ,
ADXL345_POWER_CTL_STANDBY ) ;
2017-01-31 10:12:56 +03:00
}
2017-03-04 11:31:25 +03:00
EXPORT_SYMBOL_GPL ( adxl345_core_remove ) ;
2017-01-31 10:12:56 +03:00
MODULE_AUTHOR ( " Eva Rachel Retuya <eraretuya@gmail.com> " ) ;
2017-03-04 11:31:25 +03:00
MODULE_DESCRIPTION ( " ADXL345 3-Axis Digital Accelerometer core driver " ) ;
2017-01-31 10:12:56 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;