18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Common library for ADIS16XXX devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2012 Analog Devices Inc.
68c2ecf20Sopenharmony_ci *   Author: Lars-Peter Clausen <lars@metafoo.de>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
118c2ecf20Sopenharmony_ci#include <linux/mutex.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
218c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
228c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h>
238c2ecf20Sopenharmony_ci#include <linux/iio/imu/adis.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define ADIS_MSC_CTRL_DATA_RDY_EN	BIT(2)
268c2ecf20Sopenharmony_ci#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH	BIT(1)
278c2ecf20Sopenharmony_ci#define ADIS_MSC_CTRL_DATA_RDY_DIO2	BIT(0)
288c2ecf20Sopenharmony_ci#define ADIS_GLOB_CMD_SW_RESET		BIT(7)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/**
318c2ecf20Sopenharmony_ci * __adis_write_reg() - write N bytes to register (unlocked version)
328c2ecf20Sopenharmony_ci * @adis: The adis device
338c2ecf20Sopenharmony_ci * @reg: The address of the lower of the two registers
348c2ecf20Sopenharmony_ci * @value: The value to write to device (up to 4 bytes)
358c2ecf20Sopenharmony_ci * @size: The size of the @value (in bytes)
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ciint __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value,
388c2ecf20Sopenharmony_ci		     unsigned int size)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned int page = reg / ADIS_PAGE_SIZE;
418c2ecf20Sopenharmony_ci	int ret, i;
428c2ecf20Sopenharmony_ci	struct spi_message msg;
438c2ecf20Sopenharmony_ci	struct spi_transfer xfers[] = {
448c2ecf20Sopenharmony_ci		{
458c2ecf20Sopenharmony_ci			.tx_buf = adis->tx,
468c2ecf20Sopenharmony_ci			.bits_per_word = 8,
478c2ecf20Sopenharmony_ci			.len = 2,
488c2ecf20Sopenharmony_ci			.cs_change = 1,
498c2ecf20Sopenharmony_ci			.delay.value = adis->data->write_delay,
508c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
518c2ecf20Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
528c2ecf20Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
538c2ecf20Sopenharmony_ci		}, {
548c2ecf20Sopenharmony_ci			.tx_buf = adis->tx + 2,
558c2ecf20Sopenharmony_ci			.bits_per_word = 8,
568c2ecf20Sopenharmony_ci			.len = 2,
578c2ecf20Sopenharmony_ci			.cs_change = 1,
588c2ecf20Sopenharmony_ci			.delay.value = adis->data->write_delay,
598c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
608c2ecf20Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
618c2ecf20Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
628c2ecf20Sopenharmony_ci		}, {
638c2ecf20Sopenharmony_ci			.tx_buf = adis->tx + 4,
648c2ecf20Sopenharmony_ci			.bits_per_word = 8,
658c2ecf20Sopenharmony_ci			.len = 2,
668c2ecf20Sopenharmony_ci			.cs_change = 1,
678c2ecf20Sopenharmony_ci			.delay.value = adis->data->write_delay,
688c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
698c2ecf20Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
708c2ecf20Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
718c2ecf20Sopenharmony_ci		}, {
728c2ecf20Sopenharmony_ci			.tx_buf = adis->tx + 6,
738c2ecf20Sopenharmony_ci			.bits_per_word = 8,
748c2ecf20Sopenharmony_ci			.len = 2,
758c2ecf20Sopenharmony_ci			.delay.value = adis->data->write_delay,
768c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
778c2ecf20Sopenharmony_ci		}, {
788c2ecf20Sopenharmony_ci			.tx_buf = adis->tx + 8,
798c2ecf20Sopenharmony_ci			.bits_per_word = 8,
808c2ecf20Sopenharmony_ci			.len = 2,
818c2ecf20Sopenharmony_ci			.delay.value = adis->data->write_delay,
828c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
838c2ecf20Sopenharmony_ci		},
848c2ecf20Sopenharmony_ci	};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	spi_message_init(&msg);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (adis->current_page != page) {
898c2ecf20Sopenharmony_ci		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
908c2ecf20Sopenharmony_ci		adis->tx[1] = page;
918c2ecf20Sopenharmony_ci		spi_message_add_tail(&xfers[0], &msg);
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	switch (size) {
958c2ecf20Sopenharmony_ci	case 4:
968c2ecf20Sopenharmony_ci		adis->tx[8] = ADIS_WRITE_REG(reg + 3);
978c2ecf20Sopenharmony_ci		adis->tx[9] = (value >> 24) & 0xff;
988c2ecf20Sopenharmony_ci		adis->tx[6] = ADIS_WRITE_REG(reg + 2);
998c2ecf20Sopenharmony_ci		adis->tx[7] = (value >> 16) & 0xff;
1008c2ecf20Sopenharmony_ci		fallthrough;
1018c2ecf20Sopenharmony_ci	case 2:
1028c2ecf20Sopenharmony_ci		adis->tx[4] = ADIS_WRITE_REG(reg + 1);
1038c2ecf20Sopenharmony_ci		adis->tx[5] = (value >> 8) & 0xff;
1048c2ecf20Sopenharmony_ci		fallthrough;
1058c2ecf20Sopenharmony_ci	case 1:
1068c2ecf20Sopenharmony_ci		adis->tx[2] = ADIS_WRITE_REG(reg);
1078c2ecf20Sopenharmony_ci		adis->tx[3] = value & 0xff;
1088c2ecf20Sopenharmony_ci		break;
1098c2ecf20Sopenharmony_ci	default:
1108c2ecf20Sopenharmony_ci		return -EINVAL;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	xfers[size].cs_change = 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	for (i = 1; i <= size; i++)
1168c2ecf20Sopenharmony_ci		spi_message_add_tail(&xfers[i], &msg);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ret = spi_sync(adis->spi, &msg);
1198c2ecf20Sopenharmony_ci	if (ret) {
1208c2ecf20Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
1218c2ecf20Sopenharmony_ci			reg, ret);
1228c2ecf20Sopenharmony_ci	} else {
1238c2ecf20Sopenharmony_ci		adis->current_page = page;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return ret;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_write_reg, IIO_ADISLIB);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/**
1318c2ecf20Sopenharmony_ci * __adis_read_reg() - read N bytes from register (unlocked version)
1328c2ecf20Sopenharmony_ci * @adis: The adis device
1338c2ecf20Sopenharmony_ci * @reg: The address of the lower of the two registers
1348c2ecf20Sopenharmony_ci * @val: The value read back from the device
1358c2ecf20Sopenharmony_ci * @size: The size of the @val buffer
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_ciint __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val,
1388c2ecf20Sopenharmony_ci		    unsigned int size)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	unsigned int page = reg / ADIS_PAGE_SIZE;
1418c2ecf20Sopenharmony_ci	struct spi_message msg;
1428c2ecf20Sopenharmony_ci	int ret;
1438c2ecf20Sopenharmony_ci	struct spi_transfer xfers[] = {
1448c2ecf20Sopenharmony_ci		{
1458c2ecf20Sopenharmony_ci			.tx_buf = adis->tx,
1468c2ecf20Sopenharmony_ci			.bits_per_word = 8,
1478c2ecf20Sopenharmony_ci			.len = 2,
1488c2ecf20Sopenharmony_ci			.cs_change = 1,
1498c2ecf20Sopenharmony_ci			.delay.value = adis->data->write_delay,
1508c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
1518c2ecf20Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
1528c2ecf20Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
1538c2ecf20Sopenharmony_ci		}, {
1548c2ecf20Sopenharmony_ci			.tx_buf = adis->tx + 2,
1558c2ecf20Sopenharmony_ci			.bits_per_word = 8,
1568c2ecf20Sopenharmony_ci			.len = 2,
1578c2ecf20Sopenharmony_ci			.cs_change = 1,
1588c2ecf20Sopenharmony_ci			.delay.value = adis->data->read_delay,
1598c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
1608c2ecf20Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
1618c2ecf20Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
1628c2ecf20Sopenharmony_ci		}, {
1638c2ecf20Sopenharmony_ci			.tx_buf = adis->tx + 4,
1648c2ecf20Sopenharmony_ci			.rx_buf = adis->rx,
1658c2ecf20Sopenharmony_ci			.bits_per_word = 8,
1668c2ecf20Sopenharmony_ci			.len = 2,
1678c2ecf20Sopenharmony_ci			.cs_change = 1,
1688c2ecf20Sopenharmony_ci			.delay.value = adis->data->read_delay,
1698c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
1708c2ecf20Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
1718c2ecf20Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
1728c2ecf20Sopenharmony_ci		}, {
1738c2ecf20Sopenharmony_ci			.rx_buf = adis->rx + 2,
1748c2ecf20Sopenharmony_ci			.bits_per_word = 8,
1758c2ecf20Sopenharmony_ci			.len = 2,
1768c2ecf20Sopenharmony_ci			.delay.value = adis->data->read_delay,
1778c2ecf20Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
1788c2ecf20Sopenharmony_ci		},
1798c2ecf20Sopenharmony_ci	};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	spi_message_init(&msg);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (adis->current_page != page) {
1848c2ecf20Sopenharmony_ci		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
1858c2ecf20Sopenharmony_ci		adis->tx[1] = page;
1868c2ecf20Sopenharmony_ci		spi_message_add_tail(&xfers[0], &msg);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	switch (size) {
1908c2ecf20Sopenharmony_ci	case 4:
1918c2ecf20Sopenharmony_ci		adis->tx[2] = ADIS_READ_REG(reg + 2);
1928c2ecf20Sopenharmony_ci		adis->tx[3] = 0;
1938c2ecf20Sopenharmony_ci		spi_message_add_tail(&xfers[1], &msg);
1948c2ecf20Sopenharmony_ci		fallthrough;
1958c2ecf20Sopenharmony_ci	case 2:
1968c2ecf20Sopenharmony_ci		adis->tx[4] = ADIS_READ_REG(reg);
1978c2ecf20Sopenharmony_ci		adis->tx[5] = 0;
1988c2ecf20Sopenharmony_ci		spi_message_add_tail(&xfers[2], &msg);
1998c2ecf20Sopenharmony_ci		spi_message_add_tail(&xfers[3], &msg);
2008c2ecf20Sopenharmony_ci		break;
2018c2ecf20Sopenharmony_ci	default:
2028c2ecf20Sopenharmony_ci		return -EINVAL;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ret = spi_sync(adis->spi, &msg);
2068c2ecf20Sopenharmony_ci	if (ret) {
2078c2ecf20Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
2088c2ecf20Sopenharmony_ci			reg, ret);
2098c2ecf20Sopenharmony_ci		return ret;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	adis->current_page = page;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	switch (size) {
2158c2ecf20Sopenharmony_ci	case 4:
2168c2ecf20Sopenharmony_ci		*val = get_unaligned_be32(adis->rx);
2178c2ecf20Sopenharmony_ci		break;
2188c2ecf20Sopenharmony_ci	case 2:
2198c2ecf20Sopenharmony_ci		*val = get_unaligned_be16(adis->rx + 2);
2208c2ecf20Sopenharmony_ci		break;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return ret;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_read_reg, IIO_ADISLIB);
2268c2ecf20Sopenharmony_ci/**
2278c2ecf20Sopenharmony_ci * __adis_update_bits_base() - ADIS Update bits function - Unlocked version
2288c2ecf20Sopenharmony_ci * @adis: The adis device
2298c2ecf20Sopenharmony_ci * @reg: The address of the lower of the two registers
2308c2ecf20Sopenharmony_ci * @mask: Bitmask to change
2318c2ecf20Sopenharmony_ci * @val: Value to be written
2328c2ecf20Sopenharmony_ci * @size: Size of the register to update
2338c2ecf20Sopenharmony_ci *
2348c2ecf20Sopenharmony_ci * Updates the desired bits of @reg in accordance with @mask and @val.
2358c2ecf20Sopenharmony_ci */
2368c2ecf20Sopenharmony_ciint __adis_update_bits_base(struct adis *adis, unsigned int reg, const u32 mask,
2378c2ecf20Sopenharmony_ci			    const u32 val, u8 size)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int ret;
2408c2ecf20Sopenharmony_ci	u32 __val;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ret = __adis_read_reg(adis, reg, &__val, size);
2438c2ecf20Sopenharmony_ci	if (ret)
2448c2ecf20Sopenharmony_ci		return ret;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	__val = (__val & ~mask) | (val & mask);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return __adis_write_reg(adis, reg, __val, size);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_update_bits_base, IIO_ADISLIB);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciint adis_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
2558c2ecf20Sopenharmony_ci			    unsigned int writeval, unsigned int *readval)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct adis *adis = iio_device_get_drvdata(indio_dev);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (readval) {
2608c2ecf20Sopenharmony_ci		u16 val16;
2618c2ecf20Sopenharmony_ci		int ret;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		ret = adis_read_reg_16(adis, reg, &val16);
2648c2ecf20Sopenharmony_ci		if (ret == 0)
2658c2ecf20Sopenharmony_ci			*readval = val16;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		return ret;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return adis_write_reg_16(adis, reg, writeval);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(adis_debugfs_reg_access, IIO_ADISLIB);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci#endif
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/**
2778c2ecf20Sopenharmony_ci * __adis_enable_irq() - Enable or disable data ready IRQ (unlocked)
2788c2ecf20Sopenharmony_ci * @adis: The adis device
2798c2ecf20Sopenharmony_ci * @enable: Whether to enable the IRQ
2808c2ecf20Sopenharmony_ci *
2818c2ecf20Sopenharmony_ci * Returns 0 on success, negative error code otherwise
2828c2ecf20Sopenharmony_ci */
2838c2ecf20Sopenharmony_ciint __adis_enable_irq(struct adis *adis, bool enable)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	int ret;
2868c2ecf20Sopenharmony_ci	u16 msc;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (adis->data->enable_irq)
2898c2ecf20Sopenharmony_ci		return adis->data->enable_irq(adis, enable);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (adis->data->unmasked_drdy) {
2928c2ecf20Sopenharmony_ci		if (enable)
2938c2ecf20Sopenharmony_ci			enable_irq(adis->spi->irq);
2948c2ecf20Sopenharmony_ci		else
2958c2ecf20Sopenharmony_ci			disable_irq(adis->spi->irq);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		return 0;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
3018c2ecf20Sopenharmony_ci	if (ret)
3028c2ecf20Sopenharmony_ci		return ret;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
3058c2ecf20Sopenharmony_ci	msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
3068c2ecf20Sopenharmony_ci	if (enable)
3078c2ecf20Sopenharmony_ci		msc |= ADIS_MSC_CTRL_DATA_RDY_EN;
3088c2ecf20Sopenharmony_ci	else
3098c2ecf20Sopenharmony_ci		msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(__adis_enable_irq, IIO_ADISLIB);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/**
3168c2ecf20Sopenharmony_ci * __adis_check_status() - Check the device for error conditions (unlocked)
3178c2ecf20Sopenharmony_ci * @adis: The adis device
3188c2ecf20Sopenharmony_ci *
3198c2ecf20Sopenharmony_ci * Returns 0 on success, a negative error code otherwise
3208c2ecf20Sopenharmony_ci */
3218c2ecf20Sopenharmony_ciint __adis_check_status(struct adis *adis)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	u16 status;
3248c2ecf20Sopenharmony_ci	int ret;
3258c2ecf20Sopenharmony_ci	int i;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	ret = __adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
3288c2ecf20Sopenharmony_ci	if (ret)
3298c2ecf20Sopenharmony_ci		return ret;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	status &= adis->data->status_error_mask;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (status == 0)
3348c2ecf20Sopenharmony_ci		return 0;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	for (i = 0; i < 16; ++i) {
3378c2ecf20Sopenharmony_ci		if (status & BIT(i)) {
3388c2ecf20Sopenharmony_ci			dev_err(&adis->spi->dev, "%s.\n",
3398c2ecf20Sopenharmony_ci				adis->data->status_error_msgs[i]);
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return -EIO;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_check_status, IIO_ADISLIB);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci/**
3488c2ecf20Sopenharmony_ci * __adis_reset() - Reset the device (unlocked version)
3498c2ecf20Sopenharmony_ci * @adis: The adis device
3508c2ecf20Sopenharmony_ci *
3518c2ecf20Sopenharmony_ci * Returns 0 on success, a negative error code otherwise
3528c2ecf20Sopenharmony_ci */
3538c2ecf20Sopenharmony_ciint __adis_reset(struct adis *adis)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int ret;
3568c2ecf20Sopenharmony_ci	const struct adis_timeout *timeouts = adis->data->timeouts;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg,
3598c2ecf20Sopenharmony_ci				 ADIS_GLOB_CMD_SW_RESET);
3608c2ecf20Sopenharmony_ci	if (ret) {
3618c2ecf20Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
3628c2ecf20Sopenharmony_ci		return ret;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	msleep(timeouts->sw_reset_ms);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_reset, IIO_ADIS_LIB);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int adis_self_test(struct adis *adis)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	int ret;
3748c2ecf20Sopenharmony_ci	const struct adis_timeout *timeouts = adis->data->timeouts;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	ret = __adis_write_reg_16(adis, adis->data->self_test_reg,
3778c2ecf20Sopenharmony_ci				  adis->data->self_test_mask);
3788c2ecf20Sopenharmony_ci	if (ret) {
3798c2ecf20Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
3808c2ecf20Sopenharmony_ci			ret);
3818c2ecf20Sopenharmony_ci		return ret;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	msleep(timeouts->self_test_ms);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	ret = __adis_check_status(adis);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (adis->data->self_test_no_autoclear)
3898c2ecf20Sopenharmony_ci		__adis_write_reg_16(adis, adis->data->self_test_reg, 0x00);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return ret;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/**
3958c2ecf20Sopenharmony_ci * __adis_initial_startup() - Device initial setup
3968c2ecf20Sopenharmony_ci * @adis: The adis device
3978c2ecf20Sopenharmony_ci *
3988c2ecf20Sopenharmony_ci * The function performs a HW reset via a reset pin that should be specified
3998c2ecf20Sopenharmony_ci * via GPIOLIB. If no pin is configured a SW reset will be performed.
4008c2ecf20Sopenharmony_ci * The RST pin for the ADIS devices should be configured as ACTIVE_LOW.
4018c2ecf20Sopenharmony_ci *
4028c2ecf20Sopenharmony_ci * After the self-test operation is performed, the function will also check
4038c2ecf20Sopenharmony_ci * that the product ID is as expected. This assumes that drivers providing
4048c2ecf20Sopenharmony_ci * 'prod_id_reg' will also provide the 'prod_id'.
4058c2ecf20Sopenharmony_ci *
4068c2ecf20Sopenharmony_ci * Returns 0 if the device is operational, a negative error code otherwise.
4078c2ecf20Sopenharmony_ci *
4088c2ecf20Sopenharmony_ci * This function should be called early on in the device initialization sequence
4098c2ecf20Sopenharmony_ci * to ensure that the device is in a sane and known state and that it is usable.
4108c2ecf20Sopenharmony_ci */
4118c2ecf20Sopenharmony_ciint __adis_initial_startup(struct adis *adis)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	const struct adis_timeout *timeouts = adis->data->timeouts;
4148c2ecf20Sopenharmony_ci	struct gpio_desc *gpio;
4158c2ecf20Sopenharmony_ci	u16 prod_id;
4168c2ecf20Sopenharmony_ci	int ret;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* check if the device has rst pin low */
4198c2ecf20Sopenharmony_ci	gpio = devm_gpiod_get_optional(&adis->spi->dev, "reset", GPIOD_OUT_HIGH);
4208c2ecf20Sopenharmony_ci	if (IS_ERR(gpio))
4218c2ecf20Sopenharmony_ci		return PTR_ERR(gpio);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (gpio) {
4248c2ecf20Sopenharmony_ci		usleep_range(10, 12);
4258c2ecf20Sopenharmony_ci		/* bring device out of reset */
4268c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(gpio, 0);
4278c2ecf20Sopenharmony_ci		msleep(timeouts->reset_ms);
4288c2ecf20Sopenharmony_ci	} else {
4298c2ecf20Sopenharmony_ci		ret = __adis_reset(adis);
4308c2ecf20Sopenharmony_ci		if (ret)
4318c2ecf20Sopenharmony_ci			return ret;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	ret = adis_self_test(adis);
4358c2ecf20Sopenharmony_ci	if (ret)
4368c2ecf20Sopenharmony_ci		return ret;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/*
4398c2ecf20Sopenharmony_ci	 * don't bother calling this if we can't unmask the IRQ as in this case
4408c2ecf20Sopenharmony_ci	 * the IRQ is most likely not yet requested and we will request it
4418c2ecf20Sopenharmony_ci	 * with 'IRQF_NO_AUTOEN' anyways.
4428c2ecf20Sopenharmony_ci	 */
4438c2ecf20Sopenharmony_ci	if (!adis->data->unmasked_drdy)
4448c2ecf20Sopenharmony_ci		__adis_enable_irq(adis, false);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (!adis->data->prod_id_reg)
4478c2ecf20Sopenharmony_ci		return 0;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	ret = adis_read_reg_16(adis, adis->data->prod_id_reg, &prod_id);
4508c2ecf20Sopenharmony_ci	if (ret)
4518c2ecf20Sopenharmony_ci		return ret;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (prod_id != adis->data->prod_id)
4548c2ecf20Sopenharmony_ci		dev_warn(&adis->spi->dev,
4558c2ecf20Sopenharmony_ci			 "Device ID(%u) and product ID(%u) do not match.\n",
4568c2ecf20Sopenharmony_ci			 adis->data->prod_id, prod_id);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return 0;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_initial_startup, IIO_ADISLIB);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/**
4638c2ecf20Sopenharmony_ci * adis_single_conversion() - Performs a single sample conversion
4648c2ecf20Sopenharmony_ci * @indio_dev: The IIO device
4658c2ecf20Sopenharmony_ci * @chan: The IIO channel
4668c2ecf20Sopenharmony_ci * @error_mask: Mask for the error bit
4678c2ecf20Sopenharmony_ci * @val: Result of the conversion
4688c2ecf20Sopenharmony_ci *
4698c2ecf20Sopenharmony_ci * Returns IIO_VAL_INT on success, a negative error code otherwise.
4708c2ecf20Sopenharmony_ci *
4718c2ecf20Sopenharmony_ci * The function performs a single conversion on a given channel and post
4728c2ecf20Sopenharmony_ci * processes the value accordingly to the channel spec. If a error_mask is given
4738c2ecf20Sopenharmony_ci * the function will check if the mask is set in the returned raw value. If it
4748c2ecf20Sopenharmony_ci * is set the function will perform a self-check. If the device does not report
4758c2ecf20Sopenharmony_ci * a error bit in the channels raw value set error_mask to 0.
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_ciint adis_single_conversion(struct iio_dev *indio_dev,
4788c2ecf20Sopenharmony_ci			   const struct iio_chan_spec *chan,
4798c2ecf20Sopenharmony_ci			   unsigned int error_mask, int *val)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct adis *adis = iio_device_get_drvdata(indio_dev);
4828c2ecf20Sopenharmony_ci	unsigned int uval;
4838c2ecf20Sopenharmony_ci	int ret;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	mutex_lock(&adis->state_lock);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	ret = __adis_read_reg(adis, chan->address, &uval,
4888c2ecf20Sopenharmony_ci			      chan->scan_type.storagebits / 8);
4898c2ecf20Sopenharmony_ci	if (ret)
4908c2ecf20Sopenharmony_ci		goto err_unlock;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (uval & error_mask) {
4938c2ecf20Sopenharmony_ci		ret = __adis_check_status(adis);
4948c2ecf20Sopenharmony_ci		if (ret)
4958c2ecf20Sopenharmony_ci			goto err_unlock;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (chan->scan_type.sign == 's')
4998c2ecf20Sopenharmony_ci		*val = sign_extend32(uval, chan->scan_type.realbits - 1);
5008c2ecf20Sopenharmony_ci	else
5018c2ecf20Sopenharmony_ci		*val = uval & ((1 << chan->scan_type.realbits) - 1);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ret = IIO_VAL_INT;
5048c2ecf20Sopenharmony_cierr_unlock:
5058c2ecf20Sopenharmony_ci	mutex_unlock(&adis->state_lock);
5068c2ecf20Sopenharmony_ci	return ret;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(adis_single_conversion, IIO_ADISLIB);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/**
5118c2ecf20Sopenharmony_ci * adis_init() - Initialize adis device structure
5128c2ecf20Sopenharmony_ci * @adis:	The adis device
5138c2ecf20Sopenharmony_ci * @indio_dev:	The iio device
5148c2ecf20Sopenharmony_ci * @spi:	The spi device
5158c2ecf20Sopenharmony_ci * @data:	Chip specific data
5168c2ecf20Sopenharmony_ci *
5178c2ecf20Sopenharmony_ci * Returns 0 on success, a negative error code otherwise.
5188c2ecf20Sopenharmony_ci *
5198c2ecf20Sopenharmony_ci * This function must be called, before any other adis helper function may be
5208c2ecf20Sopenharmony_ci * called.
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_ciint adis_init(struct adis *adis, struct iio_dev *indio_dev,
5238c2ecf20Sopenharmony_ci	      struct spi_device *spi, const struct adis_data *data)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	if (!data || !data->timeouts) {
5268c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "No config data or timeouts not defined!\n");
5278c2ecf20Sopenharmony_ci		return -EINVAL;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	mutex_init(&adis->state_lock);
5318c2ecf20Sopenharmony_ci	adis->spi = spi;
5328c2ecf20Sopenharmony_ci	adis->data = data;
5338c2ecf20Sopenharmony_ci	iio_device_set_drvdata(indio_dev, adis);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (data->has_paging) {
5368c2ecf20Sopenharmony_ci		/* Need to set the page before first read/write */
5378c2ecf20Sopenharmony_ci		adis->current_page = -1;
5388c2ecf20Sopenharmony_ci	} else {
5398c2ecf20Sopenharmony_ci		/* Page will always be 0 */
5408c2ecf20Sopenharmony_ci		adis->current_page = 0;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(adis_init, IIO_ADISLIB);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
5498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Common library code for ADIS16XXX devices");
550