| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs |
| * |
| * Copyright (c) 2020 AVL DiTEST GmbH |
| * Tomislav Denis <tomislav.denis@avl.com> |
| * |
| * Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf |
| */ |
| |
| #include <linux/bitfield.h> |
| #include <linux/clk.h> |
| #include <linux/delay.h> |
| #include <linux/module.h> |
| |
| #include <linux/iio/buffer.h> |
| #include <linux/iio/iio.h> |
| #include <linux/iio/sysfs.h> |
| #include <linux/iio/trigger.h> |
| #include <linux/iio/trigger_consumer.h> |
| #include <linux/iio/triggered_buffer.h> |
| |
| #include <linux/regulator/consumer.h> |
| #include <linux/spi/spi.h> |
| |
| #include <asm/unaligned.h> |
| |
| /* Commands */ |
| #define ADS131E08_CMD_RESET 0x06 |
| #define ADS131E08_CMD_START 0x08 |
| #define ADS131E08_CMD_STOP 0x0A |
| #define ADS131E08_CMD_OFFSETCAL 0x1A |
| #define ADS131E08_CMD_SDATAC 0x11 |
| #define ADS131E08_CMD_RDATA 0x12 |
| #define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0))) |
| #define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0))) |
| |
| /* Registers */ |
| #define ADS131E08_ADR_CFG1R 0x01 |
| #define ADS131E08_ADR_CFG3R 0x03 |
| #define ADS131E08_ADR_CH0R 0x05 |
| |
| /* Configuration register 1 */ |
| #define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0) |
| |
| /* Configuration register 3 */ |
| #define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7) |
| #define ADS131E08_CFG3R_VREF_4V_MASK BIT(5) |
| |
| /* Channel settings register */ |
| #define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4) |
| #define ADS131E08_CHR_MUX_MASK GENMASK(2, 0) |
| #define ADS131E08_CHR_PWD_MASK BIT(7) |
| |
| /* ADC misc */ |
| #define ADS131E08_DEFAULT_DATA_RATE 1 |
| #define ADS131E08_DEFAULT_PGA_GAIN 1 |
| #define ADS131E08_DEFAULT_MUX 0 |
| |
| #define ADS131E08_VREF_2V4_mV 2400 |
| #define ADS131E08_VREF_4V_mV 4000 |
| |
| #define ADS131E08_WAIT_RESET_CYCLES 18 |
| #define ADS131E08_WAIT_SDECODE_CYCLES 4 |
| #define ADS131E08_WAIT_OFFSETCAL_MS 153 |
| #define ADS131E08_MAX_SETTLING_TIME_MS 6 |
| |
| #define ADS131E08_NUM_STATUS_BYTES 3 |
| #define ADS131E08_NUM_DATA_BYTES_MAX 24 |
| #define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3) |
| #define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8) |
| #define ADS131E08_NUM_STORAGE_BYTES 4 |
| |
| enum ads131e08_ids { |
| ads131e04, |
| ads131e06, |
| ads131e08, |
| }; |
| |
| struct ads131e08_info { |
| unsigned int max_channels; |
| const char *name; |
| }; |
| |
| struct ads131e08_channel_config { |
| unsigned int pga_gain; |
| unsigned int mux; |
| }; |
| |
| struct ads131e08_state { |
| const struct ads131e08_info *info; |
| struct spi_device *spi; |
| struct iio_trigger *trig; |
| struct clk *adc_clk; |
| struct regulator *vref_reg; |
| struct ads131e08_channel_config *channel_config; |
| unsigned int data_rate; |
| unsigned int vref_mv; |
| unsigned int sdecode_delay_us; |
| unsigned int reset_delay_us; |
| unsigned int readback_len; |
| struct completion completion; |
| struct { |
| u8 data[ADS131E08_NUM_DATA_BYTES_MAX]; |
| s64 ts __aligned(8); |
| } tmp_buf; |
| |
| u8 tx_buf[3] __aligned(IIO_DMA_MINALIGN); |
| /* |
| * Add extra one padding byte to be able to access the last channel |
| * value using u32 pointer |
| */ |
| u8 rx_buf[ADS131E08_NUM_STATUS_BYTES + |
| ADS131E08_NUM_DATA_BYTES_MAX + 1]; |
| }; |
| |
| static const struct ads131e08_info ads131e08_info_tbl[] = { |
| [ads131e04] = { |
| .max_channels = 4, |
| .name = "ads131e04", |
| }, |
| [ads131e06] = { |
| .max_channels = 6, |
| .name = "ads131e06", |
| }, |
| [ads131e08] = { |
| .max_channels = 8, |
| .name = "ads131e08", |
| }, |
| }; |
| |
| struct ads131e08_data_rate_desc { |
| unsigned int rate; /* data rate in kSPS */ |
| u8 reg; /* reg value */ |
| }; |
| |
| static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = { |
| { .rate = 64, .reg = 0x00 }, |
| { .rate = 32, .reg = 0x01 }, |
| { .rate = 16, .reg = 0x02 }, |
| { .rate = 8, .reg = 0x03 }, |
| { .rate = 4, .reg = 0x04 }, |
| { .rate = 2, .reg = 0x05 }, |
| { .rate = 1, .reg = 0x06 }, |
| }; |
| |
| struct ads131e08_pga_gain_desc { |
| unsigned int gain; /* PGA gain value */ |
| u8 reg; /* field value */ |
| }; |
| |
| static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = { |
| { .gain = 1, .reg = 0x01 }, |
| { .gain = 2, .reg = 0x02 }, |
| { .gain = 4, .reg = 0x04 }, |
| { .gain = 8, .reg = 0x05 }, |
| { .gain = 12, .reg = 0x06 }, |
| }; |
| |
| static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 }; |
| |
| static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd) |
| { |
| int ret; |
| |
| ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0); |
| if (ret) |
| dev_err(&st->spi->dev, "Exec cmd(%02x) failed\n", cmd); |
| |
| return ret; |
| } |
| |
| static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg) |
| { |
| int ret; |
| struct spi_transfer transfer[] = { |
| { |
| .tx_buf = &st->tx_buf, |
| .len = 2, |
| .delay = { |
| .value = st->sdecode_delay_us, |
| .unit = SPI_DELAY_UNIT_USECS, |
| }, |
| }, { |
| .rx_buf = &st->rx_buf, |
| .len = 1, |
| }, |
| }; |
| |
| st->tx_buf[0] = ADS131E08_CMD_RREG(reg); |
| st->tx_buf[1] = 0; |
| |
| ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); |
| if (ret) { |
| dev_err(&st->spi->dev, "Read register failed\n"); |
| return ret; |
| } |
| |
| return st->rx_buf[0]; |
| } |
| |
| static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value) |
| { |
| int ret; |
| struct spi_transfer transfer[] = { |
| { |
| .tx_buf = &st->tx_buf, |
| .len = 3, |
| .delay = { |
| .value = st->sdecode_delay_us, |
| .unit = SPI_DELAY_UNIT_USECS, |
| }, |
| } |
| }; |
| |
| st->tx_buf[0] = ADS131E08_CMD_WREG(reg); |
| st->tx_buf[1] = 0; |
| st->tx_buf[2] = value; |
| |
| ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); |
| if (ret) |
| dev_err(&st->spi->dev, "Write register failed\n"); |
| |
| return ret; |
| } |
| |
| static int ads131e08_read_data(struct ads131e08_state *st, int rx_len) |
| { |
| int ret; |
| struct spi_transfer transfer[] = { |
| { |
| .tx_buf = &st->tx_buf, |
| .len = 1, |
| }, { |
| .rx_buf = &st->rx_buf, |
| .len = rx_len, |
| }, |
| }; |
| |
| st->tx_buf[0] = ADS131E08_CMD_RDATA; |
| |
| ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); |
| if (ret) |
| dev_err(&st->spi->dev, "Read data failed\n"); |
| |
| return ret; |
| } |
| |
| static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate) |
| { |
| int i, reg, ret; |
| |
| for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); i++) { |
| if (ads131e08_data_rate_tbl[i].rate == data_rate) |
| break; |
| } |
| |
| if (i == ARRAY_SIZE(ads131e08_data_rate_tbl)) { |
| dev_err(&st->spi->dev, "invalid data rate value\n"); |
| return -EINVAL; |
| } |
| |
| reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R); |
| if (reg < 0) |
| return reg; |
| |
| reg &= ~ADS131E08_CFG1R_DR_MASK; |
| reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK, |
| ads131e08_data_rate_tbl[i].reg); |
| |
| ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg); |
| if (ret) |
| return ret; |
| |
| st->data_rate = data_rate; |
| st->readback_len = ADS131E08_NUM_STATUS_BYTES + |
| ADS131E08_NUM_DATA_BYTES(st->data_rate) * |
| st->info->max_channels; |
| |
| return 0; |
| } |
| |
| static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st, |
| unsigned int pga_gain) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(ads131e08_pga_gain_tbl); i++) { |
| if (ads131e08_pga_gain_tbl[i].gain == pga_gain) |
| break; |
| } |
| |
| if (i == ARRAY_SIZE(ads131e08_pga_gain_tbl)) { |
| dev_err(&st->spi->dev, "invalid PGA gain value\n"); |
| return -EINVAL; |
| } |
| |
| return ads131e08_pga_gain_tbl[i].reg; |
| } |
| |
| static int ads131e08_set_pga_gain(struct ads131e08_state *st, |
| unsigned int channel, unsigned int pga_gain) |
| { |
| int field_value, reg; |
| |
| field_value = ads131e08_pga_gain_to_field_value(st, pga_gain); |
| if (field_value < 0) |
| return field_value; |
| |
| reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); |
| if (reg < 0) |
| return reg; |
| |
| reg &= ~ADS131E08_CHR_GAIN_MASK; |
| reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value); |
| |
| return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); |
| } |
| |
| static int ads131e08_validate_channel_mux(struct ads131e08_state *st, |
| unsigned int mux) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(ads131e08_valid_channel_mux_values); i++) { |
| if (ads131e08_valid_channel_mux_values[i] == mux) |
| break; |
| } |
| |
| if (i == ARRAY_SIZE(ads131e08_valid_channel_mux_values)) { |
| dev_err(&st->spi->dev, "invalid channel mux value\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int ads131e08_set_channel_mux(struct ads131e08_state *st, |
| unsigned int channel, unsigned int mux) |
| { |
| int reg; |
| |
| reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); |
| if (reg < 0) |
| return reg; |
| |
| reg &= ~ADS131E08_CHR_MUX_MASK; |
| reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux); |
| |
| return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); |
| } |
| |
| static int ads131e08_power_down_channel(struct ads131e08_state *st, |
| unsigned int channel, bool value) |
| { |
| int reg; |
| |
| reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); |
| if (reg < 0) |
| return reg; |
| |
| reg &= ~ADS131E08_CHR_PWD_MASK; |
| reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value); |
| |
| return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); |
| } |
| |
| static int ads131e08_config_reference_voltage(struct ads131e08_state *st) |
| { |
| int reg; |
| |
| reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R); |
| if (reg < 0) |
| return reg; |
| |
| reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK; |
| if (!st->vref_reg) { |
| reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1); |
| reg &= ~ADS131E08_CFG3R_VREF_4V_MASK; |
| reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK, |
| st->vref_mv == ADS131E08_VREF_4V_mV); |
| } |
| |
| return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg); |
| } |
| |
| static int ads131e08_initial_config(struct iio_dev *indio_dev) |
| { |
| const struct iio_chan_spec *channel = indio_dev->channels; |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| unsigned long active_channels = 0; |
| int ret, i; |
| |
| ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET); |
| if (ret) |
| return ret; |
| |
| udelay(st->reset_delay_us); |
| |
| /* Disable read data in continuous mode (enabled by default) */ |
| ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC); |
| if (ret) |
| return ret; |
| |
| ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE); |
| if (ret) |
| return ret; |
| |
| ret = ads131e08_config_reference_voltage(st); |
| if (ret) |
| return ret; |
| |
| for (i = 0; i < indio_dev->num_channels; i++) { |
| ret = ads131e08_set_pga_gain(st, channel->channel, |
| st->channel_config[i].pga_gain); |
| if (ret) |
| return ret; |
| |
| ret = ads131e08_set_channel_mux(st, channel->channel, |
| st->channel_config[i].mux); |
| if (ret) |
| return ret; |
| |
| active_channels |= BIT(channel->channel); |
| channel++; |
| } |
| |
| /* Power down unused channels */ |
| for_each_clear_bit(i, &active_channels, st->info->max_channels) { |
| ret = ads131e08_power_down_channel(st, i, true); |
| if (ret) |
| return ret; |
| } |
| |
| /* Request channel offset calibration */ |
| ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL); |
| if (ret) |
| return ret; |
| |
| /* |
| * Channel offset calibration is triggered with the first START |
| * command. Since calibration takes more time than settling operation, |
| * this causes timeout error when command START is sent first |
| * time (e.g. first call of the ads131e08_read_direct method). |
| * To avoid this problem offset calibration is triggered here. |
| */ |
| ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); |
| if (ret) |
| return ret; |
| |
| msleep(ADS131E08_WAIT_OFFSETCAL_MS); |
| |
| return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); |
| } |
| |
| static int ads131e08_pool_data(struct ads131e08_state *st) |
| { |
| unsigned long timeout; |
| int ret; |
| |
| reinit_completion(&st->completion); |
| |
| ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); |
| if (ret) |
| return ret; |
| |
| timeout = msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS); |
| ret = wait_for_completion_timeout(&st->completion, timeout); |
| if (!ret) |
| return -ETIMEDOUT; |
| |
| ret = ads131e08_read_data(st, st->readback_len); |
| if (ret) |
| return ret; |
| |
| return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); |
| } |
| |
| static int ads131e08_read_direct(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *channel, int *value) |
| { |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| u8 num_bits, *src; |
| int ret; |
| |
| ret = ads131e08_pool_data(st); |
| if (ret) |
| return ret; |
| |
| src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + |
| channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate); |
| |
| num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate); |
| *value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1); |
| |
| return 0; |
| } |
| |
| static int ads131e08_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *channel, int *value, |
| int *value2, long mask) |
| { |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_RAW: |
| ret = iio_device_claim_direct_mode(indio_dev); |
| if (ret) |
| return ret; |
| |
| ret = ads131e08_read_direct(indio_dev, channel, value); |
| iio_device_release_direct_mode(indio_dev); |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_INT; |
| |
| case IIO_CHAN_INFO_SCALE: |
| if (st->vref_reg) { |
| ret = regulator_get_voltage(st->vref_reg); |
| if (ret < 0) |
| return ret; |
| |
| *value = ret / 1000; |
| } else { |
| *value = st->vref_mv; |
| } |
| |
| *value /= st->channel_config[channel->address].pga_gain; |
| *value2 = ADS131E08_NUM_DATA_BITS(st->data_rate) - 1; |
| |
| return IIO_VAL_FRACTIONAL_LOG2; |
| |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| *value = st->data_rate; |
| |
| return IIO_VAL_INT; |
| |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int ads131e08_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *channel, int value, |
| int value2, long mask) |
| { |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| int ret; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| ret = iio_device_claim_direct_mode(indio_dev); |
| if (ret) |
| return ret; |
| |
| ret = ads131e08_set_data_rate(st, value); |
| iio_device_release_direct_mode(indio_dev); |
| return ret; |
| |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64"); |
| |
| static struct attribute *ads131e08_attributes[] = { |
| &iio_const_attr_sampling_frequency_available.dev_attr.attr, |
| NULL |
| }; |
| |
| static const struct attribute_group ads131e08_attribute_group = { |
| .attrs = ads131e08_attributes, |
| }; |
| |
| static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev, |
| unsigned int reg, unsigned int writeval, unsigned int *readval) |
| { |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| |
| if (readval) { |
| int ret = ads131e08_read_reg(st, reg); |
| *readval = ret; |
| return ret; |
| } |
| |
| return ads131e08_write_reg(st, reg, writeval); |
| } |
| |
| static const struct iio_info ads131e08_iio_info = { |
| .read_raw = ads131e08_read_raw, |
| .write_raw = ads131e08_write_raw, |
| .attrs = &ads131e08_attribute_group, |
| .debugfs_reg_access = &ads131e08_debugfs_reg_access, |
| }; |
| |
| static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state) |
| { |
| struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP; |
| |
| return ads131e08_exec_cmd(st, cmd); |
| } |
| |
| static const struct iio_trigger_ops ads131e08_trigger_ops = { |
| .set_trigger_state = &ads131e08_set_trigger_state, |
| .validate_device = &iio_trigger_validate_own_device, |
| }; |
| |
| static irqreturn_t ads131e08_trigger_handler(int irq, void *private) |
| { |
| struct iio_poll_func *pf = private; |
| struct iio_dev *indio_dev = pf->indio_dev; |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| unsigned int chn, i = 0; |
| u8 *src, *dest; |
| int ret; |
| |
| /* |
| * The number of data bits per channel depends on the data rate. |
| * For 32 and 64 ksps data rates, number of data bits per channel |
| * is 16. This case is not compliant with used (fixed) scan element |
| * type (be:s24/32>>8). So we use a little tweak to pack properly |
| * 16 bits of data into the buffer. |
| */ |
| unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate); |
| u8 tweek_offset = num_bytes == 2 ? 1 : 0; |
| |
| if (iio_trigger_using_own(indio_dev)) |
| ret = ads131e08_read_data(st, st->readback_len); |
| else |
| ret = ads131e08_pool_data(st); |
| |
| if (ret) |
| goto out; |
| |
| for_each_set_bit(chn, indio_dev->active_scan_mask, indio_dev->masklength) { |
| src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes; |
| dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES; |
| |
| /* |
| * Tweek offset is 0: |
| * +---+---+---+---+ |
| * |D0 |D1 |D2 | X | (3 data bytes) |
| * +---+---+---+---+ |
| * a+0 a+1 a+2 a+3 |
| * |
| * Tweek offset is 1: |
| * +---+---+---+---+ |
| * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes) |
| * +---+---+---+---+ |
| * a+0 a+1 a+2 a+3 |
| */ |
| memcpy(dest + tweek_offset, src, num_bytes); |
| |
| /* |
| * Data conversion from 16 bits of data to 24 bits of data |
| * is done by sign extension (properly filling padding byte). |
| */ |
| if (tweek_offset) |
| *dest = *src & BIT(7) ? 0xff : 0x00; |
| |
| i++; |
| } |
| |
| iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data, |
| iio_get_time_ns(indio_dev)); |
| |
| out: |
| iio_trigger_notify_done(indio_dev->trig); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t ads131e08_interrupt(int irq, void *private) |
| { |
| struct iio_dev *indio_dev = private; |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| |
| if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) |
| iio_trigger_poll(st->trig); |
| else |
| complete(&st->completion); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int ads131e08_alloc_channels(struct iio_dev *indio_dev) |
| { |
| struct ads131e08_state *st = iio_priv(indio_dev); |
| struct ads131e08_channel_config *channel_config; |
| struct device *dev = &st->spi->dev; |
| struct iio_chan_spec *channels; |
| struct fwnode_handle *node; |
| unsigned int channel, tmp; |
| int num_channels, i, ret; |
| |
| ret = device_property_read_u32(dev, "ti,vref-internal", &tmp); |
| if (ret) |
| tmp = 0; |
| |
| switch (tmp) { |
| case 0: |
| st->vref_mv = ADS131E08_VREF_2V4_mV; |
| break; |
| case 1: |
| st->vref_mv = ADS131E08_VREF_4V_mV; |
| break; |
| default: |
| dev_err(&st->spi->dev, "invalid internal voltage reference\n"); |
| return -EINVAL; |
| } |
| |
| num_channels = device_get_child_node_count(dev); |
| if (num_channels == 0) { |
| dev_err(&st->spi->dev, "no channel children\n"); |
| return -ENODEV; |
| } |
| |
| if (num_channels > st->info->max_channels) { |
| dev_err(&st->spi->dev, "num of channel children out of range\n"); |
| return -EINVAL; |
| } |
| |
| channels = devm_kcalloc(&st->spi->dev, num_channels, |
| sizeof(*channels), GFP_KERNEL); |
| if (!channels) |
| return -ENOMEM; |
| |
| channel_config = devm_kcalloc(&st->spi->dev, num_channels, |
| sizeof(*channel_config), GFP_KERNEL); |
| if (!channel_config) |
| return -ENOMEM; |
| |
| i = 0; |
| device_for_each_child_node(dev, node) { |
| ret = fwnode_property_read_u32(node, "reg", &channel); |
| if (ret) |
| goto err_child_out; |
| |
| ret = fwnode_property_read_u32(node, "ti,gain", &tmp); |
| if (ret) { |
| channel_config[i].pga_gain = ADS131E08_DEFAULT_PGA_GAIN; |
| } else { |
| ret = ads131e08_pga_gain_to_field_value(st, tmp); |
| if (ret < 0) |
| goto err_child_out; |
| |
| channel_config[i].pga_gain = tmp; |
| } |
| |
| ret = fwnode_property_read_u32(node, "ti,mux", &tmp); |
| if (ret) { |
| channel_config[i].mux = ADS131E08_DEFAULT_MUX; |
| } else { |
| ret = ads131e08_validate_channel_mux(st, tmp); |
| if (ret) |
| goto err_child_out; |
| |
| channel_config[i].mux = tmp; |
| } |
| |
| channels[i].type = IIO_VOLTAGE; |
| channels[i].indexed = 1; |
| channels[i].channel = channel; |
| channels[i].address = i; |
| channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
| BIT(IIO_CHAN_INFO_SCALE); |
| channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); |
| channels[i].scan_index = channel; |
| channels[i].scan_type.sign = 's'; |
| channels[i].scan_type.realbits = 24; |
| channels[i].scan_type.storagebits = 32; |
| channels[i].scan_type.shift = 8; |
| channels[i].scan_type.endianness = IIO_BE; |
| i++; |
| } |
| |
| indio_dev->channels = channels; |
| indio_dev->num_channels = num_channels; |
| st->channel_config = channel_config; |
| |
| return 0; |
| |
| err_child_out: |
| fwnode_handle_put(node); |
| return ret; |
| } |
| |
| static void ads131e08_regulator_disable(void *data) |
| { |
| struct ads131e08_state *st = data; |
| |
| regulator_disable(st->vref_reg); |
| } |
| |
| static void ads131e08_clk_disable(void *data) |
| { |
| struct ads131e08_state *st = data; |
| |
| clk_disable_unprepare(st->adc_clk); |
| } |
| |
| static int ads131e08_probe(struct spi_device *spi) |
| { |
| const struct ads131e08_info *info; |
| struct ads131e08_state *st; |
| struct iio_dev *indio_dev; |
| unsigned long adc_clk_hz; |
| unsigned long adc_clk_ns; |
| int ret; |
| |
| info = device_get_match_data(&spi->dev); |
| if (!info) { |
| dev_err(&spi->dev, "failed to get match data\n"); |
| return -ENODEV; |
| } |
| |
| indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
| if (!indio_dev) { |
| dev_err(&spi->dev, "failed to allocate IIO device\n"); |
| return -ENOMEM; |
| } |
| |
| st = iio_priv(indio_dev); |
| st->info = info; |
| st->spi = spi; |
| |
| ret = ads131e08_alloc_channels(indio_dev); |
| if (ret) |
| return ret; |
| |
| indio_dev->name = st->info->name; |
| indio_dev->info = &ads131e08_iio_info; |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| |
| init_completion(&st->completion); |
| |
| if (spi->irq) { |
| ret = devm_request_irq(&spi->dev, spi->irq, |
| ads131e08_interrupt, |
| IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| spi->dev.driver->name, indio_dev); |
| if (ret) |
| return dev_err_probe(&spi->dev, ret, |
| "request irq failed\n"); |
| } else { |
| dev_err(&spi->dev, "data ready IRQ missing\n"); |
| return -ENODEV; |
| } |
| |
| st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", |
| indio_dev->name, iio_device_id(indio_dev)); |
| if (!st->trig) { |
| dev_err(&spi->dev, "failed to allocate IIO trigger\n"); |
| return -ENOMEM; |
| } |
| |
| st->trig->ops = &ads131e08_trigger_ops; |
| st->trig->dev.parent = &spi->dev; |
| iio_trigger_set_drvdata(st->trig, indio_dev); |
| ret = devm_iio_trigger_register(&spi->dev, st->trig); |
| if (ret) { |
| dev_err(&spi->dev, "failed to register IIO trigger\n"); |
| return -ENOMEM; |
| } |
| |
| indio_dev->trig = iio_trigger_get(st->trig); |
| |
| ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, |
| NULL, &ads131e08_trigger_handler, NULL); |
| if (ret) { |
| dev_err(&spi->dev, "failed to setup IIO buffer\n"); |
| return ret; |
| } |
| |
| st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); |
| if (!IS_ERR(st->vref_reg)) { |
| ret = regulator_enable(st->vref_reg); |
| if (ret) { |
| dev_err(&spi->dev, |
| "failed to enable external vref supply\n"); |
| return ret; |
| } |
| |
| ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st); |
| if (ret) |
| return ret; |
| } else { |
| if (PTR_ERR(st->vref_reg) != -ENODEV) |
| return PTR_ERR(st->vref_reg); |
| |
| st->vref_reg = NULL; |
| } |
| |
| st->adc_clk = devm_clk_get(&spi->dev, "adc-clk"); |
| if (IS_ERR(st->adc_clk)) |
| return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk), |
| "failed to get the ADC clock\n"); |
| |
| ret = clk_prepare_enable(st->adc_clk); |
| if (ret) { |
| dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n"); |
| return ret; |
| } |
| |
| ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st); |
| if (ret) |
| return ret; |
| |
| adc_clk_hz = clk_get_rate(st->adc_clk); |
| if (!adc_clk_hz) { |
| dev_err(&spi->dev, "failed to get the ADC clock rate\n"); |
| return -EINVAL; |
| } |
| |
| adc_clk_ns = NSEC_PER_SEC / adc_clk_hz; |
| st->sdecode_delay_us = DIV_ROUND_UP( |
| ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, NSEC_PER_USEC); |
| st->reset_delay_us = DIV_ROUND_UP( |
| ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, NSEC_PER_USEC); |
| |
| ret = ads131e08_initial_config(indio_dev); |
| if (ret) { |
| dev_err(&spi->dev, "initial configuration failed\n"); |
| return ret; |
| } |
| |
| return devm_iio_device_register(&spi->dev, indio_dev); |
| } |
| |
| static const struct of_device_id ads131e08_of_match[] = { |
| { .compatible = "ti,ads131e04", |
| .data = &ads131e08_info_tbl[ads131e04], }, |
| { .compatible = "ti,ads131e06", |
| .data = &ads131e08_info_tbl[ads131e06], }, |
| { .compatible = "ti,ads131e08", |
| .data = &ads131e08_info_tbl[ads131e08], }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, ads131e08_of_match); |
| |
| static struct spi_driver ads131e08_driver = { |
| .driver = { |
| .name = "ads131e08", |
| .of_match_table = ads131e08_of_match, |
| }, |
| .probe = ads131e08_probe, |
| }; |
| module_spi_driver(ads131e08_driver); |
| |
| MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>"); |
| MODULE_DESCRIPTION("Driver for ADS131E0x ADC family"); |
| MODULE_LICENSE("GPL v2"); |