2019-05-29 16:57:44 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2021-03-09 15:43:14 -08:00
/*
2016-05-03 15:05:45 +03:00
* Freescale MMA7660FC 3 - Axis Accelerometer
*
* Copyright ( c ) 2016 , Intel Corporation .
*
* IIO driver for Freescale MMA7660FC ; 7 - bit I2C address : 0x4c .
*/
# include <linux/i2c.h>
2022-06-16 17:24:51 +03:00
# include <linux/mod_devicetable.h>
2016-05-03 15:05:45 +03:00
# include <linux/module.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# define MMA7660_DRIVER_NAME "mma7660"
# define MMA7660_REG_XOUT 0x00
# define MMA7660_REG_YOUT 0x01
# define MMA7660_REG_ZOUT 0x02
# define MMA7660_REG_OUT_BIT_ALERT BIT(6)
# define MMA7660_REG_MODE 0x07
# define MMA7660_REG_MODE_BIT_MODE BIT(0)
# define MMA7660_REG_MODE_BIT_TON BIT(2)
# define MMA7660_I2C_READ_RETRIES 5
/*
* The accelerometer has one measurement range :
*
* - 1.5 g - + 1.5 g ( 6 - bit , signed )
*
* scale = ( 1.5 + 1.5 ) * 9.81 / ( 2 ^ 6 - 1 ) = 0.467142857
*/
# define MMA7660_SCALE_AVAIL "0.467142857"
2016-09-24 12:01:54 +00:00
static const int mma7660_nscale = 467142857 ;
2016-05-03 15:05:45 +03:00
# define MMA7660_CHANNEL(reg, axis) { \
. type = IIO_ACCEL , \
. address = reg , \
. modified = 1 , \
. channel2 = IIO_MOD_ # # axis , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
}
static const struct iio_chan_spec mma7660_channels [ ] = {
MMA7660_CHANNEL ( MMA7660_REG_XOUT , X ) ,
MMA7660_CHANNEL ( MMA7660_REG_YOUT , Y ) ,
MMA7660_CHANNEL ( MMA7660_REG_ZOUT , Z ) ,
} ;
enum mma7660_mode {
MMA7660_MODE_STANDBY ,
MMA7660_MODE_ACTIVE
} ;
struct mma7660_data {
struct i2c_client * client ;
struct mutex lock ;
enum mma7660_mode mode ;
} ;
static IIO_CONST_ATTR ( in_accel_scale_available , MMA7660_SCALE_AVAIL ) ;
static struct attribute * mma7660_attributes [ ] = {
& iio_const_attr_in_accel_scale_available . dev_attr . attr ,
NULL ,
} ;
static const struct attribute_group mma7660_attribute_group = {
. attrs = mma7660_attributes
} ;
static int mma7660_set_mode ( struct mma7660_data * data ,
enum mma7660_mode mode )
{
int ret ;
struct i2c_client * client = data - > client ;
if ( mode = = data - > mode )
return 0 ;
ret = i2c_smbus_read_byte_data ( client , MMA7660_REG_MODE ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " failed to read sensor mode \n " ) ;
return ret ;
}
if ( mode = = MMA7660_MODE_ACTIVE ) {
ret & = ~ MMA7660_REG_MODE_BIT_TON ;
ret | = MMA7660_REG_MODE_BIT_MODE ;
} else {
ret & = ~ MMA7660_REG_MODE_BIT_TON ;
ret & = ~ MMA7660_REG_MODE_BIT_MODE ;
}
ret = i2c_smbus_write_byte_data ( client , MMA7660_REG_MODE , ret ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " failed to change sensor mode \n " ) ;
return ret ;
}
data - > mode = mode ;
return ret ;
}
static int mma7660_read_accel ( struct mma7660_data * data , u8 address )
{
int ret , retries = MMA7660_I2C_READ_RETRIES ;
struct i2c_client * client = data - > client ;
/*
* Read data . If the Alert bit is set , the register was read at
* the same time as the device was attempting to update the content .
* The solution is to read the register again . Do this only
* MMA7660_I2C_READ_RETRIES times to avoid spending too much time
* in the kernel .
*/
do {
ret = i2c_smbus_read_byte_data ( client , address ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " register read failed \n " ) ;
return ret ;
}
} while ( retries - - > 0 & & ret & MMA7660_REG_OUT_BIT_ALERT ) ;
if ( ret & MMA7660_REG_OUT_BIT_ALERT ) {
dev_err ( & client - > dev , " all register read retries failed \n " ) ;
return - ETIMEDOUT ;
}
return ret ;
}
static int mma7660_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct mma7660_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
mutex_lock ( & data - > lock ) ;
ret = mma7660_read_accel ( data , chan - > address ) ;
mutex_unlock ( & data - > lock ) ;
if ( ret < 0 )
return ret ;
* val = sign_extend32 ( ret , 5 ) ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
* val = 0 ;
* val2 = mma7660_nscale ;
return IIO_VAL_INT_PLUS_NANO ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
static const struct iio_info mma7660_info = {
. read_raw = mma7660_read_raw ,
. attrs = & mma7660_attribute_group ,
} ;
2022-10-23 15:22:53 +02:00
static int mma7660_probe ( struct i2c_client * client )
2016-05-03 15:05:45 +03:00
{
int ret ;
struct iio_dev * indio_dev ;
struct mma7660_data * data ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev ) {
dev_err ( & client - > dev , " iio allocation failed! \n " ) ;
return - ENOMEM ;
}
data = iio_priv ( indio_dev ) ;
data - > client = client ;
i2c_set_clientdata ( client , indio_dev ) ;
mutex_init ( & data - > lock ) ;
data - > mode = MMA7660_MODE_STANDBY ;
indio_dev - > info = & mma7660_info ;
indio_dev - > name = MMA7660_DRIVER_NAME ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = mma7660_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( mma7660_channels ) ;
ret = mma7660_set_mode ( data , MMA7660_MODE_ACTIVE ) ;
if ( ret < 0 )
return ret ;
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " device_register failed \n " ) ;
mma7660_set_mode ( data , MMA7660_MODE_STANDBY ) ;
}
return ret ;
}
2022-08-15 10:02:30 +02:00
static void mma7660_remove ( struct i2c_client * client )
2016-05-03 15:05:45 +03:00
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
2021-10-25 21:50:07 +02:00
int ret ;
2016-05-03 15:05:45 +03:00
iio_device_unregister ( indio_dev ) ;
2021-10-25 21:50:07 +02:00
ret = mma7660_set_mode ( iio_priv ( indio_dev ) , MMA7660_MODE_STANDBY ) ;
if ( ret )
dev_warn ( & client - > dev , " Failed to put device in stand-by mode (%pe), ignoring \n " ,
ERR_PTR ( ret ) ) ;
2016-05-03 15:05:45 +03:00
}
static int mma7660_suspend ( struct device * dev )
{
struct mma7660_data * data ;
data = iio_priv ( i2c_get_clientdata ( to_i2c_client ( dev ) ) ) ;
return mma7660_set_mode ( data , MMA7660_MODE_STANDBY ) ;
}
static int mma7660_resume ( struct device * dev )
{
struct mma7660_data * data ;
data = iio_priv ( i2c_get_clientdata ( to_i2c_client ( dev ) ) ) ;
return mma7660_set_mode ( data , MMA7660_MODE_ACTIVE ) ;
}
2022-01-30 19:31:03 +00:00
static DEFINE_SIMPLE_DEV_PM_OPS ( mma7660_pm_ops , mma7660_suspend ,
mma7660_resume ) ;
2016-05-03 15:05:45 +03:00
static const struct i2c_device_id mma7660_i2c_id [ ] = {
{ " mma7660 " , 0 } ,
{ }
} ;
2016-09-11 18:29:26 +02:00
MODULE_DEVICE_TABLE ( i2c , mma7660_i2c_id ) ;
2016-05-03 15:05:45 +03:00
2017-03-15 01:45:04 -03:00
static const struct of_device_id mma7660_of_match [ ] = {
{ . compatible = " fsl,mma7660 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , mma7660_of_match ) ;
2022-06-16 17:24:51 +03:00
static const struct acpi_device_id mma7660_acpi_id [ ] = {
2016-05-03 15:05:45 +03:00
{ " MMA7660 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , mma7660_acpi_id ) ;
static struct i2c_driver mma7660_driver = {
. driver = {
. name = " mma7660 " ,
2022-01-30 19:31:03 +00:00
. pm = pm_sleep_ptr ( & mma7660_pm_ops ) ,
2017-03-15 01:45:04 -03:00
. of_match_table = mma7660_of_match ,
2022-06-16 17:24:51 +03:00
. acpi_match_table = mma7660_acpi_id ,
2016-05-03 15:05:45 +03:00
} ,
2023-05-15 22:50:48 +02:00
. probe = mma7660_probe ,
2016-05-03 15:05:45 +03:00
. remove = mma7660_remove ,
. id_table = mma7660_i2c_id ,
} ;
module_i2c_driver ( mma7660_driver ) ;
MODULE_AUTHOR ( " Constantin Musca <constantin.musca@intel.com> " ) ;
MODULE_DESCRIPTION ( " Freescale MMA7660FC 3-Axis Accelerometer driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;