AD4130 Mbed IIO Firmware
Dependencies: tempsensors sdp_k1_sdram
Diff: app/ad413x.c
- Revision:
- 2:871d585d96ee
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/ad413x.c Fri Jul 15 17:47:44 2022 +0530 @@ -0,0 +1,1106 @@ +/***************************************************************************//** + * @file ad413x.c + * @brief Implementation of AD413X Driver. + * @author Andrei Porumb (andrei.porumb@analog.com) +******************************************************************************** + * Copyright 2022(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 "ad413x.h" +#include "no_os_error.h" +#include "no_os_irq.h" +#include "no_os_print_log.h" +#include "no_os_delay.h" +#include "no_os_crc8.h" +#include "no_os_spi.h" + +NO_OS_DECLARE_CRC8_TABLE(ad413x_crc8); +uint32_t timeout = 0xFFFFFF; + +/******************************************************************************/ +/************************** Functions Implementation **************************/ +/******************************************************************************/ + +/***************************************************************************//** + * IRQ handler for ADC read. +*******************************************************************************/ +static void irq_adc_read(struct ad413x_callback_ctx *ctx) +{ + struct ad413x_dev *dev = ctx->dev; + timeout = 0xFFFFFF; + if(ctx->buffer_size > 0) { + ad413x_reg_read(ctx->dev, AD413X_REG_DATA, ctx->buffer); + ctx->buffer_size--; + ctx->buffer++; + no_os_irq_enable(dev->irq_desc, dev->rdy_pin); + } else { + no_os_irq_disable(dev->irq_desc, dev->rdy_pin); + } +} + +/***************************************************************************//** + * SPI internal register write to device using a mask. + * + * @param dev - The device structure. + * @param reg_addr - The register address. + * @param data - The register data. + * @param mask - The mask. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_reg_write_msk(struct ad413x_dev *dev, + uint32_t reg_addr, + uint32_t data, + uint32_t mask) +{ + int32_t ret; + uint32_t reg_data; + + ret = ad413x_reg_read(dev, reg_addr, ®_data); + if (ret) + return ret; + reg_data &= ~mask; + reg_data |= data; + return ad413x_reg_write(dev, reg_addr, reg_data); +} + +/***************************************************************************//** + * Set the mode of the ADC. + * + * @param dev - The device structure. + * @param mode - The ADC mode + * Accepted values: AD4110_CONTINOUS_CONV_MODE + * AD4110_SINGLE_CONV_MODE + * AD4110_STANDBY_MODE + * AD4110_PW_DOWN_MODE + * AD4110_SYS_OFFSET_CAL + * AD4110_SYS_GAIN_CAL + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_adc_mode(struct ad413x_dev *dev, enum ad413x_adc_mode mode) +{ + int32_t ret; + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + AD413X_ADC_MODE(mode), + AD413X_ADC_MODE(0xF)); + if (ret) + return ret; + + dev->op_mode = mode; + + return 0; +} + +/***************************************************************************//** + * Set the internal reference. + * + * @param dev - The device structure. + * @param int_ref - The internal reference option. + * Accepted values: AD413X_INTREF_DISABLED + * AD413X_INTREF_2_5V, + * AD413X_INTREF_1_25V + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_int_ref(struct ad413x_dev *dev, enum ad413x_int_ref int_ref) +{ + int32_t ret; + switch (int_ref) { + case AD413X_INTREF_DISABLED: + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + 0U, + AD413X_ADC_REF_EN); + break; + + case AD413X_INTREF_2_5V: + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + AD413X_ADC_REF_EN, + AD413X_ADC_REF_EN); + if (ret) + return ret; + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + 0U, + AD413X_ADC_REF_VAL); + break; + + case AD413X_INTREF_1_25V: + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + AD413X_ADC_REF_EN, + AD413X_ADC_REF_EN); + if (ret) + return ret; + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + AD413X_ADC_REF_VAL, + AD413X_ADC_REF_VAL); + break; + + default: + return -EINVAL; + } + + if (ret) + return ret; + + dev->int_ref = int_ref; + + return 0; +} + +/***************************************************************************//** + * Enable/disable DATA_STAT field. + * + * @param dev - The device structure. + * @param enable - 0 DISABLE + * 1 ENABLE + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_data_stat_en(struct ad413x_dev *dev, uint8_t enable) +{ + int32_t ret; + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + enable ? AD413X_ADC_DATA_STATUS : 0x0U, + AD413X_ADC_DATA_STATUS); + if (ret) + return ret; + + dev->data_stat = enable; + + return 0; +} +/***************************************************************************//** + * Set the gain from configuration register. + * + * @param dev - The device structure. + * @param gain - The gain value. + * Accepted values: AD413X_GAIN_1 + * AD413X_GAIN_2 + * AD413X_GAIN_4 + * AD413X_GAIN_8 + * AD413X_GAIN_16 + * AD413X_GAIN_32 + * AD413X_GAIN_64 + * AD413X_GAIN_128 + * @param reg_nb - Number of Configuration Register + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_gain(struct ad413x_dev *dev, enum ad413x_gain gain, + enum ad413x_preset_nb reg_nb) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CONFIG(reg_nb), + AD413X_PGA_N(gain), + AD413X_PGA_N(0xF)); + if (ret) + return ret; + + dev->preset[reg_nb].gain = gain; + + return 0; +} + +/***************************************************************************//** + * Select reference from configuration register. + * + * @param dev - The device structure. + * @param ref - The reference value. + * Accepted values: AD413X_REFIN1 + * AD413X_REFIN2 + * AD413X_REFOUT_AVSS + * AD413X_AVDD_AVSS + * @param reg_nb - Number of Configuration Register + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_ref(struct ad413x_dev *dev, enum ad413x_ref_sel ref, + enum ad413x_preset_nb reg_nb) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CONFIG(reg_nb), + AD413X_REF_SEL_N(ref), + AD413X_REF_SEL_N(0xF)); + if (ret) + return ret; + + dev->preset[reg_nb].ref_sel = ref; + + return 0; +} + +/***************************************************************************//** + * Select the reference buffers. + * + * @param dev - The device structure. + * @param ref_buf - The reference buffer status. + * @param reg_nb - Number of Configuration Register + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_ref_buf(struct ad413x_dev *dev, + struct ad413x_ref_buf ref_buf, + enum ad413x_preset_nb reg_nb) +{ + int32_t ret; + uint32_t reg_val; + + reg_val = no_os_field_prep(AD413X_REF_BUFP_N, ref_buf.ref_buf_p_en); + reg_val |= no_os_field_prep(AD413X_REF_BUFM_N, ref_buf.ref_buf_m_en); + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CONFIG(reg_nb), + reg_val, + AD413X_REF_BUF_MSK); + if (ret) + return ret; + + dev->preset[reg_nb].ref_buf = ref_buf; + + return 0; +} + +/***************************************************************************//** + * Select filter from filter register. + * + * @param dev - The device structure. + * @param filter - The filter type. + * Accepted values: AD413X_SYNC4_STANDALONE + * AD413X_SYNC4_SYNC1 + * AD413X_SYNC3_STANDALONE + * AD413X_SYNC3_REJ60 + * AD413X_SYNC3_SYNC1 + * AD413X_SYNC3_PF1 + * AD413X_SYNC3_PF2 + * AD413X_SYNC3_PF3 + * AD413X_SYNC3_PF4 + * @param reg_nb - Number of Configuration Register + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_filter(struct ad413x_dev *dev, enum ad413x_filter filter, + enum ad413x_preset_nb reg_nb) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_FILTER(reg_nb), + AD413X_FILTER_MODE_N(filter), + AD413X_FILTER_MODE_N(0xF)); + if (ret) + return ret; + + dev->preset[reg_nb].filter = filter; + + return 0; +} + +/***************************************************************************//** + * Select settle time from filter register. + * + * @param dev - The device structure. + * @param s_time - The settle time value. + * Accepted values: AD413X_32_MCLK + * AD413X_64_MCLK + * AD413X_128_MCLK + * AD413X_256_MCLK + * AD413X_512_MCLK + * AD413X_1024_MCLK + * AD413X_2048_MCLK + * AD413X_4096_MCLK + * @param reg_nb - Number of Configuration Register + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_settle_time(struct ad413x_dev *dev, + enum ad413x_settle_time s_time, + enum ad413x_preset_nb reg_nb) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_FILTER(reg_nb), + AD413X_SETTLE_N(s_time), + AD413X_SETTLE_N(0xFF)); + if (ret) + return ret; + + dev->preset[reg_nb].s_time = s_time; + + return 0; +} + +/***************************************************************************//** + * Select excitation current value from config register. + * + * @param dev - The device structure. + * @param iout0_exc - The Iout0 excitation current value. + * @param iout1_exc - The Iout1 excitation current value. + * Accepted values: AD413X_EXC_OFF + * AD413X_EXC_10UA + * AD413X_EXC_20UA + * AD413X_EXC_50UA + * AD413X_EXC_100UA + * AD413X_EXC_150UA + * AD413X_EXC_200UA + * AD413X_EXC_100NA + * @param reg_nb - Number of Configuration Register + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_exc_current(struct ad413x_dev *dev, + enum ad413x_exc_current iout0_exc, + enum ad413x_exc_current iout1_exc, + enum ad413x_preset_nb reg_nb) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CONFIG(reg_nb), + AD413X_I_OUT0_N(iout0_exc) | AD413X_I_OUT1_N(iout1_exc), + AD413X_I_OUT_MSK); + if (ret) + return ret; + + dev->preset[reg_nb].iout0_exc_current = iout0_exc; + dev->preset[reg_nb].iout1_exc_current = iout1_exc; + + return 0; +} + +/***************************************************************************//** + * Select preset for adc channel. + * + * @param dev - The device structure. + * @param ch_nb - The channel number. + * @param preset_nb - The preset number. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_ch_preset(struct ad413x_dev *dev, uint8_t ch_nb, + enum ad413x_preset_nb preset_nb) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CHN(ch_nb), + AD413X_SETUP_M(preset_nb), + AD413X_SETUP_M(0xF)); + if (ret) + return ret; + + dev->ch[ch_nb].preset = preset_nb; + + return 0; +} + +/***************************************************************************//** + * Select the excitation source pins. + * + * @param dev - The device structure. + * @param ch_nb - The channel number. + * @param iout0_exc_inp - IOUT0 excitation input pin. + * @param iout1_exc_inp - IOUT1 excitation input pin. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_ch_exc_input(struct ad413x_dev *dev, uint8_t ch_nb, + enum ad413x_input iout0_exc_inp, + enum ad413x_input iout1_exc_inp) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CHN(ch_nb), + AD413X_I_OUT0_CH_M(iout0_exc_inp) | + AD413X_I_OUT1_CH_M(iout1_exc_inp), + AD413X_I_OUT_CH_MSK); + if (ret) + return ret; + + dev->ch[ch_nb].iout0_exc_input = iout0_exc_inp; + dev->ch[ch_nb].iout1_exc_input = iout1_exc_inp; + + return 0; +} + +/***************************************************************************//** + * Enable/disable channel. + * + * @param dev - The device structure. + * @param ch_nb - The channel number. + * @param enable - Channel status. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_ch_en(struct ad413x_dev *dev, uint8_t ch_nb, uint8_t enable) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CHN(ch_nb), + enable ? AD413X_ENABLE_M : 0x0U, + AD413X_ENABLE_M); + if (ret) + return ret; + + dev->ch[ch_nb].enable = enable; + + return 0; +} + +/***************************************************************************//** + * Enable/disable Power-Down Switch (PDSW). + * + * @param dev - The device structure. + * @param ch_nb - The channel number. + * @param pdsw_en - PDSW status. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_pdsw_en(struct ad413x_dev *dev, uint8_t ch_nb, bool pdsw_en) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CHN(ch_nb), + pdsw_en ? AD413X_PDSW_M : 0x0U, + AD413X_PDSW_M); + if (ret) + return ret; + + dev->ch[ch_nb].pdsw_en = pdsw_en; + + return 0; +} + +/***************************************************************************//** + * Enable/disable bipolar data coding. + * + * @param dev - The device structure. + * @param enable - 1 - Bipolar + * 0 - Unipolar + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_adc_bipolar(struct ad413x_dev *dev, uint8_t enable) +{ + int32_t ret; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + enable ? AD413X_ADC_BIPOLAR : 0x0U, + AD413X_ADC_BIPOLAR); + if (ret) + return ret; + + dev->bipolar = enable; + + return 0; +} + +/***************************************************************************//** + * Set output VBIAS on analog inputs. + * + * @param dev - The device structure. + * @param v_bias_val - V_BIAS control register value + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_v_bias(struct ad413x_dev *dev, uint16_t v_bias_val) +{ + int32_t ret; + + ret = ad413x_reg_write(dev, + AD413X_REG_VBIAS_CTRL, + v_bias_val); + if (ret) + return ret; + + dev->v_bias = v_bias_val; + + return 0; +} + +/***************************************************************************//** + * Set standby control flags. + * + * @param dev - The device structure. + * @param standby_ctrl - Standby control value + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_standby_ctrl(struct ad413x_dev *dev, + struct ad413x_standby_ctrl standby_ctrl) +{ + int32_t ret; + uint32_t reg_val; + + reg_val = no_os_field_prep(AD413X_STBY_INTREF_EN, + standby_ctrl.standby_int_ref_en); + reg_val |= no_os_field_prep(AD413X_STBY_REFHOL_EN, + standby_ctrl.standby_ref_holder_en); + reg_val |= no_os_field_prep(AD413X_STBY_IEXC_EN, standby_ctrl.standby_iexc_en); + reg_val |= no_os_field_prep(AD413X_STBY_VBIAS_EN, + standby_ctrl.standby_vbias_en); + reg_val |= no_os_field_prep(AD413X_STBY_BURNOUT_EN, + standby_ctrl.standby_burnout_en); + reg_val |= no_os_field_prep(AD413X_STBY_PDSW_EN, standby_ctrl.standby_pdsw_en); + reg_val |= no_os_field_prep(AD413X_STBY_GPO_EN, standby_ctrl.standby_gpio_en); + reg_val |= no_os_field_prep(AD413X_STBY_DIAGNOSTICS_EN, + standby_ctrl.standby_diagn_en); + reg_val |= no_os_field_prep(AD413X_STBY_OUT_EN, standby_ctrl.standby_output_en); + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_MISC, + reg_val, + AD413X_STBY_CTRL_MSK); + if (ret) + return ret; + + dev->standby_ctrl = standby_ctrl; + + return 0; +} + +/***************************************************************************//** + * Set ADC master clock mode. + * + * @param dev - The device structure. + * @param clk - The clock mode. + * Accepted values: AD413X_INT_76_8_KHZ_OUT_OFF + * AD413X_INT_76_8_KHZ_OUT_ON + * AD413X_EXT_76_8KHZ + * AD413X_EXT_153_6_KHZ_DIV_2 + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_set_mclk(struct ad413x_dev *dev, enum ad413x_mclk_sel clk) +{ + int32_t ret; + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + AD413X_ADC_CNTRL_MCLK(clk), + AD413X_ADC_CNTRL_MCLK(0xF)); + if(ret) + return ret; + + dev->mclk = clk; + + return 0; +} + +/***************************************************************************//** + * Do a SPI software reset. + * + * @param dev - The device structure. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_do_soft_reset(struct ad413x_dev *dev) +{ + int32_t ret; + uint8_t buf [ 8 ] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + /* The AD413X can be reset by writing a series of 64 consecutive 1s + * to the DIN input */ + ret = no_os_spi_write_and_read(dev->spi_dev, buf, 8); + if(ret) + return ret; + + no_os_mdelay(5); // TBD + + return 0; +} + +/***************************************************************************//** + * SPI internal register write to device. + * + * @param dev - The device structure. + * @param reg_addr - The register address. + * @param reg_data - The register data. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_reg_write(struct ad413x_dev *dev, + uint32_t reg_addr, + uint32_t reg_data) +{ + uint8_t buf[5]; + + uint8_t data_size = AD413X_TRANSF_LEN(reg_addr); + + buf[0] = AD413X_CMD_WR_COM_REG(reg_addr); + + switch(data_size) { + case 1: + buf[1] = (reg_data & 0xFF); + break; + + case 2: + buf[1] = (reg_data & 0xFF00) >> 8; + buf[2] = (reg_data & 0xFF); + break; + + case 3: + buf[1] = (reg_data & 0xFF0000) >> 16; + buf[2] = (reg_data & 0xFF00) >> 8; + buf[3] = (reg_data & 0xFF); + break; + + default: + return -EINVAL; + } + + if (dev->spi_crc_en) + buf[data_size] = no_os_crc8(ad413x_crc8, buf, ++data_size, 0); + + return no_os_spi_write_and_read(dev->spi_dev, buf, data_size + 1); +} + +/***************************************************************************//** + * SPI internal register read from device. + * + * @param dev - The device structure. + * @param reg_addr - The register address. + * @param reg_data - The register data. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_reg_read(struct ad413x_dev *dev, + uint32_t reg_addr, + uint32_t *reg_data) +{ + uint8_t ret; + uint8_t buf[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; + uint32_t data; + uint8_t crc; + + uint8_t data_size = AD413X_TRANSF_LEN(reg_addr); + + buf[0] = AD413X_CMD_RD_COM_REG(reg_addr); + + if ((reg_addr == AD413X_REG_DATA) && (dev->data_stat)) + data_size++; + + if (dev->spi_crc_en) + data_size++; + + ret = no_os_spi_write_and_read(dev->spi_dev, buf, data_size + 1); + if (ret) + return ret; + + if (dev->spi_crc_en) { + buf[0] = AD413X_CMD_RD_COM_REG(reg_addr); + crc = no_os_crc8(ad413x_crc8, buf, data_size, 0); + if (buf[data_size] != crc) + return -EBADMSG; + data_size--; + } + + switch (data_size) { + case 1: + data = buf[1]; + break; + + case 2: + data = (buf[1] << 8) | buf[2]; + break; + + case 3: + data = (buf[1] << 16) | (buf[2] << 8) | buf[3]; + break; + + case 4: + data = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]; + break; + + default: + return -EINVAL; + } + + *reg_data = data; + + return 0; +} + +/***************************************************************************//** + * Single conversion of each adc active channel. + * + * @param dev - The device structure. + * @param buffer - Buffer to store read data. Buffer size needs to be at + * least equal to the number of active channels. Results will + * be stored in consecutive order of the active channels. + * @param ch_nb - Number of active channels. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_single_conv(struct ad413x_dev *dev, uint32_t *buffer, + uint8_t ch_nb) +{ + int32_t ret; + + struct ad413x_callback_ctx ctx = { + .dev = dev, + .buffer = buffer, + .buffer_size = ch_nb + }; + + struct no_os_callback_desc irq_callback = { + .callback = &irq_adc_read, + .ctx = &ctx + }; + + ret = no_os_irq_trigger_level_set(dev->irq_desc, dev->rdy_pin, + NO_OS_IRQ_EDGE_FALLING); + if (ret) + return ret; + + ret = no_os_irq_register_callback(dev->irq_desc, dev->rdy_pin, &irq_callback); + if (ret) + return ret; + + ret = no_os_irq_enable(dev->irq_desc, dev->rdy_pin); + if (ret) + return ret; + + ret = ad413x_set_adc_mode(dev, AD413X_SINGLE_CONV_MODE); + if (ret) + return ret; + + while((ctx.buffer_size != 0U) && --timeout) ; + + if (!timeout) + pr_err("Timeout error (%s)\n", __func__); + + ret = no_os_irq_disable(dev->irq_desc, dev->rdy_pin); + if (ret) + return ret; + + return no_os_irq_unregister_callback(dev->irq_desc, dev->rdy_pin, + &irq_callback); +} + +/***************************************************************************//** + * Continuous conversion of each adc active channel. + * + * @param dev - The device structure. + * @param buffer - Buffer to store read data. Buffer size needs to be at + * least equal to the number of active channels * samples number. + * Results will be stored in consecutive order of the active + * channels. + * @param ch_nb - Number of active channels. + * @param sample_nb - Samples number. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_continuous_conv(struct ad413x_dev *dev, uint32_t *buffer, + uint8_t ch_nb, uint32_t sample_nb) +{ + int32_t ret; + + struct ad413x_callback_ctx ctx = { + .dev = dev, + .buffer = buffer, + .buffer_size = ch_nb * sample_nb + }; + + struct no_os_callback_desc irq_callback = { + .callback = &irq_adc_read, + .ctx = &ctx + }; + + ret = no_os_irq_trigger_level_set(dev->irq_desc, dev->rdy_pin, + NO_OS_IRQ_EDGE_FALLING); + if (ret) + return ret; + + ret = no_os_irq_register_callback(dev->irq_desc, dev->rdy_pin, &irq_callback); + if (ret) + return ret; + + ret = no_os_irq_enable(dev->irq_desc, dev->rdy_pin); + if (ret) + return ret; + + ret = ad413x_set_adc_mode(dev, AD413X_CONTINOUS_CONV_MODE); + if (ret) + return ret; + + while((ctx.buffer_size != 0U) && --timeout) ; + + if (!timeout) + pr_err("Timeout error (%s)\n", __func__); + + ret = ad413x_set_adc_mode(dev, AD413X_STANDBY_MODE); + if (ret) + return ret; + + return no_os_irq_unregister_callback(dev->irq_desc, dev->rdy_pin, + &irq_callback); +} + +/***************************************************************************//** + * Store adc channel presets. + * + * @param dev - The device structure. + * @param preset - The structure to be saved as preset. + * @param preset_nb - Preset's number. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_preset_store(struct ad413x_dev *dev, struct ad413x_preset preset, + enum ad413x_preset_nb preset_nb) +{ + int32_t ret; + + ret = ad413x_set_gain(dev, preset.gain, preset_nb); + if (ret) + return ret; + + ret = ad413x_set_ref(dev, preset.ref_sel, preset_nb); + if (ret) + return ret; + + ret = ad413x_set_ref_buf(dev, preset.ref_buf, preset_nb); + if (ret) + return ret; + + ret = ad413x_set_settle_time(dev, preset.s_time, preset_nb); + if (ret) + return ret; + + ret = ad413x_set_exc_current(dev, preset.iout0_exc_current, + preset.iout1_exc_current, preset_nb); + if (ret) + return ret; + + return ad413x_set_filter(dev, preset.filter, preset_nb); +} + +/***************************************************************************//** + * Initialize the device. + * + * @param device - The device structure. + * @param init_param - The structure that contains the device initial + * parameters. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_init(struct ad413x_dev **device, + struct ad413x_init_param init_param) +{ + struct ad413x_dev *dev; + uint32_t reg_data; + int32_t ret; + int32_t i; + + no_os_crc8_populate_msb(ad413x_crc8, AD413X_CRC8_POLY); + + dev = (struct ad413x_dev *)malloc(sizeof(*dev)); + if (!dev) + return -1; + + dev->chip_id = init_param.chip_id; + dev->irq_desc = init_param.irq_desc; + dev->rdy_pin = init_param.rdy_pin; + dev->spi_crc_en = 0; + + /* SPI */ + ret = no_os_spi_init(&dev->spi_dev, init_param.spi_init); + if (ret) + goto err_dev; + + /* Device Settings */ + ret = ad413x_do_soft_reset(dev); + if (ret) + goto err_spi; + + /* Reset POR flag */ + ret = ad413x_reg_read(dev, AD413X_REG_STATUS, ®_data); + if (ret) + return -1; + + /* Change SPI to 4 wire*/ + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ADC_CTRL, + AD413X_ADC_CSB_EN, + AD413X_ADC_CSB_EN); + if (ret) + goto err_spi; + + /* TBD - chip ID is 0x00 for now */ + ret = ad413x_reg_read(dev, AD413X_REG_ID, ®_data); + if (ret) + goto err_spi; + + switch(dev->chip_id) { + case AD4130_8: + if(reg_data != AD4130_8) { + goto err_spi; + } + break; + default: + goto err_spi; + } + + ret = ad413x_set_adc_mode(dev, AD413X_STANDBY_MODE); + if (ret) + goto err_spi; + + if (init_param.spi_crc_en) { + ret = ad413x_reg_write_msk(dev, + AD413X_REG_ERROR_EN, + AD413X_SPI_CRC_ERR_EN, + AD413X_SPI_CRC_ERR_EN); + if (ret) + goto err_spi; + dev->spi_crc_en = init_param.spi_crc_en; + } + + /* Preset configured and saved in dev */ + for (i = 0; i < 8; i++) { + ret = ad413x_preset_store(dev, init_param.preset[i], i); + if (ret) + goto err_spi; + } + + /* Channel setup */ + for (i = 0; i < 16; i++) { + ret = ad413x_set_ch_preset(dev, i, init_param.ch[i].preset); + if (ret) + goto err_spi; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CHN(i), + AD413X_AINP_M(init_param.ch[i].ain_p), + AD413X_AINP_M(0xFF)); + if (ret) + goto err_spi; + dev->ch[i].ain_p = init_param.ch[i].ain_p; + + ret = ad413x_reg_write_msk(dev, + AD413X_REG_CHN(i), + AD413X_AINM_M(init_param.ch[i].ain_m), + AD413X_AINM_M(0xFF)); + if (ret) + goto err_spi; + dev->ch[i].ain_m = init_param.ch[i].ain_m; + + ret = ad413x_ch_exc_input(dev, i, init_param.ch[i].iout0_exc_input, + init_param.ch[i].iout1_exc_input); + if (ret) + goto err_spi; + + ret = ad413x_pdsw_en(dev, i, init_param.ch[i].pdsw_en); + if (ret) + goto err_spi; + + ret = ad413x_ch_en(dev, i, init_param.ch[i].enable); + if (ret) + goto err_spi; + } + + ret = ad413x_set_int_ref(dev, init_param.int_ref); + if (ret) + goto err_spi; + + ret = ad413x_adc_bipolar(dev, init_param.bipolar); + if (ret) + goto err_spi; + + ret = ad413x_set_v_bias(dev, init_param.v_bias); + if (ret) + goto err_spi; + + ret = ad413x_set_standby_ctrl(dev, init_param.standby_ctrl); + if (ret) + goto err_spi; + + ret = ad413x_set_mclk(dev, init_param.mclk); + if (ret) + goto err_spi; + + ret = ad413x_data_stat_en(dev, init_param.data_stat); + if (ret) + goto err_spi; + + *device = dev; + + pr_info("AD413X successfully initialized\n"); + return 0; + +err_spi: + no_os_spi_remove(dev->spi_dev); +err_dev: + free(dev); + pr_err("AD413X initialization error (%d)\n", ret); + return ret; +} + +/***************************************************************************//** + * @brief Free the resources allocated by ad413x_init(). + * + * @param dev - The device structure. + * + * @return 0 in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad413x_remove(struct ad413x_dev *dev) +{ + int32_t ret; + + ret = no_os_spi_remove(dev->spi_dev); + if (ret) + return ret; + + free(dev); + + return 0; +}