xref: /openbmc/linux/drivers/iio/imu/adis.c (revision 97f1755e)
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