Example program for EVAL-AD4696.

Dependencies:   platform_drivers

app/ad469x.c

Committer:
Kjansen
Date:
2021-09-30
Revision:
1:edd760d6380f

File content as of revision 1:edd760d6380f:

/***************************************************************************//**
 *   @file   ad469x.c
 *   @brief  Implementation of ad469x Driver.
 *   @author Cristian Pop (cristian.pop@analog.com)
********************************************************************************
 * Copyright 2021(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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "ad469x.h"
#include "delay.h"
#include "error.h"
#include "util.h"
#include "gpio.h"

#define AD469x_TEST_DATA 0xEA
/******************************************************************************/
/********************** Macros and Constants Definitions **********************/
/******************************************************************************/

/**
 * @brief Device resolution
 */
const uint8_t ad469x_device_resol[] = {
	[AD469x_OSR_1] = 16,
	[AD469x_OSR_4] = 17,
	[AD469x_OSR_16] = 18,
	[AD469x_OSR_64] = 19
};

/******************************************************************************/
/************************** Functions Implementation **************************/
/******************************************************************************/

/**
 * Read from device.
 * @param dev - The device structure.
 * @param reg_addr - The register address.
 * @param reg_data - The register data.
 * @return SUCCESS in case of success, negative error code otherwise.
 */
int32_t ad469x_spi_reg_read(struct ad469x_dev *dev,
	uint16_t reg_addr,
	uint8_t *reg_data)
{
	int32_t ret;
	uint8_t buf[3];

#if defined (ENABLE_SPI_ENGINE)
	ret = spi_engine_set_transfer_width(dev->spi_desc, dev->reg_data_width);
	if (ret != SUCCESS)
		return ret;

	spi_engine_set_speed(dev->spi_desc, dev->reg_access_speed);
#endif

	buf[0] = (1 << 7) | ((reg_addr >> 8) & 0x7F);
	buf[1] = 0xFF & reg_addr;
	buf[2] = 0xFF;

	ret = spi_write_and_read(dev->spi_desc, buf, 3);
	*reg_data = buf[2];

#if defined (ENABLE_SPI_ENGINE)  
	ret = spi_engine_set_transfer_width(dev->spi_desc, dev->capture_data_width);
	if (ret != SUCCESS)
		return ret;

	spi_engine_set_speed(dev->spi_desc, dev->spi_desc->max_speed_hz);
#endif

	return ret;
}

/**
 * Write to device.
 * @param dev - The device structure.
 * @param reg_addr - The register address.
 * @param reg_data - The register data.
 * @@eturn SUCCESS in case of success, negative error code otherwise.
 */
int32_t ad469x_spi_reg_write(struct ad469x_dev *dev,
	uint16_t reg_addr,
	uint8_t reg_data)
{
	int32_t ret;
	uint8_t buf[3];

#if defined (ENABLE_SPI_ENGINE) 
	ret = spi_engine_set_transfer_width(dev->spi_desc, dev->reg_data_width);
	if (ret != SUCCESS)
		return ret;

	spi_engine_set_speed(dev->spi_desc, dev->reg_access_speed);
#endif

	buf[0] = ((reg_addr >> 8) & 0x7F);
	buf[1] = 0xFF & reg_addr;
	buf[2] = reg_data;

	ret = spi_write_and_read(dev->spi_desc, buf, 3);

#if defined (ENABLE_SPI_ENGINE)
	ret = spi_engine_set_transfer_width(dev->spi_desc,
		dev->capture_data_width);
	if (ret != SUCCESS)
		return ret;

	spi_engine_set_speed(dev->spi_desc, dev->spi_desc->max_speed_hz);
#endif

	return ret;
}

/**
 * SPI read from device using a mask.
 * @param dev - The device structure.
 * @param reg_addr - The register address.
 * @param mask - The mask.
 * @param data - The register data.
 * @return SUCCESS in case of success, negative error code otherwise.
 */
int32_t ad469x_spi_read_mask(struct ad469x_dev *dev,
	uint16_t reg_addr,
	uint8_t mask,
	uint8_t *data)
{
	uint8_t reg_data[3];
	int32_t ret;

	ret = ad469x_spi_reg_read(dev, reg_addr, reg_data);
	if (ret != SUCCESS)
		return ret;

	*data = (reg_data[0] & mask);

	return ret;
}

/**
 * SPI write to device using a mask.
 * @param dev - The device structure.
 * @param reg_addr - The register address.
 * @param mask - The mask.
 * @param data - The register data.
 * @return SUCCESS in case of success, negative error code otherwise.
 */
