Example program for EVAL-AD4130

Dependencies:   tempsensors sdp_k1_sdram

Revision:
2:7b2b268ea49c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/ad4130_iio.c	Wed Jul 20 18:12:00 2022 +0530
@@ -0,0 +1,1154 @@
+/***************************************************************************//**
+ *   @file    ad4130_iio.c
+ *   @brief   Implementation of AD4130 IIO application interfaces
+ *   @details This module acts as an interface for AD4130 IIO application
+********************************************************************************
+ * Copyright (c) 2020-2022 Analog Devices, Inc.
+ * All rights reserved.
+ *
+ * This software is proprietary to Analog Devices, Inc. and its licensors.
+ * By using this software you agree to the terms of the associated
+ * Analog Devices Software License Agreement.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "app_config.h"
+#include "ad4130_iio.h"
+#include "ad4130_data_capture.h"
+#include "ad4130_support.h"
+#include "ad4130_user_config.h"
+#include "ad4130_temperature_sensor.h"
+#include "ad4130_regs.h"
+#include "no_os_error.h"
+
+/******** Forward declaration of getter/setter functions ********/
+static int iio_ad4130_attr_get(void *device, char *buf, uint32_t len,
+			       const struct iio_ch_info *channel, intptr_t priv);
+
+static int iio_ad4130_attr_set(void *device, char *buf, uint32_t len,
+			       const struct iio_ch_info *channel, intptr_t priv);
+
+static int iio_ad4130_attr_available_get(void *device, char *buf,
+		uint32_t len,
+		const struct iio_ch_info *channel, intptr_t priv);
+
+static int iio_ad4130_attr_available_set(void *device, char *buf,
+		uint32_t len,
+		const struct iio_ch_info *channel, intptr_t priv);
+
+/******************************************************************************/
+/************************ Macros/Constants ************************************/
+/******************************************************************************/
+
+#define AD4130_CHN_ATTR(_name, _priv) {\
+	.name = _name,\
+	.priv = _priv,\
+	.show = iio_ad4130_attr_get,\
+	.store = iio_ad4130_attr_set\
+}
+
+#define AD4130_CHN_AVAIL_ATTR(_name, _priv) {\
+	.name = _name,\
+	.priv = _priv,\
+	.show = iio_ad4130_attr_available_get,\
+	.store = iio_ad4130_attr_available_set\
+}
+
+#define AD4130_CH(_name, _idx, _type) {\
+	.name = _name, \
+	.ch_type = _type,\
+	.ch_out = 0,\
+	.indexed = true,\
+	.channel = _idx,\
+	.scan_index = _idx,\
+	.scan_type = &chn_scan,\
+	.attributes = ad4130_iio_ch_attributes\
+}
+
+/* Minimum sampling frequency supported/configured in the firmware.
+ * Note: This is not an actual device sampling frequency.
+ * It is just used for IIO oscilloscope timeout calculations. */
+#define AD4130_MIN_SAMPLING_FREQ	(50 / ADC_USER_CHANNELS)
+
+/* Default offset value for AD4130 */
+#define AD4130_DEFAULT_OFFSET		0x800000
+
+/* Bytes per sample. This count should divide the total 256 bytes into 'n' equivalent
+ * ADC samples as IIO library requests only 256bytes of data at a time in a given
+ * data read query.
+ * For 1 to 8-bit ADC, bytes per sample = 1 (2^0)
+ * For 9 to 16-bit ADC, bytes per sample = 2 (2^1)
+ * For 17 to 32-bit ADC, bytes per sample = 4 (2^2)
+ **/
+#define	BYTES_PER_SAMPLE	sizeof(uint32_t)	// For ADC resolution of 24-bits
+
+/* Number of data storage bits (needed for IIO client to plot ADC data) */
+#define CHN_STORAGE_BITS	(BYTES_PER_SAMPLE * 8)
+
+/* Number of adc samples for loadcell calibration */
+#define LOADCELL_SAMPLES_COUNT	10
+
+/* CJC channel is 1 (common sensor for all Thermocouples).
+ * Chn0 is used for TC connections */
+#define CJC_CHANNEL		1
+
+/* Shunt resistance (in ohms) for AVDD/IOVDD current calculation */
+#define I_RSENSE	10
+
+/* Multiplier for AVDD/IOVDD voltage calculation */
+#define V_SCALE		6
+
+/******************************************************************************/
+/*************************** Types Declarations *******************************/
+/******************************************************************************/
+
+/* IIO interface descriptor */
+static struct iio_desc *p_ad4130_iio_desc;
+
+/**
+ * Device name.
+ */
+static const char dev_name[] = ACTIVE_DEVICE_NAME;
+
+/**
+ * Pointer to the struct representing the AD4130 IIO device
+ */
+struct ad413x_dev *ad4130_dev_inst = NULL;
+
+/* Device attributes with default values */
+
+/* Scale attribute value per channel */
+static float attr_scale_val[ADC_USER_CHANNELS];
+
+/* IIOD channels scan structure */
+static struct scan_type chn_scan;
+
+/* AD4130 Attribute IDs */
+enum ad4130_attribute_id {
+	RAW_ATTR_ID,
+	SCALE_ATTR_ID,
+	OFFSET_ATTR_ID,
+	SAMPLING_FREQ_ATTR_ID,
+	DEMO_CONFIG_ATTR_ID,
+	INTERNAL_CALIB_ID,
+	SYSTEM_CALIB_ID,
+	LOADCELL_GAIN_CALIB_ID,
+	LOADCELL_OFFSET_CALIB_ID,
+};
+
+/* Calibration state */
+enum calibration_state {
+	FULL_SCALE_CALIB_STATE,
+	ZERO_SCALE_CALIB_STATE,
+	CALIB_COMPLETE_STATE
+};
+
+/* Calibration status */
+enum calib_status {
+	CALIB_NOT_DONE,
+	CALIB_IN_PROGRESS,
+	CALIB_DONE,
+	CALIB_ERROR,
+	CALIB_SKIPPED
+};
+
+/* ADC calibration configs */
+typedef struct {
+	uint32_t gain_before_calib;
+	uint32_t gain_after_calib;
+	uint32_t offset_before_calib;
+	uint32_t offset_after_calib;
+} adc_calibration_configs;
+
+/* ADC calibration variables */
+static enum calibration_state system_calibration_state = ZERO_SCALE_CALIB_STATE;
+static enum calibration_state internal_calibration_state =
+	FULL_SCALE_CALIB_STATE;
+static enum calib_status adc_calibration_status[ADC_USER_CHANNELS];
+static adc_calibration_configs adc_calibration_config[ADC_USER_CHANNELS];
+
+/* IIOD channels attributes list */
+static struct iio_attribute ad4130_iio_ch_attributes[] = {
+	AD4130_CHN_ATTR("raw", RAW_ATTR_ID),
+	AD4130_CHN_ATTR("scale", SCALE_ATTR_ID),
+	AD4130_CHN_ATTR("offset", OFFSET_ATTR_ID),
+	AD4130_CHN_ATTR("internal_calibration", INTERNAL_CALIB_ID),
+	AD4130_CHN_ATTR("system_calibration", SYSTEM_CALIB_ID),
+#if (ACTIVE_DEMO_MODE_CONFIG == LOADCELL_CONFIG)
+	AD4130_CHN_ATTR("loadcell_gain_calibration", LOADCELL_GAIN_CALIB_ID),
+	AD4130_CHN_ATTR("loadcell_offset_calibration", LOADCELL_OFFSET_CALIB_ID),
+#endif
+	END_ATTRIBUTES_ARRAY
+};
+
+/* IIOD device (global) attributes list */
+static struct iio_attribute ad4130_iio_global_attributes[] = {
+	AD4130_CHN_ATTR("sampling_frequency", SAMPLING_FREQ_ATTR_ID),
+	AD4130_CHN_ATTR("demo_config", DEMO_CONFIG_ATTR_ID),
+	END_ATTRIBUTES_ARRAY
+};
+
+/* IIOD context attributes list */
+static struct iio_context_attribute ad4130_iio_context_attributes[] = {
+	{
+		.name = "hw_mezzanine",
+		.value = HW_MEZZANINE_NAME
+	},
+	{
+		.name = "hw_carrier",
+		.value = HW_CARRIER_NAME
+	},
+	{
+		.name = "hw_name",
+		.value = HW_NAME
+	},
+	END_ATTRIBUTES_ARRAY
+};
+
+static struct iio_channel ad4130_iio_channels[] = {
+#if (ACTIVE_DEMO_MODE_CONFIG == THERMISTOR_CONFIG)
+	AD4130_CH("Sensor1", SENSOR_CHANNEL0, IIO_TEMP)
+#elif (ACTIVE_DEMO_MODE_CONFIG == RTD_3WIRE_CONFIG)
+	AD4130_CH("Sensor1", SENSOR_CHANNEL0, IIO_TEMP)
+#elif (ACTIVE_DEMO_MODE_CONFIG == RTD_2WIRE_CONFIG || \
+	   ACTIVE_DEMO_MODE_CONFIG == RTD_4WIRE_CONFIG)
+	AD4130_CH("Sensor1", SENSOR_CHANNEL0, IIO_TEMP)
+#elif (ACTIVE_DEMO_MODE_CONFIG == THERMOCOUPLE_CONFIG)
+	AD4130_CH("Sensor1", SENSOR_CHANNEL0, IIO_TEMP),
+	AD4130_CH("CJC", CJC_CHANNEL, IIO_TEMP),
+#elif (ACTIVE_DEMO_MODE_CONFIG == LOADCELL_CONFIG)
+	/* Note: Channel type is considered as voltage as IIO
+	 * oscilloscope doesn't support loadcell unit fomat of gram */
+	AD4130_CH("Sensor1", SENSOR_CHANNEL0, IIO_VOLTAGE),
+#elif (ACTIVE_DEMO_MODE_CONFIG == ECG_CONFIG)
+	AD4130_CH("Sensor1", SENSOR_CHANNEL0, IIO_VOLTAGE),
+#elif (ACTIVE_DEMO_MODE_CONFIG == NOISE_TEST_CONFIG)
+	AD4130_CH("Chn0", 0, IIO_VOLTAGE),
+#elif (ACTIVE_DEMO_MODE_CONFIG == POWER_TEST_CONFIG)
+	AD4130_CH("V_AVDD", POWER_TEST_V_AVDD_CHN, IIO_VOLTAGE),
+	AD4130_CH("V_IOVDD", POWER_TEST_V_IOVDD_CHN, IIO_VOLTAGE),
+	AD4130_CH("I_AVDD", POWER_TEST_I_AVDD_CHN, IIO_CURRENT),
+	AD4130_CH("I_IOVDD", POWER_TEST_I_IOVDD_CHN, IIO_CURRENT),
+	AD4130_CH("V_AVSS-DGND", POWER_TEST_V_AVSS_DGND_CHN, IIO_VOLTAGE),
+	AD4130_CH("V_REF", POWER_TEST_V_REF_CHN, IIO_VOLTAGE),
+#else
+	/* User default config */
+	AD4130_CH("Chn0", 0, IIO_VOLTAGE),
+	AD4130_CH("Chn1", 1, IIO_VOLTAGE),
+	AD4130_CH("Chn2", 2, IIO_VOLTAGE),
+	AD4130_CH("Chn3", 3, IIO_VOLTAGE),
+	AD4130_CH("Chn4", 4, IIO_VOLTAGE),
+	AD4130_CH("Chn5", 5, IIO_VOLTAGE),
+	AD4130_CH("Chn6", 6, IIO_VOLTAGE),
+	AD4130_CH("Chn7", 7, IIO_VOLTAGE),
+#if (ADC_USER_CHANNELS > 8)
+	AD4130_CH("Chn8", 8, IIO_VOLTAGE),
+	AD4130_CH("Chn9", 9, IIO_VOLTAGE),
+	AD4130_CH("Chn10", 10, IIO_VOLTAGE),
+	AD4130_CH("Chn11", 11, IIO_VOLTAGE),
+	AD4130_CH("Chn12", 12, IIO_VOLTAGE),
+	AD4130_CH("Chn13", 13, IIO_VOLTAGE),
+	AD4130_CH("Chn14", 14, IIO_VOLTAGE),
+	AD4130_CH("Chn15", 15, IIO_VOLTAGE)
+#endif
+#endif
+};
+
+/* ADC raw averaged values from loadcell calibration */
+static uint32_t adc_raw_offset;
+static uint32_t adc_raw_gain;
+
+/******************************************************************************/
+/************************ Functions Prototypes ********************************/
+/******************************************************************************/
+
+static void update_vltg_conv_scale_factor(uint8_t chn);
+static void perform_sensor_measurement_and_update_scale(uint32_t adc_raw,
+		uint16_t chn);
+static int get_calibration_status(char *buf,
+				  uint32_t len,
+				  uint8_t chn,
+				  intptr_t id);
+static int set_calibration_routine(char *buf,
+				   uint32_t len,
+				   uint8_t chn,
+				   intptr_t id);
+static int get_loadcell_calibration_status(char *buf,
+		uint32_t len,
+		uint8_t chn,
+		intptr_t id);
+static int set_loadcell_calibration_status(char *buf,
+		uint32_t len,
+		uint8_t chn,
+		intptr_t id);
+
+/******************************************************************************/
+/************************ Functions Definitions *******************************/
+/******************************************************************************/
+
+static char *get_demo_mode_config(void)
+{
+#if (ACTIVE_DEMO_MODE_CONFIG == RTD_2WIRE_CONFIG)
+	return "2-Wire RTD";
+#elif (ACTIVE_DEMO_MODE_CONFIG == RTD_3WIRE_CONFIG)
+	return "3-Wire RTD";
+#elif (ACTIVE_DEMO_MODE_CONFIG == RTD_4WIRE_CONFIG)
+	return "4-Wire RTD";
+#elif (ACTIVE_DEMO_MODE_CONFIG == THERMISTOR_CONFIG)
+	return "Thermistor";
+#elif (ACTIVE_DEMO_MODE_CONFIG == THERMOCOUPLE_CONFIG)
+	return "Thermocouple";
+#elif (ACTIVE_DEMO_MODE_CONFIG == LOADCELL_CONFIG)
+	return "Loadcell";
+#elif (ACTIVE_DEMO_MODE_CONFIG == ECG_CONFIG)
+	return "ECG";
+#elif (ACTIVE_DEMO_MODE_CONFIG == NOISE_TEST_CONFIG)
+	return "Noise Test";
+#elif (ACTIVE_DEMO_MODE_CONFIG == POWER_TEST_CONFIG)
+	return "Power Test";
+#else
+	return "User Default";
+#endif
+}
+
+/*!
+ * @brief	Getter functions for AD4130 attributes
+ * @param	device[in]- Pointer to IIO device instance
+ * @param	buf[in]- IIO input data buffer
+ * @param	len[in]- Number of input bytes
+ * @param	channel[in] - Input channel
+ * @param	priv[in] - Attribute private ID
+ * @return	0 in case of success, negative error code otherwise
+ * @Note	This sampling_frequency attribute is used to define the
+ *			timeout period in IIO client during data capture.
+ *			Timeout (1 chn) = (requested samples * sampling frequency) + 1sec
+ *			Timeout (n chns) = ((requested samples * n) / sampling frequency) + 1sec
+ *			e.g. If sampling frequency = 31.5KSPS, requested samples = 4000, n=1min or 8max
+ *			Timeout (1 chn) = (4000 / 315000) + 1 = ~1.13sec
+ *			Timeout (8 chns) = ((4000 * 8) / 315000) + 1 = ~2.01sec
+ */
+static int iio_ad4130_attr_get(void *device,
+			       char *buf,
+			       uint32_t len,
+			       const struct iio_ch_info *channel,
+			       intptr_t priv)
+{
+	uint32_t adc_data_raw;
+	int32_t offset = 0;
+	uint8_t bipolar;
+	uint32_t val;
+	int32_t	 ret;
+	uint8_t preset = ad4130_dev_inst->ch[channel->ch_num].preset;
+
+	val = no_os_str_to_uint32(buf);
+
+	switch (priv) {
+	case RAW_ATTR_ID:
+		/* Apply calibrated coefficients before new sampling */
+		if (adc_calibration_status[channel->ch_num] == CALIB_DONE) {
+			ret = ad413x_reg_write(ad4130_dev_inst,
+					       AD413X_REG_OFFSET(preset),
+					       adc_calibration_config[channel->ch_num].offset_after_calib);
+			if (ret) {
+				break;
+			}
+
+			ret = ad413x_reg_write(ad4130_dev_inst,
+					       AD413X_REG_GAIN(preset),
+					       adc_calibration_config[channel->ch_num].gain_after_calib);
+			if (ret) {
+				break;
+			}
+		}
+
+		/* Capture the raw adc data */
+		ret = read_single_sample((uint8_t)channel->ch_num, &adc_data_raw);
+		if (ret) {
+			break;
+		}
+
+		perform_sensor_measurement_and_update_scale(adc_data_raw, channel->ch_num);
+		return sprintf(buf, "%d", adc_data_raw);
+
+	case SCALE_ATTR_ID:
+		return snprintf(buf, len, "%.10f", attr_scale_val[channel->ch_num]);
+
+	case OFFSET_ATTR_ID:
+#if (ACTIVE_DEMO_MODE_CONFIG == USER_DEFAULT_CONFIG || \
+	 ACTIVE_DEMO_MODE_CONFIG == LOADCELL_CONFIG || \
+	 ACTIVE_DEMO_MODE_CONFIG == ECG_CONFIG || \
+	 ACTIVE_DEMO_MODE_CONFIG == NOISE_TEST_CONFIG || \
+	 ACTIVE_DEMO_MODE_CONFIG == POWER_TEST_CONFIG)
+		/* Note: For temperature type channels, the offset
+		 * is ignored, as signed conversion needed for IIO client
+		 * is done through perform_sensor_measurement_and_update_scale() */
+		bipolar = ad4130_dev_inst->bipolar;
+		if (bipolar) {
+			/* For IIO clients, the raw to voltage conversion happens
+			 * using formula: voltage = (adc_raw + offset) * scale
+			 * Offset is determined based on the coding scheme of device.
+			 * 'Offset binary coding' is used in bipolar mode while
+			 * 'Streight binary coding' is used in unipolar mode.
+			 * */
+			offset = -ADC_MAX_COUNT_BIPOLAR;
+		}
+#endif
+		return sprintf(buf, "%d", offset);
+
+	case SAMPLING_FREQ_ATTR_ID:
+		/* Sampling frequency for IIO oscilloscope timeout purpose.
+		 * Does not indicate an actual sampling rate of device.
+		 * Refer the 'note' in function description above for timeout calculations */
+		return sprintf(buf, "%d", AD4130_MIN_SAMPLING_FREQ);
+
+	case DEMO_CONFIG_ATTR_ID:
+		return sprintf(buf, "%s", get_demo_mode_config());
+
+	case INTERNAL_CALIB_ID:
+	case SYSTEM_CALIB_ID:
+		return get_calibration_status(buf, len, channel->ch_num, priv);
+
+	case LOADCELL_GAIN_CALIB_ID:
+	case LOADCELL_OFFSET_CALIB_ID:
+		return get_loadcell_calibration_status(buf, len, channel->ch_num, priv);
+
+	default:
+		break;
+	}
+
+	return len;
+}
+
+/*!
+ * @brief	Setter functions for AD4130 attributes
+ * @param	device[in]- Pointer to IIO device instance
+ * @param	buf[in]- IIO input data buffer
+ * @param	len[in]- Number of input bytes
+ * @param	channel[in] - Input channel
+ * @param	priv[in] - Attribute private ID
+ * @return	0 in case of success, negative error code otherwise
+ */
+static int iio_ad4130_attr_set(void *device,
+			       char *buf,
+			       uint32_t len,
+			       const struct iio_ch_info *channel,
+			       intptr_t priv)
+{
+	switch (priv) {
+	case RAW_ATTR_ID:
+	case SCALE_ATTR_ID:
+	case SAMPLING_FREQ_ATTR_ID:
+	case DEMO_CONFIG_ATTR_ID:
+		/* All are read-only attributes */
+		break;
+
+	case INTERNAL_CALIB_ID:
+	case SYSTEM_CALIB_ID:
+		return set_calibration_routine(buf, len, channel->ch_num, priv);
+
+	case LOADCELL_GAIN_CALIB_ID:
+	case LOADCELL_OFFSET_CALIB_ID:
+		return set_loadcell_calibration_status(buf, len, channel->ch_num, priv);
+
+	default:
+		break;
+	}
+
+	return len;
+}
+
+/*!
+ * @brief	Perform the ADC internal/system calibration
+ * @param	chn[in] - ADC channel
+ * @param	calib_mode[in] - ADC calibration mode
+ * @return	0 in case of success, negative error code otherwise
+ */
+static int32_t perform_adc_calibration(uint8_t chn,
+				       enum ad413x_adc_mode calib_mode)
+{
+	int32_t ret;
+	uint32_t data;
+	uint8_t preset = ad4130_dev_inst->ch[chn].preset;
+	uint8_t pga = ad4130_dev_inst->preset[preset].gain;
+
+	/* Put ADC into standby mode */
+	ret = ad413x_set_adc_mode(ad4130_dev_inst, AD413X_STANDBY_MODE);
+	if (ret) {
+		return ret;
+	}
+
+	/* Read the gain/offset coefficient values before calibration */
+	if ((calib_mode == AD413X_INT_GAIN_CAL)
+	    || (calib_mode == AD413X_SYS_GAIN_CAL)) {
+		if (calib_mode == AD413X_INT_GAIN_CAL) {
+			/* Write offset default value before internal gain calibration
+			 * as internal offset calibration is performed after internal
+			 * gain calibration */
+			ret = ad413x_reg_write(ad4130_dev_inst,
+					       AD413X_REG_OFFSET(preset),
+					       AD4130_DEFAULT_OFFSET);
+			if (ret) {
+				return ret;
+			}
+		}
+
+		ret = ad413x_reg_read(ad4130_dev_inst,
+				      AD413X_REG_GAIN(preset),
+				      &data);
+		if (ret) {
+			return ret;
+		}
+		adc_calibration_config[chn].gain_before_calib = data;
+	} else {
+		ret = ad413x_reg_read(ad4130_dev_inst,
+				      AD413X_REG_OFFSET(preset),
+				      &data);
+
+		if (ret) {
+			return ret;
+		}
+		adc_calibration_config[chn].offset_before_calib = data;
+	}
+
+	/* Enable channel for calibration */
+	ret = ad413x_ch_en(ad4130_dev_inst, chn, 1);
+	if (ret) {
+		return ret;
+	}
+
+	if ((calib_mode == AD413X_INT_GAIN_CAL)
+	    || (calib_mode == AD413X_SYS_GAIN_CAL)) {
+		if ((calib_mode == AD413X_INT_GAIN_CAL) && (pga == AD413X_GAIN_1)) {
+			/* Internal gain calibration is not supported at gain of 1 */
+			adc_calibration_config[chn].gain_after_calib =
+				adc_calibration_config[chn].gain_before_calib;
+			adc_calibration_status[chn] = CALIB_SKIPPED;
+			return 0;
+		}
+
+		/* Perform internal/system gain (full-scale) calibration */
+		ret = ad413x_set_adc_mode(ad4130_dev_inst, calib_mode);
+		if (ret) {
+			return ret;
+		}
+
+		/* Wait for conversion to finish */
+		no_os_mdelay(200);
+
+		/* Read the gain coefficient value (post calibrated) */
+		ret = ad413x_reg_read(ad4130_dev_inst,
+				      AD413X_REG_GAIN(preset),
+				      &data);
+		if (ret) {
+			return ret;
+		}
+		adc_calibration_config[chn].gain_after_calib = data;
+
+		/* Compare the pre and post adc calibration gain coefficients to check calibration status */
+		if (adc_calibration_config[chn].gain_after_calib ==
+		    adc_calibration_config[chn].gain_before_calib) {
+			/* Error in gain calibration */
+			return -EINVAL;
+		}
+	} else {
+		/* Perform internal/system offset (zero-scale) calibration */
+		ret = ad413x_set_adc_mode(ad4130_dev_inst, calib_mode);
+		if (ret) {
+			return ret;
+		}
+
+		/* Wait for conversion to finish */
+		no_os_mdelay(200);
+
+		/* Read the coefficient value (post calibrated) */
+		ret = ad413x_reg_read(ad4130_dev_inst,
+				      AD413X_REG_OFFSET(preset),
+				      &data);
+		if (ret) {
+			return ret;
+		}
+		adc_calibration_config[chn].offset_after_calib = data;
+
+		/* Compare the pre and post adc calibration offset coefficients to check calibration status */
+		if (adc_calibration_config[chn].offset_after_calib ==
+		    adc_calibration_config[chn].offset_before_calib) {
+			/* Error in offset calibration */
+			return -EINVAL;
+		}
+	}
+
+	/* Disable previously enabled channel */
+	ret = ad413x_ch_en(ad4130_dev_inst, chn, 0);
+	if (ret) {
+		return ret;
+	}
+
+	return 0;
+}
+
+/*!
+ * @brief	Getter for the ADC internal/system calibration
+ * @param	buf[in]- pointer to buffer holding attribute value
+ * @param	len[in]- length of buffer string data
+ * @param	chn[in]- ADC channel
+ * @param	id[in]- Attribute ID
+ * @return	Number of characters read/written
+ */
+static int get_calibration_status(char *buf,
+				  uint32_t len,
+				  uint8_t chn,
+				  intptr_t id)
+{
+	uint8_t buf_offset = 0;
+
+	switch (id) {
+	case SYSTEM_CALIB_ID:
+	case INTERNAL_CALIB_ID:
+		if (id == SYSTEM_CALIB_ID && system_calibration_state == CALIB_COMPLETE_STATE) {
+			system_calibration_state = ZERO_SCALE_CALIB_STATE;
+		} else if (id == INTERNAL_CALIB_ID
+			   && internal_calibration_state == CALIB_COMPLETE_STATE) {
+			internal_calibration_state = FULL_SCALE_CALIB_STATE;
+		} else {
+			if (adc_calibration_status[chn] != CALIB_ERROR
+			    && adc_calibration_status[chn] != CALIB_SKIPPED
+			    && adc_calibration_status[chn] != CALIB_IN_PROGRESS) {
+				/* Return NA to indicate that system calibration is not supported
+				 * using IIO oscilloscope. Pyadi-iio script needs to be executed
+				 * to perform a system calibration due to manual intervention
+				 **/
+				return snprintf(buf, len, "%s", "NA");
+			}
+		}
+
+		sprintf(buf + buf_offset, "%08x",
+			adc_calibration_config[chn].gain_before_calib);
+		buf_offset += 8;
+		sprintf(buf + buf_offset, "%08x",
+			adc_calibration_config[chn].gain_after_calib);
+		buf_offset += 8;
+		sprintf(buf + buf_offset, "%08x",
+			adc_calibration_config[chn].offset_before_calib);
+		buf_offset += 8;
+		sprintf(buf + buf_offset, "%08x",
+			adc_calibration_config[chn].offset_after_calib);
+		buf_offset += 8;
+
+		if (adc_calibration_status[chn] == CALIB_ERROR) {
+			sprintf(buf + buf_offset, "%s", "calibration_failed");
+			buf_offset += (strlen("calibration_failed") + 1);
+			adc_calibration_status[chn] = CALIB_NOT_DONE;
+		} else if (adc_calibration_status[chn] == CALIB_SKIPPED) {
+			sprintf(buf + buf_offset, "%s", "calibration_skipped");
+			buf_offset += (strlen("calibration_skipped") + 1);
+			adc_calibration_status[chn] = CALIB_NOT_DONE;
+		} else {
+			sprintf(buf + buf_offset, "%s", "calibration_done");
+			buf_offset += (strlen("calibration_done") + 1);
+		}
+
+		return buf_offset;
+
+	default:
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+/*!
+ * @brief	Setter for the ADC internal/system calibration
+ * @param	buf[in]- pointer to buffer holding attribute value
+ * @param	len[in]- length of buffer string data
+ * @param	chn[in]- ADC channel
+ * @param	id[in]- Attribute ID
+ * @return	Number of characters read/written
+ */
+static int set_calibration_routine(char *buf,
+				   uint32_t len,
+				   uint8_t chn,
+				   intptr_t id)
+{
+	switch (id) {
+	case INTERNAL_CALIB_ID:
+		if (!strncmp(buf, "start_calibration", strlen(buf))) {
+			switch (internal_calibration_state) {
+			case FULL_SCALE_CALIB_STATE:
+				adc_calibration_status[chn] = CALIB_IN_PROGRESS;
+				if (perform_adc_calibration(chn,
+							    AD413X_INT_GAIN_CAL)) {
+					adc_calibration_status[chn] = CALIB_ERROR;
+				}
+				internal_calibration_state = ZERO_SCALE_CALIB_STATE;
+				break;
+
+			case ZERO_SCALE_CALIB_STATE:
+				if (perform_adc_calibration(chn,
+							    AD413X_INT_OFFSET_CAL)) {
+					adc_calibration_status[chn] = CALIB_ERROR;
+					internal_calibration_state = FULL_SCALE_CALIB_STATE;
+					break;
+				}
+				adc_calibration_status[chn] = CALIB_DONE;
+				internal_calibration_state = CALIB_COMPLETE_STATE;
+				break;
+
+			case CALIB_COMPLETE_STATE:
+			default:
+				internal_calibration_state = FULL_SCALE_CALIB_STATE;
+				break;
+			}
+		}
+		break;
+
+	case SYSTEM_CALIB_ID:
+		if (!strncmp(buf, "start_calibration", strlen(buf))) {
+			switch (system_calibration_state) {
+			case ZERO_SCALE_CALIB_STATE:
+				adc_calibration_status[chn] = CALIB_IN_PROGRESS;
+				if (perform_adc_calibration(chn,
+							    AD413X_SYS_OFFSET_CAL)) {
+					adc_calibration_status[chn] = CALIB_ERROR;
+				}
+				system_calibration_state = FULL_SCALE_CALIB_STATE;
+				break;
+
+			case FULL_SCALE_CALIB_STATE:
+				if (perform_adc_calibration(chn,
+							    AD413X_SYS_GAIN_CAL)) {
+					adc_calibration_status[chn] = CALIB_ERROR;
+					system_calibration_state = ZERO_SCALE_CALIB_STATE;
+					break;
+				}
+				adc_calibration_status[chn] = CALIB_DONE;
+				system_calibration_state = CALIB_COMPLETE_STATE;
+				break;
+
+			case CALIB_COMPLETE_STATE:
+			default:
+				system_calibration_state = ZERO_SCALE_CALIB_STATE;
+				break;
+			}
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+/*!
+ * @brief	Getter for the Loadcell offset/gain calibration
+ * @param	buf[in]- pointer to buffer holding attribute value
+ * @param	len[in]- length of buffer string data
+ * @param	chn[in]- ADC channel
+ * @param	id[in]- Attribute ID
+ * @return	Number of characters read/written
+ */
+static int get_loadcell_calibration_status(char *buf,
+		uint32_t len,
+		uint8_t chn,
+		intptr_t id)
+{
+	switch (id) {
+	case LOADCELL_OFFSET_CALIB_ID:
+		return sprintf(buf, "%d", adc_raw_offset);
+
+	case LOADCELL_GAIN_CALIB_ID:
+		return sprintf(buf, "%d", adc_raw_gain);
+
+	default:
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+/*!
+ * @brief	Setter for the Loadcell offset/gain calibration
+ * @param	buf[in]- pointer to buffer holding attribute value
+ * @param	len[in]- length of buffer string data
+ * @param	chn[in]- ADC channel
+ * @param	id[in]- Attribute ID
+ * @return	Number of characters read/written
+ */
+static int set_loadcell_calibration_status(char *buf,
+		uint32_t len,
+		uint8_t chn,
+		intptr_t id)
+{
+	uint32_t adc_raw;
+	uint8_t sample_cnt;
+	uint64_t adc_raw_avg = 0;
+
+	if (!strncmp(buf, "start_calibration", strlen(buf))) {
+		switch (id) {
+		case LOADCELL_OFFSET_CALIB_ID:
+			for (sample_cnt = 0; sample_cnt < LOADCELL_SAMPLES_COUNT; sample_cnt++) {
+				read_single_sample(chn, &adc_raw);
+				adc_raw_avg += adc_raw;
+			}
+
+			adc_raw_avg /= LOADCELL_SAMPLES_COUNT;
+			adc_raw_offset = (uint32_t)adc_raw_avg;
+			break;
+
+		case LOADCELL_GAIN_CALIB_ID:
+			for (sample_cnt = 0; sample_cnt < LOADCELL_SAMPLES_COUNT; sample_cnt++) {
+				read_single_sample(chn, &adc_raw);
+				adc_raw_avg += adc_raw;
+			}
+
+			adc_raw_avg /= LOADCELL_SAMPLES_COUNT;
+			adc_raw_gain = (uint32_t)adc_raw_avg;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return len;
+}
+
+/*!
+ * @brief	Read the debug register value
+ * @param	dev[in]- Pointer to IIO device instance
+ * @param	reg[in]- Register address to read from
+ * @param	readval[in,out]- Pointer to variable to read data into
+ * @return	0 in case of success or negative value otherwise
+ */
+int32_t debug_reg_read(void *dev, uint32_t reg, uint32_t *readval)
+{
+	int32_t ret;
+
+	if (!dev || !readval || (reg > MAX_REGISTER_ADDRESS)) {
+		return -EINVAL;
+	}
+
+	ret = ad413x_reg_read(dev, ad413x_regs[reg], readval);
+	if (ret) {
+		return ret;
+	}
+
+	return 0;
+}
+
+/*!
+ * @brief	Write into the debug register
+ * @param	dev[in]- Pointer to IIO device instance
+ * @param	reg[in]- Register address to write into
+ * @param	writeval[in]- Register value to write
+ * @return	0 in case of success or negative value otherwise
+ */
+int32_t debug_reg_write(void *dev, uint32_t reg, uint32_t writeval)
+{
+	int32_t ret;
+
+	if (!dev || (reg > MAX_REGISTER_ADDRESS)) {
+		return -EINVAL;
+	}
+
+	ret = ad413x_reg_write(dev, ad413x_regs[reg], writeval);
+	if (ret) {
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief	Read buffer data corresponding to AD4130 ADC IIO device
+ * @param	iio_dev_data[in] - IIO device data instance
+ * @return	0 in case of success or negative value otherwise
+ */
+static int32_t iio_ad4130_submit_buffer(struct iio_device_data *iio_dev_data)
+{
+	uint32_t ret;
+
+	/* Read the samples counts equal to buffer size/block */
+	ret = read_buffered_data(&iio_dev_data->buffer->buf->buff,
+				 iio_dev_data->buffer->size);
+
+	/* Increment the write spin count as buffer reads all 'n' samples
+	 * in one go which is also the size of buffer block for a given instance.
+	 * The read spin count is incremented from IIO library during buffer
+	 * write/offloading into transmit buffer */
+	if (iio_dev_data->buffer->buf->write.spin_count >= UINT32_MAX) {
+		iio_dev_data->buffer->buf->write.spin_count = 0;
+	}
+
+	iio_dev_data->buffer->buf->write.spin_count += 1;
+
+	return ret;
+}
+
+/**
+ * @brief	Prepare for data transfer
+ * @param	dev[in] - IIO device instance
+ * @param	ch_mask[in] - Channels select mask
+ * @return	0 in case of success or negative value otherwise
+ */
+static int32_t iio_ad4130_prepare_transfer(void *dev,
+		uint32_t ch_mask)
+{
+	return prepare_data_transfer(ch_mask, BYTES_PER_SAMPLE);
+}
+
+/**
+ * @brief	Terminate current data transfer
+ * @param	dev[in] - IIO device instance
+ * @return	0 in case of success or negative value otherwise
+ */
+static int32_t iio_ad4130_end_transfer(void *dev)
+{
+	return end_data_transfer();
+}
+
+/**
+ * @brief	Perform the sensor measurement as per current demo config and update
+ *			the adc_raw value to sensor conversion scale factor for IIO client
+ * @param	adc_raw[in] - ADC raw value
+ * @param	chn[in] -  ADC channel
+ * @return	none
+ */
+static void perform_sensor_measurement_and_update_scale(uint32_t adc_raw,
+		uint16_t chn)
+{
+	float temperature = 0;
+	int32_t cjc_raw_data;
+	float cjc_temp;
+
+#if (ACTIVE_DEMO_MODE_CONFIG == THERMISTOR_CONFIG)
+	temperature = get_ntc_thermistor_temperature(ad4130_dev_inst, adc_raw, chn);
+	attr_scale_val[chn] = (temperature / adc_raw) * 1000.0;
+#elif ((ACTIVE_DEMO_MODE_CONFIG == RTD_2WIRE_CONFIG) || \
+	(ACTIVE_DEMO_MODE_CONFIG == RTD_3WIRE_CONFIG) || \
+	(ACTIVE_DEMO_MODE_CONFIG == RTD_4WIRE_CONFIG))
+	temperature = get_rtd_temperature(ad4130_dev_inst, adc_raw, chn);
+	attr_scale_val[chn] = (temperature / adc_raw) * 1000.0;
+#elif (ACTIVE_DEMO_MODE_CONFIG == THERMOCOUPLE_CONFIG)
+	if (chn != CJC_CHANNEL) {
+		/* Sample the CJC channel (TC channel is already sampled through
+		 * get_raw() function) */
+		if (read_single_sample(CJC_CHANNEL, (uint32_t *)&cjc_raw_data)) {
+			return;
+		}
+	} else {
+		/* For calculating CJC temperature, TC raw data is not used */
+		chn = SENSOR_CHANNEL0;
+		cjc_raw_data = adc_raw;
+		adc_raw = 0;
+	}
+
+	/* Calculate the TC and CJC temperature and update scale factor */
+	temperature = get_tc_temperature(ad4130_dev_inst, adc_raw,
+					 cjc_raw_data, chn, CJC_CHANNEL, &cjc_temp);
+	attr_scale_val[chn] = (temperature / adc_raw) * 1000.0;
+	attr_scale_val[CJC_CHANNEL] = (cjc_temp / cjc_raw_data) * 1000.0;
+#endif
+}
+
+/*!
+ * @brief	Update scale factor for adc data to voltage conversion
+ *			for IIO client
+ * @param	chn[in] - Input channel
+ * @return	none
+ */
+static void update_vltg_conv_scale_factor(uint8_t chn)
+{
+	enum ad413x_gain pga;
+	uint8_t preset;
+	uint8_t bipolar;
+	float vref;
+
+	preset = ad4130_dev_inst->ch[chn].preset;
+	pga = ad4130_dev_inst->preset[preset].gain;
+	bipolar = ad4130_dev_inst->bipolar;
+
+	vref = ad4130_get_reference_voltage(ad4130_dev_inst, chn);
+
+	/* Get the scale factor for voltage conversion */
+	if (bipolar) {
+		attr_scale_val[chn] = (vref / (ADC_MAX_COUNT_BIPOLAR *
+					       (1 << pga))) * 1000;
+	} else {
+		attr_scale_val[chn] = (vref / (ADC_MAX_COUNT_UNIPOLAR *
+					       (1 << pga))) * 1000;
+	}
+
+#if (ACTIVE_DEMO_MODE_CONFIG == POWER_TEST_CONFIG)
+	switch (chn) {
+	case POWER_TEST_I_AVDD_CHN:
+	case POWER_TEST_I_IOVDD_CHN:
+		attr_scale_val[chn] /= I_RSENSE;
+		break;
+
+	case POWER_TEST_V_AVDD_CHN:
+	case POWER_TEST_V_IOVDD_CHN:
+		attr_scale_val[chn] *= V_SCALE;
+		break;
+
+	default:
+		break;
+	}
+#endif
+}
+
+/**
+ * @brief	Init for reading/writing and parameterization of a
+ * 			ad4130 IIO device
+ * @param 	desc[in,out] - IIO device descriptor
+ * @return	0 in case of success, negative error code otherwise
+ */
+int32_t ad4130_iio_init(struct iio_device **desc)
+{
+	struct iio_device *iio_ad4130_inst;
+	uint8_t chn;
+	uint8_t bipolar;
+
+	iio_ad4130_inst = calloc(1, sizeof(struct iio_device));
+	if (!iio_ad4130_inst) {
+		return -ENOMEM;
+	}
+
+	/* Update IIO device init parameters */
+	for (chn = 0; chn < ADC_USER_CHANNELS; chn++) {
+		update_vltg_conv_scale_factor(chn);
+	}
+
+	/* Get the polarity of device */
+	bipolar = ad4130_dev_inst->bipolar;
+
+	if (bipolar) {
+		/* Using offset-binary coding for bipolar mode */
+		chn_scan.sign = 's';
+		chn_scan.realbits = CHN_STORAGE_BITS;
+	} else {
+		/* Using streight-binary coding for bipolar mode */
+		chn_scan.sign = 'u';
+		chn_scan.realbits = ADC_RESOLUTION;
+	}
+
+	chn_scan.storagebits = CHN_STORAGE_BITS;
+	chn_scan.shift = 0;
+	chn_scan.is_big_endian = false;
+
+	iio_ad4130_inst->num_ch = NO_OS_ARRAY_SIZE(ad4130_iio_channels);
+	iio_ad4130_inst->channels = ad4130_iio_channels;
+	iio_ad4130_inst->attributes = ad4130_iio_global_attributes;
+	iio_ad4130_inst->context_attributes = ad4130_iio_context_attributes;
+
+	iio_ad4130_inst->submit = iio_ad4130_submit_buffer;
+	iio_ad4130_inst->pre_enable = iio_ad4130_prepare_transfer;
+	iio_ad4130_inst->post_disable = iio_ad4130_end_transfer;
+
+	iio_ad4130_inst->debug_reg_read = debug_reg_read;
+	iio_ad4130_inst->debug_reg_write = debug_reg_write;
+
+	*desc = iio_ad4130_inst;
+
+	return 0;
+}
+
+/**
+ * @brief Release resources allocated for IIO device
+ * @param desc[in] - IIO device descriptor
+ * @return 0 in case of success, negative error code otherwise
+ */
+int32_t ad4130_iio_remove(struct iio_desc *desc)
+{
+	int32_t status;
+
+	if (!desc) {
+		return -ENOMEM;
+	}
+
+	status = iio_remove(desc);
+	if (status) {
+		return status;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief	Initialize the IIO interface for AD4130 IIO device
+ * @return	0 in case of success, negative error code otherwise
+ */
+int32_t ad4130_iio_initialize(void)
+{
+	int32_t init_status;
+
+	/* IIO device descriptor */
+	struct iio_device *p_iio_ad4130_dev;
+
+	/* IIO interface init parameters */
+	struct iio_init_param iio_init_params = {
+		.phy_type = USE_UART,
+		.nb_devs = 1,
+	};
+
+	/* IIOD init parameters */
+	struct iio_device_init iio_device_init_params = {
+		.name = (char *)dev_name,
+		.raw_buf = adc_data_buffer,
+		.raw_buf_len = DATA_BUFFER_SIZE
+	};
+
+	/* Init the system peripherals */
+	init_status = init_system();
+	if (init_status) {
+		return init_status;
+	}
+
+	/* Initialize AD4130 device and peripheral interface */
+	init_status = ad413x_init(&ad4130_dev_inst, ad4130_init_params);
+	if (init_status) {
+		return init_status;
+	}
+
+	/* Initialize the AD4130 IIO application interface */
+	init_status = ad4130_iio_init(&p_iio_ad4130_dev);
+	if (init_status) {
+		return init_status;
+	}
+
+	/* Initialize the IIO interface */
+	iio_init_params.uart_desc = uart_desc;
+	iio_device_init_params.dev = ad4130_dev_inst;
+	iio_device_init_params.dev_descriptor = p_iio_ad4130_dev;
+	iio_init_params.devs = &iio_device_init_params;
+	init_status = iio_init(&p_ad4130_iio_desc, &iio_init_params);
+	if (init_status) {
+		return init_status;
+	}
+
+	/* Perform data capture initialization */
+	init_status = ad4130_data_capture_init();
+	if (init_status) {
+		return init_status;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief 	Run the AD4130 IIO event handler
+ * @return	none
+ * @details	This function monitors the new IIO client event
+ */
+void ad4130_iio_event_handler(void)
+{
+	(void)iio_step(p_ad4130_iio_desc);
+}