this is testing

Revision:
0:3afcd581558d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/noos_mbed/drivers/adc/ad7606/ad7606.c	Thu Jan 14 18:54:16 2021 +0530
@@ -0,0 +1,1251 @@
+/***************************************************************************//**
+ *   @file   ad7606.c
+ *   @brief  Implementation of ad7606 Driver.
+ *   @author Stefan Popa (stefan.popa@analog.com)
+ *   @author Darius Berghe (darius.berghe@analog.com)
+********************************************************************************
+ * Copyright 2020(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *  - Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  - Neither the name of Analog Devices, Inc. nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *  - The use of this software may or may not infringe the patent rights
+ *    of one or more patent holders.  This license does not release you
+ *    from the requirement that you obtain separate licenses from these
+ *    patent holders to use this software.
+ *  - Use of the software either in source or binary form, must be run
+ *    on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include "ad7606.h"
+#include "error.h"
+#include "util.h"
+#include "crc.h"
+
+struct ad7606_chip_info {
+	uint8_t num_channels;
+	uint8_t bits;
+	uint8_t max_dout_lines;
+	bool has_oversampling;
+	bool has_registers;
+	uint8_t device_id;
+	const struct ad7606_range *hw_range_table;
+	uint32_t hw_range_table_sz;
+	const struct ad7606_range *sw_range_table;
+	uint32_t sw_range_table_sz;
+};
+
+DECLARE_CRC8_TABLE(ad7606_crc8);
+DECLARE_CRC16_TABLE(ad7606_crc16);
+
+static const struct ad7606_range ad7606_range_table[] = {
+	{-5000, 5000, false},	/* RANGE pin LOW */
+	{-10000, 10000, false},	/* RANGE pin HIGH */
+};
+
+static const struct ad7606_range ad7609_range_table[] = {
+	{-10000, 10000, true},	/* RANGE pin LOW */
+	{-20000, 20000, true},	/* RANGE pin HIGH */
+};
+
+static const struct ad7606_range ad7606b_range_table[] = {
+	{-2500, 2500, false},	/* 0000 */
+	{-5000, 5000, false},	/* 0001 */
+	{-10000, 10000, false},	/* 0010 */
+	{-10000, 10000, false},	/* 0011 */
+	{-10000, 10000, false},	/* 0100 */
+	{-10000, 10000, false},	/* 0101 */
+	{-10000, 10000, false},	/* 0110 */
+	{-10000, 10000, false},	/* 0111 */
+	{-10000, 10000, false},	/* 1000 */
+	{-10000, 10000, false},	/* 1001 */
+	{-10000, 10000, false},	/* 1010 */
+	{-10000, 10000, false},	/* 1011 */
+};
+
+static const struct ad7606_range ad7606c_range_table[] = {
+	{-2500, 2500, false},	/* 0000 */
+	{-5000, 5000, false},	/* 0001 */
+	{-6250, 6250, false},	/* 0010 */
+	{-10000, 10000, false},	/* 0011 */
+	{-12500, 12500, false},	/* 0100 */
+	{0, 5000, false},	/* 0101 */
+	{0, 10000, false},	/* 0110 */
+	{0, 12500, false},	/* 0111 */
+	{-5000, 5000, true},	/* 1000 */
+	{-10000, 10000, true},	/* 1001 */
+	{-12500, 12500, true},	/* 1010 */
+	{-20000, 20000, true},	/* 1011 */
+	{-20000, 20000, true},	/* 1100 */
+	{-20000, 20000, true},	/* 1101 */
+	{-20000, 20000, true},	/* 1110 */
+	{-20000, 20000, true},	/* 1111 */
+};
+
+static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
+	[ID_AD7605_4] = {
+		.num_channels = 4,
+		.bits = 16,
+		.max_dout_lines = AD7606_2_DOUT,
+		.has_oversampling = false,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+	},
+	[ID_AD7606_4] = {
+		.num_channels = 4,
+		.bits = 16,
+		.max_dout_lines = AD7606_2_DOUT,
+		.has_oversampling = true,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+	},
+	[ID_AD7606_6] = {
+		.num_channels = 6,
+		.bits = 16,
+		.max_dout_lines = AD7606_2_DOUT,
+		.has_oversampling = true,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+	},
+	[ID_AD7606_8] = {
+		.num_channels = 8,
+		.bits = 16,
+		.max_dout_lines = AD7606_2_DOUT,
+		.has_oversampling = true,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+	},
+	[ID_AD7606B] = {
+		.num_channels = 8,
+		.bits = 16,
+		.max_dout_lines = AD7606_4_DOUT,
+		.has_oversampling = true,
+		.has_registers = true,
+		.device_id = 0x1,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+		.sw_range_table = ad7606b_range_table,
+		.sw_range_table_sz = ARRAY_SIZE(ad7606b_range_table),
+	},
+	[ID_AD7606C_16] = {
+		.num_channels = 8,
+		.bits = 16,
+		.max_dout_lines = AD7606_8_DOUT,
+		.has_oversampling = true,
+		.has_registers = true,
+		.device_id = 0x3,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+		.sw_range_table = ad7606c_range_table,
+		.sw_range_table_sz = ARRAY_SIZE(ad7606c_range_table),
+	},
+	[ID_AD7606C_18] = {
+		.num_channels = 8,
+		.bits = 18,
+		.max_dout_lines = AD7606_8_DOUT,
+		.has_oversampling = true,
+		.has_registers = true,
+		.device_id = 0x3,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+		.sw_range_table = ad7606c_range_table,
+		.sw_range_table_sz = ARRAY_SIZE(ad7606c_range_table),
+	},
+	[ID_AD7608] = {
+		.num_channels = 8,
+		.bits = 18,
+		.max_dout_lines = AD7606_2_DOUT,
+		.has_oversampling = true,
+		.hw_range_table = ad7606_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7606_range_table),
+	},
+	[ID_AD7609] = {
+		.num_channels = 8,
+		.bits = 18,
+		.max_dout_lines = AD7606_2_DOUT,
+		.has_oversampling = true,
+		.hw_range_table = ad7609_range_table,
+		.hw_range_table_sz = ARRAY_SIZE(ad7609_range_table),
+	},
+};
+
+static const uint16_t tconv_max[] = {
+	1, 	/* AD7606_OSR_1 */
+	3,	/* AD7606_OSR_2 */
+	5,	/* AD7606_OSR_4 */
+	10,	/* AD7606_OSR_8 */
+	20,	/* AD7606_OSR_16 */
+	41,	/* AD7606_OSR_32 */
+	81,	/* AD7606_OSR_64 */
+	162,	/* AD7606_OSR_128 */
+	324	/* AD7606_OSR_256 */
+};
+
+
+/***************************************************************************//**
+ * @brief Read a device register via SPI.
+ *
+ * This function performs CRC8 computation and checking if enabled in the device.
+ *
+ * @param dev        - The device structure.
+ * @param reg_addr   - Register address in device memory.
+ * @param reg_data   - Pointer to the location where to store the register value.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -ENOTSUP - Device not in software mode.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_spi_reg_read(struct ad7606_dev *dev,
+			    uint8_t reg_addr,
+			    uint8_t *reg_data)
+{
+	uint8_t buf[3];
+	uint8_t crc;
+	uint32_t sz = 2;
+	int32_t ret;
+
+	if (!dev->sw_mode)
+		return -ENOTSUP;
+
+	buf[0] = AD7606_RD_FLAG_MSK(reg_addr);
+	buf[1] = 0x00;
+	if (dev->digital_diag_enable.int_crc_err_en) {
+		crc = crc8(ad7606_crc8, buf, 2, 0);
+		buf[2] = crc;
+		sz += 1;
+	}
+	ret = spi_write_and_read(dev->spi_desc, buf, sz);
+	if (ret < 0)
+		return ret;
+
+	dev->reg_mode = true;
+
+	buf[0] = AD7606_RD_FLAG_MSK(reg_addr);
+	buf[1] = 0x00;
+	if (dev->digital_diag_enable.int_crc_err_en) {
+		crc = crc8(ad7606_crc8, buf, 2, 0);
+		buf[2] = crc;
+	}
+	ret = spi_write_and_read(dev->spi_desc, buf, sz);
+	if (ret < 0)
+		return ret;
+
+	if (dev->digital_diag_enable.int_crc_err_en) {
+		crc = crc8(ad7606_crc8, buf, 2, 0);
+		if (crc != buf[2])
+			return -EBADMSG;
+	}
+
+	if (reg_data)
+		*reg_data = buf[1];
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Write a device register via SPI.
+ *
+ * This function performs CRC8 computation and checking if enabled in the device.
+ *
+ * @param dev        - The device structure.
+ * @param reg_addr   - Register address in device memory.
+ * @param reg_data   - Value to write to register.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -ENOTSUP - Device not in software mode.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_spi_reg_write(struct ad7606_dev *dev,
+			     uint8_t reg_addr,
+			     uint8_t reg_data)
+{
+	uint8_t buf[3];
+	int32_t ret;
+	uint8_t crc;
+	uint32_t sz = 2;
+
+	if (!dev->sw_mode)
+		return -ENOTSUP;
+
+	/* Dummy read to place the chip in register mode. */
+	if (!dev->reg_mode) {
+		ret = ad7606_spi_reg_read(dev, reg_addr, NULL);
+		if (ret < 0)
+			return ret;
+	}
+
+	buf[0] = AD7606_WR_FLAG_MSK(reg_addr);
+	buf[1] = reg_data;
+	if (dev->digital_diag_enable.int_crc_err_en) {
+		crc = crc8(ad7606_crc8, buf, 2, 0);
+		buf[2] = crc;
+		sz += 1;
+	}
+
+	ret = spi_write_and_read(dev->spi_desc, buf, sz);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Write a device register via SPI with masking.
+ *
+ * @param dev        - The device structure.
+ * @param addr       - Register address in device memory.
+ * @param mask       - Only bits set to 1 in mask will be modified.
+ * @param val        - Value to write to register.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -ENOTSUP - Device not in software mode.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_spi_write_mask(struct ad7606_dev *dev,
+			      uint32_t addr,
+			      uint32_t mask,
+			      uint32_t val)
+{
+	uint8_t reg_data;
+	int ret;
+
+	ret = ad7606_spi_reg_read(dev, addr, &reg_data);
+	if (ret < 0)
+		return ret;
+
+	reg_data &= ~mask;
+	reg_data |= val;
+
+	return ad7606_spi_reg_write(dev, addr, reg_data);
+}
+
+/* Internal function to copy the content of a buffer in 18-bit chunks to a 32-bit buffer by
+ * extending the chunks to 32-bit size. */
+static int32_t cpy18b32b(uint8_t *psrc, uint32_t srcsz, uint32_t *pdst)
+{
+	unsigned int i, j;
+
+	if (srcsz % 9)
+		return -EINVAL;
+
+	for(i = 0; i < srcsz; i += 9) {
+		j = 4 * (i / 9);
+		pdst[j+0] = ((uint32_t)(psrc[i+0] & 0xff) << 10) | ((uint32_t)psrc[i+1] << 2)
+			    | ((uint32_t)psrc[i+2] >> 6);
+		pdst[j+1] = ((uint32_t)(psrc[i+2] & 0x3f) << 12) | ((uint32_t)psrc[i+3] << 4)
+			    | ((uint32_t)psrc[i+4] >> 4);
+		pdst[j+2] = ((uint32_t)(psrc[i+4] & 0x0f) << 14) | ((uint32_t)psrc[i+5] << 6)
+			    | ((uint32_t)psrc[i+6] >> 2);
+		pdst[j+3] = ((uint32_t)(psrc[i+6] & 0x03) << 16) | ((uint32_t)psrc[i+7] << 8)
+			    | ((uint32_t)psrc[i+8] >> 0);
+	}
+	return SUCCESS;
+}
+
+/* Internal function to copy the content of a buffer in 26-bit chunks to a 32-bit buffer by
+ * extending the chunks to 32-bit size. */
+static int32_t cpy26b32b(uint8_t *psrc, uint32_t srcsz, uint32_t *pdst)
+{
+	unsigned int i, j;
+
+	if (srcsz % 13)
+		return -EINVAL;
+
+	for(i = 0; i < srcsz; i += 13) {
+		j = 4 * (i / 13);
+		pdst[j+0] = ((uint32_t)(psrc[i+0] & 0xff) << 18) | ((uint32_t)psrc[i+1] << 10)
+			    | ((uint32_t)psrc[i+2] << 2) | ((uint32_t)psrc[i+3] >> 6);
+		pdst[j+1] = ((uint32_t)(psrc[i+3] & 0x3f) << 20) | ((uint32_t)psrc[i+4] << 12)
+			    | ((uint32_t)psrc[i+5] << 4) | ((uint32_t)psrc[i+6] >> 4);
+		pdst[j+2] = ((uint32_t)(psrc[i+6] & 0x0f) << 22) | ((uint32_t)psrc[i+7] << 14)
+			    | ((uint32_t)psrc[i+8] << 6) | ((uint32_t)psrc[i+9] >> 2);
+		pdst[j+3] = ((uint32_t)(psrc[i+9] & 0x03) << 24) | ((uint32_t)psrc[i+10] << 16)
+			    | ((uint32_t)psrc[i+11] << 8) | ((uint32_t)psrc[i+12] >> 0);
+	}
+	return SUCCESS;
+}
+
+/***************************************************************************//**
+ * @brief Toggle the CONVST pin to start a conversion.
+ *
+ * If needed, this function also puts the device in ADC reading mode by a write
+ * at address zero.
+ *
+ * @param dev        - The device structure.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EIO - CONVST GPIO not available.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_convst(struct ad7606_dev *dev)
+{
+	int32_t ret;
+
+	if (dev->reg_mode) {
+		/* Enter ADC reading mode by writing at address zero. */
+		ret = ad7606_spi_reg_write(dev, 0, 0);
+		if (ret < 0)
+			return ret;
+
+		dev->reg_mode = false;
+	}
+
+	ret = gpio_set_value(dev->gpio_convst, 0);
+	if (ret < 0)
+		return ret;
+
+	/* wait LP_CNV time */
+	udelay(1);
+
+	return gpio_set_value(dev->gpio_convst, 1);
+}
+
+/***************************************************************************//**
+ * @brief Read conversion data.
+ *
+ * This function performs CRC16 computation and checking if enabled in the device.
+ * If the status is enabled in device settings, each sample of data will contain
+ * status information in the lowest 8 bits.
+ *
+ * The output buffer provided by the user should be as wide as to be able to
+ * contain 1 sample from each channel since this function reads conversion data
+ * across all channels.
+ *
+ * @param dev        - The device structure.
+ * @param data       - Pointer to location of buffer where to store the data.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  -ENOTSUP - Device bits per sample not supported.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_spi_data_read(struct ad7606_dev *dev, uint32_t *data)
+{
+	uint32_t sz;
+	int32_t ret, i;
+	uint16_t crc, icrc;
+	uint8_t bits = ad7606_chip_info_tbl[dev->device_id].bits;
+	uint8_t sbits = dev->config.status_header ? 8 : 0;
+	uint8_t nchannels = ad7606_chip_info_tbl[dev->device_id].num_channels;
+
+	sz = nchannels * (bits + sbits);
+
+	/* Number of bits to read, corresponds to SCLK cycles in transfer.
+	 * This should always be a multiple of 8 to work with most SPI's.
+	 * With this chip family this holds true because we either:
+	 *  - multiply 8 channels * bits per sample
+	 *  - multiply 4 channels * bits per sample (always multiple of 2)
+	 * Therefore, due to design reasons, we don't check for the
+	 * remainder of this division because it is zero by design.
+	 */
+	sz /= 8;
+
+	if (dev->digital_diag_enable.int_crc_err_en) {
+		sz += 2;
+	}
+
+	memset(dev->data, 0, sz);
+	ret = spi_write_and_read(dev->spi_desc, dev->data, sz);
+	if (ret < 0)
+		return ret;
+
+	if (dev->digital_diag_enable.int_crc_err_en) {
+		sz -= 2;
+		crc = crc16(ad7606_crc16, dev->data, sz, 0);
+		icrc = ((uint16_t)dev->data[sz] << 8) |
+		       dev->data[sz+1];
+		if (icrc != crc)
+			return -EBADMSG;
+	}
+
+	switch(bits) {
+	case 18:
+		if (dev->config.status_header)
+			ret = cpy26b32b(dev->data, sz, data);
+		else
+			ret = cpy18b32b(dev->data, sz, data);
+		if (ret < 0)
+			return ret;
+		break;
+	case 16:
+		for(i = 0; i < nchannels; i++) {
+			if (dev->config.status_header) {
+				data[i] = (uint32_t)dev->data[i*3] << 16;
+				data[i] |= (uint32_t)dev->data[i*3+1] << 8;
+				data[i] |= (uint32_t)dev->data[i*3+2];
+			} else {
+				data[i] = (uint32_t)dev->data[i*2] << 8;
+				data[i] |= (uint32_t)dev->data[i*2+1];
+			}
+		}
+		break;
+	default:
+		ret = -ENOTSUP;
+		break;
+	};
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Blocking conversion start and data read.
+ *
+ * This function performs a conversion start and then proceeds to reading
+ * the conversion data.
+ *
+ * @param dev        - The device structure.
+ * @param data       - Pointer to location of buffer where to store the data.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -ETIME - Timeout while waiting for the BUSY signal.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_read(struct ad7606_dev *dev, uint32_t * data)
+{
+	int32_t ret;
+	uint8_t busy;
+	uint32_t timeout = tconv_max[AD7606_OSR_256];
+
+	ret = ad7606_convst(dev);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_busy) {
+		/* Wait for BUSY falling edge */
+		while(timeout) {
+			ret = gpio_get_value(dev->gpio_busy, &busy);
+			if (ret < 0)
+				return ret;
+
+			if (busy == 0)
+				break;
+
+			udelay(1);
+			timeout--;
+		}
+
+		if (timeout == 0)
+			return -ETIME;
+	} else {
+		/* wait CONV time */
+		udelay(tconv_max[dev->oversampling.os_ratio]);
+	}
+
+	return ad7606_spi_data_read(dev, data);
+}
+
+/* Internal function to reset device settings to default state after chip reset. */
+static inline void ad7606_reset_settings(struct ad7606_dev *dev)
+{
+	int i;
+	const struct ad7606_range *rt = dev->sw_mode ?
+						ad7606_chip_info_tbl[dev->device_id].sw_range_table:
+						ad7606_chip_info_tbl[dev->device_id].hw_range_table;
+
+	for(i = 0; i < dev->num_channels; i++) {
+		if (dev->sw_mode)
+			dev->range_ch[i] = rt[3];
+		else
+			dev->range_ch[i] = rt[0];
+
+		dev->offset_ch[i] = 0;
+		dev->phase_ch[i] = 0;
+		dev->gain_ch[i] = 0;
+	}
+
+	dev->oversampling.os_ratio = AD7606_OSR_1;
+	dev->oversampling.os_pad = 0;
+	dev->config.op_mode = AD7606_NORMAL;
+	dev->config.dout_format = AD7606_2_DOUT;
+	dev->config.ext_os_clock = false;
+	dev->config.status_header = false;
+	dev->digital_diag_enable.rom_crc_err_en = true;
+	dev->digital_diag_enable.mm_crc_err_en = false;
+	dev->digital_diag_enable.int_crc_err_en = false;
+	dev->digital_diag_enable.spi_write_err_en = false;
+	dev->digital_diag_enable.spi_read_err_en = false;
+	dev->digital_diag_enable.busy_stuck_high_err_en = false;
+	dev->digital_diag_enable.clk_fs_os_counter_en = false;
+	dev->digital_diag_enable.interface_check_en = false;
+	dev->reg_mode = false;
+}
+
+/***************************************************************************//**
+ * @brief Reset the device by toggling the reset GPIO.
+ *
+ * @param dev        - The device structure.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - Reset GPIO not available.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_reset(struct ad7606_dev *dev)
+{
+	int32_t ret;
+
+	ret = gpio_set_value(dev->gpio_reset, 1);
+	if (ret < 0)
+		return ret;
+
+	udelay(3);
+
+	ret = gpio_set_value(dev->gpio_reset, 0);
+	if (ret < 0)
+		return ret;
+
+	ad7606_reset_settings(dev);
+
+	return ret;
+}
+
+/* Internal function that initializes GPIOs. */
+static int32_t ad7606_request_gpios(struct ad7606_dev *dev,
+				    struct ad7606_init_param *init_param)
+{
+	int32_t ret;
+
+	ret = gpio_get_optional(&dev->gpio_reset, init_param->gpio_reset);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_reset) {
+		ret = gpio_direction_output(dev->gpio_reset, GPIO_LOW);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = gpio_get_optional(&dev->gpio_convst, init_param->gpio_convst);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_convst) {
+		ret = gpio_direction_output(dev->gpio_convst, GPIO_LOW);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = gpio_get_optional(&dev->gpio_busy, init_param->gpio_busy);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_busy) {
+		ret = gpio_direction_input(dev->gpio_busy);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = gpio_get_optional(&dev->gpio_stby_n, init_param->gpio_stby_n);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_stby_n) {
+		ret = gpio_direction_output(dev->gpio_stby_n, GPIO_HIGH);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = gpio_get_optional(&dev->gpio_range, init_param->gpio_range);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_range) {
+		ret = gpio_direction_output(dev->gpio_range, GPIO_LOW);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (!ad7606_chip_info_tbl[dev->device_id].has_oversampling)
+		return ret;
+
+	ret = gpio_get_optional(&dev->gpio_os0, init_param->gpio_os0);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_os0) {
+		ret = gpio_direction_output(dev->gpio_os0, GPIO_LOW);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = gpio_get_optional(&dev->gpio_os1, init_param->gpio_os1);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_os1) {
+		ret = gpio_direction_output(dev->gpio_os1, GPIO_LOW);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = gpio_get_optional(&dev->gpio_os2, init_param->gpio_os2);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_os2) {
+		ret = gpio_direction_output(dev->gpio_os2, GPIO_LOW);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = gpio_get_optional(&dev->gpio_par_ser, init_param->gpio_par_ser);
+	if (ret < 0)
+		return ret;
+
+	if (dev->gpio_par_ser) {
+		/* Driver currently supports only serial interface, therefore,
+		 * if available, pull the GPIO HIGH. */
+		ret = gpio_direction_output(dev->gpio_par_ser, GPIO_HIGH);
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Set the oversampling ratio.
+ *
+ * In hardware mode, it silently sets AD7606_OSR_64 if higher oversampling
+ * is provided.
+ *
+ * @param dev          - The device structure.
+ * @param oversampling - Oversampling settings.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_set_oversampling(struct ad7606_dev *dev,
+				struct ad7606_oversampling oversampling)
+{
+	int32_t ret;
+	uint8_t val;
+
+	if (dev->sw_mode) {
+		val = field_prep(AD7606_OS_RATIO_MSK, oversampling.os_ratio);
+		val |= field_prep(AD7606_OS_PAD_MSK, oversampling.os_pad);
+		ret = ad7606_spi_reg_write(dev, AD7606_REG_OVERSAMPLING, val);
+		if (ret < 0)
+			return ret;
+	} else {
+		/* In hardware mode, OSR 128 and 256 are not avaialable */
+		if (oversampling.os_ratio > AD7606_OSR_64)
+			oversampling.os_ratio = AD7606_OSR_64;
+
+		ret = gpio_set_value(dev->gpio_os0, ((oversampling.os_ratio & 0x01) >> 0));
+		if (ret < 0)
+			return ret;
+
+		ret = gpio_set_value(dev->gpio_os1, ((oversampling.os_ratio & 0x02) >> 1));
+		if (ret < 0)
+			return ret;
+
+		ret = gpio_set_value(dev->gpio_os2, ((oversampling.os_ratio & 0x04) >> 2));
+		if (ret < 0)
+			return ret;
+	}
+
+	dev->oversampling = oversampling;
+
+	return SUCCESS;
+}
+
+/* Internal function to find the index of a given operation range in the
+ * operation range table specific to a device. */
+static int8_t ad7606_find_range(struct ad7606_dev *dev,
+				struct ad7606_range range)
+{
+	uint8_t i;
+	int8_t v = -1;
+	const struct ad7606_range *rt = dev->sw_mode ?
+						ad7606_chip_info_tbl[dev->device_id].sw_range_table:
+						ad7606_chip_info_tbl[dev->device_id].hw_range_table;
+
+	uint32_t rtsz = dev->sw_mode ?
+			ad7606_chip_info_tbl[dev->device_id].sw_range_table_sz:
+			ad7606_chip_info_tbl[dev->device_id].hw_range_table_sz;
+
+	for (i = 0; i < rtsz; i++) {
+		if (range.min != rt[i].min)
+			continue;
+		if (range.max != rt[i].max)
+			continue;
+		if (range.differential != rt[i].differential)
+			continue;
+		v = i;
+		break;
+	}
+
+	return v;
+}
+
+/***************************************************************************//**
+ * @brief Set the channel operation range.
+ *
+ * @param dev        - The device structure.
+ * @param ch         - Channel number (0-7).
+ * @param range      - Operation range.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EINVAL - Invalid input.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_set_ch_range(struct ad7606_dev *dev, uint8_t ch,
+			    struct ad7606_range range)
+{
+	int value;
+	int32_t ret;
+
+	if (range.min > range.max)
+		return -EINVAL;
+
+	if (ch >= dev->num_channels)
+		return -EINVAL;
+
+	value = ad7606_find_range(dev, range);
+	if (value < 0)
+		return -EINVAL;
+
+	if (dev->sw_mode)
+		ret = ad7606_spi_write_mask(dev, AD7606_REG_RANGE_CH_ADDR(ch),
+					    AD7606_RANGE_CH_MSK(ch),
+					    AD7606_RANGE_CH_MODE(ch, value));
+	else
+		ret = gpio_set_value(dev->gpio_range, value);
+
+	if (ret)
+		return ret;
+
+	dev->range_ch[ch] = range;
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Set the channel offset.
+ *
+ * The offset parameter is a signed 8-bit integer ranging from -128 to 127 to
+ * make it intuitive and user-friendly.
+ *
+ * This offset gets converted to the register representation where 0x80 is
+ * calibration offset 0, 0x0 is calibration offset -128 and 0xFF is calibration
+ * offset 127, etc.
+ *
+ * @param dev        - The device structure.
+ * @param ch         - Channel number (0-7).
+ * @param offset     - Offset calibration amount (-128...127).
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EINVAL - Invalid input.
+ *                  -ENOTSUP - Device not in software mode.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_set_ch_offset(struct ad7606_dev *dev, uint8_t ch,
+			     int8_t offset)
+{
+	int ret;
+	uint8_t value = (uint8_t)(offset - 0x80);
+
+	if (ch >= dev->num_channels)
+		return -EINVAL;
+
+	if (!dev->sw_mode)
+		return -ENOTSUP;
+
+	ret = ad7606_spi_reg_write(dev, AD7606_REG_OFFSET_CH(ch), value);
+	if (ret < 0)
+		return ret;
+
+	dev->offset_ch[ch] = offset;
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Set the channel phase.
+ *
+ * @param dev        - The device structure.
+ * @param ch         - Channel number (0-7).
+ * @param phase      - Phase calibration amount.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EINVAL - Invalid input.
+ *                  -ENOTSUP - Device not in software mode.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_set_ch_phase(struct ad7606_dev *dev, uint8_t ch,
+			    uint8_t phase)
+{
+	int ret;
+
+	if (ch >= dev->num_channels)
+		return -EINVAL;
+
+	if (!dev->sw_mode)
+		return -ENOTSUP;
+
+	ret = ad7606_spi_reg_write(dev, AD7606_REG_PHASE_CH(ch), phase);
+	if (ret < 0)
+		return ret;
+
+	dev->phase_ch[ch] = phase;
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Set the channel gain.
+ *
+ * @param dev        - The device structure.
+ * @param ch         - Channel number (0-7).
+ * @param gain       - Gain calibration amount.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EINVAL - Invalid input.
+ *                  -ENOTSUP - Device not in software mode.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_set_ch_gain(struct ad7606_dev *dev, uint8_t ch,
+			   uint8_t gain)
+{
+	int ret;
+
+	if (ch >= dev->num_channels)
+		return -EINVAL;
+
+	if (!dev->sw_mode)
+		return -ENOTSUP;
+
+	gain = field_get(AD7606_GAIN_MSK, gain);
+	ret = ad7606_spi_reg_write(dev, AD7606_REG_GAIN_CH(ch), gain);
+	if (ret < 0)
+		return ret;
+
+	dev->gain_ch[ch] = gain;
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Set the device config register.
+ *
+ * Configuration structure affects the CONFIG register of the device.
+ *
+ * @param dev        - The device structure.
+ * @param config     - Configuration structure.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -EIO - GPIO not available.
+ *                  -EINVAL - Invalid input.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_set_config(struct ad7606_dev *dev,
+			  struct ad7606_config config)
+{
+	int32_t ret;
+	uint8_t val = 0;
+	uint8_t range_pin, stby_n_pin;
+
+	if (dev->sw_mode) {
+
+		val |= field_prep(AD7606_CONFIG_OPERATION_MODE_MSK, config.op_mode);
+		/* This driver currently supports only normal SPI with 1 DOUT line.
+		 * TODO: remove this check when implementing multi-line DOUT. */
+		if ((uint8_t)config.dout_format > AD7606_1_DOUT)
+			return -EINVAL;
+		if ((uint8_t)config.dout_format > (uint8_t)dev->max_dout_lines)
+			return -EINVAL;
+		val |= field_prep(AD7606_CONFIG_DOUT_FORMAT_MSK, config.dout_format);
+		val |= field_prep(AD7606_CONFIG_EXT_OS_CLOCK_MSK, config.ext_os_clock);
+		val |= field_prep(AD7606_CONFIG_STATUS_HEADER_MSK, config.status_header);
+
+		ret = ad7606_spi_reg_write(dev, AD7606_REG_CONFIG, val);
+		if (ret)
+			return ret;
+	} else {
+		switch(config.op_mode) {
+		case AD7606_NORMAL:
+			range_pin = GPIO_LOW;
+			stby_n_pin = GPIO_HIGH;
+			break;
+		case AD7606_STANDBY:
+			range_pin = GPIO_LOW;
+			stby_n_pin = GPIO_LOW;
+			break;
+		case AD7606_SHUTDOWN:
+			range_pin = GPIO_HIGH;
+			stby_n_pin = GPIO_LOW;
+			break;
+		default:
+			return -EINVAL;
+		};
+
+		ret = gpio_set_value(dev->gpio_stby_n, stby_n_pin);
+		if (ret)
+			return ret;
+
+		ret = gpio_set_value(dev->gpio_range, range_pin);
+		if (ret)
+			return ret;
+	}
+
+	dev->config = config;
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Set the device digital diagnostics configuration.
+ *
+ * Digital diagnostics structure affects the DIGITAL_DIAG register of the device.
+ *
+ * @param dev        - The device structure.
+ * @param diag       - Configuration structure.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  -ENOTSUP - Device not in software mode.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_set_digital_diag(struct ad7606_dev *dev,
+				struct ad7606_digital_diag diag)
+{
+	int32_t ret;
+	uint8_t val = 0;
+
+	if (!dev->sw_mode)
+		return -ENOTSUP;
+
+	val |= field_prep(AD7606_ROM_CRC_ERR_EN_MSK, diag.rom_crc_err_en);
+	val |= field_prep(AD7606_MM_CRC_ERR_EN_MSK, diag.mm_crc_err_en);
+	val |= field_prep(AD7606_INT_CRC_ERR_EN_MSK, diag.int_crc_err_en);
+	val |= field_prep(AD7606_SPI_WRITE_ERR_EN_MSK, diag.spi_write_err_en);
+	val |= field_prep(AD7606_SPI_READ_ERR_EN_MSK, diag.spi_read_err_en);
+	val |= field_prep(AD7606_BUSY_STUCK_HIGH_ERR_EN_MSK,
+			  diag.busy_stuck_high_err_en);
+	val |= field_prep(AD7606_CLK_FS_OS_COUNTER_EN_MSK, diag.clk_fs_os_counter_en);
+	val |= field_prep(AD7606_INTERFACE_CHECK_EN_MSK, diag.interface_check_en);
+
+	ret = ad7606_spi_reg_write(dev, AD7606_REG_DIGITAL_DIAG_ENABLE, val);
+	if (ret < 0)
+		return ret;
+
+	dev->digital_diag_enable = diag;
+
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Initialize the ad7606 device structure.
+ *
+ * Performs memory allocation of the device structure.
+ *
+ * @param device     - Pointer to location of device structure to write.
+ * @param init_param - Pointer to configuration of the driver.
+ *
+ * @return ret - return code.
+ *         Example: -ENOMEM - Memory allocation error.
+ *                  -EIO - SPI communication error.
+ *                  -EIO - GPIO initialization error.
+ *                  -ENODEV - Unexpected device id.
+ *                  -EBADMSG - CRC computation mismatch.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_init(struct ad7606_dev **device,
+		    struct ad7606_init_param *init_param)
+{
+	struct ad7606_dev *dev;
+	uint8_t reg, id;
+	int32_t i, ret;
+
+	crc8_populate_msb(ad7606_crc8, 0x7);
+	crc16_populate_msb(ad7606_crc16, 0x755b);
+
+	dev = (struct ad7606_dev *)calloc(1, sizeof(*dev));
+	if (!dev)
+		return -ENOMEM;
+
+	dev->device_id = init_param->device_id;
+	dev->num_channels = ad7606_chip_info_tbl[dev->device_id].num_channels;
+	dev->max_dout_lines = ad7606_chip_info_tbl[dev->device_id].max_dout_lines;
+	if (ad7606_chip_info_tbl[dev->device_id].has_registers)
+		dev->sw_mode = init_param->sw_mode;
+
+	ret = ad7606_request_gpios(dev, init_param);
+	if (ret < 0)
+		goto error;
+
+	if (init_param->sw_mode) {
+		ret = gpio_set_value(dev->gpio_os0, GPIO_HIGH);
+		if (ret < 0)
+			goto error;
+
+		ret = gpio_set_value(dev->gpio_os1, GPIO_HIGH);
+		if (ret < 0)
+			goto error;
+
+		ret = gpio_set_value(dev->gpio_os2, GPIO_HIGH);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = ad7606_reset(dev);
+	if (ret < 0)
+		goto error;
+
+	/* wait DEVICE_SETUP time */
+	udelay(253);
+
+	ret = spi_init(&dev->spi_desc, &init_param->spi_init);
+	if (ret < 0)
+		goto error;
+
+	if (dev->sw_mode) {
+		ret = ad7606_spi_reg_read(dev, AD7606_REG_ID, &reg);
+		if (ret < 0)
+			goto error;
+
+		id = ad7606_chip_info_tbl[dev->device_id].device_id;
+		if (field_get(AD7606_ID_DEVICE_ID_MSK, reg) != id) {
+			printf("ad7606: device id mismatch, expected 0x%.2x, got 0x%.2x\n",
+			       id,
+			       (int)field_get(AD7606_ID_DEVICE_ID_MSK, reg));
+			ret = -ENODEV;
+			goto error;
+		}
+
+		ret = ad7606_set_digital_diag(dev, init_param->digital_diag_enable);
+		if (ret < 0)
+			goto error;
+
+		ret = ad7606_set_config(dev, init_param->config);
+		if (ret < 0)
+			goto error;
+
+		for (i = 0; i < dev->num_channels; i++) {
+			ret = ad7606_set_ch_range(dev, i, init_param->range_ch[i]);
+			if (ret < 0)
+				goto error;
+		}
+
+		for(i = 0; i < dev->num_channels; i++) {
+			ret = ad7606_set_ch_offset(dev, i, init_param->offset_ch[i]);
+			if (ret < 0)
+				goto error;
+		}
+
+		for(i = 0; i < dev->num_channels; i++) {
+			ret = ad7606_set_ch_phase(dev, i, init_param->phase_ch[i]);
+			if (ret < 0)
+				goto error;
+		}
+
+		for(i = 0; i < dev->num_channels; i++) {
+			ret = ad7606_set_ch_gain(dev, i, init_param->gain_ch[i]);
+			if (ret < 0)
+				goto error;
+		}
+	} else {
+		ret = ad7606_set_ch_range(dev, 0, init_param->range_ch[0]);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = gpio_set_value(dev->gpio_convst, 1);
+	if (ret < 0)
+		goto error;
+
+	if (ad7606_chip_info_tbl[dev->device_id].has_oversampling)
+		ad7606_set_oversampling(dev, init_param->oversampling);
+
+	*device = dev;
+
+	printf("ad7606 successfully initialized\n");
+
+	return ret;
+error:
+	printf("ad7606 initialization failed\n");
+	ad7606_remove(dev);
+	return ret;
+}
+
+/***************************************************************************//**
+ * @brief Free any resource used by the driver.
+ *
+ * @param dev        - The device structure.
+ *
+ * @return ret - return code.
+ *         Example: -EIO - SPI communication error.
+ *                  SUCCESS - No errors encountered.
+*******************************************************************************/
+int32_t ad7606_remove(struct ad7606_dev *dev)
+{
+	int32_t ret;
+
+	gpio_remove(dev->gpio_reset);
+	gpio_remove(dev->gpio_convst);
+	gpio_remove(dev->gpio_busy);
+	gpio_remove(dev->gpio_stby_n);
+	gpio_remove(dev->gpio_range);
+	gpio_remove(dev->gpio_os0);
+	gpio_remove(dev->gpio_os1);
+	gpio_remove(dev->gpio_os2);
+	gpio_remove(dev->gpio_par_ser);
+
+	ret = spi_remove(dev->spi_desc);
+
+	free(dev);
+
+	return ret;
+}