// SPDX-License-Identifier: GPL-2.0+ /* * Analog Devices LTC2378 ADC series driver * * Copyright (C) 2026 Analog Devices Inc. * Author: Marcelo Schmitt */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LTC2378_TDSDOBUSYL_NS 5 #define LTC2378_TBUSYLH_NS 13 #define LTC2378_TCNV_HIGH_NS 20 #define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */ #define LTC2378_CHANNEL(_sign, _real_bits, _storage_bits) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .differential = _sign, \ .channel = 0, \ .channel2 = _sign ? 1 : 0, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ .scan_index = 0, \ .scan_type = { \ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ IIO_SCAN_FORMAT_UNSIGNED_INT, \ .realbits = _real_bits, \ .storagebits = _storage_bits, \ .shift = _storage_bits - _real_bits, \ .endianness = IIO_BE, \ }, \ } #define LTC2378_DIFF_CHANNEL(_real_bits) \ LTC2378_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16)) #define LTC2378_PSEUDO_DIFF_CHANNEL(_real_bits) \ LTC2378_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16)) #define LTC2378_OFFLOAD_CHANNEL(_sign, _real_bits, _storage_bits) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .differential = _sign, \ .channel = 0, \ .channel2 = _sign ? 1 : 0, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = 0, \ .scan_type = { \ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ IIO_SCAN_FORMAT_UNSIGNED_INT, \ .realbits = _real_bits, \ .storagebits = _storage_bits, \ .shift = 0, \ .endianness = IIO_CPU, \ }, \ } /* * Currently, the available offload hardware + DMA configuration only supports * pushing 32-bit data elements to DMA IIO buffers in CPU endianness. For 16-bit * precision parts, those 32-bit elements (in CPU endianness) contain 2 bytes * with data and 2 bytes always zeroed out. Nevertheless, for the offload use * case, the IIO buffer is configured for 32 storage bits in CPU endianness so * data is correctly aligned in user space despite 2 out of the 4 bytes being * zeros. */ #define LTC2378_OFFLOAD_DIFF_CHANNEL(_real_bits) \ LTC2378_OFFLOAD_CHANNEL(1, (_real_bits), 32) #define LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(_real_bits) \ LTC2378_OFFLOAD_CHANNEL(0, (_real_bits), 32) struct ltc2378_chip_info { const char *name; unsigned int internal_ref_uV; struct u32_fract internal_div; struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */ struct iio_chan_spec offload_chan; unsigned int max_sample_rate_Hz; unsigned int tconv_ns; }; struct ltc2378_state { const struct ltc2378_chip_info *info; struct gpio_desc *cnv_gpio; struct spi_device *spi; struct mutex lock; /* Protect data acquisition cycle */ int ref_uV; struct spi_transfer xfer; struct spi_transfer offload_xfer; struct spi_offload *offload; struct spi_offload_trigger *offload_trigger; struct pwm_waveform cnv_wf; struct spi_message offload_msg; struct spi_offload_trigger_config offload_trigger_config; struct pwm_device *cnv_trigger; unsigned int cnv_Hz; unsigned int sample_freq_range[3]; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. */ struct { union { __be16 sample_buf16_be; __be32 sample_buf32_be; u16 sample_buf16; u32 sample_buf32; } data; aligned_s64 timestamp; } scan __aligned(IIO_DMA_MINALIGN); }; static const struct ltc2378_chip_info ltc2338_18_chip_info = { .name = "ltc2338-18", .internal_ref_uV = 2048000, .internal_div = { .numerator = 5, .denominator = 2 }, .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2364_16_chip_info = { .name = "ltc2364-16", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2364_18_chip_info = { .name = "ltc2364-18", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2367_16_chip_info = { .name = "ltc2367-16", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2367_18_chip_info = { .name = "ltc2367-18", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2368_16_chip_info = { .name = "ltc2368-16", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2368_18_chip_info = { .name = "ltc2368-18", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2369_18_chip_info = { .name = "ltc2369-18", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, .tconv_ns = 412, }; static const struct ltc2378_chip_info ltc2370_16_chip_info = { .name = "ltc2370-16", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 2 * HZ_PER_MHZ, .tconv_ns = 322, }; static const struct ltc2378_chip_info ltc2376_16_chip_info = { .name = "ltc2376-16", .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2376_18_chip_info = { .name = "ltc2376-18", .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2376_20_chip_info = { .name = "ltc2376-20", .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2377_16_chip_info = { .name = "ltc2377-16", .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2377_18_chip_info = { .name = "ltc2377-18", .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2377_20_chip_info = { .name = "ltc2377-20", .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2378_16_chip_info = { .name = "ltc2378-16", .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2378_18_chip_info = { .name = "ltc2378-18", .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2378_20_chip_info = { .name = "ltc2378-20", .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 675, }; static const struct ltc2378_chip_info ltc2379_18_chip_info = { .name = "ltc2379-18", .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, .tconv_ns = 412, }; static const struct ltc2378_chip_info ltc2380_16_chip_info = { .name = "ltc2380-16", .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 2 * HZ_PER_MHZ, .tconv_ns = 322, }; static int ltc2378_convert_and_acquire(struct ltc2378_state *st) { int ret; /* Cause a rising edge of CNV to initiate a new ADC conversion */ gpiod_set_value_cansleep(st->cnv_gpio, 1); fsleep(LTC2378_MAX_DATA_WAIT_US); ret = spi_sync_transfer(st->spi, &st->xfer, 1); gpiod_set_value_cansleep(st->cnv_gpio, 0); return ret; } static irqreturn_t ltc2378_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ltc2378_state *st = iio_priv(indio_dev); int ret; ret = ltc2378_convert_and_acquire(st); if (ret < 0) goto err_out; iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), pf->timestamp); err_out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } static int ltc2378_channel_single_read(const struct iio_chan_spec *chan, struct ltc2378_state *st, int *val) { const struct iio_scan_type *scan_type = &chan->scan_type; u32 sample; int ret; guard(mutex)(&st->lock); ret = ltc2378_convert_and_acquire(st); if (ret) return ret; if (chan->scan_type.endianness == IIO_BE) { if (chan->scan_type.realbits > 16) sample = be32_to_cpu(st->scan.data.sample_buf32_be); else sample = be16_to_cpu(st->scan.data.sample_buf16_be); } else { /* IIO_CPU */ if (chan->scan_type.realbits > 16) sample = st->scan.data.sample_buf32; else sample = st->scan.data.sample_buf16; } sample >>= chan->scan_type.shift; if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT) *val = sign_extend32(sample, scan_type->realbits - 1); else *val = sample; return 0; } static int ltc2378_read_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long mask) { struct ltc2378_state *st = iio_priv(indio_dev); int ret; switch (mask) { case IIO_CHAN_INFO_RAW: { IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); if (IIO_DEV_ACQUIRE_FAILED(claim)) return -EBUSY; ret = ltc2378_channel_single_read(chan, st, val); if (ret) return ret; return IIO_VAL_INT; } case IIO_CHAN_INFO_SCALE: { struct u32_fract fract = st->info->internal_div; *val = st->ref_uV / MILLI; if (fract.numerator && fract.denominator) *val = mult_frac(*val, fract.numerator, fract.denominator); /* * For all LTC2378-like devices, the amount of bits that express * voltage magnitude depend on the polarity / output code format: * - straight binary: All precision/resolution bits are used. * - 2's complement: One of the precision bits is used for sign. */ if (chan->scan_type.format == IIO_SCAN_FORMAT_SIGNED_INT) *val2 = chan->scan_type.realbits - 1; else *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; } case IIO_CHAN_INFO_SAMP_FREQ: *val = st->cnv_Hz; return IIO_VAL_INT; default: return -EINVAL; } } static int ltc2378_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { struct ltc2378_state *st = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: *vals = st->sample_freq_range; *type = IIO_VAL_INT; return IIO_AVAIL_RANGE; default: return -EINVAL; } } /* * SPI offload wiring schema * * +-------------+ +-------------+ * | CNV |<-----+--| GPIO | * | | +--| PWM0 | * | | | | * | | +--| PWM1 | * | | | +-------------+ * | | +->| TRIGGER | * | | | | * | ADC | | SPI | * | | | controller | * | | | | * | SDI |<--------| SDO | * | SDO |-------->| SDI | * | SCLK |<--------| SCLK | * +-------------+ +-------------+ * */ static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz) { struct spi_offload_trigger_config config = st->offload_trigger_config; unsigned int min_read_offset, offload_period_ns; struct pwm_waveform cnv_wf = { }; u64 target = LTC2378_TCNV_HIGH_NS; unsigned int count; u64 offload_offset_ns; int ret; if (freq_Hz == 0) return -EINVAL; if (!in_range(freq_Hz, 1, st->info->max_sample_rate_Hz)) return -ERANGE; /* Configure CNV PWM waveform */ cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz); /* * Ensure CNV high time meets minimum requirement (20ns). The PWM * hardware may round the duty cycle, so iterate until we get at least * the minimum required high time (or reach a try count limit). */ count = 100; do { cnv_wf.duty_length_ns = target; ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); if (ret) return ret; target += 10; /* Increment by PWM duty cycle period */ } while (count-- && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS); /* Check the minimum CNV high time is met */ if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS) return -EDOM; /* * Configure SPI offload PWM trigger. * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL. * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns) * * Use the same period as CNV PWM to avoid timing issues. * Convert back from period to frequency for the SPI offload API. */ offload_period_ns = cnv_wf.period_length_ns; config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns); min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS; offload_offset_ns = min_read_offset; count = 100; do { config.periodic.offset_ns = offload_offset_ns; ret = spi_offload_trigger_validate(st->offload_trigger, &config); if (ret) return ret; offload_offset_ns += 10; } while (count-- && config.periodic.offset_ns < min_read_offset); /* Check the minimum CNV to SCLK delay is met */ if (config.periodic.offset_ns < min_read_offset) return -EDOM; /* Check the PWM periods remain the same */ offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz); if (cnv_wf.period_length_ns != offload_period_ns) return -EDOM; st->offload_trigger_config = config; st->cnv_wf = cnv_wf; st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns); return 0; } static int ltc2378_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct ltc2378_state *st = iio_priv(indio_dev); IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); if (IIO_DEV_ACQUIRE_FAILED(claim)) return -EBUSY; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: return ltc2378_update_conversion_rate(st, val); default: return -EINVAL; } } static const struct iio_info ltc2378_iio_info = { .read_raw = <c2378_read_raw, }; static const struct iio_info ltc2378_offload_iio_info = { .read_raw = <c2378_read_raw, .read_avail = <c2378_read_avail, .write_raw = <c2378_write_raw, }; static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev) { struct ltc2378_state *st = iio_priv(indio_dev); int ret; ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, true); if (ret) return ret; ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &st->offload_trigger_config); if (ret) goto out_pwm_disable; return 0; out_pwm_disable: pwm_disable(st->cnv_trigger); return ret; } static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev) { struct ltc2378_state *st = iio_priv(indio_dev); spi_offload_trigger_disable(st->offload, st->offload_trigger); pwm_disable(st->cnv_trigger); return 0; } static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = { .postenable = <c2378_offload_buffer_postenable, .predisable = <c2378_offload_buffer_predisable, }; static int ltc2378_prepare_offload_message(struct device *dev, struct ltc2378_state *st) { unsigned int resolution = st->info->offload_chan.scan_type.realbits; st->offload_xfer.bits_per_word = resolution; st->offload_xfer.len = spi_bpw_to_bytes(resolution); st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; /* Initialize message with offload */ spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); st->offload_msg.offload = st->offload; return devm_spi_optimize_message(dev, st->spi, &st->offload_msg); } static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev, struct ltc2378_state *st) { struct device *dev = &st->spi->dev; struct dma_chan *rx_dma; indio_dev->setup_ops = <c2378_offload_buffer_ops; st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, SPI_OFFLOAD_TRIGGER_PERIODIC); if (IS_ERR(st->offload_trigger)) return dev_err_probe(dev, PTR_ERR(st->offload_trigger), "failed to get offload trigger\n"); st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); if (IS_ERR(rx_dma)) return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n"); return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, IIO_BUFFER_DIRECTION_IN); } static int ltc2378_pwm_get(struct ltc2378_state *st) { struct device *dev = &st->spi->dev; st->cnv_trigger = devm_pwm_get(dev, NULL); if (IS_ERR(st->cnv_trigger)) return dev_err_probe(dev, PTR_ERR(st->cnv_trigger), "failed to get cnv pwm\n"); /* * Disable the PWM connected to CNV in case it was left running by * something else. */ pwm_disable(st->cnv_trigger); return 0; } static const struct spi_offload_config ltc2378_offload_config = { .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | SPI_OFFLOAD_CAP_RX_STREAM_DMA, }; static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st) { int ret; /* * The internal reference buffer amplifies both the internal reference * and REFIN by a factor of 2. */ ret = devm_regulator_get_enable_read_voltage(dev, "refin"); if (ret == -ENODEV) { /* refin is optional */ st->ref_uV = st->info->internal_ref_uV * 2; return 0; } if (ret < 0) return dev_err_probe(dev, ret, "failed to read refin regulator\n"); st->ref_uV = ret * 2; return 0; } static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st) { int ret; ret = devm_regulator_get_enable_read_voltage(dev, "ref"); if (ret < 0) return dev_err_probe(dev, ret, "failed to read ref regulator\n"); st->ref_uV = ret; return 0; } static int ltc2378_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct ltc2378_state *st; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); st->spi = spi; ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; st->info = spi_get_device_match_data(spi); if (!st->info) return -EINVAL; if (st->info->internal_ref_uV) ret = ltc2378_refin_setup(dev, st); else ret = ltc2378_ref_setup(dev, st); if (ret) return ret; indio_dev->name = st->info->name; indio_dev->modes = INDIO_DIRECT_MODE; st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); if (IS_ERR(st->cnv_gpio)) return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), "failed to get CNV GPIO"); st->offload = devm_spi_offload_get(dev, spi, <c2378_offload_config); ret = PTR_ERR_OR_ZERO(st->offload); /* Fall back to low speed usage when no SPI offload is available. */ if (ret == -ENODEV) { indio_dev->info = <c2378_iio_info; indio_dev->channels = st->info->chan; indio_dev->num_channels = ARRAY_SIZE(st->info->chan); ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, ltc2378_trigger_handler, NULL); if (ret) return ret; } else if (ret) { return dev_err_probe(dev, ret, "failed to get offload\n"); } else { indio_dev->info = <c2378_offload_iio_info; indio_dev->channels = &st->info->offload_chan; indio_dev->num_channels = 1; ret = ltc2378_spi_offload_setup(indio_dev, st); if (ret) return dev_err_probe(dev, ret, "failed to setup SPI offload\n"); ret = ltc2378_pwm_get(st); if (ret) return dev_err_probe(dev, ret, "failed to get PWM\n"); st->sample_freq_range[0] = 1; /* min */ st->sample_freq_range[1] = 1; /* step */ st->sample_freq_range[2] = st->info->max_sample_rate_Hz; /* max */ /* * Start with a slower sampling rate so there is some room for * adjusting the sample averaging and the sampling frequency * without hitting the maximum conversion rate. */ ret = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_Hz >> 4); if (ret) return dev_err_probe(dev, ret, "failed to set offload samp freq\n"); ret = ltc2378_prepare_offload_message(&spi->dev, st); if (ret) return dev_err_probe(dev, ret, "failed to optimize SPI message\n"); /* * Set single-read transfer bits_per_word so the SPI subsystem * rearranges data to CPU endianness, enabling us to reuse * offload_chan specifications for single-shot reads. */ st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits; } st->xfer.rx_buf = &st->scan.data; st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits); return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ltc2378_of_match[] = { { .compatible = "adi,ltc2338-18", .data = <c2338_18_chip_info }, { .compatible = "adi,ltc2364-16", .data = <c2364_16_chip_info }, { .compatible = "adi,ltc2364-18", .data = <c2364_18_chip_info }, { .compatible = "adi,ltc2367-16", .data = <c2367_16_chip_info }, { .compatible = "adi,ltc2367-18", .data = <c2367_18_chip_info }, { .compatible = "adi,ltc2368-16", .data = <c2368_16_chip_info }, { .compatible = "adi,ltc2368-18", .data = <c2368_18_chip_info }, { .compatible = "adi,ltc2369-18", .data = <c2369_18_chip_info }, { .compatible = "adi,ltc2370-16", .data = <c2370_16_chip_info }, { .compatible = "adi,ltc2376-16", .data = <c2376_16_chip_info }, { .compatible = "adi,ltc2376-18", .data = <c2376_18_chip_info }, { .compatible = "adi,ltc2376-20", .data = <c2376_20_chip_info }, { .compatible = "adi,ltc2377-16", .data = <c2377_16_chip_info }, { .compatible = "adi,ltc2377-18", .data = <c2377_18_chip_info }, { .compatible = "adi,ltc2377-20", .data = <c2377_20_chip_info }, { .compatible = "adi,ltc2378-16", .data = <c2378_16_chip_info }, { .compatible = "adi,ltc2378-18", .data = <c2378_18_chip_info }, { .compatible = "adi,ltc2378-20", .data = <c2378_20_chip_info }, { .compatible = "adi,ltc2379-18", .data = <c2379_18_chip_info }, { .compatible = "adi,ltc2380-16", .data = <c2380_16_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ltc2378_of_match); static const struct spi_device_id ltc2378_spi_id[] = { { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)<c2338_18_chip_info }, { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)<c2364_16_chip_info }, { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)<c2364_18_chip_info }, { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)<c2367_16_chip_info }, { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)<c2367_18_chip_info }, { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)<c2368_16_chip_info }, { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)<c2368_18_chip_info }, { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)<c2369_18_chip_info }, { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)<c2370_16_chip_info }, { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)<c2376_16_chip_info }, { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)<c2376_18_chip_info }, { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)<c2376_20_chip_info }, { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)<c2377_16_chip_info }, { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)<c2377_18_chip_info }, { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)<c2377_20_chip_info }, { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)<c2378_16_chip_info }, { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)<c2378_18_chip_info }, { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)<c2378_20_chip_info }, { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)<c2379_18_chip_info }, { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)<c2380_16_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ltc2378_spi_id); static struct spi_driver ltc2378_driver = { .driver = { .name = "ltc2378", .of_match_table = ltc2378_of_match }, .probe = ltc2378_probe, .id_table = ltc2378_spi_id, }; module_spi_driver(ltc2378_driver); MODULE_AUTHOR("Marcelo Schmitt "); MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); MODULE_IMPORT_NS("SPI_OFFLOAD");