int32_t ad469x_spi_write_mask(struct ad469x_dev *dev,
	uint16_t reg_addr,
	uint8_t mask,
	uint8_t data)
{
	uint8_t reg_data;
	int32_t ret;

	ret = ad469x_spi_reg_read(dev, reg_addr, &reg_data);
	if (ret != SUCCESS)
		return ret;

	reg_data &= ~mask;
	reg_data |= data;

	return ad469x_spi_reg_write(dev, reg_addr, reg_data);
}

/**
 * @brief Configure register access mode
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] access - Access mode
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_set_reg_access_mode(struct ad469x_dev *dev,
	enum ad469x_reg_access access)
{
	return ad469x_spi_write_mask(dev,
		AD469x_REG_IF_CONFIG_C,
		AD469x_REG_IF_CONFIG_C_MB_STRICT_MASK,
		AD469x_REG_IF_CONFIG_C_MB_STRICT(access));
}

/**
 * @brief Initialize GPIO driver handlers for the GPIOs in the system.
 *        ad469x_init() helper function.
 * @param [out] dev - ad469x_dev device handler.
 * @param [in] init_param - Pointer to the initialization structure.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
static int32_t ad469x_init_gpio(struct ad469x_dev *dev,
	struct ad469x_init_param *init_param)
{
	int32_t ret;

	do 
	{
		ret = gpio_get_optional(&dev->gpio_resetn, init_param->gpio_resetn);
		if (ret != SUCCESS)
			break;

		/** Reset to configure pins */
		if (init_param->gpio_resetn) {
			ret = gpio_direction_output(dev->gpio_resetn, GPIO_LOW);
			if (ret != SUCCESS)
				break;

			mdelay(100);
			ret = gpio_set_value(dev->gpio_resetn, GPIO_HIGH);
			if (ret != SUCCESS)
				break;

			mdelay(100);
		}
	
		/** Initialize the BUSY GPIO descriptor **/
		ret = gpio_get_optional(&dev->gpio_busy, init_param->gpio_busy);
		if (ret != SUCCESS)
			break;
		
		if (init_param->gpio_busy) {
			if (gpio_direction_input(dev->gpio_busy) != SUCCESS)
				break;
		}
		
		return SUCCESS;
	} while (0);

	return FAILURE;
}

/**
 * @brief Configure over sampling ratio in advanced sequencer mode
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] ch - Channel to configure.
 * @param [in] ratio - OSR ratio.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_adv_seq_osr(struct ad469x_dev *dev,
	uint16_t ch,
	enum ad469x_osr_ratios ratio)
{
	int32_t ret;

	if (dev->ch_sequence == AD469x_single_cycle ||
	    dev->ch_sequence == AD469x_two_cycle)
		return FAILURE;

	if (ch >= AD469x_CHANNEL_NO)
		return FAILURE;

	ret = ad469x_spi_write_mask(dev,
		AD469x_REG_CONFIG_IN(ch),
		AD469x_REG_CONFIG_IN_OSR_MASK,
		AD469x_REG_CONFIG_IN_OSR(ratio));
	if (ret != SUCCESS)
		return ret;

	dev->adv_seq_osr_resol[ch] = ad469x_device_resol[ratio];
	/* Set storage to maximum data width */
	dev->capture_data_width = ad469x_device_resol[AD469x_OSR_64];

	return SUCCESS;
}

/**
 * @brief Configure over sampling ratio to 1 in single and two cycle modes.
 * @param [in] dev - ad469x_dev device handler.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
static int32_t ad469x_seq_osr_clear(struct ad469x_dev *dev)
{
	int32_t ret;
	uint8_t i = 0;

	for (i = 0; i < AD469x_CHANNEL_NO; i++) {
		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_CONFIG_IN(i),
			AD469x_REG_CONFIG_IN_OSR_MASK,
			AD469x_REG_CONFIG_IN_OSR(AD469x_OSR_1));
		if (ret != SUCCESS)
			return ret;
		dev->adv_seq_osr_resol[i] = ad469x_device_resol[AD469x_OSR_1];
	}
	/* Set storage to minimum data width */
	dev->capture_data_width = ad469x_device_resol[AD469x_OSR_1];

	return SUCCESS;
}

