Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754440AbbKJQIO (ORCPT ); Tue, 10 Nov 2015 11:08:14 -0500 Received: from mail-wm0-f46.google.com ([74.125.82.46]:36181 "EHLO mail-wm0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754398AbbKJQH5 (ORCPT ); Tue, 10 Nov 2015 11:07:57 -0500 From: Marc Titinger To: jic23@kernel.org, knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net Cc: linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, mturquette@baylibre.com, bcousson@baylibre.com, ptitiano@baylibre.com, Marc Titinger Subject: [RFC 4/4] iio: ina2xx: add SOFTWARE buffer mode using an iio kfifo. Date: Tue, 10 Nov 2015 17:07:33 +0100 Message-Id: <1447171653-12756-5-git-send-email-mtitinger@baylibre.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1447171653-12756-1-git-send-email-mtitinger@baylibre.com> References: <1447171653-12756-1-git-send-email-mtitinger@baylibre.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 6411 Lines: 226 Capture the active scan_elements into a kfifo. The capture thread will compute the remaining time until the next capture tick, and do an active wait (udelay). This will produce a stream of up to fours channels plus a 64bits timestamps (ns). # iio_readdev ina226 | od -x WARNING: High-speed mode not enabled 0000000 042f 0d5a 002e 010c 4be8 4eb4 0013 0000 0000020 0430 0d5a 002e 010c a704 4f3e 0013 0000 0000040 0430 0d5a 002e 010c b477 4fc7 0013 0000 0000060 042f 0d5b 002e 010c 8052 5050 0013 0000 0000100 042f 0d5b 002e 010c 5d92 50d8 0013 0000 0000120 0430 0d5a 002e 010c fa59 515e 0013 0000 0000140 0430 0d5b 002e 010c 95d2 51e5 0013 0000 Signed-off-by: Marc Titinger --- Ina2xx does not support auto-increment, hence the capture threads sticks with single register reads instead of regmap_bulk_read. The proper scales must be applied to those raw register values, I'm in favor of doing the conversion in userland in a client plugin for instance a sigrok, of iio-osciloscope plugin, rather than converting in kernel. --- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/ina2xx-iio.c | 120 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b648051..6ef8ce5 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -13,6 +13,8 @@ config AD_SIGMA_DELTA config INA2XX_IIO tristate "Texas Instruments INA2xx Power Monitors IIO driver" depends on I2C + select IIO_BUFFER + select IIO_KFIFO_BUF help Say yes here to build support for TI INA2xx familly Power Monitors. diff --git a/drivers/iio/adc/ina2xx-iio.c b/drivers/iio/adc/ina2xx-iio.c index 3e4a4b0..132b8a9 100644 --- a/drivers/iio/adc/ina2xx-iio.c +++ b/drivers/iio/adc/ina2xx-iio.c @@ -16,7 +16,10 @@ * Licensed under the GPL-2 or later. */ #include -#include +#include +#include +#include + #include #include #include @@ -73,6 +76,7 @@ struct ina2xx_config { struct ina2xx_chip_info { struct iio_dev *indio_dev; + struct task_struct *task; const struct ina2xx_config *config; struct mutex state_lock; long rshunt; @@ -263,6 +267,9 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev, int ret = 0; unsigned int config, tmp; + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + mutex_lock(&chip->state_lock); ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config); @@ -321,6 +328,106 @@ static const struct iio_chan_spec ina2xx_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; +/* + * return uS until next due sampling. + */ + +static s64 prev_ns; + +static int ina2xx_work_buffer(struct ina2xx_chip_info *chip) +{ + unsigned short data[8]; + struct iio_dev *indio_dev = chip->indio_dev; + int bit, ret = 0, i = 0; + unsigned long buffer_us = 0, elapsed_us = 0; + s64 time_a, time_b; + + time_a = iio_get_time_ns(); + + /* Single register reads: bulk_read will not work with ina226 + * as there is no auto-increment of the address register for + * data length longer than 16bits. + */ + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + unsigned int val; + + ret = regmap_read(chip->regmap, + INA2XX_SHUNT_VOLTAGE + bit, &val); + if (ret < 0) + goto _err; + + data[i++] = val; + } + + time_b = iio_get_time_ns(); + + iio_push_to_buffers_with_timestamp(chip->indio_dev, + (unsigned int *)data, time_a); + + buffer_us = (unsigned long)(time_b - time_a) / 1000; + elapsed_us = (unsigned long)(time_a - prev_ns) / 1000; + + trace_printk("uS: elapsed: %lu, buf: %lu\n", elapsed_us, buffer_us); + + prev_ns = time_a; +_err: + return buffer_us; +}; + +static int ina2xx_capture_thread(void *data) +{ + struct ina2xx_chip_info *chip = (struct ina2xx_chip_info *)data; + unsigned int sampling_us = chip->period_us * chip->avg; + unsigned long buffer_us; + + do { + buffer_us = ina2xx_work_buffer(chip); + + if (sampling_us > buffer_us) + udelay(sampling_us - buffer_us); + + } while (!kthread_should_stop()); + + chip->task = NULL; + + return 0; +} + +int ina2xx_buffer_enable(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + unsigned int sampling_us = chip->period_us * chip->avg; + + trace_printk("Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n", + (unsigned int)(*indio_dev->active_scan_mask), chip->freq, + chip->avg); + + trace_printk("Expected work period is %u us\n", sampling_us); + + prev_ns = iio_get_time_ns(); + + chip->task = kthread_run(ina2xx_capture_thread, (void *)chip, + "ina2xx-%uus", sampling_us); + + return PTR_ERR_OR_ZERO(chip->task); +} + +int ina2xx_buffer_disable(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + + if (chip->task) + kthread_stop(chip->task); + + return 0; +} + +static const struct iio_buffer_setup_ops ina2xx_setup_ops = { + .postenable = &ina2xx_buffer_enable, + .postdisable = &ina2xx_buffer_disable, +}; + static int ina2xx_debug_reg(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval) { @@ -360,6 +467,7 @@ static int ina2xx_probe(struct i2c_client *client, { struct ina2xx_chip_info *chip; struct iio_dev *indio_dev; + struct iio_buffer *buffer; int ret; unsigned int val; @@ -402,7 +510,7 @@ static int ina2xx_probe(struct i2c_client *client, indio_dev->dev.parent = &client->dev; indio_dev->info = &ina2xx_info; - indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); if (IS_ERR(chip->regmap)) { @@ -424,6 +532,14 @@ static int ina2xx_probe(struct i2c_client *client, return -ENODEV; } + buffer = devm_iio_kfifo_allocate(&indio_dev->dev); + if (!buffer) + return -ENOMEM; + + indio_dev->setup_ops = &ina2xx_setup_ops; + + iio_device_attach_buffer(indio_dev, buffer); + return iio_device_register(indio_dev); } -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/