// SPDX-License-Identifier: GPL-2.0 /* * AD5758 Digital to analog converters driver * * Copyright 2018 Analog Devices Inc. * * TODO: Currently CRC is not supported in this driver
*/ #include <linux/bsearch.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/property.h> #include <linux/spi/spi.h> #include <linux/gpio/consumer.h>
struct ad5758_range { int reg; int min; int max;
};
/** * struct ad5758_state - driver instance specific data * @spi: spi_device * @lock: mutex lock * @gpio_reset: gpio descriptor for the reset line * @out_range: struct which stores the output range * @dc_dc_mode: variable which stores the mode of operation * @dc_dc_ilim: variable which stores the dc-to-dc converter current limit * @slew_time: variable which stores the target slew time * @pwr_down: variable which contains whether a channel is powered down or not * @d32: spi transfer buffers
*/ struct ad5758_state { struct spi_device *spi; struct mutex lock; struct gpio_desc *gpio_reset; struct ad5758_range out_range; unsignedint dc_dc_mode; unsignedint dc_dc_ilim; unsignedint slew_time; bool pwr_down;
__be32 d32[3];
};
/* * Output ranges corresponding to bits [3:0] from DAC_CONFIG register * 0000: 0 V to 5 V voltage range * 0001: 0 V to 10 V voltage range * 0010: ±5 V voltage range * 0011: ±10 V voltage range * 1000: 0 mA to 20 mA current range * 1001: 0 mA to 24 mA current range * 1010: 4 mA to 20 mA current range * 1011: ±20 mA current range * 1100: ±24 mA current range * 1101: -1 mA to +22 mA current range
*/ enum ad5758_output_range {
AD5758_RANGE_0V_5V,
AD5758_RANGE_0V_10V,
AD5758_RANGE_PLUSMINUS_5V,
AD5758_RANGE_PLUSMINUS_10V,
AD5758_RANGE_0mA_20mA = 8,
AD5758_RANGE_0mA_24mA,
AD5758_RANGE_4mA_24mA,
AD5758_RANGE_PLUSMINUS_20mA,
AD5758_RANGE_PLUSMINUS_24mA,
AD5758_RANGE_MINUS_1mA_PLUS_22mA,
};
timeout = 10; do {
ret = ad5758_spi_reg_read(st, reg); if (ret < 0) return ret;
if (!(ret & mask)) return 0;
usleep_range(100, 1000);
} while (--timeout);
dev_err(&st->spi->dev, "Error reading bit 0x%x in 0x%x register\n", mask, reg);
return -EIO;
}
staticint ad5758_calib_mem_refresh(struct ad5758_state *st)
{ int ret;
ret = ad5758_spi_reg_write(st, AD5758_KEY,
AD5758_KEY_CODE_CALIB_MEM_REFRESH); if (ret < 0) {
dev_err(&st->spi->dev, "Failed to initiate a calibration memory refresh\n"); return ret;
}
/* Wait to allow time for the internal calibrations to complete */ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
staticint ad5758_soft_reset(struct ad5758_state *st)
{ int ret;
ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_1); if (ret < 0) return ret;
ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_2);
/* Perform a software reset and wait at least 100us */
usleep_range(100, 1000);
return ret;
}
staticint ad5758_set_dc_dc_conv_mode(struct ad5758_state *st, enum ad5758_dc_dc_mode mode)
{ int ret;
/* * The ENABLE_PPC_BUFFERS bit must be set prior to enabling PPC current * mode.
*/ if (mode == AD5758_DCDC_MODE_PPC_CURRENT) {
ret = ad5758_spi_write_mask(st, AD5758_ADC_CONFIG,
AD5758_ADC_CONFIG_PPC_BUF_MSK,
AD5758_ADC_CONFIG_PPC_BUF_EN(1)); if (ret < 0) return ret;
}
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode)); if (ret < 0) return ret;
/* * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0. * This allows the 3-wire interface communication to complete.
*/
ret = ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK); if (ret < 0) return ret;
st->dc_dc_mode = mode;
return ret;
}
staticint ad5758_set_dc_dc_ilim(struct ad5758_state *st, unsignedint ilim)
{ int ret;
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_ILIMIT_MSK,
AD5758_DCDC_CONFIG2_ILIMIT_MODE(ilim)); if (ret < 0) return ret; /* * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0. * This allows the 3-wire interface communication to complete.
*/ return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
}
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, mask, mode); if (ret < 0) return ret;
/* Wait to allow time for the internal calibrations to complete */ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
staticint ad5758_slew_rate_config(struct ad5758_state *st)
{ unsignedint sr_clk_idx, sr_step_idx; int i, res;
s64 diff_new, diff_old;
u64 sr_step, calc_slew_time;
sr_clk_idx = 0;
sr_step_idx = 0;
diff_old = S64_MAX; /* * The slew time can be determined by using the formula: * Slew Time = (Full Scale Out / (Step Size x Update Clk Freq)) * where Slew time is expressed in microseconds * Given the desired slew time, the following algorithm determines the * best match for the step size and the update clock frequency.
*/ for (i = 0; i < ARRAY_SIZE(ad5758_sr_clk); i++) { /* * Go through each valid update clock freq and determine a raw * value for the step size by using the formula: * Step Size = Full Scale Out / (Update Clk Freq * Slew Time)
*/
sr_step = AD5758_FULL_SCALE_MICRO;
do_div(sr_step, ad5758_sr_clk[i]);
do_div(sr_step, st->slew_time); /* * After a raw value for step size was determined, find the * closest valid match
*/
res = ad5758_find_closest_match(ad5758_sr_step,
ARRAY_SIZE(ad5758_sr_step),
sr_step); /* Calculate the slew time */
calc_slew_time = AD5758_FULL_SCALE_MICRO;
do_div(calc_slew_time, ad5758_sr_step[res]);
do_div(calc_slew_time, ad5758_sr_clk[i]); /* * Determine with how many microseconds the calculated slew time * is different from the desired slew time and store the diff * for the next iteration
*/
diff_new = abs(st->slew_time - calc_slew_time); if (diff_new < diff_old) {
diff_old = diff_new;
sr_clk_idx = i;
sr_step_idx = res;
}
}
staticint ad5758_set_out_range(struct ad5758_state *st, int range)
{ int ret;
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
AD5758_DAC_CONFIG_RANGE_MSK,
AD5758_DAC_CONFIG_RANGE_MODE(range)); if (ret < 0) return ret;
/* Wait to allow time for the internal calibrations to complete */ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
staticint ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
{ int ret;
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
AD5758_DAC_CONFIG_INT_EN_MSK,
AD5758_DAC_CONFIG_INT_EN_MODE(enable)); if (ret < 0) return ret;
/* Wait to allow time for the internal calibrations to complete */ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.