diff options
Diffstat (limited to 'drivers/iio/adc/ad_sigma_delta.c')
-rw-r--r-- | drivers/iio/adc/ad_sigma_delta.c | 263 |
1 files changed, 206 insertions, 57 deletions
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 7e2192870743..d5d81581ab34 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -23,14 +23,17 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/adc/ad_sigma_delta.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define AD_SD_COMM_CHAN_MASK 0x3 #define AD_SD_REG_COMM 0x00 +#define AD_SD_REG_STATUS 0x00 #define AD_SD_REG_DATA 0x03 +#define AD_SD_REG_STATUS_RDY 0x80 + /** * ad_sd_set_comm() - Set communications register * @@ -43,7 +46,7 @@ void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm) * to select the channel */ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; } -EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_write_reg() - Write a register @@ -95,7 +98,7 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, "IIO_AD_SIGMA_DELTA"); static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, uint8_t *val) @@ -109,7 +112,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, }, { .rx_buf = val, .len = size, - .cs_change = sigma_delta->bus_locked, + .cs_change = sigma_delta->keep_cs_asserted, }, }; struct spi_message m; @@ -172,19 +175,18 @@ int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, out: return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_reset() - Reset the serial interface * * @sigma_delta: The sigma delta device - * @reset_length: Number of SCLKs with DIN = 1 * * Returns 0 on success, an error code otherwise. **/ -int ad_sd_reset(struct ad_sigma_delta *sigma_delta, - unsigned int reset_length) +int ad_sd_reset(struct ad_sigma_delta *sigma_delta) { + unsigned int reset_length = sigma_delta->info->num_resetclks; uint8_t *buf; unsigned int size; int ret; @@ -200,33 +202,136 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_reset, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_reset, "IIO_AD_SIGMA_DELTA"); + +static bool ad_sd_disable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + /* It's already off, return false to indicate nothing was changed */ + if (sigma_delta->irq_dis) + return false; + + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->irq_line); + return true; +} + +static void ad_sd_enable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->irq_line); +} + +#define AD_SD_CLEAR_DATA_BUFLEN 9 + +/* Called with `sigma_delta->bus_locked == true` only. */ +static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta) +{ + bool pending_event; + unsigned int data_read_len = BITS_TO_BYTES(sigma_delta->info->num_resetclks); + u8 *data; + struct spi_transfer t[] = { + { + .len = 1, + }, { + .len = data_read_len, + } + }; + struct spi_message m; + int ret; + + /* + * Read R̅D̅Y̅ pin (if possible) or status register to check if there is an + * old event. + */ + if (sigma_delta->rdy_gpiod) { + pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); + } else { + unsigned int status_reg; + + ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); + if (ret) + return ret; + + pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } + + if (!pending_event) + return 0; + + /* + * In general the size of the data register is unknown. It varies from + * device to device, might be one byte longer if CONTROL.DATA_STATUS is + * set and even varies on some devices depending on which input is + * selected. So send one byte to start reading the data register and + * then just clock for some bytes with DIN (aka MOSI) high to not + * confuse the register access state machine after the data register was + * completely read. Note however that the sequence length must be + * shorter than the reset procedure. + */ + + data = kzalloc(data_read_len + 1, GFP_KERNEL); + if (!data) + return -ENOMEM; + + spi_message_init(&m); + if (sigma_delta->info->has_registers) { + unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA; + + data[0] = data_reg << sigma_delta->info->addr_shift; + data[0] |= sigma_delta->info->read_mask; + data[0] |= sigma_delta->comm; + t[0].tx_buf = data; + spi_message_add_tail(&t[0], &m); + } + + /* + * The first transferred byte is part of the real data register, + * so this doesn't need to be 0xff. In the remaining + * `data_read_len - 1` bytes are less than $num_resetclks ones. + */ + t[1].tx_buf = data + 1; + data[1] = 0x00; + memset(data + 2, 0xff, data_read_len - 1); + spi_message_add_tail(&t[1], &m); + + ret = spi_sync_locked(sigma_delta->spi, &m); + + kfree(data); + + return ret; +} int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { int ret; - unsigned long timeout; + unsigned long time_left; ret = ad_sigma_delta_set_channel(sigma_delta, channel); if (ret) return ret; - spi_bus_lock(sigma_delta->spi->master); + spi_bus_lock(sigma_delta->spi->controller); sigma_delta->bus_locked = true; sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out; + ret = ad_sigma_delta_set_mode(sigma_delta, mode); if (ret < 0) goto out; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); - timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); - if (timeout == 0) { - sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->spi->irq); + ad_sd_enable_irq(sigma_delta); + time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); + if (time_left == 0) { + ad_sd_disable_irq(sigma_delta); ret = -EIO; } else { ret = 0; @@ -235,11 +340,11 @@ out: sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); sigma_delta->bus_locked = false; - spi_bus_unlock(sigma_delta->spi->master); + spi_bus_unlock(sigma_delta->spi->controller); return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_calibrate_all() - Performs channel calibration @@ -263,7 +368,7 @@ int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, return 0; } -EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, "IIO_AD_SIGMA_DELTA"); /** * ad_sigma_delta_single_conversion() - Performs a single data conversion @@ -287,15 +392,18 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, ad_sigma_delta_set_channel(sigma_delta, chan->address); - spi_bus_lock(sigma_delta->spi->master); + spi_bus_lock(sigma_delta->spi->controller); sigma_delta->bus_locked = true; sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out_unlock; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + ad_sd_enable_irq(sigma_delta); ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); @@ -314,15 +422,15 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, &raw_sample); out: - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->spi->irq); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); - sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, chan->address); + +out_unlock: + sigma_delta->keep_cs_asserted = false; sigma_delta->bus_locked = false; - spi_bus_unlock(sigma_delta->spi->master); + spi_bus_unlock(sigma_delta->spi->controller); iio_device_release_direct_mode(indio_dev); if (ret) @@ -338,7 +446,7 @@ out: return IIO_VAL_INT; } -EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, "IIO_AD_SIGMA_DELTA"); static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) { @@ -350,7 +458,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) if (sigma_delta->num_slots == 1) { channel = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); ret = ad_sigma_delta_set_channel(sigma_delta, indio_dev->channels[channel].address); if (ret) @@ -363,7 +471,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) * implementation is mandatory. */ slot = 0; - for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { sigma_delta->slots[slot] = indio_dev->channels[i].address; slot++; } @@ -387,21 +495,24 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) sigma_delta->samples_buf = samples_buf; - spi_bus_lock(sigma_delta->spi->master); + spi_bus_lock(sigma_delta->spi->controller); sigma_delta->bus_locked = true; sigma_delta->keep_cs_asserted = true; + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto err_unlock; + ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS); if (ret) goto err_unlock; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + ad_sd_enable_irq(sigma_delta); return 0; err_unlock: - spi_bus_unlock(sigma_delta->spi->master); + spi_bus_unlock(sigma_delta->spi->controller); return ret; } @@ -413,10 +524,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) reinit_completion(&sigma_delta->completion); wait_for_completion_timeout(&sigma_delta->completion, HZ); - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->spi->irq); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); @@ -426,7 +534,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) ad_sigma_delta_disable_all(sigma_delta); sigma_delta->bus_locked = false; - return spi_bus_unlock(sigma_delta->spi->master); + return spi_bus_unlock(sigma_delta->spi->controller); } static irqreturn_t ad_sd_trigger_handler(int irq, void *p) @@ -468,7 +576,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) /* * Data array after transfer will look like (if status is appended): * data[] = { [0][sample][sample][sample][status] } - * Keeping the first byte 0 shifts the status postion by 1 byte to the right. + * Keeping the first byte 0 shifts the status position by 1 byte to the right. */ status_pos = reg_size + 1; @@ -515,8 +623,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) irq_handled: iio_trigger_notify_done(indio_dev->trig); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + ad_sd_enable_irq(sigma_delta); return IRQ_HANDLED; } @@ -525,7 +632,7 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - return bitmap_weight(mask, indio_dev->masklength) <= sigma_delta->num_slots; + return bitmap_weight(mask, iio_get_masklength(indio_dev)) <= sigma_delta->num_slots; } static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { @@ -538,12 +645,31 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) { struct ad_sigma_delta *sigma_delta = private; - complete(&sigma_delta->completion); - disable_irq_nosync(irq); - sigma_delta->irq_dis = true; - iio_trigger_poll(sigma_delta->trig); + /* + * AD7124 and a few others use the same physical line for interrupt + * reporting (R̅D̅Y̅) and MISO. + * As MISO toggles when reading a register, this likely results in a + * pending interrupt. This has two consequences: a) The irq might + * trigger immediately after it's enabled even though the conversion + * isn't done yet; and b) checking the STATUS register's R̅D̅Y̅ flag is + * off-limits as reading that would trigger another irq event. + * + * So read the MOSI line as GPIO (if available) and only trigger the irq + * if the line is active. Without such a GPIO assume this is a valid + * interrupt. + * + * Also as disable_irq_nosync() is used to disable the irq, only act if + * the irq wasn't disabled before. + */ + if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && + ad_sd_disable_irq(sigma_delta)) { + complete(&sigma_delta->completion); + iio_trigger_poll(sigma_delta->trig); - return IRQ_HANDLED; + return IRQ_HANDLED; + } + + return IRQ_NONE; } /** @@ -563,11 +689,12 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) return 0; } -EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, "IIO_AD_SIGMA_DELTA"); static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned long irq_flags = irq_get_trigger_type(sigma_delta->irq_line); int ret; if (dev != &sigma_delta->spi->dev) { @@ -586,11 +713,15 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de sigma_delta->irq_dis = true; /* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */ - irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY); + irq_set_status_flags(sigma_delta->irq_line, IRQ_DISABLE_UNLAZY); - ret = devm_request_irq(dev, sigma_delta->spi->irq, + /* Allow overwriting the flags from firmware */ + if (!irq_flags) + irq_flags = sigma_delta->info->irq_flags; + + ret = devm_request_irq(dev, sigma_delta->irq_line, ad_sd_data_rdy_trig_poll, - sigma_delta->info->irq_flags | IRQF_NO_AUTOEN, + irq_flags | IRQF_NO_AUTOEN, indio_dev->name, sigma_delta); if (ret) @@ -632,7 +763,7 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi return devm_ad_sd_probe_trigger(dev, indio_dev); } -EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_init() - Initializes a ad_sigma_delta struct @@ -650,7 +781,7 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, sigma_delta->spi = spi; sigma_delta->info = info; - /* If the field is unset in ad_sigma_delta_info, asume there can only be 1 slot. */ + /* If the field is unset in ad_sigma_delta_info, assume there can only be 1 slot. */ if (!info->num_slots) sigma_delta->num_slots = 1; else @@ -668,11 +799,29 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, } } + spin_lock_init(&sigma_delta->irq_lock); + + if (info->irq_line) + sigma_delta->irq_line = info->irq_line; + else + sigma_delta->irq_line = spi->irq; + + sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN); + if (IS_ERR(sigma_delta->rdy_gpiod)) + return dev_err_probe(&spi->dev, PTR_ERR(sigma_delta->rdy_gpiod), + "Failed to find rdy gpio\n"); + + if (sigma_delta->rdy_gpiod && !sigma_delta->irq_line) { + sigma_delta->irq_line = gpiod_to_irq(sigma_delta->rdy_gpiod); + if (sigma_delta->irq_line < 0) + return sigma_delta->irq_line; + } + iio_device_set_drvdata(indio_dev, sigma_delta); return 0; } -EXPORT_SYMBOL_NS_GPL(ad_sd_init, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_init, "IIO_AD_SIGMA_DELTA"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); |