2019-06-01 10:08:37 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2021-03-09 15:43:14 -08:00
/*
2016-10-08 20:15:27 +02:00
* IIO driver for the MiraMEMS DA280 3 - axis accelerometer and
* IIO driver for the MiraMEMS DA226 2 - axis accelerometer
*
* Copyright ( c ) 2016 Hans de Goede < hdegoede @ redhat . com >
*/
# include <linux/module.h>
# include <linux/i2c.h>
2017-11-23 21:34:57 +00:00
# include <linux/acpi.h>
2016-10-08 20:15:27 +02:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/byteorder/generic.h>
# define DA280_REG_CHIP_ID 0x01
# define DA280_REG_ACC_X_LSB 0x02
# define DA280_REG_ACC_Y_LSB 0x04
# define DA280_REG_ACC_Z_LSB 0x06
# define DA280_REG_MODE_BW 0x11
# define DA280_CHIP_ID 0x13
# define DA280_MODE_ENABLE 0x1e
# define DA280_MODE_DISABLE 0x9e
2017-11-23 21:34:57 +00:00
enum da280_chipset { da226 , da280 } ;
2016-10-08 20:15:27 +02:00
/*
* a value of + or - 4096 corresponds to + or - 1 G
* scale = 9.81 / 4096 = 0.002395019
*/
static const int da280_nscale = 2395019 ;
# define DA280_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 da280_channels [ ] = {
DA280_CHANNEL ( DA280_REG_ACC_X_LSB , X ) ,
DA280_CHANNEL ( DA280_REG_ACC_Y_LSB , Y ) ,
DA280_CHANNEL ( DA280_REG_ACC_Z_LSB , Z ) ,
} ;
struct da280_data {
struct i2c_client * client ;
} ;
static int da280_enable ( struct i2c_client * client , bool enable )
{
u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE ;
return i2c_smbus_write_byte_data ( client , DA280_REG_MODE_BW , data ) ;
}
static int da280_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct da280_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
ret = i2c_smbus_read_word_data ( data - > client , chan - > address ) ;
if ( ret < 0 )
return ret ;
/*
* Values are 14 bits , stored as 16 bits with the 2
* least significant bits always 0.
*/
* val = ( short ) ret > > 2 ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
* val = 0 ;
* val2 = da280_nscale ;
return IIO_VAL_INT_PLUS_NANO ;
default :
return - EINVAL ;
}
}
static const struct iio_info da280_info = {
. read_raw = da280_read_raw ,
} ;
2017-11-23 21:34:57 +00:00
static enum da280_chipset da280_match_acpi_device ( struct device * dev )
{
const struct acpi_device_id * id ;
id = acpi_match_device ( dev - > driver - > acpi_match_table , dev ) ;
if ( ! id )
return - EINVAL ;
return ( enum da280_chipset ) id - > driver_data ;
}
2021-06-28 17:17:09 +03:00
static void da280_disable ( void * client )
{
da280_enable ( client , false ) ;
}
2016-10-08 20:15:27 +02:00
static int da280_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
int ret ;
struct iio_dev * indio_dev ;
struct da280_data * data ;
2017-11-23 21:34:57 +00:00
enum da280_chipset chip ;
2016-10-08 20:15:27 +02:00
ret = i2c_smbus_read_byte_data ( client , DA280_REG_CHIP_ID ) ;
if ( ret ! = DA280_CHIP_ID )
return ( ret < 0 ) ? ret : - ENODEV ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
data - > client = client ;
indio_dev - > info = & da280_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = da280_channels ;
2017-11-23 21:34:57 +00:00
if ( ACPI_HANDLE ( & client - > dev ) ) {
chip = da280_match_acpi_device ( & client - > dev ) ;
} else {
chip = id - > driver_data ;
}
if ( chip = = da226 ) {
2016-10-08 20:15:27 +02:00
indio_dev - > name = " da226 " ;
indio_dev - > num_channels = 2 ;
} else {
indio_dev - > name = " da280 " ;
indio_dev - > num_channels = 3 ;
}
ret = da280_enable ( client , true ) ;
if ( ret < 0 )
return ret ;
2021-06-28 17:17:09 +03:00
ret = devm_add_action_or_reset ( & client - > dev , da280_disable , client ) ;
if ( ret )
return ret ;
2016-10-08 20:15:27 +02:00
2021-06-28 17:17:09 +03:00
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
2016-10-08 20:15:27 +02:00
}
# ifdef CONFIG_PM_SLEEP
static int da280_suspend ( struct device * dev )
{
return da280_enable ( to_i2c_client ( dev ) , false ) ;
}
static int da280_resume ( struct device * dev )
{
return da280_enable ( to_i2c_client ( dev ) , true ) ;
}
# endif
static SIMPLE_DEV_PM_OPS ( da280_pm_ops , da280_suspend , da280_resume ) ;
2017-11-23 21:34:57 +00:00
static const struct acpi_device_id da280_acpi_match [ ] = {
{ " MIRAACC " , da280 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , da280_acpi_match ) ;
2016-10-08 20:15:27 +02:00
static const struct i2c_device_id da280_i2c_id [ ] = {
{ " da226 " , da226 } ,
{ " da280 " , da280 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , da280_i2c_id ) ;
static struct i2c_driver da280_driver = {
. driver = {
. name = " da280 " ,
2017-11-23 21:34:57 +00:00
. acpi_match_table = ACPI_PTR ( da280_acpi_match ) ,
2016-10-08 20:15:27 +02:00
. pm = & da280_pm_ops ,
} ,
. probe = da280_probe ,
. id_table = da280_i2c_id ,
} ;
module_i2c_driver ( da280_driver ) ;
MODULE_AUTHOR ( " Hans de Goede <hdegoede@redhat.com> " ) ;
MODULE_DESCRIPTION ( " MiraMEMS DA280 3-Axis Accelerometer driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;