* [PATCH 1/3] IIO: Direct digital synthesis abi documentation
@ 2010-12-14 14:54 michael.hennerich
2010-12-14 14:54 ` [PATCH 2/3] IIO: dds.h convenience macros michael.hennerich
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: michael.hennerich @ 2010-12-14 14:54 UTC (permalink / raw)
To: linux-iio, jic23
Cc: linux-kernel, drivers, device-drivers-devel, Michael Hennerich
From: Michael Hennerich <michael.hennerich@analog.com>
Changes since RFC/v1:
IIO: Apply list review feedback:
Apply list review feedback:
Restructure documentation according to list feedback.
Rename attributes to fit IIO convention used in other drivers.
Fix typos.
Provide ddsX_out_enable as opposed to ddsX_out_disable
Changes since v2:
IIO: sysfs-bus-iio-dds: Apply more list review feedback
Fix mixture of X and [n] notation for indices.
Fix some wording in the descriptions.
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk>
---
.../staging/iio/Documentation/sysfs-bus-iio-dds | 93 ++++++++++++++++++++
1 files changed, 93 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-dds
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-dds b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds
new file mode 100644
index 0000000..ffdd547
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds
@@ -0,0 +1,93 @@
+
+What: /sys/bus/iio/devices/.../ddsX_freqY
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Stores frequency into tuning word Y.
+ There will be more than one ddsX_freqY file, which allows for
+ pin controlled FSK Frequency Shift Keying
+ (ddsX_pincontrol_freq_en is active) or the user can control
+ the desired active tuning word by writing Y to the
+ ddsX_freqsymbol file.
+
+What: /sys/bus/iio/devices/.../ddsX_freqY_scale
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Scale to be applied to ddsX_freqY in order to obtain the
+ desired value in Hz. If shared across all frequency registers
+ Y is not present. It is also possible X is not present if
+ shared across all channels.
+
+What: /sys/bus/iio/devices/.../ddsX_freqsymbol
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Specifies the active output frequency tuning word. The value
+ corresponds to the Y in ddsX_freqY. To exit this mode the user
+ can write ddsX_pincontrol_freq_en or ddsX_out_enable file.
+
+What: /sys/bus/iio/devices/.../ddsX_phaseY
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Stores phase into Y.
+ There will be more than one ddsX_phaseY file, which allows for
+ pin controlled PSK Phase Shift Keying
+ (ddsX_pincontrol_phase_en is active) or the user can
+ control the desired phase Y which is added to the phase
+ accumulator output by writing Y to the en_phase file.
+
+What: /sys/bus/iio/devices/.../ddsX_phaseY_scale
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Scale to be applied to ddsX_phaseY in order to obtain the
+ desired value in rad. If shared across all phase registers
+ Y is not present. It is also possible X is not present if
+ shared across all channels.
+
+What: /sys/bus/iio/devices/.../ddsX_phasesymbol
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Specifies the active phase Y which is added to the phase
+ accumulator output. The value corresponds to the Y in
+ ddsX_phaseY. To exit this mode the user can write
+ ddsX_pincontrol_phase_en or disable file.
+
+What: /sys/bus/iio/devices/.../ddsX_pincontrol_en
+What: /sys/bus/iio/devices/.../ddsX_pincontrol_freq_en
+What: /sys/bus/iio/devices/.../ddsX_pincontrol_phase_en
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ ddsX_pincontrol_en: Both, the active frequency and phase is
+ controlled by the respective phase and frequency control inputs.
+ In case the device in question allows to independent controls,
+ then there are dedicated files (ddsX_pincontrol_freq_en,
+ ddsX_pincontrol_phase_en).
+
+What: /sys/bus/iio/devices/.../ddsX_out_enable
+What: /sys/bus/iio/devices/.../ddsX_outY_enable
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ ddsX_outY_enable controls signal generation on output Y of
+ channel X. Y may be suppressed if all channels are
+ controlled together.
+
+What: /sys/bus/iio/devices/.../ddsX_outY_wavetype
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Specifies the output waveform.
+ (sine, triangle, ramp, square, ...)
+ For a list of available output waveform options read
+ available_output_modes.
+
+What: /sys/bus/iio/devices/.../ddsX_outY_wavetype_available
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Lists all available output waveform options.
--
1.6.0.2
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 2/3] IIO: dds.h convenience macros 2010-12-14 14:54 [PATCH 1/3] IIO: Direct digital synthesis abi documentation michael.hennerich @ 2010-12-14 14:54 ` michael.hennerich 2010-12-14 15:10 ` Jonathan Cameron 2010-12-14 14:54 ` [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver michael.hennerich 2010-12-14 15:09 ` [PATCH 1/3] IIO: Direct digital synthesis abi documentation Jonathan Cameron 2 siblings, 1 reply; 9+ messages in thread From: michael.hennerich @ 2010-12-14 14:54 UTC (permalink / raw) To: linux-iio, jic23 Cc: linux-kernel, drivers, device-drivers-devel, Michael Hennerich From: Michael Hennerich <michael.hennerich@analog.com> Changes since RFC/v1: IIO: Apply list review feedback Apply list review feedback: Rename attributes to fit IIO convention used in other drivers. Provide ddsX_out_enable as opposed to ddsX_out_disable. Fix typos. Changes since v2: IIO: dds.h: Apply more list review feedback Fix mixture of X and [n] notation for indices. Remove duplicated descriptions. Add store methods to some dds convenience macros. Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk> --- drivers/staging/iio/dds/dds.h | 110 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 110 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/dds/dds.h diff --git a/drivers/staging/iio/dds/dds.h b/drivers/staging/iio/dds/dds.h new file mode 100644 index 0000000..d8ac3a9 --- /dev/null +++ b/drivers/staging/iio/dds/dds.h @@ -0,0 +1,110 @@ +/* + * dds.h - sysfs attributes associated with DDS devices + * + * Copyright (c) 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +/** + * /sys/bus/iio/devices/.../ddsX_freqY + */ + +#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(dds##_channel##_freq##_num, \ + _mode, _show, _store, _addr) + +/** + * /sys/bus/iio/devices/.../ddsX_freqY_scale + */ + +#define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string) \ + IIO_CONST_ATTR(dds##_channel##_freq_scale, _string) + +/** + * /sys/bus/iio/devices/.../ddsX_freqsymbol + */ + +#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(dds##_channel##_freqsymbol, \ + _mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_phaseY + */ + +#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(dds##_channel##_phase##_num, \ + _mode, _show, _store, _addr) + +/** + * /sys/bus/iio/devices/.../ddsX_phaseY_scale + */ + +#define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string) \ + IIO_CONST_ATTR(dds##_channel##_phase_scale, _string) + +/** + * /sys/bus/iio/devices/.../ddsX_phasesymbol + */ + +#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(dds##_channel##_phasesymbol, \ + _mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_pincontrol_en + */ + +#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\ + IIO_DEVICE_ATTR(dds##_channel##_pincontrol_en, \ + _mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_pincontrol_freq_en + */ + +#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\ + IIO_DEVICE_ATTR(dds##_channel##_pincontrol_freq_en, \ + _mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_pincontrol_phase_en + */ + +#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\ + IIO_DEVICE_ATTR(dds##_channel##_pincontrol_phase_en, \ + _mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_out_enable + */ + +#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(dds##_channel##_out_enable, \ + _mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_outY_enable + */ + +#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \ + _mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(dds##_channel##_out##_output##_enable, \ + _mode, _show, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_outY_wavetype + */ + +#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \ + IIO_DEVICE_ATTR(dds##_channel##_out##_output##_wavetype, \ + S_IWUSR, NULL, _store, _addr); + +/** + * /sys/bus/iio/devices/.../ddsX_outY_wavetype_available + */ + +#define IIO_CONST_ATTR_OUT_WAVETYPES_AVAILABLE(_channel, _output, _modes)\ + IIO_CONST_ATTR(dds##_channel##_out##_output##_wavetype_available,\ + _modes); -- 1.6.0.2 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/3] IIO: dds.h convenience macros 2010-12-14 14:54 ` [PATCH 2/3] IIO: dds.h convenience macros michael.hennerich @ 2010-12-14 15:10 ` Jonathan Cameron 0 siblings, 0 replies; 9+ messages in thread From: Jonathan Cameron @ 2010-12-14 15:10 UTC (permalink / raw) To: michael.hennerich; +Cc: linux-iio, linux-kernel, drivers, device-drivers-devel On 12/14/10 14:54, michael.hennerich@analog.com wrote: > From: Michael Hennerich <michael.hennerich@analog.com> > > Changes since RFC/v1: > IIO: Apply list review feedback > > Apply list review feedback: > Rename attributes to fit IIO convention used in other drivers. > Provide ddsX_out_enable as opposed to ddsX_out_disable. > Fix typos. > > Changes since v2: > IIO: dds.h: Apply more list review feedback > > Fix mixture of X and [n] notation for indices. > Remove duplicated descriptions. > Add store methods to some dds convenience macros. > > Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > drivers/staging/iio/dds/dds.h | 110 +++++++++++++++++++++++++++++++++++++++++ > 1 files changed, 110 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/dds/dds.h > > diff --git a/drivers/staging/iio/dds/dds.h b/drivers/staging/iio/dds/dds.h > new file mode 100644 > index 0000000..d8ac3a9 > --- /dev/null > +++ b/drivers/staging/iio/dds/dds.h > @@ -0,0 +1,110 @@ > +/* > + * dds.h - sysfs attributes associated with DDS devices > + * > + * Copyright (c) 2010 Analog Devices Inc. > + * > + * Licensed under the GPL-2 or later. > + */ > + > +/** > + * /sys/bus/iio/devices/.../ddsX_freqY > + */ > + > +#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \ > + IIO_DEVICE_ATTR(dds##_channel##_freq##_num, \ > + _mode, _show, _store, _addr) > + > +/** > + * /sys/bus/iio/devices/.../ddsX_freqY_scale > + */ > + > +#define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string) \ > + IIO_CONST_ATTR(dds##_channel##_freq_scale, _string) > + > +/** > + * /sys/bus/iio/devices/.../ddsX_freqsymbol > + */ > + > +#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \ > + IIO_DEVICE_ATTR(dds##_channel##_freqsymbol, \ > + _mode, _show, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_phaseY > + */ > + > +#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \ > + IIO_DEVICE_ATTR(dds##_channel##_phase##_num, \ > + _mode, _show, _store, _addr) > + > +/** > + * /sys/bus/iio/devices/.../ddsX_phaseY_scale > + */ > + > +#define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string) \ > + IIO_CONST_ATTR(dds##_channel##_phase_scale, _string) > + > +/** > + * /sys/bus/iio/devices/.../ddsX_phasesymbol > + */ > + > +#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \ > + IIO_DEVICE_ATTR(dds##_channel##_phasesymbol, \ > + _mode, _show, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_pincontrol_en > + */ > + > +#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\ > + IIO_DEVICE_ATTR(dds##_channel##_pincontrol_en, \ > + _mode, _show, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_pincontrol_freq_en > + */ > + > +#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\ > + IIO_DEVICE_ATTR(dds##_channel##_pincontrol_freq_en, \ > + _mode, _show, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_pincontrol_phase_en > + */ > + > +#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\ > + IIO_DEVICE_ATTR(dds##_channel##_pincontrol_phase_en, \ > + _mode, _show, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_out_enable > + */ > + > +#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \ > + IIO_DEVICE_ATTR(dds##_channel##_out_enable, \ > + _mode, _show, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_outY_enable > + */ > + > +#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \ > + _mode, _show, _store, _addr) \ > + IIO_DEVICE_ATTR(dds##_channel##_out##_output##_enable, \ > + _mode, _show, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_outY_wavetype > + */ > + > +#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \ > + IIO_DEVICE_ATTR(dds##_channel##_out##_output##_wavetype, \ > + S_IWUSR, NULL, _store, _addr); > + > +/** > + * /sys/bus/iio/devices/.../ddsX_outY_wavetype_available > + */ > + > +#define IIO_CONST_ATTR_OUT_WAVETYPES_AVAILABLE(_channel, _output, _modes)\ > + IIO_CONST_ATTR(dds##_channel##_out##_output##_wavetype_available,\ > + _modes); > -- > 1.6.0.2 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver 2010-12-14 14:54 [PATCH 1/3] IIO: Direct digital synthesis abi documentation michael.hennerich 2010-12-14 14:54 ` [PATCH 2/3] IIO: dds.h convenience macros michael.hennerich @ 2010-12-14 14:54 ` michael.hennerich 2010-12-14 15:09 ` [PATCH 1/3] IIO: Direct digital synthesis abi documentation Jonathan Cameron 2 siblings, 0 replies; 9+ messages in thread From: michael.hennerich @ 2010-12-14 14:54 UTC (permalink / raw) To: linux-iio, jic23 Cc: linux-kernel, drivers, device-drivers-devel, Michael Hennerich From: Michael Hennerich <michael.hennerich@analog.com> Changes since RFC/v1: IIO: Apply list review feedback Apply list review feedback: Rename attributes to fit IIO convention used in other drivers. Fix typos. Provide ddsX_out_enable as opposed to ddsX_out_disable. Use proper __devexit marking. Use strict_strtoul() to avoid negatives. Changes since v2: IIO: ad9834.c: Apply more list review feedback Update use of dds convenience macros. Fix tabbing. Remove superfluous brackets. Keep output disabled after probe. Remove unnecessary code. Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Reviewed-by: Datta Shubhrajyoti <shubhrajyoti@ti.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> --- drivers/staging/iio/dds/Kconfig | 10 + drivers/staging/iio/dds/Makefile | 1 + drivers/staging/iio/dds/ad9834.c | 477 ++++++++++++++++++++++++++++++++++++++ drivers/staging/iio/dds/ad9834.h | 112 +++++++++ 4 files changed, 600 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/dds/ad9834.c create mode 100644 drivers/staging/iio/dds/ad9834.h diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig index 7969be2..a047da6 100644 --- a/drivers/staging/iio/dds/Kconfig +++ b/drivers/staging/iio/dds/Kconfig @@ -17,6 +17,16 @@ config AD9832 Say yes here to build support for Analog Devices DDS chip ad9832 and ad9835, provides direct access via sysfs. +config AD9834 + tristate "Analog Devices ad9833/4/ driver" + depends on SPI + help + Say yes here to build support for Analog Devices DDS chip + AD9833 and AD9834, provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called ad9834. + config AD9850 tristate "Analog Devices ad9850/1 driver" depends on SPI diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile index 6f274ac..1477461 100644 --- a/drivers/staging/iio/dds/Makefile +++ b/drivers/staging/iio/dds/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_AD5930) += ad5930.o obj-$(CONFIG_AD9832) += ad9832.o +obj-$(CONFIG_AD9834) += ad9834.o obj-$(CONFIG_AD9850) += ad9850.o obj-$(CONFIG_AD9852) += ad9852.o obj-$(CONFIG_AD9910) += ad9910.o diff --git a/drivers/staging/iio/dds/ad9834.c b/drivers/staging/iio/dds/ad9834.c new file mode 100644 index 0000000..eb1a681 --- /dev/null +++ b/drivers/staging/iio/dds/ad9834.c @@ -0,0 +1,477 @@ +/* + * AD9834 SPI DAC driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <asm/div64.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "dds.h" + +#include "ad9834.h" + +static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout) +{ + unsigned long long freqreg = (u64) fout * (u64) (1 << AD9834_FREQ_BITS); + do_div(freqreg, mclk); + return freqreg; +} + +static int ad9834_write_frequency(struct ad9834_state *st, + unsigned long addr, unsigned long fout) +{ + unsigned long regval; + + if (fout > (st->mclk / 2)) + return -EINVAL; + + regval = ad9834_calc_freqreg(st->mclk, fout); + + st->freq_data[0] = cpu_to_be16(addr | (regval & + RES_MASK(AD9834_FREQ_BITS / 2))); + st->freq_data[1] = cpu_to_be16(addr | ((regval >> + (AD9834_FREQ_BITS / 2)) & + RES_MASK(AD9834_FREQ_BITS / 2))); + + return spi_sync(st->spi, &st->freq_msg);; +} + +static int ad9834_write_phase(struct ad9834_state *st, + unsigned long addr, unsigned long phase) +{ + if (phase > (1 << AD9834_PHASE_BITS)) + return -EINVAL; + st->data = cpu_to_be16(addr | phase); + + return spi_sync(st->spi, &st->msg); +} + +static ssize_t ad9834_write(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = dev_info->dev_data; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + goto error_ret; + + mutex_lock(&dev_info->mlock); + switch (this_attr->address) { + case AD9834_REG_FREQ0: + case AD9834_REG_FREQ1: + ret = ad9834_write_frequency(st, this_attr->address, val); + break; + case AD9834_REG_PHASE0: + case AD9834_REG_PHASE1: + ret = ad9834_write_phase(st, this_attr->address, val); + break; + case AD9834_OPBITEN: + if (st->control & AD9834_MODE) { + ret = -EINVAL; /* AD9843 reserved mode */ + break; + } + + if (val) + st->control |= AD9834_OPBITEN; + else + st->control &= ~AD9834_OPBITEN; + + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + case AD9834_PIN_SW: + if (val) + st->control |= AD9834_PIN_SW; + else + st->control &= ~AD9834_PIN_SW; + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + case AD9834_FSEL: + case AD9834_PSEL: + if (val == 0) + st->control &= ~(this_attr->address | AD9834_PIN_SW); + else if (val == 1) { + st->control |= this_attr->address; + st->control &= ~AD9834_PIN_SW; + } else { + ret = -EINVAL; + break; + } + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + case AD9834_RESET: + if (val) + st->control &= ~AD9834_RESET; + else + st->control |= AD9834_RESET; + + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + default: + ret = -ENODEV; + } + mutex_unlock(&dev_info->mlock); + +error_ret: + return ret ? ret : len; +} + +static ssize_t ad9834_store_wavetype(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = dev_info->dev_data; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret = 0; + bool is_ad9833 = st->devid == ID_AD9833; + + mutex_lock(&dev_info->mlock); + + switch (this_attr->address) { + case 0: + if (sysfs_streq(buf, "sine")) { + st->control &= ~AD9834_MODE; + if (is_ad9833) + st->control &= ~AD9834_OPBITEN; + } else if (sysfs_streq(buf, "triangle")) { + if (is_ad9833) { + st->control &= ~AD9834_OPBITEN; + st->control |= AD9834_MODE; + } else if (st->control & AD9834_OPBITEN) { + ret = -EINVAL; /* AD9843 reserved mode */ + } else { + st->control |= AD9834_MODE; + } + } else if (is_ad9833 && sysfs_streq(buf, "square")) { + st->control &= ~AD9834_MODE; + st->control |= AD9834_OPBITEN; + } else { + ret = -EINVAL; + } + + break; + case 1: + if (sysfs_streq(buf, "square") && + !(st->control & AD9834_MODE)) { + st->control &= ~AD9834_MODE; + st->control |= AD9834_OPBITEN; + } else { + ret = -EINVAL; + } + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) { + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + } + mutex_unlock(&dev_info->mlock); + + return ret ? ret : len; +} + +static ssize_t ad9834_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = iio_dev_get_devdata(dev_info); + + return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name); +} +static IIO_DEVICE_ATTR(name, S_IRUGO, ad9834_show_name, NULL, 0); + +static ssize_t ad9834_show_out0_wavetype_available(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = iio_dev_get_devdata(dev_info); + char *str; + + if (st->devid == ID_AD9833) + str = "sine triangle square"; + else if (st->control & AD9834_OPBITEN) + str = "sine"; + else + str = "sine triangle"; + + return sprintf(buf, "%s\n", str); +} + + +static IIO_DEVICE_ATTR(dds0_out0_wavetype_available, S_IRUGO, + ad9834_show_out0_wavetype_available, NULL, 0); + +static ssize_t ad9834_show_out1_wavetype_available(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = iio_dev_get_devdata(dev_info); + char *str; + + if (st->control & AD9834_MODE) + str = ""; + else + str = "square"; + + return sprintf(buf, "%s\n", str); +} + +static IIO_DEVICE_ATTR(dds0_out1_wavetype_available, S_IRUGO, + ad9834_show_out1_wavetype_available, NULL, 0); + +/** + * see dds.h for further information + */ + +static IIO_DEV_ATTR_FREQ(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ0); +static IIO_DEV_ATTR_FREQ(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ1); +static IIO_DEV_ATTR_FREQSYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_FSEL); +static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ + +static IIO_DEV_ATTR_PHASE(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE0); +static IIO_DEV_ATTR_PHASE(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE1); +static IIO_DEV_ATTR_PHASESYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_PSEL); +static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ + +static IIO_DEV_ATTR_PINCONTROL_EN(0, S_IWUSR, NULL, + ad9834_write, AD9834_PIN_SW); +static IIO_DEV_ATTR_OUT_ENABLE(0, S_IWUSR, NULL, ad9834_write, AD9834_RESET); +static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, S_IWUSR, NULL, + ad9834_write, AD9834_OPBITEN); +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); + +static struct attribute *ad9834_attributes[] = { + &iio_dev_attr_dds0_freq0.dev_attr.attr, + &iio_dev_attr_dds0_freq1.dev_attr.attr, + &iio_const_attr_dds0_freq_scale.dev_attr.attr, + &iio_dev_attr_dds0_phase0.dev_attr.attr, + &iio_dev_attr_dds0_phase1.dev_attr.attr, + &iio_const_attr_dds0_phase_scale.dev_attr.attr, + &iio_dev_attr_dds0_pincontrol_en.dev_attr.attr, + &iio_dev_attr_dds0_freqsymbol.dev_attr.attr, + &iio_dev_attr_dds0_phasesymbol.dev_attr.attr, + &iio_dev_attr_dds0_out_enable.dev_attr.attr, + &iio_dev_attr_dds0_out1_enable.dev_attr.attr, + &iio_dev_attr_dds0_out0_wavetype.dev_attr.attr, + &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr, + &iio_dev_attr_dds0_out0_wavetype_available.dev_attr.attr, + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + NULL, +}; + +static mode_t ad9834_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 ad9834_state *st = iio_dev_get_devdata(dev_info); + + mode_t mode = attr->mode; + + if (st->devid == ID_AD9834) + return mode; + + if ((attr == &iio_dev_attr_dds0_out1_enable.dev_attr.attr) || + (attr == &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr) || + (attr == + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr)) + mode = 0; + + return mode; +} + +static const struct attribute_group ad9834_attribute_group = { + .attrs = ad9834_attributes, + .is_visible = ad9834_attr_is_visible, +}; + +static int __devinit ad9834_probe(struct spi_device *spi) +{ + struct ad9834_platform_data *pdata = spi->dev.platform_data; + struct ad9834_state *st; + int ret; + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + st->mclk = pdata->mclk; + + spi_set_drvdata(spi, st); + + st->spi = spi; + st->devid = spi_get_device_id(spi)->driver_data; + + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_disable_reg; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->attrs = &ad9834_attribute_group; + st->indio_dev->dev_data = (void *) st; + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + /* Setup default messages */ + + st->xfer.tx_buf = &st->data; + st->xfer.len = 2; + + spi_message_init(&st->msg); + spi_message_add_tail(&st->xfer, &st->msg); + + st->freq_xfer[0].tx_buf = &st->freq_data[0]; + st->freq_xfer[0].len = 2; + st->freq_xfer[0].cs_change = 1; + st->freq_xfer[1].tx_buf = &st->freq_data[1]; + st->freq_xfer[1].len = 2; + + spi_message_init(&st->freq_msg); + spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); + spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); + + st->control = AD9834_B28 | AD9834_RESET; + + if (!pdata->en_div2) + st->control |= AD9834_DIV2; + + if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) + st->control |= AD9834_SIGN_PIB; + + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + if (ret) { + dev_err(&spi->dev, "device init failed\n"); + goto error_free_device; + } + + ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); + if (ret) + goto error_free_device; + + ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); + if (ret) + goto error_free_device; + + ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); + if (ret) + goto error_free_device; + + ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); + if (ret) + goto error_free_device; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_free_device; + + return 0; + +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 ret; +} + +static int __devexit ad9834_remove(struct spi_device *spi) +{ + struct ad9834_state *st = spi_get_drvdata(spi); + + iio_device_unregister(st->indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + kfree(st); + return 0; +} + +static const struct spi_device_id ad9834_id[] = { + {"ad9833", ID_AD9833}, + {"ad9834", ID_AD9834}, + {} +}; + +static struct spi_driver ad9834_driver = { + .driver = { + .name = "ad9834", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad9834_probe, + .remove = __devexit_p(ad9834_remove), + .id_table = ad9834_id, +}; + +static int __init ad9834_init(void) +{ + return spi_register_driver(&ad9834_driver); +} +module_init(ad9834_init); + +static void __exit ad9834_exit(void) +{ + spi_unregister_driver(&ad9834_driver); +} +module_exit(ad9834_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD9833/AD9834 DDS"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:ad9834"); diff --git a/drivers/staging/iio/dds/ad9834.h b/drivers/staging/iio/dds/ad9834.h new file mode 100644 index 0000000..0fc3b88 --- /dev/null +++ b/drivers/staging/iio/dds/ad9834.h @@ -0,0 +1,112 @@ +/* + * AD9834 SPI DDS driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_DDS_AD9834_H_ +#define IIO_DDS_AD9834_H_ + +/* Registers */ + +#define AD9834_REG_CMD (0 << 14) +#define AD9834_REG_FREQ0 (1 << 14) +#define AD9834_REG_FREQ1 (2 << 14) +#define AD9834_REG_PHASE0 (6 << 13) +#define AD9834_REG_PHASE1 (7 << 13) + +/* Command Control Bits */ + +#define AD9834_B28 (1 << 13) +#define AD9834_HLB (1 << 12) +#define AD9834_FSEL (1 << 11) +#define AD9834_PSEL (1 << 10) +#define AD9834_PIN_SW (1 << 9) +#define AD9834_RESET (1 << 8) +#define AD9834_SLEEP1 (1 << 7) +#define AD9834_SLEEP12 (1 << 6) +#define AD9834_OPBITEN (1 << 5) +#define AD9834_SIGN_PIB (1 << 4) +#define AD9834_DIV2 (1 << 3) +#define AD9834_MODE (1 << 1) + +#define AD9834_FREQ_BITS 28 +#define AD9834_PHASE_BITS 12 + +#define RES_MASK(bits) ((1 << (bits)) - 1) + +/** + * struct ad9834_state - driver instance specific data + * @indio_dev: the industrial I/O device + * @spi: spi_device + * @reg: supply regulator + * @mclk: external master clock + * @control: cached control word + * @xfer: default spi transfer + * @msg: default spi message + * @freq_xfer: tuning word spi transfer + * @freq_msg: tuning word spi message + * @data: spi transmit buffer + * @freq_data: tuning word spi transmit buffer + */ + +struct ad9834_state { + struct iio_dev *indio_dev; + struct spi_device *spi; + struct regulator *reg; + unsigned int mclk; + unsigned short control; + unsigned short devid; + struct spi_transfer xfer; + struct spi_message msg; + struct spi_transfer freq_xfer[2]; + struct spi_message freq_msg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + unsigned short data ____cacheline_aligned; + unsigned short freq_data[2] ; +}; + + +/* + * TODO: struct ad7887_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad9834_platform_data - platform specific information + * @mclk: master clock in Hz + * @freq0: power up freq0 tuning word in Hz + * @freq1: power up freq1 tuning word in Hz + * @phase0: power up phase0 value [0..4095] correlates with 0..2PI + * @phase1: power up phase1 value [0..4095] correlates with 0..2PI + * @en_div2: digital output/2 is passed to the SIGN BIT OUT pin + * @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is connected to the + * SIGN BIT OUT pin. en_div2 controls whether it is the MSB + * or MSB/2 that is output. if en_signbit_msb_out=false, + * the on-board comparator is connected to SIGN BIT OUT + */ + +struct ad9834_platform_data { + unsigned int mclk; + unsigned int freq0; + unsigned int freq1; + unsigned short phase0; + unsigned short phase1; + bool en_div2; + bool en_signbit_msb_out; +}; + +/** + * ad9834_supported_device_ids: + */ + +enum ad9834_supported_device_ids { + ID_AD9833, + ID_AD9834, +}; + +#endif /* IIO_DDS_AD9834_H_ */ -- 1.6.0.2 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] IIO: Direct digital synthesis abi documentation 2010-12-14 14:54 [PATCH 1/3] IIO: Direct digital synthesis abi documentation michael.hennerich 2010-12-14 14:54 ` [PATCH 2/3] IIO: dds.h convenience macros michael.hennerich 2010-12-14 14:54 ` [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver michael.hennerich @ 2010-12-14 15:09 ` Jonathan Cameron 2 siblings, 0 replies; 9+ messages in thread From: Jonathan Cameron @ 2010-12-14 15:09 UTC (permalink / raw) To: michael.hennerich; +Cc: linux-iio, linux-kernel, drivers, device-drivers-devel On 12/14/10 14:54, michael.hennerich@analog.com wrote: > From: Michael Hennerich <michael.hennerich@analog.com> > > Changes since RFC/v1: > IIO: Apply list review feedback: > > Apply list review feedback: > Restructure documentation according to list feedback. > Rename attributes to fit IIO convention used in other drivers. > Fix typos. > Provide ddsX_out_enable as opposed to ddsX_out_disable > > Changes since v2: > IIO: sysfs-bus-iio-dds: Apply more list review feedback > > Fix mixture of X and [n] notation for indices. > Fix some wording in the descriptions. Looks good. > Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > .../staging/iio/Documentation/sysfs-bus-iio-dds | 93 ++++++++++++++++++++ > 1 files changed, 93 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-dds > > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-dds b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds > new file mode 100644 > index 0000000..ffdd547 > --- /dev/null > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds > @@ -0,0 +1,93 @@ > + > +What: /sys/bus/iio/devices/.../ddsX_freqY > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Stores frequency into tuning word Y. > + There will be more than one ddsX_freqY file, which allows for > + pin controlled FSK Frequency Shift Keying > + (ddsX_pincontrol_freq_en is active) or the user can control > + the desired active tuning word by writing Y to the > + ddsX_freqsymbol file. > + > +What: /sys/bus/iio/devices/.../ddsX_freqY_scale > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Scale to be applied to ddsX_freqY in order to obtain the > + desired value in Hz. If shared across all frequency registers > + Y is not present. It is also possible X is not present if > + shared across all channels. > + > +What: /sys/bus/iio/devices/.../ddsX_freqsymbol > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Specifies the active output frequency tuning word. The value > + corresponds to the Y in ddsX_freqY. To exit this mode the user > + can write ddsX_pincontrol_freq_en or ddsX_out_enable file. > + > +What: /sys/bus/iio/devices/.../ddsX_phaseY > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Stores phase into Y. > + There will be more than one ddsX_phaseY file, which allows for > + pin controlled PSK Phase Shift Keying > + (ddsX_pincontrol_phase_en is active) or the user can > + control the desired phase Y which is added to the phase > + accumulator output by writing Y to the en_phase file. > + > +What: /sys/bus/iio/devices/.../ddsX_phaseY_scale > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Scale to be applied to ddsX_phaseY in order to obtain the > + desired value in rad. If shared across all phase registers > + Y is not present. It is also possible X is not present if > + shared across all channels. > + > +What: /sys/bus/iio/devices/.../ddsX_phasesymbol > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Specifies the active phase Y which is added to the phase > + accumulator output. The value corresponds to the Y in > + ddsX_phaseY. To exit this mode the user can write > + ddsX_pincontrol_phase_en or disable file. > + > +What: /sys/bus/iio/devices/.../ddsX_pincontrol_en > +What: /sys/bus/iio/devices/.../ddsX_pincontrol_freq_en > +What: /sys/bus/iio/devices/.../ddsX_pincontrol_phase_en > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + ddsX_pincontrol_en: Both, the active frequency and phase is > + controlled by the respective phase and frequency control inputs. > + In case the device in question allows to independent controls, > + then there are dedicated files (ddsX_pincontrol_freq_en, > + ddsX_pincontrol_phase_en). > + > +What: /sys/bus/iio/devices/.../ddsX_out_enable > +What: /sys/bus/iio/devices/.../ddsX_outY_enable > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + ddsX_outY_enable controls signal generation on output Y of > + channel X. Y may be suppressed if all channels are > + controlled together. > + > +What: /sys/bus/iio/devices/.../ddsX_outY_wavetype > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Specifies the output waveform. > + (sine, triangle, ramp, square, ...) > + For a list of available output waveform options read > + available_output_modes. > + > +What: /sys/bus/iio/devices/.../ddsX_outY_wavetype_available > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + Lists all available output waveform options. > -- > 1.6.0.2 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] IIO: Direct digital synthesis abi documentation @ 2010-12-13 16:27 michael.hennerich 2010-12-13 16:27 ` [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver michael.hennerich 0 siblings, 1 reply; 9+ messages in thread From: michael.hennerich @ 2010-12-13 16:27 UTC (permalink / raw) To: linux-iio, jic23 Cc: linux-kernel, drivers, device-drivers-devel, Michael Hennerich From: Michael Hennerich <michael.hennerich@analog.com> Changes since RFC/v1: IIO: Apply list review feedback: Apply list review feedback: Restructure documentation according to list feedback. Rename attributes to fit IIO convention used in other drivers. Fix typos. Provide ddsX_out_enable as opposed to ddsX_out_disable Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk> --- .../staging/iio/Documentation/sysfs-bus-iio-dds | 94 ++++++++++++++++++++ 1 files changed, 94 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-dds diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-dds b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds new file mode 100644 index 0000000..6791174 --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds @@ -0,0 +1,94 @@ + +What: /sys/bus/iio/devices/device[n]/ddsX_freqY +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Stores frequency into tuning word register Y. + There can be more than one ddsX_freqY file, which allows for + pin controlled FSK Frequency Shift Keying + (ddsX_pincontrol_freq_en is active) or the user can control + the desired active tuning word by writing Y to the + ddsX_freqsymbol file. + +What: /sys/bus/iio/devices/device[n]/ddsX_freqY_scale +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Scale to be applied to ddsX_freqY in order to obtain the + desired value in Hz. If shared across all frequency registers + Y is not present. It is also possible X is not present if + shared across all channels. + +What: /sys/bus/iio/devices/device[n]/ddsX_freqsymbol +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the active output frequency tuning word. The value + corresponds to the Y in ddsX_freqY. To exit this mode the user + can write ddsX_pincontrol_freq_en or ddsX_out_disable file. + +What: /sys/bus/iio/devices/device[n]/ddsX_phaseY +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Stores phase into phase register Y. + There can be more than one ddsX_phaseY file, which allows for + pin controlled PSK Phase Shift Keying + (ddsX_pincontrol_phase_en is active) or the user can + control the desired phase Y which is added to the phase + accumulator output by writing Y to the en_phase file. + +What: /sys/bus/iio/devices/device[n]/ddsX_phaseY_scale +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Scale to be applied to ddsX_phaseY in order to obtain the + desired value in rad. If shared across all phase registers + Y is not present. It is also possible X is not present if + shared across all channels. + +What: /sys/bus/iio/devices/device[n]/ddsX_phasesymbol +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the active phase Y which is added to the phase + accumulator output. The value corresponds to the Y in + ddsX_phaseY. To exit this mode the user can write + ddsX_pincontrol_phase_en or disable file. + +What: /sys/bus/iio/devices/device[n]/ddsX_pincontrol_en +What: /sys/bus/iio/devices/device[n]/ddsX_pincontrol_freq_en +What: /sys/bus/iio/devices/device[n]/ddsX_pincontrol_phase_en +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + ddsX_pincontrol_en: Both, the active frequency and phase is + controlled by the respective phase and frequency control inputs. + In case the device in question allows to independent controls, + then there are dedicated files (ddsX_pincontrol_freq_en, + ddsX_pincontrol_phase_en). + +What: /sys/bus/iio/devices/device[n]/ddsX_out_enable +What: /sys/bus/iio/devices/device[n]/ddsX_outY_enable +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + ddsX_out_enable: Enables signal generation on all outputs + of channel X. + ddsX_outY_enable: Enables signal generation on output Y, + of channel X. + +What: /sys/bus/iio/devices/device[n]/ddsX_outY_wavetype +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the output waveform. + (sine, triangle, ramp, square, ...) + For a list of available output waveform options read + available_output_modes. + +What: /sys/bus/iio/devices/device[n]/ddsX_outY_wavetype_available +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + Lists all available output waveform options. -- 1.6.0.2 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver 2010-12-13 16:27 michael.hennerich @ 2010-12-13 16:27 ` michael.hennerich 2010-12-13 21:40 ` Jonathan Cameron 0 siblings, 1 reply; 9+ messages in thread From: michael.hennerich @ 2010-12-13 16:27 UTC (permalink / raw) To: linux-iio, jic23 Cc: linux-kernel, drivers, device-drivers-devel, Michael Hennerich From: Michael Hennerich <michael.hennerich@analog.com> Changes since RFC/v1: IIO: Apply list review feedback Apply list review feedback: Rename attributes to fit IIO convention used in other drivers. Fix typos. Provide ddsX_out_enable as opposed to ddsX_out_disable. Use proper __devexit marking. Use strict_strtoul() to avoid negatives. Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Reviewed-by: Datta Shubhrajyoti <shubhrajyoti@ti.com> --- drivers/staging/iio/dds/Kconfig | 7 + drivers/staging/iio/dds/Makefile | 1 + drivers/staging/iio/dds/ad9834.c | 482 ++++++++++++++++++++++++++++++++++++++ drivers/staging/iio/dds/ad9834.h | 112 +++++++++ 4 files changed, 602 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/dds/ad9834.c create mode 100644 drivers/staging/iio/dds/ad9834.h diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig index 7969be2..4c9cce3 100644 --- a/drivers/staging/iio/dds/Kconfig +++ b/drivers/staging/iio/dds/Kconfig @@ -17,6 +17,13 @@ config AD9832 Say yes here to build support for Analog Devices DDS chip ad9832 and ad9835, provides direct access via sysfs. +config AD9834 + tristate "Analog Devices ad9833/4/ driver" + depends on SPI + help + Say yes here to build support for Analog Devices DDS chip + AD9833 and AD9834, provides direct access via sysfs. + config AD9850 tristate "Analog Devices ad9850/1 driver" depends on SPI diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile index 6f274ac..1477461 100644 --- a/drivers/staging/iio/dds/Makefile +++ b/drivers/staging/iio/dds/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_AD5930) += ad5930.o obj-$(CONFIG_AD9832) += ad9832.o +obj-$(CONFIG_AD9834) += ad9834.o obj-$(CONFIG_AD9850) += ad9850.o obj-$(CONFIG_AD9852) += ad9852.o obj-$(CONFIG_AD9910) += ad9910.o diff --git a/drivers/staging/iio/dds/ad9834.c b/drivers/staging/iio/dds/ad9834.c new file mode 100644 index 0000000..df3d68c --- /dev/null +++ b/drivers/staging/iio/dds/ad9834.c @@ -0,0 +1,482 @@ +/* + * AD9834 SPI DAC driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <asm/div64.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "dds.h" + +#include "ad9834.h" + +static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout) +{ + unsigned long long freqreg = (u64) fout * (u64) (1 << AD9834_FREQ_BITS); + do_div(freqreg, mclk); + return freqreg; +} + +static int ad9834_write_frequency(struct ad9834_state *st, + unsigned long addr, unsigned long fout) +{ + unsigned long regval; + + if (fout > (st->mclk / 2)) + return -EINVAL; + + regval = ad9834_calc_freqreg(st->mclk, fout); + + st->freq_data[0] = cpu_to_be16(addr | (regval & + RES_MASK(AD9834_FREQ_BITS / 2))); + st->freq_data[1] = cpu_to_be16(addr | ((regval >> + (AD9834_FREQ_BITS / 2)) & + RES_MASK(AD9834_FREQ_BITS / 2))); + + return spi_sync(st->spi, &st->freq_msg);; +} + +static int ad9834_write_phase(struct ad9834_state *st, + unsigned long addr, unsigned long phase) +{ + if (phase > (1 << AD9834_PHASE_BITS)) + return -EINVAL; + st->data = cpu_to_be16(addr | phase); + + return spi_sync(st->spi, &st->msg); +} + +static ssize_t ad9834_write(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = dev_info->dev_data; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + long val; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + goto error_ret; + + mutex_lock(&dev_info->mlock); + switch (this_attr->address) { + case AD9834_REG_FREQ0: + case AD9834_REG_FREQ1: + ret = ad9834_write_frequency(st, this_attr->address, val); + break; + case AD9834_REG_PHASE0: + case AD9834_REG_PHASE1: + ret = ad9834_write_phase(st, this_attr->address, val); + break; + case AD9834_OPBITEN: + if (st->control & AD9834_MODE) { + ret = -EINVAL; /* AD9843 reserved mode */ + break; + } + + if (val) + st->control |= AD9834_OPBITEN; + else + st->control &= ~AD9834_OPBITEN; + + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + case AD9834_PIN_SW: + if (val) + st->control |= AD9834_PIN_SW; + else + st->control &= ~AD9834_PIN_SW; + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + case AD9834_FSEL: + case AD9834_PSEL: + if (val == 0) + st->control &= ~(this_attr->address | AD9834_PIN_SW); + else if (val == 1) { + st->control |= this_attr->address; + st->control &= ~AD9834_PIN_SW; + } else { + ret = -EINVAL; + break; + } + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + case AD9834_RESET: + if (val) + st->control &= ~AD9834_RESET; + else + st->control |= AD9834_RESET; + + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + break; + default: + ret = -ENODEV; + } + mutex_unlock(&dev_info->mlock); + +error_ret: + return ret ? ret : len; +} + +static ssize_t ad9834_store_wavetype(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = dev_info->dev_data; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret = 0; + bool is_ad9833 = st->devid == ID_AD9833; + + mutex_lock(&dev_info->mlock); + + switch (this_attr->address) { + case 0: + if (sysfs_streq(buf, "sine")) { + st->control &= ~AD9834_MODE; + if (is_ad9833) + st->control &= ~AD9834_OPBITEN; + } else if (sysfs_streq(buf, "triangle")) { + if (is_ad9833) { + st->control &= ~AD9834_OPBITEN; + st->control |= AD9834_MODE; + } else if (st->control & AD9834_OPBITEN) { + ret = -EINVAL; /* AD9843 reserved mode */ + } else { + st->control |= AD9834_MODE; + } + } else if (is_ad9833 && sysfs_streq(buf, "square")) { + st->control &= ~AD9834_MODE; + st->control |= AD9834_OPBITEN; + } else { + ret = -EINVAL; + } + + break; + case 1: + if (sysfs_streq(buf, "square") && + !(st->control & AD9834_MODE)) { + st->control &= ~AD9834_MODE; + st->control |= AD9834_OPBITEN; + } else { + ret = -EINVAL; + } + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) { + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + } + mutex_unlock(&dev_info->mlock); + + return ret ? ret : len; +} + +static ssize_t ad9834_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = iio_dev_get_devdata(dev_info); + + return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name); +} +static IIO_DEVICE_ATTR(name, S_IRUGO, ad9834_show_name, NULL, 0); + +static ssize_t ad9834_show_out0_wavetype_available(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = iio_dev_get_devdata(dev_info); + char *str; + + if (st->devid == ID_AD9833) + str = "sine triangle square"; + else if (st->control & AD9834_OPBITEN) + str = "sine"; + else + str = "sine triangle"; + + return sprintf(buf, "%s\n", str); +} + + +static IIO_DEVICE_ATTR(dds0_out0_wavetype_available, S_IRUGO, + ad9834_show_out0_wavetype_available, NULL, 0); + +static ssize_t ad9834_show_out1_wavetype_available(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad9834_state *st = iio_dev_get_devdata(dev_info); + char *str; + + if (st->control & AD9834_MODE) + str = ""; + else + str = "square"; + + return sprintf(buf, "%s\n", str); +} + +static IIO_DEVICE_ATTR(dds0_out1_wavetype_available, S_IRUGO, + ad9834_show_out1_wavetype_available, NULL, 0); + +/** + * see dds.h for further information + */ + +static IIO_DEV_ATTR_FREQ(0, 0, ad9834_write, AD9834_REG_FREQ0); +static IIO_DEV_ATTR_FREQ(0, 1, ad9834_write, AD9834_REG_FREQ1); +static IIO_DEV_ATTR_FREQSYMBOL(0, ad9834_write, AD9834_FSEL); +static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ + +static IIO_DEV_ATTR_PHASE(0, 0, ad9834_write, AD9834_REG_PHASE0); +static IIO_DEV_ATTR_PHASE(0, 1, ad9834_write, AD9834_REG_PHASE1); +static IIO_DEV_ATTR_PHASESYMBOL(0, ad9834_write, AD9834_PSEL); +static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ + +static IIO_DEV_ATTR_PINCONTROL_EN(0, ad9834_write, AD9834_PIN_SW); +static IIO_DEV_ATTR_OUT_ENABLE(0, ad9834_write, AD9834_RESET); +static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, ad9834_write, AD9834_OPBITEN); +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); + +static struct attribute *ad9834_attributes[] = { + &iio_dev_attr_dds0_freq0.dev_attr.attr, + &iio_dev_attr_dds0_freq1.dev_attr.attr, + &iio_const_attr_dds0_freq_scale.dev_attr.attr, + &iio_dev_attr_dds0_phase0.dev_attr.attr, + &iio_dev_attr_dds0_phase1.dev_attr.attr, + &iio_const_attr_dds0_phase_scale.dev_attr.attr, + &iio_dev_attr_dds0_pincontrol_en.dev_attr.attr, + &iio_dev_attr_dds0_freqsymbol.dev_attr.attr, + &iio_dev_attr_dds0_phasesymbol.dev_attr.attr, + &iio_dev_attr_dds0_out_enable.dev_attr.attr, + &iio_dev_attr_dds0_out1_enable.dev_attr.attr, + &iio_dev_attr_dds0_out0_wavetype.dev_attr.attr, + &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr, + &iio_dev_attr_dds0_out0_wavetype_available.dev_attr.attr, + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr, + &iio_dev_attr_name.dev_attr.attr, + NULL, +}; + +static mode_t ad9834_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 ad9834_state *st = iio_dev_get_devdata(dev_info); + + mode_t mode = attr->mode; + + if (st->devid == ID_AD9834) + return mode; + + if ((attr == &iio_dev_attr_dds0_out1_enable.dev_attr.attr) || + (attr == &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr) || + (attr == + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr)) + mode = 0; + + return mode; +} + +static const struct attribute_group ad9834_attribute_group = { + .attrs = ad9834_attributes, + .is_visible = ad9834_attr_is_visible, +}; + +static int __devinit ad9834_probe(struct spi_device *spi) +{ + struct ad9834_platform_data *pdata = spi->dev.platform_data; + struct ad9834_state *st; + int ret; + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + st->mclk = pdata->mclk; + + spi_set_drvdata(spi, st); + + st->spi = spi; + st->devid = spi_get_device_id(spi)->driver_data; + + st->indio_dev = iio_allocate_device(); + if (st->indio_dev == NULL) { + ret = -ENOMEM; + goto error_disable_reg; + } + + st->indio_dev->dev.parent = &spi->dev; + st->indio_dev->attrs = &ad9834_attribute_group; + st->indio_dev->dev_data = (void *)(st); + st->indio_dev->driver_module = THIS_MODULE; + st->indio_dev->modes = INDIO_DIRECT_MODE; + + /* Setup default messages */ + + st->xfer.tx_buf = &st->data; + st->xfer.len = 2; + + spi_message_init(&st->msg); + spi_message_add_tail(&st->xfer, &st->msg); + + st->freq_xfer[0].tx_buf = &st->freq_data[0]; + st->freq_xfer[0].len = 2; + st->freq_xfer[0].cs_change = 1; + st->freq_xfer[1].tx_buf = &st->freq_data[1]; + st->freq_xfer[1].len = 2; + + spi_message_init(&st->freq_msg); + spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); + spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); + + st->control = AD9834_B28 | AD9834_RESET; + + if (!pdata->en_div2) + st->control |= AD9834_DIV2; + + if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) + st->control |= AD9834_SIGN_PIB; + + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + if (ret) { + dev_err(&spi->dev, "device init failed\n"); + goto error_free_device; + } + + ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); + if (ret) + goto error_free_device; + + ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); + if (ret) + goto error_free_device; + + ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); + if (ret) + goto error_free_device; + + ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); + if (ret) + goto error_free_device; + + st->control &= ~AD9834_RESET; + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); + ret = spi_sync(st->spi, &st->msg); + if (ret) + goto error_free_device; + + ret = iio_device_register(st->indio_dev); + if (ret) + goto error_free_device; + + return 0; + +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 ret; +} + +static int __devexit ad9834_remove(struct spi_device *spi) +{ + struct ad9834_state *st = spi_get_drvdata(spi); + struct iio_dev *indio_dev = st->indio_dev; + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + kfree(st); + return 0; +} + +static const struct spi_device_id ad9834_id[] = { + {"ad9833", ID_AD9833}, + {"ad9834", ID_AD9834}, + {} +}; + +static struct spi_driver ad9834_driver = { + .driver = { + .name = "ad9834", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad9834_probe, + .remove = __devexit_p(ad9834_remove), + .id_table = ad9834_id, +}; + +static int __init ad9834_init(void) +{ + return spi_register_driver(&ad9834_driver); +} +module_init(ad9834_init); + +static void __exit ad9834_exit(void) +{ + spi_unregister_driver(&ad9834_driver); +} +module_exit(ad9834_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD9833/AD9834 DDS"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:ad9834"); diff --git a/drivers/staging/iio/dds/ad9834.h b/drivers/staging/iio/dds/ad9834.h new file mode 100644 index 0000000..0fc3b88 --- /dev/null +++ b/drivers/staging/iio/dds/ad9834.h @@ -0,0 +1,112 @@ +/* + * AD9834 SPI DDS driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_DDS_AD9834_H_ +#define IIO_DDS_AD9834_H_ + +/* Registers */ + +#define AD9834_REG_CMD (0 << 14) +#define AD9834_REG_FREQ0 (1 << 14) +#define AD9834_REG_FREQ1 (2 << 14) +#define AD9834_REG_PHASE0 (6 << 13) +#define AD9834_REG_PHASE1 (7 << 13) + +/* Command Control Bits */ + +#define AD9834_B28 (1 << 13) +#define AD9834_HLB (1 << 12) +#define AD9834_FSEL (1 << 11) +#define AD9834_PSEL (1 << 10) +#define AD9834_PIN_SW (1 << 9) +#define AD9834_RESET (1 << 8) +#define AD9834_SLEEP1 (1 << 7) +#define AD9834_SLEEP12 (1 << 6) +#define AD9834_OPBITEN (1 << 5) +#define AD9834_SIGN_PIB (1 << 4) +#define AD9834_DIV2 (1 << 3) +#define AD9834_MODE (1 << 1) + +#define AD9834_FREQ_BITS 28 +#define AD9834_PHASE_BITS 12 + +#define RES_MASK(bits) ((1 << (bits)) - 1) + +/** + * struct ad9834_state - driver instance specific data + * @indio_dev: the industrial I/O device + * @spi: spi_device + * @reg: supply regulator + * @mclk: external master clock + * @control: cached control word + * @xfer: default spi transfer + * @msg: default spi message + * @freq_xfer: tuning word spi transfer + * @freq_msg: tuning word spi message + * @data: spi transmit buffer + * @freq_data: tuning word spi transmit buffer + */ + +struct ad9834_state { + struct iio_dev *indio_dev; + struct spi_device *spi; + struct regulator *reg; + unsigned int mclk; + unsigned short control; + unsigned short devid; + struct spi_transfer xfer; + struct spi_message msg; + struct spi_transfer freq_xfer[2]; + struct spi_message freq_msg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + unsigned short data ____cacheline_aligned; + unsigned short freq_data[2] ; +}; + + +/* + * TODO: struct ad7887_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad9834_platform_data - platform specific information + * @mclk: master clock in Hz + * @freq0: power up freq0 tuning word in Hz + * @freq1: power up freq1 tuning word in Hz + * @phase0: power up phase0 value [0..4095] correlates with 0..2PI + * @phase1: power up phase1 value [0..4095] correlates with 0..2PI + * @en_div2: digital output/2 is passed to the SIGN BIT OUT pin + * @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is connected to the + * SIGN BIT OUT pin. en_div2 controls whether it is the MSB + * or MSB/2 that is output. if en_signbit_msb_out=false, + * the on-board comparator is connected to SIGN BIT OUT + */ + +struct ad9834_platform_data { + unsigned int mclk; + unsigned int freq0; + unsigned int freq1; + unsigned short phase0; + unsigned short phase1; + bool en_div2; + bool en_signbit_msb_out; +}; + +/** + * ad9834_supported_device_ids: + */ + +enum ad9834_supported_device_ids { + ID_AD9833, + ID_AD9834, +}; + +#endif /* IIO_DDS_AD9834_H_ */ -- 1.6.0.2 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver 2010-12-13 16:27 ` [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver michael.hennerich @ 2010-12-13 21:40 ` Jonathan Cameron 2010-12-14 11:01 ` Hennerich, Michael 0 siblings, 1 reply; 9+ messages in thread From: Jonathan Cameron @ 2010-12-13 21:40 UTC (permalink / raw) To: michael.hennerich; +Cc: linux-iio, linux-kernel, drivers, device-drivers-devel On 12/13/10 16:27, michael.hennerich@analog.com wrote: > From: Michael Hennerich <michael.hennerich@analog.com> > > Changes since RFC/v1: > IIO: Apply list review feedback > > Apply list review feedback: > Rename attributes to fit IIO convention used in other drivers. > Fix typos. > Provide ddsX_out_enable as opposed to ddsX_out_disable. > Use proper __devexit marking. > Use strict_strtoul() to avoid negatives. > Couple of nitpicks inline. Subject to changes due to changes in macros suggested in previous patch (or you convincing me otherwise): > Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> > Reviewed-by: Datta Shubhrajyoti <shubhrajyoti@ti.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > drivers/staging/iio/dds/Kconfig | 7 + > drivers/staging/iio/dds/Makefile | 1 + > drivers/staging/iio/dds/ad9834.c | 482 ++++++++++++++++++++++++++++++++++++++ > drivers/staging/iio/dds/ad9834.h | 112 +++++++++ > 4 files changed, 602 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/dds/ad9834.c > create mode 100644 drivers/staging/iio/dds/ad9834.h > > diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig > index 7969be2..4c9cce3 100644 > --- a/drivers/staging/iio/dds/Kconfig > +++ b/drivers/staging/iio/dds/Kconfig > @@ -17,6 +17,13 @@ config AD9832 > Say yes here to build support for Analog Devices DDS chip > ad9832 and ad9835, provides direct access via sysfs. > > +config AD9834 > + tristate "Analog Devices ad9833/4/ driver" > + depends on SPI > + help > + Say yes here to build support for Analog Devices DDS chip > + AD9833 and AD9834, provides direct access via sysfs. > + > config AD9850 > tristate "Analog Devices ad9850/1 driver" > depends on SPI > diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile > index 6f274ac..1477461 100644 > --- a/drivers/staging/iio/dds/Makefile > +++ b/drivers/staging/iio/dds/Makefile > @@ -4,6 +4,7 @@ > > obj-$(CONFIG_AD5930) += ad5930.o > obj-$(CONFIG_AD9832) += ad9832.o > +obj-$(CONFIG_AD9834) += ad9834.o > obj-$(CONFIG_AD9850) += ad9850.o > obj-$(CONFIG_AD9852) += ad9852.o > obj-$(CONFIG_AD9910) += ad9910.o > diff --git a/drivers/staging/iio/dds/ad9834.c b/drivers/staging/iio/dds/ad9834.c > new file mode 100644 > index 0000000..df3d68c > --- /dev/null > +++ b/drivers/staging/iio/dds/ad9834.c > @@ -0,0 +1,482 @@ > +/* > + * AD9834 SPI DAC driver > + * > + * Copyright 2010 Analog Devices Inc. > + * > + * Licensed under the GPL-2 or later. > + */ > + > +#include <linux/interrupt.h> > +#include <linux/workqueue.h> > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/sysfs.h> > +#include <linux/list.h> > +#include <linux/spi/spi.h> > +#include <linux/regulator/consumer.h> > +#include <linux/err.h> > +#include <asm/div64.h> > + > +#include "../iio.h" > +#include "../sysfs.h" > +#include "dds.h" > + > +#include "ad9834.h" > + > +static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout) > +{ > + unsigned long long freqreg = (u64) fout * (u64) (1 << AD9834_FREQ_BITS); > + do_div(freqreg, mclk); > + return freqreg; > +} > + > +static int ad9834_write_frequency(struct ad9834_state *st, > + unsigned long addr, unsigned long fout) > +{ > + unsigned long regval; > + > + if (fout > (st->mclk / 2)) > + return -EINVAL; > + > + regval = ad9834_calc_freqreg(st->mclk, fout); > + > + st->freq_data[0] = cpu_to_be16(addr | (regval & > + RES_MASK(AD9834_FREQ_BITS / 2))); > + st->freq_data[1] = cpu_to_be16(addr | ((regval >> > + (AD9834_FREQ_BITS / 2)) & > + RES_MASK(AD9834_FREQ_BITS / 2))); > + > + return spi_sync(st->spi, &st->freq_msg);; > +} > + > +static int ad9834_write_phase(struct ad9834_state *st, > + unsigned long addr, unsigned long phase) > +{ > + if (phase > (1 << AD9834_PHASE_BITS)) > + return -EINVAL; > + st->data = cpu_to_be16(addr | phase); > + > + return spi_sync(st->spi, &st->msg); > +} > + > +static ssize_t ad9834_write(struct device *dev, > + struct device_attribute *attr, > + const char *buf, > + size_t len) > +{ > + struct iio_dev *dev_info = dev_get_drvdata(dev); > + struct ad9834_state *st = dev_info->dev_data; > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > + int ret; > + long val; > + > + ret = strict_strtoul(buf, 10, &val); > + if (ret) > + goto error_ret; > + > + mutex_lock(&dev_info->mlock); > + switch (this_attr->address) { > + case AD9834_REG_FREQ0: > + case AD9834_REG_FREQ1: > + ret = ad9834_write_frequency(st, this_attr->address, val); > + break; > + case AD9834_REG_PHASE0: > + case AD9834_REG_PHASE1: > + ret = ad9834_write_phase(st, this_attr->address, val); > + break; > + case AD9834_OPBITEN: > + if (st->control & AD9834_MODE) { > + ret = -EINVAL; /* AD9843 reserved mode */ > + break; > + } > + > + if (val) > + st->control |= AD9834_OPBITEN; > + else > + st->control &= ~AD9834_OPBITEN; > + > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > + ret = spi_sync(st->spi, &st->msg); > + break; > + case AD9834_PIN_SW: > + if (val) > + st->control |= AD9834_PIN_SW; > + else > + st->control &= ~AD9834_PIN_SW; > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > + ret = spi_sync(st->spi, &st->msg); > + break; > + case AD9834_FSEL: > + case AD9834_PSEL: > + if (val == 0) > + st->control &= ~(this_attr->address | AD9834_PIN_SW); > + else if (val == 1) { > + st->control |= this_attr->address; > + st->control &= ~AD9834_PIN_SW; > + } else { > + ret = -EINVAL; > + break; > + } > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > + ret = spi_sync(st->spi, &st->msg); > + break; > + case AD9834_RESET: > + if (val) > + st->control &= ~AD9834_RESET; > + else > + st->control |= AD9834_RESET; > + > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > + ret = spi_sync(st->spi, &st->msg); > + break; > + default: > + ret = -ENODEV; > + } > + mutex_unlock(&dev_info->mlock); > + > +error_ret: > + return ret ? ret : len; > +} > + > +static ssize_t ad9834_store_wavetype(struct device *dev, > + struct device_attribute *attr, > + const char *buf, > + size_t len) > +{ > + struct iio_dev *dev_info = dev_get_drvdata(dev); > + struct ad9834_state *st = dev_info->dev_data; > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > + int ret = 0; > + bool is_ad9833 = st->devid == ID_AD9833; > + > + mutex_lock(&dev_info->mlock); > + > + switch (this_attr->address) { > + case 0: > + if (sysfs_streq(buf, "sine")) { > + st->control &= ~AD9834_MODE; > + if (is_ad9833) > + st->control &= ~AD9834_OPBITEN; > + } else if (sysfs_streq(buf, "triangle")) { > + if (is_ad9833) { > + st->control &= ~AD9834_OPBITEN; > + st->control |= AD9834_MODE; > + } else if (st->control & AD9834_OPBITEN) { > + ret = -EINVAL; /* AD9843 reserved mode */ > + } else { > + st->control |= AD9834_MODE; > + } > + } else if (is_ad9833 && sysfs_streq(buf, "square")) { > + st->control &= ~AD9834_MODE; > + st->control |= AD9834_OPBITEN; > + } else { > + ret = -EINVAL; > + } > + > + break; > + case 1: > + if (sysfs_streq(buf, "square") && > + !(st->control & AD9834_MODE)) { > + st->control &= ~AD9834_MODE; > + st->control |= AD9834_OPBITEN; > + } else { > + ret = -EINVAL; > + } > + break; > + default: > + ret = -EINVAL; > + break; > + } > + > + if (!ret) { > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > + ret = spi_sync(st->spi, &st->msg); > + } > + mutex_unlock(&dev_info->mlock); In correct tabbing on previous line? (or my email client has messed up) > + > + return ret ? ret : len; > +} > + > +static ssize_t ad9834_show_name(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct iio_dev *dev_info = dev_get_drvdata(dev); > + struct ad9834_state *st = iio_dev_get_devdata(dev_info); > + > + return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name); > +} > +static IIO_DEVICE_ATTR(name, S_IRUGO, ad9834_show_name, NULL, 0); > + > +static ssize_t ad9834_show_out0_wavetype_available(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct iio_dev *dev_info = dev_get_drvdata(dev); > + struct ad9834_state *st = iio_dev_get_devdata(dev_info); > + char *str; > + > + if (st->devid == ID_AD9833) > + str = "sine triangle square"; > + else if (st->control & AD9834_OPBITEN) > + str = "sine"; > + else > + str = "sine triangle"; > + > + return sprintf(buf, "%s\n", str); > +} > + > + > +static IIO_DEVICE_ATTR(dds0_out0_wavetype_available, S_IRUGO, > + ad9834_show_out0_wavetype_available, NULL, 0); > + > +static ssize_t ad9834_show_out1_wavetype_available(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct iio_dev *dev_info = dev_get_drvdata(dev); > + struct ad9834_state *st = iio_dev_get_devdata(dev_info); > + char *str; > + > + if (st->control & AD9834_MODE) > + str = ""; I 'think' that the is_visible below makes this first condition impossible? I've never used the is_visible stuff so not entirely sure I've understood it correctly... That probably means that you can use a const attr for this... > + else > + str = "square"; > + > + return sprintf(buf, "%s\n", str); > +} > + > +static IIO_DEVICE_ATTR(dds0_out1_wavetype_available, S_IRUGO, > + ad9834_show_out1_wavetype_available, NULL, 0); > + > +/** > + * see dds.h for further information > + */ > + > +static IIO_DEV_ATTR_FREQ(0, 0, ad9834_write, AD9834_REG_FREQ0); > +static IIO_DEV_ATTR_FREQ(0, 1, ad9834_write, AD9834_REG_FREQ1); > +static IIO_DEV_ATTR_FREQSYMBOL(0, ad9834_write, AD9834_FSEL); > +static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ > + > +static IIO_DEV_ATTR_PHASE(0, 0, ad9834_write, AD9834_REG_PHASE0); > +static IIO_DEV_ATTR_PHASE(0, 1, ad9834_write, AD9834_REG_PHASE1); > +static IIO_DEV_ATTR_PHASESYMBOL(0, ad9834_write, AD9834_PSEL); > +static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ > + > +static IIO_DEV_ATTR_PINCONTROL_EN(0, ad9834_write, AD9834_PIN_SW); > +static IIO_DEV_ATTR_OUT_ENABLE(0, ad9834_write, AD9834_RESET); > +static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, ad9834_write, AD9834_OPBITEN); > +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); > +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); > + > +static struct attribute *ad9834_attributes[] = { > + &iio_dev_attr_dds0_freq0.dev_attr.attr, > + &iio_dev_attr_dds0_freq1.dev_attr.attr, > + &iio_const_attr_dds0_freq_scale.dev_attr.attr, > + &iio_dev_attr_dds0_phase0.dev_attr.attr, > + &iio_dev_attr_dds0_phase1.dev_attr.attr, > + &iio_const_attr_dds0_phase_scale.dev_attr.attr, > + &iio_dev_attr_dds0_pincontrol_en.dev_attr.attr, > + &iio_dev_attr_dds0_freqsymbol.dev_attr.attr, > + &iio_dev_attr_dds0_phasesymbol.dev_attr.attr, > + &iio_dev_attr_dds0_out_enable.dev_attr.attr, > + &iio_dev_attr_dds0_out1_enable.dev_attr.attr, > + &iio_dev_attr_dds0_out0_wavetype.dev_attr.attr, > + &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr, > + &iio_dev_attr_dds0_out0_wavetype_available.dev_attr.attr, > + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr, > + &iio_dev_attr_name.dev_attr.attr, > + NULL, > +}; > + > +static mode_t ad9834_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 ad9834_state *st = iio_dev_get_devdata(dev_info); > + > + mode_t mode = attr->mode; > + > + if (st->devid == ID_AD9834) > + return mode; > + > + if ((attr == &iio_dev_attr_dds0_out1_enable.dev_attr.attr) || > + (attr == &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr) || > + (attr == > + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr)) > + mode = 0; > + > + return mode; > +} > + > +static const struct attribute_group ad9834_attribute_group = { > + .attrs = ad9834_attributes, > + .is_visible = ad9834_attr_is_visible, > +}; > + > +static int __devinit ad9834_probe(struct spi_device *spi) > +{ > + struct ad9834_platform_data *pdata = spi->dev.platform_data; > + struct ad9834_state *st; > + int ret; > + > + if (!pdata) { > + dev_dbg(&spi->dev, "no platform data?\n"); > + return -ENODEV; > + } > + > + st = kzalloc(sizeof(*st), GFP_KERNEL); > + if (st == NULL) { > + ret = -ENOMEM; > + goto error_ret; > + } > + > + st->reg = regulator_get(&spi->dev, "vcc"); > + if (!IS_ERR(st->reg)) { > + ret = regulator_enable(st->reg); > + if (ret) > + goto error_put_reg; > + } > + > + st->mclk = pdata->mclk; > + > + spi_set_drvdata(spi, st); > + > + st->spi = spi; > + st->devid = spi_get_device_id(spi)->driver_data; > + > + st->indio_dev = iio_allocate_device(); > + if (st->indio_dev == NULL) { > + ret = -ENOMEM; > + goto error_disable_reg; > + } > + > + st->indio_dev->dev.parent = &spi->dev; > + st->indio_dev->attrs = &ad9834_attribute_group; > + st->indio_dev->dev_data = (void *)(st); nitpick: technically superflous brackets... > + st->indio_dev->driver_module = THIS_MODULE; > + st->indio_dev->modes = INDIO_DIRECT_MODE; > + > + /* Setup default messages */ > + > + st->xfer.tx_buf = &st->data; > + st->xfer.len = 2; > + > + spi_message_init(&st->msg); > + spi_message_add_tail(&st->xfer, &st->msg); > + > + st->freq_xfer[0].tx_buf = &st->freq_data[0]; > + st->freq_xfer[0].len = 2; > + st->freq_xfer[0].cs_change = 1; > + st->freq_xfer[1].tx_buf = &st->freq_data[1]; > + st->freq_xfer[1].len = 2; > + > + spi_message_init(&st->freq_msg); > + spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); > + spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); > + > + st->control = AD9834_B28 | AD9834_RESET; > + > + if (!pdata->en_div2) > + st->control |= AD9834_DIV2; > + > + if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) > + st->control |= AD9834_SIGN_PIB; > + > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > + ret = spi_sync(st->spi, &st->msg); > + if (ret) { > + dev_err(&spi->dev, "device init failed\n"); > + goto error_free_device; > + } > + > + ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); > + if (ret) > + goto error_free_device; > + > + ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); > + if (ret) > + goto error_free_device; > + > + ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); > + if (ret) > + goto error_free_device; > + > + ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); > + if (ret) > + goto error_free_device; > + > + st->control &= ~AD9834_RESET; > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > + ret = spi_sync(st->spi, &st->msg); > + if (ret) > + goto error_free_device; > + > + ret = iio_device_register(st->indio_dev); > + if (ret) > + goto error_free_device; > + > + return 0; > + > +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 ret; > +} > + > +static int __devexit ad9834_remove(struct spi_device *spi) > +{ > + struct ad9834_state *st = spi_get_drvdata(spi); > + struct iio_dev *indio_dev = st->indio_dev; > + nitpick: could just use st->indio_dev in the next call and lose the line above. It's the only use in this function. > + iio_device_unregister(indio_dev); > + if (!IS_ERR(st->reg)) { > + regulator_disable(st->reg); > + regulator_put(st->reg); > + } > + kfree(st); > + return 0; > +} > + > +static const struct spi_device_id ad9834_id[] = { > + {"ad9833", ID_AD9833}, > + {"ad9834", ID_AD9834}, > + {} > +}; > + > +static struct spi_driver ad9834_driver = { > + .driver = { > + .name = "ad9834", > + .bus = &spi_bus_type, > + .owner = THIS_MODULE, > + }, > + .probe = ad9834_probe, > + .remove = __devexit_p(ad9834_remove), > + .id_table = ad9834_id, > +}; > + > +static int __init ad9834_init(void) > +{ > + return spi_register_driver(&ad9834_driver); > +} > +module_init(ad9834_init); > + > +static void __exit ad9834_exit(void) > +{ > + spi_unregister_driver(&ad9834_driver); > +} > +module_exit(ad9834_exit); > + > +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); > +MODULE_DESCRIPTION("Analog Devices AD9833/AD9834 DDS"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("spi:ad9834"); > diff --git a/drivers/staging/iio/dds/ad9834.h b/drivers/staging/iio/dds/ad9834.h > new file mode 100644 > index 0000000..0fc3b88 > --- /dev/null > +++ b/drivers/staging/iio/dds/ad9834.h > @@ -0,0 +1,112 @@ > +/* > + * AD9834 SPI DDS driver > + * > + * Copyright 2010 Analog Devices Inc. > + * > + * Licensed under the GPL-2 or later. > + */ > +#ifndef IIO_DDS_AD9834_H_ > +#define IIO_DDS_AD9834_H_ > + > +/* Registers */ > + > +#define AD9834_REG_CMD (0 << 14) > +#define AD9834_REG_FREQ0 (1 << 14) > +#define AD9834_REG_FREQ1 (2 << 14) > +#define AD9834_REG_PHASE0 (6 << 13) > +#define AD9834_REG_PHASE1 (7 << 13) > + > +/* Command Control Bits */ > + > +#define AD9834_B28 (1 << 13) > +#define AD9834_HLB (1 << 12) > +#define AD9834_FSEL (1 << 11) > +#define AD9834_PSEL (1 << 10) > +#define AD9834_PIN_SW (1 << 9) > +#define AD9834_RESET (1 << 8) > +#define AD9834_SLEEP1 (1 << 7) > +#define AD9834_SLEEP12 (1 << 6) > +#define AD9834_OPBITEN (1 << 5) > +#define AD9834_SIGN_PIB (1 << 4) > +#define AD9834_DIV2 (1 << 3) > +#define AD9834_MODE (1 << 1) > + > +#define AD9834_FREQ_BITS 28 > +#define AD9834_PHASE_BITS 12 > + > +#define RES_MASK(bits) ((1 << (bits)) - 1) > + > +/** > + * struct ad9834_state - driver instance specific data > + * @indio_dev: the industrial I/O device > + * @spi: spi_device > + * @reg: supply regulator > + * @mclk: external master clock > + * @control: cached control word > + * @xfer: default spi transfer > + * @msg: default spi message > + * @freq_xfer: tuning word spi transfer > + * @freq_msg: tuning word spi message > + * @data: spi transmit buffer > + * @freq_data: tuning word spi transmit buffer > + */ > + > +struct ad9834_state { > + struct iio_dev *indio_dev; > + struct spi_device *spi; > + struct regulator *reg; > + unsigned int mclk; > + unsigned short control; > + unsigned short devid; > + struct spi_transfer xfer; > + struct spi_message msg; > + struct spi_transfer freq_xfer[2]; > + struct spi_message freq_msg; > + > + /* > + * DMA (thus cache coherency maintenance) requires the > + * transfer buffers to live in their own cache lines. > + */ > + unsigned short data ____cacheline_aligned; > + unsigned short freq_data[2] ; > +}; > + > + > +/* > + * TODO: struct ad7887_platform_data needs to go into include/linux/iio > + */ > + > +/** > + * struct ad9834_platform_data - platform specific information > + * @mclk: master clock in Hz > + * @freq0: power up freq0 tuning word in Hz > + * @freq1: power up freq1 tuning word in Hz > + * @phase0: power up phase0 value [0..4095] correlates with 0..2PI > + * @phase1: power up phase1 value [0..4095] correlates with 0..2PI > + * @en_div2: digital output/2 is passed to the SIGN BIT OUT pin > + * @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is connected to the > + * SIGN BIT OUT pin. en_div2 controls whether it is the MSB > + * or MSB/2 that is output. if en_signbit_msb_out=false, > + * the on-board comparator is connected to SIGN BIT OUT > + */ > + > +struct ad9834_platform_data { > + unsigned int mclk; > + unsigned int freq0; > + unsigned int freq1; > + unsigned short phase0; > + unsigned short phase1; > + bool en_div2; > + bool en_signbit_msb_out; > +}; > + > +/** > + * ad9834_supported_device_ids: > + */ > + > +enum ad9834_supported_device_ids { > + ID_AD9833, > + ID_AD9834, > +}; > + > +#endif /* IIO_DDS_AD9834_H_ */ > -- > 1.6.0.2 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver 2010-12-13 21:40 ` Jonathan Cameron @ 2010-12-14 11:01 ` Hennerich, Michael 2010-12-14 12:30 ` Jonathan Cameron 0 siblings, 1 reply; 9+ messages in thread From: Hennerich, Michael @ 2010-12-14 11:01 UTC (permalink / raw) To: Jonathan Cameron; +Cc: linux-iio, linux-kernel, Drivers, device-drivers-devel > Jonathan Cameron wrote on 2010-12-13: > > On 12/13/10 16:27, michael.hennerich@analog.com wrote: > > From: Michael Hennerich <michael.hennerich@analog.com> > > > > Changes since RFC/v1: > > IIO: Apply list review feedback > > > > Apply list review feedback: > > Rename attributes to fit IIO convention used in other drivers. > > Fix typos. > > Provide ddsX_out_enable as opposed to ddsX_out_disable. > > Use proper __devexit marking. > > Use strict_strtoul() to avoid negatives. > > > Couple of nitpicks inline. Subject to changes due to changes in macros > suggested in previous patch (or you convincing me otherwise): > > Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> > > Reviewed-by: Datta Shubhrajyoti <shubhrajyoti@ti.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > > --- > > drivers/staging/iio/dds/Kconfig | 7 + > > drivers/staging/iio/dds/Makefile | 1 + > > drivers/staging/iio/dds/ad9834.c | 482 > ++++++++++++++++++++++++++++++++++++++ > > drivers/staging/iio/dds/ad9834.h | 112 +++++++++ > > 4 files changed, 602 insertions(+), 0 deletions(-) > > create mode 100644 drivers/staging/iio/dds/ad9834.c > > create mode 100644 drivers/staging/iio/dds/ad9834.h > > > > diff --git a/drivers/staging/iio/dds/Kconfig > b/drivers/staging/iio/dds/Kconfig > > index 7969be2..4c9cce3 100644 > > --- a/drivers/staging/iio/dds/Kconfig > > +++ b/drivers/staging/iio/dds/Kconfig > > @@ -17,6 +17,13 @@ config AD9832 > > Say yes here to build support for Analog Devices DDS chip > > ad9832 and ad9835, provides direct access via sysfs. > > > > +config AD9834 > > + tristate "Analog Devices ad9833/4/ driver" > > + depends on SPI > > + help > > + Say yes here to build support for Analog Devices DDS chip > > + AD9833 and AD9834, provides direct access via sysfs. > > + > > config AD9850 > > tristate "Analog Devices ad9850/1 driver" > > depends on SPI > > diff --git a/drivers/staging/iio/dds/Makefile > b/drivers/staging/iio/dds/Makefile > > index 6f274ac..1477461 100644 > > --- a/drivers/staging/iio/dds/Makefile > > +++ b/drivers/staging/iio/dds/Makefile > > @@ -4,6 +4,7 @@ > > > > obj-$(CONFIG_AD5930) += ad5930.o > > obj-$(CONFIG_AD9832) += ad9832.o > > +obj-$(CONFIG_AD9834) += ad9834.o > > obj-$(CONFIG_AD9850) += ad9850.o > > obj-$(CONFIG_AD9852) += ad9852.o > > obj-$(CONFIG_AD9910) += ad9910.o > > diff --git a/drivers/staging/iio/dds/ad9834.c > b/drivers/staging/iio/dds/ad9834.c > > new file mode 100644 > > index 0000000..df3d68c > > --- /dev/null > > +++ b/drivers/staging/iio/dds/ad9834.c > > @@ -0,0 +1,482 @@ > > +/* > > + * AD9834 SPI DAC driver > > + * > > + * Copyright 2010 Analog Devices Inc. > > + * > > + * Licensed under the GPL-2 or later. > > + */ > > + > > +#include <linux/interrupt.h> > > +#include <linux/workqueue.h> > > +#include <linux/device.h> > > +#include <linux/kernel.h> > > +#include <linux/slab.h> > > +#include <linux/sysfs.h> > > +#include <linux/list.h> > > +#include <linux/spi/spi.h> > > +#include <linux/regulator/consumer.h> > > +#include <linux/err.h> > > +#include <asm/div64.h> > > + > > +#include "../iio.h" > > +#include "../sysfs.h" > > +#include "dds.h" > > + > > +#include "ad9834.h" > > + > > +static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned > long fout) > > +{ > > + unsigned long long freqreg = (u64) fout * (u64) (1 << > AD9834_FREQ_BITS); > > + do_div(freqreg, mclk); > > + return freqreg; > > +} > > + > > +static int ad9834_write_frequency(struct ad9834_state *st, > > + unsigned long addr, unsigned long fout) > > +{ > > + unsigned long regval; > > + > > + if (fout > (st->mclk / 2)) > > + return -EINVAL; > > + > > + regval = ad9834_calc_freqreg(st->mclk, fout); > > + > > + st->freq_data[0] = cpu_to_be16(addr | (regval & > > + RES_MASK(AD9834_FREQ_BITS / 2))); > > + st->freq_data[1] = cpu_to_be16(addr | ((regval >> > > + (AD9834_FREQ_BITS / 2)) & > > + RES_MASK(AD9834_FREQ_BITS / 2))); > > + > > + return spi_sync(st->spi, &st->freq_msg);; > > +} > > + > > +static int ad9834_write_phase(struct ad9834_state *st, > > + unsigned long addr, unsigned long phase) > > +{ > > + if (phase > (1 << AD9834_PHASE_BITS)) > > + return -EINVAL; > > + st->data = cpu_to_be16(addr | phase); > > + > > + return spi_sync(st->spi, &st->msg); > > +} > > + > > +static ssize_t ad9834_write(struct device *dev, > > + struct device_attribute *attr, > > + const char *buf, > > + size_t len) > > +{ > > + struct iio_dev *dev_info = dev_get_drvdata(dev); > > + struct ad9834_state *st = dev_info->dev_data; > > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > > + int ret; > > + long val; > > + > > + ret = strict_strtoul(buf, 10, &val); > > + if (ret) > > + goto error_ret; > > + > > + mutex_lock(&dev_info->mlock); > > + switch (this_attr->address) { > > + case AD9834_REG_FREQ0: > > + case AD9834_REG_FREQ1: > > + ret = ad9834_write_frequency(st, this_attr->address, val); > > + break; > > + case AD9834_REG_PHASE0: > > + case AD9834_REG_PHASE1: > > + ret = ad9834_write_phase(st, this_attr->address, val); > > + break; > > + case AD9834_OPBITEN: > > + if (st->control & AD9834_MODE) { > > + ret = -EINVAL; /* AD9843 reserved mode */ > > + break; > > + } > > + > > + if (val) > > + st->control |= AD9834_OPBITEN; > > + else > > + st->control &= ~AD9834_OPBITEN; > > + > > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > > + ret = spi_sync(st->spi, &st->msg); > > + break; > > + case AD9834_PIN_SW: > > + if (val) > > + st->control |= AD9834_PIN_SW; > > + else > > + st->control &= ~AD9834_PIN_SW; > > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > > + ret = spi_sync(st->spi, &st->msg); > > + break; > > + case AD9834_FSEL: > > + case AD9834_PSEL: > > + if (val == 0) > > + st->control &= ~(this_attr->address | AD9834_PIN_SW); > > + else if (val == 1) { > > + st->control |= this_attr->address; > > + st->control &= ~AD9834_PIN_SW; > > + } else { > > + ret = -EINVAL; > > + break; > > + } > > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > > + ret = spi_sync(st->spi, &st->msg); > > + break; > > + case AD9834_RESET: > > + if (val) > > + st->control &= ~AD9834_RESET; > > + else > > + st->control |= AD9834_RESET; > > + > > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > > + ret = spi_sync(st->spi, &st->msg); > > + break; > > + default: > > + ret = -ENODEV; > > + } > > + mutex_unlock(&dev_info->mlock); > > + > > +error_ret: > > + return ret ? ret : len; > > +} > > + > > +static ssize_t ad9834_store_wavetype(struct device *dev, > > + struct device_attribute *attr, > > + const char *buf, > > + size_t len) > > +{ > > + struct iio_dev *dev_info = dev_get_drvdata(dev); > > + struct ad9834_state *st = dev_info->dev_data; > > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > > + int ret = 0; > > + bool is_ad9833 = st->devid == ID_AD9833; > > + > > + mutex_lock(&dev_info->mlock); > > + > > + switch (this_attr->address) { > > + case 0: > > + if (sysfs_streq(buf, "sine")) { > > + st->control &= ~AD9834_MODE; > > + if (is_ad9833) > > + st->control &= ~AD9834_OPBITEN; > > + } else if (sysfs_streq(buf, "triangle")) { > > + if (is_ad9833) { > > + st->control &= ~AD9834_OPBITEN; > > + st->control |= AD9834_MODE; > > + } else if (st->control & AD9834_OPBITEN) { > > + ret = -EINVAL; /* AD9843 reserved mode */ > > + } else { > > + st->control |= AD9834_MODE; > > + } > > + } else if (is_ad9833 && sysfs_streq(buf, "square")) { > > + st->control &= ~AD9834_MODE; > > + st->control |= AD9834_OPBITEN; > > + } else { > > + ret = -EINVAL; > > + } > > + > > + break; > > + case 1: > > + if (sysfs_streq(buf, "square") && > > + !(st->control & AD9834_MODE)) { > > + st->control &= ~AD9834_MODE; > > + st->control |= AD9834_OPBITEN; > > + } else { > > + ret = -EINVAL; > > + } > > + break; > > + default: > > + ret = -EINVAL; > > + break; > > + } > > + > > + if (!ret) { > > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > > + ret = spi_sync(st->spi, &st->msg); > > + } > > + mutex_unlock(&dev_info->mlock); > In correct tabbing on previous line? (or my email client has messed up) Your email client doing the right thing. > > + > > + return ret ? ret : len; > > +} > > + > > +static ssize_t ad9834_show_name(struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct iio_dev *dev_info = dev_get_drvdata(dev); > > + struct ad9834_state *st = iio_dev_get_devdata(dev_info); > > + > > + return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name); > > +} > > +static IIO_DEVICE_ATTR(name, S_IRUGO, ad9834_show_name, NULL, 0); > > + > > +static ssize_t ad9834_show_out0_wavetype_available(struct device > *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct iio_dev *dev_info = dev_get_drvdata(dev); > > + struct ad9834_state *st = iio_dev_get_devdata(dev_info); > > + char *str; > > + > > + if (st->devid == ID_AD9833) > > + str = "sine triangle square"; > > + else if (st->control & AD9834_OPBITEN) > > + str = "sine"; > > + else > > + str = "sine triangle"; > > + > > + return sprintf(buf, "%s\n", str); > > +} > > + > > + > > +static IIO_DEVICE_ATTR(dds0_out0_wavetype_available, S_IRUGO, > > + ad9834_show_out0_wavetype_available, NULL, 0); > > + > > +static ssize_t ad9834_show_out1_wavetype_available(struct device > *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct iio_dev *dev_info = dev_get_drvdata(dev); > > + struct ad9834_state *st = iio_dev_get_devdata(dev_info); > > + char *str; > > + > > + if (st->control & AD9834_MODE) > > + str = ""; > I 'think' that the is_visible below makes this first condition > impossible? No - the is_visible prevents this file to be available on the AD9833. However this expresses a dependency on the AD9834 between the two outputs, where the 'square' output on out1 is not available in case out0 is set to 'triangle' > I've never used the is_visible stuff so not entirely sure I've > understood > it correctly... > That probably means that you can use a const attr for this... No > > + else > > + str = "square"; > > + > > + return sprintf(buf, "%s\n", str); > > +} > > + > > +static IIO_DEVICE_ATTR(dds0_out1_wavetype_available, S_IRUGO, > > + ad9834_show_out1_wavetype_available, NULL, 0); > > + > > +/** > > + * see dds.h for further information > > + */ > > + > > +static IIO_DEV_ATTR_FREQ(0, 0, ad9834_write, AD9834_REG_FREQ0); > > +static IIO_DEV_ATTR_FREQ(0, 1, ad9834_write, AD9834_REG_FREQ1); > > +static IIO_DEV_ATTR_FREQSYMBOL(0, ad9834_write, AD9834_FSEL); > > +static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ > > + > > +static IIO_DEV_ATTR_PHASE(0, 0, ad9834_write, AD9834_REG_PHASE0); > > +static IIO_DEV_ATTR_PHASE(0, 1, ad9834_write, AD9834_REG_PHASE1); > > +static IIO_DEV_ATTR_PHASESYMBOL(0, ad9834_write, AD9834_PSEL); > > +static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 > rad*/ > > + > > +static IIO_DEV_ATTR_PINCONTROL_EN(0, ad9834_write, AD9834_PIN_SW); > > +static IIO_DEV_ATTR_OUT_ENABLE(0, ad9834_write, AD9834_RESET); > > +static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, ad9834_write, AD9834_OPBITEN); > > +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); > > +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); > > + > > +static struct attribute *ad9834_attributes[] = { > > + &iio_dev_attr_dds0_freq0.dev_attr.attr, > > + &iio_dev_attr_dds0_freq1.dev_attr.attr, > > + &iio_const_attr_dds0_freq_scale.dev_attr.attr, > > + &iio_dev_attr_dds0_phase0.dev_attr.attr, > > + &iio_dev_attr_dds0_phase1.dev_attr.attr, > > + &iio_const_attr_dds0_phase_scale.dev_attr.attr, > > + &iio_dev_attr_dds0_pincontrol_en.dev_attr.attr, > > + &iio_dev_attr_dds0_freqsymbol.dev_attr.attr, > > + &iio_dev_attr_dds0_phasesymbol.dev_attr.attr, > > + &iio_dev_attr_dds0_out_enable.dev_attr.attr, > > + &iio_dev_attr_dds0_out1_enable.dev_attr.attr, > > + &iio_dev_attr_dds0_out0_wavetype.dev_attr.attr, > > + &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr, > > + &iio_dev_attr_dds0_out0_wavetype_available.dev_attr.attr, > > + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr, > > + &iio_dev_attr_name.dev_attr.attr, > > + NULL, > > +}; > > + > > +static mode_t ad9834_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 ad9834_state *st = iio_dev_get_devdata(dev_info); > > + > > + mode_t mode = attr->mode; > > + > > + if (st->devid == ID_AD9834) > > + return mode; > > + > > + if ((attr == &iio_dev_attr_dds0_out1_enable.dev_attr.attr) || > > + (attr == &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr) || > > + (attr == > > + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr)) > > + mode = 0; > > + > > + return mode; > > +} > > + > > +static const struct attribute_group ad9834_attribute_group = { > > + .attrs = ad9834_attributes, > > + .is_visible = ad9834_attr_is_visible, > > +}; > > + > > +static int __devinit ad9834_probe(struct spi_device *spi) > > +{ > > + struct ad9834_platform_data *pdata = spi->dev.platform_data; > > + struct ad9834_state *st; > > + int ret; > > + > > + if (!pdata) { > > + dev_dbg(&spi->dev, "no platform data?\n"); > > + return -ENODEV; > > + } > > + > > + st = kzalloc(sizeof(*st), GFP_KERNEL); > > + if (st == NULL) { > > + ret = -ENOMEM; > > + goto error_ret; > > + } > > + > > + st->reg = regulator_get(&spi->dev, "vcc"); > > + if (!IS_ERR(st->reg)) { > > + ret = regulator_enable(st->reg); > > + if (ret) > > + goto error_put_reg; > > + } > > + > > + st->mclk = pdata->mclk; > > + > > + spi_set_drvdata(spi, st); > > + > > + st->spi = spi; > > + st->devid = spi_get_device_id(spi)->driver_data; > > + > > + st->indio_dev = iio_allocate_device(); > > + if (st->indio_dev == NULL) { > > + ret = -ENOMEM; > > + goto error_disable_reg; > > + } > > + > > + st->indio_dev->dev.parent = &spi->dev; > > + st->indio_dev->attrs = &ad9834_attribute_group; > > + st->indio_dev->dev_data = (void *)(st); > nitpick: technically superflous brackets... ok > > + st->indio_dev->driver_module = THIS_MODULE; > > + st->indio_dev->modes = INDIO_DIRECT_MODE; > > + > > + /* Setup default messages */ > > + > > + st->xfer.tx_buf = &st->data; > > + st->xfer.len = 2; > > + > > + spi_message_init(&st->msg); > > + spi_message_add_tail(&st->xfer, &st->msg); > > + > > + st->freq_xfer[0].tx_buf = &st->freq_data[0]; > > + st->freq_xfer[0].len = 2; > > + st->freq_xfer[0].cs_change = 1; > > + st->freq_xfer[1].tx_buf = &st->freq_data[1]; > > + st->freq_xfer[1].len = 2; > > + > > + spi_message_init(&st->freq_msg); > > + spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); > > + spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); > > + > > + st->control = AD9834_B28 | AD9834_RESET; > > + > > + if (!pdata->en_div2) > > + st->control |= AD9834_DIV2; > > + > > + if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) > > + st->control |= AD9834_SIGN_PIB; > > + > > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > > + ret = spi_sync(st->spi, &st->msg); > > + if (ret) { > > + dev_err(&spi->dev, "device init failed\n"); > > + goto error_free_device; > > + } > > + > > + ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); > > + if (ret) > > + goto error_free_device; > > + > > + ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); > > + if (ret) > > + goto error_free_device; > > + > > + ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); > > + if (ret) > > + goto error_free_device; > > + > > + ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); > > + if (ret) > > + goto error_free_device; > > + > > + st->control &= ~AD9834_RESET; > > + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); > > + ret = spi_sync(st->spi, &st->msg); > > + if (ret) > > + goto error_free_device; > > + > > + ret = iio_device_register(st->indio_dev); > > + if (ret) > > + goto error_free_device; > > + > > + return 0; > > + > > +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 ret; > > +} > > + > > +static int __devexit ad9834_remove(struct spi_device *spi) > > +{ > > + struct ad9834_state *st = spi_get_drvdata(spi); > > + struct iio_dev *indio_dev = st->indio_dev; > > + > nitpick: could just use st->indio_dev in the next call and lose the > line above. It's the only use in this function. ok > > + iio_device_unregister(indio_dev); > > + if (!IS_ERR(st->reg)) { > > + regulator_disable(st->reg); > > + regulator_put(st->reg); > > + } > > + kfree(st); > > + return 0; > > +} > > + > > +static const struct spi_device_id ad9834_id[] = { > > + {"ad9833", ID_AD9833}, > > + {"ad9834", ID_AD9834}, > > + {} > > +}; > > + > > +static struct spi_driver ad9834_driver = { > > + .driver = { > > + .name = "ad9834", > > + .bus = &spi_bus_type, > > + .owner = THIS_MODULE, > > + }, > > + .probe = ad9834_probe, > > + .remove = __devexit_p(ad9834_remove), > > + .id_table = ad9834_id, > > +}; > > + > > +static int __init ad9834_init(void) > > +{ > > + return spi_register_driver(&ad9834_driver); > > +} > > +module_init(ad9834_init); > > + > > +static void __exit ad9834_exit(void) > > +{ > > + spi_unregister_driver(&ad9834_driver); > > +} > > +module_exit(ad9834_exit); > > + > > +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); > > +MODULE_DESCRIPTION("Analog Devices AD9833/AD9834 DDS"); > > +MODULE_LICENSE("GPL v2"); > > +MODULE_ALIAS("spi:ad9834"); > > diff --git a/drivers/staging/iio/dds/ad9834.h > b/drivers/staging/iio/dds/ad9834.h > > new file mode 100644 > > index 0000000..0fc3b88 > > --- /dev/null > > +++ b/drivers/staging/iio/dds/ad9834.h > > @@ -0,0 +1,112 @@ > > +/* > > + * AD9834 SPI DDS driver > > + * > > + * Copyright 2010 Analog Devices Inc. > > + * > > + * Licensed under the GPL-2 or later. > > + */ > > +#ifndef IIO_DDS_AD9834_H_ > > +#define IIO_DDS_AD9834_H_ > > + > > +/* Registers */ > > + > > +#define AD9834_REG_CMD (0 << 14) > > +#define AD9834_REG_FREQ0 (1 << 14) > > +#define AD9834_REG_FREQ1 (2 << 14) > > +#define AD9834_REG_PHASE0 (6 << 13) > > +#define AD9834_REG_PHASE1 (7 << 13) > > + > > +/* Command Control Bits */ > > + > > +#define AD9834_B28 (1 << 13) > > +#define AD9834_HLB (1 << 12) > > +#define AD9834_FSEL (1 << 11) > > +#define AD9834_PSEL (1 << 10) > > +#define AD9834_PIN_SW (1 << 9) > > +#define AD9834_RESET (1 << 8) > > +#define AD9834_SLEEP1 (1 << 7) > > +#define AD9834_SLEEP12 (1 << 6) > > +#define AD9834_OPBITEN (1 << 5) > > +#define AD9834_SIGN_PIB (1 << 4) > > +#define AD9834_DIV2 (1 << 3) > > +#define AD9834_MODE (1 << 1) > > + > > +#define AD9834_FREQ_BITS 28 > > +#define AD9834_PHASE_BITS 12 > > + > > +#define RES_MASK(bits) ((1 << (bits)) - 1) > > + > > +/** > > + * struct ad9834_state - driver instance specific data > > + * @indio_dev: the industrial I/O device > > + * @spi: spi_device > > + * @reg: supply regulator > > + * @mclk: external master clock > > + * @control: cached control word > > + * @xfer: default spi transfer > > + * @msg: default spi message > > + * @freq_xfer: tuning word spi transfer > > + * @freq_msg: tuning word spi message > > + * @data: spi transmit buffer > > + * @freq_data: tuning word spi transmit buffer > > + */ > > + > > +struct ad9834_state { > > + struct iio_dev *indio_dev; > > + struct spi_device *spi; > > + struct regulator *reg; > > + unsigned int mclk; > > + unsigned short control; > > + unsigned short devid; > > + struct spi_transfer xfer; > > + struct spi_message msg; > > + struct spi_transfer freq_xfer[2]; > > + struct spi_message freq_msg; > > + > > + /* > > + * DMA (thus cache coherency maintenance) requires the > > + * transfer buffers to live in their own cache lines. > > + */ > > + unsigned short data ____cacheline_aligned; > > + unsigned short freq_data[2] ; > > +}; > > + > > + > > +/* > > + * TODO: struct ad7887_platform_data needs to go into > include/linux/iio > > + */ > > + > > +/** > > + * struct ad9834_platform_data - platform specific information > > + * @mclk: master clock in Hz > > + * @freq0: power up freq0 tuning word in Hz > > + * @freq1: power up freq1 tuning word in Hz > > + * @phase0: power up phase0 value [0..4095] correlates with > 0..2PI > > + * @phase1: power up phase1 value [0..4095] correlates with > 0..2PI > > + * @en_div2: digital output/2 is passed to the SIGN BIT OUT > pin > > + * @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is > connected to the > > + * SIGN BIT OUT pin. en_div2 controls whether it is the > MSB > > + * or MSB/2 that is output. if en_signbit_msb_out=false, > > + * the on-board comparator is connected to SIGN BIT OUT > > + */ > > + > > +struct ad9834_platform_data { > > + unsigned int mclk; > > + unsigned int freq0; > > + unsigned int freq1; > > + unsigned short phase0; > > + unsigned short phase1; > > + bool en_div2; > > + bool en_signbit_msb_out; > > +}; > > + > > +/** > > + * ad9834_supported_device_ids: > > + */ > > + > > +enum ad9834_supported_device_ids { > > + ID_AD9833, > > + ID_AD9834, > > +}; > > + > > +#endif /* IIO_DDS_AD9834_H_ */ > > -- > > 1.6.0.2 > > > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver 2010-12-14 11:01 ` Hennerich, Michael @ 2010-12-14 12:30 ` Jonathan Cameron 0 siblings, 0 replies; 9+ messages in thread From: Jonathan Cameron @ 2010-12-14 12:30 UTC (permalink / raw) To: Hennerich, Michael; +Cc: linux-iio, linux-kernel, Drivers, device-drivers-devel On 12/14/10 11:01, Hennerich, Michael wrote: >> Jonathan Cameron wrote on 2010-12-13: >>> On 12/13/10 16:27, michael.hennerich@analog.com wrote: >>> From: Michael Hennerich <michael.hennerich@analog.com> >>> >>> Changes since RFC/v1: >>> IIO: Apply list review feedback >>> >>> Apply list review feedback: >>> Rename attributes to fit IIO convention used in other drivers. >>> Fix typos. >>> Provide ddsX_out_enable as opposed to ddsX_out_disable. >>> Use proper __devexit marking. >>> Use strict_strtoul() to avoid negatives. >>> >> Couple of nitpicks inline. Subject to changes due to changes in macros >> suggested in previous patch (or you convincing me otherwise): >>> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> >>> Reviewed-by: Datta Shubhrajyoti <shubhrajyoti@ti.com> >> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> >>> --- >>> drivers/staging/iio/dds/Kconfig | 7 + >>> drivers/staging/iio/dds/Makefile | 1 + >>> drivers/staging/iio/dds/ad9834.c | 482 >> ++++++++++++++++++++++++++++++++++++++ >>> drivers/staging/iio/dds/ad9834.h | 112 +++++++++ >>> 4 files changed, 602 insertions(+), 0 deletions(-) >>> create mode 100644 drivers/staging/iio/dds/ad9834.c >>> create mode 100644 drivers/staging/iio/dds/ad9834.h >>> >>> diff --git a/drivers/staging/iio/dds/Kconfig >> b/drivers/staging/iio/dds/Kconfig >>> index 7969be2..4c9cce3 100644 >>> --- a/drivers/staging/iio/dds/Kconfig >>> +++ b/drivers/staging/iio/dds/Kconfig >>> @@ -17,6 +17,13 @@ config AD9832 >>> Say yes here to build support for Analog Devices DDS chip >>> ad9832 and ad9835, provides direct access via sysfs. >>> >>> +config AD9834 >>> + tristate "Analog Devices ad9833/4/ driver" >>> + depends on SPI >>> + help >>> + Say yes here to build support for Analog Devices DDS chip >>> + AD9833 and AD9834, provides direct access via sysfs. >>> + >>> config AD9850 >>> tristate "Analog Devices ad9850/1 driver" >>> depends on SPI >>> diff --git a/drivers/staging/iio/dds/Makefile >> b/drivers/staging/iio/dds/Makefile >>> index 6f274ac..1477461 100644 >>> --- a/drivers/staging/iio/dds/Makefile >>> +++ b/drivers/staging/iio/dds/Makefile >>> @@ -4,6 +4,7 @@ >>> >>> obj-$(CONFIG_AD5930) += ad5930.o >>> obj-$(CONFIG_AD9832) += ad9832.o >>> +obj-$(CONFIG_AD9834) += ad9834.o >>> obj-$(CONFIG_AD9850) += ad9850.o >>> obj-$(CONFIG_AD9852) += ad9852.o >>> obj-$(CONFIG_AD9910) += ad9910.o >>> diff --git a/drivers/staging/iio/dds/ad9834.c >> b/drivers/staging/iio/dds/ad9834.c >>> new file mode 100644 >>> index 0000000..df3d68c >>> --- /dev/null >>> +++ b/drivers/staging/iio/dds/ad9834.c >>> @@ -0,0 +1,482 @@ >>> +/* >>> + * AD9834 SPI DAC driver >>> + * >>> + * Copyright 2010 Analog Devices Inc. >>> + * >>> + * Licensed under the GPL-2 or later. >>> + */ >>> + >>> +#include <linux/interrupt.h> >>> +#include <linux/workqueue.h> >>> +#include <linux/device.h> >>> +#include <linux/kernel.h> >>> +#include <linux/slab.h> >>> +#include <linux/sysfs.h> >>> +#include <linux/list.h> >>> +#include <linux/spi/spi.h> >>> +#include <linux/regulator/consumer.h> >>> +#include <linux/err.h> >>> +#include <asm/div64.h> >>> + >>> +#include "../iio.h" >>> +#include "../sysfs.h" >>> +#include "dds.h" >>> + >>> +#include "ad9834.h" >>> + >>> +static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned >> long fout) >>> +{ >>> + unsigned long long freqreg = (u64) fout * (u64) (1 << >> AD9834_FREQ_BITS); >>> + do_div(freqreg, mclk); >>> + return freqreg; >>> +} >>> + >>> +static int ad9834_write_frequency(struct ad9834_state *st, >>> + unsigned long addr, unsigned long fout) >>> +{ >>> + unsigned long regval; >>> + >>> + if (fout > (st->mclk / 2)) >>> + return -EINVAL; >>> + >>> + regval = ad9834_calc_freqreg(st->mclk, fout); >>> + >>> + st->freq_data[0] = cpu_to_be16(addr | (regval & >>> + RES_MASK(AD9834_FREQ_BITS / 2))); >>> + st->freq_data[1] = cpu_to_be16(addr | ((regval >> >>> + (AD9834_FREQ_BITS / 2)) & >>> + RES_MASK(AD9834_FREQ_BITS / 2))); >>> + >>> + return spi_sync(st->spi, &st->freq_msg);; >>> +} >>> + >>> +static int ad9834_write_phase(struct ad9834_state *st, >>> + unsigned long addr, unsigned long phase) >>> +{ >>> + if (phase > (1 << AD9834_PHASE_BITS)) >>> + return -EINVAL; >>> + st->data = cpu_to_be16(addr | phase); >>> + >>> + return spi_sync(st->spi, &st->msg); >>> +} >>> + >>> +static ssize_t ad9834_write(struct device *dev, >>> + struct device_attribute *attr, >>> + const char *buf, >>> + size_t len) >>> +{ >>> + struct iio_dev *dev_info = dev_get_drvdata(dev); >>> + struct ad9834_state *st = dev_info->dev_data; >>> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); >>> + int ret; >>> + long val; >>> + >>> + ret = strict_strtoul(buf, 10, &val); >>> + if (ret) >>> + goto error_ret; >>> + >>> + mutex_lock(&dev_info->mlock); >>> + switch (this_attr->address) { >>> + case AD9834_REG_FREQ0: >>> + case AD9834_REG_FREQ1: >>> + ret = ad9834_write_frequency(st, this_attr->address, val); >>> + break; >>> + case AD9834_REG_PHASE0: >>> + case AD9834_REG_PHASE1: >>> + ret = ad9834_write_phase(st, this_attr->address, val); >>> + break; >>> + case AD9834_OPBITEN: >>> + if (st->control & AD9834_MODE) { >>> + ret = -EINVAL; /* AD9843 reserved mode */ >>> + break; >>> + } >>> + >>> + if (val) >>> + st->control |= AD9834_OPBITEN; >>> + else >>> + st->control &= ~AD9834_OPBITEN; >>> + >>> + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); >>> + ret = spi_sync(st->spi, &st->msg); >>> + break; >>> + case AD9834_PIN_SW: >>> + if (val) >>> + st->control |= AD9834_PIN_SW; >>> + else >>> + st->control &= ~AD9834_PIN_SW; >>> + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); >>> + ret = spi_sync(st->spi, &st->msg); >>> + break; >>> + case AD9834_FSEL: >>> + case AD9834_PSEL: >>> + if (val == 0) >>> + st->control &= ~(this_attr->address | AD9834_PIN_SW); >>> + else if (val == 1) { >>> + st->control |= this_attr->address; >>> + st->control &= ~AD9834_PIN_SW; >>> + } else { >>> + ret = -EINVAL; >>> + break; >>> + } >>> + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); >>> + ret = spi_sync(st->spi, &st->msg); >>> + break; >>> + case AD9834_RESET: >>> + if (val) >>> + st->control &= ~AD9834_RESET; >>> + else >>> + st->control |= AD9834_RESET; >>> + >>> + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); >>> + ret = spi_sync(st->spi, &st->msg); >>> + break; >>> + default: >>> + ret = -ENODEV; >>> + } >>> + mutex_unlock(&dev_info->mlock); >>> + >>> +error_ret: >>> + return ret ? ret : len; >>> +} >>> + >>> +static ssize_t ad9834_store_wavetype(struct device *dev, >>> + struct device_attribute *attr, >>> + const char *buf, >>> + size_t len) >>> +{ >>> + struct iio_dev *dev_info = dev_get_drvdata(dev); >>> + struct ad9834_state *st = dev_info->dev_data; >>> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); >>> + int ret = 0; >>> + bool is_ad9833 = st->devid == ID_AD9833; >>> + >>> + mutex_lock(&dev_info->mlock); >>> + >>> + switch (this_attr->address) { >>> + case 0: >>> + if (sysfs_streq(buf, "sine")) { >>> + st->control &= ~AD9834_MODE; >>> + if (is_ad9833) >>> + st->control &= ~AD9834_OPBITEN; >>> + } else if (sysfs_streq(buf, "triangle")) { >>> + if (is_ad9833) { >>> + st->control &= ~AD9834_OPBITEN; >>> + st->control |= AD9834_MODE; >>> + } else if (st->control & AD9834_OPBITEN) { >>> + ret = -EINVAL; /* AD9843 reserved mode */ >>> + } else { >>> + st->control |= AD9834_MODE; >>> + } >>> + } else if (is_ad9833 && sysfs_streq(buf, "square")) { >>> + st->control &= ~AD9834_MODE; >>> + st->control |= AD9834_OPBITEN; >>> + } else { >>> + ret = -EINVAL; >>> + } >>> + >>> + break; >>> + case 1: >>> + if (sysfs_streq(buf, "square") && >>> + !(st->control & AD9834_MODE)) { >>> + st->control &= ~AD9834_MODE; >>> + st->control |= AD9834_OPBITEN; >>> + } else { >>> + ret = -EINVAL; >>> + } >>> + break; >>> + default: >>> + ret = -EINVAL; >>> + break; >>> + } >>> + >>> + if (!ret) { >>> + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); >>> + ret = spi_sync(st->spi, &st->msg); >>> + } >>> + mutex_unlock(&dev_info->mlock); >> In correct tabbing on previous line? (or my email client has messed up) > > Your email client doing the right thing. > >>> + >>> + return ret ? ret : len; >>> +} >>> + >>> +static ssize_t ad9834_show_name(struct device *dev, >>> + struct device_attribute *attr, >>> + char *buf) >>> +{ >>> + struct iio_dev *dev_info = dev_get_drvdata(dev); >>> + struct ad9834_state *st = iio_dev_get_devdata(dev_info); >>> + >>> + return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name); >>> +} >>> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad9834_show_name, NULL, 0); >>> + >>> +static ssize_t ad9834_show_out0_wavetype_available(struct device >> *dev, >>> + struct device_attribute *attr, >>> + char *buf) >>> +{ >>> + struct iio_dev *dev_info = dev_get_drvdata(dev); >>> + struct ad9834_state *st = iio_dev_get_devdata(dev_info); >>> + char *str; >>> + >>> + if (st->devid == ID_AD9833) >>> + str = "sine triangle square"; >>> + else if (st->control & AD9834_OPBITEN) >>> + str = "sine"; >>> + else >>> + str = "sine triangle"; >>> + >>> + return sprintf(buf, "%s\n", str); >>> +} >>> + >>> + >>> +static IIO_DEVICE_ATTR(dds0_out0_wavetype_available, S_IRUGO, >>> + ad9834_show_out0_wavetype_available, NULL, 0); >>> + >>> +static ssize_t ad9834_show_out1_wavetype_available(struct device >> *dev, >>> + struct device_attribute *attr, >>> + char *buf) >>> +{ >>> + struct iio_dev *dev_info = dev_get_drvdata(dev); >>> + struct ad9834_state *st = iio_dev_get_devdata(dev_info); >>> + char *str; >>> + >>> + if (st->control & AD9834_MODE) >>> + str = ""; >> I 'think' that the is_visible below makes this first condition >> impossible? > > No - the is_visible prevents this file to be available on the AD9833. > However this expresses a dependency on the AD9834 between the two outputs, where > the 'square' output on out1 is not available in case out0 is set to 'triangle' ah, I'd missed that. > >> I've never used the is_visible stuff so not entirely sure I've >> understood >> it correctly... >> That probably means that you can use a const attr for this... > > No > >>> + else >>> + str = "square"; >>> + >>> + return sprintf(buf, "%s\n", str); >>> +} >>> + >>> +static IIO_DEVICE_ATTR(dds0_out1_wavetype_available, S_IRUGO, >>> + ad9834_show_out1_wavetype_available, NULL, 0); >>> + >>> +/** >>> + * see dds.h for further information >>> + */ >>> + >>> +static IIO_DEV_ATTR_FREQ(0, 0, ad9834_write, AD9834_REG_FREQ0); >>> +static IIO_DEV_ATTR_FREQ(0, 1, ad9834_write, AD9834_REG_FREQ1); >>> +static IIO_DEV_ATTR_FREQSYMBOL(0, ad9834_write, AD9834_FSEL); >>> +static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ >>> + >>> +static IIO_DEV_ATTR_PHASE(0, 0, ad9834_write, AD9834_REG_PHASE0); >>> +static IIO_DEV_ATTR_PHASE(0, 1, ad9834_write, AD9834_REG_PHASE1); >>> +static IIO_DEV_ATTR_PHASESYMBOL(0, ad9834_write, AD9834_PSEL); >>> +static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 >> rad*/ >>> + >>> +static IIO_DEV_ATTR_PINCONTROL_EN(0, ad9834_write, AD9834_PIN_SW); >>> +static IIO_DEV_ATTR_OUT_ENABLE(0, ad9834_write, AD9834_RESET); >>> +static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, ad9834_write, AD9834_OPBITEN); >>> +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); >>> +static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); >>> + >>> +static struct attribute *ad9834_attributes[] = { >>> + &iio_dev_attr_dds0_freq0.dev_attr.attr, >>> + &iio_dev_attr_dds0_freq1.dev_attr.attr, >>> + &iio_const_attr_dds0_freq_scale.dev_attr.attr, >>> + &iio_dev_attr_dds0_phase0.dev_attr.attr, >>> + &iio_dev_attr_dds0_phase1.dev_attr.attr, >>> + &iio_const_attr_dds0_phase_scale.dev_attr.attr, >>> + &iio_dev_attr_dds0_pincontrol_en.dev_attr.attr, >>> + &iio_dev_attr_dds0_freqsymbol.dev_attr.attr, >>> + &iio_dev_attr_dds0_phasesymbol.dev_attr.attr, >>> + &iio_dev_attr_dds0_out_enable.dev_attr.attr, >>> + &iio_dev_attr_dds0_out1_enable.dev_attr.attr, >>> + &iio_dev_attr_dds0_out0_wavetype.dev_attr.attr, >>> + &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr, >>> + &iio_dev_attr_dds0_out0_wavetype_available.dev_attr.attr, >>> + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr, >>> + &iio_dev_attr_name.dev_attr.attr, >>> + NULL, >>> +}; >>> + >>> +static mode_t ad9834_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 ad9834_state *st = iio_dev_get_devdata(dev_info); >>> + >>> + mode_t mode = attr->mode; >>> + >>> + if (st->devid == ID_AD9834) >>> + return mode; >>> + >>> + if ((attr == &iio_dev_attr_dds0_out1_enable.dev_attr.attr) || >>> + (attr == &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr) || >>> + (attr == >>> + &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr)) >>> + mode = 0; >>> + >>> + return mode; >>> +} >>> + >>> +static const struct attribute_group ad9834_attribute_group = { >>> + .attrs = ad9834_attributes, >>> + .is_visible = ad9834_attr_is_visible, >>> +}; >>> + >>> +static int __devinit ad9834_probe(struct spi_device *spi) >>> +{ >>> + struct ad9834_platform_data *pdata = spi->dev.platform_data; >>> + struct ad9834_state *st; >>> + int ret; >>> + >>> + if (!pdata) { >>> + dev_dbg(&spi->dev, "no platform data?\n"); >>> + return -ENODEV; >>> + } >>> + >>> + st = kzalloc(sizeof(*st), GFP_KERNEL); >>> + if (st == NULL) { >>> + ret = -ENOMEM; >>> + goto error_ret; >>> + } >>> + >>> + st->reg = regulator_get(&spi->dev, "vcc"); >>> + if (!IS_ERR(st->reg)) { >>> + ret = regulator_enable(st->reg); >>> + if (ret) >>> + goto error_put_reg; >>> + } >>> + >>> + st->mclk = pdata->mclk; >>> + >>> + spi_set_drvdata(spi, st); >>> + >>> + st->spi = spi; >>> + st->devid = spi_get_device_id(spi)->driver_data; >>> + >>> + st->indio_dev = iio_allocate_device(); >>> + if (st->indio_dev == NULL) { >>> + ret = -ENOMEM; >>> + goto error_disable_reg; >>> + } >>> + >>> + st->indio_dev->dev.parent = &spi->dev; >>> + st->indio_dev->attrs = &ad9834_attribute_group; >>> + st->indio_dev->dev_data = (void *)(st); >> nitpick: technically superflous brackets... > > ok > >>> + st->indio_dev->driver_module = THIS_MODULE; >>> + st->indio_dev->modes = INDIO_DIRECT_MODE; >>> + >>> + /* Setup default messages */ >>> + >>> + st->xfer.tx_buf = &st->data; >>> + st->xfer.len = 2; >>> + >>> + spi_message_init(&st->msg); >>> + spi_message_add_tail(&st->xfer, &st->msg); >>> + >>> + st->freq_xfer[0].tx_buf = &st->freq_data[0]; >>> + st->freq_xfer[0].len = 2; >>> + st->freq_xfer[0].cs_change = 1; >>> + st->freq_xfer[1].tx_buf = &st->freq_data[1]; >>> + st->freq_xfer[1].len = 2; >>> + >>> + spi_message_init(&st->freq_msg); >>> + spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); >>> + spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); >>> + >>> + st->control = AD9834_B28 | AD9834_RESET; >>> + >>> + if (!pdata->en_div2) >>> + st->control |= AD9834_DIV2; >>> + >>> + if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) >>> + st->control |= AD9834_SIGN_PIB; >>> + >>> + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); >>> + ret = spi_sync(st->spi, &st->msg); >>> + if (ret) { >>> + dev_err(&spi->dev, "device init failed\n"); >>> + goto error_free_device; >>> + } >>> + >>> + ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); >>> + if (ret) >>> + goto error_free_device; >>> + >>> + ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); >>> + if (ret) >>> + goto error_free_device; >>> + >>> + ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); >>> + if (ret) >>> + goto error_free_device; >>> + >>> + ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); >>> + if (ret) >>> + goto error_free_device; >>> + >>> + st->control &= ~AD9834_RESET; >>> + st->data = cpu_to_be16(AD9834_REG_CMD | st->control); >>> + ret = spi_sync(st->spi, &st->msg); >>> + if (ret) >>> + goto error_free_device; >>> + >>> + ret = iio_device_register(st->indio_dev); >>> + if (ret) >>> + goto error_free_device; >>> + >>> + return 0; >>> + >>> +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 ret; >>> +} >>> + >>> +static int __devexit ad9834_remove(struct spi_device *spi) >>> +{ >>> + struct ad9834_state *st = spi_get_drvdata(spi); >>> + struct iio_dev *indio_dev = st->indio_dev; >>> + >> nitpick: could just use st->indio_dev in the next call and lose the >> line above. It's the only use in this function. > > ok > >>> + iio_device_unregister(indio_dev); >>> + if (!IS_ERR(st->reg)) { >>> + regulator_disable(st->reg); >>> + regulator_put(st->reg); >>> + } >>> + kfree(st); >>> + return 0; >>> +} >>> + >>> +static const struct spi_device_id ad9834_id[] = { >>> + {"ad9833", ID_AD9833}, >>> + {"ad9834", ID_AD9834}, >>> + {} >>> +}; >>> + >>> +static struct spi_driver ad9834_driver = { >>> + .driver = { >>> + .name = "ad9834", >>> + .bus = &spi_bus_type, >>> + .owner = THIS_MODULE, >>> + }, >>> + .probe = ad9834_probe, >>> + .remove = __devexit_p(ad9834_remove), >>> + .id_table = ad9834_id, >>> +}; >>> + >>> +static int __init ad9834_init(void) >>> +{ >>> + return spi_register_driver(&ad9834_driver); >>> +} >>> +module_init(ad9834_init); >>> + >>> +static void __exit ad9834_exit(void) >>> +{ >>> + spi_unregister_driver(&ad9834_driver); >>> +} >>> +module_exit(ad9834_exit); >>> + >>> +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); >>> +MODULE_DESCRIPTION("Analog Devices AD9833/AD9834 DDS"); >>> +MODULE_LICENSE("GPL v2"); >>> +MODULE_ALIAS("spi:ad9834"); >>> diff --git a/drivers/staging/iio/dds/ad9834.h >> b/drivers/staging/iio/dds/ad9834.h >>> new file mode 100644 >>> index 0000000..0fc3b88 >>> --- /dev/null >>> +++ b/drivers/staging/iio/dds/ad9834.h >>> @@ -0,0 +1,112 @@ >>> +/* >>> + * AD9834 SPI DDS driver >>> + * >>> + * Copyright 2010 Analog Devices Inc. >>> + * >>> + * Licensed under the GPL-2 or later. >>> + */ >>> +#ifndef IIO_DDS_AD9834_H_ >>> +#define IIO_DDS_AD9834_H_ >>> + >>> +/* Registers */ >>> + >>> +#define AD9834_REG_CMD (0 << 14) >>> +#define AD9834_REG_FREQ0 (1 << 14) >>> +#define AD9834_REG_FREQ1 (2 << 14) >>> +#define AD9834_REG_PHASE0 (6 << 13) >>> +#define AD9834_REG_PHASE1 (7 << 13) >>> + >>> +/* Command Control Bits */ >>> + >>> +#define AD9834_B28 (1 << 13) >>> +#define AD9834_HLB (1 << 12) >>> +#define AD9834_FSEL (1 << 11) >>> +#define AD9834_PSEL (1 << 10) >>> +#define AD9834_PIN_SW (1 << 9) >>> +#define AD9834_RESET (1 << 8) >>> +#define AD9834_SLEEP1 (1 << 7) >>> +#define AD9834_SLEEP12 (1 << 6) >>> +#define AD9834_OPBITEN (1 << 5) >>> +#define AD9834_SIGN_PIB (1 << 4) >>> +#define AD9834_DIV2 (1 << 3) >>> +#define AD9834_MODE (1 << 1) >>> + >>> +#define AD9834_FREQ_BITS 28 >>> +#define AD9834_PHASE_BITS 12 >>> + >>> +#define RES_MASK(bits) ((1 << (bits)) - 1) >>> + >>> +/** >>> + * struct ad9834_state - driver instance specific data >>> + * @indio_dev: the industrial I/O device >>> + * @spi: spi_device >>> + * @reg: supply regulator >>> + * @mclk: external master clock >>> + * @control: cached control word >>> + * @xfer: default spi transfer >>> + * @msg: default spi message >>> + * @freq_xfer: tuning word spi transfer >>> + * @freq_msg: tuning word spi message >>> + * @data: spi transmit buffer >>> + * @freq_data: tuning word spi transmit buffer >>> + */ >>> + >>> +struct ad9834_state { >>> + struct iio_dev *indio_dev; >>> + struct spi_device *spi; >>> + struct regulator *reg; >>> + unsigned int mclk; >>> + unsigned short control; >>> + unsigned short devid; >>> + struct spi_transfer xfer; >>> + struct spi_message msg; >>> + struct spi_transfer freq_xfer[2]; >>> + struct spi_message freq_msg; >>> + >>> + /* >>> + * DMA (thus cache coherency maintenance) requires the >>> + * transfer buffers to live in their own cache lines. >>> + */ >>> + unsigned short data ____cacheline_aligned; >>> + unsigned short freq_data[2] ; >>> +}; >>> + >>> + >>> +/* >>> + * TODO: struct ad7887_platform_data needs to go into >> include/linux/iio >>> + */ >>> + >>> +/** >>> + * struct ad9834_platform_data - platform specific information >>> + * @mclk: master clock in Hz >>> + * @freq0: power up freq0 tuning word in Hz >>> + * @freq1: power up freq1 tuning word in Hz >>> + * @phase0: power up phase0 value [0..4095] correlates with >> 0..2PI >>> + * @phase1: power up phase1 value [0..4095] correlates with >> 0..2PI >>> + * @en_div2: digital output/2 is passed to the SIGN BIT OUT >> pin >>> + * @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is >> connected to the >>> + * SIGN BIT OUT pin. en_div2 controls whether it is the >> MSB >>> + * or MSB/2 that is output. if en_signbit_msb_out=false, >>> + * the on-board comparator is connected to SIGN BIT OUT >>> + */ >>> + >>> +struct ad9834_platform_data { >>> + unsigned int mclk; >>> + unsigned int freq0; >>> + unsigned int freq1; >>> + unsigned short phase0; >>> + unsigned short phase1; >>> + bool en_div2; >>> + bool en_signbit_msb_out; >>> +}; >>> + >>> +/** >>> + * ad9834_supported_device_ids: >>> + */ >>> + >>> +enum ad9834_supported_device_ids { >>> + ID_AD9833, >>> + ID_AD9834, >>> +}; >>> + >>> +#endif /* IIO_DDS_AD9834_H_ */ >>> -- >>> 1.6.0.2 >>> >>> > > ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2010-12-14 15:10 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2010-12-14 14:54 [PATCH 1/3] IIO: Direct digital synthesis abi documentation michael.hennerich 2010-12-14 14:54 ` [PATCH 2/3] IIO: dds.h convenience macros michael.hennerich 2010-12-14 15:10 ` Jonathan Cameron 2010-12-14 14:54 ` [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver michael.hennerich 2010-12-14 15:09 ` [PATCH 1/3] IIO: Direct digital synthesis abi documentation Jonathan Cameron -- strict thread matches above, loose matches on Subject: below -- 2010-12-13 16:27 michael.hennerich 2010-12-13 16:27 ` [PATCH 3/3] IIO: DDS: AD9833 / AD9834 driver michael.hennerich 2010-12-13 21:40 ` Jonathan Cameron 2010-12-14 11:01 ` Hennerich, Michael 2010-12-14 12:30 ` Jonathan Cameron
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®