2019-05-29 16:57:44 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-31 15:12:56 +08:00
/*
2017-03-04 16:31:25 +08:00
* ADXL345 3 - Axis Digital Accelerometer IIO core driver
2017-01-31 15:12:56 +08:00
*
* Copyright ( c ) 2017 Eva Rachel Retuya < eraretuya @ gmail . com >
*
2018-06-26 00:22:40 +09:00
* Datasheet : http : //www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
2017-01-31 15:12:56 +08:00
*/
# include <linux/module.h>
2017-03-04 16:31:24 +08:00
# include <linux/regmap.h>
2017-01-31 15:12:56 +08:00
# include <linux/iio/iio.h>
2018-07-10 00:10:08 +09:00
# include <linux/iio/sysfs.h>
2017-01-31 15:12:56 +08:00
2017-03-04 16:31:25 +08:00
# include "adxl345.h"
2017-01-31 15:12:56 +08:00
# define ADXL345_REG_DEVID 0x00
2018-06-26 00:22:42 +09:00
# define ADXL345_REG_OFSX 0x1e
# define ADXL345_REG_OFSY 0x1f
# define ADXL345_REG_OFSZ 0x20
# define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
2018-07-10 00:10:08 +09:00
# define ADXL345_REG_BW_RATE 0x2C
2017-01-31 15:12:56 +08:00
# 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
2018-06-26 00:22:41 +09:00
# define ADXL345_REG_DATA_AXIS(index) \
( ADXL345_REG_DATAX0 + ( index ) * sizeof ( __le16 ) )
2017-01-31 15:12:56 +08:00
2018-07-10 00:10:08 +09:00
# define ADXL345_BW_RATE GENMASK(3, 0)
# define ADXL345_BASE_RATE_NANO_HZ 97656250LL
# define NHZ_PER_HZ 1000000000LL
2017-01-31 15:12:56 +08:00
# 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 ;
2018-07-13 14:50:44 +03:00
/*
* The Datasheet lists a resolution of Resolution is ~ 49 mg per LSB . That ' s
* ~ 480 mm / s * * 2 per LSB .
*/
static const int adxl375_uscale = 480000 ;
2017-01-31 15:12:56 +08:00
struct adxl345_data {
2017-03-04 16:31:24 +08:00
struct regmap * regmap ;
2017-01-31 15:12:56 +08:00
u8 data_range ;
2018-07-13 14:50:44 +03:00
enum adxl345_device_type type ;
2017-01-31 15:12:56 +08:00
} ;
2018-06-26 00:22:41 +09:00
# define ADXL345_CHANNEL(index, axis) { \
2017-01-31 15:12:56 +08:00
. type = IIO_ACCEL , \
. modified = 1 , \
. channel2 = IIO_MOD_ # # axis , \
2018-06-26 00:22:41 +09:00
. address = index , \
2018-06-26 00:22:42 +09:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) | \
BIT ( IIO_CHAN_INFO_CALIBBIAS ) , \
2018-07-10 00:10:08 +09:00
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) | \
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) , \
2017-01-31 15:12:56 +08:00
}
static const struct iio_chan_spec adxl345_channels [ ] = {
2018-06-26 00:22:41 +09:00
ADXL345_CHANNEL ( 0 , X ) ,
ADXL345_CHANNEL ( 1 , Y ) ,
ADXL345_CHANNEL ( 2 , Z ) ,
2017-01-31 15:12:56 +08:00
} ;
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 ) ;
2018-06-26 00:22:41 +09:00
__le16 accel ;
2018-07-10 00:10:08 +09:00
long long samp_freq_nhz ;
2018-06-26 00:22:42 +09:00
unsigned int regval ;
2017-01-31 15:12:56 +08: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
*/
2018-06-26 00:22:41 +09:00
ret = regmap_bulk_read ( data - > regmap ,
ADXL345_REG_DATA_AXIS ( chan - > address ) ,
& accel , sizeof ( accel ) ) ;
2017-01-31 15:12:56 +08:00
if ( ret < 0 )
return ret ;
2018-06-26 00:22:41 +09:00
* val = sign_extend32 ( le16_to_cpu ( accel ) , 12 ) ;
2017-01-31 15:12:56 +08:00
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
* val = 0 ;
2018-07-13 14:50:44 +03:00
switch ( data - > type ) {
case ADXL345 :
* val2 = adxl345_uscale ;
break ;
case ADXL375 :
* val2 = adxl375_uscale ;
break ;
}
2017-01-31 15:12:56 +08:00
return IIO_VAL_INT_PLUS_MICRO ;
2018-06-26 00:22:42 +09:00
case IIO_CHAN_INFO_CALIBBIAS :
ret = regmap_read ( data - > regmap ,
ADXL345_REG_OFS_AXIS ( chan - > address ) , & regval ) ;
if ( ret < 0 )
return ret ;
/*
* 8 - bit resolution at + / - 2 g , that is 4 x accel data scale
* factor
*/
* val = sign_extend32 ( regval , 7 ) * 4 ;
return IIO_VAL_INT ;
2018-07-10 00:10:08 +09:00
case IIO_CHAN_INFO_SAMP_FREQ :
ret = regmap_read ( data - > regmap , ADXL345_REG_BW_RATE , & regval ) ;
if ( ret < 0 )
return ret ;
samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ < <
( regval & ADXL345_BW_RATE ) ;
* val = div_s64_rem ( samp_freq_nhz , NHZ_PER_HZ , val2 ) ;
return IIO_VAL_INT_PLUS_NANO ;
2018-06-26 00:22:42 +09:00
}
return - EINVAL ;
}
static int adxl345_write_raw ( struct iio_dev * indio_dev ,
2019-02-09 10:25:42 -02:00
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
2018-06-26 00:22:42 +09:00
{
struct adxl345_data * data = iio_priv ( indio_dev ) ;
2018-07-10 00:10:08 +09:00
s64 n ;
2018-06-26 00:22:42 +09:00
switch ( mask ) {
case IIO_CHAN_INFO_CALIBBIAS :
/*
* 8 - bit resolution at + / - 2 g , that is 4 x accel data scale
* factor
*/
return regmap_write ( data - > regmap ,
ADXL345_REG_OFS_AXIS ( chan - > address ) ,
val / 4 ) ;
2018-07-10 00:10:08 +09:00
case IIO_CHAN_INFO_SAMP_FREQ :
n = div_s64 ( val * NHZ_PER_HZ + val2 , ADXL345_BASE_RATE_NANO_HZ ) ;
return regmap_update_bits ( data - > regmap , ADXL345_REG_BW_RATE ,
ADXL345_BW_RATE ,
clamp_val ( ilog2 ( n ) , 0 ,
ADXL345_BW_RATE ) ) ;
2017-01-31 15:12:56 +08:00
}
return - EINVAL ;
}
2018-07-10 00:10:08 +09:00
static int adxl345_write_raw_get_fmt ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
long mask )
{
switch ( mask ) {
case IIO_CHAN_INFO_CALIBBIAS :
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SAMP_FREQ :
return IIO_VAL_INT_PLUS_NANO ;
default :
return - EINVAL ;
}
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL (
" 0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200 "
) ;
static struct attribute * adxl345_attrs [ ] = {
& iio_const_attr_sampling_frequency_available . dev_attr . attr ,
NULL ,
} ;
static const struct attribute_group adxl345_attrs_group = {
. attrs = adxl345_attrs ,
} ;
2017-01-31 15:12:56 +08:00
static const struct iio_info adxl345_info = {
2018-07-10 00:10:08 +09:00
. attrs = & adxl345_attrs_group ,
2017-01-31 15:12:56 +08:00
. read_raw = adxl345_read_raw ,
2018-06-26 00:22:42 +09:00
. write_raw = adxl345_write_raw ,
2018-07-10 00:10:08 +09:00
. write_raw_get_fmt = adxl345_write_raw_get_fmt ,
2017-01-31 15:12:56 +08:00
} ;
2017-03-04 16:31:25 +08:00
int adxl345_core_probe ( struct device * dev , struct regmap * regmap ,
2018-07-13 14:50:44 +03:00
enum adxl345_device_type type , const char * name )
2017-01-31 15:12:56 +08:00
{
struct adxl345_data * data ;
struct iio_dev * indio_dev ;
2017-03-04 16:31:24 +08:00
u32 regval ;
2017-01-31 15:12:56 +08:00
int ret ;
2017-03-04 16:31:24 +08:00
ret = regmap_read ( regmap , ADXL345_REG_DEVID , & regval ) ;
2017-01-31 15:12:56 +08:00
if ( ret < 0 ) {
2017-03-04 16:31:24 +08:00
dev_err ( dev , " Error reading device ID: %d \n " , ret ) ;
2017-01-31 15:12:56 +08:00
return ret ;
}
2017-03-04 16:31:24 +08:00
if ( regval ! = ADXL345_DEVID ) {
dev_err ( dev , " Invalid device ID: %x, expected %x \n " ,
regval , ADXL345_DEVID ) ;
2017-01-31 15:12:56 +08:00
return - ENODEV ;
}
2017-03-04 16:31:24 +08:00
indio_dev = devm_iio_device_alloc ( dev , sizeof ( * data ) ) ;
2017-01-31 15:12:56 +08:00
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
2017-03-04 16:31:24 +08:00
dev_set_drvdata ( dev , indio_dev ) ;
data - > regmap = regmap ;
2018-07-13 14:50:44 +03:00
data - > type = type ;
2017-01-31 15:12:56 +08:00
/* Enable full-resolution mode */
data - > data_range = ADXL345_DATA_FORMAT_FULL_RES ;
2017-03-04 16:31:24 +08:00
ret = regmap_write ( data - > regmap , ADXL345_REG_DATA_FORMAT ,
data - > data_range ) ;
2017-01-31 15:12:56 +08:00
if ( ret < 0 ) {
2017-03-04 16:31:24 +08:00
dev_err ( dev , " Failed to set data range: %d \n " , ret ) ;
2017-01-31 15:12:56 +08:00
return ret ;
}
2017-03-04 16:31:24 +08:00
indio_dev - > dev . parent = dev ;
2017-03-04 16:31:25 +08:00
indio_dev - > name = name ;
2017-01-31 15:12:56 +08: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 16:31:24 +08:00
ret = regmap_write ( data - > regmap , ADXL345_REG_POWER_CTL ,
ADXL345_POWER_CTL_MEASURE ) ;
2017-01-31 15:12:56 +08:00
if ( ret < 0 ) {
2017-03-04 16:31:24 +08:00
dev_err ( dev , " Failed to enable measurement mode: %d \n " , ret ) ;
2017-01-31 15:12:56 +08:00
return ret ;
}
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 ) {
2017-03-04 16:31:24 +08: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 15:12:56 +08:00
}
return ret ;
}
2017-03-04 16:31:25 +08:00
EXPORT_SYMBOL_GPL ( adxl345_core_probe ) ;
2017-01-31 15:12:56 +08:00
2017-03-04 16:31:25 +08:00
int adxl345_core_remove ( struct device * dev )
2017-01-31 15:12:56 +08:00
{
2017-03-04 16:31:25 +08:00
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
2017-01-31 15:12:56 +08:00
struct adxl345_data * data = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
2017-03-04 16:31:24 +08:00
return regmap_write ( data - > regmap , ADXL345_REG_POWER_CTL ,
ADXL345_POWER_CTL_STANDBY ) ;
2017-01-31 15:12:56 +08:00
}
2017-03-04 16:31:25 +08:00
EXPORT_SYMBOL_GPL ( adxl345_core_remove ) ;
2017-01-31 15:12:56 +08:00
MODULE_AUTHOR ( " Eva Rachel Retuya <eraretuya@gmail.com> " ) ;
2017-03-04 16:31:25 +08:00
MODULE_DESCRIPTION ( " ADXL345 3-Axis Digital Accelerometer core driver " ) ;
2017-01-31 15:12:56 +08:00
MODULE_LICENSE ( " GPL v2 " ) ;