2012-11-20 13:36:00 +00:00
/*
* ADIS16133 / ADIS16135 / ADIS16136 gyroscope driver
*
* Copyright 2012 Analog Devices Inc .
* Author : Lars - Peter Clausen < lars @ metafoo . de >
*
* Licensed under the GPL - 2.
*/
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/mutex.h>
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/spi/spi.h>
# include <linux/slab.h>
# include <linux/sysfs.h>
# include <linux/module.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/buffer.h>
# include <linux/iio/imu/adis.h>
# include <linux/debugfs.h>
# define ADIS16136_REG_FLASH_CNT 0x00
# define ADIS16136_REG_TEMP_OUT 0x02
# define ADIS16136_REG_GYRO_OUT2 0x04
# define ADIS16136_REG_GYRO_OUT 0x06
# define ADIS16136_REG_GYRO_OFF2 0x08
# define ADIS16136_REG_GYRO_OFF 0x0A
# define ADIS16136_REG_ALM_MAG1 0x10
# define ADIS16136_REG_ALM_MAG2 0x12
# define ADIS16136_REG_ALM_SAMPL1 0x14
# define ADIS16136_REG_ALM_SAMPL2 0x16
# define ADIS16136_REG_ALM_CTRL 0x18
# define ADIS16136_REG_GPIO_CTRL 0x1A
# define ADIS16136_REG_MSC_CTRL 0x1C
# define ADIS16136_REG_SMPL_PRD 0x1E
# define ADIS16136_REG_AVG_CNT 0x20
# define ADIS16136_REG_DEC_RATE 0x22
# define ADIS16136_REG_SLP_CTRL 0x24
# define ADIS16136_REG_DIAG_STAT 0x26
# define ADIS16136_REG_GLOB_CMD 0x28
# define ADIS16136_REG_LOT1 0x32
# define ADIS16136_REG_LOT2 0x34
# define ADIS16136_REG_LOT3 0x36
# define ADIS16136_REG_PROD_ID 0x38
# define ADIS16136_REG_SERIAL_NUM 0x3A
# define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL 2
# define ADIS16136_DIAG_STAT_SPI_FAIL 3
# define ADIS16136_DIAG_STAT_SELF_TEST_FAIL 5
# define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL 6
# define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
# define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
struct adis16136_chip_info {
unsigned int precision ;
unsigned int fullscale ;
} ;
struct adis16136 {
const struct adis16136_chip_info * chip_info ;
struct adis adis ;
} ;
# ifdef CONFIG_DEBUG_FS
static ssize_t adis16136_show_serial ( struct file * file ,
char __user * userbuf , size_t count , loff_t * ppos )
{
struct adis16136 * adis16136 = file - > private_data ;
uint16_t lot1 , lot2 , lot3 , serial ;
char buf [ 20 ] ;
size_t len ;
int ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_SERIAL_NUM ,
& serial ) ;
if ( ret < 0 )
return ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_LOT1 , & lot1 ) ;
if ( ret < 0 )
return ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_LOT2 , & lot2 ) ;
if ( ret < 0 )
return ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_LOT3 , & lot3 ) ;
if ( ret < 0 )
return ret ;
len = snprintf ( buf , sizeof ( buf ) , " %.4x%.4x%.4x-%.4x \n " , lot1 , lot2 ,
lot3 , serial ) ;
return simple_read_from_buffer ( userbuf , count , ppos , buf , len ) ;
}
static const struct file_operations adis16136_serial_fops = {
. open = simple_open ,
. read = adis16136_show_serial ,
. llseek = default_llseek ,
. owner = THIS_MODULE ,
} ;
static int adis16136_show_product_id ( void * arg , u64 * val )
{
struct adis16136 * adis16136 = arg ;
u16 prod_id ;
int ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_PROD_ID ,
& prod_id ) ;
if ( ret < 0 )
return ret ;
* val = prod_id ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( adis16136_product_id_fops ,
adis16136_show_product_id , NULL , " %llu \n " ) ;
static int adis16136_show_flash_count ( void * arg , u64 * val )
{
struct adis16136 * adis16136 = arg ;
uint16_t flash_count ;
int ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_FLASH_CNT ,
& flash_count ) ;
if ( ret < 0 )
return ret ;
* val = flash_count ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( adis16136_flash_count_fops ,
adis16136_show_flash_count , NULL , " %lld \n " ) ;
static int adis16136_debugfs_init ( struct iio_dev * indio_dev )
{
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
debugfs_create_file ( " serial_number " , 0400 , indio_dev - > debugfs_dentry ,
adis16136 , & adis16136_serial_fops ) ;
debugfs_create_file ( " product_id " , 0400 , indio_dev - > debugfs_dentry ,
adis16136 , & adis16136_product_id_fops ) ;
debugfs_create_file ( " flash_count " , 0400 , indio_dev - > debugfs_dentry ,
adis16136 , & adis16136_flash_count_fops ) ;
return 0 ;
}
# else
static int adis16136_debugfs_init ( struct iio_dev * indio_dev )
{
return 0 ;
}
# endif
static int adis16136_set_freq ( struct adis16136 * adis16136 , unsigned int freq )
{
unsigned int t ;
t = 32768 / freq ;
if ( t < 0xf )
t = 0xf ;
else if ( t > 0xffff )
t = 0xffff ;
else
t - - ;
return adis_write_reg_16 ( & adis16136 - > adis , ADIS16136_REG_SMPL_PRD , t ) ;
}
static int adis16136_get_freq ( struct adis16136 * adis16136 , unsigned int * freq )
{
uint16_t t ;
int ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_SMPL_PRD , & t ) ;
if ( ret < 0 )
return ret ;
* freq = 32768 / ( t + 1 ) ;
return 0 ;
}
static ssize_t adis16136_write_frequency ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t len )
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
2012-11-27 07:24:00 +00:00
unsigned int val ;
2012-11-20 13:36:00 +00:00
int ret ;
2012-11-27 07:24:00 +00:00
ret = kstrtouint ( buf , 10 , & val ) ;
2012-11-20 13:36:00 +00:00
if ( ret )
return ret ;
if ( val = = 0 )
return - EINVAL ;
ret = adis16136_set_freq ( adis16136 , val ) ;
return ret ? ret : len ;
}
static ssize_t adis16136_read_frequency ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
unsigned int freq ;
int ret ;
ret = adis16136_get_freq ( adis16136 , & freq ) ;
if ( ret < 0 )
return ret ;
return sprintf ( buf , " %d \n " , freq ) ;
}
static IIO_DEV_ATTR_SAMP_FREQ ( S_IWUSR | S_IRUGO ,
adis16136_read_frequency ,
adis16136_write_frequency ) ;
static const unsigned adis16136_3db_divisors [ ] = {
[ 0 ] = 2 , /* Special case */
[ 1 ] = 6 ,
[ 2 ] = 12 ,
[ 3 ] = 25 ,
[ 4 ] = 50 ,
[ 5 ] = 100 ,
[ 6 ] = 200 ,
[ 7 ] = 200 , /* Not a valid setting */
} ;
static int adis16136_set_filter ( struct iio_dev * indio_dev , int val )
{
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
unsigned int freq ;
int i , ret ;
ret = adis16136_get_freq ( adis16136 , & freq ) ;
if ( ret < 0 )
return ret ;
for ( i = ARRAY_SIZE ( adis16136_3db_divisors ) - 1 ; i > = 1 ; i - - ) {
if ( freq / adis16136_3db_divisors [ i ] > = val )
break ;
}
return adis_write_reg_16 ( & adis16136 - > adis , ADIS16136_REG_AVG_CNT , i ) ;
}
static int adis16136_get_filter ( struct iio_dev * indio_dev , int * val )
{
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
unsigned int freq ;
uint16_t val16 ;
int ret ;
mutex_lock ( & indio_dev - > mlock ) ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_AVG_CNT , & val16 ) ;
if ( ret < 0 )
goto err_unlock ;
ret = adis16136_get_freq ( adis16136 , & freq ) ;
if ( ret < 0 )
goto err_unlock ;
* val = freq / adis16136_3db_divisors [ val16 & 0x07 ] ;
err_unlock :
mutex_unlock ( & indio_dev - > mlock ) ;
return ret ? ret : IIO_VAL_INT ;
}
static int adis16136_read_raw ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan , int * val , int * val2 , long info )
{
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
uint32_t val32 ;
int ret ;
switch ( info ) {
case IIO_CHAN_INFO_RAW :
return adis_single_conversion ( indio_dev , chan , 0 , val ) ;
case IIO_CHAN_INFO_SCALE :
switch ( chan - > type ) {
case IIO_ANGL_VEL :
* val = adis16136 - > chip_info - > precision ;
* val2 = ( adis16136 - > chip_info - > fullscale < < 16 ) ;
return IIO_VAL_FRACTIONAL ;
case IIO_TEMP :
* val = 10 ;
* val2 = 697000 ; /* 0.010697 degree Celsius */
return IIO_VAL_INT_PLUS_MICRO ;
default :
return - EINVAL ;
}
case IIO_CHAN_INFO_CALIBBIAS :
ret = adis_read_reg_32 ( & adis16136 - > adis ,
ADIS16136_REG_GYRO_OFF2 , & val32 ) ;
if ( ret < 0 )
return ret ;
* val = sign_extend32 ( val32 , 31 ) ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY :
return adis16136_get_filter ( indio_dev , val ) ;
default :
return - EINVAL ;
}
}
static int adis16136_write_raw ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan , int val , int val2 , long info )
{
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
switch ( info ) {
case IIO_CHAN_INFO_CALIBBIAS :
return adis_write_reg_32 ( & adis16136 - > adis ,
ADIS16136_REG_GYRO_OFF2 , val ) ;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY :
return adis16136_set_filter ( indio_dev , val ) ;
default :
break ;
}
return - EINVAL ;
}
enum {
ADIS16136_SCAN_GYRO ,
ADIS16136_SCAN_TEMP ,
} ;
static const struct iio_chan_spec adis16136_channels [ ] = {
{
. type = IIO_ANGL_VEL ,
. modified = 1 ,
. channel2 = IIO_MOD_X ,
. info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
IIO_CHAN_INFO_SCALE_SHARED_BIT |
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT ,
. address = ADIS16136_REG_GYRO_OUT2 ,
. scan_index = ADIS16136_SCAN_GYRO ,
. scan_type = {
. sign = ' s ' ,
. realbits = 32 ,
. storagebits = 32 ,
. endianness = IIO_BE ,
} ,
} , {
. type = IIO_TEMP ,
. indexed = 1 ,
. channel = 0 ,
. info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
IIO_CHAN_INFO_SCALE_SEPARATE_BIT ,
. address = ADIS16136_REG_TEMP_OUT ,
. scan_index = ADIS16136_SCAN_TEMP ,
. scan_type = {
. sign = ' s ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_BE ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 2 ) ,
} ;
static struct attribute * adis16136_attributes [ ] = {
& iio_dev_attr_sampling_frequency . dev_attr . attr ,
NULL
} ;
static const struct attribute_group adis16136_attribute_group = {
. attrs = adis16136_attributes ,
} ;
static const struct iio_info adis16136_info = {
. driver_module = THIS_MODULE ,
. attrs = & adis16136_attribute_group ,
. read_raw = & adis16136_read_raw ,
. write_raw = & adis16136_write_raw ,
. update_scan_mode = adis_update_scan_mode ,
. debugfs_reg_access = adis_debugfs_reg_access ,
} ;
static int adis16136_stop_device ( struct iio_dev * indio_dev )
{
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
int ret ;
ret = adis_write_reg_16 ( & adis16136 - > adis , ADIS16136_REG_SLP_CTRL , 0xff ) ;
if ( ret )
dev_err ( & indio_dev - > dev ,
" Could not power down device: %d \n " , ret ) ;
return ret ;
}
static int adis16136_initial_setup ( struct iio_dev * indio_dev )
{
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
unsigned int device_id ;
uint16_t prod_id ;
int ret ;
ret = adis_initial_startup ( & adis16136 - > adis ) ;
if ( ret )
return ret ;
ret = adis_read_reg_16 ( & adis16136 - > adis , ADIS16136_REG_PROD_ID ,
& prod_id ) ;
if ( ret )
return ret ;
sscanf ( indio_dev - > name , " adis%u \n " , & device_id ) ;
if ( prod_id ! = device_id )
dev_warn ( & indio_dev - > dev , " Device ID(%u) and product ID(%u) do not match. " ,
device_id , prod_id ) ;
return 0 ;
}
static const char * const adis16136_status_error_msgs [ ] = {
[ ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL ] = " Flash update failed " ,
[ ADIS16136_DIAG_STAT_SPI_FAIL ] = " SPI failure " ,
[ ADIS16136_DIAG_STAT_SELF_TEST_FAIL ] = " Self test error " ,
[ ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL ] = " Flash checksum error " ,
} ;
static const struct adis_data adis16136_data = {
. diag_stat_reg = ADIS16136_REG_DIAG_STAT ,
. glob_cmd_reg = ADIS16136_REG_GLOB_CMD ,
. msc_ctrl_reg = ADIS16136_REG_MSC_CTRL ,
. self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST ,
. startup_delay = 80 ,
. read_delay = 10 ,
. write_delay = 10 ,
. status_error_msgs = adis16136_status_error_msgs ,
. status_error_mask = BIT ( ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL ) |
BIT ( ADIS16136_DIAG_STAT_SPI_FAIL ) |
BIT ( ADIS16136_DIAG_STAT_SELF_TEST_FAIL ) |
BIT ( ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL ) ,
} ;
enum adis16136_id {
ID_ADIS16133 ,
ID_ADIS16135 ,
ID_ADIS16136 ,
} ;
static const struct adis16136_chip_info adis16136_chip_info [ ] = {
[ ID_ADIS16133 ] = {
. precision = IIO_DEGREE_TO_RAD ( 1200 ) ,
. fullscale = 24000 ,
} ,
[ ID_ADIS16135 ] = {
. precision = IIO_DEGREE_TO_RAD ( 300 ) ,
. fullscale = 24000 ,
} ,
[ ID_ADIS16136 ] = {
. precision = IIO_DEGREE_TO_RAD ( 450 ) ,
. fullscale = 24623 ,
} ,
} ;
static int adis16136_probe ( struct spi_device * spi )
{
const struct spi_device_id * id = spi_get_device_id ( spi ) ;
struct adis16136 * adis16136 ;
struct iio_dev * indio_dev ;
int ret ;
indio_dev = iio_device_alloc ( sizeof ( * adis16136 ) ) ;
if ( indio_dev = = NULL )
return - ENOMEM ;
spi_set_drvdata ( spi , indio_dev ) ;
adis16136 = iio_priv ( indio_dev ) ;
adis16136 - > chip_info = & adis16136_chip_info [ id - > driver_data ] ;
indio_dev - > dev . parent = & spi - > dev ;
indio_dev - > name = spi_get_device_id ( spi ) - > name ;
indio_dev - > channels = adis16136_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( adis16136_channels ) ;
indio_dev - > info = & adis16136_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
ret = adis_init ( & adis16136 - > adis , indio_dev , spi , & adis16136_data ) ;
if ( ret )
goto error_free_dev ;
ret = adis_setup_buffer_and_trigger ( & adis16136 - > adis , indio_dev , NULL ) ;
if ( ret )
goto error_free_dev ;
ret = adis16136_initial_setup ( indio_dev ) ;
if ( ret )
goto error_cleanup_buffer ;
ret = iio_device_register ( indio_dev ) ;
if ( ret )
goto error_stop_device ;
adis16136_debugfs_init ( indio_dev ) ;
return 0 ;
error_stop_device :
adis16136_stop_device ( indio_dev ) ;
error_cleanup_buffer :
adis_cleanup_buffer_and_trigger ( & adis16136 - > adis , indio_dev ) ;
error_free_dev :
iio_device_free ( indio_dev ) ;
return ret ;
}
static int adis16136_remove ( struct spi_device * spi )
{
struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
struct adis16136 * adis16136 = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
adis16136_stop_device ( indio_dev ) ;
adis_cleanup_buffer_and_trigger ( & adis16136 - > adis , indio_dev ) ;
iio_device_free ( indio_dev ) ;
return 0 ;
}
static const struct spi_device_id adis16136_ids [ ] = {
{ " adis16133 " , ID_ADIS16133 } ,
{ " adis16135 " , ID_ADIS16135 } ,
{ " adis16136 " , ID_ADIS16136 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , adis16136_ids ) ;
static struct spi_driver adis16136_driver = {
. driver = {
. name = " adis16136 " ,
. owner = THIS_MODULE ,
} ,
. id_table = adis16136_ids ,
. probe = adis16136_probe ,
. remove = adis16136_remove ,
} ;
module_spi_driver ( adis16136_driver ) ;
MODULE_AUTHOR ( " Lars-Peter Clausen <lars@metafoo.de> " ) ;
MODULE_DESCRIPTION ( " Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;