162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Common library for ADIS16XXX devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2012 Analog Devices Inc.
662306a36Sopenharmony_ci *   Author: Lars-Peter Clausen <lars@metafoo.de>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/spi/spi.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <asm/unaligned.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/iio/iio.h>
1962306a36Sopenharmony_ci#include <linux/iio/imu/adis.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define ADIS_MSC_CTRL_DATA_RDY_EN	BIT(2)
2262306a36Sopenharmony_ci#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH	BIT(1)
2362306a36Sopenharmony_ci#define ADIS_MSC_CTRL_DATA_RDY_DIO2	BIT(0)
2462306a36Sopenharmony_ci#define ADIS_GLOB_CMD_SW_RESET		BIT(7)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/**
2762306a36Sopenharmony_ci * __adis_write_reg() - write N bytes to register (unlocked version)
2862306a36Sopenharmony_ci * @adis: The adis device
2962306a36Sopenharmony_ci * @reg: The address of the lower of the two registers
3062306a36Sopenharmony_ci * @value: The value to write to device (up to 4 bytes)
3162306a36Sopenharmony_ci * @size: The size of the @value (in bytes)
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ciint __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value,
3462306a36Sopenharmony_ci		     unsigned int size)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	unsigned int page = reg / ADIS_PAGE_SIZE;
3762306a36Sopenharmony_ci	int ret, i;
3862306a36Sopenharmony_ci	struct spi_message msg;
3962306a36Sopenharmony_ci	struct spi_transfer xfers[] = {
4062306a36Sopenharmony_ci		{
4162306a36Sopenharmony_ci			.tx_buf = adis->tx,
4262306a36Sopenharmony_ci			.bits_per_word = 8,
4362306a36Sopenharmony_ci			.len = 2,
4462306a36Sopenharmony_ci			.cs_change = 1,
4562306a36Sopenharmony_ci			.delay.value = adis->data->write_delay,
4662306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
4762306a36Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
4862306a36Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
4962306a36Sopenharmony_ci		}, {
5062306a36Sopenharmony_ci			.tx_buf = adis->tx + 2,
5162306a36Sopenharmony_ci			.bits_per_word = 8,
5262306a36Sopenharmony_ci			.len = 2,
5362306a36Sopenharmony_ci			.cs_change = 1,
5462306a36Sopenharmony_ci			.delay.value = adis->data->write_delay,
5562306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
5662306a36Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
5762306a36Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
5862306a36Sopenharmony_ci		}, {
5962306a36Sopenharmony_ci			.tx_buf = adis->tx + 4,
6062306a36Sopenharmony_ci			.bits_per_word = 8,
6162306a36Sopenharmony_ci			.len = 2,
6262306a36Sopenharmony_ci			.cs_change = 1,
6362306a36Sopenharmony_ci			.delay.value = adis->data->write_delay,
6462306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
6562306a36Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
6662306a36Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
6762306a36Sopenharmony_ci		}, {
6862306a36Sopenharmony_ci			.tx_buf = adis->tx + 6,
6962306a36Sopenharmony_ci			.bits_per_word = 8,
7062306a36Sopenharmony_ci			.len = 2,
7162306a36Sopenharmony_ci			.delay.value = adis->data->write_delay,
7262306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
7362306a36Sopenharmony_ci		}, {
7462306a36Sopenharmony_ci			.tx_buf = adis->tx + 8,
7562306a36Sopenharmony_ci			.bits_per_word = 8,
7662306a36Sopenharmony_ci			.len = 2,
7762306a36Sopenharmony_ci			.delay.value = adis->data->write_delay,
7862306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
7962306a36Sopenharmony_ci		},
8062306a36Sopenharmony_ci	};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	spi_message_init(&msg);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (adis->current_page != page) {
8562306a36Sopenharmony_ci		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
8662306a36Sopenharmony_ci		adis->tx[1] = page;
8762306a36Sopenharmony_ci		spi_message_add_tail(&xfers[0], &msg);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	switch (size) {
9162306a36Sopenharmony_ci	case 4:
9262306a36Sopenharmony_ci		adis->tx[8] = ADIS_WRITE_REG(reg + 3);
9362306a36Sopenharmony_ci		adis->tx[9] = (value >> 24) & 0xff;
9462306a36Sopenharmony_ci		adis->tx[6] = ADIS_WRITE_REG(reg + 2);
9562306a36Sopenharmony_ci		adis->tx[7] = (value >> 16) & 0xff;
9662306a36Sopenharmony_ci		fallthrough;
9762306a36Sopenharmony_ci	case 2:
9862306a36Sopenharmony_ci		adis->tx[4] = ADIS_WRITE_REG(reg + 1);
9962306a36Sopenharmony_ci		adis->tx[5] = (value >> 8) & 0xff;
10062306a36Sopenharmony_ci		fallthrough;
10162306a36Sopenharmony_ci	case 1:
10262306a36Sopenharmony_ci		adis->tx[2] = ADIS_WRITE_REG(reg);
10362306a36Sopenharmony_ci		adis->tx[3] = value & 0xff;
10462306a36Sopenharmony_ci		break;
10562306a36Sopenharmony_ci	default:
10662306a36Sopenharmony_ci		return -EINVAL;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	xfers[size].cs_change = 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	for (i = 1; i <= size; i++)
11262306a36Sopenharmony_ci		spi_message_add_tail(&xfers[i], &msg);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	ret = spi_sync(adis->spi, &msg);
11562306a36Sopenharmony_ci	if (ret) {
11662306a36Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
11762306a36Sopenharmony_ci			reg, ret);
11862306a36Sopenharmony_ci	} else {
11962306a36Sopenharmony_ci		adis->current_page = page;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return ret;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_write_reg, IIO_ADISLIB);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * __adis_read_reg() - read N bytes from register (unlocked version)
12862306a36Sopenharmony_ci * @adis: The adis device
12962306a36Sopenharmony_ci * @reg: The address of the lower of the two registers
13062306a36Sopenharmony_ci * @val: The value read back from the device
13162306a36Sopenharmony_ci * @size: The size of the @val buffer
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_ciint __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val,
13462306a36Sopenharmony_ci		    unsigned int size)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	unsigned int page = reg / ADIS_PAGE_SIZE;
13762306a36Sopenharmony_ci	struct spi_message msg;
13862306a36Sopenharmony_ci	int ret;
13962306a36Sopenharmony_ci	struct spi_transfer xfers[] = {
14062306a36Sopenharmony_ci		{
14162306a36Sopenharmony_ci			.tx_buf = adis->tx,
14262306a36Sopenharmony_ci			.bits_per_word = 8,
14362306a36Sopenharmony_ci			.len = 2,
14462306a36Sopenharmony_ci			.cs_change = 1,
14562306a36Sopenharmony_ci			.delay.value = adis->data->write_delay,
14662306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
14762306a36Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
14862306a36Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
14962306a36Sopenharmony_ci		}, {
15062306a36Sopenharmony_ci			.tx_buf = adis->tx + 2,
15162306a36Sopenharmony_ci			.bits_per_word = 8,
15262306a36Sopenharmony_ci			.len = 2,
15362306a36Sopenharmony_ci			.cs_change = 1,
15462306a36Sopenharmony_ci			.delay.value = adis->data->read_delay,
15562306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
15662306a36Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
15762306a36Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
15862306a36Sopenharmony_ci		}, {
15962306a36Sopenharmony_ci			.tx_buf = adis->tx + 4,
16062306a36Sopenharmony_ci			.rx_buf = adis->rx,
16162306a36Sopenharmony_ci			.bits_per_word = 8,
16262306a36Sopenharmony_ci			.len = 2,
16362306a36Sopenharmony_ci			.cs_change = 1,
16462306a36Sopenharmony_ci			.delay.value = adis->data->read_delay,
16562306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
16662306a36Sopenharmony_ci			.cs_change_delay.value = adis->data->cs_change_delay,
16762306a36Sopenharmony_ci			.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
16862306a36Sopenharmony_ci		}, {
16962306a36Sopenharmony_ci			.rx_buf = adis->rx + 2,
17062306a36Sopenharmony_ci			.bits_per_word = 8,
17162306a36Sopenharmony_ci			.len = 2,
17262306a36Sopenharmony_ci			.delay.value = adis->data->read_delay,
17362306a36Sopenharmony_ci			.delay.unit = SPI_DELAY_UNIT_USECS,
17462306a36Sopenharmony_ci		},
17562306a36Sopenharmony_ci	};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	spi_message_init(&msg);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (adis->current_page != page) {
18062306a36Sopenharmony_ci		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
18162306a36Sopenharmony_ci		adis->tx[1] = page;
18262306a36Sopenharmony_ci		spi_message_add_tail(&xfers[0], &msg);
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	switch (size) {
18662306a36Sopenharmony_ci	case 4:
18762306a36Sopenharmony_ci		adis->tx[2] = ADIS_READ_REG(reg + 2);
18862306a36Sopenharmony_ci		adis->tx[3] = 0;
18962306a36Sopenharmony_ci		spi_message_add_tail(&xfers[1], &msg);
19062306a36Sopenharmony_ci		fallthrough;
19162306a36Sopenharmony_ci	case 2:
19262306a36Sopenharmony_ci		adis->tx[4] = ADIS_READ_REG(reg);
19362306a36Sopenharmony_ci		adis->tx[5] = 0;
19462306a36Sopenharmony_ci		spi_message_add_tail(&xfers[2], &msg);
19562306a36Sopenharmony_ci		spi_message_add_tail(&xfers[3], &msg);
19662306a36Sopenharmony_ci		break;
19762306a36Sopenharmony_ci	default:
19862306a36Sopenharmony_ci		return -EINVAL;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ret = spi_sync(adis->spi, &msg);
20262306a36Sopenharmony_ci	if (ret) {
20362306a36Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
20462306a36Sopenharmony_ci			reg, ret);
20562306a36Sopenharmony_ci		return ret;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	adis->current_page = page;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	switch (size) {
21162306a36Sopenharmony_ci	case 4:
21262306a36Sopenharmony_ci		*val = get_unaligned_be32(adis->rx);
21362306a36Sopenharmony_ci		break;
21462306a36Sopenharmony_ci	case 2:
21562306a36Sopenharmony_ci		*val = get_unaligned_be16(adis->rx + 2);
21662306a36Sopenharmony_ci		break;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return ret;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_read_reg, IIO_ADISLIB);
22262306a36Sopenharmony_ci/**
22362306a36Sopenharmony_ci * __adis_update_bits_base() - ADIS Update bits function - Unlocked version
22462306a36Sopenharmony_ci * @adis: The adis device
22562306a36Sopenharmony_ci * @reg: The address of the lower of the two registers
22662306a36Sopenharmony_ci * @mask: Bitmask to change
22762306a36Sopenharmony_ci * @val: Value to be written
22862306a36Sopenharmony_ci * @size: Size of the register to update
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Updates the desired bits of @reg in accordance with @mask and @val.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_ciint __adis_update_bits_base(struct adis *adis, unsigned int reg, const u32 mask,
23362306a36Sopenharmony_ci			    const u32 val, u8 size)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	int ret;
23662306a36Sopenharmony_ci	u32 __val;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	ret = __adis_read_reg(adis, reg, &__val, size);
23962306a36Sopenharmony_ci	if (ret)
24062306a36Sopenharmony_ci		return ret;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	__val = (__val & ~mask) | (val & mask);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return __adis_write_reg(adis, reg, __val, size);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_update_bits_base, IIO_ADISLIB);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciint adis_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
25162306a36Sopenharmony_ci			    unsigned int writeval, unsigned int *readval)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct adis *adis = iio_device_get_drvdata(indio_dev);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (readval) {
25662306a36Sopenharmony_ci		u16 val16;
25762306a36Sopenharmony_ci		int ret;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		ret = adis_read_reg_16(adis, reg, &val16);
26062306a36Sopenharmony_ci		if (ret == 0)
26162306a36Sopenharmony_ci			*readval = val16;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		return ret;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return adis_write_reg_16(adis, reg, writeval);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ciEXPORT_SYMBOL_NS(adis_debugfs_reg_access, IIO_ADISLIB);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci#endif
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/**
27362306a36Sopenharmony_ci * __adis_enable_irq() - Enable or disable data ready IRQ (unlocked)
27462306a36Sopenharmony_ci * @adis: The adis device
27562306a36Sopenharmony_ci * @enable: Whether to enable the IRQ
27662306a36Sopenharmony_ci *
27762306a36Sopenharmony_ci * Returns 0 on success, negative error code otherwise
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_ciint __adis_enable_irq(struct adis *adis, bool enable)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	int ret;
28262306a36Sopenharmony_ci	u16 msc;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (adis->data->enable_irq)
28562306a36Sopenharmony_ci		return adis->data->enable_irq(adis, enable);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (adis->data->unmasked_drdy) {
28862306a36Sopenharmony_ci		if (enable)
28962306a36Sopenharmony_ci			enable_irq(adis->spi->irq);
29062306a36Sopenharmony_ci		else
29162306a36Sopenharmony_ci			disable_irq(adis->spi->irq);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		return 0;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
29762306a36Sopenharmony_ci	if (ret)
29862306a36Sopenharmony_ci		return ret;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
30162306a36Sopenharmony_ci	msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
30262306a36Sopenharmony_ci	if (enable)
30362306a36Sopenharmony_ci		msc |= ADIS_MSC_CTRL_DATA_RDY_EN;
30462306a36Sopenharmony_ci	else
30562306a36Sopenharmony_ci		msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ciEXPORT_SYMBOL_NS(__adis_enable_irq, IIO_ADISLIB);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci/**
31262306a36Sopenharmony_ci * __adis_check_status() - Check the device for error conditions (unlocked)
31362306a36Sopenharmony_ci * @adis: The adis device
31462306a36Sopenharmony_ci *
31562306a36Sopenharmony_ci * Returns 0 on success, a negative error code otherwise
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_ciint __adis_check_status(struct adis *adis)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	u16 status;
32062306a36Sopenharmony_ci	int ret;
32162306a36Sopenharmony_ci	int i;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	ret = __adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
32462306a36Sopenharmony_ci	if (ret)
32562306a36Sopenharmony_ci		return ret;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	status &= adis->data->status_error_mask;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (status == 0)
33062306a36Sopenharmony_ci		return 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	for (i = 0; i < 16; ++i) {
33362306a36Sopenharmony_ci		if (status & BIT(i)) {
33462306a36Sopenharmony_ci			dev_err(&adis->spi->dev, "%s.\n",
33562306a36Sopenharmony_ci				adis->data->status_error_msgs[i]);
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return -EIO;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_check_status, IIO_ADISLIB);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci/**
34462306a36Sopenharmony_ci * __adis_reset() - Reset the device (unlocked version)
34562306a36Sopenharmony_ci * @adis: The adis device
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * Returns 0 on success, a negative error code otherwise
34862306a36Sopenharmony_ci */
34962306a36Sopenharmony_ciint __adis_reset(struct adis *adis)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	int ret;
35262306a36Sopenharmony_ci	const struct adis_timeout *timeouts = adis->data->timeouts;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg,
35562306a36Sopenharmony_ci				 ADIS_GLOB_CMD_SW_RESET);
35662306a36Sopenharmony_ci	if (ret) {
35762306a36Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
35862306a36Sopenharmony_ci		return ret;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	msleep(timeouts->sw_reset_ms);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_reset, IIO_ADIS_LIB);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int adis_self_test(struct adis *adis)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	int ret;
37062306a36Sopenharmony_ci	const struct adis_timeout *timeouts = adis->data->timeouts;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ret = __adis_write_reg_16(adis, adis->data->self_test_reg,
37362306a36Sopenharmony_ci				  adis->data->self_test_mask);
37462306a36Sopenharmony_ci	if (ret) {
37562306a36Sopenharmony_ci		dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
37662306a36Sopenharmony_ci			ret);
37762306a36Sopenharmony_ci		return ret;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	msleep(timeouts->self_test_ms);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ret = __adis_check_status(adis);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (adis->data->self_test_no_autoclear)
38562306a36Sopenharmony_ci		__adis_write_reg_16(adis, adis->data->self_test_reg, 0x00);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return ret;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci/**
39162306a36Sopenharmony_ci * __adis_initial_startup() - Device initial setup
39262306a36Sopenharmony_ci * @adis: The adis device
39362306a36Sopenharmony_ci *
39462306a36Sopenharmony_ci * The function performs a HW reset via a reset pin that should be specified
39562306a36Sopenharmony_ci * via GPIOLIB. If no pin is configured a SW reset will be performed.
39662306a36Sopenharmony_ci * The RST pin for the ADIS devices should be configured as ACTIVE_LOW.
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * After the self-test operation is performed, the function will also check
39962306a36Sopenharmony_ci * that the product ID is as expected. This assumes that drivers providing
40062306a36Sopenharmony_ci * 'prod_id_reg' will also provide the 'prod_id'.
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * Returns 0 if the device is operational, a negative error code otherwise.
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * This function should be called early on in the device initialization sequence
40562306a36Sopenharmony_ci * to ensure that the device is in a sane and known state and that it is usable.
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_ciint __adis_initial_startup(struct adis *adis)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	const struct adis_timeout *timeouts = adis->data->timeouts;
41062306a36Sopenharmony_ci	struct gpio_desc *gpio;
41162306a36Sopenharmony_ci	u16 prod_id;
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* check if the device has rst pin low */
41562306a36Sopenharmony_ci	gpio = devm_gpiod_get_optional(&adis->spi->dev, "reset", GPIOD_OUT_HIGH);
41662306a36Sopenharmony_ci	if (IS_ERR(gpio))
41762306a36Sopenharmony_ci		return PTR_ERR(gpio);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (gpio) {
42062306a36Sopenharmony_ci		usleep_range(10, 12);
42162306a36Sopenharmony_ci		/* bring device out of reset */
42262306a36Sopenharmony_ci		gpiod_set_value_cansleep(gpio, 0);
42362306a36Sopenharmony_ci		msleep(timeouts->reset_ms);
42462306a36Sopenharmony_ci	} else {
42562306a36Sopenharmony_ci		ret = __adis_reset(adis);
42662306a36Sopenharmony_ci		if (ret)
42762306a36Sopenharmony_ci			return ret;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	ret = adis_self_test(adis);
43162306a36Sopenharmony_ci	if (ret)
43262306a36Sopenharmony_ci		return ret;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/*
43562306a36Sopenharmony_ci	 * don't bother calling this if we can't unmask the IRQ as in this case
43662306a36Sopenharmony_ci	 * the IRQ is most likely not yet requested and we will request it
43762306a36Sopenharmony_ci	 * with 'IRQF_NO_AUTOEN' anyways.
43862306a36Sopenharmony_ci	 */
43962306a36Sopenharmony_ci	if (!adis->data->unmasked_drdy)
44062306a36Sopenharmony_ci		__adis_enable_irq(adis, false);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (!adis->data->prod_id_reg)
44362306a36Sopenharmony_ci		return 0;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	ret = adis_read_reg_16(adis, adis->data->prod_id_reg, &prod_id);
44662306a36Sopenharmony_ci	if (ret)
44762306a36Sopenharmony_ci		return ret;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (prod_id != adis->data->prod_id)
45062306a36Sopenharmony_ci		dev_warn(&adis->spi->dev,
45162306a36Sopenharmony_ci			 "Device ID(%u) and product ID(%u) do not match.\n",
45262306a36Sopenharmony_ci			 adis->data->prod_id, prod_id);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__adis_initial_startup, IIO_ADISLIB);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/**
45962306a36Sopenharmony_ci * adis_single_conversion() - Performs a single sample conversion
46062306a36Sopenharmony_ci * @indio_dev: The IIO device
46162306a36Sopenharmony_ci * @chan: The IIO channel
46262306a36Sopenharmony_ci * @error_mask: Mask for the error bit
46362306a36Sopenharmony_ci * @val: Result of the conversion
46462306a36Sopenharmony_ci *
46562306a36Sopenharmony_ci * Returns IIO_VAL_INT on success, a negative error code otherwise.
46662306a36Sopenharmony_ci *
46762306a36Sopenharmony_ci * The function performs a single conversion on a given channel and post
46862306a36Sopenharmony_ci * processes the value accordingly to the channel spec. If a error_mask is given
46962306a36Sopenharmony_ci * the function will check if the mask is set in the returned raw value. If it
47062306a36Sopenharmony_ci * is set the function will perform a self-check. If the device does not report
47162306a36Sopenharmony_ci * a error bit in the channels raw value set error_mask to 0.
47262306a36Sopenharmony_ci */
47362306a36Sopenharmony_ciint adis_single_conversion(struct iio_dev *indio_dev,
47462306a36Sopenharmony_ci			   const struct iio_chan_spec *chan,
47562306a36Sopenharmony_ci			   unsigned int error_mask, int *val)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct adis *adis = iio_device_get_drvdata(indio_dev);
47862306a36Sopenharmony_ci	unsigned int uval;
47962306a36Sopenharmony_ci	int ret;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	mutex_lock(&adis->state_lock);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	ret = __adis_read_reg(adis, chan->address, &uval,
48462306a36Sopenharmony_ci			      chan->scan_type.storagebits / 8);
48562306a36Sopenharmony_ci	if (ret)
48662306a36Sopenharmony_ci		goto err_unlock;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (uval & error_mask) {
48962306a36Sopenharmony_ci		ret = __adis_check_status(adis);
49062306a36Sopenharmony_ci		if (ret)
49162306a36Sopenharmony_ci			goto err_unlock;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (chan->scan_type.sign == 's')
49562306a36Sopenharmony_ci		*val = sign_extend32(uval, chan->scan_type.realbits - 1);
49662306a36Sopenharmony_ci	else
49762306a36Sopenharmony_ci		*val = uval & ((1 << chan->scan_type.realbits) - 1);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ret = IIO_VAL_INT;
50062306a36Sopenharmony_cierr_unlock:
50162306a36Sopenharmony_ci	mutex_unlock(&adis->state_lock);
50262306a36Sopenharmony_ci	return ret;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(adis_single_conversion, IIO_ADISLIB);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/**
50762306a36Sopenharmony_ci * adis_init() - Initialize adis device structure
50862306a36Sopenharmony_ci * @adis:	The adis device
50962306a36Sopenharmony_ci * @indio_dev:	The iio device
51062306a36Sopenharmony_ci * @spi:	The spi device
51162306a36Sopenharmony_ci * @data:	Chip specific data
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci * Returns 0 on success, a negative error code otherwise.
51462306a36Sopenharmony_ci *
51562306a36Sopenharmony_ci * This function must be called, before any other adis helper function may be
51662306a36Sopenharmony_ci * called.
51762306a36Sopenharmony_ci */
51862306a36Sopenharmony_ciint adis_init(struct adis *adis, struct iio_dev *indio_dev,
51962306a36Sopenharmony_ci	      struct spi_device *spi, const struct adis_data *data)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	if (!data || !data->timeouts) {
52262306a36Sopenharmony_ci		dev_err(&spi->dev, "No config data or timeouts not defined!\n");
52362306a36Sopenharmony_ci		return -EINVAL;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	mutex_init(&adis->state_lock);
52762306a36Sopenharmony_ci	adis->spi = spi;
52862306a36Sopenharmony_ci	adis->data = data;
52962306a36Sopenharmony_ci	iio_device_set_drvdata(indio_dev, adis);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (data->has_paging) {
53262306a36Sopenharmony_ci		/* Need to set the page before first read/write */
53362306a36Sopenharmony_ci		adis->current_page = -1;
53462306a36Sopenharmony_ci	} else {
53562306a36Sopenharmony_ci		/* Page will always be 0 */
53662306a36Sopenharmony_ci		adis->current_page = 0;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	return 0;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(adis_init, IIO_ADISLIB);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
54462306a36Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
54562306a36Sopenharmony_ciMODULE_DESCRIPTION("Common library code for ADIS16XXX devices");
546