From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Message-ID: <4D52505A.50803@analog.com> Date: Wed, 9 Feb 2011 09:29:14 +0100 From: Michael Hennerich Reply-To: michael.hennerich@analog.com MIME-Version: 1.0 To: Jonathan Cameron CC: "linux-iio@vger.kernel.org" , Drivers , "device-drivers-devel@blackfin.uclinux.org" Subject: Re: [PATCH] IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4 References: <1297085367-27208-1-git-send-email-michael.hennerich@analog.com> <4D519B30.3060208@cam.ac.uk> In-Reply-To: <4D519B30.3060208@cam.ac.uk> Content-Type: text/plain; charset="ISO-8859-1" List-ID: On 02/08/2011 08:36 PM, Jonathan Cameron wrote: > On 02/07/11 13:29, michael.hennerich@analog.com wrote: > >> From: Michael Hennerich >> >> This patch adds support for the: >> AD7606/AD7606-6/AD7606-4 8/6/4-Channel Data Acquisition >> system (DAS) with 16-Bit, Bipolar, Simultaneous Sampling ADC. >> >> Changes since V1: >> IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4: Apply review feedback >> >> Rename sysfs node oversampling to oversampling_ratio. >> Kconfig: Add GPIOLIB dependency. >> Use range in mV to better match HWMON. >> Rename ad7606_check_oversampling. >> Fix various comments and style. >> Reorder is_visible cases. >> Use new gpio_request_one/array and friends. >> Drop check for SPI max_speed_hz. >> >> > Hi Michael. > > I think there is a race condition in ad7606_scan_direct. Pretty > unlikely but at least a first glance a small reordering makes it impossible. > > I haven't checked but think it wasn't to be a dependance on GENERIC_GPIO > instead of GPIOLIB. > > > Few nitpicks / suggestions inline. > > > >> Signed-off-by: Michael Hennerich >> > Acked-by: Jonathan Cameron > > Fix the bits above and send on to Greg. > >> --- >> drivers/staging/iio/Documentation/sysfs-bus-iio | 25 + >> drivers/staging/iio/adc/Kconfig | 28 ++ >> drivers/staging/iio/adc/Makefile | 6 + >> drivers/staging/iio/adc/ad7606.h | 117 +++++ >> drivers/staging/iio/adc/ad7606_core.c | 549 +++++++++++++++++++++++ >> drivers/staging/iio/adc/ad7606_par.c | 188 ++++++++ >> drivers/staging/iio/adc/ad7606_ring.c | 259 +++++++++++ >> drivers/staging/iio/adc/ad7606_spi.c | 126 ++++++ >> 8 files changed, 1298 insertions(+), 0 deletions(-) >> create mode 100644 drivers/staging/iio/adc/ad7606.h >> create mode 100644 drivers/staging/iio/adc/ad7606_core.c >> create mode 100644 drivers/staging/iio/adc/ad7606_par.c >> create mode 100644 drivers/staging/iio/adc/ad7606_ring.c >> create mode 100644 drivers/staging/iio/adc/ad7606_spi.c >> >> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/staging/iio/Documentation/sysfs-bus-iio >> index 8e5d8d1..4116abc 100644 >> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio >> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio >> @@ -53,6 +53,31 @@ Description: >> When the internal sampling clock can only take a small >> discrete set of values, this file lists those available. >> >> +What: /sys/bus/iio/devices/deviceX/range >> +KernelVersion: 2.6.38 >> +Contact: linux-iio@vger.kernel.org >> +Description: >> + Hardware dependent ADC Full Scale Range. >> > Units specified please. > ok >> + >> +What: /sys/bus/iio/devices/deviceX/range_available >> +KernelVersion: 2.6.38 >> +Contact: linux-iio@vger.kernel.org >> +Description: >> + Hardware dependent supported vales for ADC Full Scale Range. >> + >> +What: /sys/bus/iio/devices/deviceX/oversampling_ratio >> +KernelVersion: 2.6.38 >> +Contact: linux-iio@vger.kernel.org >> +Description: >> + Hardware dependent ADC oversampling. Controls the sampling ratio >> + of the digital filter if available. >> + >> +What: /sys/bus/iio/devices/deviceX/oversampling_ratio_available >> +KernelVersion: 2.6.38 >> +Contact: linux-iio@vger.kernel.org >> +Description: >> + Hardware dependent values supported by the oversampling filter. >> + >> What: /sys/bus/iio/devices/deviceX/inY_raw >> What: /sys/bus/iio/devices/deviceX/inY_supply_raw >> KernelVersion: 2.6.35 >> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig >> index 86869cd..c622a6c 100644 >> --- a/drivers/staging/iio/adc/Kconfig >> +++ b/drivers/staging/iio/adc/Kconfig >> @@ -62,6 +62,34 @@ config AD7314 >> Say yes here to build support for Analog Devices AD7314 >> temperature sensors. >> >> +config AD7606 >> + tristate "Analog Devices AD7606 ADC driver" >> + depends on GPIOLIB >> > Is that the correct depend? I think we need Generic GPIO but it is at least > in theary posible to have that without GPIOLIB. > Well - before I utilized gpio_request_one() and friends the dependency was on GENERIC_GPIO. However gpio_request_one() is defined as a function only in gpiolib, there could have been inline replacements in case of !GPIOLIB, but unfortunately thats not the case. >> + select IIO_RING_BUFFER >> + select IIO_TRIGGER >> + select IIO_SW_RING >> + help >> + Say yes here to build support for Analog Devices: >> + ad7606, ad7606-6, ad7606-4 analog to digital convertors (ADC). >> + >> + To compile this driver as a module, choose M here: the >> + module will be called ad7606. >> + >> +config AD7606_IFACE_PARALLEL >> + bool "parallel interface support" >> + depends on AD7606 >> + help >> + Say yes here to include parallel interface support on the AD7606 >> + ADC driver. >> + >> +config AD7606_IFACE_SPI >> + bool "spi interface support" >> + depends on AD7606 >> + depends on SPI >> + help >> + Say yes here to include parallel interface support on the AD7606 >> + ADC driver. >> + >> config AD799X >> tristate "Analog Devices AD799x ADC driver" >> depends on I2C >> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile >> index 6f231a2..cb73346 100644 >> --- a/drivers/staging/iio/adc/Makefile >> +++ b/drivers/staging/iio/adc/Makefile >> @@ -7,6 +7,12 @@ max1363-y += max1363_ring.o >> >> obj-$(CONFIG_MAX1363) += max1363.o >> >> +ad7606-y := ad7606_core.o >> +ad7606-$(CONFIG_IIO_RING_BUFFER) += ad7606_ring.o >> +ad7606-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o >> +ad7606-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o >> +obj-$(CONFIG_AD7606) += ad7606.o >> + >> ad799x-y := ad799x_core.o >> ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o >> obj-$(CONFIG_AD799X) += ad799x.o >> diff --git a/drivers/staging/iio/adc/ad7606.h b/drivers/staging/iio/adc/ad7606.h >> new file mode 100644 >> index 0000000..338bade >> --- /dev/null >> +++ b/drivers/staging/iio/adc/ad7606.h >> @@ -0,0 +1,117 @@ >> +/* >> + * AD7606 ADC driver >> + * >> + * Copyright 2011 Analog Devices Inc. >> + * >> + * Licensed under the GPL-2. >> + */ >> + >> +#ifndef IIO_ADC_AD7606_H_ >> +#define IIO_ADC_AD7606_H_ >> + >> +/* >> + * TODO: struct ad7606_platform_data needs to go into include/linux/iio >> + */ >> + >> +/** >> + * struct ad7606_platform_data - platform/board specifc information >> + * @default_os: default oversampling value {0, 2, 4, 8, 16, 32, 64} >> + * @default_range: default range +/-{5000, 10000} mVolt >> + * @gpio_convst: number of gpio connected to the CONVST pin >> + * @gpio_reset: gpio connected to the RESET pin, if not used set to -1 >> + * @gpio_range: gpio connected to the RANGE pin, if not used set to -1 >> + * @gpio_os0: gpio connected to the OS0 pin, if not used set to -1 >> + * @gpio_os1: gpio connected to the OS1 pin, if not used set to -1 >> + * @gpio_os2: gpio connected to the OS2 pin, if not used set to -1 >> + * @gpio_frstdata: gpio connected to the FRSTDAT pin, if not used set to -1 >> + * @gpio_stby: gpio connected to the STBY pin, if not used set to -1 >> + */ >> + >> +struct ad7606_platform_data { >> + unsigned default_os; >> + unsigned default_range; >> + unsigned gpio_convst; >> + unsigned gpio_reset; >> + unsigned gpio_range; >> + unsigned gpio_os0; >> + unsigned gpio_os1; >> + unsigned gpio_os2; >> + unsigned gpio_frstdata; >> + unsigned gpio_stby; >> +}; >> + >> +/** >> + * struct ad7606_chip_info - chip specifc information >> + * @name: indentification string for chip >> + * @bits: accuracy of the adc in bits >> + * @bits: output coding [s]igned or [u]nsigned >> + * @int_vref_mv: the internal reference voltage >> + * @num_channels: number of physical inputs on chip >> + */ >> + >> +struct ad7606_chip_info { >> + char name[10]; >> + u8 bits; >> + char sign; >> + u16 int_vref_mv; >> + unsigned num_channels; >> +}; >> + >> +/** >> + * struct ad7606_state - driver instance specific data >> + */ >> + >> +struct ad7606_state { >> + struct iio_dev *indio_dev; >> + struct device *dev; >> + const struct ad7606_chip_info *chip_info; >> + struct ad7606_platform_data *pdata; >> + struct regulator *reg; >> + struct work_struct poll_work; >> + wait_queue_head_t wq_data_avail; >> + atomic_t protect_ring; >> + size_t d_size; >> + const struct ad7606_bus_ops *bops; >> + int irq; >> + unsigned id; >> + unsigned range; >> + unsigned oversampling; >> + bool done; >> + bool have_frstdata; >> + bool have_os; >> + bool have_stby; >> + bool have_reset; >> + bool have_range; >> + void __iomem *base_address; >> + >> + /* >> + * DMA (thus cache coherency maintenance) requires the >> + * transfer buffers to live in their own cache lines. >> + */ >> + >> + unsigned short data[8] ____cacheline_aligned; >> +}; >> + >> +struct ad7606_bus_ops { >> + /* more methods added in future? */ >> + int (*read_block)(struct device *, int, void *); >> +}; >> + >> +void ad7606_suspend(struct ad7606_state *st); >> +void ad7606_resume(struct ad7606_state *st); >> +struct ad7606_state *ad7606_probe(struct device *dev, int irq, >> + void __iomem *base_address, unsigned id, >> + const struct ad7606_bus_ops *bops); >> +int ad7606_remove(struct ad7606_state *st); >> +int ad7606_reset(struct ad7606_state *st); >> + >> +enum ad7606_supported_device_ids { >> + ID_AD7606_8, >> + ID_AD7606_6, >> + ID_AD7606_4 >> +}; >> + >> +int ad7606_scan_from_ring(struct ad7606_state *st, unsigned ch); >> +int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev); >> +void ad7606_ring_cleanup(struct iio_dev *indio_dev); >> +#endif /* IIO_ADC_AD7606_H_ */ >> diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c >> new file mode 100644 >> index 0000000..65481e1 >> --- /dev/null >> +++ b/drivers/staging/iio/adc/ad7606_core.c >> @@ -0,0 +1,549 @@ >> +/* >> + * AD7606 SPI ADC driver >> + * >> + * Copyright 2011 Analog Devices Inc. >> + * >> + * Licensed under the GPL-2. >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "../iio.h" >> +#include "../sysfs.h" >> +#include "../ring_generic.h" >> +#include "adc.h" >> + >> +#include "ad7606.h" >> + >> +int ad7606_reset(struct ad7606_state *st) >> +{ >> + if (st->have_reset) { >> + gpio_set_value(st->pdata->gpio_reset, 1); >> + ndelay(100); /* t_reset >= 100ns */ >> + gpio_set_value(st->pdata->gpio_reset, 0); >> + return 0; >> + } >> + >> + return -ENODEV; >> +} >> + >> +static int ad7606_scan_direct(struct ad7606_state *st, unsigned ch) >> +{ >> + int ret; >> + >> + gpio_set_value(st->pdata->gpio_convst, 1); >> > Race condition if for whatever reason the interrupt occurs here? > Good catch! >> + st->done = false; >> + >> + ret = wait_event_interruptible(st->wq_data_avail, st->done); >> + if (ret) >> + goto error_ret; >> + >> + if (st->have_frstdata) { >> + st->bops->read_block(st->dev, 1, st->data); >> + if (!gpio_get_value(st->pdata->gpio_frstdata)) { >> + /* This should never happen */ >> + ad7606_reset(st); >> + ret = -EIO; >> + goto error_ret; >> + } >> + st->bops->read_block(st->dev, >> + st->chip_info->num_channels - 1, &st->data[1]); >> + } else { >> + st->bops->read_block(st->dev, >> + st->chip_info->num_channels, st->data); >> + } >> + >> + ret = st->data[ch]; >> + >> +error_ret: >> + gpio_set_value(st->pdata->gpio_convst, 0); >> + >> + return ret; >> +} >> + >> +static ssize_t ad7606_scan(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = dev_info->dev_data; >> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); >> + int ret; >> + >> + mutex_lock(&dev_info->mlock); >> + if (iio_ring_enabled(dev_info)) >> + ret = ad7606_scan_from_ring(st, this_attr->address); >> + else >> + ret = ad7606_scan_direct(st, this_attr->address); >> + mutex_unlock(&dev_info->mlock); >> + >> + if (ret < 0) >> + return ret; >> + >> + return sprintf(buf, "%d\n", (short) ret); >> +} >> + >> +static IIO_DEV_ATTR_IN_RAW(0, ad7606_scan, 0); >> +static IIO_DEV_ATTR_IN_RAW(1, ad7606_scan, 1); >> +static IIO_DEV_ATTR_IN_RAW(2, ad7606_scan, 2); >> +static IIO_DEV_ATTR_IN_RAW(3, ad7606_scan, 3); >> +static IIO_DEV_ATTR_IN_RAW(4, ad7606_scan, 4); >> +static IIO_DEV_ATTR_IN_RAW(5, ad7606_scan, 5); >> +static IIO_DEV_ATTR_IN_RAW(6, ad7606_scan, 6); >> +static IIO_DEV_ATTR_IN_RAW(7, ad7606_scan, 7); >> + >> +static ssize_t ad7606_show_scale(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + /* Driver currently only support internal vref */ >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = iio_dev_get_devdata(dev_info); >> + unsigned int scale_uv = (st->range * 1000 * 2) >> st->chip_info->bits; >> + >> + return sprintf(buf, "%d.%03d\n", scale_uv / 1000, scale_uv % 1000); >> +} >> +static IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad7606_show_scale, NULL, 0); >> + >> +static ssize_t ad7606_show_name(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = iio_dev_get_devdata(dev_info); >> + >> + return sprintf(buf, "%s\n", st->chip_info->name); >> +} >> + >> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7606_show_name, NULL, 0); >> + >> +static ssize_t ad7606_show_range(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = iio_dev_get_devdata(dev_info); >> + >> + return sprintf(buf, "%u\n", st->range); >> +} >> + >> +static ssize_t ad7606_store_range(struct device *dev, >> + struct device_attribute *attr, const char *buf, size_t count) >> +{ >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = iio_dev_get_devdata(dev_info); >> + unsigned long lval; >> + >> + if (strict_strtoul(buf, 10, &lval)) >> + return -EINVAL; >> + if (!(lval == 5000 || lval == 10000)) { >> + dev_err(dev, "range is not supported\n"); >> + return -EINVAL; >> + } >> + mutex_lock(&dev_info->mlock); >> + gpio_set_value(st->pdata->gpio_range, lval == 10000); >> + st->range = lval; >> + mutex_unlock(&dev_info->mlock); >> + >> + return count; >> +} >> + >> +static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, \ >> + ad7606_show_range, ad7606_store_range, 0); >> +static IIO_CONST_ATTR(range_available, "5000 10000"); >> + >> +static ssize_t ad7606_show_oversampling_ratio(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = iio_dev_get_devdata(dev_info); >> + >> + return sprintf(buf, "%u\n", st->oversampling); >> +} >> + >> +static int ad7606_oversampling_get_index(unsigned val) >> +{ >> + unsigned char supported[] = {0, 2, 4, 8, 16, 32, 64}; >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(supported); i++) >> + if (val == supported[i]) >> + return i; >> + >> + return -EINVAL; >> +} >> + >> +static ssize_t ad7606_store_oversampling_ratio(struct device *dev, >> + struct device_attribute *attr, const char *buf, size_t count) >> +{ >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = iio_dev_get_devdata(dev_info); >> + unsigned long lval; >> + int ret; >> + >> + if (strict_strtoul(buf, 10, &lval)) >> + return -EINVAL; >> + >> + ret = ad7606_oversampling_get_index(lval); >> + if (ret < 0) { >> + dev_err(dev, "oversampling %lu is not supported\n", lval); >> + return ret; >> + } >> + >> + mutex_lock(&dev_info->mlock); >> + gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1); >> + gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1); >> + gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1); >> + st->oversampling = lval; >> + mutex_unlock(&dev_info->mlock); >> + >> + return count; >> +} >> + >> +static IIO_DEVICE_ATTR(oversampling_ratio, S_IRUGO | S_IWUSR, >> + ad7606_show_oversampling_ratio, >> + ad7606_store_oversampling_ratio, 0); >> +static IIO_CONST_ATTR(oversampling_ratio_available, "0 2 4 8 16 32 64"); >> + >> +static struct attribute *ad7606_attributes[] = { >> + &iio_dev_attr_in0_raw.dev_attr.attr, >> + &iio_dev_attr_in1_raw.dev_attr.attr, >> + &iio_dev_attr_in2_raw.dev_attr.attr, >> + &iio_dev_attr_in3_raw.dev_attr.attr, >> + &iio_dev_attr_in4_raw.dev_attr.attr, >> + &iio_dev_attr_in5_raw.dev_attr.attr, >> + &iio_dev_attr_in6_raw.dev_attr.attr, >> + &iio_dev_attr_in7_raw.dev_attr.attr, >> + &iio_dev_attr_in_scale.dev_attr.attr, >> + &iio_dev_attr_name.dev_attr.attr, >> + &iio_dev_attr_range.dev_attr.attr, >> + &iio_const_attr_range_available.dev_attr.attr, >> + &iio_dev_attr_oversampling_ratio.dev_attr.attr, >> + &iio_const_attr_oversampling_ratio_available.dev_attr.attr, >> + NULL, >> +}; >> + >> +static mode_t ad7606_attr_is_visible(struct kobject *kobj, >> + struct attribute *attr, int n) >> +{ >> + struct device *dev = container_of(kobj, struct device, kobj); >> + struct iio_dev *dev_info = dev_get_drvdata(dev); >> + struct ad7606_state *st = iio_dev_get_devdata(dev_info); >> + >> + mode_t mode = attr->mode; >> + >> + if (st->chip_info->num_channels <= 6 && >> + (attr == &iio_dev_attr_in7_raw.dev_attr.attr || >> + attr == &iio_dev_attr_in6_raw.dev_attr.attr)) >> + mode = 0; >> + else if (st->chip_info->num_channels <= 4 && >> + (attr == &iio_dev_attr_in5_raw.dev_attr.attr || >> + attr == &iio_dev_attr_in4_raw.dev_attr.attr)) >> + mode = 0; >> + else if (!st->have_os && >> + (attr == &iio_dev_attr_oversampling_ratio.dev_attr.attr || >> + attr == >> + &iio_const_attr_oversampling_ratio_available.dev_attr.attr)) >> + mode = 0; >> + else if (!st->have_range && >> + (attr == &iio_dev_attr_range.dev_attr.attr || >> + attr == &iio_const_attr_range_available.dev_attr.attr)) >> + mode = 0; >> + >> + return mode; >> +} >> + >> +static const struct attribute_group ad7606_attribute_group = { >> + .attrs = ad7606_attributes, >> + .is_visible = ad7606_attr_is_visible, >> +}; >> + >> +static const struct ad7606_chip_info ad7606_chip_info_tbl[] = { >> + /* >> + * More devices added in future >> + */ >> + [ID_AD7606_8] = { >> + .name = "ad7606", >> + .bits = 16, >> + .sign = IIO_SCAN_EL_TYPE_SIGNED, >> + .int_vref_mv = 2500, >> + .num_channels = 8, >> + }, >> + [ID_AD7606_6] = { >> + .name = "ad7606-6", >> + .bits = 16, >> + .sign = IIO_SCAN_EL_TYPE_SIGNED, >> + .int_vref_mv = 2500, >> + .num_channels = 6, >> + }, >> + [ID_AD7606_4] = { >> + .name = "ad7606-4", >> + .bits = 16, >> + .sign = IIO_SCAN_EL_TYPE_SIGNED, >> + .int_vref_mv = 2500, >> + .num_channels = 4, >> + }, >> +}; >> + >> +static int ad7606_request_gpios(struct ad7606_state *st) >> +{ >> + struct gpio gpio_array[3] = { >> + [0] = { >> + .gpio = st->pdata->gpio_os0, >> + .flags = GPIOF_DIR_OUT | ((st->oversampling & 1) ? >> + GPIOF_INIT_HIGH : GPIOF_INIT_LOW), >> + .label = "AD7606_OS0", >> + }, >> + [1] = { >> + .gpio = st->pdata->gpio_os1, >> + .flags = GPIOF_DIR_OUT | ((st->oversampling & 2) ? >> + GPIOF_INIT_HIGH : GPIOF_INIT_LOW), >> + .label = "AD7606_OS1", >> + }, >> + [2] = { >> + .gpio = st->pdata->gpio_os2, >> + .flags = GPIOF_DIR_OUT | ((st->oversampling & 4) ? >> + GPIOF_INIT_HIGH : GPIOF_INIT_LOW), >> + .label = "AD7606_OS2", >> + }, >> + }; >> + int ret; >> + >> + ret = gpio_request_one(st->pdata->gpio_convst, GPIOF_OUT_INIT_LOW, >> + "AD7606_CONVST"); >> + if (ret) { >> + dev_err(st->dev, "failed to request GPIO CONVST\n"); >> + return ret; >> + } >> + >> + ret = gpio_request_array(gpio_array, ARRAY_SIZE(gpio_array)); >> + if (!ret) { >> + st->have_os = true; >> + } >> + >> + ret = gpio_request_one(st->pdata->gpio_reset, GPIOF_OUT_INIT_LOW, >> + "AD7606_RESET"); >> + if (!ret) >> + st->have_reset = true; >> + >> + ret = gpio_request_one(st->pdata->gpio_range, GPIOF_DIR_OUT | >> + ((st->range == 10000) ? GPIOF_INIT_HIGH : >> + GPIOF_INIT_LOW), "AD7606_RANGE"); >> + if (!ret) >> + st->have_range = true; >> + >> + ret = gpio_request_one(st->pdata->gpio_stby, GPIOF_OUT_INIT_HIGH, >> + "AD7606_STBY"); >> + if (!ret) >> + st->have_stby = true; >> + >> + if (gpio_is_valid(st->pdata->gpio_frstdata)) { >> + ret = gpio_request_one(st->pdata->gpio_frstdata, GPIOF_IN, >> + "AD7606_FRSTDATA"); >> + if (!ret) >> + st->have_frstdata = true; >> + } >> + >> + return 0; >> +} >> + >> +static void ad7606_free_gpios(struct ad7606_state *st) >> +{ >> + if (st->have_range) >> + gpio_free(st->pdata->gpio_range); >> + >> + if (st->have_stby) >> + gpio_free(st->pdata->gpio_stby); >> + >> + if (st->have_os) { >> + gpio_free(st->pdata->gpio_os0); >> + gpio_free(st->pdata->gpio_os1); >> + gpio_free(st->pdata->gpio_os2); >> + } >> + >> + if (st->have_reset) >> + gpio_free(st->pdata->gpio_reset); >> + >> + if (st->have_frstdata) >> + gpio_free(st->pdata->gpio_frstdata); >> + >> + gpio_free(st->pdata->gpio_convst); >> +} >> + >> +/** >> + * Interrupt handler >> + */ >> +static irqreturn_t ad7606_interrupt(int irq, void *dev_id) >> +{ >> + struct ad7606_state *st = dev_id; >> + >> + if (iio_ring_enabled(st->indio_dev)) { >> + if (!work_pending(&st->poll_work)) >> + schedule_work(&st->poll_work); >> + } else { >> + st->done = true; >> + wake_up_interruptible(&st->wq_data_avail); >> + } >> + >> + return IRQ_HANDLED; >> +}; >> + >> +struct ad7606_state *ad7606_probe(struct device *dev, int irq, >> + void __iomem *base_address, >> + unsigned id, >> + const struct ad7606_bus_ops *bops) >> +{ >> + struct ad7606_platform_data *pdata = dev->platform_data; >> + struct ad7606_state *st; >> + int ret; >> + >> + st = kzalloc(sizeof(*st), GFP_KERNEL); >> + if (st == NULL) { >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + >> + st->dev = dev; >> + st->id = id; >> + st->irq = irq; >> + st->bops = bops; >> + st->base_address = base_address; >> + st->range = pdata->default_range == 10000 ? 10000 : 5000; >> + >> + ret = ad7606_oversampling_get_index(pdata->default_os); >> + if (ret < 0) { >> + dev_warn(dev, "oversampling %d is not supported\n", >> + pdata->default_os); >> + st->oversampling = 0; >> + } else { >> + st->oversampling = pdata->default_os; >> + } >> + >> + st->reg = regulator_get(dev, "vcc"); >> + if (!IS_ERR(st->reg)) { >> + ret = regulator_enable(st->reg); >> + if (ret) >> + goto error_put_reg; >> + } >> + >> + st->pdata = pdata; >> + st->chip_info = &ad7606_chip_info_tbl[id]; >> + >> + atomic_set(&st->protect_ring, 0); >> + >> + st->indio_dev = iio_allocate_device(); >> + if (st->indio_dev == NULL) { >> + ret = -ENOMEM; >> + goto error_disable_reg; >> + } >> + >> + st->indio_dev->dev.parent = dev; >> + st->indio_dev->attrs = &ad7606_attribute_group; >> + st->indio_dev->dev_data = (void *)(st); >> + st->indio_dev->driver_module = THIS_MODULE; >> + st->indio_dev->modes = INDIO_DIRECT_MODE; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + >> + ret = ad7606_request_gpios(st); >> + if (ret) >> + goto error_free_device; >> + >> + ret = ad7606_reset(st); >> + if (ret) >> + dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); >> + >> + ret = request_irq(st->irq, ad7606_interrupt, >> + IRQF_TRIGGER_FALLING, st->chip_info->name, st); >> + if (ret) >> + goto error_free_gpios; >> + >> + ret = ad7606_register_ring_funcs_and_init(st->indio_dev); >> + if (ret) >> + goto error_free_irq; >> + >> + ret = iio_device_register(st->indio_dev); >> + if (ret) >> + goto error_free_irq; >> + >> + ret = iio_ring_buffer_register(st->indio_dev->ring, 0); >> + if (ret) >> + goto error_cleanup_ring; >> + >> + return st; >> + >> +error_cleanup_ring: >> + ad7606_ring_cleanup(st->indio_dev); >> + iio_device_unregister(st->indio_dev); >> + >> +error_free_irq: >> + free_irq(st->irq, st); >> + >> +error_free_gpios: >> + ad7606_free_gpios(st); >> + >> +error_free_device: >> + iio_free_device(st->indio_dev); >> + >> +error_disable_reg: >> + if (!IS_ERR(st->reg)) >> + regulator_disable(st->reg); >> +error_put_reg: >> + if (!IS_ERR(st->reg)) >> + regulator_put(st->reg); >> + kfree(st); >> +error_ret: >> + return ERR_PTR(ret); >> +} >> + >> +int ad7606_remove(struct ad7606_state *st) >> +{ >> + struct iio_dev *indio_dev = st->indio_dev; >> + iio_ring_buffer_unregister(indio_dev->ring); >> + ad7606_ring_cleanup(indio_dev); >> + iio_device_unregister(indio_dev); >> + free_irq(st->irq, st); >> + if (!IS_ERR(st->reg)) { >> + regulator_disable(st->reg); >> + regulator_put(st->reg); >> + } >> + >> + ad7606_free_gpios(st); >> + >> + kfree(st); >> + return 0; >> +} >> + >> +void ad7606_suspend(struct ad7606_state *st) >> +{ >> + if (st->have_stby) { >> + if (st->have_range) >> + gpio_set_value(st->pdata->gpio_range, 1); >> + gpio_set_value(st->pdata->gpio_stby, 0); >> + } >> +} >> + >> +void ad7606_resume(struct ad7606_state *st) >> +{ >> + if (st->have_stby) { >> + if (st->have_range) >> + gpio_set_value(st->pdata->gpio_range, >> + st->range == 10000); >> + >> + gpio_set_value(st->pdata->gpio_stby, 1); >> + ad7606_reset(st); >> + } >> +} >> + >> +MODULE_AUTHOR("Michael Hennerich "); >> +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); >> +MODULE_LICENSE("GPL v2"); >> diff --git a/drivers/staging/iio/adc/ad7606_par.c b/drivers/staging/iio/adc/ad7606_par.c >> new file mode 100644 >> index 0000000..43a554c >> --- /dev/null >> +++ b/drivers/staging/iio/adc/ad7606_par.c >> @@ -0,0 +1,188 @@ >> +/* >> + * AD7606 Parallel Interface ADC driver >> + * >> + * Copyright 2011 Analog Devices Inc. >> + * >> + * Licensed under the GPL-2. >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "ad7606.h" >> + >> +static int ad7606_par16_read_block(struct device *dev, >> + int count, void *buf) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + struct ad7606_state *st = platform_get_drvdata(pdev); >> + >> + insw((unsigned long) st->base_address, buf, count); >> + >> + return 0; >> +} >> + >> +static const struct ad7606_bus_ops ad7606_par16_bops = { >> + .read_block = ad7606_par16_read_block, >> +}; >> + >> +static int ad7606_par8_read_block(struct device *dev, >> + int count, void *buf) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + struct ad7606_state *st = platform_get_drvdata(pdev); >> + >> + insb((unsigned long) st->base_address, buf, count * 2); >> + >> + return 0; >> +} >> + >> +static const struct ad7606_bus_ops ad7606_par8_bops = { >> + .read_block = ad7606_par8_read_block, >> +}; >> + >> +static int __devinit ad7606_par_probe(struct platform_device *pdev) >> +{ >> + struct resource *res; >> + struct ad7606_state *st; >> + void __iomem *addr; >> + resource_size_t remap_size; >> + int ret, irq; >> + >> + irq = platform_get_irq(pdev, 0); >> + if (irq < 0) { >> + dev_err(&pdev->dev, "no irq\n"); >> + return -ENODEV; >> + } >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) >> + return -ENODEV; >> + >> + remap_size = resource_size(res); >> + >> + /* Request the regions */ >> + if (!request_mem_region(res->start, remap_size, "iio-ad7606")) { >> + ret = -EBUSY; >> + goto out1; >> + } >> + addr = ioremap(res->start, remap_size); >> + if (!addr) { >> + ret = -ENOMEM; >> + goto out1; >> + } >> + >> + st = ad7606_probe(&pdev->dev, irq, addr, >> + platform_get_device_id(pdev)->driver_data, >> + remap_size > 1 ? &ad7606_par16_bops : >> + &ad7606_par8_bops); >> + >> + if (IS_ERR(st)) { >> + ret = PTR_ERR(st); >> + goto out2; >> + } >> + >> + platform_set_drvdata(pdev, st); >> + >> + return 0; >> + >> +out2: >> + iounmap(addr); >> +out1: >> + release_mem_region(res->start, remap_size); >> + >> + return ret; >> +} >> + >> +static int __devexit ad7606_par_remove(struct platform_device *pdev) >> +{ >> + struct ad7606_state *st = platform_get_drvdata(pdev); >> + struct resource *res; >> + >> + ad7606_remove(st); >> + >> + iounmap(st->base_address); >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + release_mem_region(res->start, resource_size(res)); >> + >> + platform_set_drvdata(pdev, NULL); >> + >> + return 0; >> +} >> + >> +#ifdef CONFIG_PM >> +static int ad7606_par_suspend(struct device *dev) >> +{ >> + struct ad7606_state *st = dev_get_drvdata(dev); >> + >> + ad7606_suspend(st); >> + >> + return 0; >> +} >> + >> +static int ad7606_par_resume(struct device *dev) >> +{ >> + struct ad7606_state *st = dev_get_drvdata(dev); >> + >> + ad7606_resume(st); >> + >> + return 0; >> +} >> + >> +static const struct dev_pm_ops ad7606_pm_ops = { >> + .suspend = ad7606_par_suspend, >> + .resume = ad7606_par_resume, >> +}; >> +#define AD7606_PAR_PM_OPS (&ad7606_pm_ops) >> + >> +#else >> +#define AD7606_PAR_PM_OPS NULL >> +#endif /* CONFIG_PM */ >> + >> +static struct platform_device_id ad7606_driver_ids[] = { >> + { >> + .name = "ad7606-8", >> + .driver_data = ID_AD7606_8, >> + }, { >> + .name = "ad7606-6", >> + .driver_data = ID_AD7606_6, >> + }, { >> + .name = "ad7606-4", >> + .driver_data = ID_AD7606_4, >> + }, >> + { } >> +}; >> + >> +MODULE_DEVICE_TABLE(platform, ad7606_driver_ids); >> + >> +static struct platform_driver ad7606_driver = { >> + .probe = ad7606_par_probe, >> + .remove = __devexit_p(ad7606_par_remove), >> + .id_table = ad7606_driver_ids, >> + .driver = { >> + .name = "ad7606", >> + .owner = THIS_MODULE, >> + .pm = AD7606_PAR_PM_OPS, >> + }, >> +}; >> + >> +static int __init ad7606_init(void) >> +{ >> + return platform_driver_register(&ad7606_driver); >> +} >> + >> +static void __exit ad7606_cleanup(void) >> +{ >> + platform_driver_unregister(&ad7606_driver); >> +} >> + >> +module_init(ad7606_init); >> +module_exit(ad7606_cleanup); >> + >> +MODULE_AUTHOR("Michael Hennerich "); >> +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); >> +MODULE_LICENSE("GPL v2"); >> +MODULE_ALIAS("platform:ad7606_par"); >> diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c >> new file mode 100644 >> index 0000000..3506ef6 >> --- /dev/null >> +++ b/drivers/staging/iio/adc/ad7606_ring.c >> @@ -0,0 +1,259 @@ >> +/* >> + * Copyright 2011 Analog Devices Inc. >> + * >> + * Licensed under the GPL-2. >> + * >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "../iio.h" >> +#include "../ring_generic.h" >> +#include "../ring_sw.h" >> +#include "../trigger.h" >> +#include "../sysfs.h" >> + >> +#include "ad7606.h" >> + >> +static IIO_SCAN_EL_C(in0, 0, 0, NULL); >> +static IIO_SCAN_EL_C(in1, 1, 0, NULL); >> +static IIO_SCAN_EL_C(in2, 2, 0, NULL); >> +static IIO_SCAN_EL_C(in3, 3, 0, NULL); >> +static IIO_SCAN_EL_C(in4, 4, 0, NULL); >> +static IIO_SCAN_EL_C(in5, 5, 0, NULL); >> +static IIO_SCAN_EL_C(in6, 6, 0, NULL); >> +static IIO_SCAN_EL_C(in7, 7, 0, NULL); >> + >> +static ssize_t ad7606_show_type(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + struct iio_ring_buffer *ring = dev_get_drvdata(dev); >> + struct iio_dev *indio_dev = ring->indio_dev; >> + struct ad7606_state *st = indio_dev->dev_data; >> + >> + return sprintf(buf, "%c%d/%d\n", st->chip_info->sign, >> + st->chip_info->bits, st->chip_info->bits); >> +} >> +static IIO_DEVICE_ATTR(in_type, S_IRUGO, ad7606_show_type, NULL, 0); >> + >> +static struct attribute *ad7606_scan_el_attrs[] = { >> + &iio_scan_el_in0.dev_attr.attr, >> + &iio_const_attr_in0_index.dev_attr.attr, >> + &iio_scan_el_in1.dev_attr.attr, >> + &iio_const_attr_in1_index.dev_attr.attr, >> + &iio_scan_el_in2.dev_attr.attr, >> + &iio_const_attr_in2_index.dev_attr.attr, >> + &iio_scan_el_in3.dev_attr.attr, >> + &iio_const_attr_in3_index.dev_attr.attr, >> + &iio_scan_el_in4.dev_attr.attr, >> + &iio_const_attr_in4_index.dev_attr.attr, >> + &iio_scan_el_in5.dev_attr.attr, >> + &iio_const_attr_in5_index.dev_attr.attr, >> + &iio_scan_el_in6.dev_attr.attr, >> + &iio_const_attr_in6_index.dev_attr.attr, >> + &iio_scan_el_in7.dev_attr.attr, >> + &iio_const_attr_in7_index.dev_attr.attr, >> + &iio_dev_attr_in_type.dev_attr.attr, >> + NULL, >> +}; >> + >> +static mode_t ad7606_scan_el_attr_is_visible(struct kobject *kobj, >> + struct attribute *attr, int n) >> +{ >> + struct device *dev = container_of(kobj, struct device, kobj); >> + struct iio_ring_buffer *ring = dev_get_drvdata(dev); >> + struct iio_dev *indio_dev = ring->indio_dev; >> + struct ad7606_state *st = indio_dev->dev_data; >> + >> + mode_t mode = attr->mode; >> + >> + if (st->chip_info->num_channels <= 6 && >> + (attr == &iio_scan_el_in7.dev_attr.attr || >> + attr == &iio_const_attr_in7_index.dev_attr.attr || >> + attr == &iio_scan_el_in6.dev_attr.attr || >> + attr == &iio_const_attr_in6_index.dev_attr.attr)) >> + mode = 0; >> + else if (st->chip_info->num_channels <= 4 && >> + (attr == &iio_scan_el_in5.dev_attr.attr || >> + attr == &iio_const_attr_in5_index.dev_attr.attr || >> + attr == &iio_scan_el_in4.dev_attr.attr || >> + attr == &iio_const_attr_in4_index.dev_attr.attr)) >> + mode = 0; >> + >> + return mode; >> +} >> + >> +static struct attribute_group ad7606_scan_el_group = { >> + .name = "scan_elements", >> + .attrs = ad7606_scan_el_attrs, >> + .is_visible = ad7606_scan_el_attr_is_visible, >> +}; >> + >> +int ad7606_scan_from_ring(struct ad7606_state *st, unsigned ch) >> +{ >> + struct iio_ring_buffer *ring = st->indio_dev->ring; >> + int ret; >> + u16 *ring_data; >> + >> + ring_data = kmalloc(ring->access.get_bytes_per_datum(ring), GFP_KERNEL); >> + if (ring_data == NULL) { >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + ret = ring->access.read_last(ring, (u8 *) ring_data); >> + if (ret) >> + goto error_free_ring_data; >> + >> + ret = ring_data[ch]; >> + >> +error_free_ring_data: >> + kfree(ring_data); >> +error_ret: >> + return ret; >> +} >> + >> +/** >> + * ad7606_ring_preenable() setup the parameters of the ring before enabling >> + * >> + * The complex nature of the setting of the nuber of bytes per datum is due >> + * to this driver currently ensuring that the timestamp is stored at an 8 >> + * byte boundary. >> + **/ >> +static int ad7606_ring_preenable(struct iio_dev *indio_dev) >> +{ >> + struct ad7606_state *st = indio_dev->dev_data; >> + struct iio_ring_buffer *ring = indio_dev->ring; >> + size_t d_size; >> + >> + d_size = st->chip_info->num_channels * >> + st->chip_info->bits / 8 + sizeof(s64); >> + >> + if (d_size % sizeof(s64)) >> + d_size += sizeof(s64) - (d_size % sizeof(s64)); >> + >> + if (ring->access.set_bytes_per_datum) >> + ring->access.set_bytes_per_datum(ring, d_size); >> + >> + st->d_size = d_size; >> + >> + return 0; >> +} >> + >> +/** >> + * ad7606_poll_func_th() th of trigger launched polling to ring buffer >> + * >> + **/ >> +static void ad7606_poll_func_th(struct iio_dev *indio_dev, s64 time) >> +{ >> + struct ad7606_state *st = indio_dev->dev_data; >> + gpio_set_value(st->pdata->gpio_convst, 1); >> + >> + return; >> +} >> +/** >> + * ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer >> + * @work_s: the work struct through which this was scheduled >> + * >> + * Currently there is no option in this driver to disable the saving of >> + * timestamps within the ring. >> + * I think the one copy of this at a time was to avoid problems if the >> + * trigger was set far too high and the reads then locked up the computer. >> + **/ >> +static void ad7606_poll_bh_to_ring(struct work_struct *work_s) >> +{ >> + struct ad7606_state *st = container_of(work_s, struct ad7606_state, >> + poll_work); >> + struct iio_dev *indio_dev = st->indio_dev; >> + struct iio_sw_ring_buffer *sw_ring = iio_to_sw_ring(indio_dev->ring); >> + struct iio_ring_buffer *ring = indio_dev->ring; >> + s64 time_ns; >> + __u8 *buf; >> + >> + /* Ensure only one copy of this function running at a time */ >> + if (atomic_inc_return(&st->protect_ring) > 1) >> + return; >> + >> + buf = kzalloc(st->d_size, GFP_KERNEL); >> + if (buf == NULL) >> + return; >> + >> + if (st->have_frstdata) { >> > Worth adding some error handling round the read blocks? All we can > do if it happens here is not push anything into the ring, but even > that might be better than pushing garbage in. > Ok. > >> + st->bops->read_block(st->dev, 1, buf); >> + if (!gpio_get_value(st->pdata->gpio_frstdata)) { >> + /* This should never happen. However >> + * some signal glitch caused by bad PCB desgin or >> + * electrostatic discharge, could cause an extra read >> + * or clock. This allows recovery. >> + */ >> + ad7606_reset(st); >> + goto done; >> + } >> + st->bops->read_block(st->dev, >> + st->chip_info->num_channels - 1, buf + 2); >> + } else { >> + st->bops->read_block(st->dev, >> + st->chip_info->num_channels, buf); >> + } >> + >> + time_ns = iio_get_time_ns(); >> + memcpy(buf + st->d_size - sizeof(s64), &time_ns, sizeof(time_ns)); >> + >> + ring->access.store_to(&sw_ring->buf, buf, time_ns); >> +done: >> + gpio_set_value(st->pdata->gpio_convst, 0); >> + kfree(buf); >> + atomic_dec(&st->protect_ring); >> +} >> + >> +int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev) >> +{ >> + struct ad7606_state *st = indio_dev->dev_data; >> + int ret; >> + >> + indio_dev->ring = iio_sw_rb_allocate(indio_dev); >> + if (!indio_dev->ring) { >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + >> + /* Effectively select the ring buffer implementation */ >> + iio_ring_sw_register_funcs(&indio_dev->ring->access); >> > Given the pollfunc simply sets a gpio and can't sleep, it is > probably the first example we have in tree of a device that > ought to register it's function in the first of the trigger > lists rather than the second. It's for this purpose that we > have two lists in the first place (I was actualy thinking > of removing the immediate one given we didn't have any users > as yet). > > Please don't remove. >> + ret = iio_alloc_pollfunc(indio_dev, NULL, &ad7606_poll_func_th); >> + if (ret) >> + goto error_deallocate_sw_rb; >> + >> + /* Ring buffer functions - here trigger setup related */ >> + >> + indio_dev->ring->preenable = &ad7606_ring_preenable; >> + indio_dev->ring->postenable = &iio_triggered_ring_postenable; >> + indio_dev->ring->predisable = &iio_triggered_ring_predisable; >> + indio_dev->ring->scan_el_attrs = &ad7606_scan_el_group; >> + >> + INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring); >> + >> + /* Flag that polled ring buffering is possible */ >> + indio_dev->modes |= INDIO_RING_TRIGGERED; >> + return 0; >> +error_deallocate_sw_rb: >> + iio_sw_rb_free(indio_dev->ring); >> +error_ret: >> + return ret; >> +} >> + >> +void ad7606_ring_cleanup(struct iio_dev *indio_dev) >> +{ >> + if (indio_dev->trig) { >> + iio_put_trigger(indio_dev->trig); >> + iio_trigger_dettach_poll_func(indio_dev->trig, >> + indio_dev->pollfunc); >> + } >> + kfree(indio_dev->pollfunc); >> + iio_sw_rb_free(indio_dev->ring); >> +} >> diff --git a/drivers/staging/iio/adc/ad7606_spi.c b/drivers/staging/iio/adc/ad7606_spi.c >> new file mode 100644 >> index 0000000..d738491 >> --- /dev/null >> +++ b/drivers/staging/iio/adc/ad7606_spi.c >> @@ -0,0 +1,126 @@ >> +/* >> + * AD7606 SPI ADC driver >> + * >> + * Copyright 2011 Analog Devices Inc. >> + * >> + * Licensed under the GPL-2. >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include "ad7606.h" >> + >> +#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */ >> + >> +static int ad7606_spi_read_block(struct device *dev, >> + int count, void *buf) >> +{ >> + struct spi_device *spi = to_spi_device(dev); >> + int i, ret; >> + unsigned short *data = buf; >> + >> + ret = spi_read(spi, (u8 *)buf, count * 2); >> + if (ret < 0) { >> + dev_err(&spi->dev, "SPI read error\n"); >> + return ret; >> + } >> + >> + for (i = 0; i < count; i++) >> + data[i] = be16_to_cpu(data[i]); >> + >> + return 0; >> +} >> + >> +static const struct ad7606_bus_ops ad7606_spi_bops = { >> + .read_block = ad7606_spi_read_block, >> +}; >> + >> +static int __devinit ad7606_spi_probe(struct spi_device *spi) >> +{ >> + struct ad7606_state *st; >> + >> + st = ad7606_probe(&spi->dev, spi->irq, NULL, >> + spi_get_device_id(spi)->driver_data, >> + &ad7606_spi_bops); >> + >> + if (IS_ERR(st)) >> + return PTR_ERR(st); >> + >> + spi_set_drvdata(spi, st); >> + >> + return 0; >> +} >> + >> +static int __devexit ad7606_spi_remove(struct spi_device *spi) >> +{ >> + struct ad7606_state *st = dev_get_drvdata(&spi->dev); >> + >> + return ad7606_remove(st); >> +} >> + >> > Nitpick: The next few functions are the same for both buses. Could > just roll the dev_get_drvdata into the ad7606_suspend etc and get > rid of the definitions from here entirely. It doesn't really > matter and perhaps one can argue they should be here for clarity. > > I prefer the leave this as is. suspend/resume belong to the bus. >> +#ifdef CONFIG_PM >> +static int ad7606_spi_suspend(struct device *dev) >> +{ >> + struct ad7606_state *st = dev_get_drvdata(dev); >> + >> + ad7606_suspend(st); >> + >> + return 0; >> +} >> + >> +static int ad7606_spi_resume(struct device *dev) >> +{ >> + struct ad7606_state *st = dev_get_drvdata(dev); >> + >> + ad7606_resume(st); >> + >> + return 0; >> +} >> + >> +static const struct dev_pm_ops ad7606_pm_ops = { >> + .suspend = ad7606_spi_suspend, >> + .resume = ad7606_spi_resume, >> +}; >> +#define AD7606_SPI_PM_OPS (&ad7606_pm_ops) >> + >> +#else >> +#define AD7606_SPI_PM_OPS NULL >> +#endif >> + >> +static const struct spi_device_id ad7606_id[] = { >> + {"ad7606-8", ID_AD7606_8}, >> + {"ad7606-6", ID_AD7606_6}, >> + {"ad7606-4", ID_AD7606_4}, >> + {} >> +}; >> + >> +static struct spi_driver ad7606_driver = { >> + .driver = { >> + .name = "ad7606", >> + .bus = &spi_bus_type, >> + .owner = THIS_MODULE, >> + .pm = AD7606_SPI_PM_OPS, >> + }, >> + .probe = ad7606_spi_probe, >> + .remove = __devexit_p(ad7606_spi_remove), >> + .id_table = ad7606_id, >> +}; >> + >> +static int __init ad7606_spi_init(void) >> +{ >> + return spi_register_driver(&ad7606_driver); >> +} >> +module_init(ad7606_spi_init); >> + >> +static void __exit ad7606_spi_exit(void) >> +{ >> + spi_unregister_driver(&ad7606_driver); >> +} >> +module_exit(ad7606_spi_exit); >> + >> +MODULE_AUTHOR("Michael Hennerich "); >> +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); >> +MODULE_LICENSE("GPL v2"); >> +MODULE_ALIAS("spi:ad7606_spi"); >> -- >> 1.6.0.2 >> >> >> > -- Greetings, Michael -- Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368; Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin, Margaret Seif