| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * AD7770, AD7771, AD7779 ADC |
| * |
| * Copyright 2023-2024 Analog Devices Inc. |
| */ |
| |
| #include <linux/bitfield.h> |
| #include <linux/bitmap.h> |
| #include <linux/clk.h> |
| #include <linux/crc8.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/math.h> |
| #include <linux/module.h> |
| #include <linux/mod_devicetable.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/spi/spi.h> |
| #include <linux/string.h> |
| #include <linux/types.h> |
| #include <linux/unaligned.h> |
| #include <linux/units.h> |
| |
| #include <linux/iio/iio.h> |
| #include <linux/iio/buffer.h> |
| #include <linux/iio/sysfs.h> |
| #include <linux/iio/trigger.h> |
| #include <linux/iio/triggered_buffer.h> |
| #include <linux/iio/trigger_consumer.h> |
| |
| #define AD7779_SPI_READ_CMD BIT(7) |
| |
| #define AD7779_DISABLE_SD BIT(7) |
| |
| #define AD7779_REG_CH_DISABLE 0x08 |
| #define AD7779_REG_CH_SYNC_OFFSET(ch) (0x09 + (ch)) |
| #define AD7779_REG_CH_CONFIG(ch) (0x00 + (ch)) |
| #define AD7779_REG_GENERAL_USER_CONFIG_1 0x11 |
| #define AD7779_REG_GENERAL_USER_CONFIG_2 0x12 |
| #define AD7779_REG_GENERAL_USER_CONFIG_3 0x13 |
| #define AD7779_REG_DOUT_FORMAT 0x14 |
| #define AD7779_REG_ADC_MUX_CONFIG 0x15 |
| #define AD7779_REG_GPIO_CONFIG 0x17 |
| #define AD7779_REG_BUFFER_CONFIG_1 0x19 |
| #define AD7779_REG_GLOBAL_MUX_CONFIG 0x16 |
| #define AD7779_REG_BUFFER_CONFIG_2 0x1A |
| #define AD7779_REG_GPIO_DATA 0x18 |
| #define AD7779_REG_CH_OFFSET_UPPER_BYTE(ch) (0x1C + (ch) * 6) |
| #define AD7779_REG_CH_OFFSET_LOWER_BYTE(ch) (0x1E + (ch) * 6) |
| #define AD7779_REG_CH_GAIN_UPPER_BYTE(ch) (0x1F + (ch) * 6) |
| #define AD7779_REG_CH_OFFSET_MID_BYTE(ch) (0x1D + (ch) * 6) |
| #define AD7779_REG_CH_GAIN_MID_BYTE(ch) (0x20 + (ch) * 6) |
| #define AD7779_REG_CH_ERR_REG(ch) (0x4C + (ch)) |
| #define AD7779_REG_CH0_1_SAT_ERR 0x54 |
| #define AD7779_REG_CH_GAIN_LOWER_BYTE(ch) (0x21 + (ch) * 6) |
| #define AD7779_REG_CH2_3_SAT_ERR 0x55 |
| #define AD7779_REG_CH4_5_SAT_ERR 0x56 |
| #define AD7779_REG_CH6_7_SAT_ERR 0x57 |
| #define AD7779_REG_CHX_ERR_REG_EN 0x58 |
| #define AD7779_REG_GEN_ERR_REG_1 0x59 |
| #define AD7779_REG_GEN_ERR_REG_1_EN 0x5A |
| #define AD7779_REG_GEN_ERR_REG_2 0x5B |
| #define AD7779_REG_GEN_ERR_REG_2_EN 0x5C |
| #define AD7779_REG_STATUS_REG_1 0x5D |
| #define AD7779_REG_STATUS_REG_2 0x5E |
| #define AD7779_REG_STATUS_REG_3 0x5F |
| #define AD7779_REG_SRC_N_MSB 0x60 |
| #define AD7779_REG_SRC_N_LSB 0x61 |
| #define AD7779_REG_SRC_IF_MSB 0x62 |
| #define AD7779_REG_SRC_IF_LSB 0x63 |
| #define AD7779_REG_SRC_UPDATE 0x64 |
| |
| #define AD7779_FILTER_MSK BIT(6) |
| #define AD7779_MOD_POWERMODE_MSK BIT(6) |
| #define AD7779_MOD_PDB_REFOUT_MSK BIT(4) |
| #define AD7779_MOD_SPI_EN_MSK BIT(4) |
| #define AD7779_USRMOD_INIT_MSK GENMASK(6, 4) |
| |
| /* AD7779_REG_DOUT_FORMAT */ |
| #define AD7779_DOUT_FORMAT_MSK GENMASK(7, 6) |
| #define AD7779_DOUT_HEADER_FORMAT BIT(5) |
| #define AD7779_DCLK_CLK_DIV_MSK GENMASK(3, 1) |
| |
| #define AD7779_REFMUX_CTRL_MSK GENMASK(7, 6) |
| #define AD7779_SPI_CRC_EN_MSK BIT(0) |
| |
| #define AD7779_MAXCLK_LOWPOWER (4096 * HZ_PER_KHZ) |
| #define AD7779_NUM_CHANNELS 8 |
| #define AD7779_RESET_BUF_SIZE 8 |
| #define AD7779_CHAN_DATA_SIZE 4 |
| |
| #define AD7779_LOWPOWER_DIV 512 |
| #define AD7779_HIGHPOWER_DIV 2048 |
| |
| #define AD7779_SINC3_MAXFREQ (16 * HZ_PER_KHZ) |
| #define AD7779_SINC5_MAXFREQ (128 * HZ_PER_KHZ) |
| |
| #define AD7779_DEFAULT_SAMPLING_FREQ (8 * HZ_PER_KHZ) |
| #define AD7779_DEFAULT_SAMPLING_2LINE (4 * HZ_PER_KHZ) |
| #define AD7779_DEFAULT_SAMPLING_1LINE (2 * HZ_PER_KHZ) |
| |
| #define AD7779_SPIMODE_MAX_SAMP_FREQ (16 * HZ_PER_KHZ) |
| |
| #define GAIN_REL 0x555555 |
| #define AD7779_FREQ_MSB_MSK GENMASK(15, 8) |
| #define AD7779_FREQ_LSB_MSK GENMASK(7, 0) |
| #define AD7779_UPPER GENMASK(23, 16) |
| #define AD7779_MID GENMASK(15, 8) |
| #define AD7779_LOWER GENMASK(7, 0) |
| |
| #define AD7779_REG_MSK GENMASK(6, 0) |
| |
| #define AD7779_CRC8_POLY 0x07 |
| DECLARE_CRC8_TABLE(ad7779_crc8_table); |
| |
| enum ad7779_filter { |
| AD7779_SINC3, |
| AD7779_SINC5, |
| }; |
| |
| enum ad7779_variant { |
| ad7770, |
| ad7771, |
| ad7779, |
| }; |
| |
| enum ad7779_power_mode { |
| AD7779_LOW_POWER, |
| AD7779_HIGH_POWER, |
| }; |
| |
| struct ad7779_chip_info { |
| const char *name; |
| struct iio_chan_spec const *channels; |
| }; |
| |
| struct ad7779_state { |
| struct spi_device *spi; |
| const struct ad7779_chip_info *chip_info; |
| struct clk *mclk; |
| struct iio_trigger *trig; |
| struct completion completion; |
| unsigned int sampling_freq; |
| enum ad7779_filter filter_enabled; |
| /* |
| * DMA (thus cache coherency maintenance) requires the |
| * transfer buffers to live in their own cache lines. |
| */ |
| struct { |
| u32 chans[8]; |
| aligned_s64 timestamp; |
| } data __aligned(IIO_DMA_MINALIGN); |
| u32 spidata_tx[8]; |
| u8 reg_rx_buf[3]; |
| u8 reg_tx_buf[3]; |
| u8 reset_buf[8]; |
| }; |
| |
| static const char * const ad7779_filter_type[] = { |
| [AD7779_SINC3] = "sinc3", |
| [AD7779_SINC5] = "sinc5", |
| }; |
| |
| static const char * const ad7779_power_supplies[] = { |
| "avdd1", "avdd2", "avdd4", |
| }; |
| |
| static int ad7779_spi_read(struct ad7779_state *st, u8 reg, u8 *rbuf) |
| { |
| int ret; |
| u8 crc_buf[2]; |
| u8 exp_crc; |
| struct spi_transfer t = { |
| .tx_buf = st->reg_tx_buf, |
| .rx_buf = st->reg_rx_buf, |
| }; |
| |
| st->reg_tx_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg); |
| st->reg_tx_buf[1] = 0; |
| |
| if (reg == AD7779_REG_GEN_ERR_REG_1_EN) { |
| t.len = 2; |
| } else { |
| t.len = 3; |
| st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf, |
| t.len - 1, 0); |
| } |
| |
| ret = spi_sync_transfer(st->spi, &t, 1); |
| if (ret) |
| return ret; |
| |
| crc_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg); |
| crc_buf[1] = st->reg_rx_buf[1]; |
| exp_crc = crc8(ad7779_crc8_table, crc_buf, ARRAY_SIZE(crc_buf), 0); |
| if (reg != AD7779_REG_GEN_ERR_REG_1_EN && exp_crc != st->reg_rx_buf[2]) { |
| dev_err(&st->spi->dev, "Bad CRC %x, expected %x", |
| st->reg_rx_buf[2], exp_crc); |
| return -EINVAL; |
| } |
| *rbuf = st->reg_rx_buf[1]; |
| |
| return 0; |
| } |
| |
| static int ad7779_spi_write(struct ad7779_state *st, u8 reg, u8 val) |
| { |
| u8 length = 3; |
| |
| st->reg_tx_buf[0] = FIELD_GET(AD7779_REG_MSK, reg); |
| st->reg_tx_buf[1] = val; |
| if (reg == AD7779_REG_GEN_ERR_REG_1_EN) |
| length = 2; |
| else |
| st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf, |
| length - 1, 0); |
| |
| return spi_write(st->spi, st->reg_tx_buf, length); |
| } |
| |
| static int ad7779_spi_write_mask(struct ad7779_state *st, u8 reg, u8 mask, |
| u8 val) |
| { |
| int ret; |
| u8 regval, data; |
| |
| ret = ad7779_spi_read(st, reg, &data); |
| if (ret) |
| return ret; |
| |
| regval = (data & ~mask) | (val & mask); |
| |
| if (regval == data) |
| return 0; |
| |
| return ad7779_spi_write(st, reg, regval); |
| } |
| |
| static int ad7779_reg_access(struct iio_dev *indio_dev, |
| unsigned int reg, |
| unsigned int writeval, |
| unsigned int *readval) |
| { |
| struct ad7779_state *st = iio_priv(indio_dev); |
| u8 rval; |
| int ret; |
| |
| if (readval) { |
| ret = ad7779_spi_read(st, reg, &rval); |
| *readval = rval; |
| return ret; |
| } |
| |
| return ad7779_spi_write(st, reg, writeval); |
| } |
| |
| static int ad7779_set_sampling_frequency(struct ad7779_state *st, |
| unsigned int sampling_freq) |
| { |
| int ret; |
| unsigned int dec; |
| unsigned int frac; |
| unsigned int div; |
| unsigned int decimal; |
| unsigned int freq_khz; |
| |
| if (st->filter_enabled == AD7779_SINC3 && |
| sampling_freq > AD7779_SINC3_MAXFREQ) |
| return -EINVAL; |
| |
| if (st->filter_enabled == AD7779_SINC5 && |
| sampling_freq > AD7779_SINC5_MAXFREQ) |
| return -EINVAL; |
| |
| if (sampling_freq > AD7779_SPIMODE_MAX_SAMP_FREQ) |
| return -EINVAL; |
| |
| div = AD7779_HIGHPOWER_DIV; |
| |
| freq_khz = sampling_freq / HZ_PER_KHZ; |
| dec = div / freq_khz; |
| frac = div % freq_khz; |
| |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, |
| FIELD_GET(AD7779_FREQ_MSB_MSK, dec)); |
| if (ret) |
| return ret; |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, |
| FIELD_GET(AD7779_FREQ_LSB_MSK, dec)); |
| if (ret) |
| return ret; |
| |
| if (frac) { |
| /* |
| * In order to obtain the first three decimals of the decimation |
| * the initial number is multiplied with 10^3 prior to the |
| * division, then the original division result is subtracted and |
| * the number is divided by 10^3. |
| */ |
| decimal = ((mult_frac(div, KILO, freq_khz) - dec * KILO) << 16) |
| / KILO; |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, |
| FIELD_GET(AD7779_FREQ_MSB_MSK, decimal)); |
| if (ret) |
| return ret; |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, |
| FIELD_GET(AD7779_FREQ_LSB_MSK, decimal)); |
| if (ret) |
| return ret; |
| } else { |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, |
| FIELD_GET(AD7779_FREQ_MSB_MSK, 0x0)); |
| if (ret) |
| return ret; |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, |
| FIELD_GET(AD7779_FREQ_LSB_MSK, 0x0)); |
| if (ret) |
| return ret; |
| } |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, BIT(0)); |
| if (ret) |
| return ret; |
| |
| /* SRC update settling time */ |
| fsleep(15); |
| |
| ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, 0x0); |
| if (ret) |
| return ret; |
| |
| /* SRC update settling time */ |
| fsleep(15); |
| |
| st->sampling_freq = sampling_freq; |
| |
| return 0; |
| } |
| |
| static int ad7779_get_filter(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan) |
| { |
| struct ad7779_state *st = iio_priv(indio_dev); |
| u8 temp; |
| int ret; |
| |
| ret = ad7779_spi_read(st, AD7779_REG_GENERAL_USER_CONFIG_2, &temp); |
| if (ret) |
| return ret; |
| |
| return FIELD_GET(AD7779_FILTER_MSK, temp); |
| } |
| |
| static int ad7779_set_filter(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| unsigned int mode) |
| { |
| struct ad7779_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| ret = ad7779_spi_write_mask(st, |
| AD7779_REG_GENERAL_USER_CONFIG_2, |
| AD7779_FILTER_MSK, |
| FIELD_PREP(AD7779_FILTER_MSK, mode)); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_set_sampling_frequency(st, st->sampling_freq); |
| if (ret) |
| return ret; |
| |
| st->filter_enabled = mode; |
| |
| return 0; |
| } |
| |
| static int ad7779_get_calibscale(struct ad7779_state *st, int channel) |
| { |
| int ret; |
| u8 calibscale[3]; |
| |
| ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel), |
| &calibscale[0]); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_MID_BYTE(channel), |
| &calibscale[1]); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel), |
| &calibscale[2]); |
| if (ret) |
| return ret; |
| |
| return get_unaligned_be24(calibscale); |
| } |
| |
| static int ad7779_set_calibscale(struct ad7779_state *st, int channel, int val) |
| { |
| int ret; |
| unsigned int gain; |
| u8 gain_bytes[3]; |
| |
| /* |
| * The gain value is relative to 0x555555, which represents a gain of 1 |
| */ |
| gain = DIV_ROUND_CLOSEST_ULL((u64)val * 5592405LL, MEGA); |
| put_unaligned_be24(gain, gain_bytes); |
| ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel), |
| gain_bytes[0]); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_MID_BYTE(channel), |
| gain_bytes[1]); |
| if (ret) |
| return ret; |
| |
| return ad7779_spi_write(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel), |
| gain_bytes[2]); |
| } |
| |
| static int ad7779_get_calibbias(struct ad7779_state *st, int channel) |
| { |
| int ret; |
| u8 calibbias[3]; |
| |
| ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel), |
| &calibbias[0]); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel), |
| &calibbias[1]); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel), |
| &calibbias[2]); |
| if (ret) |
| return ret; |
| |
| return get_unaligned_be24(calibbias); |
| } |
| |
| static int ad7779_set_calibbias(struct ad7779_state *st, int channel, int val) |
| { |
| int ret; |
| u8 calibbias[3]; |
| |
| put_unaligned_be24(val, calibbias); |
| ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel), |
| calibbias[0]); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel), |
| calibbias[1]); |
| if (ret) |
| return ret; |
| |
| return ad7779_spi_write(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel), |
| calibbias[2]); |
| } |
| |
| static int __ad7779_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int *val, |
| int *val2, long mask) |
| { |
| struct ad7779_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_CALIBSCALE: |
| ret = ad7779_get_calibscale(st, chan->channel); |
| if (ret < 0) |
| return ret; |
| *val = ret; |
| *val2 = GAIN_REL; |
| return IIO_VAL_FRACTIONAL; |
| case IIO_CHAN_INFO_CALIBBIAS: |
| ret = ad7779_get_calibbias(st, chan->channel); |
| if (ret < 0) |
| return ret; |
| *val = ret; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| *val = st->sampling_freq; |
| if (*val < 0) |
| return -EINVAL; |
| return IIO_VAL_INT; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int ad7779_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int *val, |
| int *val2, long mask) |
| { |
| int ret; |
| |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| |
| ret = __ad7779_read_raw(indio_dev, chan, val, val2, mask); |
| iio_device_release_direct(indio_dev); |
| return ret; |
| } |
| |
| static int __ad7779_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| int val, int val2, |
| long mask) |
| { |
| struct ad7779_state *st = iio_priv(indio_dev); |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_CALIBSCALE: |
| return ad7779_set_calibscale(st, chan->channel, val2); |
| case IIO_CHAN_INFO_CALIBBIAS: |
| return ad7779_set_calibbias(st, chan->channel, val); |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| return ad7779_set_sampling_frequency(st, val); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int ad7779_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int val, int val2, |
| long mask) |
| { |
| int ret; |
| |
| if (!iio_device_claim_direct(indio_dev)) |
| return -EBUSY; |
| |
| ret = __ad7779_write_raw(indio_dev, chan, val, val2, mask); |
| iio_device_release_direct(indio_dev); |
| return ret; |
| } |
| |
| static int ad7779_buffer_preenable(struct iio_dev *indio_dev) |
| { |
| int ret; |
| struct ad7779_state *st = iio_priv(indio_dev); |
| |
| ret = ad7779_spi_write_mask(st, |
| AD7779_REG_GENERAL_USER_CONFIG_3, |
| AD7779_MOD_SPI_EN_MSK, |
| FIELD_PREP(AD7779_MOD_SPI_EN_MSK, 1)); |
| if (ret) |
| return ret; |
| |
| /* |
| * DRDY output cannot be disabled at device level therefore we mask |
| * the irq at host end. |
| */ |
| enable_irq(st->spi->irq); |
| |
| return 0; |
| } |
| |
| static int ad7779_buffer_postdisable(struct iio_dev *indio_dev) |
| { |
| struct ad7779_state *st = iio_priv(indio_dev); |
| |
| disable_irq(st->spi->irq); |
| |
| return ad7779_spi_write(st, AD7779_REG_GENERAL_USER_CONFIG_3, |
| AD7779_DISABLE_SD); |
| } |
| |
| static irqreturn_t ad7779_trigger_handler(int irq, void *p) |
| { |
| struct iio_poll_func *pf = p; |
| struct iio_dev *indio_dev = pf->indio_dev; |
| struct ad7779_state *st = iio_priv(indio_dev); |
| int ret; |
| struct spi_transfer t = { |
| .rx_buf = st->data.chans, |
| .tx_buf = st->spidata_tx, |
| .len = AD7779_NUM_CHANNELS * AD7779_CHAN_DATA_SIZE, |
| }; |
| |
| st->spidata_tx[0] = AD7779_SPI_READ_CMD; |
| ret = spi_sync_transfer(st->spi, &t, 1); |
| if (ret) { |
| dev_err(&st->spi->dev, "SPI transfer error in IRQ handler"); |
| goto exit_handler; |
| } |
| |
| iio_push_to_buffers_with_timestamp(indio_dev, &st->data, pf->timestamp); |
| |
| exit_handler: |
| iio_trigger_notify_done(indio_dev->trig); |
| return IRQ_HANDLED; |
| } |
| |
| static int ad7779_reset(struct iio_dev *indio_dev, struct gpio_desc *reset_gpio) |
| { |
| struct ad7779_state *st = iio_priv(indio_dev); |
| int ret; |
| struct spi_transfer t = { |
| .tx_buf = st->reset_buf, |
| .len = 8, |
| }; |
| |
| if (reset_gpio) { |
| gpiod_set_value(reset_gpio, 1); |
| /* Delay for reset to occur is 225 microseconds */ |
| fsleep(230); |
| ret = 0; |
| } else { |
| memset(st->reset_buf, 0xff, sizeof(st->reset_buf)); |
| ret = spi_sync_transfer(st->spi, &t, 1); |
| if (ret) |
| return ret; |
| } |
| |
| /* Delay for reset to occur is 225 microseconds */ |
| fsleep(230); |
| |
| return ret; |
| } |
| |
| static const struct iio_info ad7779_info = { |
| .read_raw = ad7779_read_raw, |
| .write_raw = ad7779_write_raw, |
| .debugfs_reg_access = &ad7779_reg_access, |
| }; |
| |
| static const struct iio_enum ad7779_filter_enum = { |
| .items = ad7779_filter_type, |
| .num_items = ARRAY_SIZE(ad7779_filter_type), |
| .get = ad7779_get_filter, |
| .set = ad7779_set_filter, |
| }; |
| |
| static const struct iio_chan_spec_ext_info ad7779_ext_filter[] = { |
| IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7779_filter_enum), |
| IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, |
| &ad7779_filter_enum), |
| { } |
| }; |
| |
| #define AD777x_CHAN_S(index, _ext_info) \ |
| { \ |
| .type = IIO_VOLTAGE, \ |
| .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ |
| BIT(IIO_CHAN_INFO_CALIBBIAS), \ |
| .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ |
| .address = (index), \ |
| .indexed = 1, \ |
| .channel = (index), \ |
| .scan_index = (index), \ |
| .ext_info = (_ext_info), \ |
| .scan_type = { \ |
| .sign = 's', \ |
| .realbits = 24, \ |
| .storagebits = 32, \ |
| .endianness = IIO_BE, \ |
| }, \ |
| } |
| |
| #define AD777x_CHAN_NO_FILTER_S(index) \ |
| AD777x_CHAN_S(index, NULL) |
| |
| #define AD777x_CHAN_FILTER_S(index) \ |
| AD777x_CHAN_S(index, ad7779_ext_filter) |
| static const struct iio_chan_spec ad7779_channels[] = { |
| AD777x_CHAN_NO_FILTER_S(0), |
| AD777x_CHAN_NO_FILTER_S(1), |
| AD777x_CHAN_NO_FILTER_S(2), |
| AD777x_CHAN_NO_FILTER_S(3), |
| AD777x_CHAN_NO_FILTER_S(4), |
| AD777x_CHAN_NO_FILTER_S(5), |
| AD777x_CHAN_NO_FILTER_S(6), |
| AD777x_CHAN_NO_FILTER_S(7), |
| IIO_CHAN_SOFT_TIMESTAMP(8), |
| }; |
| |
| static const struct iio_chan_spec ad7779_channels_filter[] = { |
| AD777x_CHAN_FILTER_S(0), |
| AD777x_CHAN_FILTER_S(1), |
| AD777x_CHAN_FILTER_S(2), |
| AD777x_CHAN_FILTER_S(3), |
| AD777x_CHAN_FILTER_S(4), |
| AD777x_CHAN_FILTER_S(5), |
| AD777x_CHAN_FILTER_S(6), |
| AD777x_CHAN_FILTER_S(7), |
| IIO_CHAN_SOFT_TIMESTAMP(8), |
| }; |
| |
| static const struct iio_buffer_setup_ops ad7779_buffer_setup_ops = { |
| .preenable = ad7779_buffer_preenable, |
| .postdisable = ad7779_buffer_postdisable, |
| }; |
| |
| static const struct iio_trigger_ops ad7779_trigger_ops = { |
| .validate_device = iio_trigger_validate_own_device, |
| }; |
| |
| static int ad7779_conf(struct ad7779_state *st, struct gpio_desc *start_gpio) |
| { |
| int ret; |
| |
| ret = ad7779_spi_write_mask(st, AD7779_REG_GEN_ERR_REG_1_EN, |
| AD7779_SPI_CRC_EN_MSK, |
| FIELD_PREP(AD7779_SPI_CRC_EN_MSK, 1)); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, |
| AD7779_USRMOD_INIT_MSK, |
| FIELD_PREP(AD7779_USRMOD_INIT_MSK, 5)); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT, |
| AD7779_DCLK_CLK_DIV_MSK, |
| FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 1)); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_write_mask(st, AD7779_REG_ADC_MUX_CONFIG, |
| AD7779_REFMUX_CTRL_MSK, |
| FIELD_PREP(AD7779_REFMUX_CTRL_MSK, 1)); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_set_sampling_frequency(st, AD7779_DEFAULT_SAMPLING_FREQ); |
| if (ret) |
| return ret; |
| |
| gpiod_set_value(start_gpio, 0); |
| /* Start setup time */ |
| fsleep(15); |
| gpiod_set_value(start_gpio, 1); |
| /* Start setup time */ |
| fsleep(15); |
| gpiod_set_value(start_gpio, 0); |
| /* Start setup time */ |
| fsleep(15); |
| |
| return 0; |
| } |
| |
| static int ad7779_probe(struct spi_device *spi) |
| { |
| struct iio_dev *indio_dev; |
| struct ad7779_state *st; |
| struct gpio_desc *reset_gpio, *start_gpio; |
| struct device *dev = &spi->dev; |
| int ret = -EINVAL; |
| |
| if (!spi->irq) |
| return dev_err_probe(dev, ret, "DRDY irq not present\n"); |
| |
| indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); |
| if (!indio_dev) |
| return -ENOMEM; |
| |
| st = iio_priv(indio_dev); |
| |
| ret = devm_regulator_bulk_get_enable(dev, |
| ARRAY_SIZE(ad7779_power_supplies), |
| ad7779_power_supplies); |
| if (ret) |
| return dev_err_probe(dev, ret, |
| "failed to get and enable supplies\n"); |
| |
| st->mclk = devm_clk_get_enabled(dev, "mclk"); |
| if (IS_ERR(st->mclk)) |
| return PTR_ERR(st->mclk); |
| |
| reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); |
| if (IS_ERR(reset_gpio)) |
| return PTR_ERR(reset_gpio); |
| |
| start_gpio = devm_gpiod_get(dev, "start", GPIOD_OUT_HIGH); |
| if (IS_ERR(start_gpio)) |
| return PTR_ERR(start_gpio); |
| |
| crc8_populate_msb(ad7779_crc8_table, AD7779_CRC8_POLY); |
| st->spi = spi; |
| |
| st->chip_info = spi_get_device_match_data(spi); |
| if (!st->chip_info) |
| return -ENODEV; |
| |
| ret = ad7779_reset(indio_dev, reset_gpio); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_conf(st, start_gpio); |
| if (ret) |
| return ret; |
| |
| indio_dev->name = st->chip_info->name; |
| indio_dev->info = &ad7779_info; |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| indio_dev->channels = st->chip_info->channels; |
| indio_dev->num_channels = ARRAY_SIZE(ad7779_channels); |
| |
| 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 = &ad7779_trigger_ops; |
| |
| iio_trigger_set_drvdata(st->trig, st); |
| |
| ret = devm_request_irq(dev, spi->irq, iio_trigger_generic_data_rdy_poll, |
| IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name, |
| st->trig); |
| if (ret) |
| return dev_err_probe(dev, ret, "request IRQ %d failed\n", |
| st->spi->irq); |
| |
| ret = devm_iio_trigger_register(dev, st->trig); |
| if (ret) |
| return ret; |
| |
| indio_dev->trig = iio_trigger_get(st->trig); |
| |
| init_completion(&st->completion); |
| |
| ret = devm_iio_triggered_buffer_setup(dev, indio_dev, |
| &iio_pollfunc_store_time, |
| &ad7779_trigger_handler, |
| &ad7779_buffer_setup_ops); |
| if (ret) |
| return ret; |
| |
| ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT, |
| AD7779_DCLK_CLK_DIV_MSK, |
| FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 7)); |
| if (ret) |
| return ret; |
| |
| return devm_iio_device_register(dev, indio_dev); |
| } |
| |
| static int ad7779_suspend(struct device *dev) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct ad7779_state *st = iio_priv(indio_dev); |
| |
| return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, |
| AD7779_MOD_POWERMODE_MSK, |
| FIELD_PREP(AD7779_MOD_POWERMODE_MSK, |
| AD7779_LOW_POWER)); |
| } |
| |
| static int ad7779_resume(struct device *dev) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct ad7779_state *st = iio_priv(indio_dev); |
| |
| return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, |
| AD7779_MOD_POWERMODE_MSK, |
| FIELD_PREP(AD7779_MOD_POWERMODE_MSK, |
| AD7779_HIGH_POWER)); |
| } |
| |
| static DEFINE_SIMPLE_DEV_PM_OPS(ad7779_pm_ops, ad7779_suspend, ad7779_resume); |
| |
| static const struct ad7779_chip_info ad7770_chip_info = { |
| .name = "ad7770", |
| .channels = ad7779_channels, |
| }; |
| |
| static const struct ad7779_chip_info ad7771_chip_info = { |
| .name = "ad7771", |
| .channels = ad7779_channels_filter, |
| }; |
| |
| static const struct ad7779_chip_info ad7779_chip_info = { |
| .name = "ad7779", |
| .channels = ad7779_channels, |
| }; |
| |
| static const struct spi_device_id ad7779_id[] = { |
| { |
| .name = "ad7770", |
| .driver_data = (kernel_ulong_t)&ad7770_chip_info, |
| }, |
| { |
| .name = "ad7771", |
| .driver_data = (kernel_ulong_t)&ad7771_chip_info, |
| }, |
| { |
| .name = "ad7779", |
| .driver_data = (kernel_ulong_t)&ad7779_chip_info, |
| }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(spi, ad7779_id); |
| |
| static const struct of_device_id ad7779_of_table[] = { |
| { |
| .compatible = "adi,ad7770", |
| .data = &ad7770_chip_info, |
| }, |
| { |
| .compatible = "adi,ad7771", |
| .data = &ad7771_chip_info, |
| }, |
| { |
| .compatible = "adi,ad7779", |
| .data = &ad7779_chip_info, |
| }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, ad7779_of_table); |
| |
| static struct spi_driver ad7779_driver = { |
| .driver = { |
| .name = "ad7779", |
| .pm = pm_sleep_ptr(&ad7779_pm_ops), |
| .of_match_table = ad7779_of_table, |
| }, |
| .probe = ad7779_probe, |
| .id_table = ad7779_id, |
| }; |
| module_spi_driver(ad7779_driver); |
| |
| MODULE_AUTHOR("Ramona Alexandra Nechita <ramona.nechita@analog.com>"); |
| MODULE_DESCRIPTION("Analog Devices AD7779 ADC"); |
| MODULE_LICENSE("GPL"); |