mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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

* [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

* 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

* 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

* 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-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

* [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

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®