/**
 * @brief Configure over sampling ratio in standard sequencer mode
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] ratio - OSR ratio.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_std_seq_osr(struct ad469x_dev *dev, enum ad469x_osr_ratios ratio)
{
	int ret;

	if (dev->ch_sequence == AD469x_single_cycle ||
	    dev->ch_sequence == AD469x_two_cycle)
		return FAILURE;

	ret = ad469x_spi_write_mask(dev,
		AD469x_REG_CONFIG_IN(0),
		AD469x_REG_CONFIG_IN_OSR_MASK,
		AD469x_REG_CONFIG_IN_OSR(ratio));
	if (ret != SUCCESS)
		return ret;

	dev->capture_data_width = ad469x_device_resol[ratio];

	return ret;
}

/**
 * @brief Set channel sequence.
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] seq - Channel sequence.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_set_channel_sequence(struct ad469x_dev *dev,
	enum ad469x_channel_sequencing seq)
{
	int32_t ret;

	switch (seq) {
	case AD469x_single_cycle:
		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SEQ_CTRL,
			AD469x_SEQ_CTRL_STD_SEQ_EN_MASK,
			AD469x_SEQ_CTRL_STD_SEQ_EN(0));
		if (ret != SUCCESS)
			return ret;

		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SEQ_CTRL,
			AD469x_SEQ_CTRL_NUM_SLOTS_AS_MASK,
			AD469x_SEQ_CTRL_NUM_SLOTS_AS(0));
		if (ret != SUCCESS)
			return ret;

		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SETUP,
			AD469x_SETUP_CYC_CTRL_MASK,
			AD469x_SETUP_CYC_CTRL_SINGLE(0));
		if (ret != SUCCESS)
			return ret;

		ret = ad469x_seq_osr_clear(dev);
		if (ret != SUCCESS)
			return ret;

		break;

	case AD469x_two_cycle:
		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SEQ_CTRL,
			AD469x_SEQ_CTRL_STD_SEQ_EN_MASK,
			AD469x_SEQ_CTRL_STD_SEQ_EN(0));
		if (ret != SUCCESS)
			return ret;

		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SEQ_CTRL,
			AD469x_SEQ_CTRL_NUM_SLOTS_AS_MASK,
			AD469x_SEQ_CTRL_NUM_SLOTS_AS(0));
		if (ret != SUCCESS)
			return ret;

		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SETUP,
			AD469x_SETUP_CYC_CTRL_MASK,
			AD469x_SETUP_CYC_CTRL_SINGLE(1));
		if (ret != SUCCESS)
			return ret;

		ret = ad469x_seq_osr_clear(dev);
		if (ret != SUCCESS)
			return ret;

		break;

	case AD469x_standard_seq:
		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SEQ_CTRL,
			AD469x_SEQ_CTRL_STD_SEQ_EN_MASK,
			AD469x_SEQ_CTRL_STD_SEQ_EN(1));
		if (ret != SUCCESS)
			return ret;

		break;

	case AD469x_advanced_seq:
		ret = ad469x_spi_write_mask(dev,
			AD469x_REG_SEQ_CTRL,
			AD469x_SEQ_CTRL_STD_SEQ_EN_MASK,
			AD469x_SEQ_CTRL_STD_SEQ_EN(0));
		if (ret != SUCCESS)
			return ret;

		break;

	default:
		return FAILURE;
		break;
	}

	dev->ch_sequence = seq;

	return ret;
}

/**
 * @brief Configure advanced sequencer number of slots, temp channel not
 * included
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] num_slots - Number of slots, max value = 0x7f
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_adv_sequence_set_num_slots(struct ad469x_dev *dev,
	uint8_t num_slots)
{
	int32_t ret;
	uint8_t write_num_slots = 0;

	if (num_slots)
		write_num_slots = num_slots - 1;

	ret = ad469x_spi_write_mask(dev,
		AD469x_REG_SEQ_CTRL,
		AD469x_SEQ_CTRL_NUM_SLOTS_AS_MASK,
		AD469x_SEQ_CTRL_NUM_SLOTS_AS(write_num_slots));
	if (ret != SUCCESS)
		return ret;

	dev->num_slots = num_slots;

	return SUCCESS;
}

/**
 * @brief Advanced sequencer, assign channel to a slot
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] slot - Slot number [0x00, 0x7f]
 * @param [in] channel - Assigned channel [0x00, 0x0f].
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_adv_sequence_set_slot(struct ad469x_dev *dev,
	uint8_t slot,
	uint8_t channel)
{
	int32_t ret;
	ret = ad469x_spi_reg_write(dev,
		AD469x_REG_AS_SLOT(slot),
		AD469x_REG_AS_SLOT_INX(channel));
	if (ret != SUCCESS)
		return ret;

	dev->ch_slots[slot] = channel;

	return SUCCESS;
}

/**
 * @brief Configure standard sequencer channels
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] ch_mask - Extra channels to activate.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_std_sequence_ch(struct ad469x_dev *dev, uint32_t ch_mask)
{
	int32_t ret;

	ret = ad469x_spi_reg_write(dev,
		AD469x_REG_STD_SEQ_CONFIG,
		0xff & ch_mask);
	if (ret != SUCCESS)
		return ret;

	ret = ad469x_spi_reg_write(dev,
		AD469x_REG_STD_SEQ_CONFIG + 1,
		ch_mask >> 8);
	if (ret != SUCCESS)
		return ret;

	dev->num_slots = hweight8(ch_mask);

	return ret;
}

/**
 * @brief Enable temperature read at the end of the sequence, for standard and
 * advanced sequencer
 * @param [in] dev - ad469x_dev device handler.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_sequence_enable_temp(struct ad469x_dev *dev)
{
	int32_t ret;

	ret = ad469x_spi_write_mask(dev,
		AD469x_REG_TEMP_CTRL,
		AD469x_REG_TEMP_CTRL_TEMP_EN_MASK,
		AD469x_REG_TEMP_CTRL_TEMP_EN(1));
	if (ret != SUCCESS)
		return ret;

	dev->temp_enabled = true;

	return ret;
}

/**
 * @brief Disable temperature read at the end of the sequence, for standard and
 * advanced sequencer
 * @param [in] dev - ad469x_dev device handler.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_sequence_disable_temp(struct ad469x_dev *dev)
{
	int32_t ret;

	ret = ad469x_spi_write_mask(dev,
		AD469x_REG_TEMP_CTRL,
		AD469x_REG_TEMP_CTRL_TEMP_EN_MASK,
		AD469x_REG_TEMP_CTRL_TEMP_EN(0));
	if (ret != SUCCESS)
		return ret;

	dev->temp_enabled = false;

	return ret;
}

/**
 * @brief Configure converter busy indicator to the output of the specified port
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] gp_sel - Port.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_set_busy(struct ad469x_dev *dev,
	enum ad469x_busy_gp_sel gp_sel)
{
	int32_t ret;

	ret = ad469x_spi_write_mask(dev,
		AD469x_REG_GP_MODE,
		AD469x_GP_MODE_BUSY_GP_EN_MASK,
		AD469x_GP_MODE_BUSY_GP_EN(1));
	if (ret != SUCCESS)
		return ret;


	ret = ad469x_spi_write_mask(dev,
		AD469x_REG_GP_MODE,
		AD469x_GP_MODE_BUSY_GP_SEL_MASK,
		AD469x_GP_MODE_BUSY_GP_SEL(gp_sel));
	if (ret != SUCCESS)
		return ret;


	return ret;
}

/**
 * @brief Enter conversion mode.
 *        To exit conversion mode send a 5 bit conversion mode command
 *        AD469x_CMD_REG_CONFIG_MODE
 * @param [in] dev - ad469x_dev device handler.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_enter_conversion_mode(struct ad469x_dev *dev)
{
	return ad469x_spi_write_mask(dev,
				     AD469x_REG_SETUP,
				     AD469x_SETUP_IF_MODE_MASK,
				     AD469x_SETUP_IF_MODE_CONV);
}

/**
 * @brief Exit conversion mode.
 *        Enter register mode to read/write registers
 * @param [in] dev - ad469x_dev device handler.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_exit_conversion_mode(struct ad469x_dev *dev)
{
	int32_t ret ; 
	
#if defined(ENABLE_SPI_ENGINE)
	uint32_t commands_data[1], buf;
	struct spi_engine_offload_message msg;
	uint32_t spi_eng_msg_cmds[3] = {
		CS_LOW,
		WRITE_READ(1),
		CS_HIGH
	};

	pwm_enable(dev->trigger_pwm_desc);

	commands_data[0] = AD469x_CMD_REG_CONFIG_MODE << 8;

	ret = spi_engine_offload_init(dev->spi_desc, dev->offload_init_param);
	if (ret != SUCCESS)
		return ret;

	msg.commands = spi_eng_msg_cmds;
	msg.no_commands = ARRAY_SIZE(spi_eng_msg_cmds);
	msg.rx_addr = (uint32_t)&buf;
	msg.commands_data = commands_data;

	ret = spi_engine_offload_transfer(dev->spi_desc, msg, 1);
	if (ret != SUCCESS)
		return ret;

	pwm_disable(dev->trigger_pwm_desc);
	if (ret != SUCCESS)
		return ret;
#else
	uint8_t cmd = AD469x_CMD_REG_CONFIG_MODE; 
	
	ret = spi_write_and_read(dev->spi_desc, &cmd, 1);
	if (ret != SUCCESS)
		return ret;
#endif

	return SUCCESS;
}

/**
 * @brief Advanced sequencer, get util data bits in a sample
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] cur_sample - Current sample number
 * @param [in] sample - Sample data
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
static int32_t ad469x_adv_seq_osr_get_util_data(struct ad469x_dev *dev,
	uint16_t cur_sample,
	uint32_t *sample)
{
	uint8_t cur_slot, cur_ch;

	cur_slot = cur_sample % (dev->num_slots + dev->temp_enabled);
	cur_ch = dev->ch_slots[cur_slot];

	/* Temperature channel sample */
	if (dev->temp_enabled && cur_slot == dev->num_slots)
		return SUCCESS;

	*sample = (*sample) >> (dev->capture_data_width -
				dev->adv_seq_osr_resol[cur_ch]);

	return SUCCESS;
}

