314 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer4 *5 * Copyright 2010 Analog Devices Inc.6 */7 8#include <linux/device.h>9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/spi/spi.h>12 13#include <linux/iio/iio.h>14#include <linux/iio/imu/adis.h>15 16#define ADIS16209_STARTUP_DELAY_MS 22017#define ADIS16209_FLASH_CNT_REG 0x0018 19/* Data Output Register Definitions */20#define ADIS16209_SUPPLY_OUT_REG 0x0221#define ADIS16209_XACCL_OUT_REG 0x0422#define ADIS16209_YACCL_OUT_REG 0x0623/* Output, auxiliary ADC input */24#define ADIS16209_AUX_ADC_REG 0x0825/* Output, temperature */26#define ADIS16209_TEMP_OUT_REG 0x0A27/* Output, +/- 90 degrees X-axis inclination */28#define ADIS16209_XINCL_OUT_REG 0x0C29#define ADIS16209_YINCL_OUT_REG 0x0E30/* Output, +/-180 vertical rotational position */31#define ADIS16209_ROT_OUT_REG 0x1032 33/*34 * Calibration Register Definitions.35 * Acceleration, inclination or rotation offset null.36 */37#define ADIS16209_XACCL_NULL_REG 0x1238#define ADIS16209_YACCL_NULL_REG 0x1439#define ADIS16209_XINCL_NULL_REG 0x1640#define ADIS16209_YINCL_NULL_REG 0x1841#define ADIS16209_ROT_NULL_REG 0x1A42 43/* Alarm Register Definitions */44#define ADIS16209_ALM_MAG1_REG 0x2045#define ADIS16209_ALM_MAG2_REG 0x2246#define ADIS16209_ALM_SMPL1_REG 0x2447#define ADIS16209_ALM_SMPL2_REG 0x2648#define ADIS16209_ALM_CTRL_REG 0x2849 50#define ADIS16209_AUX_DAC_REG 0x3051#define ADIS16209_GPIO_CTRL_REG 0x3252#define ADIS16209_SMPL_PRD_REG 0x3653#define ADIS16209_AVG_CNT_REG 0x3854#define ADIS16209_SLP_CNT_REG 0x3A55 56#define ADIS16209_MSC_CTRL_REG 0x3457#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10)58#define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8)59#define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2)60/* Data-ready polarity: 1 = active high, 0 = active low */61#define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1)62#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0)63 64#define ADIS16209_STAT_REG 0x3C65#define ADIS16209_STAT_ALARM2 BIT(9)66#define ADIS16209_STAT_ALARM1 BIT(8)67#define ADIS16209_STAT_SELFTEST_FAIL_BIT 568#define ADIS16209_STAT_SPI_FAIL_BIT 369#define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 270/* Power supply above 3.625 V */71#define ADIS16209_STAT_POWER_HIGH_BIT 172/* Power supply below 2.975 V */73#define ADIS16209_STAT_POWER_LOW_BIT 074 75#define ADIS16209_CMD_REG 0x3E76#define ADIS16209_CMD_SW_RESET BIT(7)77#define ADIS16209_CMD_CLEAR_STAT BIT(4)78#define ADIS16209_CMD_FACTORY_CAL BIT(1)79 80#define ADIS16209_ERROR_ACTIVE BIT(14)81 82enum adis16209_scan {83 ADIS16209_SCAN_SUPPLY,84 ADIS16209_SCAN_ACC_X,85 ADIS16209_SCAN_ACC_Y,86 ADIS16209_SCAN_AUX_ADC,87 ADIS16209_SCAN_TEMP,88 ADIS16209_SCAN_INCLI_X,89 ADIS16209_SCAN_INCLI_Y,90 ADIS16209_SCAN_ROT,91};92 93static const u8 adis16209_addresses[8][1] = {94 [ADIS16209_SCAN_SUPPLY] = { },95 [ADIS16209_SCAN_AUX_ADC] = { },96 [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL_REG },97 [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL_REG },98 [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL_REG },99 [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL_REG },100 [ADIS16209_SCAN_ROT] = { },101 [ADIS16209_SCAN_TEMP] = { },102};103 104static int adis16209_write_raw(struct iio_dev *indio_dev,105 struct iio_chan_spec const *chan,106 int val,107 int val2,108 long mask)109{110 struct adis *st = iio_priv(indio_dev);111 int m;112 113 if (mask != IIO_CHAN_INFO_CALIBBIAS)114 return -EINVAL;115 116 switch (chan->type) {117 case IIO_ACCEL:118 case IIO_INCLI:119 m = GENMASK(13, 0);120 break;121 default:122 return -EINVAL;123 }124 125 return adis_write_reg_16(st, adis16209_addresses[chan->scan_index][0],126 val & m);127}128 129static int adis16209_read_raw(struct iio_dev *indio_dev,130 struct iio_chan_spec const *chan,131 int *val, int *val2,132 long mask)133{134 struct adis *st = iio_priv(indio_dev);135 int ret;136 int bits;137 u8 addr;138 s16 val16;139 140 switch (mask) {141 case IIO_CHAN_INFO_RAW:142 return adis_single_conversion(indio_dev, chan,143 ADIS16209_ERROR_ACTIVE, val);144 case IIO_CHAN_INFO_SCALE:145 switch (chan->type) {146 case IIO_VOLTAGE:147 *val = 0;148 switch (chan->channel) {149 case 0:150 *val2 = 305180; /* 0.30518 mV */151 break;152 case 1:153 *val2 = 610500; /* 0.6105 mV */154 break;155 default:156 return -EINVAL;157 }158 return IIO_VAL_INT_PLUS_MICRO;159 case IIO_TEMP:160 *val = -470;161 *val2 = 0;162 return IIO_VAL_INT_PLUS_MICRO;163 case IIO_ACCEL:164 /*165 * IIO base unit for sensitivity of accelerometer166 * is milli g.167 * 1 LSB represents 0.244 mg.168 */169 *val = 0;170 *val2 = IIO_G_TO_M_S_2(244140);171 return IIO_VAL_INT_PLUS_NANO;172 case IIO_INCLI:173 case IIO_ROT:174 /*175 * IIO base units for rotation are degrees.176 * 1 LSB represents 0.025 milli degrees.177 */178 *val = 0;179 *val2 = 25000;180 return IIO_VAL_INT_PLUS_MICRO;181 default:182 return -EINVAL;183 }184 break;185 case IIO_CHAN_INFO_OFFSET:186 /*187 * The raw ADC value is 0x4FE when the temperature188 * is 45 degrees and the scale factor per milli189 * degree celcius is -470.190 */191 *val = 25000 / -470 - 0x4FE;192 return IIO_VAL_INT;193 case IIO_CHAN_INFO_CALIBBIAS:194 switch (chan->type) {195 case IIO_ACCEL:196 bits = 14;197 break;198 default:199 return -EINVAL;200 }201 addr = adis16209_addresses[chan->scan_index][0];202 ret = adis_read_reg_16(st, addr, &val16);203 if (ret)204 return ret;205 206 *val = sign_extend32(val16, bits - 1);207 return IIO_VAL_INT;208 }209 return -EINVAL;210}211 212static const struct iio_chan_spec adis16209_channels[] = {213 ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT_REG, ADIS16209_SCAN_SUPPLY,214 0, 14),215 ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT_REG, ADIS16209_SCAN_TEMP, 0, 12),216 ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT_REG, ADIS16209_SCAN_ACC_X,217 BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),218 ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT_REG, ADIS16209_SCAN_ACC_Y,219 BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),220 ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC_REG, ADIS16209_SCAN_AUX_ADC, 0, 12),221 ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT_REG, ADIS16209_SCAN_INCLI_X,222 0, 0, 14),223 ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT_REG, ADIS16209_SCAN_INCLI_Y,224 0, 0, 14),225 ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT_REG, ADIS16209_SCAN_ROT, 0, 0, 14),226 IIO_CHAN_SOFT_TIMESTAMP(8)227};228 229static const struct iio_info adis16209_info = {230 .read_raw = adis16209_read_raw,231 .write_raw = adis16209_write_raw,232 .update_scan_mode = adis_update_scan_mode,233};234 235static const char * const adis16209_status_error_msgs[] = {236 [ADIS16209_STAT_SELFTEST_FAIL_BIT] = "Self test failure",237 [ADIS16209_STAT_SPI_FAIL_BIT] = "SPI failure",238 [ADIS16209_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",239 [ADIS16209_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",240 [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",241};242 243static const struct adis_timeout adis16209_timeouts = {244 .reset_ms = ADIS16209_STARTUP_DELAY_MS,245 .self_test_ms = ADIS16209_STARTUP_DELAY_MS,246 .sw_reset_ms = ADIS16209_STARTUP_DELAY_MS,247};248 249static const struct adis_data adis16209_data = {250 .read_delay = 30,251 .msc_ctrl_reg = ADIS16209_MSC_CTRL_REG,252 .glob_cmd_reg = ADIS16209_CMD_REG,253 .diag_stat_reg = ADIS16209_STAT_REG,254 255 .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,256 .self_test_reg = ADIS16209_MSC_CTRL_REG,257 .self_test_no_autoclear = true,258 .timeouts = &adis16209_timeouts,259 260 .status_error_msgs = adis16209_status_error_msgs,261 .status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) |262 BIT(ADIS16209_STAT_SPI_FAIL_BIT) |263 BIT(ADIS16209_STAT_FLASH_UPT_FAIL_BIT) |264 BIT(ADIS16209_STAT_POWER_HIGH_BIT) |265 BIT(ADIS16209_STAT_POWER_LOW_BIT),266};267 268static int adis16209_probe(struct spi_device *spi)269{270 struct iio_dev *indio_dev;271 struct adis *st;272 int ret;273 274 indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));275 if (!indio_dev)276 return -ENOMEM;277 278 st = iio_priv(indio_dev);279 280 indio_dev->name = spi->dev.driver->name;281 indio_dev->info = &adis16209_info;282 indio_dev->channels = adis16209_channels;283 indio_dev->num_channels = ARRAY_SIZE(adis16209_channels);284 indio_dev->modes = INDIO_DIRECT_MODE;285 286 ret = adis_init(st, indio_dev, spi, &adis16209_data);287 if (ret)288 return ret;289 290 ret = devm_adis_setup_buffer_and_trigger(st, indio_dev, NULL);291 if (ret)292 return ret;293 294 ret = __adis_initial_startup(st);295 if (ret)296 return ret;297 298 return devm_iio_device_register(&spi->dev, indio_dev);299}300 301static struct spi_driver adis16209_driver = {302 .driver = {303 .name = "adis16209",304 },305 .probe = adis16209_probe,306};307module_spi_driver(adis16209_driver);308 309MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");310MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer");311MODULE_LICENSE("GPL v2");312MODULE_ALIAS("spi:adis16209");313MODULE_IMPORT_NS(IIO_ADISLIB);314