180503b23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2ec04cb04SLars-Peter Clausen /* 3ec04cb04SLars-Peter Clausen * Common library for ADIS16XXX devices 4ec04cb04SLars-Peter Clausen * 5ec04cb04SLars-Peter Clausen * Copyright 2012 Analog Devices Inc. 6ec04cb04SLars-Peter Clausen * Author: Lars-Peter Clausen <lars@metafoo.de> 7ec04cb04SLars-Peter Clausen */ 8ec04cb04SLars-Peter Clausen 9ec04cb04SLars-Peter Clausen #include <linux/delay.h> 10ecb010d4SNuno Sá #include <linux/gpio/consumer.h> 11ec04cb04SLars-Peter Clausen #include <linux/mutex.h> 12ec04cb04SLars-Peter Clausen #include <linux/device.h> 13ec04cb04SLars-Peter Clausen #include <linux/kernel.h> 14ec04cb04SLars-Peter Clausen #include <linux/spi/spi.h> 15ec04cb04SLars-Peter Clausen #include <linux/slab.h> 16ec04cb04SLars-Peter Clausen #include <linux/sysfs.h> 17ec04cb04SLars-Peter Clausen #include <linux/module.h> 18ec04cb04SLars-Peter Clausen #include <asm/unaligned.h> 19ec04cb04SLars-Peter Clausen 20ec04cb04SLars-Peter Clausen #include <linux/iio/iio.h> 21ec04cb04SLars-Peter Clausen #include <linux/iio/sysfs.h> 22ec04cb04SLars-Peter Clausen #include <linux/iio/buffer.h> 23ec04cb04SLars-Peter Clausen #include <linux/iio/imu/adis.h> 24ec04cb04SLars-Peter Clausen 25ec04cb04SLars-Peter Clausen #define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2) 26ec04cb04SLars-Peter Clausen #define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH BIT(1) 27ec04cb04SLars-Peter Clausen #define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0) 28ec04cb04SLars-Peter Clausen #define ADIS_GLOB_CMD_SW_RESET BIT(7) 29ec04cb04SLars-Peter Clausen 30770d4656SAlexandru Ardelean /** 31770d4656SAlexandru Ardelean * __adis_write_reg() - write N bytes to register (unlocked version) 32770d4656SAlexandru Ardelean * @adis: The adis device 33770d4656SAlexandru Ardelean * @reg: The address of the lower of the two registers 34770d4656SAlexandru Ardelean * @value: The value to write to device (up to 4 bytes) 35770d4656SAlexandru Ardelean * @size: The size of the @value (in bytes) 36770d4656SAlexandru Ardelean */ 37770d4656SAlexandru Ardelean int __adis_write_reg(struct adis *adis, unsigned int reg, 3857a1228aSLars-Peter Clausen unsigned int value, unsigned int size) 39ec04cb04SLars-Peter Clausen { 40484a0bf0SLars-Peter Clausen unsigned int page = reg / ADIS_PAGE_SIZE; 4157a1228aSLars-Peter Clausen int ret, i; 42ec04cb04SLars-Peter Clausen struct spi_message msg; 43ec04cb04SLars-Peter Clausen struct spi_transfer xfers[] = { 44ec04cb04SLars-Peter Clausen { 45ec04cb04SLars-Peter Clausen .tx_buf = adis->tx, 46ec04cb04SLars-Peter Clausen .bits_per_word = 8, 47ec04cb04SLars-Peter Clausen .len = 2, 48ec04cb04SLars-Peter Clausen .cs_change = 1, 49a4e6f40cSAlexandru Ardelean .delay.value = adis->data->write_delay, 50a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 51329f0dacSAlexandru Ardelean .cs_change_delay.value = adis->data->cs_change_delay, 52329f0dacSAlexandru Ardelean .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, 53ec04cb04SLars-Peter Clausen }, { 54ec04cb04SLars-Peter Clausen .tx_buf = adis->tx + 2, 55ec04cb04SLars-Peter Clausen .bits_per_word = 8, 56ec04cb04SLars-Peter Clausen .len = 2, 5757a1228aSLars-Peter Clausen .cs_change = 1, 58a4e6f40cSAlexandru Ardelean .delay.value = adis->data->write_delay, 59a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 60329f0dacSAlexandru Ardelean .cs_change_delay.value = adis->data->cs_change_delay, 61329f0dacSAlexandru Ardelean .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, 6257a1228aSLars-Peter Clausen }, { 6357a1228aSLars-Peter Clausen .tx_buf = adis->tx + 4, 6457a1228aSLars-Peter Clausen .bits_per_word = 8, 6557a1228aSLars-Peter Clausen .len = 2, 6657a1228aSLars-Peter Clausen .cs_change = 1, 67a4e6f40cSAlexandru Ardelean .delay.value = adis->data->write_delay, 68a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 69329f0dacSAlexandru Ardelean .cs_change_delay.value = adis->data->cs_change_delay, 70329f0dacSAlexandru Ardelean .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, 7157a1228aSLars-Peter Clausen }, { 7257a1228aSLars-Peter Clausen .tx_buf = adis->tx + 6, 7357a1228aSLars-Peter Clausen .bits_per_word = 8, 7457a1228aSLars-Peter Clausen .len = 2, 75a4e6f40cSAlexandru Ardelean .delay.value = adis->data->write_delay, 76a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 77484a0bf0SLars-Peter Clausen }, { 78484a0bf0SLars-Peter Clausen .tx_buf = adis->tx + 8, 79484a0bf0SLars-Peter Clausen .bits_per_word = 8, 80484a0bf0SLars-Peter Clausen .len = 2, 81a4e6f40cSAlexandru Ardelean .delay.value = adis->data->write_delay, 82a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 83ec04cb04SLars-Peter Clausen }, 84ec04cb04SLars-Peter Clausen }; 85ec04cb04SLars-Peter Clausen 86ec04cb04SLars-Peter Clausen spi_message_init(&msg); 87484a0bf0SLars-Peter Clausen 88484a0bf0SLars-Peter Clausen if (adis->current_page != page) { 89484a0bf0SLars-Peter Clausen adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); 90484a0bf0SLars-Peter Clausen adis->tx[1] = page; 91484a0bf0SLars-Peter Clausen spi_message_add_tail(&xfers[0], &msg); 92484a0bf0SLars-Peter Clausen } 93484a0bf0SLars-Peter Clausen 9457a1228aSLars-Peter Clausen switch (size) { 9557a1228aSLars-Peter Clausen case 4: 96484a0bf0SLars-Peter Clausen adis->tx[8] = ADIS_WRITE_REG(reg + 3); 97484a0bf0SLars-Peter Clausen adis->tx[9] = (value >> 24) & 0xff; 98484a0bf0SLars-Peter Clausen adis->tx[6] = ADIS_WRITE_REG(reg + 2); 99484a0bf0SLars-Peter Clausen adis->tx[7] = (value >> 16) & 0xff; 10082d65f9dSGustavo A. R. Silva /* fall through */ 10157a1228aSLars-Peter Clausen case 2: 102484a0bf0SLars-Peter Clausen adis->tx[4] = ADIS_WRITE_REG(reg + 1); 103484a0bf0SLars-Peter Clausen adis->tx[5] = (value >> 8) & 0xff; 10482d65f9dSGustavo A. R. Silva /* fall through */ 10557a1228aSLars-Peter Clausen case 1: 106484a0bf0SLars-Peter Clausen adis->tx[2] = ADIS_WRITE_REG(reg); 107484a0bf0SLars-Peter Clausen adis->tx[3] = value & 0xff; 10857a1228aSLars-Peter Clausen break; 10957a1228aSLars-Peter Clausen default: 110770d4656SAlexandru Ardelean return -EINVAL; 11157a1228aSLars-Peter Clausen } 11257a1228aSLars-Peter Clausen 113484a0bf0SLars-Peter Clausen xfers[size].cs_change = 0; 11457a1228aSLars-Peter Clausen 115484a0bf0SLars-Peter Clausen for (i = 1; i <= size; i++) 11657a1228aSLars-Peter Clausen spi_message_add_tail(&xfers[i], &msg); 11757a1228aSLars-Peter Clausen 118ec04cb04SLars-Peter Clausen ret = spi_sync(adis->spi, &msg); 11957a1228aSLars-Peter Clausen if (ret) { 12057a1228aSLars-Peter Clausen dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n", 12157a1228aSLars-Peter Clausen reg, ret); 122484a0bf0SLars-Peter Clausen } else { 123484a0bf0SLars-Peter Clausen adis->current_page = page; 12457a1228aSLars-Peter Clausen } 12557a1228aSLars-Peter Clausen 126ec04cb04SLars-Peter Clausen return ret; 127ec04cb04SLars-Peter Clausen } 128770d4656SAlexandru Ardelean EXPORT_SYMBOL_GPL(__adis_write_reg); 129ec04cb04SLars-Peter Clausen 130ec04cb04SLars-Peter Clausen /** 131770d4656SAlexandru Ardelean * __adis_read_reg() - read N bytes from register (unlocked version) 132ec04cb04SLars-Peter Clausen * @adis: The adis device 133ec04cb04SLars-Peter Clausen * @reg: The address of the lower of the two registers 134ec04cb04SLars-Peter Clausen * @val: The value read back from the device 135770d4656SAlexandru Ardelean * @size: The size of the @val buffer 136ec04cb04SLars-Peter Clausen */ 137770d4656SAlexandru Ardelean int __adis_read_reg(struct adis *adis, unsigned int reg, 13857a1228aSLars-Peter Clausen unsigned int *val, unsigned int size) 139ec04cb04SLars-Peter Clausen { 140484a0bf0SLars-Peter Clausen unsigned int page = reg / ADIS_PAGE_SIZE; 141ec04cb04SLars-Peter Clausen struct spi_message msg; 142ec04cb04SLars-Peter Clausen int ret; 143ec04cb04SLars-Peter Clausen struct spi_transfer xfers[] = { 144ec04cb04SLars-Peter Clausen { 145ec04cb04SLars-Peter Clausen .tx_buf = adis->tx, 146ec04cb04SLars-Peter Clausen .bits_per_word = 8, 147ec04cb04SLars-Peter Clausen .len = 2, 148ec04cb04SLars-Peter Clausen .cs_change = 1, 149a4e6f40cSAlexandru Ardelean .delay.value = adis->data->write_delay, 150a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 151329f0dacSAlexandru Ardelean .cs_change_delay.value = adis->data->cs_change_delay, 152329f0dacSAlexandru Ardelean .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, 153ec04cb04SLars-Peter Clausen }, { 15457a1228aSLars-Peter Clausen .tx_buf = adis->tx + 2, 155484a0bf0SLars-Peter Clausen .bits_per_word = 8, 156484a0bf0SLars-Peter Clausen .len = 2, 157484a0bf0SLars-Peter Clausen .cs_change = 1, 158a4e6f40cSAlexandru Ardelean .delay.value = adis->data->read_delay, 159a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 160329f0dacSAlexandru Ardelean .cs_change_delay.value = adis->data->cs_change_delay, 161329f0dacSAlexandru Ardelean .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, 162484a0bf0SLars-Peter Clausen }, { 163484a0bf0SLars-Peter Clausen .tx_buf = adis->tx + 4, 164ec04cb04SLars-Peter Clausen .rx_buf = adis->rx, 165ec04cb04SLars-Peter Clausen .bits_per_word = 8, 166ec04cb04SLars-Peter Clausen .len = 2, 16757a1228aSLars-Peter Clausen .cs_change = 1, 168a4e6f40cSAlexandru Ardelean .delay.value = adis->data->read_delay, 169a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 170329f0dacSAlexandru Ardelean .cs_change_delay.value = adis->data->cs_change_delay, 171329f0dacSAlexandru Ardelean .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, 17257a1228aSLars-Peter Clausen }, { 17357a1228aSLars-Peter Clausen .rx_buf = adis->rx + 2, 17457a1228aSLars-Peter Clausen .bits_per_word = 8, 17557a1228aSLars-Peter Clausen .len = 2, 176a4e6f40cSAlexandru Ardelean .delay.value = adis->data->read_delay, 177a4e6f40cSAlexandru Ardelean .delay.unit = SPI_DELAY_UNIT_USECS, 178ec04cb04SLars-Peter Clausen }, 179ec04cb04SLars-Peter Clausen }; 180ec04cb04SLars-Peter Clausen 181ec04cb04SLars-Peter Clausen spi_message_init(&msg); 18257a1228aSLars-Peter Clausen 183484a0bf0SLars-Peter Clausen if (adis->current_page != page) { 184484a0bf0SLars-Peter Clausen adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); 185484a0bf0SLars-Peter Clausen adis->tx[1] = page; 186484a0bf0SLars-Peter Clausen spi_message_add_tail(&xfers[0], &msg); 187484a0bf0SLars-Peter Clausen } 188484a0bf0SLars-Peter Clausen 18957a1228aSLars-Peter Clausen switch (size) { 19057a1228aSLars-Peter Clausen case 4: 191484a0bf0SLars-Peter Clausen adis->tx[2] = ADIS_READ_REG(reg + 2); 19257a1228aSLars-Peter Clausen adis->tx[3] = 0; 193ec04cb04SLars-Peter Clausen spi_message_add_tail(&xfers[1], &msg); 19482d65f9dSGustavo A. R. Silva /* fall through */ 195484a0bf0SLars-Peter Clausen case 2: 196484a0bf0SLars-Peter Clausen adis->tx[4] = ADIS_READ_REG(reg); 197484a0bf0SLars-Peter Clausen adis->tx[5] = 0; 19857a1228aSLars-Peter Clausen spi_message_add_tail(&xfers[2], &msg); 199484a0bf0SLars-Peter Clausen spi_message_add_tail(&xfers[3], &msg); 20057a1228aSLars-Peter Clausen break; 20157a1228aSLars-Peter Clausen default: 202770d4656SAlexandru Ardelean return -EINVAL; 20357a1228aSLars-Peter Clausen } 20457a1228aSLars-Peter Clausen 205ec04cb04SLars-Peter Clausen ret = spi_sync(adis->spi, &msg); 206ec04cb04SLars-Peter Clausen if (ret) { 20757a1228aSLars-Peter Clausen dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n", 208ec04cb04SLars-Peter Clausen reg, ret); 209770d4656SAlexandru Ardelean return ret; 210484a0bf0SLars-Peter Clausen } else { 211484a0bf0SLars-Peter Clausen adis->current_page = page; 212ec04cb04SLars-Peter Clausen } 213ec04cb04SLars-Peter Clausen 21457a1228aSLars-Peter Clausen switch (size) { 21557a1228aSLars-Peter Clausen case 4: 21657a1228aSLars-Peter Clausen *val = get_unaligned_be32(adis->rx); 21757a1228aSLars-Peter Clausen break; 21857a1228aSLars-Peter Clausen case 2: 21957a1228aSLars-Peter Clausen *val = get_unaligned_be16(adis->rx + 2); 22057a1228aSLars-Peter Clausen break; 22157a1228aSLars-Peter Clausen } 22257a1228aSLars-Peter Clausen 223ec04cb04SLars-Peter Clausen return ret; 224ec04cb04SLars-Peter Clausen } 225770d4656SAlexandru Ardelean EXPORT_SYMBOL_GPL(__adis_read_reg); 226ec04cb04SLars-Peter Clausen 22778026a6fSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 22878026a6fSLars-Peter Clausen 22978026a6fSLars-Peter Clausen int adis_debugfs_reg_access(struct iio_dev *indio_dev, 23078026a6fSLars-Peter Clausen unsigned int reg, unsigned int writeval, unsigned int *readval) 23178026a6fSLars-Peter Clausen { 23278026a6fSLars-Peter Clausen struct adis *adis = iio_device_get_drvdata(indio_dev); 23378026a6fSLars-Peter Clausen 23478026a6fSLars-Peter Clausen if (readval) { 23578026a6fSLars-Peter Clausen uint16_t val16; 23678026a6fSLars-Peter Clausen int ret; 23778026a6fSLars-Peter Clausen 23878026a6fSLars-Peter Clausen ret = adis_read_reg_16(adis, reg, &val16); 23938262c01SAlexandru Ardelean if (ret == 0) 24078026a6fSLars-Peter Clausen *readval = val16; 24178026a6fSLars-Peter Clausen 24278026a6fSLars-Peter Clausen return ret; 24378026a6fSLars-Peter Clausen } else { 24478026a6fSLars-Peter Clausen return adis_write_reg_16(adis, reg, writeval); 24578026a6fSLars-Peter Clausen } 24678026a6fSLars-Peter Clausen } 24778026a6fSLars-Peter Clausen EXPORT_SYMBOL(adis_debugfs_reg_access); 24878026a6fSLars-Peter Clausen 24978026a6fSLars-Peter Clausen #endif 25078026a6fSLars-Peter Clausen 251ec04cb04SLars-Peter Clausen /** 252ec04cb04SLars-Peter Clausen * adis_enable_irq() - Enable or disable data ready IRQ 253ec04cb04SLars-Peter Clausen * @adis: The adis device 254ec04cb04SLars-Peter Clausen * @enable: Whether to enable the IRQ 255ec04cb04SLars-Peter Clausen * 256ec04cb04SLars-Peter Clausen * Returns 0 on success, negative error code otherwise 257ec04cb04SLars-Peter Clausen */ 258ec04cb04SLars-Peter Clausen int adis_enable_irq(struct adis *adis, bool enable) 259ec04cb04SLars-Peter Clausen { 260ec04cb04SLars-Peter Clausen int ret = 0; 261ec04cb04SLars-Peter Clausen uint16_t msc; 262ec04cb04SLars-Peter Clausen 263100bfa38SAlexandru Ardelean mutex_lock(&adis->state_lock); 2642f3abe6cSLars-Peter Clausen 265100bfa38SAlexandru Ardelean if (adis->data->enable_irq) { 266100bfa38SAlexandru Ardelean ret = adis->data->enable_irq(adis, enable); 267100bfa38SAlexandru Ardelean goto out_unlock; 268100bfa38SAlexandru Ardelean } 269100bfa38SAlexandru Ardelean 270100bfa38SAlexandru Ardelean ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); 271ec04cb04SLars-Peter Clausen if (ret) 272100bfa38SAlexandru Ardelean goto out_unlock; 273ec04cb04SLars-Peter Clausen 274ec04cb04SLars-Peter Clausen msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH; 275ec04cb04SLars-Peter Clausen msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2; 276ec04cb04SLars-Peter Clausen if (enable) 277ec04cb04SLars-Peter Clausen msc |= ADIS_MSC_CTRL_DATA_RDY_EN; 278ec04cb04SLars-Peter Clausen else 279ec04cb04SLars-Peter Clausen msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN; 280ec04cb04SLars-Peter Clausen 281100bfa38SAlexandru Ardelean ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc); 282ec04cb04SLars-Peter Clausen 283100bfa38SAlexandru Ardelean out_unlock: 284100bfa38SAlexandru Ardelean mutex_unlock(&adis->state_lock); 285ec04cb04SLars-Peter Clausen return ret; 286ec04cb04SLars-Peter Clausen } 287ec04cb04SLars-Peter Clausen EXPORT_SYMBOL(adis_enable_irq); 288ec04cb04SLars-Peter Clausen 289ec04cb04SLars-Peter Clausen /** 2906a4d6a7dSAlexandru Ardelean * __adis_check_status() - Check the device for error conditions (unlocked) 291ec04cb04SLars-Peter Clausen * @adis: The adis device 292ec04cb04SLars-Peter Clausen * 293ec04cb04SLars-Peter Clausen * Returns 0 on success, a negative error code otherwise 294ec04cb04SLars-Peter Clausen */ 2956a4d6a7dSAlexandru Ardelean int __adis_check_status(struct adis *adis) 296ec04cb04SLars-Peter Clausen { 297ec04cb04SLars-Peter Clausen uint16_t status; 298ec04cb04SLars-Peter Clausen int ret; 299ec04cb04SLars-Peter Clausen int i; 300ec04cb04SLars-Peter Clausen 3016a4d6a7dSAlexandru Ardelean ret = __adis_read_reg_16(adis, adis->data->diag_stat_reg, &status); 3026a39ab3bSAlexandru Ardelean if (ret) 303ec04cb04SLars-Peter Clausen return ret; 304ec04cb04SLars-Peter Clausen 305ec04cb04SLars-Peter Clausen status &= adis->data->status_error_mask; 306ec04cb04SLars-Peter Clausen 307ec04cb04SLars-Peter Clausen if (status == 0) 308ec04cb04SLars-Peter Clausen return 0; 309ec04cb04SLars-Peter Clausen 310ec04cb04SLars-Peter Clausen for (i = 0; i < 16; ++i) { 311ec04cb04SLars-Peter Clausen if (status & BIT(i)) { 312ec04cb04SLars-Peter Clausen dev_err(&adis->spi->dev, "%s.\n", 313ec04cb04SLars-Peter Clausen adis->data->status_error_msgs[i]); 314ec04cb04SLars-Peter Clausen } 315ec04cb04SLars-Peter Clausen } 316ec04cb04SLars-Peter Clausen 317ec04cb04SLars-Peter Clausen return -EIO; 318ec04cb04SLars-Peter Clausen } 3196a4d6a7dSAlexandru Ardelean EXPORT_SYMBOL_GPL(__adis_check_status); 320ec04cb04SLars-Peter Clausen 321ec04cb04SLars-Peter Clausen /** 322762ab093SAlexandru Ardelean * __adis_reset() - Reset the device (unlocked version) 323ec04cb04SLars-Peter Clausen * @adis: The adis device 324ec04cb04SLars-Peter Clausen * 325ec04cb04SLars-Peter Clausen * Returns 0 on success, a negative error code otherwise 326ec04cb04SLars-Peter Clausen */ 327762ab093SAlexandru Ardelean int __adis_reset(struct adis *adis) 328ec04cb04SLars-Peter Clausen { 329ec04cb04SLars-Peter Clausen int ret; 330380b107bSNuno Sá const struct adis_timeout *timeouts = adis->data->timeouts; 331ec04cb04SLars-Peter Clausen 332762ab093SAlexandru Ardelean ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg, 333ec04cb04SLars-Peter Clausen ADIS_GLOB_CMD_SW_RESET); 334380b107bSNuno Sá if (ret) { 335ec04cb04SLars-Peter Clausen dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret); 336ec04cb04SLars-Peter Clausen return ret; 337ec04cb04SLars-Peter Clausen } 338380b107bSNuno Sá 339380b107bSNuno Sá msleep(timeouts->sw_reset_ms); 340380b107bSNuno Sá 341380b107bSNuno Sá return 0; 342380b107bSNuno Sá } 343762ab093SAlexandru Ardelean EXPORT_SYMBOL_GPL(__adis_reset); 344ec04cb04SLars-Peter Clausen 345ec04cb04SLars-Peter Clausen static int adis_self_test(struct adis *adis) 346ec04cb04SLars-Peter Clausen { 347ec04cb04SLars-Peter Clausen int ret; 348380b107bSNuno Sá const struct adis_timeout *timeouts = adis->data->timeouts; 349ec04cb04SLars-Peter Clausen 350fdcf6bbbSNuno Sá ret = __adis_write_reg_16(adis, adis->data->self_test_reg, 351ec04cb04SLars-Peter Clausen adis->data->self_test_mask); 352ec04cb04SLars-Peter Clausen if (ret) { 353ec04cb04SLars-Peter Clausen dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n", 354ec04cb04SLars-Peter Clausen ret); 355ec04cb04SLars-Peter Clausen return ret; 356ec04cb04SLars-Peter Clausen } 357ec04cb04SLars-Peter Clausen 358380b107bSNuno Sá msleep(timeouts->self_test_ms); 359ec04cb04SLars-Peter Clausen 360cb5a07f1SAlexandru Ardelean ret = __adis_check_status(adis); 361af8a4127SLars-Peter Clausen 362af8a4127SLars-Peter Clausen if (adis->data->self_test_no_autoclear) 363fdcf6bbbSNuno Sá __adis_write_reg_16(adis, adis->data->self_test_reg, 0x00); 364af8a4127SLars-Peter Clausen 365af8a4127SLars-Peter Clausen return ret; 366ec04cb04SLars-Peter Clausen } 367ec04cb04SLars-Peter Clausen 368ec04cb04SLars-Peter Clausen /** 3693f17ada8SAlexandru Ardelean * __adis_initial_startup() - Device initial setup 370ec04cb04SLars-Peter Clausen * @adis: The adis device 371ec04cb04SLars-Peter Clausen * 372ecb010d4SNuno Sá * The function performs a HW reset via a reset pin that should be specified 373ecb010d4SNuno Sá * via GPIOLIB. If no pin is configured a SW reset will be performed. 374ecb010d4SNuno Sá * The RST pin for the ADIS devices should be configured as ACTIVE_LOW. 375ecb010d4SNuno Sá * 3761fd45670SAlexandru Ardelean * After the self-test operation is performed, the function will also check 3771fd45670SAlexandru Ardelean * that the product ID is as expected. This assumes that drivers providing 3781fd45670SAlexandru Ardelean * 'prod_id_reg' will also provide the 'prod_id'. 3791fd45670SAlexandru Ardelean * 380ec04cb04SLars-Peter Clausen * Returns 0 if the device is operational, a negative error code otherwise. 381ec04cb04SLars-Peter Clausen * 382ec04cb04SLars-Peter Clausen * This function should be called early on in the device initialization sequence 383ec04cb04SLars-Peter Clausen * to ensure that the device is in a sane and known state and that it is usable. 384ec04cb04SLars-Peter Clausen */ 3853f17ada8SAlexandru Ardelean int __adis_initial_startup(struct adis *adis) 386ec04cb04SLars-Peter Clausen { 387ecb010d4SNuno Sá const struct adis_timeout *timeouts = adis->data->timeouts; 388ecb010d4SNuno Sá struct gpio_desc *gpio; 3891fd45670SAlexandru Ardelean uint16_t prod_id; 390ec04cb04SLars-Peter Clausen int ret; 391ec04cb04SLars-Peter Clausen 392ecb010d4SNuno Sá /* check if the device has rst pin low */ 393ecb010d4SNuno Sá gpio = devm_gpiod_get_optional(&adis->spi->dev, "reset", GPIOD_ASIS); 394ecb010d4SNuno Sá if (IS_ERR(gpio)) 395ecb010d4SNuno Sá return PTR_ERR(gpio); 396ecb010d4SNuno Sá 397ecb010d4SNuno Sá if (gpio) { 398ecb010d4SNuno Sá gpiod_set_value_cansleep(gpio, 1); 399ecb010d4SNuno Sá msleep(10); 400ecb010d4SNuno Sá /* bring device out of reset */ 401ecb010d4SNuno Sá gpiod_set_value_cansleep(gpio, 0); 402ecb010d4SNuno Sá msleep(timeouts->reset_ms); 403ecb010d4SNuno Sá } else { 404ecb010d4SNuno Sá ret = __adis_reset(adis); 4053f17ada8SAlexandru Ardelean if (ret) 406ecb010d4SNuno Sá return ret; 407ec04cb04SLars-Peter Clausen } 408ec04cb04SLars-Peter Clausen 4091fd45670SAlexandru Ardelean ret = adis_self_test(adis); 4101fd45670SAlexandru Ardelean if (ret) 4111fd45670SAlexandru Ardelean return ret; 4121fd45670SAlexandru Ardelean 4131fd45670SAlexandru Ardelean if (!adis->data->prod_id_reg) 4141fd45670SAlexandru Ardelean return 0; 4151fd45670SAlexandru Ardelean 4161fd45670SAlexandru Ardelean ret = adis_read_reg_16(adis, adis->data->prod_id_reg, &prod_id); 4171fd45670SAlexandru Ardelean if (ret) 4181fd45670SAlexandru Ardelean return ret; 4191fd45670SAlexandru Ardelean 4201fd45670SAlexandru Ardelean if (prod_id != adis->data->prod_id) 4211fd45670SAlexandru Ardelean dev_warn(&adis->spi->dev, 42297f1755eSChristophe JAILLET "Device ID(%u) and product ID(%u) do not match.\n", 4231fd45670SAlexandru Ardelean adis->data->prod_id, prod_id); 4241fd45670SAlexandru Ardelean 4251fd45670SAlexandru Ardelean return 0; 426ec04cb04SLars-Peter Clausen } 4273f17ada8SAlexandru Ardelean EXPORT_SYMBOL_GPL(__adis_initial_startup); 428ec04cb04SLars-Peter Clausen 429ec04cb04SLars-Peter Clausen /** 430ec04cb04SLars-Peter Clausen * adis_single_conversion() - Performs a single sample conversion 431ec04cb04SLars-Peter Clausen * @indio_dev: The IIO device 432ec04cb04SLars-Peter Clausen * @chan: The IIO channel 433ec04cb04SLars-Peter Clausen * @error_mask: Mask for the error bit 434ec04cb04SLars-Peter Clausen * @val: Result of the conversion 435ec04cb04SLars-Peter Clausen * 436ec04cb04SLars-Peter Clausen * Returns IIO_VAL_INT on success, a negative error code otherwise. 437ec04cb04SLars-Peter Clausen * 438ec04cb04SLars-Peter Clausen * The function performs a single conversion on a given channel and post 439ec04cb04SLars-Peter Clausen * processes the value accordingly to the channel spec. If a error_mask is given 440ec04cb04SLars-Peter Clausen * the function will check if the mask is set in the returned raw value. If it 441ec04cb04SLars-Peter Clausen * is set the function will perform a self-check. If the device does not report 442ec04cb04SLars-Peter Clausen * a error bit in the channels raw value set error_mask to 0. 443ec04cb04SLars-Peter Clausen */ 444ec04cb04SLars-Peter Clausen int adis_single_conversion(struct iio_dev *indio_dev, 445ec04cb04SLars-Peter Clausen const struct iio_chan_spec *chan, unsigned int error_mask, int *val) 446ec04cb04SLars-Peter Clausen { 447ec04cb04SLars-Peter Clausen struct adis *adis = iio_device_get_drvdata(indio_dev); 44857a1228aSLars-Peter Clausen unsigned int uval; 449ec04cb04SLars-Peter Clausen int ret; 450ec04cb04SLars-Peter Clausen 451c5485a5dSAlexandru Ardelean mutex_lock(&adis->state_lock); 452ec04cb04SLars-Peter Clausen 453c5485a5dSAlexandru Ardelean ret = __adis_read_reg(adis, chan->address, &uval, 45457a1228aSLars-Peter Clausen chan->scan_type.storagebits / 8); 455ec04cb04SLars-Peter Clausen if (ret) 456ec04cb04SLars-Peter Clausen goto err_unlock; 457ec04cb04SLars-Peter Clausen 45857a1228aSLars-Peter Clausen if (uval & error_mask) { 459c5485a5dSAlexandru Ardelean ret = __adis_check_status(adis); 460ec04cb04SLars-Peter Clausen if (ret) 461ec04cb04SLars-Peter Clausen goto err_unlock; 462ec04cb04SLars-Peter Clausen } 463ec04cb04SLars-Peter Clausen 464ec04cb04SLars-Peter Clausen if (chan->scan_type.sign == 's') 46557a1228aSLars-Peter Clausen *val = sign_extend32(uval, chan->scan_type.realbits - 1); 466ec04cb04SLars-Peter Clausen else 46757a1228aSLars-Peter Clausen *val = uval & ((1 << chan->scan_type.realbits) - 1); 468ec04cb04SLars-Peter Clausen 469ec04cb04SLars-Peter Clausen ret = IIO_VAL_INT; 470ec04cb04SLars-Peter Clausen err_unlock: 471c5485a5dSAlexandru Ardelean mutex_unlock(&adis->state_lock); 472ec04cb04SLars-Peter Clausen return ret; 473ec04cb04SLars-Peter Clausen } 474ec04cb04SLars-Peter Clausen EXPORT_SYMBOL_GPL(adis_single_conversion); 475ec04cb04SLars-Peter Clausen 476ec04cb04SLars-Peter Clausen /** 477ec04cb04SLars-Peter Clausen * adis_init() - Initialize adis device structure 478ec04cb04SLars-Peter Clausen * @adis: The adis device 479ec04cb04SLars-Peter Clausen * @indio_dev: The iio device 480ec04cb04SLars-Peter Clausen * @spi: The spi device 481ec04cb04SLars-Peter Clausen * @data: Chip specific data 482ec04cb04SLars-Peter Clausen * 483ec04cb04SLars-Peter Clausen * Returns 0 on success, a negative error code otherwise. 484ec04cb04SLars-Peter Clausen * 485ec04cb04SLars-Peter Clausen * This function must be called, before any other adis helper function may be 486ec04cb04SLars-Peter Clausen * called. 487ec04cb04SLars-Peter Clausen */ 488ec04cb04SLars-Peter Clausen int adis_init(struct adis *adis, struct iio_dev *indio_dev, 489ec04cb04SLars-Peter Clausen struct spi_device *spi, const struct adis_data *data) 490ec04cb04SLars-Peter Clausen { 491380b107bSNuno Sá if (!data || !data->timeouts) { 492380b107bSNuno Sá dev_err(&spi->dev, "No config data or timeouts not defined!\n"); 493380b107bSNuno Sá return -EINVAL; 494380b107bSNuno Sá } 495380b107bSNuno Sá 4966a9afcb1SAlexandru Ardelean mutex_init(&adis->state_lock); 497ec04cb04SLars-Peter Clausen adis->spi = spi; 498ec04cb04SLars-Peter Clausen adis->data = data; 499ec04cb04SLars-Peter Clausen iio_device_set_drvdata(indio_dev, adis); 500ec04cb04SLars-Peter Clausen 501484a0bf0SLars-Peter Clausen if (data->has_paging) { 502484a0bf0SLars-Peter Clausen /* Need to set the page before first read/write */ 503484a0bf0SLars-Peter Clausen adis->current_page = -1; 504484a0bf0SLars-Peter Clausen } else { 505484a0bf0SLars-Peter Clausen /* Page will always be 0 */ 506484a0bf0SLars-Peter Clausen adis->current_page = 0; 507484a0bf0SLars-Peter Clausen } 508484a0bf0SLars-Peter Clausen 509ec04cb04SLars-Peter Clausen return adis_enable_irq(adis, false); 510ec04cb04SLars-Peter Clausen } 511ec04cb04SLars-Peter Clausen EXPORT_SYMBOL_GPL(adis_init); 512ec04cb04SLars-Peter Clausen 513ec04cb04SLars-Peter Clausen MODULE_LICENSE("GPL"); 514ec04cb04SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 515ec04cb04SLars-Peter Clausen MODULE_DESCRIPTION("Common library code for ADIS16XXX devices"); 516