2015-10-31 15:49:16 +03:00
/*
* IIO accel core driver for Freescale MMA7455L 3 - axis 10 - bit accelerometer
* Copyright 2015 Joachim Eastwood < manabian @ gmail . com >
*
* 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 .
*
* UNSUPPORTED hardware features :
* - 8 - bit mode with different scales
* - INT1 / INT2 interrupts
* - Offset calibration
* - Events
*/
# include <linux/delay.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/buffer.h>
# include <linux/iio/trigger.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include "mma7455.h"
# define MMA7455_REG_XOUTL 0x00
# define MMA7455_REG_XOUTH 0x01
# define MMA7455_REG_YOUTL 0x02
# define MMA7455_REG_YOUTH 0x03
# define MMA7455_REG_ZOUTL 0x04
# define MMA7455_REG_ZOUTH 0x05
# define MMA7455_REG_STATUS 0x09
# define MMA7455_STATUS_DRDY BIT(0)
# define MMA7455_REG_WHOAMI 0x0f
# define MMA7455_WHOAMI_ID 0x55
# define MMA7455_REG_MCTL 0x16
# define MMA7455_MCTL_MODE_STANDBY 0x00
# define MMA7455_MCTL_MODE_MEASURE 0x01
# define MMA7455_REG_CTL1 0x18
# define MMA7455_CTL1_DFBW_MASK BIT(7)
# define MMA7455_CTL1_DFBW_125HZ BIT(7)
# define MMA7455_CTL1_DFBW_62_5HZ 0
# define MMA7455_REG_TW 0x1e
/*
* When MMA7455 is used in 10 - bit it has a fullscale of - 8 g
* corresponding to raw value - 512. The userspace interface
* uses m / s ^ 2 and we declare micro units .
* So scale factor is given by :
* g * 8 * 1e6 / 512 = 153228.90625 , with g = 9.80665
*/
# define MMA7455_10BIT_SCALE 153229
struct mma7455_data {
struct regmap * regmap ;
} ;
static int mma7455_drdy ( struct mma7455_data * mma7455 )
{
2016-04-06 08:18:44 +03:00
struct device * dev = regmap_get_device ( mma7455 - > regmap ) ;
2015-10-31 15:49:16 +03:00
unsigned int reg ;
int tries = 3 ;
int ret ;
while ( tries - - > 0 ) {
ret = regmap_read ( mma7455 - > regmap , MMA7455_REG_STATUS , & reg ) ;
if ( ret )
return ret ;
if ( reg & MMA7455_STATUS_DRDY )
return 0 ;
msleep ( 20 ) ;
}
2016-04-06 08:18:44 +03:00
dev_warn ( dev , " data not ready \n " ) ;
2015-10-31 15:49:16 +03:00
return - EIO ;
}
static irqreturn_t mma7455_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct mma7455_data * mma7455 = iio_priv ( indio_dev ) ;
u8 buf [ 16 ] ; /* 3 x 16-bit channels + padding + ts */
int ret ;
ret = mma7455_drdy ( mma7455 ) ;
if ( ret )
goto done ;
ret = regmap_bulk_read ( mma7455 - > regmap , MMA7455_REG_XOUTL , buf ,
sizeof ( __le16 ) * 3 ) ;
if ( ret )
goto done ;
2016-03-09 21:05:49 +03:00
iio_push_to_buffers_with_timestamp ( indio_dev , buf ,
iio_get_time_ns ( indio_dev ) ) ;
2015-10-31 15:49:16 +03:00
done :
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
static int mma7455_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct mma7455_data * mma7455 = iio_priv ( indio_dev ) ;
unsigned int reg ;
__le16 data ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
if ( iio_buffer_enabled ( indio_dev ) )
return - EBUSY ;
ret = mma7455_drdy ( mma7455 ) ;
if ( ret )
return ret ;
ret = regmap_bulk_read ( mma7455 - > regmap , chan - > address , & data ,
sizeof ( data ) ) ;
if ( ret )
return ret ;
* val = sign_extend32 ( le16_to_cpu ( data ) , 9 ) ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
* val = 0 ;
* val2 = MMA7455_10BIT_SCALE ;
return IIO_VAL_INT_PLUS_MICRO ;
case IIO_CHAN_INFO_SAMP_FREQ :
ret = regmap_read ( mma7455 - > regmap , MMA7455_REG_CTL1 , & reg ) ;
if ( ret )
return ret ;
if ( reg & MMA7455_CTL1_DFBW_MASK )
* val = 250 ;
else
* val = 125 ;
return IIO_VAL_INT ;
}
return - EINVAL ;
}
static int mma7455_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
{
struct mma7455_data * mma7455 = iio_priv ( indio_dev ) ;
int i ;
switch ( mask ) {
case IIO_CHAN_INFO_SAMP_FREQ :
if ( val = = 250 & & val2 = = 0 )
i = MMA7455_CTL1_DFBW_125HZ ;
else if ( val = = 125 & & val2 = = 0 )
i = MMA7455_CTL1_DFBW_62_5HZ ;
else
return - EINVAL ;
return regmap_update_bits ( mma7455 - > regmap , MMA7455_REG_CTL1 ,
MMA7455_CTL1_DFBW_MASK , i ) ;
case IIO_CHAN_INFO_SCALE :
/* In 10-bit mode there is only one scale available */
if ( val = = 0 & & val2 = = MMA7455_10BIT_SCALE )
return 0 ;
break ;
}
return - EINVAL ;
}
static IIO_CONST_ATTR ( sampling_frequency_available , " 125 250 " ) ;
static struct attribute * mma7455_attributes [ ] = {
& iio_const_attr_sampling_frequency_available . dev_attr . attr ,
NULL
} ;
static const struct attribute_group mma7455_group = {
. attrs = mma7455_attributes ,
} ;
static const struct iio_info mma7455_info = {
. attrs = & mma7455_group ,
. read_raw = mma7455_read_raw ,
. write_raw = mma7455_write_raw ,
. driver_module = THIS_MODULE ,
} ;
# define MMA7455_CHANNEL(axis, idx) { \
. type = IIO_ACCEL , \
. modified = 1 , \
. address = MMA7455_REG_ # # axis # # OUTL , \
. channel2 = IIO_MOD_ # # axis , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SAMP_FREQ ) | \
BIT ( IIO_CHAN_INFO_SCALE ) , \
. scan_index = idx , \
. scan_type = { \
. sign = ' s ' , \
. realbits = 10 , \
. storagebits = 16 , \
. endianness = IIO_LE , \
} , \
}
static const struct iio_chan_spec mma7455_channels [ ] = {
MMA7455_CHANNEL ( X , 0 ) ,
MMA7455_CHANNEL ( Y , 1 ) ,
MMA7455_CHANNEL ( Z , 2 ) ,
IIO_CHAN_SOFT_TIMESTAMP ( 3 ) ,
} ;
static const unsigned long mma7455_scan_masks [ ] = { 0x7 , 0 } ;
const struct regmap_config mma7455_core_regmap = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = MMA7455_REG_TW ,
} ;
EXPORT_SYMBOL_GPL ( mma7455_core_regmap ) ;
int mma7455_core_probe ( struct device * dev , struct regmap * regmap ,
const char * name )
{
struct mma7455_data * mma7455 ;
struct iio_dev * indio_dev ;
unsigned int reg ;
int ret ;
ret = regmap_read ( regmap , MMA7455_REG_WHOAMI , & reg ) ;
if ( ret ) {
dev_err ( dev , " unable to read reg \n " ) ;
return ret ;
}
if ( reg ! = MMA7455_WHOAMI_ID ) {
dev_err ( dev , " device id mismatch \n " ) ;
return - ENODEV ;
}
indio_dev = devm_iio_device_alloc ( dev , sizeof ( * mma7455 ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
dev_set_drvdata ( dev , indio_dev ) ;
mma7455 = iio_priv ( indio_dev ) ;
mma7455 - > regmap = regmap ;
indio_dev - > info = & mma7455_info ;
indio_dev - > name = name ;
indio_dev - > dev . parent = dev ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = mma7455_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( mma7455_channels ) ;
indio_dev - > available_scan_masks = mma7455_scan_masks ;
regmap_write ( mma7455 - > regmap , MMA7455_REG_MCTL ,
MMA7455_MCTL_MODE_MEASURE ) ;
ret = iio_triggered_buffer_setup ( indio_dev , NULL ,
mma7455_trigger_handler , NULL ) ;
if ( ret ) {
dev_err ( dev , " unable to setup triggered buffer \n " ) ;
return ret ;
}
ret = iio_device_register ( indio_dev ) ;
if ( ret ) {
dev_err ( dev , " unable to register device \n " ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( mma7455_core_probe ) ;
int mma7455_core_remove ( struct device * dev )
{
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
struct mma7455_data * mma7455 = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
regmap_write ( mma7455 - > regmap , MMA7455_REG_MCTL ,
MMA7455_MCTL_MODE_STANDBY ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( mma7455_core_remove ) ;
MODULE_AUTHOR ( " Joachim Eastwood <manabian@gmail.com> " ) ;
MODULE_DESCRIPTION ( " Freescale MMA7455L core accelerometer driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;