1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29caed0d9SLars-Peter Clausen /* 39caed0d9SLars-Peter Clausen * ADIS16133/ADIS16135/ADIS16136 gyroscope driver 49caed0d9SLars-Peter Clausen * 59caed0d9SLars-Peter Clausen * Copyright 2012 Analog Devices Inc. 69caed0d9SLars-Peter Clausen * Author: Lars-Peter Clausen <lars@metafoo.de> 79caed0d9SLars-Peter Clausen */ 89caed0d9SLars-Peter Clausen 99caed0d9SLars-Peter Clausen #include <linux/interrupt.h> 109caed0d9SLars-Peter Clausen #include <linux/delay.h> 119caed0d9SLars-Peter Clausen #include <linux/mutex.h> 129caed0d9SLars-Peter Clausen #include <linux/device.h> 139caed0d9SLars-Peter Clausen #include <linux/kernel.h> 149caed0d9SLars-Peter Clausen #include <linux/spi/spi.h> 159caed0d9SLars-Peter Clausen #include <linux/slab.h> 169caed0d9SLars-Peter Clausen #include <linux/sysfs.h> 179caed0d9SLars-Peter Clausen #include <linux/module.h> 189caed0d9SLars-Peter Clausen 199caed0d9SLars-Peter Clausen #include <linux/iio/iio.h> 209caed0d9SLars-Peter Clausen #include <linux/iio/sysfs.h> 219caed0d9SLars-Peter Clausen #include <linux/iio/buffer.h> 229caed0d9SLars-Peter Clausen #include <linux/iio/imu/adis.h> 239caed0d9SLars-Peter Clausen 249caed0d9SLars-Peter Clausen #include <linux/debugfs.h> 259caed0d9SLars-Peter Clausen 269caed0d9SLars-Peter Clausen #define ADIS16136_REG_FLASH_CNT 0x00 279caed0d9SLars-Peter Clausen #define ADIS16136_REG_TEMP_OUT 0x02 289caed0d9SLars-Peter Clausen #define ADIS16136_REG_GYRO_OUT2 0x04 299caed0d9SLars-Peter Clausen #define ADIS16136_REG_GYRO_OUT 0x06 309caed0d9SLars-Peter Clausen #define ADIS16136_REG_GYRO_OFF2 0x08 319caed0d9SLars-Peter Clausen #define ADIS16136_REG_GYRO_OFF 0x0A 329caed0d9SLars-Peter Clausen #define ADIS16136_REG_ALM_MAG1 0x10 339caed0d9SLars-Peter Clausen #define ADIS16136_REG_ALM_MAG2 0x12 349caed0d9SLars-Peter Clausen #define ADIS16136_REG_ALM_SAMPL1 0x14 359caed0d9SLars-Peter Clausen #define ADIS16136_REG_ALM_SAMPL2 0x16 369caed0d9SLars-Peter Clausen #define ADIS16136_REG_ALM_CTRL 0x18 379caed0d9SLars-Peter Clausen #define ADIS16136_REG_GPIO_CTRL 0x1A 389caed0d9SLars-Peter Clausen #define ADIS16136_REG_MSC_CTRL 0x1C 399caed0d9SLars-Peter Clausen #define ADIS16136_REG_SMPL_PRD 0x1E 409caed0d9SLars-Peter Clausen #define ADIS16136_REG_AVG_CNT 0x20 419caed0d9SLars-Peter Clausen #define ADIS16136_REG_DEC_RATE 0x22 429caed0d9SLars-Peter Clausen #define ADIS16136_REG_SLP_CTRL 0x24 439caed0d9SLars-Peter Clausen #define ADIS16136_REG_DIAG_STAT 0x26 449caed0d9SLars-Peter Clausen #define ADIS16136_REG_GLOB_CMD 0x28 459caed0d9SLars-Peter Clausen #define ADIS16136_REG_LOT1 0x32 469caed0d9SLars-Peter Clausen #define ADIS16136_REG_LOT2 0x34 479caed0d9SLars-Peter Clausen #define ADIS16136_REG_LOT3 0x36 489caed0d9SLars-Peter Clausen #define ADIS16136_REG_PROD_ID 0x38 499caed0d9SLars-Peter Clausen #define ADIS16136_REG_SERIAL_NUM 0x3A 509caed0d9SLars-Peter Clausen 519caed0d9SLars-Peter Clausen #define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL 2 529caed0d9SLars-Peter Clausen #define ADIS16136_DIAG_STAT_SPI_FAIL 3 539caed0d9SLars-Peter Clausen #define ADIS16136_DIAG_STAT_SELF_TEST_FAIL 5 549caed0d9SLars-Peter Clausen #define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL 6 559caed0d9SLars-Peter Clausen 569caed0d9SLars-Peter Clausen #define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11) 579caed0d9SLars-Peter Clausen #define ADIS16136_MSC_CTRL_SELF_TEST BIT(10) 589caed0d9SLars-Peter Clausen 599caed0d9SLars-Peter Clausen struct adis16136_chip_info { 609caed0d9SLars-Peter Clausen unsigned int precision; 619caed0d9SLars-Peter Clausen unsigned int fullscale; 62*380b107bSNuno Sá const struct adis_timeout *timeouts; 639caed0d9SLars-Peter Clausen }; 649caed0d9SLars-Peter Clausen 659caed0d9SLars-Peter Clausen struct adis16136 { 669caed0d9SLars-Peter Clausen const struct adis16136_chip_info *chip_info; 679caed0d9SLars-Peter Clausen 689caed0d9SLars-Peter Clausen struct adis adis; 699caed0d9SLars-Peter Clausen }; 709caed0d9SLars-Peter Clausen 719caed0d9SLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 729caed0d9SLars-Peter Clausen 739caed0d9SLars-Peter Clausen static ssize_t adis16136_show_serial(struct file *file, 749caed0d9SLars-Peter Clausen char __user *userbuf, size_t count, loff_t *ppos) 759caed0d9SLars-Peter Clausen { 769caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = file->private_data; 779caed0d9SLars-Peter Clausen uint16_t lot1, lot2, lot3, serial; 789caed0d9SLars-Peter Clausen char buf[20]; 799caed0d9SLars-Peter Clausen size_t len; 809caed0d9SLars-Peter Clausen int ret; 819caed0d9SLars-Peter Clausen 829caed0d9SLars-Peter Clausen ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM, 839caed0d9SLars-Peter Clausen &serial); 8426ba6db6SAlexandru Ardelean if (ret) 859caed0d9SLars-Peter Clausen return ret; 869caed0d9SLars-Peter Clausen 879caed0d9SLars-Peter Clausen ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1); 8826ba6db6SAlexandru Ardelean if (ret) 899caed0d9SLars-Peter Clausen return ret; 909caed0d9SLars-Peter Clausen 919caed0d9SLars-Peter Clausen ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2); 9226ba6db6SAlexandru Ardelean if (ret) 939caed0d9SLars-Peter Clausen return ret; 949caed0d9SLars-Peter Clausen 959caed0d9SLars-Peter Clausen ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3); 9626ba6db6SAlexandru Ardelean if (ret) 979caed0d9SLars-Peter Clausen return ret; 989caed0d9SLars-Peter Clausen 999caed0d9SLars-Peter Clausen len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2, 1009caed0d9SLars-Peter Clausen lot3, serial); 1019caed0d9SLars-Peter Clausen 1029caed0d9SLars-Peter Clausen return simple_read_from_buffer(userbuf, count, ppos, buf, len); 1039caed0d9SLars-Peter Clausen } 1049caed0d9SLars-Peter Clausen 1059caed0d9SLars-Peter Clausen static const struct file_operations adis16136_serial_fops = { 1069caed0d9SLars-Peter Clausen .open = simple_open, 1079caed0d9SLars-Peter Clausen .read = adis16136_show_serial, 1089caed0d9SLars-Peter Clausen .llseek = default_llseek, 1099caed0d9SLars-Peter Clausen .owner = THIS_MODULE, 1109caed0d9SLars-Peter Clausen }; 1119caed0d9SLars-Peter Clausen 1129caed0d9SLars-Peter Clausen static int adis16136_show_product_id(void *arg, u64 *val) 1139caed0d9SLars-Peter Clausen { 1149caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = arg; 1159caed0d9SLars-Peter Clausen u16 prod_id; 1169caed0d9SLars-Peter Clausen int ret; 1179caed0d9SLars-Peter Clausen 1189caed0d9SLars-Peter Clausen ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID, 1199caed0d9SLars-Peter Clausen &prod_id); 12026ba6db6SAlexandru Ardelean if (ret) 1219caed0d9SLars-Peter Clausen return ret; 1229caed0d9SLars-Peter Clausen 1239caed0d9SLars-Peter Clausen *val = prod_id; 1249caed0d9SLars-Peter Clausen 1259caed0d9SLars-Peter Clausen return 0; 1269caed0d9SLars-Peter Clausen } 1279aaea09bSVenkat Prashanth B U DEFINE_DEBUGFS_ATTRIBUTE(adis16136_product_id_fops, 1289caed0d9SLars-Peter Clausen adis16136_show_product_id, NULL, "%llu\n"); 1299caed0d9SLars-Peter Clausen 1309caed0d9SLars-Peter Clausen static int adis16136_show_flash_count(void *arg, u64 *val) 1319caed0d9SLars-Peter Clausen { 1329caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = arg; 1339caed0d9SLars-Peter Clausen uint16_t flash_count; 1349caed0d9SLars-Peter Clausen int ret; 1359caed0d9SLars-Peter Clausen 1369caed0d9SLars-Peter Clausen ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT, 1379caed0d9SLars-Peter Clausen &flash_count); 13826ba6db6SAlexandru Ardelean if (ret) 1399caed0d9SLars-Peter Clausen return ret; 1409caed0d9SLars-Peter Clausen 1419caed0d9SLars-Peter Clausen *val = flash_count; 1429caed0d9SLars-Peter Clausen 1439caed0d9SLars-Peter Clausen return 0; 1449caed0d9SLars-Peter Clausen } 1459aaea09bSVenkat Prashanth B U DEFINE_DEBUGFS_ATTRIBUTE(adis16136_flash_count_fops, 1469caed0d9SLars-Peter Clausen adis16136_show_flash_count, NULL, "%lld\n"); 1479caed0d9SLars-Peter Clausen 1489caed0d9SLars-Peter Clausen static int adis16136_debugfs_init(struct iio_dev *indio_dev) 1499caed0d9SLars-Peter Clausen { 1509caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 1519caed0d9SLars-Peter Clausen 1529aaea09bSVenkat Prashanth B U debugfs_create_file_unsafe("serial_number", 0400, 1539aaea09bSVenkat Prashanth B U indio_dev->debugfs_dentry, adis16136, 1549aaea09bSVenkat Prashanth B U &adis16136_serial_fops); 1559aaea09bSVenkat Prashanth B U debugfs_create_file_unsafe("product_id", 0400, 1569aaea09bSVenkat Prashanth B U indio_dev->debugfs_dentry, 1579caed0d9SLars-Peter Clausen adis16136, &adis16136_product_id_fops); 1589aaea09bSVenkat Prashanth B U debugfs_create_file_unsafe("flash_count", 0400, 1599aaea09bSVenkat Prashanth B U indio_dev->debugfs_dentry, 1609caed0d9SLars-Peter Clausen adis16136, &adis16136_flash_count_fops); 1619caed0d9SLars-Peter Clausen 1629caed0d9SLars-Peter Clausen return 0; 1639caed0d9SLars-Peter Clausen } 1649caed0d9SLars-Peter Clausen 1659caed0d9SLars-Peter Clausen #else 1669caed0d9SLars-Peter Clausen 1679caed0d9SLars-Peter Clausen static int adis16136_debugfs_init(struct iio_dev *indio_dev) 1689caed0d9SLars-Peter Clausen { 1699caed0d9SLars-Peter Clausen return 0; 1709caed0d9SLars-Peter Clausen } 1719caed0d9SLars-Peter Clausen 1729caed0d9SLars-Peter Clausen #endif 1739caed0d9SLars-Peter Clausen 1749caed0d9SLars-Peter Clausen static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq) 1759caed0d9SLars-Peter Clausen { 1769caed0d9SLars-Peter Clausen unsigned int t; 1779caed0d9SLars-Peter Clausen 1789caed0d9SLars-Peter Clausen t = 32768 / freq; 1799caed0d9SLars-Peter Clausen if (t < 0xf) 1809caed0d9SLars-Peter Clausen t = 0xf; 1819caed0d9SLars-Peter Clausen else if (t > 0xffff) 1829caed0d9SLars-Peter Clausen t = 0xffff; 1839caed0d9SLars-Peter Clausen else 1849caed0d9SLars-Peter Clausen t--; 1859caed0d9SLars-Peter Clausen 1869caed0d9SLars-Peter Clausen return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t); 1879caed0d9SLars-Peter Clausen } 1889caed0d9SLars-Peter Clausen 1890aee99a1SAlexandru Ardelean static int __adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq) 1909caed0d9SLars-Peter Clausen { 1919caed0d9SLars-Peter Clausen uint16_t t; 1929caed0d9SLars-Peter Clausen int ret; 1939caed0d9SLars-Peter Clausen 1940aee99a1SAlexandru Ardelean ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t); 19526ba6db6SAlexandru Ardelean if (ret) 1969caed0d9SLars-Peter Clausen return ret; 1979caed0d9SLars-Peter Clausen 1989caed0d9SLars-Peter Clausen *freq = 32768 / (t + 1); 1999caed0d9SLars-Peter Clausen 2009caed0d9SLars-Peter Clausen return 0; 2019caed0d9SLars-Peter Clausen } 2029caed0d9SLars-Peter Clausen 2039caed0d9SLars-Peter Clausen static ssize_t adis16136_write_frequency(struct device *dev, 2049caed0d9SLars-Peter Clausen struct device_attribute *attr, const char *buf, size_t len) 2059caed0d9SLars-Peter Clausen { 2069caed0d9SLars-Peter Clausen struct iio_dev *indio_dev = dev_to_iio_dev(dev); 2079caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 20812660138SDan Carpenter unsigned int val; 2099caed0d9SLars-Peter Clausen int ret; 2109caed0d9SLars-Peter Clausen 21112660138SDan Carpenter ret = kstrtouint(buf, 10, &val); 2129caed0d9SLars-Peter Clausen if (ret) 2139caed0d9SLars-Peter Clausen return ret; 2149caed0d9SLars-Peter Clausen 2159caed0d9SLars-Peter Clausen if (val == 0) 2169caed0d9SLars-Peter Clausen return -EINVAL; 2179caed0d9SLars-Peter Clausen 2189caed0d9SLars-Peter Clausen ret = adis16136_set_freq(adis16136, val); 2199caed0d9SLars-Peter Clausen 2209caed0d9SLars-Peter Clausen return ret ? ret : len; 2219caed0d9SLars-Peter Clausen } 2229caed0d9SLars-Peter Clausen 2239caed0d9SLars-Peter Clausen static ssize_t adis16136_read_frequency(struct device *dev, 2249caed0d9SLars-Peter Clausen struct device_attribute *attr, char *buf) 2259caed0d9SLars-Peter Clausen { 2269caed0d9SLars-Peter Clausen struct iio_dev *indio_dev = dev_to_iio_dev(dev); 2279caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 2280aee99a1SAlexandru Ardelean struct mutex *slock = &adis16136->adis.state_lock; 2299caed0d9SLars-Peter Clausen unsigned int freq; 2309caed0d9SLars-Peter Clausen int ret; 2319caed0d9SLars-Peter Clausen 2320aee99a1SAlexandru Ardelean mutex_lock(slock); 2330aee99a1SAlexandru Ardelean ret = __adis16136_get_freq(adis16136, &freq); 2340aee99a1SAlexandru Ardelean mutex_unlock(slock); 23526ba6db6SAlexandru Ardelean if (ret) 2369caed0d9SLars-Peter Clausen return ret; 2379caed0d9SLars-Peter Clausen 2389caed0d9SLars-Peter Clausen return sprintf(buf, "%d\n", freq); 2399caed0d9SLars-Peter Clausen } 2409caed0d9SLars-Peter Clausen 2419caed0d9SLars-Peter Clausen static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, 2429caed0d9SLars-Peter Clausen adis16136_read_frequency, 2439caed0d9SLars-Peter Clausen adis16136_write_frequency); 2449caed0d9SLars-Peter Clausen 2459caed0d9SLars-Peter Clausen static const unsigned adis16136_3db_divisors[] = { 2469caed0d9SLars-Peter Clausen [0] = 2, /* Special case */ 2479caed0d9SLars-Peter Clausen [1] = 6, 2489caed0d9SLars-Peter Clausen [2] = 12, 2499caed0d9SLars-Peter Clausen [3] = 25, 2509caed0d9SLars-Peter Clausen [4] = 50, 2519caed0d9SLars-Peter Clausen [5] = 100, 2529caed0d9SLars-Peter Clausen [6] = 200, 2539caed0d9SLars-Peter Clausen [7] = 200, /* Not a valid setting */ 2549caed0d9SLars-Peter Clausen }; 2559caed0d9SLars-Peter Clausen 2569caed0d9SLars-Peter Clausen static int adis16136_set_filter(struct iio_dev *indio_dev, int val) 2579caed0d9SLars-Peter Clausen { 2589caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 2590aee99a1SAlexandru Ardelean struct mutex *slock = &adis16136->adis.state_lock; 2609caed0d9SLars-Peter Clausen unsigned int freq; 2619caed0d9SLars-Peter Clausen int i, ret; 2629caed0d9SLars-Peter Clausen 2630aee99a1SAlexandru Ardelean mutex_lock(slock); 2640aee99a1SAlexandru Ardelean ret = __adis16136_get_freq(adis16136, &freq); 26526ba6db6SAlexandru Ardelean if (ret) 2660aee99a1SAlexandru Ardelean goto out_unlock; 2679caed0d9SLars-Peter Clausen 2689caed0d9SLars-Peter Clausen for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) { 2699caed0d9SLars-Peter Clausen if (freq / adis16136_3db_divisors[i] >= val) 2709caed0d9SLars-Peter Clausen break; 2719caed0d9SLars-Peter Clausen } 2729caed0d9SLars-Peter Clausen 2730aee99a1SAlexandru Ardelean ret = __adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i); 2740aee99a1SAlexandru Ardelean out_unlock: 2750aee99a1SAlexandru Ardelean mutex_unlock(slock); 2760aee99a1SAlexandru Ardelean 2770aee99a1SAlexandru Ardelean return ret; 2789caed0d9SLars-Peter Clausen } 2799caed0d9SLars-Peter Clausen 2809caed0d9SLars-Peter Clausen static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) 2819caed0d9SLars-Peter Clausen { 2829caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 2830aee99a1SAlexandru Ardelean struct mutex *slock = &adis16136->adis.state_lock; 2849caed0d9SLars-Peter Clausen unsigned int freq; 2859caed0d9SLars-Peter Clausen uint16_t val16; 2869caed0d9SLars-Peter Clausen int ret; 2879caed0d9SLars-Peter Clausen 2880aee99a1SAlexandru Ardelean mutex_lock(slock); 2899caed0d9SLars-Peter Clausen 2900aee99a1SAlexandru Ardelean ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, 2910aee99a1SAlexandru Ardelean &val16); 29226ba6db6SAlexandru Ardelean if (ret) 2939caed0d9SLars-Peter Clausen goto err_unlock; 2949caed0d9SLars-Peter Clausen 2950aee99a1SAlexandru Ardelean ret = __adis16136_get_freq(adis16136, &freq); 29626ba6db6SAlexandru Ardelean if (ret) 2979caed0d9SLars-Peter Clausen goto err_unlock; 2989caed0d9SLars-Peter Clausen 2999caed0d9SLars-Peter Clausen *val = freq / adis16136_3db_divisors[val16 & 0x07]; 3009caed0d9SLars-Peter Clausen 3019caed0d9SLars-Peter Clausen err_unlock: 3020aee99a1SAlexandru Ardelean mutex_unlock(slock); 3039caed0d9SLars-Peter Clausen 3049caed0d9SLars-Peter Clausen return ret ? ret : IIO_VAL_INT; 3059caed0d9SLars-Peter Clausen } 3069caed0d9SLars-Peter Clausen 3079caed0d9SLars-Peter Clausen static int adis16136_read_raw(struct iio_dev *indio_dev, 3089caed0d9SLars-Peter Clausen const struct iio_chan_spec *chan, int *val, int *val2, long info) 3099caed0d9SLars-Peter Clausen { 3109caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 3119caed0d9SLars-Peter Clausen uint32_t val32; 3129caed0d9SLars-Peter Clausen int ret; 3139caed0d9SLars-Peter Clausen 3149caed0d9SLars-Peter Clausen switch (info) { 3159caed0d9SLars-Peter Clausen case IIO_CHAN_INFO_RAW: 3169caed0d9SLars-Peter Clausen return adis_single_conversion(indio_dev, chan, 0, val); 3179caed0d9SLars-Peter Clausen case IIO_CHAN_INFO_SCALE: 3189caed0d9SLars-Peter Clausen switch (chan->type) { 3199caed0d9SLars-Peter Clausen case IIO_ANGL_VEL: 3209caed0d9SLars-Peter Clausen *val = adis16136->chip_info->precision; 3219caed0d9SLars-Peter Clausen *val2 = (adis16136->chip_info->fullscale << 16); 3229caed0d9SLars-Peter Clausen return IIO_VAL_FRACTIONAL; 3239caed0d9SLars-Peter Clausen case IIO_TEMP: 3249caed0d9SLars-Peter Clausen *val = 10; 3259caed0d9SLars-Peter Clausen *val2 = 697000; /* 0.010697 degree Celsius */ 3269caed0d9SLars-Peter Clausen return IIO_VAL_INT_PLUS_MICRO; 3279caed0d9SLars-Peter Clausen default: 3289caed0d9SLars-Peter Clausen return -EINVAL; 3299caed0d9SLars-Peter Clausen } 3309caed0d9SLars-Peter Clausen case IIO_CHAN_INFO_CALIBBIAS: 3319caed0d9SLars-Peter Clausen ret = adis_read_reg_32(&adis16136->adis, 3329caed0d9SLars-Peter Clausen ADIS16136_REG_GYRO_OFF2, &val32); 33326ba6db6SAlexandru Ardelean if (ret) 3349caed0d9SLars-Peter Clausen return ret; 3359caed0d9SLars-Peter Clausen 3369caed0d9SLars-Peter Clausen *val = sign_extend32(val32, 31); 3379caed0d9SLars-Peter Clausen 3389caed0d9SLars-Peter Clausen return IIO_VAL_INT; 3399caed0d9SLars-Peter Clausen case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 3409caed0d9SLars-Peter Clausen return adis16136_get_filter(indio_dev, val); 3419caed0d9SLars-Peter Clausen default: 3429caed0d9SLars-Peter Clausen return -EINVAL; 3439caed0d9SLars-Peter Clausen } 3449caed0d9SLars-Peter Clausen } 3459caed0d9SLars-Peter Clausen 3469caed0d9SLars-Peter Clausen static int adis16136_write_raw(struct iio_dev *indio_dev, 3479caed0d9SLars-Peter Clausen const struct iio_chan_spec *chan, int val, int val2, long info) 3489caed0d9SLars-Peter Clausen { 3499caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 3509caed0d9SLars-Peter Clausen 3519caed0d9SLars-Peter Clausen switch (info) { 3529caed0d9SLars-Peter Clausen case IIO_CHAN_INFO_CALIBBIAS: 3539caed0d9SLars-Peter Clausen return adis_write_reg_32(&adis16136->adis, 3549caed0d9SLars-Peter Clausen ADIS16136_REG_GYRO_OFF2, val); 3559caed0d9SLars-Peter Clausen case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 3569caed0d9SLars-Peter Clausen return adis16136_set_filter(indio_dev, val); 3579caed0d9SLars-Peter Clausen default: 3589caed0d9SLars-Peter Clausen break; 3599caed0d9SLars-Peter Clausen } 3609caed0d9SLars-Peter Clausen 3619caed0d9SLars-Peter Clausen return -EINVAL; 3629caed0d9SLars-Peter Clausen } 3639caed0d9SLars-Peter Clausen 3649caed0d9SLars-Peter Clausen enum { 3659caed0d9SLars-Peter Clausen ADIS16136_SCAN_GYRO, 3669caed0d9SLars-Peter Clausen ADIS16136_SCAN_TEMP, 3679caed0d9SLars-Peter Clausen }; 3689caed0d9SLars-Peter Clausen 3699caed0d9SLars-Peter Clausen static const struct iio_chan_spec adis16136_channels[] = { 3709caed0d9SLars-Peter Clausen { 3719caed0d9SLars-Peter Clausen .type = IIO_ANGL_VEL, 3729caed0d9SLars-Peter Clausen .modified = 1, 3739caed0d9SLars-Peter Clausen .channel2 = IIO_MOD_X, 374606f9067SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 375606f9067SJonathan Cameron BIT(IIO_CHAN_INFO_CALIBBIAS) | 376606f9067SJonathan Cameron BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), 377606f9067SJonathan Cameron .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 378606f9067SJonathan Cameron 3799caed0d9SLars-Peter Clausen .address = ADIS16136_REG_GYRO_OUT2, 3809caed0d9SLars-Peter Clausen .scan_index = ADIS16136_SCAN_GYRO, 3819caed0d9SLars-Peter Clausen .scan_type = { 3829caed0d9SLars-Peter Clausen .sign = 's', 3839caed0d9SLars-Peter Clausen .realbits = 32, 3849caed0d9SLars-Peter Clausen .storagebits = 32, 3859caed0d9SLars-Peter Clausen .endianness = IIO_BE, 3869caed0d9SLars-Peter Clausen }, 3879caed0d9SLars-Peter Clausen }, { 3889caed0d9SLars-Peter Clausen .type = IIO_TEMP, 3899caed0d9SLars-Peter Clausen .indexed = 1, 3909caed0d9SLars-Peter Clausen .channel = 0, 391606f9067SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 392606f9067SJonathan Cameron BIT(IIO_CHAN_INFO_SCALE), 3939caed0d9SLars-Peter Clausen .address = ADIS16136_REG_TEMP_OUT, 3949caed0d9SLars-Peter Clausen .scan_index = ADIS16136_SCAN_TEMP, 3959caed0d9SLars-Peter Clausen .scan_type = { 3969caed0d9SLars-Peter Clausen .sign = 's', 3979caed0d9SLars-Peter Clausen .realbits = 16, 3989caed0d9SLars-Peter Clausen .storagebits = 16, 3999caed0d9SLars-Peter Clausen .endianness = IIO_BE, 4009caed0d9SLars-Peter Clausen }, 4019caed0d9SLars-Peter Clausen }, 4029caed0d9SLars-Peter Clausen IIO_CHAN_SOFT_TIMESTAMP(2), 4039caed0d9SLars-Peter Clausen }; 4049caed0d9SLars-Peter Clausen 4059caed0d9SLars-Peter Clausen static struct attribute *adis16136_attributes[] = { 4069caed0d9SLars-Peter Clausen &iio_dev_attr_sampling_frequency.dev_attr.attr, 4079caed0d9SLars-Peter Clausen NULL 4089caed0d9SLars-Peter Clausen }; 4099caed0d9SLars-Peter Clausen 4109caed0d9SLars-Peter Clausen static const struct attribute_group adis16136_attribute_group = { 4119caed0d9SLars-Peter Clausen .attrs = adis16136_attributes, 4129caed0d9SLars-Peter Clausen }; 4139caed0d9SLars-Peter Clausen 4149caed0d9SLars-Peter Clausen static const struct iio_info adis16136_info = { 4159caed0d9SLars-Peter Clausen .attrs = &adis16136_attribute_group, 4169caed0d9SLars-Peter Clausen .read_raw = &adis16136_read_raw, 4179caed0d9SLars-Peter Clausen .write_raw = &adis16136_write_raw, 4189caed0d9SLars-Peter Clausen .update_scan_mode = adis_update_scan_mode, 4199caed0d9SLars-Peter Clausen .debugfs_reg_access = adis_debugfs_reg_access, 4209caed0d9SLars-Peter Clausen }; 4219caed0d9SLars-Peter Clausen 4229caed0d9SLars-Peter Clausen static int adis16136_stop_device(struct iio_dev *indio_dev) 4239caed0d9SLars-Peter Clausen { 4249caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 4259caed0d9SLars-Peter Clausen int ret; 4269caed0d9SLars-Peter Clausen 4279caed0d9SLars-Peter Clausen ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff); 4289caed0d9SLars-Peter Clausen if (ret) 4299caed0d9SLars-Peter Clausen dev_err(&indio_dev->dev, 4309caed0d9SLars-Peter Clausen "Could not power down device: %d\n", ret); 4319caed0d9SLars-Peter Clausen 4329caed0d9SLars-Peter Clausen return ret; 4339caed0d9SLars-Peter Clausen } 4349caed0d9SLars-Peter Clausen 4359caed0d9SLars-Peter Clausen static int adis16136_initial_setup(struct iio_dev *indio_dev) 4369caed0d9SLars-Peter Clausen { 4379caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 4389caed0d9SLars-Peter Clausen unsigned int device_id; 4399caed0d9SLars-Peter Clausen uint16_t prod_id; 4409caed0d9SLars-Peter Clausen int ret; 4419caed0d9SLars-Peter Clausen 4429caed0d9SLars-Peter Clausen ret = adis_initial_startup(&adis16136->adis); 4439caed0d9SLars-Peter Clausen if (ret) 4449caed0d9SLars-Peter Clausen return ret; 4459caed0d9SLars-Peter Clausen 4469caed0d9SLars-Peter Clausen ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID, 4479caed0d9SLars-Peter Clausen &prod_id); 4489caed0d9SLars-Peter Clausen if (ret) 4499caed0d9SLars-Peter Clausen return ret; 4509caed0d9SLars-Peter Clausen 451a106b474SIoana Ciornei ret = sscanf(indio_dev->name, "adis%u\n", &device_id); 452a106b474SIoana Ciornei if (ret != 1) 453a106b474SIoana Ciornei return -EINVAL; 4549caed0d9SLars-Peter Clausen 4559caed0d9SLars-Peter Clausen if (prod_id != device_id) 4569caed0d9SLars-Peter Clausen dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", 4579caed0d9SLars-Peter Clausen device_id, prod_id); 4589caed0d9SLars-Peter Clausen 4599caed0d9SLars-Peter Clausen return 0; 4609caed0d9SLars-Peter Clausen } 4619caed0d9SLars-Peter Clausen 4629caed0d9SLars-Peter Clausen static const char * const adis16136_status_error_msgs[] = { 4639caed0d9SLars-Peter Clausen [ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed", 4649caed0d9SLars-Peter Clausen [ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure", 4659caed0d9SLars-Peter Clausen [ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error", 4669caed0d9SLars-Peter Clausen [ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error", 4679caed0d9SLars-Peter Clausen }; 4689caed0d9SLars-Peter Clausen 4699caed0d9SLars-Peter Clausen static const struct adis_data adis16136_data = { 4709caed0d9SLars-Peter Clausen .diag_stat_reg = ADIS16136_REG_DIAG_STAT, 4719caed0d9SLars-Peter Clausen .glob_cmd_reg = ADIS16136_REG_GLOB_CMD, 4729caed0d9SLars-Peter Clausen .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, 4739caed0d9SLars-Peter Clausen 4749caed0d9SLars-Peter Clausen .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, 4759caed0d9SLars-Peter Clausen .startup_delay = 80, 4769caed0d9SLars-Peter Clausen 4779caed0d9SLars-Peter Clausen .read_delay = 10, 4789caed0d9SLars-Peter Clausen .write_delay = 10, 4799caed0d9SLars-Peter Clausen 4809caed0d9SLars-Peter Clausen .status_error_msgs = adis16136_status_error_msgs, 4819caed0d9SLars-Peter Clausen .status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) | 4829caed0d9SLars-Peter Clausen BIT(ADIS16136_DIAG_STAT_SPI_FAIL) | 4839caed0d9SLars-Peter Clausen BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) | 4849caed0d9SLars-Peter Clausen BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL), 4859caed0d9SLars-Peter Clausen }; 4869caed0d9SLars-Peter Clausen 4879caed0d9SLars-Peter Clausen enum adis16136_id { 4889caed0d9SLars-Peter Clausen ID_ADIS16133, 4899caed0d9SLars-Peter Clausen ID_ADIS16135, 4909caed0d9SLars-Peter Clausen ID_ADIS16136, 4915450c360SLars-Peter Clausen ID_ADIS16137, 4929caed0d9SLars-Peter Clausen }; 4939caed0d9SLars-Peter Clausen 494*380b107bSNuno Sá static const struct adis_timeout adis16133_timeouts = { 495*380b107bSNuno Sá .reset_ms = 75, 496*380b107bSNuno Sá .sw_reset_ms = 75, 497*380b107bSNuno Sá .self_test_ms = 50, 498*380b107bSNuno Sá }; 499*380b107bSNuno Sá 500*380b107bSNuno Sá static const struct adis_timeout adis16136_timeouts = { 501*380b107bSNuno Sá .reset_ms = 128, 502*380b107bSNuno Sá .sw_reset_ms = 75, 503*380b107bSNuno Sá .self_test_ms = 245, 504*380b107bSNuno Sá }; 505*380b107bSNuno Sá 5069caed0d9SLars-Peter Clausen static const struct adis16136_chip_info adis16136_chip_info[] = { 5079caed0d9SLars-Peter Clausen [ID_ADIS16133] = { 5089caed0d9SLars-Peter Clausen .precision = IIO_DEGREE_TO_RAD(1200), 5099caed0d9SLars-Peter Clausen .fullscale = 24000, 510*380b107bSNuno Sá .timeouts = &adis16133_timeouts, 5119caed0d9SLars-Peter Clausen }, 5129caed0d9SLars-Peter Clausen [ID_ADIS16135] = { 5139caed0d9SLars-Peter Clausen .precision = IIO_DEGREE_TO_RAD(300), 5149caed0d9SLars-Peter Clausen .fullscale = 24000, 515*380b107bSNuno Sá .timeouts = &adis16133_timeouts, 5169caed0d9SLars-Peter Clausen }, 5179caed0d9SLars-Peter Clausen [ID_ADIS16136] = { 5189caed0d9SLars-Peter Clausen .precision = IIO_DEGREE_TO_RAD(450), 5199caed0d9SLars-Peter Clausen .fullscale = 24623, 520*380b107bSNuno Sá .timeouts = &adis16136_timeouts, 5219caed0d9SLars-Peter Clausen }, 5225450c360SLars-Peter Clausen [ID_ADIS16137] = { 5235450c360SLars-Peter Clausen .precision = IIO_DEGREE_TO_RAD(1000), 5245450c360SLars-Peter Clausen .fullscale = 24609, 525*380b107bSNuno Sá .timeouts = &adis16136_timeouts, 5265450c360SLars-Peter Clausen }, 5279caed0d9SLars-Peter Clausen }; 5289caed0d9SLars-Peter Clausen 529*380b107bSNuno Sá static struct adis_data *adis16136_adis_data_alloc(struct adis16136 *st, 530*380b107bSNuno Sá struct device *dev) 531*380b107bSNuno Sá { 532*380b107bSNuno Sá struct adis_data *data; 533*380b107bSNuno Sá 534*380b107bSNuno Sá data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL); 535*380b107bSNuno Sá if (!data) 536*380b107bSNuno Sá return ERR_PTR(-ENOMEM); 537*380b107bSNuno Sá 538*380b107bSNuno Sá memcpy(data, &adis16136_data, sizeof(*data)); 539*380b107bSNuno Sá 540*380b107bSNuno Sá data->timeouts = st->chip_info->timeouts; 541*380b107bSNuno Sá 542*380b107bSNuno Sá return data; 543*380b107bSNuno Sá } 544*380b107bSNuno Sá 5459caed0d9SLars-Peter Clausen static int adis16136_probe(struct spi_device *spi) 5469caed0d9SLars-Peter Clausen { 5479caed0d9SLars-Peter Clausen const struct spi_device_id *id = spi_get_device_id(spi); 5489caed0d9SLars-Peter Clausen struct adis16136 *adis16136; 5499caed0d9SLars-Peter Clausen struct iio_dev *indio_dev; 550*380b107bSNuno Sá const struct adis_data *adis16136_data; 5519caed0d9SLars-Peter Clausen int ret; 5529caed0d9SLars-Peter Clausen 553c0ca6d31SSachin Kamat indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis16136)); 5549caed0d9SLars-Peter Clausen if (indio_dev == NULL) 5559caed0d9SLars-Peter Clausen return -ENOMEM; 5569caed0d9SLars-Peter Clausen 5579caed0d9SLars-Peter Clausen spi_set_drvdata(spi, indio_dev); 5589caed0d9SLars-Peter Clausen 5599caed0d9SLars-Peter Clausen adis16136 = iio_priv(indio_dev); 5609caed0d9SLars-Peter Clausen 5619caed0d9SLars-Peter Clausen adis16136->chip_info = &adis16136_chip_info[id->driver_data]; 5629caed0d9SLars-Peter Clausen indio_dev->dev.parent = &spi->dev; 5639caed0d9SLars-Peter Clausen indio_dev->name = spi_get_device_id(spi)->name; 5649caed0d9SLars-Peter Clausen indio_dev->channels = adis16136_channels; 5659caed0d9SLars-Peter Clausen indio_dev->num_channels = ARRAY_SIZE(adis16136_channels); 5669caed0d9SLars-Peter Clausen indio_dev->info = &adis16136_info; 5679caed0d9SLars-Peter Clausen indio_dev->modes = INDIO_DIRECT_MODE; 5689caed0d9SLars-Peter Clausen 569*380b107bSNuno Sá adis16136_data = adis16136_adis_data_alloc(adis16136, &spi->dev); 570*380b107bSNuno Sá if (IS_ERR(adis16136_data)) 571*380b107bSNuno Sá return PTR_ERR(adis16136_data); 572*380b107bSNuno Sá 573*380b107bSNuno Sá ret = adis_init(&adis16136->adis, indio_dev, spi, adis16136_data); 5749caed0d9SLars-Peter Clausen if (ret) 575c0ca6d31SSachin Kamat return ret; 5769caed0d9SLars-Peter Clausen 5779caed0d9SLars-Peter Clausen ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL); 5789caed0d9SLars-Peter Clausen if (ret) 579c0ca6d31SSachin Kamat return ret; 5809caed0d9SLars-Peter Clausen 5819caed0d9SLars-Peter Clausen ret = adis16136_initial_setup(indio_dev); 5829caed0d9SLars-Peter Clausen if (ret) 5839caed0d9SLars-Peter Clausen goto error_cleanup_buffer; 5849caed0d9SLars-Peter Clausen 5859caed0d9SLars-Peter Clausen ret = iio_device_register(indio_dev); 5869caed0d9SLars-Peter Clausen if (ret) 5879caed0d9SLars-Peter Clausen goto error_stop_device; 5889caed0d9SLars-Peter Clausen 5899caed0d9SLars-Peter Clausen adis16136_debugfs_init(indio_dev); 5909caed0d9SLars-Peter Clausen 5919caed0d9SLars-Peter Clausen return 0; 5929caed0d9SLars-Peter Clausen 5939caed0d9SLars-Peter Clausen error_stop_device: 5949caed0d9SLars-Peter Clausen adis16136_stop_device(indio_dev); 5959caed0d9SLars-Peter Clausen error_cleanup_buffer: 5969caed0d9SLars-Peter Clausen adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev); 5979caed0d9SLars-Peter Clausen return ret; 5989caed0d9SLars-Peter Clausen } 5999caed0d9SLars-Peter Clausen 6009caed0d9SLars-Peter Clausen static int adis16136_remove(struct spi_device *spi) 6019caed0d9SLars-Peter Clausen { 6029caed0d9SLars-Peter Clausen struct iio_dev *indio_dev = spi_get_drvdata(spi); 6039caed0d9SLars-Peter Clausen struct adis16136 *adis16136 = iio_priv(indio_dev); 6049caed0d9SLars-Peter Clausen 6059caed0d9SLars-Peter Clausen iio_device_unregister(indio_dev); 6069caed0d9SLars-Peter Clausen adis16136_stop_device(indio_dev); 6079caed0d9SLars-Peter Clausen 6089caed0d9SLars-Peter Clausen adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev); 6099caed0d9SLars-Peter Clausen 6109caed0d9SLars-Peter Clausen return 0; 6119caed0d9SLars-Peter Clausen } 6129caed0d9SLars-Peter Clausen 6139caed0d9SLars-Peter Clausen static const struct spi_device_id adis16136_ids[] = { 6149caed0d9SLars-Peter Clausen { "adis16133", ID_ADIS16133 }, 6159caed0d9SLars-Peter Clausen { "adis16135", ID_ADIS16135 }, 6169caed0d9SLars-Peter Clausen { "adis16136", ID_ADIS16136 }, 6175450c360SLars-Peter Clausen { "adis16137", ID_ADIS16137 }, 6189caed0d9SLars-Peter Clausen { } 6199caed0d9SLars-Peter Clausen }; 6209caed0d9SLars-Peter Clausen MODULE_DEVICE_TABLE(spi, adis16136_ids); 6219caed0d9SLars-Peter Clausen 6229caed0d9SLars-Peter Clausen static struct spi_driver adis16136_driver = { 6239caed0d9SLars-Peter Clausen .driver = { 6249caed0d9SLars-Peter Clausen .name = "adis16136", 6259caed0d9SLars-Peter Clausen }, 6269caed0d9SLars-Peter Clausen .id_table = adis16136_ids, 6279caed0d9SLars-Peter Clausen .probe = adis16136_probe, 6289caed0d9SLars-Peter Clausen .remove = adis16136_remove, 6299caed0d9SLars-Peter Clausen }; 6309caed0d9SLars-Peter Clausen module_spi_driver(adis16136_driver); 6319caed0d9SLars-Peter Clausen 6329caed0d9SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 6339caed0d9SLars-Peter Clausen MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver"); 6349caed0d9SLars-Peter Clausen MODULE_LICENSE("GPL v2"); 635