Hi Jonathan, Lars and folks, Thanks a lot for your review and comments. Really appreciated. Further comments and questions below. Lars: thanks for your comments in the other message. The issues you point will be addressed. On Wed, 31 Jul 2013 22:08:13 +0100 Jonathan Cameron <jic23@xxxxxxxxxx> wrote: > On 07/26/13 22:10, Mario Domenech Goulart wrote: >> >> We are working on a driver for TI ADS124x ADC series (ADS1246, >> ADS1247 and ADS1248. Datasheet: http://www.ti.com/litv/pdf/sbas426g) >> >> The attached patch is what we have so far. It is by no means >> finished. We are actually submitting it in the hope you can >> review it and provide feedback. >> >> Some observations and questions in advance: >> >> * we've set the chip to only convert on-demand. I.e., it is not >> constantly converting. Conversions are only performed when >> requested via sysfs. We are not sure about the best approach >> with regard to that behavior. Should it be constantly >> converting? > Depends on how long it takes to bring the chip up to take a sample. > If you ever provide a buffered interface (probably doesn't make sense > with a reasonably slow chip like this) then continuous mode would be > what you would use for that. The sampling rate for those chips go from 5 up to 2000 samples/second. Are higher sampling rates like 2000 samples/second feasible when operating with interruptions? >> * we've set the device as IIO_TEMP, although what is currently >> exposed in sysfs is voltage. The chip is targeted to >> temperature sensors, but the actual output of the ADC is >> voltage, so we don't know exactly what to use as type. > Voltage in that case. If it is feasible to provide the > stuff to do the temperature calculation via the device tree > then perhaps temperature would be better. Ok. For simplicity, we'll assume voltage from now. >> * we are aware of some ugly hacks like wait_for_drdy. :-) What's >> the best approach to wait for the data ready signal? > Interrupt if at all possible. If you have to poll there isn't > really a better way than what you have, hideous though it is! Ok. If the sampling rate is not a problem, we will try to make it interrupt-driven. >> * in fact we've been mostly working with ADS1247 and haven't >> concentrated on supporting ADS1246 and ADS1248 for now, but we >> intend to do so. > > A few general comments. Why do you want to do the explicit channel > configuration (i.e. only provide specific channels from those supported > by the chip)? Normally we'd provide all channels. The only common > exception is SoC integrated parts. There has been some discussion of > generic configuration of this sort of thing but it hasn't yet come to > any conclusion (e.g. move this into core code if we are going to allow > it). I guess this might be justfiable here as the chip is very much directed > at differential signals for temperature measurement. > > Please document the device tree stuff so we can discuss that without > digging through the code. Here's what we currently have for the ADC: adc_pins_a: adc@0 { reg = <0>; fsl,pinmux-ids = < 0x0073 /* MX23_PAD_GPMI_D07__GPIO_0_7 */ 0x00e3 /* MX23_PAD_GPMI_D14__GPIO_0_14 */ 0x00f3 /* MX23_PAD_GPMI_D15__GPIO_0_15 */ >; fsl,drive-strength = <1>; fsl,voltage = <1>; fsl,pull-up = <1>; }; ssp1: ssp@80034000 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,imx23-spi"; pinctrl-names = "default"; pinctrl-0 = <&spi2_pins_a>; status = "okay"; adc: ads1247@0 { compatible = "ti,ads1247"; pinctrl-names = "default"; pinctrl-0 = <&adc_pins_a>; drdy-gpio = <&gpio0 7 0>; start-gpio = <&gpio0 14 0>; reset-gpio = <&gpio0 15 0>; spi-max-frequency = <2000000>; vref-mv = <2670>; #channels = <2>; channels = <0 1 2 3>; reg = <0>; }; }; Those are the parts specific to the ADC. Please, let me know if you need more context. We were thinking about using `#channels' to specify the number of channels and `channels' to specify the channels configuration as pairs of inputs. So, in the case of the above dt, we'd have two channels: Channel 1: Positive input 0 Negative input 1 Channel 2: Positive input 2 Negative input 3 > Otherwise, some bits and bobs in line. Mostly the code is fine, but > could do with a bit of tidying up. Thanks for reviewing it. Further comments below. >> 0001-Initial-and-incomplete-support-for-TI-ADS124x-ADC-se.patch >> >> >> From a31b12f59cc51d414a1e56404707c1d5d227e2f2 Mon Sep 17 00:00:00 2001 >> From: Mario Domenech Goulart <mario@xxxxxxxxxxxxxxxx> >> Date: Fri, 26 Jul 2013 16:27:08 -0300 >> Subject: [PATCH] Initial (and incomplete) support for TI ADS124x ADC series >> (WIP) >> >> Signed-off-by: Mario Domenech Goulart <mario@xxxxxxxxxxxxxxxx> >> --- >> drivers/staging/iio/adc/Kconfig | 10 + >> drivers/staging/iio/adc/Makefile | 1 + >> drivers/staging/iio/adc/ti-ads124x.c | 495 ++++++++++++++++++++++++++++++++++ >> 3 files changed, 506 insertions(+) >> create mode 100644 drivers/staging/iio/adc/ti-ads124x.c >> >> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig >> index cabc7a3..fec2966 100644 >> --- a/drivers/staging/iio/adc/Kconfig >> +++ b/drivers/staging/iio/adc/Kconfig >> @@ -130,4 +130,14 @@ config SPEAR_ADC >> Say yes here to build support for the integrated ADC inside the >> ST SPEAr SoC. Provides direct access via sysfs. >> >> +config TI_ADS124X >> + tristate "Texas Instruments ADS1246/7/8 temperature sensor and ADC driver" >> + depends on SPI >> + help >> + Say yes here to build support for Texas Instruments ADS1246/7/8 temperature >> + sensor and ADC driver. >> + >> + To compile this driver as a module, choose M here: the >> + module will be called ti-ads124x >> + >> endmenu >> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile >> index 3e9fb14..3badf8c 100644 >> --- a/drivers/staging/iio/adc/Makefile >> +++ b/drivers/staging/iio/adc/Makefile >> @@ -20,3 +20,4 @@ obj-$(CONFIG_AD7280) += ad7280a.o >> obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o >> obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o >> obj-$(CONFIG_SPEAR_ADC) += spear_adc.o >> +obj-$(CONFIG_TI_ADS124X) += ti-ads124x.o >> diff --git a/drivers/staging/iio/adc/ti-ads124x.c b/drivers/staging/iio/adc/ti-ads124x.c >> new file mode 100644 >> index 0000000..13467ca >> --- /dev/null >> +++ b/drivers/staging/iio/adc/ti-ads124x.c >> @@ -0,0 +1,495 @@ >> +/* >> + * Texas Instruments ADS1246/7/8 ADC driver >> + * >> + * Copyright (c) 2013 O.S. Systems Software LTDA. >> + * Copyright (c) 2013 Otavio Salvador <otavio@xxxxxxxxxxxxxxxx> >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#include <linux/module.h> >> + >> +#include <linux/of.h> >> +#include <linux/of_gpio.h> >> +#include <linux/spi/spi.h> >> +#include <linux/iio/iio.h> >> +#include <linux/iio/sysfs.h> >> +#include <linux/delay.h> >> + >> +/* Register addresses for ADS1247 and ADS1248 */ > Pick a part and name everything after that rather > than using a wild card. For example there is are > ads1240, ads1241 and ads1243 that aren't supported > by this driver. Even if there were not this > is bad practice as who is to say that just because > you have ads1240-ads1249 covered by your driver > no one will release and ads124a and be confused. Good point. Is there a convention for naming in situations like that (assuming we intend to support ads1246, ads1247 and ads1248)? >> +#define ADS124X_REG_MUX0 0x00 >> +#define ADS124X_REG_VBIAS 0x01 >> +#define ADS124X_REG_MUX1 0x02 >> +#define ADS124X_REG_SYS0 0x03 >> +#define ADS124X_REG_OFC0 0x04 >> +#define ADS124X_REG_OFC1 0x05 >> +#define ADS124X_REG_OFC2 0x06 >> +#define ADS124X_REG_FSC0 0x07 >> +#define ADS124X_REG_FSC1 0x08 >> +#define ADS124X_REG_FSC2 0x09 >> +#define ADS124X_REG_IDAC0 0x0a >> +#define ADS124X_REG_IDAC1 0x0b >> +#define ADS124X_REG_GPIOCFG 0x0c >> +#define ADS124X_REG_GPIODIR 0x0d >> +#define ADS124X_REG_GPIODAT 0x0e >> + >> +/* SPI Commands */ >> +#define ADS124X_SPI_WAKEUP 0x00 >> +#define ADS124X_SPI_SLEEP 0x02 >> +#define ADS124X_SPI_SYNC1 0x04 >> +#define ADS124X_SPI_SYNC2 0x04 >> +#define ADS124X_SPI_RESET 0x06 >> +#define ADS124X_SPI_NOP 0xff >> +#define ADS124X_SPI_RDATA 0x12 >> +#define ADS124X_SPI_RDATAC 0x14 >> +#define ADS124X_SPI_SDATAC 0x16 >> +#define ADS124X_SPI_RREG 0x20 >> +#define ADS124X_SPI_WREG 0x40 >> +#define ADS124X_SPI_SYSOCAL 0x60 >> +#define ADS124X_SPI_SYSGCAL 0x61 >> +#define ADS124X_SPI_SELFOCAL 0x62 >> + >> +#define ADS124X_SINGLE_REG 0x00 >> + >> +static const u16 ads124x_sample_freq_avail[] = { 5, 10, 20, 40, 80, 160, >> + 320, 640, 1000, 2000 >> +}; >> + >> +static const u8 ads124x_sample_gain_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; >> + >> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("5 10 20 40 80 160 320 640 1000 2000"); >> + >> +static IIO_CONST_ATTR(sampling_gain_available, "1 2 4 8 16 32 64 128"); >> + >> +static struct attribute *ads124x_attributes[] = { >> + &iio_const_attr_sampling_frequency_available.dev_attr.attr, >> + &iio_const_attr_sampling_gain_available.dev_attr.attr, >> + NULL >> +}; >> + >> +static const struct attribute_group ads124x_attribute_group = { >> + .attrs = ads124x_attributes, >> +}; >> + >> +struct ads124x_state { >> + struct spi_device *spi; >> + int drdy_gpio; >> + int start_gpio; >> + int reset_gpio; >> + u32 vref_mv; >> + int sample_rate; >> + >> + struct mutex lock; >> +}; >> + >> +static const struct of_device_id ads124x_ids[] = { >> + {.compatible = "ti,ads1247"}, >> + {} >> +}; >> + > I wouldn't bother with a blank line here. Ok. >> +MODULE_DEVICE_TABLE(of, ads124x_ids); >> + >> +static int ads124x_stop_reading_continuously(struct ads124x_state *st) >> +{ >> + u8 cmd[1]; >> + int ret; >> + cmd[0] = ADS124X_SPI_SDATAC; >> + >> + ret = spi_write(st->spi, cmd, 1); >> + >> + return ret; >> +} >> + >> +static int ads124x_read_reg(struct ads124x_state *st, u8 reg, u8 *buf) >> +{ >> + u8 read_cmd[2]; >> + int ret; >> + >> + read_cmd[0] = ADS124X_SPI_RREG | reg; >> + read_cmd[1] = ADS124X_SINGLE_REG; >> + spi_write(st->spi, read_cmd, 2); >> + >> + ret = spi_read(st->spi, buf, 1); >> + >> + return ret; > return spi_read(...). > > Same in other similar cases. Ok. Those are leftovers from debugging sessions that printed the return value. They are not necessary anymore, indeed. >> +} >> + >> +static int ads124x_write_reg(struct ads124x_state *st, >> + u8 reg, u8 *buf, size_t len) >> +{ >> + u8 write_cmd[3]; >> + int ret; >> + >> + write_cmd[0] = ADS124X_SPI_WREG | reg; >> + write_cmd[1] = ADS124X_SINGLE_REG; >> + write_cmd[2] = *buf; >> + >> + ret = spi_write(st->spi, write_cmd, 3); >> + >> + return ret; >> +} >> + >> +static u32 ads124x_sample_to_32bit(u8 *sample) >> +{ >> + int sample32 = 0; >> + sample32 = sample[0] << 16; >> + sample32 |= sample[1] << 8; >> + sample32 |= sample[2]; > I'd specify the above as one line and for clarity perhaps mask the unused bits > even if always 0. Ok. >> + return sign_extend32(sample32, 23); >> +} >> + >> +static void wait_for_drdy(int drdy_gpio) >> +{ >> + u8 drdy; >> + >> + for (;;) { >> + drdy = gpio_get_value(drdy_gpio); >> + if (!drdy) >> + return; >> + usleep_range(1000, 2000); >> + } > Would normally expect this to be interrupt driven with a > completion used to signal to the waiting code that it was > done. See the ad_sigma_delta.c code that does something similar > iirc. Thanks for the pointer. >> +} >> + >> +static int ads124x_convert(struct ads124x_state *st) >> +{ >> + u8 cmd[1], res[3]; >> + u32 res32; >> + int ret; >> + cmd[0] = ADS124X_SPI_RDATA; >> + >> + ret = spi_write(st->spi, cmd, 1); >> + > Make sure to handle all possible errors. Ok. >> + /* Wait for conversion results */ >> + wait_for_drdy(st->drdy_gpio); >> + >> + ret = spi_read(st->spi, res, 3); >> + >> + res32 = ads124x_sample_to_32bit(res); >> + >> + return res32; >> +} >> + >> +static void ads124x_start(struct ads124x_state *st) >> +{ >> + gpio_set_value(st->start_gpio, 1); >> + /* FIXME: the sleep time is not accurate: see the datasheet, */ >> + /* table 15 at page 33. */ >> + msleep(200); >> + return; >> +} >> + >> +static void ads124x_reset(struct ads124x_state *st) >> +{ >> + u8 cmd[1]; >> + int ret; >> + >> + gpio_set_value(st->reset_gpio, 0); >> + gpio_set_value(st->reset_gpio, 1); >> + >> + cmd[0] = ADS124X_SPI_RESET; >> + ret = spi_write(st->spi, cmd, 1); >> + >> + msleep(200); /* FIXME: that's arbitrary. */ > Fix it then ;) Fair enough. :-) >> + >> + return; >> +} >> + >> +static int ads124x_select_input(struct ads124x_state *st, >> + struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan) >> +{ >> + u8 mux0; >> + int ret; >> + >> + mutex_lock(&st->lock); >> + >> + ret = ads124x_read_reg(st, ADS124X_REG_MUX0, &mux0); >> + > No blank line here or in similar locations. Ok. >> + if (ret < 0) >> + goto release_lock_and_return; >> + >> + /* Preserve the two most significant bits */ >> + mux0 &= 0xc0; >> + >> + /* Select positive and negative inputs */ >> + mux0 |= (chan->channel << 3) | chan->channel2; >> + >> + ret = ads124x_write_reg(st, ADS124X_REG_MUX0, &mux0, 1); >> + >> +release_lock_and_return: >> + mutex_unlock(&st->lock); >> + return ret; >> +} >> + >> +static int ads124x_set_pga_gain(struct ads124x_state *st, u8 gain) >> +{ >> + int ret; >> + u8 sys0; >> + >> + mutex_lock(&st->lock); >> + >> + ret = ads124x_read_reg(st, ADS124X_REG_SYS0, &sys0); >> + >> + if (ret < 0) >> + goto release_lock_and_return; >> + >> + sys0 = (sys0 & 0x8f) | (gain << 4); >> + >> + ret = ads124x_write_reg(st, ADS124X_REG_SYS0, &sys0, 1); >> + >> +release_lock_and_return: >> + mutex_unlock(&st->lock); > Normally a blank line before a return statement. Ok. >> + return ret; >> +} >> + >> +static int ads124x_set_sample_rate(struct ads124x_state *st) >> +{ >> + u8 sys0; >> + int ret; > Blank line here for conventional formatting. Ok. >> + ret = ads124x_read_reg(st, ADS124X_REG_SYS0, &sys0); >> + if (ret < 0) >> + return ret; >> + >> + sys0 |= 0x0f & st->sample_rate; >> + >> + ret = ads124x_write_reg(st, ADS124X_REG_SYS0, &sys0, 1); >> + >> + return ret; >> +} >> + >> +static int ads124x_read_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int *val, >> + int *val2, >> + long mask) >> +{ >> + struct ads124x_state *st = iio_priv(indio_dev); >> + int ret; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_RAW: >> + ads124x_select_input(st, indio_dev, chan); >> + wait_for_drdy(st->drdy_gpio); >> + ret = ads124x_convert(st); >> + *val = ret; >> + return IIO_VAL_INT; >> + >> + case IIO_CHAN_INFO_SCALE: >> + *val = st->vref_mv; >> + *val2 = (1 << 23) - 1; >> + return IIO_VAL_FRACTIONAL; >> + >> + case IIO_CHAN_INFO_SAMP_FREQ: >> + *val = ads124x_sample_freq_avail[st->sample_rate]; >> + *val2 = 0; >> + return IIO_VAL_INT; >> + >> + default: >> + break; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int ads124x_write_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int val, int val2, long mask) >> +{ >> + struct ads124x_state *st = iio_priv(indio_dev); >> + int ret; >> + u8 i; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_SCALE: >> + for (i = 0; i < 8; i++) /* 8 possible values for PGA gain */ >> + if (val == ads124x_sample_gain_avail[i]) >> + return ads124x_set_pga_gain(st, i); >> + break; >> + >> + case IIO_CHAN_INFO_SAMP_FREQ: >> + for (i = 0; i < ARRAY_SIZE(ads124x_sample_freq_avail); i++) >> + if (val == ads124x_sample_freq_avail[i]) { >> + mutex_lock(&st->lock); >> + st->sample_rate = i; >> + ret = ads124x_set_sample_rate(st); >> + mutex_unlock(&st->lock); >> + return ret; >> + } >> + break; >> + >> + default: >> + break; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static const struct iio_info ads124x_iio_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &ads124x_read_raw, >> + .write_raw = &ads124x_write_raw, >> + .attrs = &ads124x_attribute_group, >> +}; >> + >> +static int ads124x_init_chan_array(struct iio_dev *indio_dev, >> + struct device_node *np) >> +{ >> + struct iio_chan_spec *chan_array; >> + int num_inputs = indio_dev->num_channels * 2; >> + int *channels_config; >> + int i, ret; >> + >> + channels_config = kcalloc(num_inputs, sizeof(u32), GFP_KERNEL); >> + >> + ret = of_property_read_u32_array(np, "channels", >> + channels_config, num_inputs); >> + if (ret < 0) >> + return ret; >> + >> + chan_array = kcalloc(indio_dev->num_channels, >> + sizeof(struct iio_chan_spec), GFP_KERNEL); >> + >> + if (chan_array == NULL) >> + return -ENOMEM; >> + >> + for (i = 0; i < num_inputs; i += 2) { >> + struct iio_chan_spec *chan = chan_array + (i / 2); >> + chan->type = IIO_TEMP; >> + chan->indexed = 1; >> + chan->channel = channels_config[i]; >> + chan->channel2 = channels_config[i + 1]; > So the any pair of inputs is a valid differential pair? It actually assumes the dt contains channels configuration that make sense. Should it validate the configuration? >> + chan->differential = 1; >> + chan->scan_index = i; >> + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); >> + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | >> + BIT(IIO_CHAN_INFO_SAMP_FREQ); >> + } >> + >> + indio_dev->channels = chan_array; >> + > Why return a variable that is effectively passed in? Thanks for pointing that out. It's pointless, indeed. >> + return indio_dev->num_channels; >> +} >> + >> +static int ads124x_probe(struct spi_device *spi) >> +{ >> + struct device_node *np = spi->dev.of_node; >> + struct iio_dev *indio_dev; >> + struct ads124x_state *st; >> + int ret; >> + >> + indio_dev = iio_device_alloc(sizeof(*st)); >> + if (indio_dev == NULL) >> + return -ENOMEM; >> + >> + st = iio_priv(indio_dev); >> + >> + /* Initialize GPIO pins */ >> + st->drdy_gpio = of_get_named_gpio(np, "drdy-gpio", 0); >> + st->start_gpio = of_get_named_gpio(np, "start-gpio", 0); >> + st->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); >> + >> + ret = devm_gpio_request_one(&indio_dev->dev, st->drdy_gpio, >> + GPIOF_IN, "adc-drdy"); >> + if (ret) { >> + dev_err(&indio_dev->dev, "failed to get adc-drdy-gpios: %d\n", >> + ret); >> + goto error; >> + } >> + >> + ret = devm_gpio_request_one(&indio_dev->dev, st->start_gpio, >> + GPIOF_OUT_INIT_LOW, "adc-start"); >> + if (ret) { >> + dev_err(&indio_dev->dev, "failed to get adc-start-gpios: %d\n", >> + ret); >> + goto error; >> + } >> + >> + ret = devm_gpio_request_one(&indio_dev->dev, st->reset_gpio, >> + GPIOF_OUT_INIT_LOW, "adc-reset"); > Cleaner to do this with the spi->dev if you use devm_iio_device_alloc > that was recently introduced. Ok. I'll look into that. >> + if (ret) { >> + dev_err(&indio_dev->dev, "failed to get adc-reset-gpios: %d\n", >> + ret); >> + goto error; >> + } >> + >> + ret = of_property_read_u32(np, "vref-mv", &st->vref_mv); >> + if (ret < 0) >> + goto error; > Why not use the regulator framework to supply this. Ok. >> + >> + /* Initialize SPI */ >> + spi_set_drvdata(spi, indio_dev); >> + st->spi = spi; >> + st->spi->mode = SPI_MODE_1; >> + st->spi->bits_per_word = 8; >> + ret = spi_setup(spi); >> + >> + indio_dev->dev.parent = &spi->dev; >> + indio_dev->name = np->name; >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + indio_dev->info = &ads124x_iio_info; >> + >> + /* Setup the ADC channels available on the board */ >> + ret = of_property_read_u32(np, "#channels", &indio_dev->num_channels); >> + if (ret < 0) >> + goto error; > I wouldn't normally expect to see this for a non integrated. > We'd normally expect to see all channels as one tends not to put an N channel > device on a board without wiring it to something. > > Is this actually a way of handling the different parts? Yeah, we didn't assume non integrated systems. We assumed a device tree with proper channel configuration. Maybe that's not feasible for the general case. > If so don't do that, just have separate static channel arrays dependent > on what parts are present. Ok. >> + >> + ret = ads124x_init_chan_array(indio_dev, np); >> + if (ret < 0) >> + goto error; >> + >> + ret = iio_device_register(indio_dev); >> + if (ret) >> + goto error; >> + > These next 3 lines certainly need explanation. I'd also suggest > they want to go before the iio_device_register as that brings > up the userspace interfaces. Ok. I'm taking a closer look at the datashet and I'm not totally sure they are necessary here. I'll need to investigate it further. >> + ads124x_reset(st); >> + ads124x_start(st); >> + ads124x_stop_reading_continuously(st); >> + > This definitely wants to go before the iio_device_register. Ok. >> + mutex_init(&st->lock); >> + >> + return 0; >> + >> +error: >> + iio_device_free(indio_dev); >> + dev_err(&spi->dev, "ADS124x: Error while probing.\n"); >> + >> + return ret; >> +} >> + >> +static int ads124x_remove(struct spi_device *spi) >> +{ >> + struct iio_dev *indio_dev = spi_get_drvdata(spi); >> + struct ads124x_state *st; >> + >> + iio_device_unregister(indio_dev); >> + iio_device_free(indio_dev); >> + > The above frees the iio_dev structure and associated > private region so the next two lines are accessing memory > already freed. Thanks for pointing that out. Will fix it. > The recent addition of devm_iio_allocate_device() > simplifies this sort of handling by providing a managed interface. Ok. >> + st = iio_priv(indio_dev); >> + mutex_destroy(&st->lock); > >> + >> + return 0; >> +} >> + >> +static struct spi_driver ads124x_driver = { >> + .driver = { >> + .name = "ti-ads124x", >> + .owner = THIS_MODULE, >> + .of_match_table = of_match_ptr(ads124x_ids), >> + }, >> + .probe = ads124x_probe, >> + .remove = ads124x_remove, >> +}; >> + >> +module_spi_driver(ads124x_driver); >> + >> +MODULE_AUTHOR("Otavio Salvador <otavio@xxxxxxxxxxxxxxxx>"); >> +MODULE_DESCRIPTION("Texas Instruments ADS1246/7/8 driver"); >> +MODULE_LICENSE("GPL v2"); >> -- 1.7.10.4 Best wishes. Mario -- http://www.ossystems.com.br -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html