2014-11-24 11:43:15 +02:00
/*
* Freescale MMA9551L Intelligent Motion - Sensing Platform driver
* Copyright ( c ) 2014 , Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/acpi.h>
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/events.h>
2015-01-11 21:10:14 +02:00
# include <linux/pm_runtime.h>
2015-01-11 21:10:15 +02:00
# include "mma9551_core.h"
2014-11-24 11:43:15 +02:00
# define MMA9551_DRV_NAME "mma9551"
# define MMA9551_IRQ_NAME "mma9551_event"
# define MMA9551_GPIO_NAME "mma9551_int"
# define MMA9551_GPIO_COUNT 4
/* Tilt application (inclination in IIO terms). */
# define MMA9551_TILT_XZ_ANG_REG 0x00
# define MMA9551_TILT_YZ_ANG_REG 0x01
# define MMA9551_TILT_XY_ANG_REG 0x02
# define MMA9551_TILT_ANGFLG BIT(7)
# define MMA9551_TILT_QUAD_REG 0x03
# define MMA9551_TILT_XY_QUAD_SHIFT 0
# define MMA9551_TILT_YZ_QUAD_SHIFT 2
# define MMA9551_TILT_XZ_QUAD_SHIFT 4
# define MMA9551_TILT_CFG_REG 0x01
# define MMA9551_TILT_ANG_THRESH_MASK GENMASK(3, 0)
2015-01-11 21:10:15 +02:00
# define MMA9551_DEFAULT_SAMPLE_RATE 122 /* Hz */
2014-11-24 11:43:15 +02:00
/* Tilt events are mapped to the first three GPIO pins. */
enum mma9551_tilt_axis {
mma9551_x = 0 ,
mma9551_y ,
mma9551_z ,
} ;
struct mma9551_data {
struct i2c_client * client ;
struct mutex mutex ;
int event_enabled [ 3 ] ;
int irqs [ MMA9551_GPIO_COUNT ] ;
} ;
static int mma9551_read_incli_chan ( struct i2c_client * client ,
const struct iio_chan_spec * chan ,
int * val )
{
u8 quad_shift , angle , quadrant ;
u16 reg_addr ;
int ret ;
switch ( chan - > channel2 ) {
case IIO_MOD_X :
reg_addr = MMA9551_TILT_YZ_ANG_REG ;
quad_shift = MMA9551_TILT_YZ_QUAD_SHIFT ;
break ;
case IIO_MOD_Y :
reg_addr = MMA9551_TILT_XZ_ANG_REG ;
quad_shift = MMA9551_TILT_XZ_QUAD_SHIFT ;
break ;
case IIO_MOD_Z :
reg_addr = MMA9551_TILT_XY_ANG_REG ;
quad_shift = MMA9551_TILT_XY_QUAD_SHIFT ;
break ;
default :
return - EINVAL ;
}
2015-01-11 21:10:14 +02:00
ret = mma9551_set_power_state ( client , true ) ;
if ( ret < 0 )
return ret ;
2014-11-24 11:43:15 +02:00
ret = mma9551_read_status_byte ( client , MMA9551_APPID_TILT ,
reg_addr , & angle ) ;
if ( ret < 0 )
2015-01-11 21:10:14 +02:00
goto out_poweroff ;
2014-11-24 11:43:15 +02:00
ret = mma9551_read_status_byte ( client , MMA9551_APPID_TILT ,
MMA9551_TILT_QUAD_REG , & quadrant ) ;
if ( ret < 0 )
2015-01-11 21:10:14 +02:00
goto out_poweroff ;
2014-11-24 11:43:15 +02:00
angle & = ~ MMA9551_TILT_ANGFLG ;
quadrant = ( quadrant > > quad_shift ) & 0x03 ;
if ( quadrant = = 1 | | quadrant = = 3 )
* val = 90 * ( quadrant + 1 ) - angle ;
else
* val = angle + 90 * quadrant ;
2015-01-11 21:10:14 +02:00
ret = IIO_VAL_INT ;
out_poweroff :
mma9551_set_power_state ( client , false ) ;
return ret ;
2014-11-24 11:43:15 +02:00
}
static int mma9551_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct mma9551_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_PROCESSED :
switch ( chan - > type ) {
case IIO_INCLI :
mutex_lock ( & data - > mutex ) ;
ret = mma9551_read_incli_chan ( data - > client , chan , val ) ;
mutex_unlock ( & data - > mutex ) ;
return ret ;
default :
return - EINVAL ;
}
case IIO_CHAN_INFO_RAW :
switch ( chan - > type ) {
case IIO_ACCEL :
mutex_lock ( & data - > mutex ) ;
ret = mma9551_read_accel_chan ( data - > client ,
chan , val , val2 ) ;
mutex_unlock ( & data - > mutex ) ;
return ret ;
default :
return - EINVAL ;
}
case IIO_CHAN_INFO_SCALE :
switch ( chan - > type ) {
case IIO_ACCEL :
2015-01-11 21:10:15 +02:00
return mma9551_read_accel_scale ( val , val2 ) ;
2014-11-24 11:43:15 +02:00
default :
return - EINVAL ;
}
default :
return - EINVAL ;
}
}
static int mma9551_read_event_config ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir )
{
struct mma9551_data * data = iio_priv ( indio_dev ) ;
switch ( chan - > type ) {
case IIO_INCLI :
/* IIO counts axes from 1, because IIO_NO_MOD is 0. */
return data - > event_enabled [ chan - > channel2 - 1 ] ;
default :
return - EINVAL ;
}
}
static int mma9551_config_incli_event ( struct iio_dev * indio_dev ,
enum iio_modifier axis ,
int state )
{
struct mma9551_data * data = iio_priv ( indio_dev ) ;
enum mma9551_tilt_axis mma_axis ;
int ret ;
/* IIO counts axes from 1, because IIO_NO_MOD is 0. */
mma_axis = axis - 1 ;
if ( data - > event_enabled [ mma_axis ] = = state )
return 0 ;
if ( state = = 0 ) {
2014-12-15 17:16:51 +02:00
ret = mma9551_gpio_config ( data - > client ,
( enum mma9551_gpio_pin ) mma_axis ,
2014-11-24 11:43:15 +02:00
MMA9551_APPID_NONE , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
2015-01-11 21:10:14 +02:00
ret = mma9551_set_power_state ( data - > client , false ) ;
if ( ret < 0 )
return ret ;
2014-11-24 11:43:15 +02:00
} else {
int bitnum ;
/* Bit 7 of each angle register holds the angle flag. */
switch ( axis ) {
case IIO_MOD_X :
bitnum = 7 + 8 * MMA9551_TILT_YZ_ANG_REG ;
break ;
case IIO_MOD_Y :
bitnum = 7 + 8 * MMA9551_TILT_XZ_ANG_REG ;
break ;
case IIO_MOD_Z :
bitnum = 7 + 8 * MMA9551_TILT_XY_ANG_REG ;
break ;
default :
return - EINVAL ;
}
2015-01-11 21:10:14 +02:00
ret = mma9551_set_power_state ( data - > client , true ) ;
if ( ret < 0 )
return ret ;
2014-12-15 17:16:51 +02:00
ret = mma9551_gpio_config ( data - > client ,
( enum mma9551_gpio_pin ) mma_axis ,
2014-11-24 11:43:15 +02:00
MMA9551_APPID_TILT , bitnum , 0 ) ;
2015-01-11 21:10:14 +02:00
if ( ret < 0 ) {
mma9551_set_power_state ( data - > client , false ) ;
2014-11-24 11:43:15 +02:00
return ret ;
2015-01-11 21:10:14 +02:00
}
2014-11-24 11:43:15 +02:00
}
data - > event_enabled [ mma_axis ] = state ;
return ret ;
}
static int mma9551_write_event_config ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir ,
int state )
{
struct mma9551_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( chan - > type ) {
case IIO_INCLI :
mutex_lock ( & data - > mutex ) ;
ret = mma9551_config_incli_event ( indio_dev ,
chan - > channel2 , state ) ;
mutex_unlock ( & data - > mutex ) ;
return ret ;
default :
return - EINVAL ;
}
}
static int mma9551_write_event_value ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir ,
enum iio_event_info info ,
int val , int val2 )
{
struct mma9551_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( chan - > type ) {
case IIO_INCLI :
if ( val2 ! = 0 | | val < 1 | | val > 10 )
return - EINVAL ;
mutex_lock ( & data - > mutex ) ;
ret = mma9551_update_config_bits ( data - > client ,
MMA9551_APPID_TILT ,
MMA9551_TILT_CFG_REG ,
MMA9551_TILT_ANG_THRESH_MASK ,
val ) ;
mutex_unlock ( & data - > mutex ) ;
return ret ;
default :
return - EINVAL ;
}
}
static int mma9551_read_event_value ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir ,
enum iio_event_info info ,
int * val , int * val2 )
{
struct mma9551_data * data = iio_priv ( indio_dev ) ;
int ret ;
u8 tmp ;
switch ( chan - > type ) {
case IIO_INCLI :
mutex_lock ( & data - > mutex ) ;
ret = mma9551_read_config_byte ( data - > client ,
MMA9551_APPID_TILT ,
MMA9551_TILT_CFG_REG , & tmp ) ;
mutex_unlock ( & data - > mutex ) ;
if ( ret < 0 )
return ret ;
* val = tmp & MMA9551_TILT_ANG_THRESH_MASK ;
* val2 = 0 ;
return IIO_VAL_INT ;
default :
return - EINVAL ;
}
}
static const struct iio_event_spec mma9551_incli_event = {
. type = IIO_EV_TYPE_ROC ,
. dir = IIO_EV_DIR_RISING ,
. mask_separate = BIT ( IIO_EV_INFO_ENABLE ) ,
. mask_shared_by_type = BIT ( IIO_EV_INFO_VALUE ) ,
} ;
# define MMA9551_INCLI_CHANNEL(axis) { \
. type = IIO_INCLI , \
. modified = 1 , \
. channel2 = axis , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) , \
. event_spec = & mma9551_incli_event , \
. num_event_specs = 1 , \
}
static const struct iio_chan_spec mma9551_channels [ ] = {
MMA9551_ACCEL_CHANNEL ( IIO_MOD_X ) ,
MMA9551_ACCEL_CHANNEL ( IIO_MOD_Y ) ,
MMA9551_ACCEL_CHANNEL ( IIO_MOD_Z ) ,
MMA9551_INCLI_CHANNEL ( IIO_MOD_X ) ,
MMA9551_INCLI_CHANNEL ( IIO_MOD_Y ) ,
MMA9551_INCLI_CHANNEL ( IIO_MOD_Z ) ,
} ;
static const struct iio_info mma9551_info = {
. driver_module = THIS_MODULE ,
. read_raw = mma9551_read_raw ,
. read_event_config = mma9551_read_event_config ,
. write_event_config = mma9551_write_event_config ,
. read_event_value = mma9551_read_event_value ,
. write_event_value = mma9551_write_event_value ,
} ;
static irqreturn_t mma9551_event_handler ( int irq , void * private )
{
struct iio_dev * indio_dev = private ;
struct mma9551_data * data = iio_priv ( indio_dev ) ;
int i , ret , mma_axis = - 1 ;
u16 reg ;
u8 val ;
mutex_lock ( & data - > mutex ) ;
for ( i = 0 ; i < 3 ; i + + )
if ( irq = = data - > irqs [ i ] ) {
mma_axis = i ;
break ;
}
if ( mma_axis = = - 1 ) {
/* IRQ was triggered on 4th line, which we don't use. */
dev_warn ( & data - > client - > dev ,
" irq triggered on unused line %d \n " , data - > irqs [ 3 ] ) ;
goto out ;
}
switch ( mma_axis ) {
case mma9551_x :
reg = MMA9551_TILT_YZ_ANG_REG ;
break ;
case mma9551_y :
reg = MMA9551_TILT_XZ_ANG_REG ;
break ;
case mma9551_z :
reg = MMA9551_TILT_XY_ANG_REG ;
break ;
}
/*
* Read the angle even though we don ' t use it , otherwise we
* won ' t get any further interrupts .
*/
ret = mma9551_read_status_byte ( data - > client , MMA9551_APPID_TILT ,
reg , & val ) ;
if ( ret < 0 ) {
dev_err ( & data - > client - > dev ,
" error %d reading tilt register in IRQ \n " , ret ) ;
goto out ;
}
iio_push_event ( indio_dev ,
IIO_MOD_EVENT_CODE ( IIO_INCLI , 0 , ( mma_axis + 1 ) ,
IIO_EV_TYPE_ROC , IIO_EV_DIR_RISING ) ,
iio_get_time_ns ( ) ) ;
out :
mutex_unlock ( & data - > mutex ) ;
return IRQ_HANDLED ;
}
static int mma9551_init ( struct mma9551_data * data )
{
int ret ;
ret = mma9551_read_version ( data - > client ) ;
if ( ret )
return ret ;
2015-01-11 21:10:14 +02:00
return mma9551_set_device_state ( data - > client , true ) ;
2014-11-24 11:43:15 +02:00
}
static int mma9551_gpio_probe ( struct iio_dev * indio_dev )
{
struct gpio_desc * gpio ;
int i , ret ;
struct mma9551_data * data = iio_priv ( indio_dev ) ;
struct device * dev = & data - > client - > dev ;
for ( i = 0 ; i < MMA9551_GPIO_COUNT ; i + + ) {
2015-02-18 13:47:11 +01:00
gpio = devm_gpiod_get_index ( dev , MMA9551_GPIO_NAME , i ,
GPIOD_IN ) ;
2014-11-24 11:43:15 +02:00
if ( IS_ERR ( gpio ) ) {
dev_err ( dev , " acpi gpio get index failed \n " ) ;
return PTR_ERR ( gpio ) ;
}
2015-03-02 12:39:12 +02:00
ret = gpiod_to_irq ( gpio ) ;
if ( ret < 0 )
return ret ;
data - > irqs [ i ] = ret ;
2014-11-24 11:43:15 +02:00
ret = devm_request_threaded_irq ( dev , data - > irqs [ i ] ,
NULL , mma9551_event_handler ,
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
MMA9551_IRQ_NAME , indio_dev ) ;
if ( ret < 0 ) {
dev_err ( dev , " request irq %d failed \n " , data - > irqs [ i ] ) ;
return ret ;
}
dev_dbg ( dev , " gpio resource, no:%d irq:%d \n " ,
desc_to_gpio ( gpio ) , data - > irqs [ i ] ) ;
}
return 0 ;
}
static const char * mma9551_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 NULL ;
return dev_name ( dev ) ;
}
static int mma9551_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct mma9551_data * data ;
struct iio_dev * indio_dev ;
const char * name = NULL ;
int ret ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
if ( id )
name = id - > name ;
else if ( ACPI_HANDLE ( & client - > dev ) )
name = mma9551_match_acpi_device ( & client - > dev ) ;
ret = mma9551_init ( data ) ;
if ( ret < 0 )
return ret ;
mutex_init ( & data - > mutex ) ;
indio_dev - > dev . parent = & client - > dev ;
indio_dev - > channels = mma9551_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( mma9551_channels ) ;
indio_dev - > name = name ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > info = & mma9551_info ;
ret = mma9551_gpio_probe ( indio_dev ) ;
if ( ret < 0 )
goto out_poweroff ;
2015-01-11 21:10:14 +02:00
ret = pm_runtime_set_active ( & client - > dev ) ;
if ( ret < 0 )
2015-11-05 16:25:29 +02:00
goto out_poweroff ;
2015-01-11 21:10:14 +02:00
pm_runtime_enable ( & client - > dev ) ;
pm_runtime_set_autosuspend_delay ( & client - > dev ,
MMA9551_AUTO_SUSPEND_DELAY_MS ) ;
pm_runtime_use_autosuspend ( & client - > dev ) ;
2015-11-05 16:25:29 +02:00
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " unable to register iio device \n " ) ;
goto out_poweroff ;
}
2014-11-24 11:43:15 +02:00
return 0 ;
out_poweroff :
mma9551_set_device_state ( client , false ) ;
return ret ;
}
static int mma9551_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
struct mma9551_data * data = iio_priv ( indio_dev ) ;
2015-11-05 16:25:29 +02:00
iio_device_unregister ( indio_dev ) ;
2015-01-11 21:10:14 +02:00
pm_runtime_disable ( & client - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
pm_runtime_put_noidle ( & client - > dev ) ;
2014-11-24 11:43:15 +02:00
mutex_lock ( & data - > mutex ) ;
mma9551_set_device_state ( data - > client , false ) ;
mutex_unlock ( & data - > mutex ) ;
return 0 ;
}
2015-01-11 21:10:14 +02:00
# ifdef CONFIG_PM
static int mma9551_runtime_suspend ( struct device * dev )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct mma9551_data * data = iio_priv ( indio_dev ) ;
int ret ;
mutex_lock ( & data - > mutex ) ;
ret = mma9551_set_device_state ( data - > client , false ) ;
mutex_unlock ( & data - > mutex ) ;
if ( ret < 0 ) {
dev_err ( & data - > client - > dev , " powering off device failed \n " ) ;
return - EAGAIN ;
}
return 0 ;
}
static int mma9551_runtime_resume ( struct device * dev )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct mma9551_data * data = iio_priv ( indio_dev ) ;
int ret ;
ret = mma9551_set_device_state ( data - > client , true ) ;
if ( ret < 0 )
return ret ;
mma9551_sleep ( MMA9551_DEFAULT_SAMPLE_RATE ) ;
return 0 ;
}
# endif
2014-11-24 11:43:15 +02:00
# ifdef CONFIG_PM_SLEEP
static int mma9551_suspend ( struct device * dev )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct mma9551_data * data = iio_priv ( indio_dev ) ;
2015-01-11 21:10:14 +02:00
int ret ;
2014-11-24 11:43:15 +02:00
mutex_lock ( & data - > mutex ) ;
2015-01-11 21:10:14 +02:00
ret = mma9551_set_device_state ( data - > client , false ) ;
2014-11-24 11:43:15 +02:00
mutex_unlock ( & data - > mutex ) ;
2015-01-11 21:10:14 +02:00
return ret ;
2014-11-24 11:43:15 +02:00
}
static int mma9551_resume ( struct device * dev )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct mma9551_data * data = iio_priv ( indio_dev ) ;
2015-01-11 21:10:14 +02:00
int ret ;
2014-11-24 11:43:15 +02:00
mutex_lock ( & data - > mutex ) ;
2015-01-11 21:10:14 +02:00
ret = mma9551_set_device_state ( data - > client , true ) ;
2014-11-24 11:43:15 +02:00
mutex_unlock ( & data - > mutex ) ;
2015-01-11 21:10:14 +02:00
return ret ;
2014-11-24 11:43:15 +02:00
}
# endif
static const struct dev_pm_ops mma9551_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( mma9551_suspend , mma9551_resume )
2015-01-11 21:10:14 +02:00
SET_RUNTIME_PM_OPS ( mma9551_runtime_suspend ,
mma9551_runtime_resume , NULL )
2014-11-24 11:43:15 +02:00
} ;
static const struct acpi_device_id mma9551_acpi_match [ ] = {
{ " MMA9551 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , mma9551_acpi_match ) ;
static const struct i2c_device_id mma9551_id [ ] = {
{ " mma9551 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , mma9551_id ) ;
static struct i2c_driver mma9551_driver = {
. driver = {
. name = MMA9551_DRV_NAME ,
. acpi_match_table = ACPI_PTR ( mma9551_acpi_match ) ,
. pm = & mma9551_pm_ops ,
} ,
. probe = mma9551_probe ,
. remove = mma9551_remove ,
. id_table = mma9551_id ,
} ;
module_i2c_driver ( mma9551_driver ) ;
MODULE_AUTHOR ( " Irina Tirdea <irina.tirdea@intel.com> " ) ;
MODULE_AUTHOR ( " Vlad Dogaru <vlad.dogaru@intel.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " MMA9551L motion-sensing platform driver " ) ;