| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * AD7606 SPI ADC driver |
| * |
| * Copyright 2011 Analog Devices Inc. |
| */ |
| |
| #include <linux/cleanup.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/err.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/interrupt.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/mutex.h> |
| #include <linux/property.h> |
| #include <linux/pwm.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/sched.h> |
| #include <linux/slab.h> |
| #include <linux/sysfs.h> |
| #include <linux/units.h> |
| #include <linux/util_macros.h> |
| |
| #include <linux/iio/backend.h> |
| #include <linux/iio/buffer.h> |
| #include <linux/iio/iio.h> |
| #include <linux/iio/sysfs.h> |
| #include <linux/iio/trigger.h> |
| #include <linux/iio/triggered_buffer.h> |
| #include <linux/iio/trigger_consumer.h> |
| |
| #include "ad7606.h" |
| |
| #define AD7606_CALIB_GAIN_MIN 0 |
| #define AD7606_CALIB_GAIN_STEP 1024 |
| #define AD7606_CALIB_GAIN_MAX (63 * AD7606_CALIB_GAIN_STEP) |
| |
| /* |
| * Scales are computed as 5000/32768 and 10000/32768 respectively, |
| * so that when applied to the raw values they provide mV values. |
| * The scale arrays are kept as IIO_VAL_INT_PLUS_MICRO, so index |
| * X is the integer part and X + 1 is the fractional part. |
| */ |
| static const unsigned int ad7606_16bit_hw_scale_avail[2][2] = { |
| { 0, 152588 }, { 0, 305176 } |
| }; |
| |
| static const unsigned int ad7606_18bit_hw_scale_avail[2][2] = { |
| { 0, 38147 }, { 0, 76294 } |
| }; |
| |
| static const unsigned int ad7606c_16bit_single_ended_unipolar_scale_avail[3][2] = { |
| { 0, 76294 }, { 0, 152588 }, { 0, 190735 } |
| }; |
| |
| static const unsigned int ad7606c_16bit_single_ended_bipolar_scale_avail[5][2] = { |
| { 0, 76294 }, { 0, 152588 }, { 0, 190735 }, { 0, 305176 }, { 0, 381470 } |
| }; |
| |
| static const unsigned int ad7606c_16bit_differential_bipolar_scale_avail[4][2] = { |
| { 0, 152588 }, { 0, 305176 }, { 0, 381470 }, { 0, 610352 } |
| }; |
| |
| static const unsigned int ad7606c_18bit_single_ended_unipolar_scale_avail[3][2] = { |
| { 0, 19073 }, { 0, 38147 }, { 0, 47684 } |
| }; |
| |
| static const unsigned int ad7606c_18bit_single_ended_bipolar_scale_avail[5][2] = { |
| { 0, 19073 }, { 0, 38147 }, { 0, 47684 }, { 0, 76294 }, { 0, 95367 } |
| }; |
| |
| static const unsigned int ad7606c_18bit_differential_bipolar_scale_avail[4][2] = { |
| { 0, 38147 }, { 0, 76294 }, { 0, 95367 }, { 0, 152588 } |
| }; |
| |
| static const unsigned int ad7606_16bit_sw_scale_avail[3][2] = { |
| { 0, 76293 }, { 0, 152588 }, { 0, 305176 } |
| }; |
| |
| static const unsigned int ad7607_hw_scale_avail[2][2] = { |
| { 0, 610352 }, { 1, 220703 } |
| }; |
| |
| static const unsigned int ad7609_hw_scale_avail[2][2] = { |
| { 0, 152588 }, { 0, 305176 } |
| }; |
| |
| static const unsigned int ad7606_oversampling_avail[7] = { |
| 1, 2, 4, 8, 16, 32, 64, |
| }; |
| |
| static const unsigned int ad7606b_oversampling_avail[9] = { |
| 1, 2, 4, 8, 16, 32, 64, 128, 256, |
| }; |
| |
| static const unsigned int ad7616_oversampling_avail[8] = { |
| 1, 2, 4, 8, 16, 32, 64, 128, |
| }; |
| |
| static const int ad7606_calib_offset_avail[3] = { |
| -128, 1, 127, |
| }; |
| |
| static const int ad7606c_18bit_calib_offset_avail[3] = { |
| -512, 4, 508, |
| }; |
| |
| static const int ad7606b_calib_phase_avail[][2] = { |
| { 0, 0 }, { 0, 1250 }, { 0, 318750 }, |
| }; |
| |
| static const int ad7606c_calib_phase_avail[][2] = { |
| { 0, 0 }, { 0, 1000 }, { 0, 255000 }, |
| }; |
| |
| static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan); |
| static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan); |
| static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan); |
| static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan); |
| static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan); |
| static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan); |
| static int ad7616_sw_mode_setup(struct iio_dev *indio_dev); |
| static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev); |
| |
| const struct ad7606_chip_info ad7605_4_info = { |
| .max_samplerate = 300 * KILO, |
| .name = "ad7605-4", |
| .bits = 16, |
| .num_adc_channels = 4, |
| .scale_setup_cb = ad7606_16bit_chan_scale_setup, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7605_4_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7606_8_info = { |
| .max_samplerate = 200 * KILO, |
| .name = "ad7606-8", |
| .bits = 16, |
| .num_adc_channels = 8, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7606_16bit_chan_scale_setup, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7606_8_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7606_6_info = { |
| .max_samplerate = 200 * KILO, |
| .name = "ad7606-6", |
| .bits = 16, |
| .num_adc_channels = 6, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7606_16bit_chan_scale_setup, |
| .offload_storagebits = 32, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7606_6_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7606_4_info = { |
| .max_samplerate = 200 * KILO, |
| .name = "ad7606-4", |
| .bits = 16, |
| .num_adc_channels = 4, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7606_16bit_chan_scale_setup, |
| .offload_storagebits = 32, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7606_4_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7606b_info = { |
| .max_samplerate = 800 * KILO, |
| .name = "ad7606b", |
| .bits = 16, |
| .num_adc_channels = 8, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7606_16bit_chan_scale_setup, |
| .sw_setup_cb = ad7606b_sw_mode_setup, |
| .offload_storagebits = 32, |
| .calib_gain_avail = true, |
| .calib_offset_avail = ad7606_calib_offset_avail, |
| .calib_phase_avail = ad7606b_calib_phase_avail, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7606c_16_info = { |
| .max_samplerate = 1 * MEGA, |
| .name = "ad7606c16", |
| .bits = 16, |
| .num_adc_channels = 8, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7606c_16bit_chan_scale_setup, |
| .sw_setup_cb = ad7606b_sw_mode_setup, |
| .offload_storagebits = 32, |
| .calib_gain_avail = true, |
| .calib_offset_avail = ad7606_calib_offset_avail, |
| .calib_phase_avail = ad7606c_calib_phase_avail, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7607_info = { |
| .max_samplerate = 200 * KILO, |
| .name = "ad7607", |
| .bits = 14, |
| .num_adc_channels = 8, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7607_chan_scale_setup, |
| .offload_storagebits = 32, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7607_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7608_info = { |
| .max_samplerate = 200 * KILO, |
| .name = "ad7608", |
| .bits = 18, |
| .num_adc_channels = 8, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7608_chan_scale_setup, |
| .offload_storagebits = 32, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7608_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7609_info = { |
| .max_samplerate = 200 * KILO, |
| .name = "ad7609", |
| .bits = 18, |
| .num_adc_channels = 8, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7609_chan_scale_setup, |
| .offload_storagebits = 32, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7609_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7606c_18_info = { |
| .max_samplerate = 1 * MEGA, |
| .name = "ad7606c18", |
| .bits = 18, |
| .num_adc_channels = 8, |
| .oversampling_avail = ad7606_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), |
| .scale_setup_cb = ad7606c_18bit_chan_scale_setup, |
| .sw_setup_cb = ad7606b_sw_mode_setup, |
| .offload_storagebits = 32, |
| .calib_gain_avail = true, |
| .calib_offset_avail = ad7606c_18bit_calib_offset_avail, |
| .calib_phase_avail = ad7606c_calib_phase_avail, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606"); |
| |
| const struct ad7606_chip_info ad7616_info = { |
| .max_samplerate = 1 * MEGA, |
| .init_delay_ms = 15, |
| .name = "ad7616", |
| .bits = 16, |
| .num_adc_channels = 16, |
| .oversampling_avail = ad7616_oversampling_avail, |
| .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail), |
| .os_req_reset = true, |
| .scale_setup_cb = ad7606_16bit_chan_scale_setup, |
| .sw_setup_cb = ad7616_sw_mode_setup, |
| .offload_storagebits = 16, |
| }; |
| EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606"); |
| |
| int ad7606_reset(struct ad7606_state *st) |
| { |
| if (st->gpio_reset) { |
| gpiod_set_value(st->gpio_reset, 1); |
| ndelay(100); /* t_reset >= 100ns */ |
| gpiod_set_value(st->gpio_reset, 0); |
| return 0; |
| } |
| |
| return -ENODEV; |
| } |
| EXPORT_SYMBOL_NS_GPL(ad7606_reset, "IIO_AD7606"); |
| |
| static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; |
| |
| if (!st->sw_mode_en) { |
| /* tied to logic low, analog input range is +/- 5V */ |
| ci->range = 0; |
| ci->scale_avail = ad7606_16bit_hw_scale_avail; |
| ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); |
| return 0; |
| } |
| |
| /* Scale of 0.076293 is only available in sw mode */ |
| /* After reset, in software mode, ±10 V is set by default */ |
| ci->range = 2; |
| ci->scale_avail = ad7606_16bit_sw_scale_avail; |
| ci->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail); |
| |
| return 0; |
| } |
| |
| static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch, |
| bool *bipolar, bool *differential) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci; |
| unsigned int num_channels = st->chip_info->num_adc_channels; |
| struct device *dev = st->dev; |
| int ret; |
| |
| *bipolar = false; |
| *differential = false; |
| |
| device_for_each_child_node_scoped(dev, child) { |
| u32 pins[2]; |
| int reg; |
| |
| ret = fwnode_property_read_u32(child, "reg", ®); |
| if (ret) |
| return ret; |
| |
| /* channel number (here) is from 1 to num_channels */ |
| if (reg < 1 || reg > num_channels) |
| return -EINVAL; |
| |
| /* Loop until we are in the right channel. */ |
| if (reg != (ch + 1)) |
| continue; |
| |
| *bipolar = fwnode_property_read_bool(child, "bipolar"); |
| |
| ret = fwnode_property_read_u32_array(child, "diff-channels", |
| pins, ARRAY_SIZE(pins)); |
| /* Channel is differential, if pins are the same as 'reg' */ |
| if (ret == 0 && (pins[0] != reg || pins[1] != reg)) { |
| dev_err(dev, |
| "Differential pins must be the same as 'reg'"); |
| return -EINVAL; |
| } |
| |
| *differential = (ret == 0); |
| |
| if (*differential && !*bipolar) { |
| dev_err(dev, |
| "'bipolar' must be added for diff channel %d\n", |
| reg); |
| return -EINVAL; |
| } |
| |
| ci = &st->chan_info[reg - 1]; |
| |
| ci->r_gain = 0; |
| ret = fwnode_property_read_u32(child, "adi,rfilter-ohms", |
| &ci->r_gain); |
| if (ret == 0 && ci->r_gain > AD7606_CALIB_GAIN_MAX) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; |
| bool bipolar, differential; |
| int ret; |
| |
| if (!st->sw_mode_en) { |
| ci->range = 0; |
| ci->scale_avail = ad7606_18bit_hw_scale_avail; |
| ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); |
| return 0; |
| } |
| |
| ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar, |
| &differential); |
| if (ret) |
| return ret; |
| |
| if (differential) { |
| ci->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail; |
| ci->num_scales = |
| ARRAY_SIZE(ad7606c_18bit_differential_bipolar_scale_avail); |
| /* Bipolar differential ranges start at 8 (b1000) */ |
| ci->reg_offset = 8; |
| ci->range = 1; |
| chan->differential = 1; |
| chan->channel2 = chan->channel; |
| |
| return 0; |
| } |
| |
| chan->differential = 0; |
| |
| if (bipolar) { |
| ci->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail; |
| ci->num_scales = |
| ARRAY_SIZE(ad7606c_18bit_single_ended_bipolar_scale_avail); |
| /* Bipolar single-ended ranges start at 0 (b0000) */ |
| ci->reg_offset = 0; |
| ci->range = 3; |
| chan->scan_type.sign = 's'; |
| |
| return 0; |
| } |
| |
| ci->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail; |
| ci->num_scales = |
| ARRAY_SIZE(ad7606c_18bit_single_ended_unipolar_scale_avail); |
| /* Unipolar single-ended ranges start at 5 (b0101) */ |
| ci->reg_offset = 5; |
| ci->range = 1; |
| chan->scan_type.sign = 'u'; |
| |
| return 0; |
| } |
| |
| static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; |
| bool bipolar, differential; |
| int ret; |
| |
| if (!st->sw_mode_en) { |
| ci->range = 0; |
| ci->scale_avail = ad7606_16bit_hw_scale_avail; |
| ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); |
| return 0; |
| } |
| |
| ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar, |
| &differential); |
| if (ret) |
| return ret; |
| |
| if (differential) { |
| ci->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail; |
| ci->num_scales = |
| ARRAY_SIZE(ad7606c_16bit_differential_bipolar_scale_avail); |
| /* Bipolar differential ranges start at 8 (b1000) */ |
| ci->reg_offset = 8; |
| ci->range = 1; |
| chan->differential = 1; |
| chan->channel2 = chan->channel; |
| chan->scan_type.sign = 's'; |
| |
| return 0; |
| } |
| |
| chan->differential = 0; |
| |
| if (bipolar) { |
| ci->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail; |
| ci->num_scales = |
| ARRAY_SIZE(ad7606c_16bit_single_ended_bipolar_scale_avail); |
| /* Bipolar single-ended ranges start at 0 (b0000) */ |
| ci->reg_offset = 0; |
| ci->range = 3; |
| chan->scan_type.sign = 's'; |
| |
| return 0; |
| } |
| |
| ci->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail; |
| ci->num_scales = |
| ARRAY_SIZE(ad7606c_16bit_single_ended_unipolar_scale_avail); |
| /* Unipolar single-ended ranges start at 5 (b0101) */ |
| ci->reg_offset = 5; |
| ci->range = 1; |
| chan->scan_type.sign = 'u'; |
| |
| return 0; |
| } |
| |
| static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; |
| |
| ci->range = 0; |
| ci->scale_avail = ad7607_hw_scale_avail; |
| ci->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail); |
| return 0; |
| } |
| |
| static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; |
| |
| ci->range = 0; |
| ci->scale_avail = ad7606_18bit_hw_scale_avail; |
| ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); |
| return 0; |
| } |
| |
| static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, |
| struct iio_chan_spec *chan) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; |
| |
| ci->range = 0; |
| ci->scale_avail = ad7609_hw_scale_avail; |
| ci->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail); |
| return 0; |
| } |
| |
| static int ad7606_reg_access(struct iio_dev *indio_dev, |
| unsigned int reg, |
| unsigned int writeval, |
| unsigned int *readval) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| guard(mutex)(&st->lock); |
| |
| if (readval) { |
| ret = st->bops->reg_read(st, reg); |
| if (ret < 0) |
| return ret; |
| *readval = ret; |
| return 0; |
| } else { |
| return st->bops->reg_write(st, reg, writeval); |
| } |
| } |
| |
| static int ad7606_pwm_set_high(struct ad7606_state *st) |
| { |
| struct pwm_state cnvst_pwm_state; |
| int ret; |
| |
| pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); |
| cnvst_pwm_state.enabled = true; |
| cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period; |
| |
| ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); |
| |
| return ret; |
| } |
| |
| int ad7606_pwm_set_low(struct ad7606_state *st) |
| { |
| struct pwm_state cnvst_pwm_state; |
| int ret; |
| |
| pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); |
| cnvst_pwm_state.enabled = true; |
| cnvst_pwm_state.duty_cycle = 0; |
| |
| ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_low, "IIO_AD7606"); |
| |
| int ad7606_pwm_set_swing(struct ad7606_state *st) |
| { |
| struct pwm_state cnvst_pwm_state; |
| |
| pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); |
| cnvst_pwm_state.enabled = true; |
| cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2; |
| |
| return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); |
| } |
| EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_swing, "IIO_AD7606"); |
| |
| static bool ad7606_pwm_is_swinging(struct ad7606_state *st) |
| { |
| struct pwm_state cnvst_pwm_state; |
| |
| pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); |
| |
| return cnvst_pwm_state.duty_cycle != cnvst_pwm_state.period && |
| cnvst_pwm_state.duty_cycle != 0; |
| } |
| |
| static int ad7606_set_sampling_freq(struct ad7606_state *st, unsigned long freq) |
| { |
| struct pwm_state cnvst_pwm_state; |
| bool is_swinging = ad7606_pwm_is_swinging(st); |
| bool is_high; |
| |
| if (freq == 0) |
| return -EINVAL; |
| |
| /* Retrieve the previous state. */ |
| pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); |
| is_high = cnvst_pwm_state.duty_cycle == cnvst_pwm_state.period; |
| |
| cnvst_pwm_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq); |
| cnvst_pwm_state.polarity = PWM_POLARITY_NORMAL; |
| if (is_high) |
| cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period; |
| else if (is_swinging) |
| cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2; |
| else |
| cnvst_pwm_state.duty_cycle = 0; |
| |
| return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); |
| } |
| |
| static int ad7606_read_samples(struct ad7606_state *st) |
| { |
| unsigned int num = st->chip_info->num_adc_channels; |
| |
| return st->bops->read_block(st->dev, num, &st->data); |
| } |
| |
| static irqreturn_t ad7606_trigger_handler(int irq, void *p) |
| { |
| struct iio_poll_func *pf = p; |
| struct iio_dev *indio_dev = pf->indio_dev; |
| struct ad7606_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| guard(mutex)(&st->lock); |
| |
| ret = ad7606_read_samples(st); |
| if (ret) |
| goto error_ret; |
| |
| iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), |
| iio_get_time_ns(indio_dev)); |
| error_ret: |
| iio_trigger_notify_done(indio_dev->trig); |
| /* The rising edge of the CONVST signal starts a new conversion. */ |
| gpiod_set_value(st->gpio_convst, 1); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch, |
| int *val) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| const struct iio_chan_spec *chan; |
| unsigned int realbits; |
| int ret; |
| |
| if (st->gpio_convst) { |
| gpiod_set_value(st->gpio_convst, 1); |
| } else { |
| ret = ad7606_pwm_set_high(st); |
| if (ret < 0) |
| return ret; |
| } |
| |
| /* |
| * If no backend, wait for the interruption on busy pin, otherwise just add |
| * a delay to leave time for the data to be available. For now, the latter |
| * will not happen because IIO_CHAN_INFO_RAW is not supported for the backend. |
| * TODO: Add support for reading a single value when the backend is used. |
| */ |
| if (st->trig) { |
| ret = wait_for_completion_timeout(&st->completion, |
| msecs_to_jiffies(1000)); |
| if (!ret) { |
| ret = -ETIMEDOUT; |
| goto error_ret; |
| } |
| } else { |
| /* |
| * If the BUSY interrupt is not available, wait enough time for |
| * the longest possible conversion (max for the whole family is |
| * around 350us). |
| */ |
| fsleep(400); |
| } |
| |
| ret = ad7606_read_samples(st); |
| if (ret) |
| goto error_ret; |
| |
| chan = &indio_dev->channels[ch]; |
| realbits = chan->scan_type.realbits; |
| |
| if (realbits > 16) |
| *val = st->data.buf32[ch]; |
| else |
| *val = st->data.buf16[ch]; |
| |
| *val &= GENMASK(realbits - 1, 0); |
| |
| if (chan->scan_type.sign == 's') |
| *val = sign_extend32(*val, realbits - 1); |
| |
| error_ret: |
| if (!st->gpio_convst) { |
| ret = ad7606_pwm_set_low(st); |
| if (ret < 0) |
| return ret; |
| } |
| gpiod_set_value(st->gpio_convst, 0); |
| |
| return ret; |
| } |
| |
| static int ad7606_get_calib_offset(struct ad7606_state *st, int ch, int *val) |
| { |
| int ret; |
| |
| ret = st->bops->reg_read(st, AD7606_CALIB_OFFSET(ch)); |
| if (ret < 0) |
| return ret; |
| |
| *val = st->chip_info->calib_offset_avail[0] + |
| ret * st->chip_info->calib_offset_avail[1]; |
| |
| return 0; |
| } |
| |
| static int ad7606_get_calib_phase(struct ad7606_state *st, int ch, int *val, |
| int *val2) |
| { |
| int ret; |
| |
| ret = st->bops->reg_read(st, AD7606_CALIB_PHASE(ch)); |
| if (ret < 0) |
| return ret; |
| |
| *val = 0; |
| |
| /* |
| * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs. |
| * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs. |
| */ |
| *val2 = ret * st->chip_info->calib_phase_avail[1][1]; |
| |
| return 0; |
| } |
| |
| static int ad7606_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| int *val, |
| int *val2, |
| long m) |
| { |
| int ret, ch = 0; |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci; |
| struct pwm_state cnvst_pwm_state; |
| |
| switch (m) { |
| case IIO_CHAN_INFO_RAW: |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| ret = ad7606_scan_direct(indio_dev, chan->scan_index, val); |
| iio_device_release_direct(indio_dev); |
| if (ret < 0) |
| return ret; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_SCALE: |
| if (st->sw_mode_en) |
| ch = chan->scan_index; |
| ci = &st->chan_info[ch]; |
| *val = ci->scale_avail[ci->range][0]; |
| *val2 = ci->scale_avail[ci->range][1]; |
| return IIO_VAL_INT_PLUS_MICRO; |
| case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| *val = st->oversampling; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); |
| *val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period); |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_CALIBBIAS: |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| ret = ad7606_get_calib_offset(st, chan->scan_index, val); |
| iio_device_release_direct(indio_dev); |
| if (ret) |
| return ret; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_CONVDELAY: |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| ret = ad7606_get_calib_phase(st, chan->scan_index, val, val2); |
| iio_device_release_direct(indio_dev); |
| if (ret) |
| return ret; |
| return IIO_VAL_INT_PLUS_NANO; |
| } |
| return -EINVAL; |
| } |
| |
| static ssize_t in_voltage_scale_available_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci = &st->chan_info[0]; |
| const unsigned int (*vals)[2] = ci->scale_avail; |
| unsigned int i; |
| size_t len = 0; |
| |
| for (i = 0; i < ci->num_scales; i++) |
| len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", |
| vals[i][0], vals[i][1]); |
| buf[len - 1] = '\n'; |
| |
| return len; |
| } |
| |
| static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0); |
| |
| static int ad7606_write_scale_hw(struct iio_dev *indio_dev, int ch, int val) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| gpiod_set_value(st->gpio_range, val); |
| |
| return 0; |
| } |
| |
| static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| DECLARE_BITMAP(values, 3); |
| |
| values[0] = val & GENMASK(2, 0); |
| |
| gpiod_multi_set_value_cansleep(st->gpio_os, values); |
| |
| /* AD7616 requires a reset to update value */ |
| if (st->chip_info->os_req_reset) |
| ad7606_reset(st); |
| |
| return 0; |
| } |
| |
| static int ad7606_set_calib_offset(struct ad7606_state *st, int ch, int val) |
| { |
| int start_val, step_val, stop_val; |
| int offset; |
| |
| start_val = st->chip_info->calib_offset_avail[0]; |
| step_val = st->chip_info->calib_offset_avail[1]; |
| stop_val = st->chip_info->calib_offset_avail[2]; |
| |
| if (val < start_val || val > stop_val) |
| return -EINVAL; |
| |
| offset = (val - start_val) / step_val; |
| |
| return st->bops->reg_write(st, AD7606_CALIB_OFFSET(ch), offset); |
| } |
| |
| static int ad7606_set_calib_phase(struct ad7606_state *st, int ch, int val, |
| int val2) |
| { |
| int wreg, start_ns, step_ns, stop_ns; |
| |
| if (val != 0) |
| return -EINVAL; |
| |
| start_ns = st->chip_info->calib_phase_avail[0][1]; |
| step_ns = st->chip_info->calib_phase_avail[1][1]; |
| stop_ns = st->chip_info->calib_phase_avail[2][1]; |
| |
| /* |
| * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs. |
| * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs. |
| */ |
| if (val2 < start_ns || val2 > stop_ns) |
| return -EINVAL; |
| |
| wreg = val2 / step_ns; |
| |
| return st->bops->reg_write(st, AD7606_CALIB_PHASE(ch), wreg); |
| } |
| |
| static int ad7606_write_raw_get_fmt(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, long info) |
| { |
| switch (info) { |
| case IIO_CHAN_INFO_SCALE: |
| return IIO_VAL_INT_PLUS_MICRO; |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| case IIO_CHAN_INFO_CALIBBIAS: |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_CONVDELAY: |
| return IIO_VAL_INT_PLUS_NANO; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int ad7606_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| int val, |
| int val2, |
| long mask) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| unsigned int scale_avail_uv[AD760X_MAX_SCALES]; |
| struct ad7606_chan_info *ci; |
| int i, ret, ch = 0; |
| |
| guard(mutex)(&st->lock); |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_SCALE: |
| if (st->sw_mode_en) |
| ch = chan->scan_index; |
| ci = &st->chan_info[ch]; |
| for (i = 0; i < ci->num_scales; i++) { |
| scale_avail_uv[i] = ci->scale_avail[i][0] * MICRO + |
| ci->scale_avail[i][1]; |
| } |
| val = (val * MICRO) + val2; |
| i = find_closest(val, scale_avail_uv, ci->num_scales); |
| |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| ret = st->write_scale(indio_dev, ch, i + ci->reg_offset); |
| iio_device_release_direct(indio_dev); |
| if (ret < 0) |
| return ret; |
| ci->range = i; |
| |
| return 0; |
| case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| if (val2) |
| return -EINVAL; |
| i = find_closest(val, st->oversampling_avail, |
| st->num_os_ratios); |
| |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| ret = st->write_os(indio_dev, i); |
| iio_device_release_direct(indio_dev); |
| if (ret < 0) |
| return ret; |
| st->oversampling = st->oversampling_avail[i]; |
| |
| return 0; |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| if (val < 0 && val2 != 0) |
| return -EINVAL; |
| return ad7606_set_sampling_freq(st, val); |
| case IIO_CHAN_INFO_CALIBBIAS: |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| ret = ad7606_set_calib_offset(st, chan->scan_index, val); |
| iio_device_release_direct(indio_dev); |
| return ret; |
| case IIO_CHAN_INFO_CONVDELAY: |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| ret = ad7606_set_calib_phase(st, chan->scan_index, val, val2); |
| iio_device_release_direct(indio_dev); |
| return ret; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static ssize_t ad7606_oversampling_ratio_avail(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| struct ad7606_state *st = iio_priv(indio_dev); |
| const unsigned int *vals = st->oversampling_avail; |
| unsigned int i; |
| size_t len = 0; |
| |
| for (i = 0; i < st->num_os_ratios; i++) |
| len += scnprintf(buf + len, PAGE_SIZE - len, "%u ", vals[i]); |
| buf[len - 1] = '\n'; |
| |
| return len; |
| } |
| |
| static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444, |
| ad7606_oversampling_ratio_avail, NULL, 0); |
| |
| static struct attribute *ad7606_attributes_os_and_range[] = { |
| &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, |
| &iio_dev_attr_oversampling_ratio_available.dev_attr.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group ad7606_attribute_group_os_and_range = { |
| .attrs = ad7606_attributes_os_and_range, |
| }; |
| |
| static struct attribute *ad7606_attributes_os[] = { |
| &iio_dev_attr_oversampling_ratio_available.dev_attr.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group ad7606_attribute_group_os = { |
| .attrs = ad7606_attributes_os, |
| }; |
| |
| static struct attribute *ad7606_attributes_range[] = { |
| &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group ad7606_attribute_group_range = { |
| .attrs = ad7606_attributes_range, |
| }; |
| |
| static int ad7606_request_gpios(struct ad7606_state *st) |
| { |
| struct device *dev = st->dev; |
| |
| st->gpio_convst = devm_gpiod_get_optional(dev, "adi,conversion-start", |
| GPIOD_OUT_LOW); |
| |
| if (IS_ERR(st->gpio_convst)) |
| return PTR_ERR(st->gpio_convst); |
| |
| st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); |
| if (IS_ERR(st->gpio_reset)) |
| return PTR_ERR(st->gpio_reset); |
| |
| st->gpio_range = devm_gpiod_get_optional(dev, "adi,range", |
| GPIOD_OUT_LOW); |
| if (IS_ERR(st->gpio_range)) |
| return PTR_ERR(st->gpio_range); |
| |
| st->gpio_standby = devm_gpiod_get_optional(dev, "standby", |
| GPIOD_OUT_LOW); |
| if (IS_ERR(st->gpio_standby)) |
| return PTR_ERR(st->gpio_standby); |
| |
| st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data", |
| GPIOD_IN); |
| if (IS_ERR(st->gpio_frstdata)) |
| return PTR_ERR(st->gpio_frstdata); |
| |
| if (!st->chip_info->oversampling_num) |
| return 0; |
| |
| st->gpio_os = devm_gpiod_get_array_optional(dev, |
| "adi,oversampling-ratio", |
| GPIOD_OUT_LOW); |
| return PTR_ERR_OR_ZERO(st->gpio_os); |
| } |
| |
| /* |
| * The BUSY signal indicates when conversions are in progress, so when a rising |
| * edge of CONVST is applied, BUSY goes logic high and transitions low at the |
| * end of the entire conversion process. The falling edge of the BUSY signal |
| * triggers this interrupt. |
| */ |
| static irqreturn_t ad7606_interrupt(int irq, void *dev_id) |
| { |
| struct iio_dev *indio_dev = dev_id; |
| struct ad7606_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| if (iio_buffer_enabled(indio_dev)) { |
| if (st->gpio_convst) { |
| gpiod_set_value(st->gpio_convst, 0); |
| } else { |
| ret = ad7606_pwm_set_low(st); |
| if (ret < 0) { |
| dev_err(st->dev, "PWM set low failed"); |
| goto done; |
| } |
| } |
| iio_trigger_poll_nested(st->trig); |
| } else { |
| complete(&st->completion); |
| } |
| |
| done: |
| return IRQ_HANDLED; |
| }; |
| |
| static int ad7606_validate_trigger(struct iio_dev *indio_dev, |
| struct iio_trigger *trig) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| if (st->trig != trig) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| static int ad7606_buffer_postenable(struct iio_dev *indio_dev) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| gpiod_set_value(st->gpio_convst, 1); |
| |
| return 0; |
| } |
| |
| static int ad7606_buffer_predisable(struct iio_dev *indio_dev) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| gpiod_set_value(st->gpio_convst, 0); |
| |
| return 0; |
| } |
| |
| static int ad7606_read_avail(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| const int **vals, int *type, int *length, |
| long info) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct ad7606_chan_info *ci; |
| unsigned int ch = 0; |
| |
| switch (info) { |
| case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| *vals = st->oversampling_avail; |
| *length = st->num_os_ratios; |
| *type = IIO_VAL_INT; |
| |
| return IIO_AVAIL_LIST; |
| |
| case IIO_CHAN_INFO_SCALE: |
| if (st->sw_mode_en) |
| ch = chan->scan_index; |
| |
| ci = &st->chan_info[ch]; |
| *vals = (int *)ci->scale_avail; |
| *length = ci->num_scales * 2; |
| *type = IIO_VAL_INT_PLUS_MICRO; |
| |
| return IIO_AVAIL_LIST; |
| case IIO_CHAN_INFO_CALIBBIAS: |
| *vals = st->chip_info->calib_offset_avail; |
| *type = IIO_VAL_INT; |
| return IIO_AVAIL_RANGE; |
| case IIO_CHAN_INFO_CONVDELAY: |
| *vals = (const int *)st->chip_info->calib_phase_avail; |
| *type = IIO_VAL_INT_PLUS_NANO; |
| return IIO_AVAIL_RANGE; |
| } |
| return -EINVAL; |
| } |
| |
| static int ad7606_backend_buffer_postenable(struct iio_dev *indio_dev) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| return ad7606_pwm_set_swing(st); |
| } |
| |
| static int ad7606_backend_buffer_predisable(struct iio_dev *indio_dev) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| return ad7606_pwm_set_low(st); |
| } |
| |
| static int ad7606_update_scan_mode(struct iio_dev *indio_dev, |
| const unsigned long *scan_mask) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| /* |
| * The update scan mode is only for iio backend compatible drivers. |
| * If the specific update_scan_mode is not defined in the bus ops, |
| * just do nothing and return 0. |
| */ |
| if (!st->bops->update_scan_mode) |
| return 0; |
| |
| return st->bops->update_scan_mode(indio_dev, scan_mask); |
| } |
| |
| static const struct iio_buffer_setup_ops ad7606_buffer_ops = { |
| .postenable = &ad7606_buffer_postenable, |
| .predisable = &ad7606_buffer_predisable, |
| }; |
| |
| static const struct iio_buffer_setup_ops ad7606_backend_buffer_ops = { |
| .postenable = &ad7606_backend_buffer_postenable, |
| .predisable = &ad7606_backend_buffer_predisable, |
| }; |
| |
| static const struct iio_info ad7606_info_no_os_or_range = { |
| .read_raw = &ad7606_read_raw, |
| .validate_trigger = &ad7606_validate_trigger, |
| .update_scan_mode = &ad7606_update_scan_mode, |
| }; |
| |
| static const struct iio_info ad7606_info_os_and_range = { |
| .read_raw = &ad7606_read_raw, |
| .write_raw = &ad7606_write_raw, |
| .attrs = &ad7606_attribute_group_os_and_range, |
| .validate_trigger = &ad7606_validate_trigger, |
| .update_scan_mode = &ad7606_update_scan_mode, |
| }; |
| |
| static const struct iio_info ad7606_info_sw_mode = { |
| .read_raw = &ad7606_read_raw, |
| .write_raw = &ad7606_write_raw, |
| .read_avail = &ad7606_read_avail, |
| .write_raw_get_fmt = ad7606_write_raw_get_fmt, |
| .debugfs_reg_access = &ad7606_reg_access, |
| .validate_trigger = &ad7606_validate_trigger, |
| .update_scan_mode = &ad7606_update_scan_mode, |
| }; |
| |
| static const struct iio_info ad7606_info_os = { |
| .read_raw = &ad7606_read_raw, |
| .write_raw = &ad7606_write_raw, |
| .attrs = &ad7606_attribute_group_os, |
| .validate_trigger = &ad7606_validate_trigger, |
| .update_scan_mode = &ad7606_update_scan_mode, |
| }; |
| |
| static const struct iio_info ad7606_info_range = { |
| .read_raw = &ad7606_read_raw, |
| .write_raw = &ad7606_write_raw, |
| .attrs = &ad7606_attribute_group_range, |
| .validate_trigger = &ad7606_validate_trigger, |
| .update_scan_mode = &ad7606_update_scan_mode, |
| }; |
| |
| static const struct iio_trigger_ops ad7606_trigger_ops = { |
| .validate_device = iio_trigger_validate_own_device, |
| }; |
| |
| static int ad7606_write_mask(struct ad7606_state *st, unsigned int addr, |
| unsigned long mask, unsigned int val) |
| { |
| int readval; |
| |
| readval = st->bops->reg_read(st, addr); |
| if (readval < 0) |
| return readval; |
| |
| readval &= ~mask; |
| readval |= val; |
| |
| return st->bops->reg_write(st, addr, readval); |
| } |
| |
| static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| unsigned int ch_addr, mode, ch_index; |
| |
| /* |
| * Ad7616 has 16 channels divided in group A and group B. |
| * The range of channels from A are stored in registers with address 4 |
| * while channels from B are stored in register with address 6. |
| * The last bit from channels determines if it is from group A or B |
| * because the order of channels in iio is 0A, 0B, 1A, 1B... |
| */ |
| ch_index = ch >> 1; |
| |
| ch_addr = AD7616_RANGE_CH_ADDR(ch_index); |
| |
| if ((ch & 0x1) == 0) /* channel A */ |
| ch_addr += AD7616_RANGE_CH_A_ADDR_OFF; |
| else /* channel B */ |
| ch_addr += AD7616_RANGE_CH_B_ADDR_OFF; |
| |
| /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */ |
| mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11)); |
| |
| return ad7606_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index), |
| mode); |
| } |
| |
| static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER, |
| AD7616_OS_MASK, val << 2); |
| } |
| |
| static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| return ad7606_write_mask(st, AD7606_RANGE_CH_ADDR(ch), |
| AD7606_RANGE_CH_MSK(ch), |
| AD7606_RANGE_CH_MODE(ch, val)); |
| } |
| |
| static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| return st->bops->reg_write(st, AD7606_OS_MODE, val); |
| } |
| |
| static int ad7616_sw_mode_setup(struct iio_dev *indio_dev) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| /* |
| * Scale can be configured individually for each channel |
| * in software mode. |
| */ |
| |
| st->write_scale = ad7616_write_scale_sw; |
| st->write_os = &ad7616_write_os_sw; |
| |
| if (st->bops->sw_mode_config) { |
| ret = st->bops->sw_mode_config(indio_dev); |
| if (ret) |
| return ret; |
| } |
| |
| /* Activate Burst mode and SEQEN MODE */ |
| return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER, |
| AD7616_BURST_MODE | AD7616_SEQEN_MODE, |
| AD7616_BURST_MODE | AD7616_SEQEN_MODE); |
| } |
| |
| static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| DECLARE_BITMAP(os, 3); |
| |
| bitmap_fill(os, 3); |
| /* |
| * Software mode is enabled when all three oversampling |
| * pins are set to high. If oversampling gpios are defined |
| * in the device tree, then they need to be set to high, |
| * otherwise, they must be hardwired to VDD |
| */ |
| if (st->gpio_os) |
| gpiod_multi_set_value_cansleep(st->gpio_os, os); |
| |
| /* OS of 128 and 256 are available only in software mode */ |
| st->oversampling_avail = ad7606b_oversampling_avail; |
| st->num_os_ratios = ARRAY_SIZE(ad7606b_oversampling_avail); |
| |
| st->write_scale = ad7606_write_scale_sw; |
| st->write_os = &ad7606_write_os_sw; |
| |
| if (!st->bops->sw_mode_config) |
| return 0; |
| |
| return st->bops->sw_mode_config(indio_dev); |
| } |
| |
| static int ad7606_set_gain_calib(struct ad7606_state *st) |
| { |
| struct ad7606_chan_info *ci; |
| int i, ret; |
| |
| for (i = 0; i < st->chip_info->num_adc_channels; i++) { |
| ci = &st->chan_info[i]; |
| ret = st->bops->reg_write(st, AD7606_CALIB_GAIN(i), |
| DIV_ROUND_CLOSEST(ci->r_gain, |
| AD7606_CALIB_GAIN_STEP)); |
| if (ret) |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int ad7606_probe_channels(struct iio_dev *indio_dev) |
| { |
| struct ad7606_state *st = iio_priv(indio_dev); |
| struct device *dev = indio_dev->dev.parent; |
| struct iio_chan_spec *channels; |
| bool slow_bus; |
| int ret, i; |
| |
| slow_bus = !(st->bops->iio_backend_config || st->offload_en); |
| indio_dev->num_channels = st->chip_info->num_adc_channels; |
| |
| /* Slow buses also get 1 more channel for soft timestamp */ |
| if (slow_bus) |
| indio_dev->num_channels++; |
| |
| channels = devm_kcalloc(dev, indio_dev->num_channels, sizeof(*channels), |
| GFP_KERNEL); |
| if (!channels) |
| return -ENOMEM; |
| |
| for (i = 0; i < st->chip_info->num_adc_channels; i++) { |
| struct iio_chan_spec *chan = &channels[i]; |
| |
| chan->type = IIO_VOLTAGE; |
| chan->indexed = 1; |
| chan->channel = i; |
| chan->scan_index = i; |
| chan->scan_type.sign = 's'; |
| chan->scan_type.realbits = st->chip_info->bits; |
| /* |
| * If in SPI offload mode, storagebits are set based |
| * on the spi-engine hw implementation. |
| */ |
| chan->scan_type.storagebits = st->offload_en ? |
| st->chip_info->offload_storagebits : |
| (st->chip_info->bits > 16 ? 32 : 16); |
| |
| chan->scan_type.endianness = IIO_CPU; |
| |
| if (indio_dev->modes & INDIO_DIRECT_MODE) |
| chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); |
| |
| if (st->sw_mode_en) { |
| chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); |
| chan->info_mask_separate_available |= |
| BIT(IIO_CHAN_INFO_SCALE); |
| |
| if (st->chip_info->calib_offset_avail) { |
| chan->info_mask_separate |= |
| BIT(IIO_CHAN_INFO_CALIBBIAS) | |
| BIT(IIO_CHAN_INFO_CONVDELAY); |
| chan->info_mask_separate_available |= |
| BIT(IIO_CHAN_INFO_CALIBBIAS) | |
| BIT(IIO_CHAN_INFO_CONVDELAY); |
| } |
| |
| /* |
| * All chips with software mode support oversampling, |
| * so we skip the oversampling_available check. And the |
| * shared_by_type instead of shared_by_all on slow |
| * buses is for backward compatibility. |
| */ |
| if (slow_bus) |
| chan->info_mask_shared_by_type |= |
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); |
| else |
| chan->info_mask_shared_by_all |= |
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); |
| |
| chan->info_mask_shared_by_all_available |= |
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); |
| } else { |
| chan->info_mask_shared_by_type |= |
| BIT(IIO_CHAN_INFO_SCALE); |
| |
| if (st->chip_info->oversampling_avail) |
| chan->info_mask_shared_by_all |= |
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); |
| } |
| |
| if (!slow_bus) |
| chan->info_mask_shared_by_all |= |
| BIT(IIO_CHAN_INFO_SAMP_FREQ); |
| |
| ret = st->chip_info->scale_setup_cb(indio_dev, chan); |
| if (ret) |
| return ret; |
| } |
| |
| if (slow_bus) |
| channels[i] = (struct iio_chan_spec)IIO_CHAN_SOFT_TIMESTAMP(i); |
| |
| indio_dev->channels = channels; |
| |
| return 0; |
| } |
| |
| static void ad7606_pwm_disable(void *data) |
| { |
| pwm_disable(data); |
| } |
| |
| int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, |
| const struct ad7606_chip_info *chip_info, |
| const struct ad7606_bus_ops *bops) |
| { |
| struct ad7606_state *st; |
| int ret; |
| struct iio_dev *indio_dev; |
| |
| indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); |
| if (!indio_dev) |
| return -ENOMEM; |
| |
| st = iio_priv(indio_dev); |
| dev_set_drvdata(dev, indio_dev); |
| |
| ret = devm_mutex_init(dev, &st->lock); |
| if (ret) |
| return ret; |
| |
| st->dev = dev; |
| st->bops = bops; |
| st->base_address = base_address; |
| st->oversampling = 1; |
| st->sw_mode_en = device_property_read_bool(dev, "adi,sw-mode"); |
| |
| if (st->sw_mode_en && !chip_info->sw_setup_cb) |
| return dev_err_probe(dev, -EINVAL, |
| "Software mode is not supported for this chip\n"); |
| |
| ret = devm_regulator_get_enable(dev, "avcc"); |
| if (ret) |
| return dev_err_probe(dev, ret, |
| "Failed to enable specified AVcc supply\n"); |
| |
| ret = devm_regulator_get_enable(dev, "vdrive"); |
| if (ret) |
| return dev_err_probe(dev, ret, |
| "Failed to enable Vdrive supply\n"); |
| |
| ret = devm_regulator_get_enable_optional(dev, "refin"); |
| if (ret && ret != -ENODEV) |
| return dev_err_probe(dev, ret, |
| "Failed to enable REFIN supply\n"); |
| |
| st->chip_info = chip_info; |
| |
| if (st->chip_info->oversampling_num) { |
| st->oversampling_avail = st->chip_info->oversampling_avail; |
| st->num_os_ratios = st->chip_info->oversampling_num; |
| } |
| |
| ret = ad7606_request_gpios(st); |
| if (ret) |
| return ret; |
| |
| if (st->gpio_os) { |
| if (st->gpio_range) |
| indio_dev->info = &ad7606_info_os_and_range; |
| else |
| indio_dev->info = &ad7606_info_os; |
| } else { |
| if (st->gpio_range) |
| indio_dev->info = &ad7606_info_range; |
| else |
| indio_dev->info = &ad7606_info_no_os_or_range; |
| } |
| |
| /* AXI ADC backend doesn't support single read. */ |
| indio_dev->modes = st->bops->iio_backend_config ? 0 : INDIO_DIRECT_MODE; |
| indio_dev->name = chip_info->name; |
| |
| /* Using spi-engine with offload support ? */ |
| if (st->bops->offload_config) { |
| ret = st->bops->offload_config(dev, indio_dev); |
| if (ret) |
| return ret; |
| } |
| |
| ret = ad7606_probe_channels(indio_dev); |
| if (ret) |
| return ret; |
| |
| ret = ad7606_reset(st); |
| if (ret) |
| dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); |
| |
| /* AD7616 requires al least 15ms to reconfigure after a reset */ |
| if (st->chip_info->init_delay_ms) { |
| if (msleep_interruptible(st->chip_info->init_delay_ms)) |
| return -ERESTARTSYS; |
| } |
| |
| /* If convst pin is not defined, setup PWM. */ |
| if (!st->gpio_convst || st->offload_en) { |
| st->cnvst_pwm = devm_pwm_get(dev, NULL); |
| if (IS_ERR(st->cnvst_pwm)) |
| return PTR_ERR(st->cnvst_pwm); |
| |
| /* The PWM is initialized at 1MHz to have a fast enough GPIO emulation. */ |
| ret = ad7606_set_sampling_freq(st, 1 * MEGA); |
| if (ret) |
| return ret; |
| |
| ret = ad7606_pwm_set_low(st); |
| if (ret) |
| return ret; |
| |
| /* |
| * PWM is not disabled when sampling stops, but instead its duty cycle is set |
| * to 0% to be sure we have a "low" state. After we unload the driver, let's |
| * disable the PWM. |
| */ |
| ret = devm_add_action_or_reset(dev, ad7606_pwm_disable, |
| st->cnvst_pwm); |
| if (ret) |
| return ret; |
| } |
| |
| if (st->bops->iio_backend_config) { |
| /* |
| * If there is a backend, the PWM should not overpass the maximum sampling |
| * frequency the chip supports. |
| */ |
| ret = ad7606_set_sampling_freq(st, chip_info->max_samplerate); |
| if (ret) |
| return ret; |
| |
| ret = st->bops->iio_backend_config(dev, indio_dev); |
| if (ret) |
| return ret; |
| |
| indio_dev->setup_ops = &ad7606_backend_buffer_ops; |
| } else if (!st->offload_en) { |
| /* Reserve the PWM use only for backend (force gpio_convst definition) */ |
| if (!st->gpio_convst) |
| return dev_err_probe(dev, -EINVAL, |
| "No backend, connect convst to a GPIO"); |
| |
| init_completion(&st->completion); |
| st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", |
| indio_dev->name, |
| iio_device_id(indio_dev)); |
| if (!st->trig) |
| return -ENOMEM; |
| |
| st->trig->ops = &ad7606_trigger_ops; |
| iio_trigger_set_drvdata(st->trig, indio_dev); |
| ret = devm_iio_trigger_register(dev, st->trig); |
| if (ret) |
| return ret; |
| |
| indio_dev->trig = iio_trigger_get(st->trig); |
| |
| ret = devm_request_threaded_irq(dev, irq, NULL, &ad7606_interrupt, |
| IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| chip_info->name, indio_dev); |
| if (ret) |
| return ret; |
| |
| ret = devm_iio_triggered_buffer_setup(dev, indio_dev, |
| &iio_pollfunc_store_time, |
| &ad7606_trigger_handler, |
| &ad7606_buffer_ops); |
| if (ret) |
| return ret; |
| } |
| |
| st->write_scale = ad7606_write_scale_hw; |
| st->write_os = ad7606_write_os_hw; |
| |
| /* Offload needs 1 DOUT line, applying this setting in sw_setup_cb. */ |
| if (st->sw_mode_en || st->offload_en) { |
| indio_dev->info = &ad7606_info_sw_mode; |
| st->chip_info->sw_setup_cb(indio_dev); |
| } |
| |
| if (st->sw_mode_en && st->chip_info->calib_gain_avail) { |
| ret = ad7606_set_gain_calib(st); |
| if (ret) |
| return ret; |
| } |
| |
| return devm_iio_device_register(dev, indio_dev); |
| } |
| EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606"); |
| |
| #ifdef CONFIG_PM_SLEEP |
| |
| static int ad7606_suspend(struct device *dev) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| if (st->gpio_standby) { |
| gpiod_set_value(st->gpio_range, 1); |
| gpiod_set_value(st->gpio_standby, 1); |
| } |
| |
| return 0; |
| } |
| |
| static int ad7606_resume(struct device *dev) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct ad7606_state *st = iio_priv(indio_dev); |
| |
| if (st->gpio_standby) { |
| gpiod_set_value(st->gpio_range, st->chan_info[0].range); |
| gpiod_set_value(st->gpio_standby, 1); |
| ad7606_reset(st); |
| } |
| |
| return 0; |
| } |
| |
| SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume); |
| EXPORT_SYMBOL_NS_GPL(ad7606_pm_ops, "IIO_AD7606"); |
| |
| #endif |
| |
| MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); |
| MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); |
| MODULE_LICENSE("GPL v2"); |