/**
 * @brief Read from device when converter has the channel sequencer activated.
 *        Enter register mode to read/write registers
 * @param [in] dev - ad469x_dev device handler.
 * @param [out] buf - data buffer.
 * @param [in] samples - Number of samples per channel. For example, if  with
 * ad469x_std_sequence_ch 2 channel where activated, buf will be filled with
 * 10 samples for each of them. If temp is enable, the there will be an other 10
 * samples for temperature
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_seq_read_data(struct ad469x_dev *dev,
	uint32_t *buf,
	uint16_t samples)
{
	int32_t ret;
	uint16_t i;
	uint32_t total_samples;

	total_samples = samples * (dev->num_slots + dev->temp_enabled);
	ret = ad469x_read_data(dev, 0, buf, total_samples);
	if (ret != SUCCESS)
		return ret;

	if (dev->ch_sequence != AD469x_advanced_seq)
		return SUCCESS;

	for (i = 0; i < total_samples; i++) {
		ret = ad469x_adv_seq_osr_get_util_data(dev, i, &buf[i]);
		if (ret != SUCCESS)
			return ret;
	}

	return SUCCESS;
}

/**
 * @brief Read from device.
 *        Enter register mode to read/write registers
 * @param [in] dev - ad469x_dev device handler.
 * @param [in] channel - ad469x selected channel.
 * @param [out] buf - data buffer.
 * @param [in] samples - sample number.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_read_data(struct ad469x_dev *dev,
	uint8_t channel,
	uint32_t *buf,
	uint16_t samples)
{
	int32_t ret;

#if defined(ENABLE_SPI_ENGINE)
	uint32_t commands_data[1];
	struct spi_engine_offload_message msg;
	uint32_t spi_eng_msg_cmds[3] = {
		CS_LOW,
		WRITE_READ(1),
		CS_HIGH
	};
	if (channel < AD469x_CHANNEL_NO)
		commands_data[0] = AD469x_CMD_CONFIG_CH_SEL(channel) << 8;
	else if (channel == AD469x_CHANNEL_TEMP)
		commands_data[0] = AD469x_CMD_SEL_TEMP_SNSOR_CH << 8;
	else
		return FAILURE;

	pwm_enable(dev->trigger_pwm_desc);

	ret = spi_engine_offload_init(dev->spi_desc, dev->offload_init_param);
	if (ret != SUCCESS)
		return ret;
	
	msg.commands = spi_eng_msg_cmds;
	msg.no_commands = ARRAY_SIZE(spi_eng_msg_cmds);
	msg.rx_addr = (uint32_t)buf;
	msg.commands_data = commands_data;

	ret = spi_engine_offload_transfer(dev->spi_desc, msg, samples * 2);
	if (ret != SUCCESS)
		return ret;

	if (dev->dcache_invalidate_range)
		dev->dcache_invalidate_range(msg.rx_addr, samples * 4);
#else 
	// Dummy Data
	dev->data[0] = 0x0;
	dev->data[1] = 0x0;
	ret = spi_write_and_read(dev->spi_desc, dev->data, (samples * 2));
	if (ret != SUCCESS)
		return FAILURE;
		
	*buf = (uint16_t)(dev->data[0] << 8) | dev->data[1];
	
#endif
	
	return ret;
}

/**
 * Initialize the device.
 * @param [out] device - The device structure.
 * @param [in] init_param - The structure that contains the device initial
 * 		parameters.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t ad469x_init(struct ad469x_dev **device,
	struct ad469x_init_param *init_param)
{
	struct ad469x_dev *dev;
	int32_t ret;
	uint8_t data = 0;

	dev = (struct ad469x_dev *)malloc(sizeof(*dev));
	if (!dev)
		return FAILURE;

#if defined(SPI_ENGINE)
	ret = axi_clkgen_init(&dev->clkgen, init_param->clkgen_init);
	if (ret != SUCCESS) {
		printf("error: %s: axi_clkgen_init() failed\n",
			init_param->clkgen_init->name);
		goto error_dev;
	}

	ret = axi_clkgen_set_rate(dev->clkgen, init_param->axi_clkgen_rate);
	if (ret != SUCCESS) {
		printf("error: %s: axi_clkgen_set_rate() failed\n",
			init_param->clkgen_init->name);
		goto error_clkgen;
	}
	
#endif

	ret = ad469x_init_gpio(dev, init_param);
	if (ret != SUCCESS)
		goto error_gpio;
	
	ret = spi_init(&dev->spi_desc, init_param->spi_init);
	if (ret != SUCCESS)
		goto error_gpio;

	dev->offload_init_param = init_param->offload_init_param;

	dev->reg_access_speed = init_param->reg_access_speed;
	dev->reg_data_width = init_param->reg_data_width;
	dev->capture_data_width = init_param->capture_data_width;
	dev->dev_id = init_param->dev_id;
	dev->dcache_invalidate_range = init_param->dcache_invalidate_range;
	dev->ch_sequence = AD469x_standard_seq;
	dev->num_slots = 0;
	dev->temp_enabled = false;
	memset(dev->ch_slots, 0, sizeof(dev->ch_slots));
	
	ret = ad469x_spi_reg_write(dev, AD469x_REG_SCRATCH_PAD, AD469x_TEST_DATA);
	if (ret != SUCCESS)
		goto error_spi;
	
	ret = ad469x_spi_reg_read(dev, AD469x_REG_SCRATCH_PAD, &data);
	if (ret != SUCCESS)
		goto error_spi;

 	if (data != AD469x_TEST_DATA)
		goto error_spi;

	ret = ad469x_set_reg_access_mode(dev, AD469x_BYTE_ACCESS);
	if (ret != SUCCESS)
		goto error_spi;

	ret = ad469x_set_busy(dev, AD469x_busy_gp0);
	if (ret != SUCCESS)
		goto error_spi;

	ret = ad469x_seq_osr_clear(dev);
	if (ret != SUCCESS)
		goto error_spi;

#if defined(SPI_ENGINE)
	ret = pwm_init(&dev->trigger_pwm_desc, init_param->trigger_pwm_init);
	if (ret != SUCCESS)
		goto error_spi;
#endif


	*device = dev;

	return ret;

error_spi:
	spi_remove(dev->spi_desc);
error_gpio:
	gpio_remove(dev->gpio_resetn);
	
#if defined(SPI_ENGINE)
error_clkgen:
	axi_clkgen_remove(dev->clkgen);
error_dev:
#endif
	
	free(dev);

	return FAILURE;
	
}

/**
 * @brief Free the memory allocated by ad469x_init().
 * @param [in] dev - Pointer to the device handler.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise
 */
int32_t ad469x_remove(struct ad469x_dev *dev)
{
	int32_t ret;

	if (!dev)
		return FAILURE;
	
	ret = spi_remove(dev->spi_desc);
	if (ret != SUCCESS)
		return ret;

	ret = gpio_remove(dev->gpio_resetn);
	if (ret != SUCCESS)
		return ret;
	
	ret = gpio_remove(dev->gpio_convst);
	if (ret != SUCCESS)
		return ret;
	
	ret = gpio_remove(dev->gpio_busy);
	if (ret != SUCCESS)
		return ret;

#if defined(SPI_ENGINE)
	ret = pwm_remove(dev->trigger_pwm_desc);
	if (ret != SUCCESS)
		return ret;
	
	ret = axi_clkgen_remove(dev->clkgen);
	if (ret != SUCCESS)
		return ret;
#endif

	free(dev);

	return ret;
}