All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next
@ 2010-10-26 18:17 Mike Frysinger
  2010-10-26 18:17 ` [PATCH 01/36] staging: iio: adis16350: add missing reference to temp offset Mike Frysinger
                   ` (36 more replies)
  0 siblings, 37 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel

Changes since previous patchset:
	- dropped drives duplicated with hwmon
		- AD7414
		- AD7416
		- ADT7408
	- addressed a bunch of random feedback across most drivers
		- renamed ad774x.c to ad7745.c
		- renamed adis16261* to adis16251*
		- improve Kconfig text to help out `grep`
		- fix up abi in a few places
		- add some error checking
		- disable ring buffer support when missing
		- drop i2c irq_flags for now
	- added Jonathan's Acked-by tag
	- added ADI e-mail info to TODO

Hopefully there shouldn't be any roadblocks now before pushing to GregKH.
I'll do so in a day or two if there aren't any issues.

Barry Song (16):
  staging: iio: new adis16201 driver
  staging: iio: new adis16203 driver
  staging: iio: new adis16204 driver
  staging: iio: adc: new driver for AD7150/1/6 devices
  staging: iio: adc: new driver for AD7152/3 devices
  staging: iio: adc: new driver for AD7745/6/7 devices
  staging: iio: gyro: new driver for ADIS16251 devices
  staging: iio: gyro: new driver for ADIS16060 digital output gyros
  staging: iio: gyro: new driver for ADIS16080 digital output gyros
  staging: iio: gyro: new driver for ADIS16130 digital output gyros
  staging: iio: dac: new driver for AD5624R devices
  staging: iio: meter: new driver for ADE7753/6 devices
  staging: iio: meter: new driver for ADE7754 devices
  staging: iio: meter: new driver for ADE7758 devices
  staging: iio: meter: new driver for ADE7759 devices
  staging: iio: meter: new driver for ADE7854/58/68/78 devices

Cliff Cai (6):
  staging: iio: dds: new driver for AD5930/2 devices
  staging: iio: dds: new driver for AD9832/3/4/5 devices
  staging: iio: dds: new driver for AD9850/1 devices
  staging: iio: dds: new driver for AD9852/4 devices
  staging: iio: dds: new driver for AD9910 devices
  staging: iio: dds: new driver for AD9951 devices

Graf Yang (3):
  staging: iio: resolver: new driver for AD2S90 devices
  staging: iio: resolver: new driver for AD2S1200/1205 devices
  staging: iio: resolver: new driver for AD2S1210 devices

Michael Hennerich (1):
  staging: iio: gyro: make sure grep can find the ADIS16265 support

Mike Frysinger (2):
  staging: iio: adis16350: add missing reference to temp offset
  staging: iio: add ADI info to TODO

Sonic Zhang (8):
  staging: iio: new ADT7316/7/8 and ADT7516/7/9 driver
  staging: iio: adc: new driver for AD7291 devices
  staging: iio: adc: new driver for AD7298 devices
  staging: iio: adc: new driver for AD7314 devices
  staging: iio: adc: new driver for AD7816 devices
  staging: iio: adc: new driver for ADT75 temperature sensors
  staging: iio: adc: new driver for ADT7310 temperature sensors
  staging: iio: adc: new driver for ADT7410 temperature sensors

 drivers/staging/iio/Kconfig                   |    6 +-
 drivers/staging/iio/Makefile                  |    7 +-
 drivers/staging/iio/TODO                      |    4 +
 drivers/staging/iio/accel/Kconfig             |   27 +
 drivers/staging/iio/accel/Makefile            |   12 +
 drivers/staging/iio/accel/accel.h             |   20 +
 drivers/staging/iio/accel/adis16201.h         |  150 ++
 drivers/staging/iio/accel/adis16201_core.c    |  659 +++++++
 drivers/staging/iio/accel/adis16201_ring.c    |  218 +++
 drivers/staging/iio/accel/adis16201_trigger.c |  122 ++
 drivers/staging/iio/accel/adis16203.h         |  143 ++
 drivers/staging/iio/accel/adis16203_core.c    |  568 ++++++
 drivers/staging/iio/accel/adis16203_ring.c    |  211 +++
 drivers/staging/iio/accel/adis16203_trigger.c |  122 ++
 drivers/staging/iio/accel/adis16204.h         |  151 ++
 drivers/staging/iio/accel/adis16204_core.c    |  613 +++++++
 drivers/staging/iio/accel/adis16204_ring.c    |  206 +++
 drivers/staging/iio/accel/adis16204_trigger.c |  122 ++
 drivers/staging/iio/adc/Kconfig               |   77 +-
 drivers/staging/iio/adc/Makefile              |   11 +
 drivers/staging/iio/adc/ad7150.c              |  877 +++++++++
 drivers/staging/iio/adc/ad7152.c              |  610 +++++++
 drivers/staging/iio/adc/ad7291.c              | 1039 +++++++++++
 drivers/staging/iio/adc/ad7298.c              |  501 +++++
 drivers/staging/iio/adc/ad7314.c              |  308 ++++
 drivers/staging/iio/adc/ad7745.c              |  734 ++++++++
 drivers/staging/iio/adc/ad7816.c              |  535 ++++++
 drivers/staging/iio/adc/adt7310.c             |  952 ++++++++++
 drivers/staging/iio/adc/adt7410.c             |  915 ++++++++++
 drivers/staging/iio/adc/adt75.c               |  732 ++++++++
 drivers/staging/iio/addac/Kconfig             |   25 +
 drivers/staging/iio/addac/Makefile            |    7 +
 drivers/staging/iio/addac/adt7316-i2c.c       |  170 ++
 drivers/staging/iio/addac/adt7316-spi.c       |  180 ++
 drivers/staging/iio/addac/adt7316.c           | 2402 +++++++++++++++++++++++++
 drivers/staging/iio/addac/adt7316.h           |   33 +
 drivers/staging/iio/dac/Kconfig               |   11 +
 drivers/staging/iio/dac/Makefile              |    5 +
 drivers/staging/iio/dac/ad5624r.h             |   21 +
 drivers/staging/iio/dac/ad5624r_spi.c         |  313 ++++
 drivers/staging/iio/dac/dac.h                 |    6 +
 drivers/staging/iio/dds/Kconfig               |   46 +
 drivers/staging/iio/dds/Makefile              |   10 +
 drivers/staging/iio/dds/ad5930.c              |  170 ++
 drivers/staging/iio/dds/ad9832.c              |  266 +++
 drivers/staging/iio/dds/ad9850.c              |  156 ++
 drivers/staging/iio/dds/ad9852.c              |  314 ++++
 drivers/staging/iio/dds/ad9910.c              |  454 +++++
 drivers/staging/iio/dds/ad9951.c              |  254 +++
 drivers/staging/iio/gyro/Kconfig              |   38 +-
 drivers/staging/iio/gyro/Makefile             |   12 +
 drivers/staging/iio/gyro/adis16060.h          |  101 +
 drivers/staging/iio/gyro/adis16060_core.c     |  319 ++++
 drivers/staging/iio/gyro/adis16080.h          |  102 ++
 drivers/staging/iio/gyro/adis16080_core.c     |  271 +++
 drivers/staging/iio/gyro/adis16130.h          |  108 ++
 drivers/staging/iio/gyro/adis16130_core.c     |  313 ++++
 drivers/staging/iio/gyro/adis16251.h          |  185 ++
 drivers/staging/iio/gyro/adis16251_core.c     |  777 ++++++++
 drivers/staging/iio/gyro/adis16260_core.c     |    2 +-
 drivers/staging/iio/imu/adis16350_core.c      |    1 +
 drivers/staging/iio/meter/Kconfig             |   61 +
 drivers/staging/iio/meter/Makefile            |   15 +
 drivers/staging/iio/meter/ade7753.c           |  730 ++++++++
 drivers/staging/iio/meter/ade7753.h           |  140 ++
 drivers/staging/iio/meter/ade7754.c           |  756 ++++++++
 drivers/staging/iio/meter/ade7754.h           |  161 ++
 drivers/staging/iio/meter/ade7758.h           |  171 ++
 drivers/staging/iio/meter/ade7758_core.c      |  866 +++++++++
 drivers/staging/iio/meter/ade7758_ring.c      |  212 +++
 drivers/staging/iio/meter/ade7758_trigger.c   |  125 ++
 drivers/staging/iio/meter/ade7759.c           |  670 +++++++
 drivers/staging/iio/meter/ade7759.h           |  122 ++
 drivers/staging/iio/meter/ade7854-i2c.c       |  272 +++
 drivers/staging/iio/meter/ade7854-spi.c       |  360 ++++
 drivers/staging/iio/meter/ade7854.c           |  680 +++++++
 drivers/staging/iio/meter/ade7854.h           |  245 +++
 drivers/staging/iio/meter/meter.h             |  396 ++++
 drivers/staging/iio/resolver/Kconfig          |   54 +
 drivers/staging/iio/resolver/Makefile         |    7 +
 drivers/staging/iio/resolver/ad2s120x.c       |  310 ++++
 drivers/staging/iio/resolver/ad2s1210.c       |  872 +++++++++
 drivers/staging/iio/resolver/ad2s90.c         |  159 ++
 drivers/staging/iio/sysfs.h                   |    6 +
 84 files changed, 25107 insertions(+), 7 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16201.h
 create mode 100644 drivers/staging/iio/accel/adis16201_core.c
 create mode 100644 drivers/staging/iio/accel/adis16201_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16201_trigger.c
 create mode 100644 drivers/staging/iio/accel/adis16203.h
 create mode 100644 drivers/staging/iio/accel/adis16203_core.c
 create mode 100644 drivers/staging/iio/accel/adis16203_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16203_trigger.c
 create mode 100644 drivers/staging/iio/accel/adis16204.h
 create mode 100644 drivers/staging/iio/accel/adis16204_core.c
 create mode 100644 drivers/staging/iio/accel/adis16204_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16204_trigger.c
 create mode 100644 drivers/staging/iio/adc/ad7150.c
 create mode 100644 drivers/staging/iio/adc/ad7152.c
 create mode 100644 drivers/staging/iio/adc/ad7291.c
 create mode 100644 drivers/staging/iio/adc/ad7298.c
 create mode 100644 drivers/staging/iio/adc/ad7314.c
 create mode 100644 drivers/staging/iio/adc/ad7745.c
 create mode 100644 drivers/staging/iio/adc/ad7816.c
 create mode 100644 drivers/staging/iio/adc/adt7310.c
 create mode 100644 drivers/staging/iio/adc/adt7410.c
 create mode 100644 drivers/staging/iio/adc/adt75.c
 create mode 100644 drivers/staging/iio/addac/Kconfig
 create mode 100644 drivers/staging/iio/addac/Makefile
 create mode 100644 drivers/staging/iio/addac/adt7316-i2c.c
 create mode 100644 drivers/staging/iio/addac/adt7316-spi.c
 create mode 100644 drivers/staging/iio/addac/adt7316.c
 create mode 100644 drivers/staging/iio/addac/adt7316.h
 create mode 100644 drivers/staging/iio/dac/Kconfig
 create mode 100644 drivers/staging/iio/dac/Makefile
 create mode 100644 drivers/staging/iio/dac/ad5624r.h
 create mode 100644 drivers/staging/iio/dac/ad5624r_spi.c
 create mode 100644 drivers/staging/iio/dac/dac.h
 create mode 100644 drivers/staging/iio/dds/Kconfig
 create mode 100644 drivers/staging/iio/dds/Makefile
 create mode 100644 drivers/staging/iio/dds/ad5930.c
 create mode 100644 drivers/staging/iio/dds/ad9832.c
 create mode 100644 drivers/staging/iio/dds/ad9850.c
 create mode 100644 drivers/staging/iio/dds/ad9852.c
 create mode 100644 drivers/staging/iio/dds/ad9910.c
 create mode 100644 drivers/staging/iio/dds/ad9951.c
 create mode 100644 drivers/staging/iio/gyro/adis16060.h
 create mode 100644 drivers/staging/iio/gyro/adis16060_core.c
 create mode 100644 drivers/staging/iio/gyro/adis16080.h
 create mode 100644 drivers/staging/iio/gyro/adis16080_core.c
 create mode 100644 drivers/staging/iio/gyro/adis16130.h
 create mode 100644 drivers/staging/iio/gyro/adis16130_core.c
 create mode 100644 drivers/staging/iio/gyro/adis16251.h
 create mode 100644 drivers/staging/iio/gyro/adis16251_core.c
 create mode 100644 drivers/staging/iio/meter/Kconfig
 create mode 100644 drivers/staging/iio/meter/Makefile
 create mode 100644 drivers/staging/iio/meter/ade7753.c
 create mode 100644 drivers/staging/iio/meter/ade7753.h
 create mode 100644 drivers/staging/iio/meter/ade7754.c
 create mode 100644 drivers/staging/iio/meter/ade7754.h
 create mode 100644 drivers/staging/iio/meter/ade7758.h
 create mode 100644 drivers/staging/iio/meter/ade7758_core.c
 create mode 100644 drivers/staging/iio/meter/ade7758_ring.c
 create mode 100644 drivers/staging/iio/meter/ade7758_trigger.c
 create mode 100644 drivers/staging/iio/meter/ade7759.c
 create mode 100644 drivers/staging/iio/meter/ade7759.h
 create mode 100644 drivers/staging/iio/meter/ade7854-i2c.c
 create mode 100644 drivers/staging/iio/meter/ade7854-spi.c
 create mode 100644 drivers/staging/iio/meter/ade7854.c
 create mode 100644 drivers/staging/iio/meter/ade7854.h
 create mode 100644 drivers/staging/iio/meter/meter.h
 create mode 100644 drivers/staging/iio/resolver/Kconfig
 create mode 100644 drivers/staging/iio/resolver/Makefile
 create mode 100644 drivers/staging/iio/resolver/ad2s120x.c
 create mode 100644 drivers/staging/iio/resolver/ad2s1210.c
 create mode 100644 drivers/staging/iio/resolver/ad2s90.c

-- 
1.7.3.2

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 01/36] staging: iio: adis16350: add missing reference to temp offset
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 02/36] staging: iio: gyro: make sure grep can find the ADIS16265 support Mike Frysinger
                   ` (35 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel

We declare this attr but never link it in to the attr list.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/imu/adis16350_core.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/staging/iio/imu/adis16350_core.c b/drivers/staging/iio/imu/adis16350_core.c
index 97c1ec8..cf7176b 100644
--- a/drivers/staging/iio/imu/adis16350_core.c
+++ b/drivers/staging/iio/imu/adis16350_core.c
@@ -570,6 +570,7 @@ static struct attribute *adis16350_attributes[] = {
 	&iio_dev_attr_temp_y_raw.dev_attr.attr,
 	&iio_dev_attr_temp_z_raw.dev_attr.attr,
 	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
 	&iio_dev_attr_in1_raw.dev_attr.attr,
 	&iio_const_attr_in1_scale.dev_attr.attr,
 	&iio_dev_attr_sampling_frequency.dev_attr.attr,
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 02/36] staging: iio: gyro: make sure grep can find the ADIS16265 support
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
  2010-10-26 18:17 ` [PATCH 01/36] staging: iio: adis16350: add missing reference to temp offset Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 03/36] staging: iio: add ADI info to TODO Mike Frysinger
                   ` (34 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/gyro/Kconfig          |    7 +++++--
 drivers/staging/iio/gyro/adis16260_core.c |    2 +-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
index c404361..d1dde82 100644
--- a/drivers/staging/iio/gyro/Kconfig
+++ b/drivers/staging/iio/gyro/Kconfig
@@ -4,10 +4,13 @@
 comment "Digital gyroscope sensors"
 
 config ADIS16260
-	tristate "Analog Devices ADIS16260/5 Digital Gyroscope Sensor SPI driver"
+	tristate "Analog Devices ADIS16260 ADIS16265 Digital Gyroscope Sensor SPI driver"
 	depends on SPI
 	select IIO_TRIGGER if IIO_RING_BUFFER
 	select IIO_SW_RING if IIO_RING_BUFFER
 	help
-	  Say yes here to build support for Analog Devices adis16260/5
+	  Say yes here to build support for Analog Devices ADIS16260 ADIS16265
 	  programmable digital gyroscope sensor.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called adis16260.
diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c
index 7d7716e..8190c0f 100644
--- a/drivers/staging/iio/gyro/adis16260_core.c
+++ b/drivers/staging/iio/gyro/adis16260_core.c
@@ -1,5 +1,5 @@
 /*
- * ADIS16260 Programmable Digital Gyroscope Sensor Driver
+ * ADIS16260/ADIS16265 Programmable Digital Gyroscope Sensor Driver
  *
  * Copyright 2010 Analog Devices Inc.
  *
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 03/36] staging: iio: add ADI info to TODO
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
  2010-10-26 18:17 ` [PATCH 01/36] staging: iio: adis16350: add missing reference to temp offset Mike Frysinger
  2010-10-26 18:17 ` [PATCH 02/36] staging: iio: gyro: make sure grep can find the ADIS16265 support Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 04/36] staging: iio: new adis16201 driver Mike Frysinger
                   ` (33 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/TODO |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/staging/iio/TODO b/drivers/staging/iio/TODO
index 898cba1..d1ad35e 100644
--- a/drivers/staging/iio/TODO
+++ b/drivers/staging/iio/TODO
@@ -61,6 +61,10 @@ necessitate a header that is also visible from arch board
 files. (avoided at the moment to keep the driver set
 contained in staging).
 
+ADI Drivers:
+CC the device-drivers-devel@blackfin.uclinux.org mailing list when
+e-mailing the normal IIO list (see below).
+
 Documentation
 1) Lots of cleanup and expansion.
 2) Some device require indvidual docs.
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 04/36] staging: iio: new adis16201 driver
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (2 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 03/36] staging: iio: add ADI info to TODO Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 05/36] staging: iio: new adis16203 driver Mike Frysinger
                   ` (32 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/accel/Kconfig             |    9 +
 drivers/staging/iio/accel/Makefile            |    4 +
 drivers/staging/iio/accel/adis16201.h         |  150 ++++++
 drivers/staging/iio/accel/adis16201_core.c    |  659 +++++++++++++++++++++++++
 drivers/staging/iio/accel/adis16201_ring.c    |  218 ++++++++
 drivers/staging/iio/accel/adis16201_trigger.c |  122 +++++
 6 files changed, 1162 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16201.h
 create mode 100644 drivers/staging/iio/accel/adis16201_core.c
 create mode 100644 drivers/staging/iio/accel/adis16201_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16201_trigger.c

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index 5926c03..0b87557 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -3,6 +3,15 @@
 #
 comment "Accelerometers"
 
+config ADIS16201
+	tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16201 dual-axis
+	  digital inclinometer and accelerometer.
+
 config ADIS16209
 	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index ff84703..4a22a01 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -2,6 +2,10 @@
 # Makefile for industrial I/O accelerometer drivers
 #
 
+adis16201-y             := adis16201_core.o
+adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
+obj-$(CONFIG_ADIS16201) += adis16201.o
+
 adis16209-y             := adis16209_core.o
 adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
 obj-$(CONFIG_ADIS16209) += adis16209.o
diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h
new file mode 100644
index 0000000..c9bf22c
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201.h
@@ -0,0 +1,150 @@
+#ifndef SPI_ADIS16201_H_
+#define SPI_ADIS16201_H_
+
+#define ADIS16201_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16201_READ_REG(a)    a
+#define ADIS16201_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16201_FLASH_CNT      0x00 /* Flash memory write count */
+#define ADIS16201_SUPPLY_OUT     0x02 /* Output, power supply */
+#define ADIS16201_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
+#define ADIS16201_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
+#define ADIS16201_AUX_ADC        0x08 /* Output, auxiliary ADC input */
+#define ADIS16201_TEMP_OUT       0x0A /* Output, temperature */
+#define ADIS16201_XINCL_OUT      0x0C /* Output, x-axis inclination */
+#define ADIS16201_YINCL_OUT      0x0E /* Output, y-axis inclination */
+#define ADIS16201_XACCL_OFFS     0x10 /* Calibration, x-axis acceleration offset */
+#define ADIS16201_YACCL_OFFS     0x12 /* Calibration, y-axis acceleration offset */
+#define ADIS16201_XACCL_SCALE    0x14 /* x-axis acceleration scale factor */
+#define ADIS16201_YACCL_SCALE    0x16 /* y-axis acceleration scale factor */
+#define ADIS16201_XINCL_OFFS     0x18 /* Calibration, x-axis inclination offset */
+#define ADIS16201_YINCL_OFFS     0x1A /* Calibration, y-axis inclination offset */
+#define ADIS16201_XINCL_SCALE    0x1C /* x-axis inclination scale factor */
+#define ADIS16201_YINCL_SCALE    0x1E /* y-axis inclination scale factor */
+#define ADIS16201_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16201_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16201_ALM_SMPL1      0x24 /* Alarm 1, sample period */
+#define ADIS16201_ALM_SMPL2      0x26 /* Alarm 2, sample period */
+#define ADIS16201_ALM_CTRL       0x28 /* Alarm control */
+#define ADIS16201_AUX_DAC        0x30 /* Auxiliary DAC data */
+#define ADIS16201_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
+#define ADIS16201_MSC_CTRL       0x34 /* Miscellaneous control */
+#define ADIS16201_SMPL_PRD       0x36 /* Internal sample period (rate) control */
+#define ADIS16201_AVG_CNT        0x38 /* Operation, filter configuration */
+#define ADIS16201_SLP_CNT        0x3A /* Operation, sleep mode control */
+#define ADIS16201_DIAG_STAT      0x3C /* Diagnostics, system status register */
+#define ADIS16201_GLOB_CMD       0x3E /* Operation, system command register */
+
+#define ADIS16201_OUTPUTS        7
+
+/* MSC_CTRL */
+#define ADIS16201_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
+#define ADIS16201_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16201_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16201_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
+#define ADIS16201_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
+#define ADIS16201_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
+#define ADIS16201_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16201_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16201_GLOB_CMD_FACTORY_CAL	(1<<1)
+
+#define ADIS16201_MAX_TX 14
+#define ADIS16201_MAX_RX 14
+
+#define ADIS16201_ERROR_ACTIVE          (1<<14)
+
+/**
+ * struct adis16201_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct adis16201_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16201_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16201_scan {
+	ADIS16201_SCAN_SUPPLY,
+	ADIS16201_SCAN_ACC_X,
+	ADIS16201_SCAN_ACC_Y,
+	ADIS16201_SCAN_AUX_ADC,
+	ADIS16201_SCAN_TEMP,
+	ADIS16201_SCAN_INCLI_X,
+	ADIS16201_SCAN_INCLI_Y,
+};
+
+void adis16201_remove_trigger(struct iio_dev *indio_dev);
+int adis16201_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16201_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+int adis16201_configure_ring(struct iio_dev *indio_dev);
+void adis16201_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16201_initialize_ring(struct iio_ring_buffer *ring);
+void adis16201_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16201_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16201_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16201_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16201_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16201_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16201_H_ */
diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
new file mode 100644
index 0000000..79b785a
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_core.c
@@ -0,0 +1,659 @@
+/*
+ * ADIS16201 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "inclinometer.h"
+#include "../gyro/gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16201.h"
+
+#define DRIVER_NAME		"adis16201"
+
+static int adis16201_check_status(struct device *dev);
+
+/**
+ * adis16201_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+static int adis16201_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16201_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16201_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16201_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16201_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16201_WRITE_REG(lower_reg_address + 1);
+	st->tx[3] = (value >> 8) & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16201_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16201_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16201_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16201_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16201_ERROR_ACTIVE) {
+		ret = adis16201_check_status(dev);
+		if (ret)
+			return ret;
+	}
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16201_read_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 val;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, ADIS16201_TEMP_OUT, (u16 *)&val);
+	if (ret)
+		goto error_ret;
+
+	if (val & ADIS16201_ERROR_ACTIVE) {
+		ret = adis16201_check_status(dev);
+		if (ret)
+			goto error_ret;
+	}
+
+	val &= 0xFFF;
+	ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static ssize_t adis16201_read_9bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16201_ERROR_ACTIVE) {
+			ret = adis16201_check_status(dev);
+			if (ret)
+				goto error_ret;
+		}
+		val = ((s16)(val << 7) >> 7);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16201_read_12bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16201_ERROR_ACTIVE) {
+			ret = adis16201_check_status(dev);
+			if (ret)
+				goto error_ret;
+		}
+
+		val = ((s16)(val << 4) >> 4);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16201_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16201_ERROR_ACTIVE) {
+			ret = adis16201_check_status(dev);
+			if (ret)
+				goto error_ret;
+		}
+
+		val = ((s16)(val << 2) >> 2);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16201_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = adis16201_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int adis16201_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16201_spi_write_reg_8(dev,
+			ADIS16201_GLOB_CMD,
+			ADIS16201_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16201_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -EINVAL;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16201_reset(dev);
+	}
+	return -EINVAL;
+}
+
+int adis16201_set_irq(struct device *dev, bool enable)
+{
+	int ret = 0;
+	u16 msc;
+
+	ret = adis16201_spi_read_reg_16(dev, ADIS16201_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16201_MSC_CTRL_ACTIVE_HIGH;
+	msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_DIO1;
+	if (enable)
+		msc |= ADIS16201_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16201_spi_write_reg_16(dev, ADIS16201_MSC_CTRL, msc);
+
+error_ret:
+	return ret;
+}
+
+static int adis16201_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16201_spi_read_reg_16(dev, ADIS16201_DIAG_STAT, &status);
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status & 0xF;
+	if (ret)
+		ret = -EFAULT;
+
+	if (status & ADIS16201_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16201_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16201_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 3.625V\n");
+	if (status & ADIS16201_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 3.15V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16201_self_test(struct device *dev)
+{
+	int ret;
+	ret = adis16201_spi_write_reg_16(dev,
+			ADIS16201_MSC_CTRL,
+			ADIS16201_MSC_CTRL_SELF_TEST_EN);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	ret = adis16201_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16201_initial_setup(struct adis16201_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = adis16201_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+	ret = adis16201_self_test(dev);
+	if (ret) {
+		dev_err(dev, "self test failure");
+		goto err_ret;
+	}
+
+	/* Read status register to check the result */
+	ret = adis16201_check_status(dev);
+	if (ret) {
+		adis16201_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16201_STARTUP_DELAY);
+		ret = adis16201_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16201_read_12bit_unsigned,
+		ADIS16201_SUPPLY_OUT);
+static IIO_CONST_ATTR(in0_supply_scale, "0.00122");
+static IIO_DEV_ATTR_IN_RAW(1, adis16201_read_12bit_unsigned,
+		ADIS16201_AUX_ADC);
+static IIO_CONST_ATTR(in1_scale, "0.00061");
+
+static IIO_DEV_ATTR_ACCEL_X(adis16201_read_14bit_signed,
+		ADIS16201_XACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_Y(adis16201_read_14bit_signed,
+		ADIS16201_YACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_12bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_XACCL_OFFS);
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_12bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_YACCL_OFFS);
+static IIO_CONST_ATTR(accel_scale, "0.4625");
+
+static IIO_DEV_ATTR_INCLI_X(adis16201_read_14bit_signed,
+		ADIS16201_XINCL_OUT);
+static IIO_DEV_ATTR_INCLI_Y(adis16201_read_14bit_signed,
+		ADIS16201_YINCL_OUT);
+static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_9bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_XACCL_OFFS);
+static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_9bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_YACCL_OFFS);
+static IIO_CONST_ATTR(incli_scale, "0.1");
+
+static IIO_DEV_ATTR_TEMP_RAW(adis16201_read_temp);
+static IIO_CONST_ATTR(temp_offset, "25");
+static IIO_CONST_ATTR(temp_scale, "-0.47");
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16201_write_reset, 0);
+
+static IIO_CONST_ATTR(name, "adis16201");
+
+static struct attribute *adis16201_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16201_event_attribute_group = {
+	.attrs = adis16201_event_attributes,
+};
+
+static struct attribute *adis16201_attributes[] = {
+	&iio_dev_attr_in0_supply_raw.dev_attr.attr,
+	&iio_const_attr_in0_supply_scale.dev_attr.attr,
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_in1_raw.dev_attr.attr,
+	&iio_const_attr_in1_scale.dev_attr.attr,
+	&iio_dev_attr_accel_x_raw.dev_attr.attr,
+	&iio_dev_attr_accel_y_raw.dev_attr.attr,
+	&iio_dev_attr_accel_x_offset.dev_attr.attr,
+	&iio_dev_attr_accel_y_offset.dev_attr.attr,
+	&iio_const_attr_accel_scale.dev_attr.attr,
+	&iio_dev_attr_incli_x_raw.dev_attr.attr,
+	&iio_dev_attr_incli_y_raw.dev_attr.attr,
+	&iio_dev_attr_incli_x_offset.dev_attr.attr,
+	&iio_dev_attr_incli_y_offset.dev_attr.attr,
+	&iio_const_attr_incli_scale.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16201_attribute_group = {
+	.attrs = adis16201_attributes,
+};
+
+static int __devinit adis16201_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16201_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16201_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16201_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16201_event_attribute_group;
+	st->indio_dev->attrs = &adis16201_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16201_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16201_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16201");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16201_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16201_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	adis16201_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (spi->irq)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16201_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16201_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int adis16201_remove(struct spi_device *spi)
+{
+	struct adis16201_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16201_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16201_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	adis16201_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16201_driver = {
+	.driver = {
+		.name = "adis16201",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16201_probe,
+	.remove = __devexit_p(adis16201_remove),
+};
+
+static __init int adis16201_init(void)
+{
+	return spi_register_driver(&adis16201_driver);
+}
+module_init(adis16201_init);
+
+static __exit void adis16201_exit(void)
+{
+	spi_unregister_driver(&adis16201_driver);
+}
+module_exit(adis16201_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c
new file mode 100644
index 0000000..e6870a2
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_ring.c
@@ -0,0 +1,218 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16201.h"
+
+static IIO_SCAN_EL_C(in_supply, ADIS16201_SCAN_SUPPLY, ADIS16201_SUPPLY_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(in_supply, u, 12, 16);
+static IIO_SCAN_EL_C(accel_x, ADIS16201_SCAN_ACC_X, ADIS16201_XACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_y, ADIS16201_SCAN_ACC_Y, ADIS16201_YACCL_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 14, 16);
+static IIO_SCAN_EL_C(in0, ADIS16201_SCAN_AUX_ADC, ADIS16201_AUX_ADC, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(in0, u, 12, 16);
+static IIO_SCAN_EL_C(temp, ADIS16201_SCAN_TEMP, ADIS16201_TEMP_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, u, 12, 16);
+static IIO_SCAN_EL_C(incli_x, ADIS16201_SCAN_INCLI_X,
+		     ADIS16201_XINCL_OUT, NULL);
+static IIO_SCAN_EL_C(incli_y, ADIS16201_SCAN_INCLI_Y,
+		     ADIS16201_YINCL_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(incli, s, 14, 16);
+static IIO_SCAN_EL_TIMESTAMP(7);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64);
+
+static struct attribute *adis16201_scan_el_attrs[] = {
+	&iio_scan_el_in_supply.dev_attr.attr,
+	&iio_const_attr_in_supply_index.dev_attr.attr,
+	&iio_const_attr_in_supply_type.dev_attr.attr,
+	&iio_scan_el_accel_x.dev_attr.attr,
+	&iio_const_attr_accel_x_index.dev_attr.attr,
+	&iio_scan_el_accel_y.dev_attr.attr,
+	&iio_const_attr_accel_y_index.dev_attr.attr,
+	&iio_const_attr_accel_type.dev_attr.attr,
+	&iio_scan_el_in0.dev_attr.attr,
+	&iio_const_attr_in0_index.dev_attr.attr,
+	&iio_const_attr_in0_type.dev_attr.attr,
+	&iio_scan_el_temp.dev_attr.attr,
+	&iio_const_attr_temp_index.dev_attr.attr,
+	&iio_const_attr_temp_type.dev_attr.attr,
+	&iio_scan_el_incli_x.dev_attr.attr,
+	&iio_const_attr_incli_x_index.dev_attr.attr,
+	&iio_scan_el_incli_y.dev_attr.attr,
+	&iio_const_attr_incli_y_index.dev_attr.attr,
+	&iio_const_attr_incli_type.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	&iio_const_attr_timestamp_index.dev_attr.attr,
+	&iio_const_attr_timestamp_type.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adis16201_scan_el_group = {
+	.attrs = adis16201_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * adis16201_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void adis16201_poll_func_th(struct iio_dev *indio_dev, s64 time)
+{
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = time;
+	schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16201_read_ring_data() read data registers which will be placed into ring
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read
+ **/
+static int adis16201_read_ring_data(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[ADIS16201_OUTPUTS + 1];
+	int ret;
+	int i;
+
+	mutex_lock(&st->buf_lock);
+
+	spi_message_init(&msg);
+
+	memset(xfers, 0, sizeof(xfers));
+	for (i = 0; i <= ADIS16201_OUTPUTS; i++) {
+		xfers[i].bits_per_word = 8;
+		xfers[i].cs_change = 1;
+		xfers[i].len = 2;
+		xfers[i].delay_usecs = 20;
+		xfers[i].tx_buf = st->tx + 2 * i;
+		st->tx[2 * i] = ADIS16201_READ_REG(ADIS16201_SUPPLY_OUT + 2 * i);
+		st->tx[2 * i + 1] = 0;
+		if (i >= 1)
+			xfers[i].rx_buf = rx + 2 * (i - 1);
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
+ * specific to be rolled into the core.
+ */
+static void adis16201_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct adis16201_state *st
+		= container_of(work_s, struct adis16201_state,
+			       work_trigger_to_ring);
+	struct iio_ring_buffer *ring = st->indio_dev->ring;
+
+	int i = 0;
+	s16 *data;
+	size_t datasize = ring->access.get_bytes_per_datum(ring);
+
+	data = kmalloc(datasize, GFP_KERNEL);
+	if (data == NULL) {
+		dev_err(&st->us->dev, "memory alloc failed in ring bh");
+		return;
+	}
+
+	if (ring->scan_count)
+		if (adis16201_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < ring->scan_count; i++)
+				data[i] = be16_to_cpup(
+					(__be16 *)&(st->rx[i*2]));
+
+	/* Guaranteed to be aligned with 8 byte boundary */
+	if (ring->scan_timestamp)
+		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp;
+
+	ring->access.store_to(ring,
+			      (u8 *)data,
+			      st->last_timestamp);
+
+	iio_trigger_notify_done(st->indio_dev->trig);
+	kfree(data);
+
+	return;
+}
+
+void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16201_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct adis16201_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, adis16201_trigger_bh_to_ring);
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->bpe = 2;
+	ring->scan_el_attrs = &adis16201_scan_el_group;
+	ring->scan_timestamp = true;
+	ring->preenable = &iio_sw_ring_preenable;
+	ring->postenable = &iio_triggered_ring_postenable;
+	ring->predisable = &iio_triggered_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	/* Set default scan mode */
+	iio_scan_mask_set(ring, iio_scan_el_in_supply.number);
+	iio_scan_mask_set(ring, iio_scan_el_accel_x.number);
+	iio_scan_mask_set(ring, iio_scan_el_accel_y.number);
+	iio_scan_mask_set(ring, iio_scan_el_temp.number);
+	iio_scan_mask_set(ring, iio_scan_el_in0.number);
+	iio_scan_mask_set(ring, iio_scan_el_incli_x.number);
+	iio_scan_mask_set(ring, iio_scan_el_incli_y.number);
+
+	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16201_poll_func_th);
+	if (ret)
+		goto error_iio_sw_rb_free;
+
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int adis16201_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16201_trigger.c b/drivers/staging/iio/accel/adis16201_trigger.c
new file mode 100644
index 0000000..8a9cea19
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_trigger.c
@@ -0,0 +1,122 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "adis16201.h"
+
+/**
+ * adis16201_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16201_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct adis16201_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	iio_trigger_poll(trig, timestamp);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &adis16201_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16201_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group adis16201_trigger_attr_group = {
+	.attrs = adis16201_trigger_attrs,
+};
+
+/**
+ * adis16201_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16201_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis16201_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = adis16201_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * adis16201_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int adis16201_trig_try_reen(struct iio_trigger *trig)
+{
+	struct adis16201_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	return 0;
+}
+
+int adis16201_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct adis16201_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kasprintf(GFP_KERNEL,
+				"adis16201-dev%d",
+				indio_dev->id);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &adis16201_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &adis16201_trig_try_reen;
+	st->trig->control_attrs = &adis16201_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void adis16201_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct adis16201_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 05/36] staging: iio: new adis16203 driver
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (3 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 04/36] staging: iio: new adis16201 driver Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 06/36] staging: iio: new adis16204 driver Mike Frysinger
                   ` (31 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

IIO driver for Programmable 360 Degrees Inclinometer adis16203 parts.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/accel/Kconfig             |    9 +
 drivers/staging/iio/accel/Makefile            |    4 +
 drivers/staging/iio/accel/adis16203.h         |  143 +++++++
 drivers/staging/iio/accel/adis16203_core.c    |  568 +++++++++++++++++++++++++
 drivers/staging/iio/accel/adis16203_ring.c    |  211 +++++++++
 drivers/staging/iio/accel/adis16203_trigger.c |  122 ++++++
 6 files changed, 1057 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16203.h
 create mode 100644 drivers/staging/iio/accel/adis16203_core.c
 create mode 100644 drivers/staging/iio/accel/adis16203_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16203_trigger.c

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index 0b87557..cc057c6 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -12,6 +12,15 @@ config ADIS16201
 	  Say yes here to build support for Analog Devices adis16201 dual-axis
 	  digital inclinometer and accelerometer.
 
+config ADIS16203
+	tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16203 Programmable
+	  360 Degrees Inclinometer.
+
 config ADIS16209
 	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index 4a22a01..76cc92f 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -6,6 +6,10 @@ adis16201-y             := adis16201_core.o
 adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
 obj-$(CONFIG_ADIS16201) += adis16201.o
 
+adis16203-y             := adis16203_core.o
+adis16203-$(CONFIG_IIO_RING_BUFFER) += adis16203_ring.o adis16203_trigger.o
+obj-$(CONFIG_ADIS16203) += adis16203.o
+
 adis16209-y             := adis16209_core.o
 adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
 obj-$(CONFIG_ADIS16209) += adis16209.o
diff --git a/drivers/staging/iio/accel/adis16203.h b/drivers/staging/iio/accel/adis16203.h
new file mode 100644
index 0000000..b39323e
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203.h
@@ -0,0 +1,143 @@
+#ifndef SPI_ADIS16203_H_
+#define SPI_ADIS16203_H_
+
+#define ADIS16203_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16203_READ_REG(a)    a
+#define ADIS16203_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16203_FLASH_CNT      0x00 /* Flash memory write count */
+#define ADIS16203_SUPPLY_OUT     0x02 /* Output, power supply */
+#define ADIS16203_AUX_ADC        0x08 /* Output, auxiliary ADC input */
+#define ADIS16203_TEMP_OUT       0x0A /* Output, temperature */
+#define ADIS16203_XINCL_OUT      0x0C /* Output, x-axis inclination */
+#define ADIS16203_YINCL_OUT      0x0E /* Output, y-axis inclination */
+#define ADIS16203_INCL_NULL      0x18 /* Incline null calibration */
+#define ADIS16203_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16203_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16203_ALM_SMPL1      0x24 /* Alarm 1, sample period */
+#define ADIS16203_ALM_SMPL2      0x26 /* Alarm 2, sample period */
+#define ADIS16203_ALM_CTRL       0x28 /* Alarm control */
+#define ADIS16203_AUX_DAC        0x30 /* Auxiliary DAC data */
+#define ADIS16203_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
+#define ADIS16203_MSC_CTRL       0x34 /* Miscellaneous control */
+#define ADIS16203_SMPL_PRD       0x36 /* Internal sample period (rate) control */
+#define ADIS16203_AVG_CNT        0x38 /* Operation, filter configuration */
+#define ADIS16203_SLP_CNT        0x3A /* Operation, sleep mode control */
+#define ADIS16203_DIAG_STAT      0x3C /* Diagnostics, system status register */
+#define ADIS16203_GLOB_CMD       0x3E /* Operation, system command register */
+
+#define ADIS16203_OUTPUTS        5
+
+/* MSC_CTRL */
+#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN	(1 << 9)  /* Reverses rotation of both inclination outputs */
+#define ADIS16203_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
+#define ADIS16203_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16203_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16203_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag */
+#define ADIS16203_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
+#define ADIS16203_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
+#define ADIS16203_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
+#define ADIS16203_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16203_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16203_GLOB_CMD_CLEAR_STAT	(1<<4)
+#define ADIS16203_GLOB_CMD_FACTORY_CAL	(1<<1)
+
+#define ADIS16203_MAX_TX 12
+#define ADIS16203_MAX_RX 10
+
+#define ADIS16203_ERROR_ACTIVE          (1<<14)
+
+/**
+ * struct adis16203_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct adis16203_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16203_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16203_scan {
+	ADIS16203_SCAN_SUPPLY,
+	ADIS16203_SCAN_AUX_ADC,
+	ADIS16203_SCAN_TEMP,
+	ADIS16203_SCAN_INCLI_X,
+	ADIS16203_SCAN_INCLI_Y,
+};
+
+void adis16203_remove_trigger(struct iio_dev *indio_dev);
+int adis16203_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16203_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+int adis16203_configure_ring(struct iio_dev *indio_dev);
+void adis16203_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16203_initialize_ring(struct iio_ring_buffer *ring);
+void adis16203_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16203_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16203_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16203_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16203_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16203_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16203_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16203_H_ */
diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c
new file mode 100644
index 0000000..b57f190
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_core.c
@@ -0,0 +1,568 @@
+/*
+ * ADIS16203 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "inclinometer.h"
+#include "../gyro/gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16203.h"
+
+#define DRIVER_NAME		"adis16203"
+
+static int adis16203_check_status(struct device *dev);
+
+/**
+ * adis16203_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+static int adis16203_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16203_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16203_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16203_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16203_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16203_WRITE_REG(lower_reg_address + 1);
+	st->tx[3] = (value >> 8) & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16203_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16203_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16203_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16203_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16203_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16203_ERROR_ACTIVE)
+		adis16203_check_status(dev);
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16203_read_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 val;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16203_spi_read_reg_16(dev, ADIS16203_TEMP_OUT, (u16 *)&val);
+	if (ret)
+		goto error_ret;
+
+	if (val & ADIS16203_ERROR_ACTIVE)
+		adis16203_check_status(dev);
+
+	val &= 0xFFF;
+	ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static ssize_t adis16203_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16203_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16203_ERROR_ACTIVE)
+			adis16203_check_status(dev);
+
+		val = ((s16)(val << 2) >> 2);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16203_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = adis16203_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int adis16203_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16203_spi_write_reg_8(dev,
+			ADIS16203_GLOB_CMD,
+			ADIS16203_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16203_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -EINVAL;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16203_reset(dev);
+	}
+	return -EINVAL;
+}
+
+int adis16203_set_irq(struct device *dev, bool enable)
+{
+	int ret = 0;
+	u16 msc;
+
+	ret = adis16203_spi_read_reg_16(dev, ADIS16203_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16203_MSC_CTRL_ACTIVE_HIGH;
+	msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_DIO1;
+	if (enable)
+		msc |= ADIS16203_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16203_spi_write_reg_16(dev, ADIS16203_MSC_CTRL, msc);
+
+error_ret:
+	return ret;
+}
+
+static int adis16203_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16203_spi_read_reg_16(dev, ADIS16203_DIAG_STAT, &status);
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status & 0x1F;
+
+	if (status & ADIS16203_DIAG_STAT_SELFTEST_FAIL)
+		dev_err(dev, "Self test failure\n");
+	if (status & ADIS16203_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16203_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16203_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 3.625V\n");
+	if (status & ADIS16203_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 3.15V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16203_self_test(struct device *dev)
+{
+	int ret;
+	ret = adis16203_spi_write_reg_16(dev,
+			ADIS16203_MSC_CTRL,
+			ADIS16203_MSC_CTRL_SELF_TEST_EN);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	adis16203_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16203_initial_setup(struct adis16203_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = adis16203_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+	ret = adis16203_self_test(dev);
+	if (ret) {
+		dev_err(dev, "self test failure");
+		goto err_ret;
+	}
+
+	/* Read status register to check the result */
+	ret = adis16203_check_status(dev);
+	if (ret) {
+		adis16203_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16203_STARTUP_DELAY);
+		ret = adis16203_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16203_read_12bit_unsigned,
+		ADIS16203_SUPPLY_OUT);
+static IIO_CONST_ATTR(in0_supply_scale, "0.00122");
+static IIO_DEV_ATTR_IN_RAW(1, adis16203_read_12bit_unsigned,
+		ADIS16203_AUX_ADC);
+static IIO_CONST_ATTR(in1_scale, "0.00061");
+
+static IIO_DEV_ATTR_INCLI_X(adis16203_read_14bit_signed,
+		ADIS16203_XINCL_OUT);
+static IIO_DEV_ATTR_INCLI_Y(adis16203_read_14bit_signed,
+		ADIS16203_YINCL_OUT);
+static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16203_read_14bit_signed,
+		adis16203_write_16bit,
+		ADIS16203_INCL_NULL);
+static IIO_CONST_ATTR(incli_scale, "0.025");
+
+static IIO_DEV_ATTR_TEMP_RAW(adis16203_read_temp);
+static IIO_CONST_ATTR(temp_offset, "25");
+static IIO_CONST_ATTR(temp_scale, "-0.47");
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16203_write_reset, 0);
+
+static IIO_CONST_ATTR(name, "adis16203");
+
+static struct attribute *adis16203_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16203_event_attribute_group = {
+	.attrs = adis16203_event_attributes,
+};
+
+static struct attribute *adis16203_attributes[] = {
+	&iio_dev_attr_in0_supply_raw.dev_attr.attr,
+	&iio_const_attr_in0_supply_scale.dev_attr.attr,
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_in1_raw.dev_attr.attr,
+	&iio_const_attr_in1_scale.dev_attr.attr,
+	&iio_dev_attr_incli_x_raw.dev_attr.attr,
+	&iio_dev_attr_incli_y_raw.dev_attr.attr,
+	&iio_dev_attr_incli_x_offset.dev_attr.attr,
+	&iio_const_attr_incli_scale.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16203_attribute_group = {
+	.attrs = adis16203_attributes,
+};
+
+static int __devinit adis16203_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16203_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16203_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16203_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16203_event_attribute_group;
+	st->indio_dev->attrs = &adis16203_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16203_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16203_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16203");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16203_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16203_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	adis16203_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (spi->irq)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16203_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16203_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int adis16203_remove(struct spi_device *spi)
+{
+	struct adis16203_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16203_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16203_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	adis16203_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16203_driver = {
+	.driver = {
+		.name = "adis16203",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16203_probe,
+	.remove = __devexit_p(adis16203_remove),
+};
+
+static __init int adis16203_init(void)
+{
+	return spi_register_driver(&adis16203_driver);
+}
+module_init(adis16203_init);
+
+static __exit void adis16203_exit(void)
+{
+	spi_unregister_driver(&adis16203_driver);
+}
+module_exit(adis16203_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16203_ring.c b/drivers/staging/iio/accel/adis16203_ring.c
new file mode 100644
index 0000000..3d774f7
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_ring.c
@@ -0,0 +1,211 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16203.h"
+
+static IIO_SCAN_EL_C(in_supply, ADIS16203_SCAN_SUPPLY, ADIS16203_SUPPLY_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(in_supply, u, 12, 16);
+static IIO_SCAN_EL_C(in0, ADIS16203_SCAN_AUX_ADC, ADIS16203_AUX_ADC, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(in0, u, 12, 16);
+static IIO_SCAN_EL_C(temp, ADIS16203_SCAN_TEMP, ADIS16203_TEMP_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, u, 12, 16);
+static IIO_SCAN_EL_C(incli_x, ADIS16203_SCAN_INCLI_X,
+		     ADIS16203_XINCL_OUT, NULL);
+static IIO_SCAN_EL_C(incli_y, ADIS16203_SCAN_INCLI_Y,
+		     ADIS16203_YINCL_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(incli, s, 14, 16);
+static IIO_SCAN_EL_TIMESTAMP(5);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64);
+
+static struct attribute *adis16203_scan_el_attrs[] = {
+	&iio_scan_el_in_supply.dev_attr.attr,
+	&iio_const_attr_in_supply_index.dev_attr.attr,
+	&iio_const_attr_in_supply_type.dev_attr.attr,
+	&iio_scan_el_in0.dev_attr.attr,
+	&iio_const_attr_in0_index.dev_attr.attr,
+	&iio_const_attr_in0_type.dev_attr.attr,
+	&iio_scan_el_temp.dev_attr.attr,
+	&iio_const_attr_temp_index.dev_attr.attr,
+	&iio_const_attr_temp_type.dev_attr.attr,
+	&iio_scan_el_incli_x.dev_attr.attr,
+	&iio_const_attr_incli_x_index.dev_attr.attr,
+	&iio_scan_el_incli_y.dev_attr.attr,
+	&iio_const_attr_incli_y_index.dev_attr.attr,
+	&iio_const_attr_incli_type.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	&iio_const_attr_timestamp_index.dev_attr.attr,
+	&iio_const_attr_timestamp_type.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adis16203_scan_el_group = {
+	.attrs = adis16203_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * adis16203_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void adis16203_poll_func_th(struct iio_dev *indio_dev, s64 timestamp)
+{
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = timestamp;
+	schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16203_read_ring_data() read data registers which will be placed into ring
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read
+ **/
+static int adis16203_read_ring_data(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[ADIS16203_OUTPUTS + 1];
+	int ret;
+	int i;
+
+	mutex_lock(&st->buf_lock);
+
+	spi_message_init(&msg);
+
+	memset(xfers, 0, sizeof(xfers));
+	for (i = 0; i <= ADIS16203_OUTPUTS; i++) {
+		xfers[i].bits_per_word = 8;
+		xfers[i].cs_change = 1;
+		xfers[i].len = 2;
+		xfers[i].delay_usecs = 20;
+		xfers[i].tx_buf = st->tx + 2 * i;
+		if (i < 1) /* SUPPLY_OUT: 0x02, AUX_ADC: 0x08 */
+			st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i);
+		else
+			st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i + 6);
+		st->tx[2 * i + 1] = 0;
+		if (i >= 1)
+			xfers[i].rx_buf = rx + 2 * (i - 1);
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
+ * specific to be rolled into the core.
+ */
+static void adis16203_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct adis16203_state *st
+		= container_of(work_s, struct adis16203_state,
+			       work_trigger_to_ring);
+	struct iio_ring_buffer *ring = st->indio_dev->ring;
+
+	int i = 0;
+	s16 *data;
+	size_t datasize = ring->access.get_bytes_per_datum(ring);
+
+	data = kmalloc(datasize, GFP_KERNEL);
+	if (data == NULL) {
+		dev_err(&st->us->dev, "memory alloc failed in ring bh");
+		return;
+	}
+
+	if (ring->scan_count)
+		if (adis16203_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < ring->scan_count; i++)
+				data[i] = be16_to_cpup(
+					(__be16 *)&(st->rx[i*2]));
+
+	/* Guaranteed to be aligned with 8 byte boundary */
+	if (ring->scan_timestamp)
+		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp;
+
+	ring->access.store_to(ring,
+			      (u8 *)data,
+			      st->last_timestamp);
+
+	iio_trigger_notify_done(st->indio_dev->trig);
+	kfree(data);
+
+	return;
+}
+
+void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16203_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct adis16203_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, adis16203_trigger_bh_to_ring);
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->bpe = 2;
+	ring->scan_el_attrs = &adis16203_scan_el_group;
+	ring->scan_timestamp = true;
+	ring->preenable = &iio_sw_ring_preenable;
+	ring->postenable = &iio_triggered_ring_postenable;
+	ring->predisable = &iio_triggered_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	/* Set default scan mode */
+	iio_scan_mask_set(ring, iio_scan_el_in_supply.number);
+	iio_scan_mask_set(ring, iio_scan_el_temp.number);
+	iio_scan_mask_set(ring, iio_scan_el_in0.number);
+	iio_scan_mask_set(ring, iio_scan_el_incli_x.number);
+	iio_scan_mask_set(ring, iio_scan_el_incli_y.number);
+
+	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16203_poll_func_th);
+	if (ret)
+		goto error_iio_sw_rb_free;
+
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int adis16203_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16203_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16203_trigger.c b/drivers/staging/iio/accel/adis16203_trigger.c
new file mode 100644
index 0000000..50be51c
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_trigger.c
@@ -0,0 +1,122 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "adis16203.h"
+
+/**
+ * adis16203_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16203_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct adis16203_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	iio_trigger_poll(trig, timestamp);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &adis16203_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16203_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group adis16203_trigger_attr_group = {
+	.attrs = adis16203_trigger_attrs,
+};
+
+/**
+ * adis16203_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16203_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis16203_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = adis16203_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * adis16203_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int adis16203_trig_try_reen(struct iio_trigger *trig)
+{
+	struct adis16203_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	return 0;
+}
+
+int adis16203_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct adis16203_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kasprintf(GFP_KERNEL,
+				"adis16203-dev%d",
+				indio_dev->id);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &adis16203_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &adis16203_trig_try_reen;
+	st->trig->control_attrs = &adis16203_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void adis16203_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct adis16203_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 06/36] staging: iio: new adis16204 driver
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (4 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 05/36] staging: iio: new adis16203 driver Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 07/36] staging: iio: new ADT7316/7/8 and ADT7516/7/9 driver Mike Frysinger
                   ` (30 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

IIO driver for Programmable High-g Digital Impact Sensor and Recorder.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/accel/Kconfig             |    9 +
 drivers/staging/iio/accel/Makefile            |    4 +
 drivers/staging/iio/accel/accel.h             |   20 +
 drivers/staging/iio/accel/adis16204.h         |  151 ++++++
 drivers/staging/iio/accel/adis16204_core.c    |  613 +++++++++++++++++++++++++
 drivers/staging/iio/accel/adis16204_ring.c    |  206 +++++++++
 drivers/staging/iio/accel/adis16204_trigger.c |  122 +++++
 7 files changed, 1125 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16204.h
 create mode 100644 drivers/staging/iio/accel/adis16204_core.c
 create mode 100644 drivers/staging/iio/accel/adis16204_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16204_trigger.c

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index cc057c6..a34f1d3 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -21,6 +21,15 @@ config ADIS16203
 	  Say yes here to build support for Analog Devices adis16203 Programmable
 	  360 Degrees Inclinometer.
 
+config ADIS16204
+	tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16204 Programmable
+	  High-g Digital Impact Sensor and Recorder.
+
 config ADIS16209
 	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index 76cc92f..1b2a6d3 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -10,6 +10,10 @@ adis16203-y             := adis16203_core.o
 adis16203-$(CONFIG_IIO_RING_BUFFER) += adis16203_ring.o adis16203_trigger.o
 obj-$(CONFIG_ADIS16203) += adis16203.o
 
+adis16204-y             := adis16204_core.o
+adis16204-$(CONFIG_IIO_RING_BUFFER) += adis16204_ring.o adis16204_trigger.o
+obj-$(CONFIG_ADIS16204) += adis16204.o
+
 adis16209-y             := adis16209_core.o
 adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
 obj-$(CONFIG_ADIS16209) += adis16209.o
diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h
index f5f61b2..50651f8 100644
--- a/drivers/staging/iio/accel/accel.h
+++ b/drivers/staging/iio/accel/accel.h
@@ -65,3 +65,23 @@
 #define IIO_DEV_ATTR_ACCEL_Z(_show, _addr)			\
 	IIO_DEVICE_ATTR(accel_z_raw, S_IRUGO, _show, NULL, _addr)
 
+#define IIO_DEV_ATTR_ACCEL_XY(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_xy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_PEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_peak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_xpeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_YPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_ypeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_ZPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_zpeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XYPEAK(_show, _addr)		\
+	IIO_DEVICE_ATTR(accel_xypeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XYZPEAK(_show, _addr)		\
+	IIO_DEVICE_ATTR(accel_xyzpeak, S_IRUGO, _show, NULL, _addr)
diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h
new file mode 100644
index 0000000..e9ed7cb
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204.h
@@ -0,0 +1,151 @@
+#ifndef SPI_ADIS16204_H_
+#define SPI_ADIS16204_H_
+
+#define ADIS16204_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16204_READ_REG(a)    a
+#define ADIS16204_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16204_FLASH_CNT      0x00 /* Flash memory write count */
+#define ADIS16204_SUPPLY_OUT     0x02 /* Output, power supply */
+#define ADIS16204_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
+#define ADIS16204_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
+#define ADIS16204_AUX_ADC        0x08 /* Output, auxiliary ADC input */
+#define ADIS16204_TEMP_OUT       0x0A /* Output, temperature */
+#define ADIS16204_X_PEAK_OUT     0x0C /* Twos complement */
+#define ADIS16204_Y_PEAK_OUT     0x0E /* Twos complement */
+#define ADIS16204_XACCL_NULL     0x10 /* Calibration, x-axis acceleration offset null */
+#define ADIS16204_YACCL_NULL     0x12 /* Calibration, y-axis acceleration offset null */
+#define ADIS16204_XACCL_SCALE    0x14 /* X-axis scale factor calibration register */
+#define ADIS16204_YACCL_SCALE    0x16 /* Y-axis scale factor calibration register */
+#define ADIS16204_XY_RSS_OUT     0x18 /* XY combined acceleration (RSS) */
+#define ADIS16204_XY_PEAK_OUT    0x1A /* Peak, XY combined output (RSS) */
+#define ADIS16204_CAP_BUF_1      0x1C /* Capture buffer output register 1 */
+#define ADIS16204_CAP_BUF_2      0x1E /* Capture buffer output register 2 */
+#define ADIS16204_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16204_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16204_ALM_CTRL       0x28 /* Alarm control */
+#define ADIS16204_CAPT_PNTR      0x2A /* Capture register address pointer */
+#define ADIS16204_AUX_DAC        0x30 /* Auxiliary DAC data */
+#define ADIS16204_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
+#define ADIS16204_MSC_CTRL       0x34 /* Miscellaneous control */
+#define ADIS16204_SMPL_PRD       0x36 /* Internal sample period (rate) control */
+#define ADIS16204_AVG_CNT        0x38 /* Operation, filter configuration */
+#define ADIS16204_SLP_CNT        0x3A /* Operation, sleep mode control */
+#define ADIS16204_DIAG_STAT      0x3C /* Diagnostics, system status register */
+#define ADIS16204_GLOB_CMD       0x3E /* Operation, system command register */
+
+#define ADIS16204_OUTPUTS        5
+
+/* MSC_CTRL */
+#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16204_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
+#define ADIS16204_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16204_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2	(1 << 0)  /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+
+/* DIAG_STAT */
+#define ADIS16204_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag: 1 = error condition,
+						0 = normal operation */
+#define ADIS16204_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
+#define ADIS16204_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
+#define ADIS16204_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
+#define ADIS16204_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 2.975 V */
+
+/* GLOB_CMD */
+#define ADIS16204_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16204_GLOB_CMD_CLEAR_STAT	(1<<4)
+#define ADIS16204_GLOB_CMD_FACTORY_CAL	(1<<1)
+
+#define ADIS16204_MAX_TX 24
+#define ADIS16204_MAX_RX 24
+
+#define ADIS16204_ERROR_ACTIVE          (1<<14)
+
+/**
+ * struct adis16204_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct adis16204_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16204_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16204_scan {
+	ADIS16204_SCAN_SUPPLY,
+	ADIS16204_SCAN_ACC_X,
+	ADIS16204_SCAN_ACC_Y,
+	ADIS16204_SCAN_AUX_ADC,
+	ADIS16204_SCAN_TEMP,
+};
+
+void adis16204_remove_trigger(struct iio_dev *indio_dev);
+int adis16204_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16204_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+int adis16204_configure_ring(struct iio_dev *indio_dev);
+void adis16204_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16204_initialize_ring(struct iio_ring_buffer *ring);
+void adis16204_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16204_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16204_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16204_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16204_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16204_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16204_H_ */
diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
new file mode 100644
index 0000000..cc15e40
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_core.c
@@ -0,0 +1,613 @@
+/*
+ * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "../gyro/gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16204.h"
+
+#define DRIVER_NAME		"adis16204"
+
+static int adis16204_check_status(struct device *dev);
+
+/**
+ * adis16204_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+static int adis16204_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16204_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16204_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16204_WRITE_REG(lower_reg_address + 1);
+	st->tx[3] = (value >> 8) & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16204_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16204_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16204_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16204_ERROR_ACTIVE)
+		adis16204_check_status(dev);
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16204_read_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 val;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_TEMP_OUT, (u16 *)&val);
+	if (ret)
+		goto error_ret;
+
+	if (val & ADIS16204_ERROR_ACTIVE)
+		adis16204_check_status(dev);
+
+	val &= 0xFFF;
+	ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static ssize_t adis16204_read_12bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16204_ERROR_ACTIVE)
+			adis16204_check_status(dev);
+
+		val = ((s16)(val << 4) >> 4);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16204_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16204_ERROR_ACTIVE)
+			adis16204_check_status(dev);
+
+		val = ((s16)(val << 2) >> 2);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16204_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = adis16204_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int adis16204_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16204_spi_write_reg_8(dev,
+			ADIS16204_GLOB_CMD,
+			ADIS16204_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16204_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -EINVAL;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16204_reset(dev);
+	}
+	return -EINVAL;
+}
+
+int adis16204_set_irq(struct device *dev, bool enable)
+{
+	int ret = 0;
+	u16 msc;
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16204_MSC_CTRL_ACTIVE_HIGH;
+	msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_DIO2;
+	if (enable)
+		msc |= ADIS16204_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16204_spi_write_reg_16(dev, ADIS16204_MSC_CTRL, msc);
+
+error_ret:
+	return ret;
+}
+
+static int adis16204_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_DIAG_STAT, &status);
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status & 0x1F;
+
+	if (status & ADIS16204_DIAG_STAT_SELFTEST_FAIL)
+		dev_err(dev, "Self test failure\n");
+	if (status & ADIS16204_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16204_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16204_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 3.625V\n");
+	if (status & ADIS16204_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 2.975V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16204_self_test(struct device *dev)
+{
+	int ret;
+	ret = adis16204_spi_write_reg_16(dev,
+			ADIS16204_MSC_CTRL,
+			ADIS16204_MSC_CTRL_SELF_TEST_EN);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	adis16204_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16204_initial_setup(struct adis16204_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = adis16204_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+	ret = adis16204_self_test(dev);
+	if (ret) {
+		dev_err(dev, "self test failure");
+		goto err_ret;
+	}
+
+	/* Read status register to check the result */
+	ret = adis16204_check_status(dev);
+	if (ret) {
+		adis16204_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16204_STARTUP_DELAY);
+		ret = adis16204_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16204_read_12bit_unsigned,
+		ADIS16204_SUPPLY_OUT);
+static IIO_CONST_ATTR(in0_supply_scale, "0.00122");
+static IIO_DEV_ATTR_IN_RAW(1, adis16204_read_12bit_unsigned,
+		ADIS16204_AUX_ADC);
+static IIO_CONST_ATTR(in1_scale, "0.00061");
+
+static IIO_DEV_ATTR_ACCEL_X(adis16204_read_14bit_signed,
+		ADIS16204_XACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_Y(adis16204_read_14bit_signed,
+		ADIS16204_YACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_XY(adis16204_read_14bit_signed,
+		ADIS16204_XY_RSS_OUT);
+static IIO_DEV_ATTR_ACCEL_XPEAK(adis16204_read_14bit_signed,
+		ADIS16204_X_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_YPEAK(adis16204_read_14bit_signed,
+		ADIS16204_Y_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_XYPEAK(adis16204_read_14bit_signed,
+		ADIS16204_XY_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16204_read_12bit_signed,
+		adis16204_write_16bit,
+		ADIS16204_XACCL_NULL);
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+		adis16204_read_12bit_signed,
+		adis16204_write_16bit,
+		ADIS16204_YACCL_NULL);
+static IIO_CONST_ATTR(accel_x_scale, "0.017125");
+static IIO_CONST_ATTR(accel_y_scale, "0.008407");
+static IIO_CONST_ATTR(accel_xy_scale, "0.017125");
+
+static IIO_DEV_ATTR_TEMP_RAW(adis16204_read_temp);
+static IIO_CONST_ATTR(temp_offset, "25");
+static IIO_CONST_ATTR(temp_scale, "-0.47");
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16204_write_reset, 0);
+
+static IIO_CONST_ATTR(name, "adis16204");
+
+static struct attribute *adis16204_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16204_event_attribute_group = {
+	.attrs = adis16204_event_attributes,
+};
+
+static struct attribute *adis16204_attributes[] = {
+	&iio_dev_attr_in0_supply_raw.dev_attr.attr,
+	&iio_const_attr_in0_supply_scale.dev_attr.attr,
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_in1_raw.dev_attr.attr,
+	&iio_const_attr_in1_scale.dev_attr.attr,
+	&iio_dev_attr_accel_x_raw.dev_attr.attr,
+	&iio_dev_attr_accel_y_raw.dev_attr.attr,
+	&iio_dev_attr_accel_xy.dev_attr.attr,
+	&iio_dev_attr_accel_xpeak.dev_attr.attr,
+	&iio_dev_attr_accel_ypeak.dev_attr.attr,
+	&iio_dev_attr_accel_xypeak.dev_attr.attr,
+	&iio_dev_attr_accel_x_offset.dev_attr.attr,
+	&iio_dev_attr_accel_y_offset.dev_attr.attr,
+	&iio_const_attr_accel_x_scale.dev_attr.attr,
+	&iio_const_attr_accel_y_scale.dev_attr.attr,
+	&iio_const_attr_accel_xy_scale.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16204_attribute_group = {
+	.attrs = adis16204_attributes,
+};
+
+static int __devinit adis16204_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16204_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16204_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16204_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16204_event_attribute_group;
+	st->indio_dev->attrs = &adis16204_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16204_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16204_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16204");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16204_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16204_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	adis16204_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (spi->irq)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16204_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16204_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int adis16204_remove(struct spi_device *spi)
+{
+	struct adis16204_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16204_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16204_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	adis16204_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16204_driver = {
+	.driver = {
+		.name = "adis16204",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16204_probe,
+	.remove = __devexit_p(adis16204_remove),
+};
+
+static __init int adis16204_init(void)
+{
+	return spi_register_driver(&adis16204_driver);
+}
+module_init(adis16204_init);
+
+static __exit void adis16204_exit(void)
+{
+	spi_unregister_driver(&adis16204_driver);
+}
+module_exit(adis16204_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c
new file mode 100644
index 0000000..420b160
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_ring.c
@@ -0,0 +1,206 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16204.h"
+
+static IIO_SCAN_EL_C(in_supply, ADIS16204_SCAN_SUPPLY, ADIS16204_SUPPLY_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(in_supply, u, 12, 16);
+static IIO_SCAN_EL_C(accel_x, ADIS16204_SCAN_ACC_X, ADIS16204_XACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_y, ADIS16204_SCAN_ACC_Y, ADIS16204_YACCL_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 14, 16);
+static IIO_SCAN_EL_C(in0, ADIS16204_SCAN_AUX_ADC, ADIS16204_AUX_ADC, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(in0, u, 12, 16);
+static IIO_SCAN_EL_C(temp, ADIS16204_SCAN_TEMP, ADIS16204_TEMP_OUT, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(temp, u, 12, 16);
+static IIO_SCAN_EL_TIMESTAMP(5);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64);
+
+static struct attribute *adis16204_scan_el_attrs[] = {
+	&iio_scan_el_in_supply.dev_attr.attr,
+	&iio_const_attr_in_supply_index.dev_attr.attr,
+	&iio_const_attr_in_supply_type.dev_attr.attr,
+	&iio_scan_el_accel_x.dev_attr.attr,
+	&iio_const_attr_accel_x_index.dev_attr.attr,
+	&iio_scan_el_accel_y.dev_attr.attr,
+	&iio_const_attr_accel_y_index.dev_attr.attr,
+	&iio_const_attr_accel_type.dev_attr.attr,
+	&iio_scan_el_in0.dev_attr.attr,
+	&iio_const_attr_in0_index.dev_attr.attr,
+	&iio_const_attr_in0_type.dev_attr.attr,
+	&iio_scan_el_temp.dev_attr.attr,
+	&iio_const_attr_temp_index.dev_attr.attr,
+	&iio_const_attr_temp_type.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	&iio_const_attr_timestamp_index.dev_attr.attr,
+	&iio_const_attr_timestamp_type.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adis16204_scan_el_group = {
+	.attrs = adis16204_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * adis16204_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void adis16204_poll_func_th(struct iio_dev *indio_dev, s64 timestamp)
+{
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = timestamp;
+	schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16204_read_ring_data() read data registers which will be placed into ring
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read
+ **/
+static int adis16204_read_ring_data(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[ADIS16204_OUTPUTS + 1];
+	int ret;
+	int i;
+
+	mutex_lock(&st->buf_lock);
+
+	spi_message_init(&msg);
+
+	memset(xfers, 0, sizeof(xfers));
+	for (i = 0; i <= ADIS16204_OUTPUTS; i++) {
+		xfers[i].bits_per_word = 8;
+		xfers[i].cs_change = 1;
+		xfers[i].len = 2;
+		xfers[i].delay_usecs = 20;
+		xfers[i].tx_buf = st->tx + 2 * i;
+		st->tx[2 * i] = ADIS16204_READ_REG(ADIS16204_SUPPLY_OUT + 2 * i);
+		st->tx[2 * i + 1] = 0;
+		if (i >= 1)
+			xfers[i].rx_buf = rx + 2 * (i - 1);
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
+ * specific to be rolled into the core.
+ */
+static void adis16204_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct adis16204_state *st
+		= container_of(work_s, struct adis16204_state,
+			       work_trigger_to_ring);
+	struct iio_ring_buffer *ring = st->indio_dev->ring;
+
+	int i = 0;
+	s16 *data;
+	size_t datasize = ring->access.get_bytes_per_datum(ring);
+
+	data = kmalloc(datasize, GFP_KERNEL);
+	if (data == NULL) {
+		dev_err(&st->us->dev, "memory alloc failed in ring bh");
+		return;
+	}
+
+	if (ring->scan_count)
+		if (adis16204_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < ring->scan_count; i++)
+				data[i] = be16_to_cpup(
+					(__be16 *)&(st->rx[i*2]));
+
+	/* Guaranteed to be aligned with 8 byte boundary */
+	if (ring->scan_timestamp)
+		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp;
+
+	ring->access.store_to(ring,
+			      (u8 *)data,
+			      st->last_timestamp);
+
+	iio_trigger_notify_done(st->indio_dev->trig);
+	kfree(data);
+
+	return;
+}
+
+void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16204_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct adis16204_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, adis16204_trigger_bh_to_ring);
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->bpe = 2;
+	ring->scan_el_attrs = &adis16204_scan_el_group;
+	ring->scan_timestamp = true;
+	ring->preenable = &iio_sw_ring_preenable;
+	ring->postenable = &iio_triggered_ring_postenable;
+	ring->predisable = &iio_triggered_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	/* Set default scan mode */
+	iio_scan_mask_set(ring, iio_scan_el_in_supply.number);
+	iio_scan_mask_set(ring, iio_scan_el_accel_x.number);
+	iio_scan_mask_set(ring, iio_scan_el_accel_y.number);
+	iio_scan_mask_set(ring, iio_scan_el_temp.number);
+	iio_scan_mask_set(ring, iio_scan_el_in0.number);
+
+	ret = iio_alloc_pollfunc(indio_dev, NULL, &adis16204_poll_func_th);
+	if (ret)
+		goto error_iio_sw_rb_free;
+
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int adis16204_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16204_trigger.c b/drivers/staging/iio/accel/adis16204_trigger.c
new file mode 100644
index 0000000..8e9db90
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_trigger.c
@@ -0,0 +1,122 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "adis16204.h"
+
+/**
+ * adis16204_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16204_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct adis16204_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	iio_trigger_poll(trig, timestamp);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &adis16204_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16204_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group adis16204_trigger_attr_group = {
+	.attrs = adis16204_trigger_attrs,
+};
+
+/**
+ * adis16204_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16204_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis16204_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = adis16204_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * adis16204_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int adis16204_trig_try_reen(struct iio_trigger *trig)
+{
+	struct adis16204_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	return 0;
+}
+
+int adis16204_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct adis16204_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kasprintf(GFP_KERNEL,
+				"adis16204-dev%d",
+				indio_dev->id);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &adis16204_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &adis16204_trig_try_reen;
+	st->trig->control_attrs = &adis16204_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void adis16204_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct adis16204_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 07/36] staging: iio: new ADT7316/7/8 and ADT7516/7/9 driver
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (5 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 06/36] staging: iio: new adis16204 driver Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 08/36] staging: iio: adc: new driver for AD7150/1/6 devices Mike Frysinger
                   ` (29 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

IIO driver for temperature sensor, ADC and DAC devices over SPI and I2C.

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/Kconfig             |    1 +
 drivers/staging/iio/Makefile            |    1 +
 drivers/staging/iio/addac/Kconfig       |   25 +
 drivers/staging/iio/addac/Makefile      |    7 +
 drivers/staging/iio/addac/adt7316-i2c.c |  170 +++
 drivers/staging/iio/addac/adt7316-spi.c |  180 +++
 drivers/staging/iio/addac/adt7316.c     | 2402 +++++++++++++++++++++++++++++++
 drivers/staging/iio/addac/adt7316.h     |   33 +
 8 files changed, 2819 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/addac/Kconfig
 create mode 100644 drivers/staging/iio/addac/Makefile
 create mode 100644 drivers/staging/iio/addac/adt7316-i2c.c
 create mode 100644 drivers/staging/iio/addac/adt7316-spi.c
 create mode 100644 drivers/staging/iio/addac/adt7316.c
 create mode 100644 drivers/staging/iio/addac/adt7316.h

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index ed48815..b8bb5f1 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -42,6 +42,7 @@ config IIO_TRIGGER
 
 source "drivers/staging/iio/accel/Kconfig"
 source "drivers/staging/iio/adc/Kconfig"
+source "drivers/staging/iio/addac/Kconfig"
 source "drivers/staging/iio/gyro/Kconfig"
 source "drivers/staging/iio/imu/Kconfig"
 source "drivers/staging/iio/light/Kconfig"
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index e909674..0111647 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 
 obj-y += accel/
 obj-y += adc/
+obj-y += addac/
 obj-y += gyro/
 obj-y += imu/
 obj-y += light/
diff --git a/drivers/staging/iio/addac/Kconfig b/drivers/staging/iio/addac/Kconfig
new file mode 100644
index 0000000..9847baf
--- /dev/null
+++ b/drivers/staging/iio/addac/Kconfig
@@ -0,0 +1,25 @@
+#
+# ADDAC drivers
+#
+comment "Analog digital bi-direction convertors"
+
+config ADT7316
+	tristate "Analog Devices ADT7316/7/8 ADT7516/7/9 temperature sensor, ADC and DAC driver"
+	help
+	  Say yes here to build support for Analog Devices ADT7316, ADT7317, ADT7318
+	  and ADT7516, ADT7517, ADT7519 temperature sensors, ADC and DAC.
+
+config ADT7316_SPI
+	tristate "support SPI bus connection"
+	depends on SPI && ADT7316
+	default y
+	help
+	  Say yes here to build SPI bus support for Analog Devices ADT7316/7/8
+	  and ADT7516/7/9.
+
+config ADT7316_I2C
+	tristate "support I2C bus connection"
+	depends on I2C && ADT7316
+	help
+	  Say yes here to build I2C bus support for Analog Devices ADT7316/7/8
+	  and ADT7516/7/9.
diff --git a/drivers/staging/iio/addac/Makefile b/drivers/staging/iio/addac/Makefile
new file mode 100644
index 0000000..4c76861
--- /dev/null
+++ b/drivers/staging/iio/addac/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for industrial I/O ADDAC drivers
+#
+
+obj-$(CONFIG_ADT7316) += adt7316.o
+obj-$(CONFIG_ADT7316_SPI) += adt7316-spi.o
+obj-$(CONFIG_ADT7316_I2C) += adt7316-i2c.o
diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c
new file mode 100644
index 0000000..52d1ea3
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316-i2c.c
@@ -0,0 +1,170 @@
+/*
+ * I2C bus driver for ADT7316/7/8 ADT7516/7/9 digital temperature
+ * sensor, ADC and DAC
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+
+#include "adt7316.h"
+
+/*
+ * adt7316 register access by I2C
+ */
+static int adt7316_i2c_read(void *client, u8 reg, u8 *data)
+{
+	struct i2c_client *cl = client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_byte(cl, reg);
+	if (ret < 0) {
+		dev_err(&cl->dev, "I2C fail to select reg\n");
+		return ret;
+	}
+
+	ret = i2c_smbus_read_byte(client);
+	if (ret < 0) {
+		dev_err(&cl->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int adt7316_i2c_write(void *client, u8 reg, u8 data)
+{
+	struct i2c_client *cl = client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_byte_data(cl, reg, data);
+	if (ret < 0)
+		dev_err(&cl->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static int adt7316_i2c_multi_read(void *client, u8 reg, u8 count, u8 *data)
+{
+	struct i2c_client *cl = client;
+	int i, ret = 0;
+
+	if (count > ADT7316_REG_MAX_ADDR)
+		count = ADT7316_REG_MAX_ADDR;
+
+	for (i = 0; i < count; i++) {
+		ret = adt7316_i2c_read(cl, reg, &data[i]);
+		if (ret < 0) {
+			dev_err(&cl->dev, "I2C multi read error\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int adt7316_i2c_multi_write(void *client, u8 reg, u8 count, u8 *data)
+{
+	struct i2c_client *cl = client;
+	int i, ret = 0;
+
+	if (count > ADT7316_REG_MAX_ADDR)
+		count = ADT7316_REG_MAX_ADDR;
+
+	for (i = 0; i < count; i++) {
+		ret = adt7316_i2c_write(cl, reg, data[i]);
+		if (ret < 0) {
+			dev_err(&cl->dev, "I2C multi write error\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt7316_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct adt7316_bus bus = {
+		.client = client,
+		.irq = client->irq,
+		.irq_flags = IRQF_TRIGGER_LOW,
+		.read = adt7316_i2c_read,
+		.write = adt7316_i2c_write,
+		.multi_read = adt7316_i2c_multi_read,
+		.multi_write = adt7316_i2c_multi_write,
+	};
+
+	return adt7316_probe(&client->dev, &bus, id->name);
+}
+
+static int __devexit adt7316_i2c_remove(struct i2c_client *client)
+{
+	return adt7316_remove(&client->dev);;
+}
+
+static const struct i2c_device_id adt7316_i2c_id[] = {
+	{ "adt7316", 0 },
+	{ "adt7317", 0 },
+	{ "adt7318", 0 },
+	{ "adt7516", 0 },
+	{ "adt7517", 0 },
+	{ "adt7519", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, adt7316_i2c_id);
+
+#ifdef CONFIG_PM
+static int adt7316_i2c_suspend(struct i2c_client *client, pm_message_t message)
+{
+	return adt7316_disable(&client->dev);
+}
+
+static int adt7316_i2c_resume(struct i2c_client *client)
+{
+	return adt7316_enable(&client->dev);
+}
+#else
+# define adt7316_i2c_suspend NULL
+# define adt7316_i2c_resume  NULL
+#endif
+
+static struct i2c_driver adt7316_driver = {
+	.driver = {
+		.name = "adt7316",
+		.owner  = THIS_MODULE,
+	},
+	.probe = adt7316_i2c_probe,
+	.remove = __devexit_p(adt7316_i2c_remove),
+	.suspend = adt7316_i2c_suspend,
+	.resume = adt7316_i2c_resume,
+	.id_table = adt7316_i2c_id,
+};
+
+static __init int adt7316_i2c_init(void)
+{
+	return i2c_add_driver(&adt7316_driver);
+}
+
+static __exit void adt7316_i2c_exit(void)
+{
+	i2c_del_driver(&adt7316_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("I2C bus driver for Analog Devices ADT7316/7/9 and"
+			"ADT7516/7/8 digital temperature sensor, ADC and DAC");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt7316_i2c_init);
+module_exit(adt7316_i2c_exit);
diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c
new file mode 100644
index 0000000..369d4d0
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316-spi.c
@@ -0,0 +1,180 @@
+/*
+ * API bus driver for ADT7316/7/8 ADT7516/7/9 digital temperature
+ * sensor, ADC and DAC
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+
+#include "adt7316.h"
+
+#define ADT7316_SPI_MAX_FREQ_HZ		5000000
+#define ADT7316_SPI_CMD_READ		0x91
+#define ADT7316_SPI_CMD_WRITE		0x90
+
+/*
+ * adt7316 register access by SPI
+ */
+
+static int adt7316_spi_multi_read(void *client, u8 reg, u8 count, u8 *data)
+{
+	struct spi_device *spi_dev = client;
+	u8 cmd[2];
+	int ret = 0;
+
+	if (count > ADT7316_REG_MAX_ADDR)
+		count = ADT7316_REG_MAX_ADDR;
+
+	cmd[0] = ADT7316_SPI_CMD_WRITE;
+	cmd[1] = reg;
+
+	ret = spi_write(spi_dev, cmd, 2);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI fail to select reg\n");
+		return ret;
+	}
+
+	cmd[0] = ADT7316_SPI_CMD_READ;
+
+	ret = spi_write_then_read(spi_dev, cmd, 1, data, count);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read data error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int adt7316_spi_multi_write(void *client, u8 reg, u8 count, u8 *data)
+{
+	struct spi_device *spi_dev = client;
+	u8 buf[ADT7316_REG_MAX_ADDR + 2];
+	int i, ret = 0;
+
+	if (count > ADT7316_REG_MAX_ADDR)
+		count = ADT7316_REG_MAX_ADDR;
+
+	buf[0] = ADT7316_SPI_CMD_WRITE;
+	buf[1] = reg;
+	for (i = 0; i < count; i++)
+		buf[i + 2] = data[i];
+
+	ret = spi_write(spi_dev, buf, count + 2);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int adt7316_spi_read(void *client, u8 reg, u8 *data)
+{
+	return adt7316_spi_multi_read(client, reg, 1, data);
+}
+
+static int adt7316_spi_write(void *client, u8 reg, u8 val)
+{
+	return adt7316_spi_multi_write(client, reg, 1, &val);
+}
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt7316_spi_probe(struct spi_device *spi_dev)
+{
+	struct adt7316_bus bus = {
+		.client = spi_dev,
+		.irq = spi_dev->irq,
+		.irq_flags = IRQF_TRIGGER_LOW,
+		.read = adt7316_spi_read,
+		.write = adt7316_spi_write,
+		.multi_read = adt7316_spi_multi_read,
+		.multi_write = adt7316_spi_multi_write,
+	};
+
+	/* don't exceed max specified SPI CLK frequency */
+	if (spi_dev->max_speed_hz > ADT7316_SPI_MAX_FREQ_HZ) {
+		dev_err(&spi_dev->dev, "SPI CLK %d Hz?\n",
+			spi_dev->max_speed_hz);
+		return -EINVAL;
+	}
+
+	/* switch from default I2C protocol to SPI protocol */
+	adt7316_spi_write(spi_dev, 0, 0);
+	adt7316_spi_write(spi_dev, 0, 0);
+	adt7316_spi_write(spi_dev, 0, 0);
+
+	return adt7316_probe(&spi_dev->dev, &bus, spi_dev->modalias);
+}
+
+static int __devexit adt7316_spi_remove(struct spi_device *spi_dev)
+{
+	return adt7316_remove(&spi_dev->dev);
+}
+
+static const struct spi_device_id adt7316_spi_id[] = {
+	{ "adt7316", 0 },
+	{ "adt7317", 0 },
+	{ "adt7318", 0 },
+	{ "adt7516", 0 },
+	{ "adt7517", 0 },
+	{ "adt7519", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(spi, adt7316_spi_id);
+
+#ifdef CONFIG_PM
+static int adt7316_spi_suspend(struct spi_device *spi_dev, pm_message_t message)
+{
+	return adt7316_disable(&spi_dev->dev);
+}
+
+static int adt7316_spi_resume(struct spi_device *spi_dev)
+{
+	return adt7316_enable(&spi_dev->dev);
+}
+#else
+# define adt7316_spi_suspend NULL
+# define adt7316_spi_resume  NULL
+#endif
+
+static struct spi_driver adt7316_driver = {
+	.driver = {
+		.name = "adt7316",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = adt7316_spi_probe,
+	.remove = __devexit_p(adt7316_spi_remove),
+	.suspend = adt7316_spi_suspend,
+	.resume = adt7316_spi_resume,
+	.id_table = adt7316_spi_id,
+};
+
+static __init int adt7316_spi_init(void)
+{
+	return spi_register_driver(&adt7316_driver);
+}
+
+static __exit void adt7316_spi_exit(void)
+{
+	spi_unregister_driver(&adt7316_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("SPI bus driver for Analog Devices ADT7316/7/8 and"
+			"ADT7516/7/9 digital temperature sensor, ADC and DAC");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt7316_spi_init);
+module_exit(adt7316_spi_exit);
diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c
new file mode 100644
index 0000000..d1b5b13
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316.c
@@ -0,0 +1,2402 @@
+/*
+ * ADT7316 digital temperature sensor driver supporting ADT7316/7/8 ADT7516/7/9
+ *
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "adt7316.h"
+
+/*
+ * ADT7316 registers definition
+ */
+#define ADT7316_INT_STAT1		0x0
+#define ADT7316_INT_STAT2		0x1
+#define ADT7316_LSB_IN_TEMP_VDD		0x3
+#define ADT7316_LSB_IN_TEMP_MASK	0x3
+#define ADT7316_LSB_VDD_MASK		0xC
+#define ADT7316_LSB_VDD_OFFSET		2
+#define ADT7316_LSB_EX_TEMP_AIN		0x4
+#define ADT7316_LSB_EX_TEMP_MASK	0x3
+#define ADT7516_LSB_AIN_SHIFT		2
+#define ADT7316_AD_MSB_DATA_BASE        0x6
+#define ADT7316_AD_MSB_DATA_REGS        3
+#define ADT7516_AD_MSB_DATA_REGS        6
+#define ADT7316_MSB_VDD			0x6
+#define ADT7316_MSB_IN_TEMP		0x7
+#define ADT7316_MSB_EX_TEMP		0x8
+#define ADT7516_MSB_AIN1		0x8
+#define ADT7516_MSB_AIN2		0x9
+#define ADT7516_MSB_AIN3		0xA
+#define ADT7516_MSB_AIN4		0xB
+#define ADT7316_DA_DATA_BASE		0x10
+#define ADT7316_DA_MSB_DATA_REGS	4
+#define ADT7316_LSB_DAC_A		0x10
+#define ADT7316_MSB_DAC_A		0x11
+#define ADT7316_LSB_DAC_B		0x12
+#define ADT7316_MSB_DAC_B		0x13
+#define ADT7316_LSB_DAC_C		0x14
+#define ADT7316_MSB_DAC_C		0x15
+#define ADT7316_LSB_DAC_D		0x16
+#define ADT7316_MSB_DAC_D		0x17
+#define ADT7316_CONFIG1			0x18
+#define ADT7316_CONFIG2			0x19
+#define ADT7316_CONFIG3			0x1A
+#define ADT7316_LDAC_CONFIG		0x1B
+#define ADT7316_DAC_CONFIG		0x1C
+#define ADT7316_INT_MASK1		0x1D
+#define ADT7316_INT_MASK2		0x1E
+#define ADT7316_IN_TEMP_OFFSET		0x1F
+#define ADT7316_EX_TEMP_OFFSET		0x20
+#define ADT7316_IN_ANALOG_TEMP_OFFSET	0x21
+#define ADT7316_EX_ANALOG_TEMP_OFFSET	0x22
+#define ADT7316_VDD_HIGH		0x23
+#define ADT7316_VDD_LOW			0x24
+#define ADT7316_IN_TEMP_HIGH		0x25
+#define ADT7316_IN_TEMP_LOW		0x26
+#define ADT7316_EX_TEMP_HIGH		0x27
+#define ADT7316_EX_TEMP_LOW		0x28
+#define ADT7516_AIN2_HIGH		0x2B
+#define ADT7516_AIN2_LOW		0x2C
+#define ADT7516_AIN3_HIGH		0x2D
+#define ADT7516_AIN3_LOW		0x2E
+#define ADT7516_AIN4_HIGH		0x2F
+#define ADT7516_AIN4_LOW		0x30
+#define ADT7316_DEVICE_ID		0x4D
+#define ADT7316_MANUFACTURE_ID		0x4E
+#define ADT7316_DEVICE_REV		0x4F
+#define ADT7316_SPI_LOCK_STAT		0x7F
+
+/*
+ * ADT7316 config1
+ */
+#define ADT7316_EN			0x1
+#define ADT7516_SEL_EX_TEMP		0x4
+#define ADT7516_SEL_AIN1_2_EX_TEMP_MASK	0x6
+#define ADT7516_SEL_AIN3		0x8
+#define ADT7316_INT_EN			0x20
+#define ADT7316_INT_POLARITY		0x40
+#define ADT7316_PD			0x80
+
+/*
+ * ADT7316 config2
+ */
+#define ADT7316_AD_SINGLE_CH_MASK	0x3
+#define ADT7516_AD_SINGLE_CH_MASK	0x7
+#define ADT7316_AD_SINGLE_CH_VDD	0
+#define ADT7316_AD_SINGLE_CH_IN		1
+#define ADT7316_AD_SINGLE_CH_EX		2
+#define ADT7516_AD_SINGLE_CH_AIN1	2
+#define ADT7516_AD_SINGLE_CH_AIN2	3
+#define ADT7516_AD_SINGLE_CH_AIN3	4
+#define ADT7516_AD_SINGLE_CH_AIN4	5
+#define ADT7316_AD_SINGLE_CH_MODE	0x10
+#define ADT7316_DISABLE_AVERAGING	0x20
+#define ADT7316_EN_SMBUS_TIMEOUT	0x40
+#define ADT7316_RESET			0x80
+
+/*
+ * ADT7316 config3
+ */
+#define ADT7316_ADCLK_22_5		0x1
+#define ADT7316_DA_HIGH_RESOLUTION	0x2
+#define ADT7316_DA_EN_VIA_DAC_LDCA	0x4
+#define ADT7516_AIN_IN_VREF		0x10
+#define ADT7316_EN_IN_TEMP_PROP_DACA	0x20
+#define ADT7316_EN_EX_TEMP_PROP_DACB	0x40
+
+/*
+ * ADT7316 DAC config
+ */
+#define ADT7316_DA_2VREF_CH_MASK	0xF
+#define ADT7316_DA_EN_MODE_MASK		0x30
+#define ADT7316_DA_EN_MODE_SINGLE	0x00
+#define ADT7316_DA_EN_MODE_AB_CD	0x10
+#define ADT7316_DA_EN_MODE_ABCD		0x20
+#define ADT7316_DA_EN_MODE_LDAC		0x30
+#define ADT7316_VREF_BYPASS_DAC_AB	0x40
+#define ADT7316_VREF_BYPASS_DAC_CD	0x80
+
+/*
+ * ADT7316 LDAC config
+ */
+#define ADT7316_LDAC_EN_DA_MASK		0xF
+#define ADT7316_DAC_IN_VREF		0x10
+#define ADT7516_DAC_AB_IN_VREF		0x10
+#define ADT7516_DAC_CD_IN_VREF		0x20
+#define ADT7516_DAC_IN_VREF_OFFSET	4
+#define ADT7516_DAC_IN_VREF_MASK	0x30
+
+/*
+ * ADT7316 INT_MASK2
+ */
+#define ADT7316_INT_MASK2_VDD		0x10
+
+/*
+ * ADT7316 value masks
+ */
+#define ADT7316_VALUE_MASK		0xfff
+#define ADT7316_T_VALUE_SIGN		0x400
+#define ADT7316_T_VALUE_FLOAT_OFFSET	2
+#define ADT7316_T_VALUE_FLOAT_MASK	0x2
+
+/*
+ * Chip ID
+ */
+#define ID_ADT7316		0x1
+#define ID_ADT7317		0x2
+#define ID_ADT7318		0x3
+#define ID_ADT7516		0x11
+#define ID_ADT7517		0x12
+#define ID_ADT7519		0x14
+
+#define ID_FAMILY_MASK		0xF0
+#define ID_ADT73XX		0x0
+#define ID_ADT75XX		0x10
+
+/*
+ * struct adt7316_chip_info - chip specifc information
+ */
+
+struct adt7316_chip_info {
+	const char		*name;
+	struct iio_dev		*indio_dev;
+	struct work_struct	thresh_work;
+	s64			last_timestamp;
+	struct adt7316_bus	bus;
+	u16			ldac_pin;
+	u16			int_mask;	/* 0x2f */
+	u8			config1;
+	u8			config2;
+	u8			config3;
+	u8			dac_config;	/* DAC config */
+	u8			ldac_config;	/* LDAC config */
+	u8			dac_bits;	/* 8, 10, 12 */
+	u8			id;		/* chip id */
+};
+
+/*
+ * Logic interrupt mask for user application to enable
+ * interrupts.
+ */
+#define ADT7316_IN_TEMP_HIGH_INT_MASK	0x1
+#define ADT7316_IN_TEMP_LOW_INT_MASK	0x2
+#define ADT7316_EX_TEMP_HIGH_INT_MASK	0x4
+#define ADT7316_EX_TEMP_LOW_INT_MASK	0x8
+#define ADT7316_EX_TEMP_FAULT_INT_MASK	0x10
+#define ADT7516_AIN1_INT_MASK		0x4
+#define ADT7516_AIN2_INT_MASK		0x20
+#define ADT7516_AIN3_INT_MASK		0x40
+#define ADT7516_AIN4_INT_MASK		0x80
+#define ADT7316_VDD_INT_MASK		0x100
+#define ADT7316_TEMP_INT_MASK		0x1F
+#define ADT7516_AIN_INT_MASK		0xE0
+#define ADT7316_TEMP_AIN_INT_MASK	\
+	(ADT7316_TEMP_INT_MASK | ADT7316_TEMP_INT_MASK)
+
+/*
+ * struct adt7316_chip_info - chip specifc information
+ */
+
+struct adt7316_limit_regs {
+	u16	data_high;
+	u16	data_low;
+};
+
+static ssize_t adt7316_show_enabled(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_EN));
+}
+
+static ssize_t _adt7316_store_enabled(struct adt7316_chip_info *chip,
+		int enable)
+{
+	u8 config1;
+	int ret;
+
+	if (enable)
+		config1 = chip->config1 | ADT7316_EN;
+	else
+		config1 = chip->config1 & ~ADT7316_EN;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+	if (ret)
+		return -EIO;
+
+	chip->config1 = config1;
+
+	return ret;
+
+}
+
+static ssize_t adt7316_store_enabled(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	int enable;
+
+	if (!memcmp(buf, "1", 1))
+		enable = 1;
+	else
+		enable = 0;
+
+	if (_adt7316_store_enabled(chip, enable) < 0)
+		return -EIO;
+	else
+		return len;
+}
+
+static IIO_DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR,
+		adt7316_show_enabled,
+		adt7316_store_enabled,
+		0);
+
+static ssize_t adt7316_show_select_ex_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+		return -EPERM;
+
+	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7516_SEL_EX_TEMP));
+}
+
+static ssize_t adt7316_store_select_ex_temp(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config1;
+	int ret;
+
+	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+		return -EPERM;
+
+	config1 = chip->config1 & (~ADT7516_SEL_EX_TEMP);
+	if (!memcmp(buf, "1", 1))
+		config1 |= ADT7516_SEL_EX_TEMP;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+	if (ret)
+		return -EIO;
+
+	chip->config1 = config1;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(select_ex_temp, S_IRUGO | S_IWUSR,
+		adt7316_show_select_ex_temp,
+		adt7316_store_select_ex_temp,
+		0);
+
+static ssize_t adt7316_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if (chip->config2 & ADT7316_AD_SINGLE_CH_MODE)
+		return sprintf(buf, "single_channel\n");
+	else
+		return sprintf(buf, "round_robin\n");
+}
+
+static ssize_t adt7316_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config2;
+	int ret;
+
+	config2 = chip->config2 & (~ADT7316_AD_SINGLE_CH_MODE);
+	if (!memcmp(buf, "single_channel", 14))
+		config2 |= ADT7316_AD_SINGLE_CH_MODE;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+	if (ret)
+		return -EIO;
+
+	chip->config2 = config2;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt7316_show_mode,
+		adt7316_store_mode,
+		0);
+
+static ssize_t adt7316_show_all_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "single_channel\nround_robin\n");
+}
+
+static IIO_DEVICE_ATTR(all_modes, S_IRUGO, adt7316_show_all_modes, NULL, 0);
+
+static ssize_t adt7316_show_ad_channel(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE))
+		return -EPERM;
+
+	switch (chip->config2 & ADT7516_AD_SINGLE_CH_MASK) {
+	case ADT7316_AD_SINGLE_CH_VDD:
+		return sprintf(buf, "0 - VDD\n");
+	case ADT7316_AD_SINGLE_CH_IN:
+		return sprintf(buf, "1 - Internal Temperature\n");
+	case ADT7316_AD_SINGLE_CH_EX:
+		if (((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) &&
+			(chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0)
+			return sprintf(buf, "2 - AIN1\n");
+		else
+			return sprintf(buf, "2 - External Temperature\n");
+	case ADT7516_AD_SINGLE_CH_AIN2:
+		if ((chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0)
+			return sprintf(buf, "3 - AIN2\n");
+		else
+			return sprintf(buf, "N/A\n");
+	case ADT7516_AD_SINGLE_CH_AIN3:
+		if (chip->config1 & ADT7516_SEL_AIN3)
+			return sprintf(buf, "4 - AIN3\n");
+		else
+			return sprintf(buf, "N/A\n");
+	case ADT7516_AD_SINGLE_CH_AIN4:
+		return sprintf(buf, "5 - AIN4\n");
+	default:
+		return sprintf(buf, "N/A\n");
+	};
+}
+
+static ssize_t adt7316_store_ad_channel(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config2;
+	unsigned long data = 0;
+	int ret;
+
+	if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE))
+		return -EPERM;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) {
+		if (data > 5)
+			return -EINVAL;
+
+		config2 = chip->config2 & (~ADT7516_AD_SINGLE_CH_MASK);
+	} else {
+		if (data > 2)
+			return -EINVAL;
+
+		config2 = chip->config2 & (~ADT7316_AD_SINGLE_CH_MASK);
+	}
+
+
+	config2 |= data;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+	if (ret)
+		return -EIO;
+
+	chip->config2 = config2;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(ad_channel, S_IRUGO | S_IWUSR,
+		adt7316_show_ad_channel,
+		adt7316_store_ad_channel,
+		0);
+
+static ssize_t adt7316_show_all_ad_channels(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE))
+		return -EPERM;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+		return sprintf(buf, "0 - VDD\n1 - Internal Temperature\n"
+				"2 - External Temperature or AIN2\n"
+				"3 - AIN2\n4 - AIN3\n5 - AIN4\n");
+	else
+		return sprintf(buf, "0 - VDD\n1 - Internal Temperature\n"
+				"2 - External Temperature\n");
+}
+
+static IIO_DEVICE_ATTR(all_ad_channels, S_IRUGO,
+		adt7316_show_all_ad_channels, NULL, 0);
+
+static ssize_t adt7316_show_disable_averaging(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n",
+		!!(chip->config2 & ADT7316_DISABLE_AVERAGING));
+}
+
+static ssize_t adt7316_store_disable_averaging(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config2;
+	int ret;
+
+	config2 = chip->config2 & (~ADT7316_DISABLE_AVERAGING);
+	if (!memcmp(buf, "1", 1))
+		config2 |= ADT7316_DISABLE_AVERAGING;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+	if (ret)
+		return -EIO;
+
+	chip->config2 = config2;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(disable_averaging, S_IRUGO | S_IWUSR,
+		adt7316_show_disable_averaging,
+		adt7316_store_disable_averaging,
+		0);
+
+static ssize_t adt7316_show_enable_smbus_timeout(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n",
+		!!(chip->config2 & ADT7316_EN_SMBUS_TIMEOUT));
+}
+
+static ssize_t adt7316_store_enable_smbus_timeout(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config2;
+	int ret;
+
+	config2 = chip->config2 & (~ADT7316_EN_SMBUS_TIMEOUT);
+	if (!memcmp(buf, "1", 1))
+		config2 |= ADT7316_EN_SMBUS_TIMEOUT;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+	if (ret)
+		return -EIO;
+
+	chip->config2 = config2;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(enable_smbus_timeout, S_IRUGO | S_IWUSR,
+		adt7316_show_enable_smbus_timeout,
+		adt7316_store_enable_smbus_timeout,
+		0);
+
+
+static ssize_t adt7316_store_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config2;
+	int ret;
+
+	config2 = chip->config2 | ADT7316_RESET;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR,
+		NULL,
+		adt7316_store_reset,
+		0);
+
+static ssize_t adt7316_show_powerdown(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_PD));
+}
+
+static ssize_t adt7316_store_powerdown(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config1;
+	int ret;
+
+	config1 = chip->config1 & (~ADT7316_PD);
+	if (!memcmp(buf, "1", 1))
+		config1 |= ADT7316_PD;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+	if (ret)
+		return -EIO;
+
+	chip->config1 = config1;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(powerdown, S_IRUGO | S_IWUSR,
+		adt7316_show_powerdown,
+		adt7316_store_powerdown,
+		0);
+
+static ssize_t adt7316_show_fast_ad_clock(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->config3 & ADT7316_ADCLK_22_5));
+}
+
+static ssize_t adt7316_store_fast_ad_clock(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config3;
+	int ret;
+
+	config3 = chip->config3 & (~ADT7316_ADCLK_22_5);
+	if (!memcmp(buf, "1", 1))
+		config3 |= ADT7316_ADCLK_22_5;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+	if (ret)
+		return -EIO;
+
+	chip->config3 = config3;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(fast_ad_clock, S_IRUGO | S_IWUSR,
+		adt7316_show_fast_ad_clock,
+		adt7316_store_fast_ad_clock,
+		0);
+
+static ssize_t adt7316_show_da_high_resolution(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if (chip->config3 & ADT7316_DA_HIGH_RESOLUTION) {
+		if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516)
+			return sprintf(buf, "1 (12 bits)\n");
+		else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
+			return sprintf(buf, "1 (10 bits)\n");
+	}
+
+	return sprintf(buf, "0 (8 bits)\n");
+}
+
+static ssize_t adt7316_store_da_high_resolution(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config3;
+	int ret;
+
+	chip->dac_bits = 8;
+
+	if (!memcmp(buf, "1", 1)) {
+		config3 = chip->config3 | ADT7316_DA_HIGH_RESOLUTION;
+		if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516)
+			chip->dac_bits = 12;
+		else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
+			chip->dac_bits = 10;
+	} else
+		config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION);
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+	if (ret)
+		return -EIO;
+
+	chip->config3 = config3;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(da_high_resolution, S_IRUGO | S_IWUSR,
+		adt7316_show_da_high_resolution,
+		adt7316_store_da_high_resolution,
+		0);
+
+static ssize_t adt7316_show_AIN_internal_Vref(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+		return -EPERM;
+
+	return sprintf(buf, "%d\n",
+		!!(chip->config3 & ADT7516_AIN_IN_VREF));
+}
+
+static ssize_t adt7316_store_AIN_internal_Vref(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config3;
+	int ret;
+
+	if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+		return -EPERM;
+
+	if (memcmp(buf, "1", 1))
+		config3 = chip->config3 & (~ADT7516_AIN_IN_VREF);
+	else
+		config3 = chip->config3 | ADT7516_AIN_IN_VREF;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+	if (ret)
+		return -EIO;
+
+	chip->config3 = config3;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(AIN_internal_Vref, S_IRUGO | S_IWUSR,
+		adt7316_show_AIN_internal_Vref,
+		adt7316_store_AIN_internal_Vref,
+		0);
+
+
+static ssize_t adt7316_show_enable_prop_DACA(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n",
+		!!(chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA));
+}
+
+static ssize_t adt7316_store_enable_prop_DACA(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config3;
+	int ret;
+
+	config3 = chip->config3 & (~ADT7316_EN_IN_TEMP_PROP_DACA);
+	if (!memcmp(buf, "1", 1))
+		config3 |= ADT7316_EN_IN_TEMP_PROP_DACA;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+	if (ret)
+		return -EIO;
+
+	chip->config3 = config3;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(enable_proportion_DACA, S_IRUGO | S_IWUSR,
+		adt7316_show_enable_prop_DACA,
+		adt7316_store_enable_prop_DACA,
+		0);
+
+static ssize_t adt7316_show_enable_prop_DACB(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n",
+		!!(chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB));
+}
+
+static ssize_t adt7316_store_enable_prop_DACB(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config3;
+	int ret;
+
+	config3 = chip->config3 & (~ADT7316_EN_EX_TEMP_PROP_DACB);
+	if (!memcmp(buf, "1", 1))
+		config3 |= ADT7316_EN_EX_TEMP_PROP_DACB;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+	if (ret)
+		return -EIO;
+
+	chip->config3 = config3;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(enable_proportion_DACB, S_IRUGO | S_IWUSR,
+		adt7316_show_enable_prop_DACB,
+		adt7316_store_enable_prop_DACB,
+		0);
+
+static ssize_t adt7316_show_DAC_2Vref_ch_mask(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%x\n",
+		chip->dac_config & ADT7316_DA_2VREF_CH_MASK);
+}
+
+static ssize_t adt7316_store_DAC_2Vref_ch_mask(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 dac_config;
+	unsigned long data = 0;
+	int ret;
+
+	ret = strict_strtoul(buf, 16, &data);
+	if (ret || data > ADT7316_DA_2VREF_CH_MASK)
+		return -EINVAL;
+
+	dac_config = chip->dac_config & (~ADT7316_DA_2VREF_CH_MASK);
+	dac_config |= data;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+	if (ret)
+		return -EIO;
+
+	chip->dac_config = dac_config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(DAC_2Vref_channels_mask, S_IRUGO | S_IWUSR,
+		adt7316_show_DAC_2Vref_ch_mask,
+		adt7316_store_DAC_2Vref_ch_mask,
+		0);
+
+static ssize_t adt7316_show_DAC_update_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA))
+		return sprintf(buf, "manual\n");
+	else {
+		switch (chip->dac_config & ADT7316_DA_EN_MODE_MASK) {
+		case ADT7316_DA_EN_MODE_SINGLE:
+			return sprintf(buf, "0 - auto at any MSB DAC writing\n");
+		case ADT7316_DA_EN_MODE_AB_CD:
+			return sprintf(buf, "1 - auto at MSB DAC AB and CD writing\n");
+		case ADT7316_DA_EN_MODE_ABCD:
+			return sprintf(buf, "2 - auto at MSB DAC ABCD writing\n");
+		default: /* ADT7316_DA_EN_MODE_LDAC */
+			return sprintf(buf, "3 - manual\n");
+		};
+	}
+}
+
+static ssize_t adt7316_store_DAC_update_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 dac_config;
+	unsigned long data;
+	int ret;
+
+	if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA))
+		return -EPERM;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > ADT7316_DA_EN_MODE_MASK)
+		return -EINVAL;
+
+	dac_config = chip->dac_config & (~ADT7316_DA_EN_MODE_MASK);
+	dac_config |= data;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+	if (ret)
+		return -EIO;
+
+	chip->dac_config = dac_config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(DAC_update_mode, S_IRUGO | S_IWUSR,
+		adt7316_show_DAC_update_mode,
+		adt7316_store_DAC_update_mode,
+		0);
+
+static ssize_t adt7316_show_all_DAC_update_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA)
+		return sprintf(buf, "0 - auto at any MSB DAC writing\n"
+				"1 - auto at MSB DAC AB and CD writing\n"
+				"2 - auto at MSB DAC ABCD writing\n"
+				"3 - manual\n");
+	else
+		return sprintf(buf, "manual\n");
+}
+
+static IIO_DEVICE_ATTR(all_DAC_update_modes, S_IRUGO,
+		adt7316_show_all_DAC_update_modes, NULL, 0);
+
+
+static ssize_t adt7316_store_update_DAC(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 ldac_config;
+	unsigned long data;
+	int ret;
+
+	if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA) {
+		if ((chip->dac_config & ADT7316_DA_EN_MODE_MASK) !=
+			ADT7316_DA_EN_MODE_LDAC)
+			return -EPERM;
+
+		ret = strict_strtoul(buf, 16, &data);
+		if (ret || data > ADT7316_LDAC_EN_DA_MASK)
+			return -EINVAL;
+
+		ldac_config = chip->ldac_config & (~ADT7316_LDAC_EN_DA_MASK);
+		ldac_config |= data;
+
+		ret = chip->bus.write(chip->bus.client, ADT7316_LDAC_CONFIG,
+			ldac_config);
+		if (ret)
+			return -EIO;
+	} else {
+		gpio_set_value(chip->ldac_pin, 0);
+		gpio_set_value(chip->ldac_pin, 1);
+	}
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(update_DAC, S_IRUGO | S_IWUSR,
+		NULL,
+		adt7316_store_update_DAC,
+		0);
+
+static ssize_t adt7316_show_DA_AB_Vref_bypass(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+		return -EPERM;
+
+	return sprintf(buf, "%d\n",
+		!!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_AB));
+}
+
+static ssize_t adt7316_store_DA_AB_Vref_bypass(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 dac_config;
+	int ret;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+		return -EPERM;
+
+	dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_AB);
+	if (!memcmp(buf, "1", 1))
+		dac_config |= ADT7316_VREF_BYPASS_DAC_AB;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+	if (ret)
+		return -EIO;
+
+	chip->dac_config = dac_config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(DA_AB_Vref_bypass, S_IRUGO | S_IWUSR,
+		adt7316_show_DA_AB_Vref_bypass,
+		adt7316_store_DA_AB_Vref_bypass,
+		0);
+
+static ssize_t adt7316_show_DA_CD_Vref_bypass(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+		return -EPERM;
+
+	return sprintf(buf, "%d\n",
+		!!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_CD));
+}
+
+static ssize_t adt7316_store_DA_CD_Vref_bypass(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 dac_config;
+	int ret;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+		return -EPERM;
+
+	dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_CD);
+	if (!memcmp(buf, "1", 1))
+		dac_config |= ADT7316_VREF_BYPASS_DAC_CD;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+	if (ret)
+		return -EIO;
+
+	chip->dac_config = dac_config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(DA_CD_Vref_bypass, S_IRUGO | S_IWUSR,
+		adt7316_show_DA_CD_Vref_bypass,
+		adt7316_store_DA_CD_Vref_bypass,
+		0);
+
+static ssize_t adt7316_show_DAC_internal_Vref(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+		return sprintf(buf, "0x%x\n",
+			(chip->dac_config & ADT7516_DAC_IN_VREF_MASK) >>
+			ADT7516_DAC_IN_VREF_OFFSET);
+	else
+		return sprintf(buf, "%d\n",
+			!!(chip->dac_config & ADT7316_DAC_IN_VREF));
+}
+
+static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 ldac_config;
+	unsigned long data;
+	int ret;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) {
+		ret = strict_strtoul(buf, 16, &data);
+		if (ret || data > 3)
+			return -EINVAL;
+
+		ldac_config = chip->ldac_config & (~ADT7516_DAC_IN_VREF_MASK);
+		if (data & 0x1)
+			ldac_config |= ADT7516_DAC_AB_IN_VREF;
+		else if (data & 0x2)
+			ldac_config |= ADT7516_DAC_CD_IN_VREF;
+	} else {
+		ret = strict_strtoul(buf, 16, &data);
+		if (ret)
+			return -EINVAL;
+
+		ldac_config = chip->ldac_config & (~ADT7316_DAC_IN_VREF);
+		if (data)
+			ldac_config = chip->ldac_config | ADT7316_DAC_IN_VREF;
+	}
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_LDAC_CONFIG, ldac_config);
+	if (ret)
+		return -EIO;
+
+	chip->ldac_config = ldac_config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(DAC_internal_Vref, S_IRUGO | S_IWUSR,
+		adt7316_show_DAC_internal_Vref,
+		adt7316_store_DAC_internal_Vref,
+		0);
+
+static ssize_t adt7316_show_ad(struct adt7316_chip_info *chip,
+		int channel, char *buf)
+{
+	u16 data;
+	u8 msb, lsb;
+	char sign = ' ';
+	int ret;
+
+	if ((chip->config2 & ADT7316_AD_SINGLE_CH_MODE) &&
+		channel != (chip->config2 & ADT7516_AD_SINGLE_CH_MASK))
+		return -EPERM;
+
+	switch (channel) {
+	case ADT7316_AD_SINGLE_CH_IN:
+		ret = chip->bus.read(chip->bus.client,
+			ADT7316_LSB_IN_TEMP_VDD, &lsb);
+		if (ret)
+			return -EIO;
+
+		ret = chip->bus.read(chip->bus.client,
+			ADT7316_AD_MSB_DATA_BASE + channel, &msb);
+		if (ret)
+			return -EIO;
+
+		data = msb << ADT7316_T_VALUE_FLOAT_OFFSET;
+		data |= lsb & ADT7316_LSB_IN_TEMP_MASK;
+		break;
+	case ADT7316_AD_SINGLE_CH_VDD:
+		ret = chip->bus.read(chip->bus.client,
+			ADT7316_LSB_IN_TEMP_VDD, &lsb);
+		if (ret)
+			return -EIO;
+
+		ret = chip->bus.read(chip->bus.client,
+
+			ADT7316_AD_MSB_DATA_BASE + channel, &msb);
+		if (ret)
+			return -EIO;
+
+		data = msb << ADT7316_T_VALUE_FLOAT_OFFSET;
+		data |= (lsb & ADT7316_LSB_VDD_MASK) >> ADT7316_LSB_VDD_OFFSET;
+		return sprintf(buf, "%d\n", data);
+	default: /* ex_temp and ain */
+		ret = chip->bus.read(chip->bus.client,
+			ADT7316_LSB_EX_TEMP_AIN, &lsb);
+		if (ret)
+			return -EIO;
+
+		ret = chip->bus.read(chip->bus.client,
+			ADT7316_AD_MSB_DATA_BASE + channel, &msb);
+		if (ret)
+			return -EIO;
+
+		data = msb << ADT7316_T_VALUE_FLOAT_OFFSET;
+		data |= lsb & (ADT7316_LSB_EX_TEMP_MASK <<
+			(ADT7516_LSB_AIN_SHIFT * (channel -
+			(ADT7316_MSB_EX_TEMP - ADT7316_AD_MSB_DATA_BASE))));
+
+		if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+			return sprintf(buf, "%d\n", data);
+		else
+			break;
+	};
+
+	if (data & ADT7316_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (ADT7316_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data >> ADT7316_T_VALUE_FLOAT_OFFSET),
+		(data & ADT7316_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static ssize_t adt7316_show_VDD(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_VDD, buf);
+}
+static IIO_DEVICE_ATTR(VDD, S_IRUGO, adt7316_show_VDD, NULL, 0);
+
+static ssize_t adt7316_show_in_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_IN, buf);
+}
+
+static IIO_DEVICE_ATTR(in_temp, S_IRUGO, adt7316_show_in_temp, NULL, 0);
+
+static ssize_t adt7316_show_ex_temp_AIN1(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_EX, buf);
+}
+
+static IIO_DEVICE_ATTR(ex_temp_AIN1, S_IRUGO, adt7316_show_ex_temp_AIN1, NULL, 0);
+static IIO_DEVICE_ATTR(ex_temp, S_IRUGO, adt7316_show_ex_temp_AIN1, NULL, 0);
+
+static ssize_t adt7316_show_AIN2(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN2, buf);
+}
+static IIO_DEVICE_ATTR(AIN2, S_IRUGO, adt7316_show_AIN2, NULL, 0);
+
+static ssize_t adt7316_show_AIN3(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN3, buf);
+}
+static IIO_DEVICE_ATTR(AIN3, S_IRUGO, adt7316_show_AIN3, NULL, 0);
+
+static ssize_t adt7316_show_AIN4(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN4, buf);
+}
+static IIO_DEVICE_ATTR(AIN4, S_IRUGO, adt7316_show_AIN4, NULL, 0);
+
+static ssize_t adt7316_show_temp_offset(struct adt7316_chip_info *chip,
+		int offset_addr, char *buf)
+{
+	int data;
+	u8 val;
+	int ret;
+
+	ret = chip->bus.read(chip->bus.client, offset_addr, &val);
+	if (ret)
+		return -EIO;
+
+	data = (int)val;
+	if (val & 0x80)
+		data -= 256;
+
+	return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t adt7316_store_temp_offset(struct adt7316_chip_info *chip,
+		int offset_addr, const char *buf, size_t len)
+{
+	long data;
+	u8 val;
+	int ret;
+
+	ret = strict_strtol(buf, 10, &data);
+	if (ret || data > 127 || data < -128)
+		return -EINVAL;
+
+	if (data < 0)
+		data += 256;
+
+	val = (u8)data;
+
+	ret = chip->bus.write(chip->bus.client, offset_addr, val);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static ssize_t adt7316_show_in_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_temp_offset(chip, ADT7316_IN_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_in_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_temp_offset(chip, ADT7316_IN_TEMP_OFFSET, buf, len);
+}
+
+static IIO_DEVICE_ATTR(in_temp_offset, S_IRUGO | S_IWUSR,
+		adt7316_show_in_temp_offset,
+		adt7316_store_in_temp_offset, 0);
+
+static ssize_t adt7316_show_ex_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_temp_offset(chip, ADT7316_EX_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_ex_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_temp_offset(chip, ADT7316_EX_TEMP_OFFSET, buf, len);
+}
+
+static IIO_DEVICE_ATTR(ex_temp_offset, S_IRUGO | S_IWUSR,
+		adt7316_show_ex_temp_offset,
+		adt7316_store_ex_temp_offset, 0);
+
+static ssize_t adt7316_show_in_analog_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_temp_offset(chip,
+			ADT7316_IN_ANALOG_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_in_analog_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_temp_offset(chip,
+			ADT7316_IN_ANALOG_TEMP_OFFSET, buf, len);
+}
+
+static IIO_DEVICE_ATTR(in_analog_temp_offset, S_IRUGO | S_IWUSR,
+		adt7316_show_in_analog_temp_offset,
+		adt7316_store_in_analog_temp_offset, 0);
+
+static ssize_t adt7316_show_ex_analog_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_temp_offset(chip,
+			ADT7316_EX_ANALOG_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_ex_analog_temp_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_temp_offset(chip,
+			ADT7316_EX_ANALOG_TEMP_OFFSET, buf, len);
+}
+
+static IIO_DEVICE_ATTR(ex_analog_temp_offset, S_IRUGO | S_IWUSR,
+		adt7316_show_ex_analog_temp_offset,
+		adt7316_store_ex_analog_temp_offset, 0);
+
+static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip,
+		int channel, char *buf)
+{
+	u16 data;
+	u8 msb, lsb, offset;
+	int ret;
+
+	if (channel >= ADT7316_DA_MSB_DATA_REGS ||
+		(channel == 0 &&
+		(chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA)) ||
+		(channel == 1 &&
+		(chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB)))
+		return -EPERM;
+
+	offset = chip->dac_bits - 8;
+
+	if (chip->dac_bits > 8) {
+		ret = chip->bus.read(chip->bus.client,
+			ADT7316_DA_DATA_BASE + channel * 2, &lsb);
+		if (ret)
+			return -EIO;
+	}
+
+	ret = chip->bus.read(chip->bus.client,
+		ADT7316_DA_DATA_BASE + 1 + channel * 2, &msb);
+	if (ret)
+		return -EIO;
+
+	data = (msb << offset) + (lsb & ((1 << offset) - 1));
+
+	return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip,
+		int channel, const char *buf, size_t len)
+{
+	u8 msb, lsb, offset;
+	unsigned long data;
+	int ret;
+
+	if (channel >= ADT7316_DA_MSB_DATA_REGS ||
+		(channel == 0 &&
+		(chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA)) ||
+		(channel == 1 &&
+		(chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB)))
+		return -EPERM;
+
+	offset = chip->dac_bits - 8;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data >= (1 << chip->dac_bits))
+		return -EINVAL;
+
+	if (chip->dac_bits > 8) {
+		lsb = data & (1 << offset);
+		ret = chip->bus.write(chip->bus.client,
+			ADT7316_DA_DATA_BASE + channel * 2, lsb);
+		if (ret)
+			return -EIO;
+	}
+
+	msb = data >> offset;
+	ret = chip->bus.write(chip->bus.client,
+		ADT7316_DA_DATA_BASE + 1 + channel * 2, msb);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static ssize_t adt7316_show_DAC_A(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_DAC(chip, 0, buf);
+}
+
+static ssize_t adt7316_store_DAC_A(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_DAC(chip, 0, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_A, S_IRUGO | S_IWUSR, adt7316_show_DAC_A,
+		adt7316_store_DAC_A, 0);
+
+static ssize_t adt7316_show_DAC_B(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_DAC(chip, 1, buf);
+}
+
+static ssize_t adt7316_store_DAC_B(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_DAC(chip, 1, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_B, S_IRUGO | S_IWUSR, adt7316_show_DAC_B,
+		adt7316_store_DAC_B, 0);
+
+static ssize_t adt7316_show_DAC_C(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_DAC(chip, 2, buf);
+}
+
+static ssize_t adt7316_store_DAC_C(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_DAC(chip, 2, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_C, S_IRUGO | S_IWUSR, adt7316_show_DAC_C,
+		adt7316_store_DAC_C, 0);
+
+static ssize_t adt7316_show_DAC_D(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_show_DAC(chip, 3, buf);
+}
+
+static ssize_t adt7316_store_DAC_D(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return adt7316_store_DAC(chip, 3, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_D, S_IRUGO | S_IWUSR, adt7316_show_DAC_D,
+		adt7316_store_DAC_D, 0);
+
+static ssize_t adt7316_show_device_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 id;
+	int ret;
+
+	ret = chip->bus.read(chip->bus.client, ADT7316_DEVICE_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", id);
+}
+
+static IIO_DEVICE_ATTR(device_id, S_IRUGO, adt7316_show_device_id, NULL, 0);
+
+static ssize_t adt7316_show_manufactorer_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 id;
+	int ret;
+
+	ret = chip->bus.read(chip->bus.client, ADT7316_MANUFACTURE_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", id);
+}
+
+static IIO_DEVICE_ATTR(manufactorer_id, S_IRUGO,
+		adt7316_show_manufactorer_id, NULL, 0);
+
+static ssize_t adt7316_show_device_rev(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 rev;
+	int ret;
+
+	ret = chip->bus.read(chip->bus.client, ADT7316_DEVICE_REV, &rev);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", rev);
+}
+
+static IIO_DEVICE_ATTR(device_rev, S_IRUGO, adt7316_show_device_rev, NULL, 0);
+
+static ssize_t adt7316_show_bus_type(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 stat;
+	int ret;
+
+	ret = chip->bus.read(chip->bus.client, ADT7316_SPI_LOCK_STAT, &stat);
+	if (ret)
+		return -EIO;
+
+	if (stat)
+		return sprintf(buf, "spi\n");
+	else
+		return sprintf(buf, "i2c\n");
+}
+
+static IIO_DEVICE_ATTR(bus_type, S_IRUGO, adt7316_show_bus_type, NULL, 0);
+
+static ssize_t adt7316_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt7316_show_name, NULL, 0);
+
+static struct attribute *adt7316_attributes[] = {
+	&iio_dev_attr_all_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_dev_attr_enabled.dev_attr.attr,
+	&iio_dev_attr_ad_channel.dev_attr.attr,
+	&iio_dev_attr_all_ad_channels.dev_attr.attr,
+	&iio_dev_attr_disable_averaging.dev_attr.attr,
+	&iio_dev_attr_enable_smbus_timeout.dev_attr.attr,
+	&iio_dev_attr_powerdown.dev_attr.attr,
+	&iio_dev_attr_fast_ad_clock.dev_attr.attr,
+	&iio_dev_attr_da_high_resolution.dev_attr.attr,
+	&iio_dev_attr_enable_proportion_DACA.dev_attr.attr,
+	&iio_dev_attr_enable_proportion_DACB.dev_attr.attr,
+	&iio_dev_attr_DAC_2Vref_channels_mask.dev_attr.attr,
+	&iio_dev_attr_DAC_update_mode.dev_attr.attr,
+	&iio_dev_attr_all_DAC_update_modes.dev_attr.attr,
+	&iio_dev_attr_update_DAC.dev_attr.attr,
+	&iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr,
+	&iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr,
+	&iio_dev_attr_DAC_internal_Vref.dev_attr.attr,
+	&iio_dev_attr_VDD.dev_attr.attr,
+	&iio_dev_attr_in_temp.dev_attr.attr,
+	&iio_dev_attr_ex_temp.dev_attr.attr,
+	&iio_dev_attr_in_temp_offset.dev_attr.attr,
+	&iio_dev_attr_ex_temp_offset.dev_attr.attr,
+	&iio_dev_attr_in_analog_temp_offset.dev_attr.attr,
+	&iio_dev_attr_ex_analog_temp_offset.dev_attr.attr,
+	&iio_dev_attr_DAC_A.dev_attr.attr,
+	&iio_dev_attr_DAC_B.dev_attr.attr,
+	&iio_dev_attr_DAC_C.dev_attr.attr,
+	&iio_dev_attr_DAC_D.dev_attr.attr,
+	&iio_dev_attr_device_id.dev_attr.attr,
+	&iio_dev_attr_manufactorer_id.dev_attr.attr,
+	&iio_dev_attr_device_rev.dev_attr.attr,
+	&iio_dev_attr_bus_type.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt7316_attribute_group = {
+	.attrs = adt7316_attributes,
+};
+
+static struct attribute *adt7516_attributes[] = {
+	&iio_dev_attr_all_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_select_ex_temp.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_dev_attr_enabled.dev_attr.attr,
+	&iio_dev_attr_ad_channel.dev_attr.attr,
+	&iio_dev_attr_all_ad_channels.dev_attr.attr,
+	&iio_dev_attr_disable_averaging.dev_attr.attr,
+	&iio_dev_attr_enable_smbus_timeout.dev_attr.attr,
+	&iio_dev_attr_powerdown.dev_attr.attr,
+	&iio_dev_attr_fast_ad_clock.dev_attr.attr,
+	&iio_dev_attr_AIN_internal_Vref.dev_attr.attr,
+	&iio_dev_attr_da_high_resolution.dev_attr.attr,
+	&iio_dev_attr_enable_proportion_DACA.dev_attr.attr,
+	&iio_dev_attr_enable_proportion_DACB.dev_attr.attr,
+	&iio_dev_attr_DAC_2Vref_channels_mask.dev_attr.attr,
+	&iio_dev_attr_DAC_update_mode.dev_attr.attr,
+	&iio_dev_attr_all_DAC_update_modes.dev_attr.attr,
+	&iio_dev_attr_update_DAC.dev_attr.attr,
+	&iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr,
+	&iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr,
+	&iio_dev_attr_DAC_internal_Vref.dev_attr.attr,
+	&iio_dev_attr_VDD.dev_attr.attr,
+	&iio_dev_attr_in_temp.dev_attr.attr,
+	&iio_dev_attr_ex_temp_AIN1.dev_attr.attr,
+	&iio_dev_attr_AIN2.dev_attr.attr,
+	&iio_dev_attr_AIN3.dev_attr.attr,
+	&iio_dev_attr_AIN4.dev_attr.attr,
+	&iio_dev_attr_in_temp_offset.dev_attr.attr,
+	&iio_dev_attr_ex_temp_offset.dev_attr.attr,
+	&iio_dev_attr_in_analog_temp_offset.dev_attr.attr,
+	&iio_dev_attr_ex_analog_temp_offset.dev_attr.attr,
+	&iio_dev_attr_DAC_A.dev_attr.attr,
+	&iio_dev_attr_DAC_B.dev_attr.attr,
+	&iio_dev_attr_DAC_C.dev_attr.attr,
+	&iio_dev_attr_DAC_D.dev_attr.attr,
+	&iio_dev_attr_device_id.dev_attr.attr,
+	&iio_dev_attr_manufactorer_id.dev_attr.attr,
+	&iio_dev_attr_device_rev.dev_attr.attr,
+	&iio_dev_attr_bus_type.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt7516_attribute_group = {
+	.attrs = adt7516_attributes,
+};
+
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT7316_IN_TEMP_HIGH   IIO_BUFFER_EVENT_CODE(0)
+#define IIO_EVENT_CODE_ADT7316_IN_TEMP_LOW    IIO_BUFFER_EVENT_CODE(1)
+#define IIO_EVENT_CODE_ADT7316_EX_TEMP_HIGH   IIO_BUFFER_EVENT_CODE(2)
+#define IIO_EVENT_CODE_ADT7316_EX_TEMP_LOW    IIO_BUFFER_EVENT_CODE(3)
+#define IIO_EVENT_CODE_ADT7316_EX_TEMP_FAULT  IIO_BUFFER_EVENT_CODE(4)
+#define IIO_EVENT_CODE_ADT7516_AIN1           IIO_BUFFER_EVENT_CODE(5)
+#define IIO_EVENT_CODE_ADT7516_AIN2           IIO_BUFFER_EVENT_CODE(6)
+#define IIO_EVENT_CODE_ADT7516_AIN3           IIO_BUFFER_EVENT_CODE(7)
+#define IIO_EVENT_CODE_ADT7516_AIN4           IIO_BUFFER_EVENT_CODE(8)
+#define IIO_EVENT_CODE_ADT7316_VDD            IIO_BUFFER_EVENT_CODE(9)
+
+static void adt7316_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt7316_chip_info *chip =
+		container_of(work_s, struct adt7316_chip_info, thresh_work);
+	u8 stat1, stat2;
+	int i, ret, count;
+
+	ret = chip->bus.read(chip->bus.client, ADT7316_INT_STAT1, &stat1);
+	if (!ret) {
+		if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+			count = 8;
+		else
+			count = 5;
+
+		for (i = 0; i < count; i++) {
+			if (stat1 & (1 << i))
+				iio_push_event(chip->indio_dev, 0,
+					IIO_EVENT_CODE_ADT7316_IN_TEMP_HIGH + i,
+					chip->last_timestamp);
+		}
+	}
+
+	ret = chip->bus.read(chip->bus.client, ADT7316_INT_STAT2, &stat2);
+	if (!ret) {
+		if (stat2 & ADT7316_INT_MASK2_VDD)
+			iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_ADT7316_VDD,
+				chip->last_timestamp);
+	}
+
+	enable_irq(chip->bus.irq);
+}
+
+static int adt7316_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt7316, &adt7316_interrupt);
+
+/*
+ * Show mask of enabled interrupts in Hex.
+ */
+static ssize_t adt7316_show_int_mask(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%x\n", chip->int_mask);
+}
+
+/*
+ * Set 1 to the mask in Hex to enabled interrupts.
+ */
+static ssize_t adt7316_set_int_mask(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+	u8 mask;
+
+	ret = strict_strtoul(buf, 16, &data);
+	if (ret || data >= ADT7316_VDD_INT_MASK + 1)
+		return -EINVAL;
+
+	if (data & ADT7316_VDD_INT_MASK)
+		mask = 0;			/* enable vdd int */
+	else
+		mask = ADT7316_INT_MASK2_VDD;	/* disable vdd int */
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_INT_MASK2, mask);
+	if (!ret) {
+		chip->int_mask &= ~ADT7316_VDD_INT_MASK;
+		chip->int_mask |= data & ADT7316_VDD_INT_MASK;
+	}
+
+	if (data & ADT7316_TEMP_AIN_INT_MASK) {
+		if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX)
+			/* mask in reg is opposite, set 1 to disable */
+			mask = (~data) & ADT7316_TEMP_INT_MASK;
+		else
+			/* mask in reg is opposite, set 1 to disable */
+			mask = (~data) & ADT7316_TEMP_AIN_INT_MASK;
+	}
+	ret = chip->bus.write(chip->bus.client, ADT7316_INT_MASK1, mask);
+
+	chip->int_mask = mask;
+
+	return len;
+}
+static inline ssize_t adt7316_show_ad_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 val;
+	int data;
+	int ret;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX &&
+		bound_reg > ADT7316_EX_TEMP_LOW)
+		return -EPERM;
+
+	ret = chip->bus.read(chip->bus.client, bound_reg, &val);
+	if (ret)
+		return -EIO;
+
+	data = (int)val;
+
+	if (!((chip->id & ID_FAMILY_MASK) == ID_ADT75XX &&
+		(chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0)) {
+		if (data & 0x80)
+			data -= 256;
+	}
+
+	return sprintf(buf, "%d\n", data);
+}
+
+static inline ssize_t adt7316_set_ad_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	long data;
+	u8 val;
+	int ret;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX &&
+		bound_reg > ADT7316_EX_TEMP_LOW)
+		return -EPERM;
+
+	ret = strict_strtol(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX &&
+		(chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0) {
+		if (data > 255 || data < 0)
+			return -EINVAL;
+	} else {
+		if (data > 127 || data < -128)
+			return -EINVAL;
+
+		if (data < 0)
+			data += 256;
+	}
+
+	val = (u8)data;
+
+	ret = chip->bus.write(chip->bus.client, bound_reg, val);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static ssize_t adt7316_show_in_temp_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7316_IN_TEMP_HIGH, buf);
+}
+
+static inline ssize_t adt7316_set_in_temp_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7316_IN_TEMP_HIGH, buf, len);
+}
+
+static ssize_t adt7316_show_in_temp_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7316_IN_TEMP_LOW, buf);
+}
+
+static inline ssize_t adt7316_set_in_temp_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7316_IN_TEMP_LOW, buf, len);
+}
+
+static ssize_t adt7316_show_ex_temp_ain1_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7316_EX_TEMP_HIGH, buf);
+}
+
+static inline ssize_t adt7316_set_ex_temp_ain1_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7316_EX_TEMP_HIGH, buf, len);
+}
+
+static ssize_t adt7316_show_ex_temp_ain1_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7316_EX_TEMP_LOW, buf);
+}
+
+static inline ssize_t adt7316_set_ex_temp_ain1_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7316_EX_TEMP_LOW, buf, len);
+}
+
+static ssize_t adt7316_show_ain2_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7516_AIN2_HIGH, buf);
+}
+
+static inline ssize_t adt7316_set_ain2_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7516_AIN2_HIGH, buf, len);
+}
+
+static ssize_t adt7316_show_ain2_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7516_AIN2_LOW, buf);
+}
+
+static inline ssize_t adt7316_set_ain2_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7516_AIN2_LOW, buf, len);
+}
+
+static ssize_t adt7316_show_ain3_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7516_AIN3_HIGH, buf);
+}
+
+static inline ssize_t adt7316_set_ain3_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7516_AIN3_HIGH, buf, len);
+}
+
+static ssize_t adt7316_show_ain3_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7516_AIN3_LOW, buf);
+}
+
+static inline ssize_t adt7316_set_ain3_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7516_AIN3_LOW, buf, len);
+}
+
+static ssize_t adt7316_show_ain4_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7516_AIN4_HIGH, buf);
+}
+
+static inline ssize_t adt7316_set_ain4_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7516_AIN4_HIGH, buf, len);
+}
+
+static ssize_t adt7316_show_ain4_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7316_show_ad_bound(dev, attr,
+			ADT7516_AIN4_LOW, buf);
+}
+
+static inline ssize_t adt7316_set_ain4_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7316_set_ad_bound(dev, attr,
+			ADT7516_AIN4_LOW, buf, len);
+}
+
+static ssize_t adt7316_show_int_enabled(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_INT_EN));
+}
+
+static ssize_t adt7316_set_int_enabled(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	u8 config1;
+	int ret;
+
+	config1 = chip->config1 & (~ADT7316_INT_EN);
+	if (!memcmp(buf, "1", 1))
+		config1 |= ADT7316_INT_EN;
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+	if (ret)
+		return -EIO;
+
+	chip->config1 = config1;
+
+	return len;
+}
+
+
+IIO_EVENT_ATTR_SH(int_mask, iio_event_adt7316,
+		adt7316_show_int_mask, adt7316_set_int_mask, 0);
+IIO_EVENT_ATTR_SH(in_temp_high, iio_event_adt7316,
+		adt7316_show_in_temp_high, adt7316_set_in_temp_high, 0);
+IIO_EVENT_ATTR_SH(in_temp_low, iio_event_adt7316,
+		adt7316_show_in_temp_low, adt7316_set_in_temp_low, 0);
+IIO_EVENT_ATTR_SH(ex_temp_high, iio_event_adt7316,
+		adt7316_show_ex_temp_ain1_high,
+		adt7316_set_ex_temp_ain1_high, 0);
+IIO_EVENT_ATTR_SH(ex_temp_low, iio_event_adt7316,
+		adt7316_show_ex_temp_ain1_low,
+		adt7316_set_ex_temp_ain1_low, 0);
+IIO_EVENT_ATTR_SH(ex_temp_ain1_high, iio_event_adt7316,
+		adt7316_show_ex_temp_ain1_high,
+		adt7316_set_ex_temp_ain1_high, 0);
+IIO_EVENT_ATTR_SH(ex_temp_ain1_low, iio_event_adt7316,
+		adt7316_show_ex_temp_ain1_low,
+		adt7316_set_ex_temp_ain1_low, 0);
+IIO_EVENT_ATTR_SH(ain2_high, iio_event_adt7316,
+		adt7316_show_ain2_high, adt7316_set_ain2_high, 0);
+IIO_EVENT_ATTR_SH(ain2_low, iio_event_adt7316,
+		adt7316_show_ain2_low, adt7316_set_ain2_low, 0);
+IIO_EVENT_ATTR_SH(ain3_high, iio_event_adt7316,
+		adt7316_show_ain3_high, adt7316_set_ain3_high, 0);
+IIO_EVENT_ATTR_SH(ain3_low, iio_event_adt7316,
+		adt7316_show_ain3_low, adt7316_set_ain3_low, 0);
+IIO_EVENT_ATTR_SH(ain4_high, iio_event_adt7316,
+		adt7316_show_ain4_high, adt7316_set_ain4_high, 0);
+IIO_EVENT_ATTR_SH(ain4_low, iio_event_adt7316,
+		adt7316_show_ain4_low, adt7316_set_ain4_low, 0);
+IIO_EVENT_ATTR_SH(int_enabled, iio_event_adt7316,
+		adt7316_show_int_enabled, adt7316_set_int_enabled, 0);
+
+static struct attribute *adt7316_event_attributes[] = {
+	&iio_event_attr_int_mask.dev_attr.attr,
+	&iio_event_attr_in_temp_high.dev_attr.attr,
+	&iio_event_attr_in_temp_low.dev_attr.attr,
+	&iio_event_attr_ex_temp_high.dev_attr.attr,
+	&iio_event_attr_ex_temp_low.dev_attr.attr,
+	&iio_event_attr_int_enabled.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt7316_event_attribute_group = {
+	.attrs = adt7316_event_attributes,
+};
+
+static struct attribute *adt7516_event_attributes[] = {
+	&iio_event_attr_int_mask.dev_attr.attr,
+	&iio_event_attr_in_temp_high.dev_attr.attr,
+	&iio_event_attr_in_temp_low.dev_attr.attr,
+	&iio_event_attr_ex_temp_ain1_high.dev_attr.attr,
+	&iio_event_attr_ex_temp_ain1_low.dev_attr.attr,
+	&iio_event_attr_ain2_high.dev_attr.attr,
+	&iio_event_attr_ain2_low.dev_attr.attr,
+	&iio_event_attr_ain3_high.dev_attr.attr,
+	&iio_event_attr_ain3_low.dev_attr.attr,
+	&iio_event_attr_ain4_high.dev_attr.attr,
+	&iio_event_attr_ain4_low.dev_attr.attr,
+	&iio_event_attr_int_enabled.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt7516_event_attribute_group = {
+	.attrs = adt7516_event_attributes,
+};
+
+#ifdef CONFIG_PM
+int adt7316_disable(struct device *dev)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return _adt7316_store_enabled(chip, 0);
+}
+EXPORT_SYMBOL(adt7316_disable);
+
+int adt7316_enable(struct device *dev)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+
+	return _adt7316_store_enabled(chip, 1);
+}
+EXPORT_SYMBOL(adt7316_enable);
+#endif
+
+/*
+ * device probe and remove
+ */
+int __devinit adt7316_probe(struct device *dev, struct adt7316_bus *bus,
+		const char *name)
+{
+	struct adt7316_chip_info *chip;
+	unsigned short *adt7316_platform_data = dev->platform_data;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct adt7316_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(dev, chip);
+
+	chip->bus = *bus;
+	chip->name = name;
+
+	if (name[4] == '3')
+		chip->id = ID_ADT7316 + (name[6] - '6');
+	else if (name[4] == '5')
+		chip->id = ID_ADT7516 + (name[6] - '6');
+	else
+		return -ENODEV;
+
+	chip->ldac_pin = adt7316_platform_data[1];
+	if (chip->ldac_pin) {
+		chip->config3 |= ADT7316_DA_EN_VIA_DAC_LDCA;
+		if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+			chip->config1 |= ADT7516_SEL_AIN3;
+	}
+	chip->int_mask = ADT7316_TEMP_INT_MASK | ADT7316_VDD_INT_MASK;
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+		chip->int_mask |= ADT7516_AIN_INT_MASK;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = dev;
+	if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) {
+		chip->indio_dev->attrs = &adt7516_attribute_group;
+		chip->indio_dev->event_attrs = &adt7516_event_attribute_group;
+	} else {
+		chip->indio_dev->attrs = &adt7316_attribute_group;
+		chip->indio_dev->event_attrs = &adt7316_event_attribute_group;
+	}
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (chip->bus.irq > 0) {
+		if (adt7316_platform_data[0])
+			chip->bus.irq_flags = adt7316_platform_data[0];
+
+		ret = iio_register_interrupt_line(chip->bus.irq,
+				chip->indio_dev,
+				0,
+				chip->bus.irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7316.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_adt7316,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, adt7316_interrupt_bh);
+
+		if (chip->bus.irq_flags & IRQF_TRIGGER_HIGH)
+			chip->config1 |= ADT7316_INT_POLARITY;
+	}
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, chip->config1);
+	if (ret) {
+		ret = -EIO;
+		goto error_unreg_irq;
+	}
+
+	ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, chip->config3);
+	if (ret) {
+		ret = -EIO;
+		goto error_unreg_irq;
+	}
+
+	dev_info(dev, "%s temperature sensor, ADC and DAC registered.\n",
+			chip->name);
+
+	return 0;
+
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+EXPORT_SYMBOL(adt7316_probe);
+
+int __devexit adt7316_remove(struct device *dev)
+{
+
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7316_chip_info *chip = dev_info->dev_data;
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	dev_set_drvdata(dev, NULL);
+	if (chip->bus.irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+EXPORT_SYMBOL(adt7316_remove);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT7316/7/8 and ADT7516/7/9 digital"
+			" temperature sensor, ADC and DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/addac/adt7316.h b/drivers/staging/iio/addac/adt7316.h
new file mode 100644
index 0000000..d34bd67
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316.h
@@ -0,0 +1,33 @@
+/*
+ * ADT7316 digital temperature sensor driver supporting ADT7316/7/8 ADT7516/7/9
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADT7316_H_
+#define _ADT7316_H_
+
+#include <linux/types.h>
+
+#define ADT7316_REG_MAX_ADDR		0x3F
+
+struct adt7316_bus {
+	void *client;
+	int irq;
+	int irq_flags;
+	int (*read) (void *client, u8 reg, u8 *data);
+	int (*write) (void *client, u8 reg, u8 val);
+	int (*multi_read) (void *client, u8 first_reg, u8 count, u8 *data);
+	int (*multi_write) (void *client, u8 first_reg, u8 count, u8 *data);
+};
+
+#ifdef CONFIG_PM
+int adt7316_disable(struct device *dev);
+int adt7316_enable(struct device *dev);
+#endif
+int adt7316_probe(struct device *dev, struct adt7316_bus *bus, const char *name);
+int adt7316_remove(struct device *dev);
+
+#endif
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 08/36] staging: iio: adc: new driver for AD7150/1/6 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (6 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 07/36] staging: iio: new ADT7316/7/8 and ADT7516/7/9 driver Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 09/36] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (28 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    2 +
 drivers/staging/iio/adc/ad7150.c |  877 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 886 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7150.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index acb6767..51b9cc6 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -27,6 +27,13 @@ config MAX1363_RING_BUFFER
 	  Say yes here to include ring buffer support in the MAX1363
 	  ADC driver.
 
+config AD7150
+	tristate "Analog Devices ad7150/1/6 capacitive sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices capacitive sensors.
+	  (ad7150, ad7151, ad7156) Provides direct access via sysfs.
+
 config AD799X
 	tristate "Analog Devices AD799x ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index b62c319b..51b39aa 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -14,3 +14,5 @@ obj-$(CONFIG_AD799X) += ad799x.o
 ad7476-y := ad7476_core.o
 ad7476-$(CONFIG_IIO_RING_BUFFER) += ad7476_ring.o
 obj-$(CONFIG_AD7476) += ad7476.o
+
+obj-$(CONFIG_AD7150) += ad7150.o
diff --git a/drivers/staging/iio/adc/ad7150.c b/drivers/staging/iio/adc/ad7150.c
new file mode 100644
index 0000000..8555766
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7150.c
@@ -0,0 +1,877 @@
+/*
+ * AD7150 capacitive sensor driver supporting AD7150/1/6
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7150 registers definition
+ */
+
+#define AD7150_STATUS              0
+#define AD7150_STATUS_OUT1         (1 << 3)
+#define AD7150_STATUS_OUT2         (1 << 5)
+#define AD7150_CH1_DATA_HIGH       1
+#define AD7150_CH1_DATA_LOW        2
+#define AD7150_CH2_DATA_HIGH       3
+#define AD7150_CH2_DATA_LOW        4
+#define AD7150_CH1_AVG_HIGH        5
+#define AD7150_CH1_AVG_LOW         6
+#define AD7150_CH2_AVG_HIGH        7
+#define AD7150_CH2_AVG_LOW         8
+#define AD7150_CH1_SENSITIVITY     9
+#define AD7150_CH1_THR_HOLD_H      9
+#define AD7150_CH1_TIMEOUT         10
+#define AD7150_CH1_THR_HOLD_L      10
+#define AD7150_CH1_SETUP           11
+#define AD7150_CH2_SENSITIVITY     12
+#define AD7150_CH2_THR_HOLD_H      12
+#define AD7150_CH2_TIMEOUT         13
+#define AD7150_CH2_THR_HOLD_L      13
+#define AD7150_CH2_SETUP           14
+#define AD7150_CFG                 15
+#define AD7150_CFG_FIX             (1 << 7)
+#define AD7150_PD_TIMER            16
+#define AD7150_CH1_CAPDAC          17
+#define AD7150_CH2_CAPDAC          18
+#define AD7150_SN3                 19
+#define AD7150_SN2                 20
+#define AD7150_SN1                 21
+#define AD7150_SN0                 22
+#define AD7150_ID                  23
+
+#define AD7150_MAX_CONV_MODE       4
+
+/*
+ * struct ad7150_chip_info - chip specifc information
+ */
+
+struct ad7150_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	bool inter;
+	s64 last_timestamp;
+	u16 ch1_threshold;     /* Ch1 Threshold (in fixed threshold mode) */
+	u8  ch1_sensitivity;   /* Ch1 Sensitivity (in adaptive threshold mode) */
+	u8  ch1_timeout;       /* Ch1 Timeout (in adaptive threshold mode) */
+	u8  ch1_setup;
+	u16 ch2_threshold;     /* Ch2 Threshold (in fixed threshold mode) */
+	u8  ch2_sensitivity;   /* Ch1 Sensitivity (in adaptive threshold mode) */
+	u8  ch2_timeout;       /* Ch1 Timeout (in adaptive threshold mode) */
+	u8  ch2_setup;
+	u8  powerdown_timer;
+	char threshold_mode[10]; /* adaptive/fixed threshold mode */
+	int old_state;
+	char *conversion_mode;
+};
+
+struct ad7150_conversion_mode {
+	char *name;
+	u8 reg_cfg;
+};
+
+struct ad7150_conversion_mode ad7150_conv_mode_table[AD7150_MAX_CONV_MODE] = {
+	{ "idle", 0 },
+	{ "continuous-conversion", 1 },
+	{ "single-conversion", 2 },
+	{ "power-down", 3 },
+};
+
+/*
+ * ad7150 register access by I2C
+ */
+
+static int ad7150_i2c_read(struct ad7150_chip_info *chip, u8 reg, u8 *data, int len)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C write error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ad7150_i2c_write(struct ad7150_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	u8 tx[2] = {
+		reg,
+		data,
+	};
+
+	ret = i2c_master_send(client, tx, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/*
+ * sysfs nodes
+ */
+
+#define IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_conversion_modes, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CONVERSION_MODE(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(conversion_mode, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_AVAIL_THRESHOLD_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_threshold_modes, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_THRESHOLD_MODE(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(threshold_mode, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_THRESHOLD(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(ch1_threshold, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_THRESHOLD(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(ch2_threshold, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_SENSITIVITY(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_sensitivity, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_SENSITIVITY(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_sensitivity, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_TIMEOUT(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_timeout, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_TIMEOUT(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_timeout, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch1_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CH2_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch2_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CH1_SETUP(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(ch2_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_POWERDOWN_TIMER(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(powerdown_timer, _mode, _show, _store, 0)
+
+static ssize_t ad7150_show_conversion_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int i;
+	int len = 0;
+
+	for (i = 0; i < AD7150_MAX_CONV_MODE; i++)
+		len += sprintf(buf + len, "%s\n", ad7150_conv_mode_table[i].name);
+
+	return len;
+}
+
+static IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(ad7150_show_conversion_modes);
+
+static ssize_t ad7150_show_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->conversion_mode);
+}
+
+static ssize_t ad7150_store_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	u8 cfg;
+	int i;
+
+	ad7150_i2c_read(chip, AD7150_CFG, &cfg, 1);
+
+	for (i = 0; i < AD7150_MAX_CONV_MODE; i++) {
+		if (strncmp(buf, ad7150_conv_mode_table[i].name,
+				strlen(ad7150_conv_mode_table[i].name) - 1) == 0) {
+			chip->conversion_mode = ad7150_conv_mode_table[i].name;
+			cfg |= 0x18 | ad7150_conv_mode_table[i].reg_cfg;
+			ad7150_i2c_write(chip, AD7150_CFG, cfg);
+			return len;
+		}
+	}
+
+	dev_err(dev, "not supported conversion mode\n");
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CONVERSION_MODE(S_IRUGO | S_IWUSR,
+		ad7150_show_conversion_mode,
+		ad7150_store_conversion_mode);
+
+static ssize_t ad7150_show_threshold_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "adaptive\nfixed\n");
+}
+
+static IIO_DEV_ATTR_AVAIL_THRESHOLD_MODES(ad7150_show_threshold_modes);
+
+static ssize_t ad7150_show_ch1_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	u8 data[2];
+
+	ad7150_i2c_read(chip, AD7150_CH1_DATA_HIGH, data, 2);
+	return sprintf(buf, "%d\n", ((int) data[0] << 8) | data[1]);
+}
+
+static IIO_DEV_ATTR_CH1_VALUE(ad7150_show_ch1_value);
+
+static ssize_t ad7150_show_ch2_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	u8 data[2];
+
+	ad7150_i2c_read(chip, AD7150_CH2_DATA_HIGH, data, 2);
+	return sprintf(buf, "%d\n", ((int) data[0] << 8) | data[1]);
+}
+
+static IIO_DEV_ATTR_CH2_VALUE(ad7150_show_ch2_value);
+
+static ssize_t ad7150_show_threshold_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->threshold_mode);
+}
+
+static ssize_t ad7150_store_threshold_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	u8 cfg;
+
+	ad7150_i2c_read(chip, AD7150_CFG, &cfg, 1);
+
+	if (strncmp(buf, "fixed", 5) == 0) {
+		strcpy(chip->threshold_mode, "fixed");
+		cfg |= AD7150_CFG_FIX;
+		ad7150_i2c_write(chip, AD7150_CFG, cfg);
+
+		return len;
+	} else if (strncmp(buf, "adaptive", 8) == 0) {
+		strcpy(chip->threshold_mode, "adaptive");
+		cfg &= ~AD7150_CFG_FIX;
+		ad7150_i2c_write(chip, AD7150_CFG, cfg);
+
+		return len;
+	}
+
+	dev_err(dev, "not supported threshold mode\n");
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_THRESHOLD_MODE(S_IRUGO | S_IWUSR,
+		ad7150_show_threshold_mode,
+		ad7150_store_threshold_mode);
+
+static ssize_t ad7150_show_ch1_threshold(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_threshold);
+}
+
+static ssize_t ad7150_store_ch1_threshold(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7150_i2c_write(chip, AD7150_CH1_THR_HOLD_H, data >> 8);
+		ad7150_i2c_write(chip, AD7150_CH1_THR_HOLD_L, data);
+		chip->ch1_threshold = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_THRESHOLD(S_IRUGO | S_IWUSR,
+		ad7150_show_ch1_threshold,
+		ad7150_store_ch1_threshold);
+
+static ssize_t ad7150_show_ch2_threshold(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_threshold);
+}
+
+static ssize_t ad7150_store_ch2_threshold(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7150_i2c_write(chip, AD7150_CH2_THR_HOLD_H, data >> 8);
+		ad7150_i2c_write(chip, AD7150_CH2_THR_HOLD_L, data);
+		chip->ch2_threshold = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_THRESHOLD(S_IRUGO | S_IWUSR,
+		ad7150_show_ch2_threshold,
+		ad7150_store_ch2_threshold);
+
+static ssize_t ad7150_show_ch1_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_sensitivity);
+}
+
+static ssize_t ad7150_store_ch1_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7150_i2c_write(chip, AD7150_CH1_SENSITIVITY, data);
+		chip->ch1_sensitivity = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_SENSITIVITY(S_IRUGO | S_IWUSR,
+		ad7150_show_ch1_sensitivity,
+		ad7150_store_ch1_sensitivity);
+
+static ssize_t ad7150_show_ch2_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_sensitivity);
+}
+
+static ssize_t ad7150_store_ch2_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7150_i2c_write(chip, AD7150_CH2_SENSITIVITY, data);
+		chip->ch2_sensitivity = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_SENSITIVITY(S_IRUGO | S_IWUSR,
+		ad7150_show_ch2_sensitivity,
+		ad7150_store_ch2_sensitivity);
+
+static ssize_t ad7150_show_ch1_timeout(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_timeout);
+}
+
+static ssize_t ad7150_store_ch1_timeout(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7150_i2c_write(chip, AD7150_CH1_TIMEOUT, data);
+		chip->ch1_timeout = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_TIMEOUT(S_IRUGO | S_IWUSR,
+		ad7150_show_ch1_timeout,
+		ad7150_store_ch1_timeout);
+
+static ssize_t ad7150_show_ch2_timeout(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_timeout);
+}
+
+static ssize_t ad7150_store_ch2_timeout(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7150_i2c_write(chip, AD7150_CH2_TIMEOUT, data);
+		chip->ch2_timeout = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_TIMEOUT(S_IRUGO | S_IWUSR,
+		ad7150_show_ch2_timeout,
+		ad7150_store_ch2_timeout);
+
+static ssize_t ad7150_show_ch1_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->ch1_setup);
+}
+
+static ssize_t ad7150_store_ch1_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7150_i2c_write(chip, AD7150_CH1_SETUP, data);
+		chip->ch1_setup = data;
+		return len;
+	}
+
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_SETUP(S_IRUGO | S_IWUSR,
+		ad7150_show_ch1_setup,
+		ad7150_store_ch1_setup);
+
+static ssize_t ad7150_show_ch2_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->ch2_setup);
+}
+
+static ssize_t ad7150_store_ch2_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7150_i2c_write(chip, AD7150_CH2_SETUP, data);
+		chip->ch2_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_SETUP(S_IRUGO | S_IWUSR,
+		ad7150_show_ch2_setup,
+		ad7150_store_ch2_setup);
+
+static ssize_t ad7150_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7150_show_name, NULL, 0);
+
+static ssize_t ad7150_show_powerdown_timer(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->powerdown_timer);
+}
+
+static ssize_t ad7150_store_powerdown_timer(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x40)) {
+		chip->powerdown_timer = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_POWERDOWN_TIMER(S_IRUGO | S_IWUSR,
+		ad7150_show_powerdown_timer,
+		ad7150_store_powerdown_timer);
+
+static struct attribute *ad7150_attributes[] = {
+	&iio_dev_attr_available_threshold_modes.dev_attr.attr,
+	&iio_dev_attr_threshold_mode.dev_attr.attr,
+	&iio_dev_attr_ch1_threshold.dev_attr.attr,
+	&iio_dev_attr_ch2_threshold.dev_attr.attr,
+	&iio_dev_attr_ch1_timeout.dev_attr.attr,
+	&iio_dev_attr_ch2_timeout.dev_attr.attr,
+	&iio_dev_attr_ch1_setup.dev_attr.attr,
+	&iio_dev_attr_ch2_setup.dev_attr.attr,
+	&iio_dev_attr_ch1_sensitivity.dev_attr.attr,
+	&iio_dev_attr_ch2_sensitivity.dev_attr.attr,
+	&iio_dev_attr_powerdown_timer.dev_attr.attr,
+	&iio_dev_attr_ch1_value.dev_attr.attr,
+	&iio_dev_attr_ch2_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7150_attribute_group = {
+	.attrs = ad7150_attributes,
+};
+
+/*
+ * threshold events
+ */
+
+#define IIO_EVENT_CODE_CH1_HIGH    IIO_BUFFER_EVENT_CODE(0)
+#define IIO_EVENT_CODE_CH1_LOW     IIO_BUFFER_EVENT_CODE(1)
+#define IIO_EVENT_CODE_CH2_HIGH    IIO_BUFFER_EVENT_CODE(2)
+#define IIO_EVENT_CODE_CH2_LOW     IIO_BUFFER_EVENT_CODE(3)
+
+#define IIO_EVENT_ATTR_CH1_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch1_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_CH2_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch2_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_CH1_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch1_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_CH2_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch2_low, _evlist, _show, _store, _mask)
+
+static void ad7150_interrupt_handler_bh(struct work_struct *work_s)
+{
+	struct ad7150_chip_info *chip =
+		container_of(work_s, struct ad7150_chip_info, thresh_work);
+	u8 int_status;
+
+	enable_irq(chip->client->irq);
+
+	ad7150_i2c_read(chip, AD7150_STATUS, &int_status, 1);
+
+	if ((int_status & AD7150_STATUS_OUT1) && !(chip->old_state & AD7150_STATUS_OUT1))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH1_HIGH,
+				chip->last_timestamp);
+	else if ((!(int_status & AD7150_STATUS_OUT1)) && (chip->old_state & AD7150_STATUS_OUT1))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH1_LOW,
+				chip->last_timestamp);
+
+	if ((int_status & AD7150_STATUS_OUT2) && !(chip->old_state & AD7150_STATUS_OUT2))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH2_HIGH,
+				chip->last_timestamp);
+	else if ((!(int_status & AD7150_STATUS_OUT2)) && (chip->old_state & AD7150_STATUS_OUT2))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH2_LOW,
+				chip->last_timestamp);
+}
+
+static int ad7150_interrupt_handler_th(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad7150_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(threshold, &ad7150_interrupt_handler_th);
+
+static ssize_t ad7150_query_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	/*
+	 * AD7150 provides two logic output channels, which can be used as interrupt
+	 * but the pins are not configurable
+	 */
+	return sprintf(buf, "1\n");
+}
+
+static ssize_t ad7150_set_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return len;
+}
+
+IIO_EVENT_ATTR_CH1_HIGH_SH(iio_event_threshold, ad7150_query_out_mode, ad7150_set_out_mode, 0);
+IIO_EVENT_ATTR_CH2_HIGH_SH(iio_event_threshold, ad7150_query_out_mode, ad7150_set_out_mode, 0);
+IIO_EVENT_ATTR_CH1_LOW_SH(iio_event_threshold, ad7150_query_out_mode, ad7150_set_out_mode, 0);
+IIO_EVENT_ATTR_CH2_LOW_SH(iio_event_threshold, ad7150_query_out_mode, ad7150_set_out_mode, 0);
+
+static struct attribute *ad7150_event_attributes[] = {
+	&iio_event_attr_ch1_high.dev_attr.attr,
+	&iio_event_attr_ch2_high.dev_attr.attr,
+	&iio_event_attr_ch1_low.dev_attr.attr,
+	&iio_event_attr_ch2_low.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7150_event_attribute_group = {
+	.attrs = ad7150_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7150_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret = 0, regdone = 0;
+	struct ad7150_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	/* Echipabilish that the iio_dev is a child of the i2c device */
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad7150_attribute_group;
+	chip->indio_dev->event_attrs = &ad7150_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)(chip);
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+	regdone = 1;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"ad7150");
+		if (ret)
+			goto error_free_dev;
+
+		iio_add_event_to_list(iio_event_attr_ch2_low.listel,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad7150_interrupt_handler_bh);
+	}
+
+	dev_err(&client->dev, "%s capacitive sensor registered, irq: %d\n", id->name, client->irq);
+
+	return 0;
+
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(chip->indio_dev);
+	else
+		iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad7150_remove(struct i2c_client *client)
+{
+	struct ad7150_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7150_id[] = {
+	{ "ad7150", 0 },
+	{ "ad7151", 0 },
+	{ "ad7156", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7150_id);
+
+static struct i2c_driver ad7150_driver = {
+	.driver = {
+		.name = "ad7150",
+	},
+	.probe = ad7150_probe,
+	.remove = __devexit_p(ad7150_remove),
+	.id_table = ad7150_id,
+};
+
+static __init int ad7150_init(void)
+{
+	return i2c_add_driver(&ad7150_driver);
+}
+
+static __exit void ad7150_exit(void)
+{
+	i2c_del_driver(&ad7150_driver);
+}
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ad7150/1/6 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7150_init);
+module_exit(ad7150_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 09/36] staging: iio: adc: new driver for AD7152/3 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (7 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 08/36] staging: iio: adc: new driver for AD7150/1/6 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 10/36] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
                   ` (27 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7152.c |  610 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 618 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7152.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 51b9cc6..7f03571 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -34,6 +34,13 @@ config AD7150
 	  Say yes here to build support for Analog Devices capacitive sensors.
 	  (ad7150, ad7151, ad7156) Provides direct access via sysfs.
 
+config AD7152
+	tristate "Analog Devices ad7152/3 capacitive sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices capacitive sensors.
+	  (ad7152, ad7153) Provides direct access via sysfs.
+
 config AD799X
 	tristate "Analog Devices AD799x ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 51b39aa..a134754 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -16,3 +16,4 @@ ad7476-$(CONFIG_IIO_RING_BUFFER) += ad7476_ring.o
 obj-$(CONFIG_AD7476) += ad7476.o
 
 obj-$(CONFIG_AD7150) += ad7150.o
+obj-$(CONFIG_AD7152) += ad7152.o
diff --git a/drivers/staging/iio/adc/ad7152.c b/drivers/staging/iio/adc/ad7152.c
new file mode 100644
index 0000000..fa7f8406
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7152.c
@@ -0,0 +1,610 @@
+/*
+ * AD7152 capacitive sensor driver supporting AD7152/3
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7152 registers definition
+ */
+
+#define AD7152_STATUS              0
+#define AD7152_STATUS_RDY1         (1 << 0)
+#define AD7152_STATUS_RDY2         (1 << 1)
+#define AD7152_CH1_DATA_HIGH       1
+#define AD7152_CH1_DATA_LOW        2
+#define AD7152_CH2_DATA_HIGH       3
+#define AD7152_CH2_DATA_LOW        4
+#define AD7152_CH1_OFFS_HIGH       5
+#define AD7152_CH1_OFFS_LOW        6
+#define AD7152_CH2_OFFS_HIGH       7
+#define AD7152_CH2_OFFS_LOW        8
+#define AD7152_CH1_GAIN_HIGH       9
+#define AD7152_CH1_GAIN_LOW        10
+#define AD7152_CH1_SETUP           11
+#define AD7152_CH2_GAIN_HIGH       12
+#define AD7152_CH2_GAIN_LOW        13
+#define AD7152_CH2_SETUP           14
+#define AD7152_CFG                 15
+#define AD7152_RESEVERD            16
+#define AD7152_CAPDAC_POS          17
+#define AD7152_CAPDAC_NEG          18
+#define AD7152_CFG2                26
+
+#define AD7152_MAX_CONV_MODE       6
+
+/*
+ * struct ad7152_chip_info - chip specifc information
+ */
+
+struct ad7152_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	u16 ch1_offset;     /* Channel 1 offset calibration coefficient */
+	u16 ch1_gain;       /* Channel 1 gain coefficient */
+	u8  ch1_setup;
+	u16 ch2_offset;     /* Channel 2 offset calibration coefficient */
+	u16 ch2_gain;       /* Channel 1 gain coefficient */
+	u8  ch2_setup;
+	u8  filter_rate_setup; /* Capacitive channel digital filter setup; conversion time/update rate setup per channel */
+	char *conversion_mode;
+};
+
+struct ad7152_conversion_mode {
+	char *name;
+	u8 reg_cfg;
+};
+
+struct ad7152_conversion_mode ad7152_conv_mode_table[AD7152_MAX_CONV_MODE] = {
+	{ "idle", 0 },
+	{ "continuous-conversion", 1 },
+	{ "single-conversion", 2 },
+	{ "power-down", 3 },
+	{ "offset-calibration", 5 },
+	{ "gain-calibration", 6 },
+};
+
+/*
+ * ad7152 register access by I2C
+ */
+
+static int ad7152_i2c_read(struct ad7152_chip_info *chip, u8 reg, u8 *data, int len)
+{
+	struct i2c_client *client = chip->client;
+	int ret;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C write error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+	}
+
+	return ret;
+}
+
+static int ad7152_i2c_write(struct ad7152_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret;
+
+	u8 tx[2] = {
+		reg,
+		data,
+	};
+
+	ret = i2c_master_send(client, tx, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/*
+ * sysfs nodes
+ */
+
+#define IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_conversion_modes, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CONVERSION_MODE(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(conversion_mode, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_OFFSET(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_offset, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_OFFSET(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_offset, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch1_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CH2_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch2_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CH1_SETUP(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(ch2_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_FILTER_RATE_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(filter_rate_setup, _mode, _show, _store, 0)
+
+static ssize_t ad7152_show_conversion_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int i;
+	int len = 0;
+
+	for (i = 0; i < AD7152_MAX_CONV_MODE; i++)
+		len += sprintf(buf + len, "%s ", ad7152_conv_mode_table[i].name);
+
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+static IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(ad7152_show_conversion_modes);
+
+static ssize_t ad7152_show_ch1_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	u8 data[2];
+
+	ad7152_i2c_read(chip, AD7152_CH1_DATA_HIGH, data, 2);
+	return sprintf(buf, "%d\n", ((int)data[0] << 8) | data[1]);
+}
+
+static IIO_DEV_ATTR_CH1_VALUE(ad7152_show_ch1_value);
+
+static ssize_t ad7152_show_ch2_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	u8 data[2];
+
+	ad7152_i2c_read(chip, AD7152_CH2_DATA_HIGH, data, 2);
+	return sprintf(buf, "%d\n", ((int)data[0] << 8) | data[1]);
+}
+
+static IIO_DEV_ATTR_CH2_VALUE(ad7152_show_ch2_value);
+
+static ssize_t ad7152_show_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->conversion_mode);
+}
+
+static ssize_t ad7152_store_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	u8 cfg;
+	int i;
+
+	ad7152_i2c_read(chip, AD7152_CFG, &cfg, 1);
+
+	for (i = 0; i < AD7152_MAX_CONV_MODE; i++)
+		if (strncmp(buf, ad7152_conv_mode_table[i].name,
+				strlen(ad7152_conv_mode_table[i].name) - 1) == 0) {
+			chip->conversion_mode = ad7152_conv_mode_table[i].name;
+			cfg |= 0x18 | ad7152_conv_mode_table[i].reg_cfg;
+			ad7152_i2c_write(chip, AD7152_CFG, cfg);
+			return len;
+		}
+
+	dev_err(dev, "not supported conversion mode\n");
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CONVERSION_MODE(S_IRUGO | S_IWUSR,
+		ad7152_show_conversion_mode,
+		ad7152_store_conversion_mode);
+
+static ssize_t ad7152_show_ch1_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_offset);
+}
+
+static ssize_t ad7152_store_ch1_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH1_OFFS_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH1_OFFS_LOW, data);
+		chip->ch1_offset = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_OFFSET(S_IRUGO | S_IWUSR,
+		ad7152_show_ch1_offset,
+		ad7152_store_ch1_offset);
+
+static ssize_t ad7152_show_ch2_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_offset);
+}
+
+static ssize_t ad7152_store_ch2_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH2_OFFS_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH2_OFFS_LOW, data);
+		chip->ch2_offset = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_OFFSET(S_IRUGO | S_IWUSR,
+		ad7152_show_ch2_offset,
+		ad7152_store_ch2_offset);
+
+static ssize_t ad7152_show_ch1_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_gain);
+}
+
+static ssize_t ad7152_store_ch1_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH1_GAIN_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH1_GAIN_LOW, data);
+		chip->ch1_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_GAIN(S_IRUGO | S_IWUSR,
+		ad7152_show_ch1_gain,
+		ad7152_store_ch1_gain);
+
+static ssize_t ad7152_show_ch2_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_gain);
+}
+
+static ssize_t ad7152_store_ch2_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH2_GAIN_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH2_GAIN_LOW, data);
+		chip->ch2_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_GAIN(S_IRUGO | S_IWUSR,
+		ad7152_show_ch2_gain,
+		ad7152_store_ch2_gain);
+
+static ssize_t ad7152_show_ch1_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->ch1_setup);
+}
+
+static ssize_t ad7152_store_ch1_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7152_i2c_write(chip, AD7152_CH1_SETUP, data);
+		chip->ch1_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_SETUP(S_IRUGO | S_IWUSR,
+		ad7152_show_ch1_setup,
+		ad7152_store_ch1_setup);
+
+static ssize_t ad7152_show_ch2_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->ch2_setup);
+}
+
+static ssize_t ad7152_store_ch2_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7152_i2c_write(chip, AD7152_CH2_SETUP, data);
+		chip->ch2_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_SETUP(S_IRUGO | S_IWUSR,
+		ad7152_show_ch2_setup,
+		ad7152_store_ch2_setup);
+
+static ssize_t ad7152_show_filter_rate_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->filter_rate_setup);
+}
+
+static ssize_t ad7152_store_filter_rate_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7152_i2c_write(chip, AD7152_CFG2, data);
+		chip->filter_rate_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_FILTER_RATE_SETUP(S_IRUGO | S_IWUSR,
+		ad7152_show_filter_rate_setup,
+		ad7152_store_filter_rate_setup);
+
+static ssize_t ad7152_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7152_show_name, NULL, 0);
+
+static struct attribute *ad7152_attributes[] = {
+	&iio_dev_attr_available_conversion_modes.dev_attr.attr,
+	&iio_dev_attr_conversion_mode.dev_attr.attr,
+	&iio_dev_attr_ch1_gain.dev_attr.attr,
+	&iio_dev_attr_ch2_gain.dev_attr.attr,
+	&iio_dev_attr_ch1_offset.dev_attr.attr,
+	&iio_dev_attr_ch2_offset.dev_attr.attr,
+	&iio_dev_attr_ch1_value.dev_attr.attr,
+	&iio_dev_attr_ch2_value.dev_attr.attr,
+	&iio_dev_attr_ch1_setup.dev_attr.attr,
+	&iio_dev_attr_ch2_setup.dev_attr.attr,
+	&iio_dev_attr_filter_rate_setup.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7152_attribute_group = {
+	.attrs = ad7152_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7152_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct ad7152_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	/* Echipabilish that the iio_dev is a child of the i2c device */
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad7152_attribute_group;
+	chip->indio_dev->dev_data = (void *)(chip);
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	dev_err(&client->dev, "%s capacitive sensor registered\n", id->name);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad7152_remove(struct i2c_client *client)
+{
+	struct ad7152_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7152_id[] = {
+	{ "ad7152", 0 },
+	{ "ad7153", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7152_id);
+
+static struct i2c_driver ad7152_driver = {
+	.driver = {
+		.name = "ad7152",
+	},
+	.probe = ad7152_probe,
+	.remove = __devexit_p(ad7152_remove),
+	.id_table = ad7152_id,
+};
+
+static __init int ad7152_init(void)
+{
+	return i2c_add_driver(&ad7152_driver);
+}
+
+static __exit void ad7152_exit(void)
+{
+	i2c_del_driver(&ad7152_driver);
+}
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ad7152/3 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7152_init);
+module_exit(ad7152_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 10/36] staging: iio: adc: new driver for AD7291 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (8 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 09/36] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 11/36] staging: iio: adc: new driver for AD7298 devices Mike Frysinger
                   ` (26 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7291.c | 1039 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1047 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7291.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 7f03571..fa78413 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -41,6 +41,13 @@ config AD7152
 	  Say yes here to build support for Analog Devices capacitive sensors.
 	  (ad7152, ad7153) Provides direct access via sysfs.
 
+config AD7291
+	tristate "Analog Devices AD7291 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices AD7291
+	  temperature sensors.
+
 config AD799X
 	tristate "Analog Devices AD799x ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index a134754..5e99a86 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_AD7476) += ad7476.o
 
 obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
+obj-$(CONFIG_AD7291) += ad7291.o
diff --git a/drivers/staging/iio/adc/ad7291.c b/drivers/staging/iio/adc/ad7291.c
new file mode 100644
index 0000000..34041a7
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7291.c
@@ -0,0 +1,1039 @@
+/*
+ * AD7291 digital temperature sensor driver supporting AD7291
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7291 registers definition
+ */
+#define AD7291_COMMAND			0
+#define AD7291_VOLTAGE			1
+#define AD7291_T_SENSE			2
+#define AD7291_T_AVERAGE		3
+#define AD7291_VOLTAGE_LIMIT_BASE	4
+#define AD7291_VOLTAGE_LIMIT_COUNT	8
+#define AD7291_T_SENSE_HIGH		0x1c
+#define AD7291_T_SENSE_LOW		0x1d
+#define AD7291_T_SENSE_HYST		0x1e
+#define AD7291_VOLTAGE_ALERT_STATUS	0x1f
+#define AD7291_T_ALERT_STATUS		0x20
+
+/*
+ * AD7291 command
+ */
+#define AD7291_AUTOCYCLE		0x1
+#define AD7291_RESET			0x2
+#define AD7291_ALART_CLEAR		0x4
+#define AD7291_ALART_POLARITY		0x8
+#define AD7291_EXT_REF			0x10
+#define AD7291_NOISE_DELAY		0x20
+#define AD7291_T_SENSE_MASK		0x40
+#define AD7291_VOLTAGE_MASK		0xff00
+#define AD7291_VOLTAGE_OFFSET		0x8
+
+/*
+ * AD7291 value masks
+ */
+#define AD7291_CHANNEL_MASK		0xf000
+#define AD7291_VALUE_MASK		0xfff
+#define AD7291_T_VALUE_SIGN		0x400
+#define AD7291_T_VALUE_FLOAT_OFFSET	2
+#define AD7291_T_VALUE_FLOAT_MASK	0x2
+
+/*
+ * struct ad7291_chip_info - chip specifc information
+ */
+
+struct ad7291_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u16 command;
+	u8  channels;	/* Active voltage channels */
+};
+
+/*
+ * struct ad7291_chip_info - chip specifc information
+ */
+
+struct ad7291_limit_regs {
+	u16	data_high;
+	u16	data_low;
+	u16	hysteresis;
+};
+
+/*
+ * ad7291 register access by I2C
+ */
+static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_read_word_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	*data = swab16((u16)ret);
+
+	return 0;
+}
+
+static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/* Returns negative errno, or else the number of words read. */
+static int ad7291_i2c_read_data(struct ad7291_chip_info *chip, u8 reg, u16 *data)
+{
+	struct i2c_client *client = chip->client;
+	u8 commands[4];
+	int ret = 0;
+	int i, count;
+
+	if (reg == AD7291_T_SENSE || reg == AD7291_T_AVERAGE)
+		count = 2;
+	else if (reg == AD7291_VOLTAGE) {
+		if (!chip->channels) {
+			dev_err(&client->dev, "No voltage channel is selected.\n");
+			return -EINVAL;
+		}
+		count = 2 + chip->channels * 2;
+	} else {
+		dev_err(&client->dev, "I2C wrong data register\n");
+		return -EINVAL;
+	}
+
+	commands[0] = 0;
+	commands[1] = (chip->command >> 8) & 0xff;
+	commands[2] = chip->command & 0xff;
+	commands[3] = reg;
+
+	ret = i2c_master_send(client, commands, 4);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C master send error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, (u8 *)data, count);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C master receive error\n");
+		return ret;
+	}
+	ret >>= 2;
+
+	for (i = 0; i < ret; i++)
+		data[i] = swab16(data[i]);
+
+	return ret;
+}
+
+static ssize_t ad7291_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	if (chip->command & AD7291_AUTOCYCLE)
+		return sprintf(buf, "autocycle\n");
+	else
+		return sprintf(buf, "command\n");
+}
+
+static ssize_t ad7291_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7291_AUTOCYCLE);
+	if (strcmp(buf, "autocycle"))
+		command |= AD7291_AUTOCYCLE;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7291_show_mode,
+		ad7291_store_mode,
+		0);
+
+static ssize_t ad7291_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "command\nautocycle\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7291_show_available_modes, NULL, 0);
+
+static ssize_t ad7291_store_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command | AD7291_RESET;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR,
+		NULL,
+		ad7291_store_reset,
+		0);
+
+static ssize_t ad7291_show_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->command & AD7291_EXT_REF));
+}
+
+static ssize_t ad7291_store_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7291_EXT_REF);
+	if (strcmp(buf, "1"))
+		command |= AD7291_EXT_REF;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(ext_ref, S_IRUGO | S_IWUSR,
+		ad7291_show_ext_ref,
+		ad7291_store_ext_ref,
+		0);
+
+static ssize_t ad7291_show_noise_delay(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->command & AD7291_NOISE_DELAY));
+}
+
+static ssize_t ad7291_store_noise_delay(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7291_NOISE_DELAY);
+	if (strcmp(buf, "1"))
+		command |= AD7291_NOISE_DELAY;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(noise_delay, S_IRUGO | S_IWUSR,
+		ad7291_show_noise_delay,
+		ad7291_store_noise_delay,
+		0);
+
+static ssize_t ad7291_show_t_sense(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7291_i2c_read_data(chip, AD7291_T_SENSE, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & AD7291_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data >> AD7291_T_VALUE_FLOAT_OFFSET),
+		(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_sense, S_IRUGO, ad7291_show_t_sense, NULL, 0);
+
+static ssize_t ad7291_show_t_average(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7291_i2c_read_data(chip, AD7291_T_AVERAGE, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & AD7291_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data >> AD7291_T_VALUE_FLOAT_OFFSET),
+		(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_average, S_IRUGO, ad7291_show_t_average, NULL, 0);
+
+static ssize_t ad7291_show_voltage(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data[AD7291_VOLTAGE_LIMIT_COUNT];
+	int i, size, ret;
+
+	ret = ad7291_i2c_read_data(chip, AD7291_VOLTAGE, data);
+	if (ret)
+		return -EIO;
+
+	for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7291_T_SENSE_MASK << i)) {
+			ret = sprintf(buf, "channel[%d]=%d\n", i,
+					data[i] & AD7291_VALUE_MASK);
+			if (ret < 0)
+				break;
+			buf += ret;
+			size += ret;
+		}
+	}
+
+	return size;
+}
+
+static IIO_DEVICE_ATTR(voltage, S_IRUGO, ad7291_show_voltage, NULL, 0);
+
+static ssize_t ad7291_show_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%x\n", (chip->command & AD7291_VOLTAGE_MASK) >>
+			AD7291_VOLTAGE_OFFSET);
+}
+
+static ssize_t ad7291_store_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	unsigned long data;
+	int i, ret;
+
+	ret = strict_strtoul(buf, 16, &data);
+	if (ret || data > 0xff)
+		return -EINVAL;
+
+	command = chip->command & (~AD7291_VOLTAGE_MASK);
+	command |= data << AD7291_VOLTAGE_OFFSET;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	for (i = 0, chip->channels = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7291_T_SENSE_MASK << i))
+			chip->channels++;
+	}
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(channel_mask, S_IRUGO | S_IWUSR,
+		ad7291_show_channel_mask,
+		ad7291_store_channel_mask,
+		0);
+
+static ssize_t ad7291_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7291_show_name, NULL, 0);
+
+static struct attribute *ad7291_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_dev_attr_ext_ref.dev_attr.attr,
+	&iio_dev_attr_noise_delay.dev_attr.attr,
+	&iio_dev_attr_t_sense.dev_attr.attr,
+	&iio_dev_attr_t_average.dev_attr.attr,
+	&iio_dev_attr_voltage.dev_attr.attr,
+	&iio_dev_attr_channel_mask.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7291_attribute_group = {
+	.attrs = ad7291_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_AD7291_T_SENSE_HIGH  IIO_BUFFER_EVENT_CODE(0)
+#define IIO_EVENT_CODE_AD7291_T_SENSE_LOW   IIO_BUFFER_EVENT_CODE(1)
+#define IIO_EVENT_CODE_AD7291_T_AVG_HIGH    IIO_BUFFER_EVENT_CODE(2)
+#define IIO_EVENT_CODE_AD7291_T_AVG_LOW     IIO_BUFFER_EVENT_CODE(3)
+#define IIO_EVENT_CODE_AD7291_VOLTAGE_BASE  IIO_BUFFER_EVENT_CODE(4)
+
+static void ad7291_interrupt_bh(struct work_struct *work_s)
+{
+	struct ad7291_chip_info *chip =
+		container_of(work_s, struct ad7291_chip_info, thresh_work);
+	u16 t_status, v_status;
+	u16 command;
+	int i;
+
+	if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status))
+		return;
+
+	if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status))
+		return;
+
+	if (!(t_status || v_status))
+		return;
+
+	command = chip->command | AD7291_ALART_CLEAR;
+	ad7291_i2c_write(chip, AD7291_COMMAND, command);
+
+	command = chip->command & ~AD7291_ALART_CLEAR;
+	ad7291_i2c_write(chip, AD7291_COMMAND, command);
+
+	enable_irq(chip->client->irq);
+
+	for (i = 0; i < 4; i++) {
+		if (t_status & (1 << i))
+			iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_AD7291_T_SENSE_HIGH + i,
+				chip->last_timestamp);
+	}
+
+	for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT*2; i++) {
+		if (v_status & (1 << i))
+			iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_AD7291_VOLTAGE_BASE + i,
+				chip->last_timestamp);
+	}
+}
+
+static int ad7291_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(ad7291, &ad7291_interrupt);
+
+static inline ssize_t ad7291_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7291_i2c_read(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	data &= AD7291_VALUE_MASK;
+	if (data & AD7291_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+			data >> AD7291_T_VALUE_FLOAT_OFFSET,
+			(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static inline ssize_t ad7291_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+		if (len > AD7291_T_VALUE_FLOAT_OFFSET)
+			len = AD7291_T_VALUE_FLOAT_OFFSET;
+		pos[len] = 0;
+		ret = strict_strtol(pos, 10, &tmp2);
+
+		if (!ret)
+			tmp2 = (tmp2 / 25) * 25;
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+	data = (data << AD7291_T_VALUE_FLOAT_OFFSET) |
+		(tmp2 & AD7291_T_VALUE_FLOAT_MASK);
+	if (tmp1 < 0)
+		/* convert positive value to supplyment */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+
+	ret = ad7291_i2c_write(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t ad7291_show_t_sense_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7291_show_t_bound(dev, attr,
+			AD7291_T_SENSE_HIGH, buf);
+}
+
+static inline ssize_t ad7291_set_t_sense_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7291_set_t_bound(dev, attr,
+			AD7291_T_SENSE_HIGH, buf, len);
+}
+
+static ssize_t ad7291_show_t_sense_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7291_show_t_bound(dev, attr,
+			AD7291_T_SENSE_LOW, buf);
+}
+
+static inline ssize_t ad7291_set_t_sense_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7291_set_t_bound(dev, attr,
+			AD7291_T_SENSE_LOW, buf, len);
+}
+
+static ssize_t ad7291_show_t_sense_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7291_show_t_bound(dev, attr,
+			AD7291_T_SENSE_HYST, buf);
+}
+
+static inline ssize_t ad7291_set_t_sense_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7291_set_t_bound(dev, attr,
+			AD7291_T_SENSE_HYST, buf, len);
+}
+
+static inline ssize_t ad7291_show_v_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	int ret;
+
+	if (bound_reg < AD7291_VOLTAGE_LIMIT_BASE ||
+		bound_reg >= AD7291_VOLTAGE_LIMIT_BASE +
+		AD7291_VOLTAGE_LIMIT_COUNT)
+		return -EINVAL;
+
+	ret = ad7291_i2c_read(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	data &= AD7291_VALUE_MASK;
+
+	return sprintf(buf, "%d\n", data);
+}
+
+static inline ssize_t ad7291_set_v_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	unsigned long value;
+	u16 data;
+	int ret;
+
+	if (bound_reg < AD7291_VOLTAGE_LIMIT_BASE ||
+		bound_reg >= AD7291_VOLTAGE_LIMIT_BASE +
+		AD7291_VOLTAGE_LIMIT_COUNT)
+		return -EINVAL;
+
+	ret = strict_strtoul(buf, 10, &value);
+
+	if (ret || value >= 4096)
+		return -EINVAL;
+
+	data = (u16)value;
+	ret = ad7291_i2c_write(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static int ad7291_get_voltage_limit_regs(const char *channel)
+{
+	int index;
+
+	if (strlen(channel) < 3 && channel[0] != 'v')
+		return -EINVAL;
+
+	index = channel[1] - '0';
+	if (index >= AD7291_VOLTAGE_LIMIT_COUNT)
+		return -EINVAL;
+
+	return index;
+}
+
+static ssize_t ad7291_show_voltage_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_show_t_bound(dev, attr, regs, buf);
+}
+
+static inline ssize_t ad7291_set_voltage_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_set_t_bound(dev, attr, regs, buf, len);
+}
+
+static ssize_t ad7291_show_voltage_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_show_t_bound(dev, attr, regs+1, buf);
+}
+
+static inline ssize_t ad7291_set_voltage_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_set_t_bound(dev, attr, regs+1, buf, len);
+}
+
+static ssize_t ad7291_show_voltage_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_show_t_bound(dev, attr, regs+2, buf);
+}
+
+static inline ssize_t ad7291_set_voltage_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_set_t_bound(dev, attr, regs+2, buf, len);
+}
+
+IIO_EVENT_ATTR_SH(t_sense_high, iio_event_ad7291,
+		ad7291_show_t_sense_high, ad7291_set_t_sense_high, 0);
+IIO_EVENT_ATTR_SH(t_sense_low, iio_event_ad7291,
+		ad7291_show_t_sense_low, ad7291_set_t_sense_low, 0);
+IIO_EVENT_ATTR_SH(t_sense_hyst, iio_event_ad7291,
+		ad7291_show_t_sense_hyst, ad7291_set_t_sense_hyst, 0);
+
+IIO_EVENT_ATTR_SH(v0_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v0_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v0_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v1_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v1_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v1_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v2_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v2_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v2_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v3_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v3_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v3_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v4_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v4_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v4_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v5_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v5_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v5_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v6_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v6_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v6_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v7_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v7_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v7_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+
+static struct attribute *ad7291_event_attributes[] = {
+	&iio_event_attr_t_sense_high.dev_attr.attr,
+	&iio_event_attr_t_sense_low.dev_attr.attr,
+	&iio_event_attr_t_sense_hyst.dev_attr.attr,
+	&iio_event_attr_v0_high.dev_attr.attr,
+	&iio_event_attr_v0_low.dev_attr.attr,
+	&iio_event_attr_v0_hyst.dev_attr.attr,
+	&iio_event_attr_v1_high.dev_attr.attr,
+	&iio_event_attr_v1_low.dev_attr.attr,
+	&iio_event_attr_v1_hyst.dev_attr.attr,
+	&iio_event_attr_v2_high.dev_attr.attr,
+	&iio_event_attr_v2_low.dev_attr.attr,
+	&iio_event_attr_v2_hyst.dev_attr.attr,
+	&iio_event_attr_v3_high.dev_attr.attr,
+	&iio_event_attr_v3_low.dev_attr.attr,
+	&iio_event_attr_v3_hyst.dev_attr.attr,
+	&iio_event_attr_v4_high.dev_attr.attr,
+	&iio_event_attr_v4_low.dev_attr.attr,
+	&iio_event_attr_v4_hyst.dev_attr.attr,
+	&iio_event_attr_v5_high.dev_attr.attr,
+	&iio_event_attr_v5_low.dev_attr.attr,
+	&iio_event_attr_v5_hyst.dev_attr.attr,
+	&iio_event_attr_v6_high.dev_attr.attr,
+	&iio_event_attr_v6_low.dev_attr.attr,
+	&iio_event_attr_v6_hyst.dev_attr.attr,
+	&iio_event_attr_v7_high.dev_attr.attr,
+	&iio_event_attr_v7_low.dev_attr.attr,
+	&iio_event_attr_v7_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7291_event_attribute_group = {
+	.attrs = ad7291_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7291_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ad7291_chip_info *chip;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct ad7291_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+	chip->command = AD7291_NOISE_DELAY | AD7291_T_SENSE_MASK;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad7291_attribute_group;
+	chip->indio_dev->event_attrs = &ad7291_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (client->irq > 0) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_LOW,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_ad7291.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_ad7291,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad7291_interrupt_bh);
+
+		/* set irq polarity low level */
+		chip->command |= AD7291_ALART_POLARITY;
+	}
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command);
+	if (ret) {
+		ret = -EIO;
+		goto error_unreg_irq;
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7291_remove(struct i2c_client *client)
+{
+	struct ad7291_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7291_id[] = {
+	{ "ad7291", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7291_id);
+
+static struct i2c_driver ad7291_driver = {
+	.driver = {
+		.name = "ad7291",
+	},
+	.probe = ad7291_probe,
+	.remove = __devexit_p(ad7291_remove),
+	.id_table = ad7291_id,
+};
+
+static __init int ad7291_init(void)
+{
+	return i2c_add_driver(&ad7291_driver);
+}
+
+static __exit void ad7291_exit(void)
+{
+	i2c_del_driver(&ad7291_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7291 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7291_init);
+module_exit(ad7291_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 11/36] staging: iio: adc: new driver for AD7298 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (9 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 10/36] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 12/36] staging: iio: adc: new driver for AD7314 devices Mike Frysinger
                   ` (25 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7298.c |  501 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 509 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7298.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index fa78413..e6bc7fa 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -48,6 +48,13 @@ config AD7291
 	  Say yes here to build support for Analog Devices AD7291
 	  temperature sensors.
 
+config AD7298
+	tristate "Analog Devices AD7298 temperature sensor and ADC driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD7298
+	  temperature sensors and ADC.
+
 config AD799X
 	tristate "Analog Devices AD799x ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 5e99a86..6c03632 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_AD7476) += ad7476.o
 obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
 obj-$(CONFIG_AD7291) += ad7291.o
+obj-$(CONFIG_AD7298) += ad7298.o
diff --git a/drivers/staging/iio/adc/ad7298.c b/drivers/staging/iio/adc/ad7298.c
new file mode 100644
index 0000000..1a080c9
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7298.c
@@ -0,0 +1,501 @@
+/*
+ * AD7298 digital temperature sensor driver supporting AD7298
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7298 command
+ */
+#define AD7298_PD			0x1
+#define AD7298_T_AVG_MASK		0x2
+#define AD7298_EXT_REF			0x4
+#define AD7298_T_SENSE_MASK		0x20
+#define AD7298_VOLTAGE_MASK		0x3fc0
+#define AD7298_VOLTAGE_OFFSET		0x6
+#define AD7298_VOLTAGE_LIMIT_COUNT	8
+#define AD7298_REPEAT			0x40
+#define AD7298_WRITE			0x80
+
+/*
+ * AD7298 value masks
+ */
+#define AD7298_CHANNEL_MASK		0xf000
+#define AD7298_VALUE_MASK		0xfff
+#define AD7298_T_VALUE_SIGN		0x400
+#define AD7298_T_VALUE_FLOAT_OFFSET	2
+#define AD7298_T_VALUE_FLOAT_MASK	0x2
+
+/*
+ * struct ad7298_chip_info - chip specifc information
+ */
+
+struct ad7298_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	u16 command;
+	u16 busy_pin;
+	u8  channels;	/* Active voltage channels */
+};
+
+/*
+ * ad7298 register access by SPI
+ */
+static int ad7298_spi_write(struct ad7298_chip_info *chip, u16 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+
+	data |= AD7298_WRITE;
+	data = cpu_to_be16(data);
+	ret = spi_write(spi_dev, (u8 *)&data, sizeof(data));
+	if (ret < 0)
+		dev_err(&spi_dev->dev, "SPI write error\n");
+
+	return ret;
+}
+
+static int ad7298_spi_read(struct ad7298_chip_info *chip, u16 mask, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+	u8 count = chip->channels;
+	u16 command;
+	int i;
+
+	if (mask & AD7298_T_SENSE_MASK) {
+		command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_VOLTAGE_MASK);
+		command |= AD7298_T_SENSE_MASK;
+		count = 1;
+	} else if (mask & AD7298_T_AVG_MASK) {
+		command = chip->command & ~AD7298_VOLTAGE_MASK;
+		command |= AD7298_T_SENSE_MASK | AD7298_T_AVG_MASK;
+		count = 2;
+	} else if (mask & AD7298_VOLTAGE_MASK) {
+		command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_T_SENSE_MASK);
+		count = chip->channels;
+	}
+
+	ret = ad7298_spi_write(chip, chip->command);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi_dev, (u8 *)&command, sizeof(command));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read error\n");
+		return ret;
+	}
+
+	i = 10000;
+	while (i && gpio_get_value(chip->busy_pin)) {
+		cpu_relax();
+		i--;
+	}
+	if (!i) {
+		dev_err(&spi_dev->dev, "Always in busy convertion.\n");
+		return -EBUSY;
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = spi_read(spi_dev, (u8 *)&data[i], sizeof(data[i]));
+		if (ret < 0) {
+			dev_err(&spi_dev->dev, "SPI read error\n");
+			return ret;
+		}
+		*data = be16_to_cpu(data[i]);
+	}
+
+	return 0;
+}
+
+static ssize_t ad7298_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	if (chip->command & AD7298_REPEAT)
+		return sprintf(buf, "repeat\n");
+	else
+		return sprintf(buf, "normal\n");
+}
+
+static ssize_t ad7298_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	if (strcmp(buf, "repeat"))
+		chip->command |= AD7298_REPEAT;
+	else
+		chip->command &= (~AD7298_REPEAT);
+
+	return 1;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7298_show_mode,
+		ad7298_store_mode,
+		0);
+
+static ssize_t ad7298_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "normal\nrepeat\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7298_show_available_modes, NULL, 0);
+
+static ssize_t ad7298_store_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & ~AD7298_PD;
+
+	ret = ad7298_spi_write(chip, command);
+	if (ret)
+		return -EIO;
+
+	command = chip->command | AD7298_PD;
+
+	ret = ad7298_spi_write(chip, command);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR,
+		NULL,
+		ad7298_store_reset,
+		0);
+
+static ssize_t ad7298_show_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->command & AD7298_EXT_REF));
+}
+
+static ssize_t ad7298_store_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7298_EXT_REF);
+	if (strcmp(buf, "1"))
+		command |= AD7298_EXT_REF;
+
+	ret = ad7298_spi_write(chip, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(ext_ref, S_IRUGO | S_IWUSR,
+		ad7298_show_ext_ref,
+		ad7298_store_ext_ref,
+		0);
+
+static ssize_t ad7298_show_t_sense(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7298_spi_read(chip, AD7298_T_SENSE_MASK, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & AD7298_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7298_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data >> AD7298_T_VALUE_FLOAT_OFFSET),
+		(data & AD7298_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_sense, S_IRUGO, ad7298_show_t_sense, NULL, 0);
+
+static ssize_t ad7298_show_t_average(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 data[2];
+	char sign = ' ';
+	int ret;
+
+	ret = ad7298_spi_read(chip, AD7298_T_AVG_MASK, data);
+	if (ret)
+		return -EIO;
+
+	if (data[1] & AD7298_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data[1] = (AD7298_T_VALUE_SIGN << 1) - data[1];
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data[1] >> AD7298_T_VALUE_FLOAT_OFFSET),
+		(data[1] & AD7298_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_average, S_IRUGO, ad7298_show_t_average, NULL, 0);
+
+static ssize_t ad7298_show_voltage(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 data[AD7298_VOLTAGE_LIMIT_COUNT];
+	int i, size, ret;
+
+	ret = ad7298_spi_read(chip, AD7298_VOLTAGE_MASK, data);
+	if (ret)
+		return -EIO;
+
+	for (i = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7298_T_SENSE_MASK << i)) {
+			ret = sprintf(buf, "channel[%d]=%d\n", i,
+					data[i] & AD7298_VALUE_MASK);
+			if (ret < 0)
+				break;
+			buf += ret;
+			size += ret;
+		}
+	}
+
+	return size;
+}
+
+static IIO_DEVICE_ATTR(voltage, S_IRUGO, ad7298_show_voltage, NULL, 0);
+
+static ssize_t ad7298_show_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%x\n", (chip->command & AD7298_VOLTAGE_MASK) >>
+			AD7298_VOLTAGE_OFFSET);
+}
+
+static ssize_t ad7298_store_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int i, ret;
+
+	ret = strict_strtoul(buf, 16, &data);
+	if (ret || data > 0xff)
+		return -EINVAL;
+
+	chip->command &= (~AD7298_VOLTAGE_MASK);
+	chip->command |= data << AD7298_VOLTAGE_OFFSET;
+
+	for (i = 0, chip->channels = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7298_T_SENSE_MASK << i))
+			chip->channels++;
+	}
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(channel_mask, S_IRUGO | S_IWUSR,
+		ad7298_show_channel_mask,
+		ad7298_store_channel_mask,
+		0);
+
+static ssize_t ad7298_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7298_show_name, NULL, 0);
+
+static struct attribute *ad7298_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_dev_attr_ext_ref.dev_attr.attr,
+	&iio_dev_attr_t_sense.dev_attr.attr,
+	&iio_dev_attr_t_average.dev_attr.attr,
+	&iio_dev_attr_voltage.dev_attr.attr,
+	&iio_dev_attr_channel_mask.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7298_attribute_group = {
+	.attrs = ad7298_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+static int __devinit ad7298_probe(struct spi_device *spi_dev)
+{
+	struct ad7298_chip_info *chip;
+	unsigned short *pins = spi_dev->dev.platform_data;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct ad7298_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+	chip->busy_pin = pins[0];
+
+	ret = gpio_request(chip->busy_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n",
+			chip->busy_pin);
+		goto error_free_chip;
+	}
+	gpio_direction_input(chip->busy_pin);
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_gpio;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &ad7298_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n",
+			 chip->name);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_gpio:
+	gpio_free(chip->busy_pin);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7298_remove(struct spi_device *spi_dev)
+{
+	struct ad7298_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	gpio_free(chip->busy_pin);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id ad7298_id[] = {
+	{ "ad7298", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, ad7298_id);
+
+static struct spi_driver ad7298_driver = {
+	.driver = {
+		.name = "ad7298",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad7298_probe,
+	.remove = __devexit_p(ad7298_remove),
+	.id_table = ad7298_id,
+};
+
+static __init int ad7298_init(void)
+{
+	return spi_register_driver(&ad7298_driver);
+}
+
+static __exit void ad7298_exit(void)
+{
+	spi_unregister_driver(&ad7298_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7298 digital"
+			" temperature sensor and ADC driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7298_init);
+module_exit(ad7298_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 12/36] staging: iio: adc: new driver for AD7314 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (10 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 11/36] staging: iio: adc: new driver for AD7298 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 13/36] staging: iio: adc: new driver for AD7745/6/7 devices Mike Frysinger
                   ` (24 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7314.c |  308 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 316 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7314.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index e6bc7fa..4e7b6a8 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -55,6 +55,13 @@ config AD7298
 	  Say yes here to build support for Analog Devices AD7298
 	  temperature sensors and ADC.
 
+config AD7314
+	tristate "Analog Devices AD7314 temperature sensor driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD7314
+	  temperature sensors.
+
 config AD799X
 	tristate "Analog Devices AD799x ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 6c03632..36e66f9 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
 obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7298) += ad7298.o
+obj-$(CONFIG_AD7314) += ad7314.o
diff --git a/drivers/staging/iio/adc/ad7314.c b/drivers/staging/iio/adc/ad7314.c
new file mode 100644
index 0000000..8c17b1f
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7314.c
@@ -0,0 +1,308 @@
+/*
+ * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7314 power mode
+ */
+#define AD7314_PD		0x2000
+
+/*
+ * AD7314 temperature masks
+ */
+#define AD7314_TEMP_SIGN		0x200
+#define AD7314_TEMP_MASK		0x7FE0
+#define AD7314_TEMP_OFFSET		5
+#define AD7314_TEMP_FLOAT_OFFSET	2
+#define AD7314_TEMP_FLOAT_MASK		0x3
+
+/*
+ * ADT7301 and ADT7302 temperature masks
+ */
+#define ADT7301_TEMP_SIGN		0x2000
+#define ADT7301_TEMP_MASK		0x2FFF
+#define ADT7301_TEMP_FLOAT_OFFSET	5
+#define ADT7301_TEMP_FLOAT_MASK		0x1F
+
+/*
+ * struct ad7314_chip_info - chip specifc information
+ */
+
+struct ad7314_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	s64 last_timestamp;
+	u8  mode;
+};
+
+/*
+ * ad7314 register access by SPI
+ */
+
+static int ad7314_spi_read(struct ad7314_chip_info *chip, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+	u16 value;
+
+	ret = spi_read(spi_dev, (u8 *)&value, sizeof(value));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read error\n");
+		return ret;
+	}
+
+	*data = be16_to_cpu((u16)value);
+
+	return ret;
+}
+
+static int ad7314_spi_write(struct ad7314_chip_info *chip, u16 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+	u16 value = cpu_to_be16(data);
+
+	ret = spi_write(spi_dev, (u8 *)&value, sizeof(value));
+	if (ret < 0)
+		dev_err(&spi_dev->dev, "SPI write error\n");
+
+	return ret;
+}
+
+static ssize_t ad7314_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+
+	if (chip->mode)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t ad7314_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+	u16 mode = 0;
+	int ret;
+
+	if (!strcmp(buf, "full"))
+		mode = AD7314_PD;
+
+	ret = ad7314_spi_write(chip, mode);
+	if (ret)
+		return -EIO;
+
+	chip->mode = mode;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7314_show_mode,
+		ad7314_store_mode,
+		0);
+
+static ssize_t ad7314_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-save\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7314_show_available_modes, NULL, 0);
+
+static ssize_t ad7314_show_temperature(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	if (chip->mode) {
+		ret = ad7314_spi_write(chip, 0);
+		if (ret)
+			return -EIO;
+	}
+
+	ret = ad7314_spi_read(chip, &data);
+	if (ret)
+		return -EIO;
+
+	if (chip->mode)
+		ad7314_spi_write(chip, chip->mode);
+
+	if (strcmp(chip->name, "ad7314")) {
+		data = (data & AD7314_TEMP_MASK) >>
+			AD7314_TEMP_OFFSET;
+		if (data & AD7314_TEMP_SIGN) {
+			data = (AD7314_TEMP_SIGN << 1) - data;
+			sign = '-';
+		}
+
+		return sprintf(buf, "%c%d.%.2d\n", sign,
+				data >> AD7314_TEMP_FLOAT_OFFSET,
+				(data & AD7314_TEMP_FLOAT_MASK) * 25);
+	} else {
+		data &= ADT7301_TEMP_MASK;
+		if (data & ADT7301_TEMP_SIGN) {
+			data = (ADT7301_TEMP_SIGN << 1) - data;
+			sign = '-';
+		}
+
+		return sprintf(buf, "%c%d.%.5d\n", sign,
+				data >> ADT7301_TEMP_FLOAT_OFFSET,
+				(data & ADT7301_TEMP_FLOAT_MASK) * 3125);
+	}
+}
+
+static IIO_DEVICE_ATTR(temperature, S_IRUGO, ad7314_show_temperature, NULL, 0);
+
+static ssize_t ad7314_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL, 0);
+
+static struct attribute *ad7314_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_temperature.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7314_attribute_group = {
+	.attrs = ad7314_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7314_probe(struct spi_device *spi_dev)
+{
+	struct ad7314_chip_info *chip;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct ad7314_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &ad7314_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	dev_info(&spi_dev->dev, "%s temperature sensor registered.\n",
+			 chip->name);
+
+	return 0;
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7314_remove(struct spi_device *spi_dev)
+{
+	struct ad7314_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	if (spi_dev->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id ad7314_id[] = {
+	{ "adt7301", 0 },
+	{ "adt7302", 0 },
+	{ "ad7314", 0 },
+	{}
+};
+
+static struct spi_driver ad7314_driver = {
+	.driver = {
+		.name = "ad7314",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad7314_probe,
+	.remove = __devexit_p(ad7314_remove),
+	.id_table = ad7314_id,
+};
+
+static __init int ad7314_init(void)
+{
+	return spi_register_driver(&ad7314_driver);
+}
+
+static __exit void ad7314_exit(void)
+{
+	spi_unregister_driver(&ad7314_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7314_init);
+module_exit(ad7314_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 13/36] staging: iio: adc: new driver for AD7745/6/7 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (11 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 12/36] staging: iio: adc: new driver for AD7314 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 14/36] staging: iio: adc: new driver for AD7816 devices Mike Frysinger
                   ` (23 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |   14 +-
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7745.c |  734 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 747 insertions(+), 2 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7745.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 4e7b6a8..34c6822 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -85,9 +85,9 @@ config AD799X_RING_BUFFER
 config AD7476
 	tristate "Analog Devices AD7475/6/7/8 AD7466/7/8 and AD7495 ADC driver"
 	depends on SPI
-	select IIO_RING_BUFFER	
+	select IIO_RING_BUFFER
 	select IIO_SW_RING
-	select IIO_TRIGGER 	
+	select IIO_TRIGGER
 	help
 	  Say yes here to build support for Analog Devices
 	  AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495
@@ -96,3 +96,13 @@ config AD7476
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7476.
+
+config AD7745
+	tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices capacitive sensors.
+	  (AD7745, AD7746, AD7747) Provides direct access via sysfs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7745.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 36e66f9..ac83bad 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_AD7152) += ad7152.o
 obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7298) += ad7298.o
 obj-$(CONFIG_AD7314) += ad7314.o
+obj-$(CONFIG_AD7745) += ad7745.o
diff --git a/drivers/staging/iio/adc/ad7745.c b/drivers/staging/iio/adc/ad7745.c
new file mode 100644
index 0000000..ab7ef84
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7745.c
@@ -0,0 +1,734 @@
+/*
+ * AD774X capacitive sensor driver supporting AD7745/6/7
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD774X registers definition
+ */
+
+#define AD774X_STATUS		0
+#define AD774X_STATUS_RDY	(1 << 2)
+#define AD774X_STATUS_RDYVT	(1 << 1)
+#define AD774X_STATUS_RDYCAP	(1 << 0)
+#define AD774X_CAP_DATA_HIGH	1
+#define AD774X_CAP_DATA_MID	2
+#define AD774X_CAP_DATA_LOW	3
+#define AD774X_VT_DATA_HIGH	4
+#define AD774X_VT_DATA_MID	5
+#define AD774X_VT_DATA_LOW	6
+#define AD774X_CAP_SETUP	7
+#define AD774X_VT_SETUP		8
+#define AD774X_EXEC_SETUP	9
+#define AD774X_CFG		10
+#define AD774X_CAPDACA		11
+#define AD774X_CAPDACB		12
+#define AD774X_CAPDAC_EN	(1 << 7)
+#define AD774X_CAP_OFFH		13
+#define AD774X_CAP_OFFL		14
+#define AD774X_CAP_GAINH	15
+#define AD774X_CAP_GAINL	16
+#define AD774X_VOLT_GAINH	17
+#define AD774X_VOLT_GAINL	18
+
+#define AD774X_MAX_CONV_MODE	6
+
+/*
+ * struct ad774x_chip_info - chip specifc information
+ */
+
+struct ad774x_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	bool inter;
+	s64 last_timestamp;
+	u16 cap_offs;                   /* Capacitive offset */
+	u16 cap_gain;                   /* Capacitive gain calibration */
+	u16 volt_gain;                  /* Voltage gain calibration */
+	u8  cap_setup;
+	u8  vt_setup;
+	u8  exec_setup;
+
+	char *conversion_mode;
+};
+
+struct ad774x_conversion_mode {
+	char *name;
+	u8 reg_cfg;
+};
+
+struct ad774x_conversion_mode ad774x_conv_mode_table[AD774X_MAX_CONV_MODE] = {
+	{ "idle", 0 },
+	{ "continuous-conversion", 1 },
+	{ "single-conversion", 2 },
+	{ "power-down", 3 },
+	{ "offset-calibration", 5 },
+	{ "gain-calibration", 6 },
+};
+
+/*
+ * ad774x register access by I2C
+ */
+
+static int ad774x_i2c_read(struct ad774x_chip_info *chip, u8 reg, u8 *data, int len)
+{
+	struct i2c_client *client = chip->client;
+	int ret;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C write error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ad774x_i2c_write(struct ad774x_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret;
+
+	u8 tx[2] = {
+		reg,
+		data,
+	};
+
+	ret = i2c_master_send(client, tx, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/*
+ * sysfs nodes
+ */
+
+#define IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_conversion_modes, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CONVERSION_MODE(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(conversion_mode, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CAP_SETUP(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(cap_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_VT_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(in0_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_EXEC_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(exec_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_VOLT_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(in0_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CAP_OFFS(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(cap_offs, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CAP_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(cap_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CAP_DATA(_show)		\
+	IIO_DEVICE_ATTR(cap0_raw, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_VT_DATA(_show)		\
+	IIO_DEVICE_ATTR(in0_raw, S_IRUGO, _show, NULL, 0)
+
+static ssize_t ad774x_show_conversion_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int i;
+	int len = 0;
+
+	for (i = 0; i < AD774X_MAX_CONV_MODE; i++)
+		len += sprintf(buf + len, "%s ", ad774x_conv_mode_table[i].name);
+
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+static IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(ad774x_show_conversion_modes);
+
+static ssize_t ad774x_show_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->conversion_mode);
+}
+
+static ssize_t ad774x_store_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	u8 cfg;
+	int i;
+
+	ad774x_i2c_read(chip, AD774X_CFG, &cfg, 1);
+
+	for (i = 0; i < AD774X_MAX_CONV_MODE; i++) {
+		if (strncmp(buf, ad774x_conv_mode_table[i].name,
+				strlen(ad774x_conv_mode_table[i].name) - 1) == 0) {
+			chip->conversion_mode = ad774x_conv_mode_table[i].name;
+			cfg |= 0x18 | ad774x_conv_mode_table[i].reg_cfg;
+			ad774x_i2c_write(chip, AD774X_CFG, cfg);
+			return len;
+		}
+	}
+
+	dev_err(dev, "not supported conversion mode\n");
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CONVERSION_MODE(S_IRUGO | S_IWUSR,
+		ad774x_show_conversion_mode,
+		ad774x_store_conversion_mode);
+
+static ssize_t ad774x_show_dac_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	u8 data;
+
+	ad774x_i2c_read(chip, this_attr->address, &data, 1);
+
+	return sprintf(buf, "%02x\n", data & 0x7F);
+}
+
+static ssize_t ad774x_store_dac_value(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if (!ret) {
+		ad774x_i2c_write(chip, this_attr->address,
+			(data ? AD774X_CAPDAC_EN : 0) | (data & 0x7F));
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEVICE_ATTR(capdac0_raw, S_IRUGO | S_IWUSR,
+			ad774x_show_dac_value,
+			ad774x_store_dac_value,
+			AD774X_CAPDACA);
+
+static IIO_DEVICE_ATTR(capdac1_raw, S_IRUGO | S_IWUSR,
+			ad774x_show_dac_value,
+			ad774x_store_dac_value,
+			AD774X_CAPDACB);
+
+static ssize_t ad774x_show_cap_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->cap_setup);
+}
+
+static ssize_t ad774x_store_cap_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad774x_i2c_write(chip, AD774X_CAP_SETUP, data);
+		chip->cap_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CAP_SETUP(S_IRUGO | S_IWUSR,
+		ad774x_show_cap_setup,
+		ad774x_store_cap_setup);
+
+static ssize_t ad774x_show_vt_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->vt_setup);
+}
+
+static ssize_t ad774x_store_vt_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad774x_i2c_write(chip, AD774X_VT_SETUP, data);
+		chip->vt_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_VT_SETUP(S_IRUGO | S_IWUSR,
+		ad774x_show_vt_setup,
+		ad774x_store_vt_setup);
+
+static ssize_t ad774x_show_exec_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->exec_setup);
+}
+
+static ssize_t ad774x_store_exec_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad774x_i2c_write(chip, AD774X_EXEC_SETUP, data);
+		chip->exec_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_EXEC_SETUP(S_IRUGO | S_IWUSR,
+		ad774x_show_exec_setup,
+		ad774x_store_exec_setup);
+
+static ssize_t ad774x_show_volt_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->volt_gain);
+}
+
+static ssize_t ad774x_store_volt_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad774x_i2c_write(chip, AD774X_VOLT_GAINH, data >> 8);
+		ad774x_i2c_write(chip, AD774X_VOLT_GAINL, data);
+		chip->volt_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_VOLT_GAIN(S_IRUGO | S_IWUSR,
+		ad774x_show_volt_gain,
+		ad774x_store_volt_gain);
+
+static ssize_t ad774x_show_cap_data(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	char tmp[3];
+
+	ad774x_i2c_read(chip, AD774X_CAP_DATA_HIGH, tmp, 3);
+	data = ((int)tmp[0] << 16) | ((int)tmp[1] << 8) | (int)tmp[2];
+
+	return sprintf(buf, "%ld\n", data);
+}
+
+static IIO_DEV_ATTR_CAP_DATA(ad774x_show_cap_data);
+
+static ssize_t ad774x_show_vt_data(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	char tmp[3];
+
+	ad774x_i2c_read(chip, AD774X_VT_DATA_HIGH, tmp, 3);
+	data = ((int)tmp[0] << 16) | ((int)tmp[1] << 8) | (int)tmp[2];
+
+	return sprintf(buf, "%ld\n", data);
+}
+
+static IIO_DEV_ATTR_VT_DATA(ad774x_show_vt_data);
+
+static ssize_t ad774x_show_cap_offs(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->cap_offs);
+}
+
+static ssize_t ad774x_store_cap_offs(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad774x_i2c_write(chip, AD774X_CAP_OFFH, data >> 8);
+		ad774x_i2c_write(chip, AD774X_CAP_OFFL, data);
+		chip->cap_offs = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CAP_OFFS(S_IRUGO | S_IWUSR,
+		ad774x_show_cap_offs,
+		ad774x_store_cap_offs);
+
+static ssize_t ad774x_show_cap_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->cap_gain);
+}
+
+static ssize_t ad774x_store_cap_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad774x_i2c_write(chip, AD774X_CAP_GAINH, data >> 8);
+		ad774x_i2c_write(chip, AD774X_CAP_GAINL, data);
+		chip->cap_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CAP_GAIN(S_IRUGO | S_IWUSR,
+		ad774x_show_cap_gain,
+		ad774x_store_cap_gain);
+
+static ssize_t ad774x_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad774x_show_name, NULL, 0);
+
+static struct attribute *ad774x_attributes[] = {
+	&iio_dev_attr_available_conversion_modes.dev_attr.attr,
+	&iio_dev_attr_conversion_mode.dev_attr.attr,
+	&iio_dev_attr_cap_setup.dev_attr.attr,
+	&iio_dev_attr_in0_setup.dev_attr.attr,
+	&iio_dev_attr_exec_setup.dev_attr.attr,
+	&iio_dev_attr_cap_offs.dev_attr.attr,
+	&iio_dev_attr_cap_gain.dev_attr.attr,
+	&iio_dev_attr_in0_gain.dev_attr.attr,
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_dev_attr_cap0_raw.dev_attr.attr,
+	&iio_dev_attr_capdac0_raw.dev_attr.attr,
+	&iio_dev_attr_capdac1_raw.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad774x_attribute_group = {
+	.attrs = ad774x_attributes,
+};
+
+/*
+ * data ready events
+ */
+
+#define IIO_EVENT_CODE_CAP_RDY     IIO_BUFFER_EVENT_CODE(0)
+#define IIO_EVENT_CODE_VT_RDY      IIO_BUFFER_EVENT_CODE(1)
+
+#define IIO_EVENT_ATTR_CAP_RDY_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(cap_rdy, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_VT_RDY_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(vt_rdy, _evlist, _show, _store, _mask)
+
+static void ad774x_interrupt_handler_bh(struct work_struct *work_s)
+{
+	struct ad774x_chip_info *chip =
+		container_of(work_s, struct ad774x_chip_info, thresh_work);
+	u8 int_status;
+
+	enable_irq(chip->client->irq);
+
+	ad774x_i2c_read(chip, AD774X_STATUS, &int_status, 1);
+
+	if (int_status & AD774X_STATUS_RDYCAP)
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CAP_RDY,
+				chip->last_timestamp);
+
+	if (int_status & AD774X_STATUS_RDYVT)
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_VT_RDY,
+				chip->last_timestamp);
+}
+
+static int ad774x_interrupt_handler_th(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(data_rdy, &ad774x_interrupt_handler_th);
+
+static ssize_t ad774x_query_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	/*
+	 * AD774X provides one /RDY pin, which can be used as interrupt
+	 * but the pin is not configurable
+	 */
+	return sprintf(buf, "1\n");
+}
+
+static ssize_t ad774x_set_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return len;
+}
+
+IIO_EVENT_ATTR_CAP_RDY_SH(iio_event_data_rdy, ad774x_query_out_mode, ad774x_set_out_mode, 0);
+IIO_EVENT_ATTR_VT_RDY_SH(iio_event_data_rdy, ad774x_query_out_mode, ad774x_set_out_mode, 0);
+
+static struct attribute *ad774x_event_attributes[] = {
+	&iio_event_attr_cap_rdy.dev_attr.attr,
+	&iio_event_attr_vt_rdy.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad774x_event_attribute_group = {
+	.attrs = ad774x_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad774x_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret = 0, regdone = 0;
+	struct ad774x_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	/* Establish that the iio_dev is a child of the i2c device */
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad774x_attribute_group;
+	chip->indio_dev->event_attrs = &ad774x_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)(chip);
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+	regdone = 1;
+
+	if (client->irq) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				"ad774x");
+		if (ret)
+			goto error_free_dev;
+
+		iio_add_event_to_list(iio_event_attr_cap_rdy.listel,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad774x_interrupt_handler_bh);
+	}
+
+	dev_err(&client->dev, "%s capacitive sensor registered, irq: %d\n", id->name, client->irq);
+
+	return 0;
+
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(chip->indio_dev);
+	else
+		iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad774x_remove(struct i2c_client *client)
+{
+	struct ad774x_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad774x_id[] = {
+	{ "ad7745", 0 },
+	{ "ad7746", 0 },
+	{ "ad7747", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad774x_id);
+
+static struct i2c_driver ad774x_driver = {
+	.driver = {
+		.name = "ad774x",
+	},
+	.probe = ad774x_probe,
+	.remove = __devexit_p(ad774x_remove),
+	.id_table = ad774x_id,
+};
+
+static __init int ad774x_init(void)
+{
+	return i2c_add_driver(&ad774x_driver);
+}
+
+static __exit void ad774x_exit(void)
+{
+	i2c_del_driver(&ad774x_driver);
+}
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ad7745/6/7 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad774x_init);
+module_exit(ad774x_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 14/36] staging: iio: adc: new driver for AD7816 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (12 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 13/36] staging: iio: adc: new driver for AD7745/6/7 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 15/36] staging: iio: adc: new driver for ADT75 temperature sensors Mike Frysinger
                   ` (22 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7816.c |  535 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 543 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7816.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 34c6822..e722b26 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -106,3 +106,10 @@ config AD7745
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7745.
+
+config AD7816
+	tristate "Analog Devices AD7816/7/8 temperature sensor and ADC driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD7816/7/8
+	  temperature sensors and ADC.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index ac83bad..b8d641c 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7298) += ad7298.o
 obj-$(CONFIG_AD7314) += ad7314.o
 obj-$(CONFIG_AD7745) += ad7745.o
+obj-$(CONFIG_AD7816) += ad7816.o
diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c
new file mode 100644
index 0000000..ad7415a
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7816.c
@@ -0,0 +1,535 @@
+/*
+ * AD7816 digital temperature sensor driver supporting AD7816/7/8
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7816 config masks
+ */
+#define AD7816_FULL			0x1
+#define AD7816_PD			0x2
+#define AD7816_CS_MASK			0x7
+#define AD7816_CS_MAX			0x4
+
+/*
+ * AD7816 temperature masks
+ */
+#define AD7816_VALUE_OFFSET		6
+#define AD7816_BOUND_VALUE_BASE		0x8
+#define AD7816_BOUND_VALUE_MIN		-95
+#define AD7816_BOUND_VALUE_MAX		152
+#define AD7816_TEMP_FLOAT_OFFSET	2
+#define AD7816_TEMP_FLOAT_MASK		0x3
+
+
+/*
+ * struct ad7816_chip_info - chip specifc information
+ */
+
+struct ad7816_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u16 rdwr_pin;
+	u16 convert_pin;
+	u16 busy_pin;
+	u8  oti_data[AD7816_CS_MAX+1];
+	u8  channel_id;	/* 0 always be temperature */
+	u8  mode;
+};
+
+/*
+ * ad7816 data access by SPI
+ */
+static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+
+	gpio_set_value(chip->rdwr_pin, 1);
+	gpio_set_value(chip->rdwr_pin, 0);
+	ret = spi_write(spi_dev, &chip->channel_id, sizeof(chip->channel_id));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI channel setting error\n");
+		return ret;
+	}
+	gpio_set_value(chip->rdwr_pin, 1);
+
+
+	if (chip->mode == AD7816_PD) { /* operating mode 2 */
+		gpio_set_value(chip->convert_pin, 1);
+		gpio_set_value(chip->convert_pin, 0);
+	} else { /* operating mode 1 */
+		gpio_set_value(chip->convert_pin, 0);
+		gpio_set_value(chip->convert_pin, 1);
+	}
+
+	while (gpio_get_value(chip->busy_pin))
+		cpu_relax();
+
+	gpio_set_value(chip->rdwr_pin, 0);
+	gpio_set_value(chip->rdwr_pin, 1);
+	ret = spi_read(spi_dev, (u8 *)data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI data read error\n");
+		return ret;
+	}
+
+	*data = be16_to_cpu(*data);
+
+	return ret;
+}
+
+static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+
+	gpio_set_value(chip->rdwr_pin, 1);
+	gpio_set_value(chip->rdwr_pin, 0);
+	ret = spi_write(spi_dev, &data, sizeof(data));
+	if (ret < 0)
+		dev_err(&spi_dev->dev, "SPI oti data write error\n");
+
+	return ret;
+}
+
+static ssize_t ad7816_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	if (chip->mode)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t ad7816_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	if (strcmp(buf, "full")) {
+		gpio_set_value(chip->rdwr_pin, 1);
+		chip->mode = AD7816_FULL;
+	} else {
+		gpio_set_value(chip->rdwr_pin, 0);
+		chip->mode = AD7816_PD;
+	}
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7816_show_mode,
+		ad7816_store_mode,
+		0);
+
+static ssize_t ad7816_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-save\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7816_show_available_modes, NULL, 0);
+
+static ssize_t ad7816_show_channel(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->channel_id);
+}
+
+static ssize_t ad7816_store_channel(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	if (data > AD7816_CS_MAX && data != AD7816_CS_MASK) {
+		dev_err(&chip->spi_dev->dev, "Invalid channel id %lu for %s.\n",
+			data, chip->name);
+		return -EINVAL;
+	} else if (strcmp(chip->name, "ad7818") == 0 && data > 1) {
+		dev_err(&chip->spi_dev->dev,
+			"Invalid channel id %lu for ad7818.\n", data);
+		return -EINVAL;
+	} else if (strcmp(chip->name, "ad7816") == 0 && data > 0) {
+		dev_err(&chip->spi_dev->dev,
+			"Invalid channel id %lu for ad7816.\n", data);
+		return -EINVAL;
+	}
+
+	chip->channel_id = data;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(channel, S_IRUGO | S_IWUSR,
+		ad7816_show_channel,
+		ad7816_store_channel,
+		0);
+
+
+static ssize_t ad7816_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	s8 value;
+	int ret;
+
+	ret = ad7816_spi_read(chip, &data);
+	if (ret)
+		return -EIO;
+
+	data >>= AD7816_VALUE_OFFSET;
+
+	if (chip->channel_id == 0) {
+		value = (s8)((data >> AD7816_TEMP_FLOAT_OFFSET) - 103);
+		data &= AD7816_TEMP_FLOAT_MASK;
+		if (value < 0)
+			data = (1 << AD7816_TEMP_FLOAT_OFFSET) - data;
+		return sprintf(buf, "%d.%.2d\n", value, data * 25);
+	} else
+		return sprintf(buf, "%u\n", data);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, ad7816_show_value, NULL, 0);
+
+static ssize_t ad7816_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7816_show_name, NULL, 0);
+
+static struct attribute *ad7816_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_channel.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7816_attribute_group = {
+	.attrs = ad7816_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_AD7816_OTI    IIO_BUFFER_EVENT_CODE(0)
+
+static void ad7816_interrupt_bh(struct work_struct *work_s)
+{
+	struct ad7816_chip_info *chip =
+		container_of(work_s, struct ad7816_chip_info, thresh_work);
+
+	enable_irq(chip->spi_dev->irq);
+
+	iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_AD7816_OTI,
+			chip->last_timestamp);
+}
+
+static int ad7816_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(ad7816, &ad7816_interrupt);
+
+static ssize_t ad7816_show_oti(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	int value;
+
+	if (chip->channel_id > AD7816_CS_MAX) {
+		dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
+		return -EINVAL;
+	} else if (chip->channel_id == 0) {
+		value = AD7816_BOUND_VALUE_MIN +
+			(chip->oti_data[chip->channel_id] -
+			AD7816_BOUND_VALUE_BASE);
+		return sprintf(buf, "%d\n", value);
+	} else
+		return sprintf(buf, "%u\n", chip->oti_data[chip->channel_id]);
+}
+
+static inline ssize_t ad7816_set_oti(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	long value;
+	u8 data;
+	int ret;
+
+	ret = strict_strtol(buf, 10, &value);
+
+	if (chip->channel_id > AD7816_CS_MAX) {
+		dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
+		return -EINVAL;
+	} else if (chip->channel_id == 0) {
+		if (ret || value < AD7816_BOUND_VALUE_MIN ||
+			value > AD7816_BOUND_VALUE_MAX)
+			return -EINVAL;
+
+		data = (u8)(value - AD7816_BOUND_VALUE_MIN +
+			AD7816_BOUND_VALUE_BASE);
+	} else {
+		if (ret || value < AD7816_BOUND_VALUE_BASE || value > 255)
+			return -EINVAL;
+
+		data = (u8)value;
+	}
+
+	ret = ad7816_spi_write(chip, data);
+	if (ret)
+		return -EIO;
+
+	chip->oti_data[chip->channel_id] = data;
+
+	return len;
+}
+
+IIO_EVENT_ATTR_SH(oti, iio_event_ad7816,
+		ad7816_show_oti, ad7816_set_oti, 0);
+
+static struct attribute *ad7816_event_attributes[] = {
+	&iio_event_attr_oti.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7816_event_attribute_group = {
+	.attrs = ad7816_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7816_probe(struct spi_device *spi_dev)
+{
+	struct ad7816_chip_info *chip;
+	unsigned short *pins = spi_dev->dev.platform_data;
+	int ret = 0;
+	int i;
+
+	if (!pins) {
+		dev_err(&spi_dev->dev, "No necessary GPIO platform data.\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct ad7816_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+	for (i = 0; i <= AD7816_CS_MAX; i++)
+		chip->oti_data[i] = 203;
+	chip->rdwr_pin = pins[0];
+	chip->convert_pin = pins[1];
+	chip->busy_pin = pins[2];
+
+	ret = gpio_request(chip->rdwr_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request rdwr gpio PIN %d.\n",
+			chip->rdwr_pin);
+		goto error_free_chip;
+	}
+	gpio_direction_input(chip->rdwr_pin);
+	ret = gpio_request(chip->convert_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request convert gpio PIN %d.\n",
+			chip->convert_pin);
+		goto error_free_gpio_rdwr;
+	}
+	gpio_direction_input(chip->convert_pin);
+	ret = gpio_request(chip->busy_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n",
+			chip->busy_pin);
+		goto error_free_gpio_convert;
+	}
+	gpio_direction_input(chip->busy_pin);
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_gpio;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &ad7816_attribute_group;
+	chip->indio_dev->event_attrs = &ad7816_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (spi_dev->irq) {
+		/* Only low trigger is supported in ad7816/7/8 */
+		ret = iio_register_interrupt_line(spi_dev->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_LOW,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_ad7816.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_ad7816,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad7816_interrupt_bh);
+	}
+
+	dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n",
+			 chip->name);
+
+	return 0;
+
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_gpio:
+	gpio_free(chip->busy_pin);
+error_free_gpio_convert:
+	gpio_free(chip->convert_pin);
+error_free_gpio_rdwr:
+	gpio_free(chip->rdwr_pin);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7816_remove(struct spi_device *spi_dev)
+{
+	struct ad7816_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	if (spi_dev->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	gpio_free(chip->busy_pin);
+	gpio_free(chip->convert_pin);
+	gpio_free(chip->rdwr_pin);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id ad7816_id[] = {
+	{ "ad7816", 0 },
+	{ "ad7817", 0 },
+	{ "ad7818", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, ad7816_id);
+
+static struct spi_driver ad7816_driver = {
+	.driver = {
+		.name = "ad7816",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad7816_probe,
+	.remove = __devexit_p(ad7816_remove),
+	.id_table = ad7816_id,
+};
+
+static __init int ad7816_init(void)
+{
+	return spi_register_driver(&ad7816_driver);
+}
+
+static __exit void ad7816_exit(void)
+{
+	spi_unregister_driver(&ad7816_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7816/7/8 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7816_init);
+module_exit(ad7816_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 15/36] staging: iio: adc: new driver for ADT75 temperature sensors
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (13 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 14/36] staging: iio: adc: new driver for AD7816 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 16/36] staging: iio: adc: new driver for ADT7310 " Mike Frysinger
                   ` (21 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/adt75.c  |  732 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 740 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/adt75.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index e722b26..a7c6b81 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -113,3 +113,10 @@ config AD7816
 	help
 	  Say yes here to build support for Analog Devices AD7816/7/8
 	  temperature sensors and ADC.
+
+config ADT75
+	tristate "Analog Devices ADT75 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices ADT75
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index b8d641c..d1f1b57 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_AD7298) += ad7298.o
 obj-$(CONFIG_AD7314) += ad7314.o
 obj-$(CONFIG_AD7745) += ad7745.o
 obj-$(CONFIG_AD7816) += ad7816.o
+obj-$(CONFIG_ADT75) += adt75.o
diff --git a/drivers/staging/iio/adc/adt75.c b/drivers/staging/iio/adc/adt75.c
new file mode 100644
index 0000000..aff4d31
--- /dev/null
+++ b/drivers/staging/iio/adc/adt75.c
@@ -0,0 +1,732 @@
+/*
+ * ADT75 digital temperature sensor driver supporting ADT75
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * ADT75 registers definition
+ */
+
+#define ADT75_TEMPERATURE		0
+#define ADT75_CONFIG			1
+#define ADT75_T_HYST			2
+#define ADT75_T_OS			3
+#define ADT75_ONESHOT			4
+
+/*
+ * ADT75 config
+ */
+#define ADT75_PD			0x1
+#define ADT75_OS_INT			0x2
+#define ADT75_OS_POLARITY		0x4
+#define ADT75_FAULT_QUEUE_MASK		0x18
+#define ADT75_FAULT_QUEUE_OFFSET	3
+#define ADT75_SMBUS_ALART		0x8
+
+/*
+ * ADT75 masks
+ */
+#define ADT75_VALUE_SIGN		0x800
+#define ADT75_VALUE_OFFSET		4
+#define ADT75_VALUE_FLOAT_OFFSET	4
+#define ADT75_VALUE_FLOAT_MASK		0xF
+
+
+/*
+ * struct adt75_chip_info - chip specifc information
+ */
+
+struct adt75_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u8  config;
+};
+
+/*
+ * adt75 register access by I2C
+ */
+
+static int adt75_i2c_read(struct adt75_chip_info *chip, u8 reg, u8 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0, len;
+
+	ret = i2c_smbus_write_byte(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read register address error\n");
+		return ret;
+	}
+
+	if (reg == ADT75_CONFIG || reg == ADT75_ONESHOT)
+		len = 1;
+	else
+		len = 2;
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int adt75_i2c_write(struct adt75_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	if (reg == ADT75_CONFIG || reg == ADT75_ONESHOT)
+		ret = i2c_smbus_write_byte_data(client, reg, data);
+	else
+		ret = i2c_smbus_write_word_data(client, reg, data);
+
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static ssize_t adt75_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+
+	if (chip->config & ADT75_PD)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t adt75_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 config;
+
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_PD;
+	if (!strcmp(buf, "full"))
+		config |= ADT75_PD;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt75_show_mode,
+		adt75_store_mode,
+		0);
+
+static ssize_t adt75_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-down\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt75_show_available_modes, NULL, 0);
+
+static ssize_t adt75_show_oneshot(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT75_ONESHOT));
+}
+
+static ssize_t adt75_store_oneshot(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	unsigned long data = 0;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_ONESHOT;
+	if (data)
+		config |= ADT75_ONESHOT;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(oneshot, S_IRUGO | S_IWUSR,
+		adt75_show_oneshot,
+		adt75_store_oneshot,
+		0);
+
+static ssize_t adt75_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	if (chip->config & ADT75_PD) {
+		dev_err(dev, "Can't read value in power-down mode.\n");
+		return -EIO;
+	}
+
+	if (chip->config & ADT75_ONESHOT) {
+		/* write to active converter */
+		ret = i2c_smbus_write_byte(chip->client, ADT75_ONESHOT);
+		if (ret)
+			return -EIO;
+	}
+
+	ret = adt75_i2c_read(chip, ADT75_TEMPERATURE, (u8 *)&data);
+	if (ret)
+		return -EIO;
+
+	data = swab16(data) >> ADT75_VALUE_OFFSET;
+	if (data & ADT75_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (ADT75_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.4d\n", sign,
+		(data >> ADT75_VALUE_FLOAT_OFFSET),
+		(data & ADT75_VALUE_FLOAT_MASK) * 625);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, adt75_show_value, NULL, 0);
+
+static ssize_t adt75_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt75_show_name, NULL, 0);
+
+static struct attribute *adt75_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_oneshot.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt75_attribute_group = {
+	.attrs = adt75_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT75_OTI    IIO_BUFFER_EVENT_CODE(0)
+
+static void adt75_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt75_chip_info *chip =
+		container_of(work_s, struct adt75_chip_info, thresh_work);
+
+	enable_irq(chip->client->irq);
+
+	iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT75_OTI,
+			chip->last_timestamp);
+}
+
+static int adt75_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt75_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt75, &adt75_interrupt);
+
+static ssize_t adt75_show_oti_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT75_OS_INT)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t adt75_set_oti_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 config;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_OS_INT;
+	if (strcmp(buf, "comparator") != 0)
+		config |= ADT75_OS_INT;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt75_show_available_oti_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t adt75_show_smbus_alart(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT75_SMBUS_ALART));
+}
+
+static ssize_t adt75_set_smbus_alart(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	unsigned long data = 0;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_SMBUS_ALART;
+	if (data)
+		config |= ADT75_SMBUS_ALART;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt75_show_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", (chip->config & ADT75_FAULT_QUEUE_MASK) >>
+				ADT75_FAULT_QUEUE_OFFSET);
+}
+
+static ssize_t adt75_set_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > 3)
+		return -EINVAL;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_FAULT_QUEUE_MASK;
+	config |= (data << ADT75_FAULT_QUEUE_OFFSET);
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+static inline ssize_t adt75_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = adt75_i2c_read(chip, bound_reg, (u8 *)&data);
+	if (ret)
+		return -EIO;
+
+	data = swab16(data) >> ADT75_VALUE_OFFSET;
+	if (data & ADT75_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (ADT75_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.4d\n", sign,
+		(data >> ADT75_VALUE_FLOAT_OFFSET),
+		(data & ADT75_VALUE_FLOAT_MASK) * 625);
+}
+
+static inline ssize_t adt75_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+		if (len > ADT75_VALUE_FLOAT_OFFSET)
+			len = ADT75_VALUE_FLOAT_OFFSET;
+		pos[len] = 0;
+		ret = strict_strtol(pos, 10, &tmp2);
+
+		if (!ret)
+			tmp2 = (tmp2 / 625) * 625;
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+	data = (data << ADT75_VALUE_FLOAT_OFFSET) | (tmp2 & ADT75_VALUE_FLOAT_MASK);
+	if (tmp1 < 0)
+		/* convert positive value to supplyment */
+		data = (ADT75_VALUE_SIGN << 1) - data;
+	data <<= ADT75_VALUE_OFFSET;
+	data = swab16(data);
+
+	ret = adt75_i2c_write(chip, bound_reg, (u8)data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t adt75_show_t_os(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt75_show_t_bound(dev, attr,
+			ADT75_T_OS, buf);
+}
+
+static inline ssize_t adt75_set_t_os(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt75_set_t_bound(dev, attr,
+			ADT75_T_OS, buf, len);
+}
+
+static ssize_t adt75_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt75_show_t_bound(dev, attr,
+			ADT75_T_HYST, buf);
+}
+
+static inline ssize_t adt75_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt75_set_t_bound(dev, attr,
+			ADT75_T_HYST, buf, len);
+}
+
+IIO_EVENT_ATTR_SH(oti_mode, iio_event_adt75,
+		adt75_show_oti_mode, adt75_set_oti_mode, 0);
+IIO_EVENT_ATTR_SH(available_oti_modes, iio_event_adt75,
+		adt75_show_available_oti_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(smbus_alart, iio_event_adt75,
+		adt75_show_smbus_alart, adt75_set_smbus_alart, 0);
+IIO_EVENT_ATTR_SH(fault_queue, iio_event_adt75,
+		adt75_show_fault_queue, adt75_set_fault_queue, 0);
+IIO_EVENT_ATTR_SH(t_os, iio_event_adt75,
+		adt75_show_t_os, adt75_set_t_os, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt75,
+		adt75_show_t_hyst, adt75_set_t_hyst, 0);
+
+static struct attribute *adt75_event_attributes[] = {
+	&iio_event_attr_oti_mode.dev_attr.attr,
+	&iio_event_attr_available_oti_modes.dev_attr.attr,
+	&iio_event_attr_smbus_alart.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_os.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt75_event_attribute_group = {
+	.attrs = adt75_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt75_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct adt75_chip_info *chip;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct adt75_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &adt75_attribute_group;
+	chip->indio_dev->event_attrs = &adt75_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (client->irq > 0) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_LOW,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt75.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_adt75,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, adt75_interrupt_bh);
+
+		ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+
+		/* set irq polarity low level */
+		chip->config &= ~ADT75_OS_POLARITY;
+
+		ret = adt75_i2c_write(chip, ADT75_CONFIG, chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adt75_remove(struct i2c_client *client)
+{
+	struct adt75_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id adt75_id[] = {
+	{ "adt75", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, adt75_id);
+
+static struct i2c_driver adt75_driver = {
+	.driver = {
+		.name = "adt75",
+	},
+	.probe = adt75_probe,
+	.remove = __devexit_p(adt75_remove),
+	.id_table = adt75_id,
+};
+
+static __init int adt75_init(void)
+{
+	return i2c_add_driver(&adt75_driver);
+}
+
+static __exit void adt75_exit(void)
+{
+	i2c_del_driver(&adt75_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT75 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt75_init);
+module_exit(adt75_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 16/36] staging: iio: adc: new driver for ADT7310 temperature sensors
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (14 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 15/36] staging: iio: adc: new driver for ADT75 temperature sensors Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 17/36] staging: iio: adc: new driver for ADT7410 " Mike Frysinger
                   ` (20 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig   |    7 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/adt7310.c |  952 +++++++++++++++++++++++++++++++++++++
 3 files changed, 960 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/adt7310.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index a7c6b81..cd35526 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -120,3 +120,10 @@ config ADT75
 	help
 	  Say yes here to build support for Analog Devices ADT75
 	  temperature sensors.
+
+config ADT7310
+	tristate "Analog Devices ADT7310 temperature sensor driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices ADT7310
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index d1f1b57..6067b66 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_AD7314) += ad7314.o
 obj-$(CONFIG_AD7745) += ad7745.o
 obj-$(CONFIG_AD7816) += ad7816.o
 obj-$(CONFIG_ADT75) += adt75.o
+obj-$(CONFIG_ADT7310) += adt7310.o
diff --git a/drivers/staging/iio/adc/adt7310.c b/drivers/staging/iio/adc/adt7310.c
new file mode 100644
index 0000000..771a409
--- /dev/null
+++ b/drivers/staging/iio/adc/adt7310.c
@@ -0,0 +1,952 @@
+/*
+ * ADT7310 digital temperature sensor driver supporting ADT7310
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * ADT7310 registers definition
+ */
+
+#define ADT7310_STATUS			0
+#define ADT7310_CONFIG			1
+#define ADT7310_TEMPERATURE		2
+#define ADT7310_ID			3
+#define ADT7310_T_CRIT			4
+#define ADT7310_T_HYST			5
+#define ADT7310_T_ALARM_HIGH		6
+#define ADT7310_T_ALARM_LOW		7
+
+/*
+ * ADT7310 status
+ */
+#define ADT7310_STAT_T_LOW		0x10
+#define ADT7310_STAT_T_HIGH		0x20
+#define ADT7310_STAT_T_CRIT		0x40
+#define ADT7310_STAT_NOT_RDY		0x80
+
+/*
+ * ADT7310 config
+ */
+#define ADT7310_FAULT_QUEUE_MASK	0x3
+#define ADT7310_CT_POLARITY		0x4
+#define ADT7310_INT_POLARITY		0x8
+#define ADT7310_EVENT_MODE		0x10
+#define ADT7310_MODE_MASK		0x60
+#define ADT7310_ONESHOT			0x20
+#define ADT7310_SPS			0x40
+#define ADT7310_PD			0x60
+#define ADT7310_RESOLUTION		0x80
+
+/*
+ * ADT7310 masks
+ */
+#define ADT7310_T16_VALUE_SIGN			0x8000
+#define ADT7310_T16_VALUE_FLOAT_OFFSET		7
+#define ADT7310_T16_VALUE_FLOAT_MASK		0x7F
+#define ADT7310_T13_VALUE_SIGN			0x1000
+#define ADT7310_T13_VALUE_OFFSET		3
+#define ADT7310_T13_VALUE_FLOAT_OFFSET		4
+#define ADT7310_T13_VALUE_FLOAT_MASK		0xF
+#define ADT7310_T_HYST_MASK			0xF
+#define ADT7310_DEVICE_ID_MASK			0x7
+#define ADT7310_MANUFACTORY_ID_MASK		0xF8
+#define ADT7310_MANUFACTORY_ID_OFFSET		3
+
+
+#define ADT7310_CMD_REG_MASK			0x28
+#define ADT7310_CMD_REG_OFFSET			3
+#define ADT7310_CMD_READ			0x40
+#define ADT7310_CMD_CON_READ			0x4
+
+#define ADT7310_IRQS				2
+
+/*
+ * struct adt7310_chip_info - chip specifc information
+ */
+
+struct adt7310_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u8  config;
+};
+
+/*
+ * adt7310 register access by SPI
+ */
+
+static int adt7310_spi_read_word(struct adt7310_chip_info *chip, u8 reg, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 command = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	int ret = 0;
+
+	command |= ADT7310_CMD_READ;
+	ret = spi_write(spi_dev, &command, sizeof(command));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi_dev, (u8 *)data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read word error\n");
+		return ret;
+	}
+
+	*data = be16_to_cpu(*data);
+
+	return 0;
+}
+
+static int adt7310_spi_write_word(struct adt7310_chip_info *chip, u8 reg, u16 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 buf[3];
+	int ret = 0;
+
+	buf[0] = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	buf[1] = (u8)(data >> 8);
+	buf[2] = (u8)(data & 0xFF);
+
+	ret = spi_write(spi_dev, buf, 3);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write word error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int adt7310_spi_read_byte(struct adt7310_chip_info *chip, u8 reg, u8 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 command = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	int ret = 0;
+
+	command |= ADT7310_CMD_READ;
+	ret = spi_write(spi_dev, &command, sizeof(command));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi_dev, data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read byte error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int adt7310_spi_write_byte(struct adt7310_chip_info *chip, u8 reg, u8 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 buf[2];
+	int ret = 0;
+
+	buf[0] = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	buf[1] = data;
+
+	ret = spi_write(spi_dev, buf, 2);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write byte error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static ssize_t adt7310_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u8 config;
+
+	config = chip->config & ADT7310_MODE_MASK;
+
+	switch (config) {
+	case ADT7310_PD:
+		return sprintf(buf, "power-down\n");
+	case ADT7310_ONESHOT:
+		return sprintf(buf, "one-shot\n");
+	case ADT7310_SPS:
+		return sprintf(buf, "sps\n");
+	default:
+		return sprintf(buf, "full\n");
+	}
+}
+
+static ssize_t adt7310_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7310_MODE_MASK);
+	if (strcmp(buf, "power-down"))
+		config |= ADT7310_PD;
+	else if (strcmp(buf, "one-shot"))
+		config |= ADT7310_ONESHOT;
+	else if (strcmp(buf, "sps"))
+		config |= ADT7310_SPS;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt7310_show_mode,
+		adt7310_store_mode,
+		0);
+
+static ssize_t adt7310_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7310_show_available_modes, NULL, 0);
+
+static ssize_t adt7310_show_resolution(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+	int bits;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7310_RESOLUTION)
+		bits = 16;
+	else
+		bits = 13;
+
+	return sprintf(buf, "%d bits\n", bits);
+}
+
+static ssize_t adt7310_store_resolution(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u16 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7310_RESOLUTION);
+	if (data)
+		config |= ADT7310_RESOLUTION;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
+		adt7310_show_resolution,
+		adt7310_store_resolution,
+		0);
+
+static ssize_t adt7310_show_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u8 id;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
+			id & ADT7310_DEVICE_ID_MASK,
+			(id & ADT7310_MANUFACTORY_ID_MASK) >> ADT7310_MANUFACTORY_ID_OFFSET);
+}
+
+static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
+		adt7310_show_id,
+		NULL,
+		0);
+
+static ssize_t adt7310_convert_temperature(struct adt7310_chip_info *chip,
+		u16 data, char *buf)
+{
+	char sign = ' ';
+
+	if (chip->config & ADT7310_RESOLUTION) {
+		if (data & ADT7310_T16_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data = (u16)((ADT7310_T16_VALUE_SIGN << 1) - (u32)data);
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.7d\n", sign,
+				(data >> ADT7310_T16_VALUE_FLOAT_OFFSET),
+				(data & ADT7310_T16_VALUE_FLOAT_MASK) * 78125);
+	} else {
+		if (data & ADT7310_T13_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data >>= ADT7310_T13_VALUE_OFFSET;
+			data = (ADT7310_T13_VALUE_SIGN << 1) - data;
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.4d\n", sign,
+				(data >> ADT7310_T13_VALUE_FLOAT_OFFSET),
+				(data & ADT7310_T13_VALUE_FLOAT_MASK) * 625);
+	}
+}
+
+static ssize_t adt7310_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u8 status;
+	u16 data;
+	int ret, i = 0;
+
+	do {
+		ret = adt7310_spi_read_byte(chip, ADT7310_STATUS, &status);
+		if (ret)
+			return -EIO;
+		i++;
+		if (i == 10000)
+			return -EIO;
+	} while (status & ADT7310_STAT_NOT_RDY);
+
+	ret = adt7310_spi_read_word(chip, ADT7310_TEMPERATURE, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7310_convert_temperature(chip, data, buf);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, adt7310_show_value, NULL, 0);
+
+static ssize_t adt7310_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt7310_show_name, NULL, 0);
+
+static struct attribute *adt7310_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_resolution.dev_attr.attr,
+	&iio_dev_attr_id.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt7310_attribute_group = {
+	.attrs = adt7310_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT7310_ABOVE_ALARM    IIO_BUFFER_EVENT_CODE(0)
+#define IIO_EVENT_CODE_ADT7310_BELLOW_ALARM   IIO_BUFFER_EVENT_CODE(1)
+#define IIO_EVENT_CODE_ADT7310_ABOVE_CRIT     IIO_BUFFER_EVENT_CODE(2)
+
+static void adt7310_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt7310_chip_info *chip =
+		container_of(work_s, struct adt7310_chip_info, thresh_work);
+	u8 status;
+
+	if (adt7310_spi_read_byte(chip, ADT7310_STATUS, &status))
+		return;
+
+	if (status & ADT7310_STAT_T_HIGH)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7310_ABOVE_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7310_STAT_T_LOW)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7310_BELLOW_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7310_STAT_T_CRIT)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7310_ABOVE_CRIT,
+			chip->last_timestamp);
+}
+
+static int adt7310_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt7310, &adt7310_interrupt);
+IIO_EVENT_SH(adt7310_ct, &adt7310_interrupt);
+
+static ssize_t adt7310_show_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7310_EVENT_MODE)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t adt7310_set_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config &= ~ADT7310_EVENT_MODE;
+	if (strcmp(buf, "comparator") != 0)
+		config |= ADT7310_EVENT_MODE;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static ssize_t adt7310_show_available_event_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t adt7310_show_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", chip->config & ADT7310_FAULT_QUEUE_MASK);
+}
+
+static ssize_t adt7310_set_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > 3)
+		return -EINVAL;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT7310_FAULT_QUEUE_MASK;
+	config |= data;
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static inline ssize_t adt7310_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	int ret;
+
+	ret = adt7310_spi_read_word(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7310_convert_temperature(chip, data, buf);
+}
+
+static inline ssize_t adt7310_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+
+		if (chip->config & ADT7310_RESOLUTION) {
+			if (len > ADT7310_T16_VALUE_FLOAT_OFFSET)
+				len = ADT7310_T16_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 78125) * 78125;
+		} else {
+			if (len > ADT7310_T13_VALUE_FLOAT_OFFSET)
+				len = ADT7310_T13_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 625) * 625;
+		}
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+
+	if (chip->config & ADT7310_RESOLUTION) {
+		data = (data << ADT7310_T16_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7310_T16_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (u16)((ADT7310_T16_VALUE_SIGN << 1) - (u32)data);
+	} else {
+		data = (data << ADT7310_T13_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7310_T13_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (ADT7310_T13_VALUE_SIGN << 1) - data;
+		data <<= ADT7310_T13_VALUE_OFFSET;
+	}
+
+	ret = adt7310_spi_write_word(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static ssize_t adt7310_show_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7310_show_t_bound(dev, attr,
+			ADT7310_T_ALARM_HIGH, buf);
+}
+
+static inline ssize_t adt7310_set_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7310_set_t_bound(dev, attr,
+			ADT7310_T_ALARM_HIGH, buf, len);
+}
+
+static ssize_t adt7310_show_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7310_show_t_bound(dev, attr,
+			ADT7310_T_ALARM_LOW, buf);
+}
+
+static inline ssize_t adt7310_set_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7310_set_t_bound(dev, attr,
+			ADT7310_T_ALARM_LOW, buf, len);
+}
+
+static ssize_t adt7310_show_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7310_show_t_bound(dev, attr,
+			ADT7310_T_CRIT, buf);
+}
+
+static inline ssize_t adt7310_set_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7310_set_t_bound(dev, attr,
+			ADT7310_T_CRIT, buf, len);
+}
+
+static ssize_t adt7310_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 t_hyst;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_T_HYST, &t_hyst);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", t_hyst & ADT7310_T_HYST_MASK);
+}
+
+static inline ssize_t adt7310_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+	unsigned long data;
+	u8 t_hyst;
+
+	ret = strict_strtol(buf, 10, &data);
+
+	if (ret || data > ADT7310_T_HYST_MASK)
+		return -EINVAL;
+
+	t_hyst = (u8)data;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_T_HYST, t_hyst);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7310,
+		adt7310_show_event_mode, adt7310_set_event_mode, 0);
+IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7310,
+		adt7310_show_available_event_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(fault_queue, iio_event_adt7310,
+		adt7310_show_fault_queue, adt7310_set_fault_queue, 0);
+IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7310,
+		adt7310_show_t_alarm_high, adt7310_set_t_alarm_high, 0);
+IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7310,
+		adt7310_show_t_alarm_low, adt7310_set_t_alarm_low, 0);
+IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7310_ct,
+		adt7310_show_t_crit, adt7310_set_t_crit, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7310,
+		adt7310_show_t_hyst, adt7310_set_t_hyst, 0);
+
+static struct attribute *adt7310_event_int_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_alarm_high.dev_attr.attr,
+	&iio_event_attr_t_alarm_low.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *adt7310_event_ct_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_crit.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt7310_event_attribute_group[ADT7310_IRQS] = {
+	{
+		.attrs = adt7310_event_int_attributes,
+	},
+	{
+		.attrs = adt7310_event_ct_attributes,
+	}
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt7310_probe(struct spi_device *spi_dev)
+{
+	struct adt7310_chip_info *chip;
+	int ret = 0;
+	unsigned long *adt7310_platform_data = spi_dev->dev.platform_data;
+	unsigned long irq_flags;
+
+	chip = kzalloc(sizeof(struct adt7310_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &adt7310_attribute_group;
+	chip->indio_dev->event_attrs = adt7310_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = ADT7310_IRQS;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	/* CT critcal temperature event. line 0 */
+	if (spi_dev->irq) {
+		if (adt7310_platform_data[2])
+			irq_flags = adt7310_platform_data[2];
+		else
+			irq_flags = IRQF_TRIGGER_LOW;
+		ret = iio_register_interrupt_line(spi_dev->irq,
+				chip->indio_dev,
+				0,
+				irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7310.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7310,
+				&chip->indio_dev->interrupts[0]->ev_list);
+	}
+
+	/* INT bound temperature alarm event. line 1 */
+	if (adt7310_platform_data[0]) {
+		ret = iio_register_interrupt_line(adt7310_platform_data[0],
+				chip->indio_dev,
+				1,
+				adt7310_platform_data[1],
+				chip->name);
+		if (ret)
+			goto error_unreg_ct_irq;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7310.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7310_ct,
+				&chip->indio_dev->interrupts[1]->ev_list);
+	}
+
+	if (spi_dev->irq && adt7310_platform_data[0]) {
+		INIT_WORK(&chip->thresh_work, adt7310_interrupt_bh);
+
+		ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+
+		/* set irq polarity low level */
+		chip->config &= ~ADT7310_CT_POLARITY;
+
+		if (adt7310_platform_data[1] & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT7310_INT_POLARITY;
+		else
+			chip->config &= ~ADT7310_INT_POLARITY;
+
+		ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+	}
+
+	dev_info(&spi_dev->dev, "%s temperature sensor registered.\n",
+			chip->name);
+
+	return 0;
+
+error_unreg_int_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 1);
+error_unreg_ct_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adt7310_remove(struct spi_device *spi_dev)
+{
+	struct adt7310_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+	unsigned long *adt7310_platform_data = spi_dev->dev.platform_data;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	if (adt7310_platform_data[0])
+		iio_unregister_interrupt_line(indio_dev, 1);
+	if (spi_dev->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id adt7310_id[] = {
+	{ "adt7310", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, adt7310_id);
+
+static struct spi_driver adt7310_driver = {
+	.driver = {
+		.name = "adt7310",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = adt7310_probe,
+	.remove = __devexit_p(adt7310_remove),
+	.id_table = adt7310_id,
+};
+
+static __init int adt7310_init(void)
+{
+	return spi_register_driver(&adt7310_driver);
+}
+
+static __exit void adt7310_exit(void)
+{
+	spi_unregister_driver(&adt7310_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT7310 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt7310_init);
+module_exit(adt7310_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 17/36] staging: iio: adc: new driver for ADT7410 temperature sensors
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (15 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 16/36] staging: iio: adc: new driver for ADT7310 " Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 18/36] staging: iio: gyro: new driver for ADIS16251 devices Mike Frysinger
                   ` (19 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Sonic Zhang, Michael Hennerich

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig   |    7 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/adt7410.c |  915 +++++++++++++++++++++++++++++++++++++
 3 files changed, 923 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/adt7410.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index cd35526..9ca6565 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -127,3 +127,10 @@ config ADT7310
 	help
 	  Say yes here to build support for Analog Devices ADT7310
 	  temperature sensors.
+
+config ADT7410
+	tristate "Analog Devices ADT7410 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices ADT7410
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 6067b66..a7dce6b 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_AD7745) += ad7745.o
 obj-$(CONFIG_AD7816) += ad7816.o
 obj-$(CONFIG_ADT75) += adt75.o
 obj-$(CONFIG_ADT7310) += adt7310.o
+obj-$(CONFIG_ADT7410) += adt7410.o
diff --git a/drivers/staging/iio/adc/adt7410.c b/drivers/staging/iio/adc/adt7410.c
new file mode 100644
index 0000000..c345f27
--- /dev/null
+++ b/drivers/staging/iio/adc/adt7410.c
@@ -0,0 +1,915 @@
+/*
+ * ADT7410 digital temperature sensor driver supporting ADT7410
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * ADT7410 registers definition
+ */
+
+#define ADT7410_TEMPERATURE		0
+#define ADT7410_STATUS			2
+#define ADT7410_CONFIG			3
+#define ADT7410_T_ALARM_HIGH		4
+#define ADT7410_T_ALARM_LOW		6
+#define ADT7410_T_CRIT			8
+#define ADT7410_T_HYST			0xA
+#define ADT7410_ID			0xB
+#define ADT7410_RESET			0x2F
+
+/*
+ * ADT7410 status
+ */
+#define ADT7410_STAT_T_LOW		0x10
+#define ADT7410_STAT_T_HIGH		0x20
+#define ADT7410_STAT_T_CRIT		0x40
+#define ADT7410_STAT_NOT_RDY		0x80
+
+/*
+ * ADT7410 config
+ */
+#define ADT7410_FAULT_QUEUE_MASK	0x3
+#define ADT7410_CT_POLARITY		0x4
+#define ADT7410_INT_POLARITY		0x8
+#define ADT7410_EVENT_MODE		0x10
+#define ADT7410_MODE_MASK		0x60
+#define ADT7410_ONESHOT			0x20
+#define ADT7410_SPS			0x40
+#define ADT7410_PD			0x60
+#define ADT7410_RESOLUTION		0x80
+
+/*
+ * ADT7410 masks
+ */
+#define ADT7410_T16_VALUE_SIGN			0x8000
+#define ADT7410_T16_VALUE_FLOAT_OFFSET		7
+#define ADT7410_T16_VALUE_FLOAT_MASK		0x7F
+#define ADT7410_T13_VALUE_SIGN			0x1000
+#define ADT7410_T13_VALUE_OFFSET		3
+#define ADT7410_T13_VALUE_FLOAT_OFFSET		4
+#define ADT7410_T13_VALUE_FLOAT_MASK		0xF
+#define ADT7410_T_HYST_MASK			0xF
+#define ADT7410_DEVICE_ID_MASK			0xF
+#define ADT7410_MANUFACTORY_ID_MASK		0xF0
+#define ADT7410_MANUFACTORY_ID_OFFSET		4
+
+#define ADT7410_IRQS				2
+
+/*
+ * struct adt7410_chip_info - chip specifc information
+ */
+
+struct adt7410_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u8  config;
+};
+
+/*
+ * adt7410 register access by I2C
+ */
+
+static int adt7410_i2c_read_word(struct adt7410_chip_info *chip, u8 reg, u16 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_read_word_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	*data = swab16((u16)ret);
+
+	return 0;
+}
+
+static int adt7410_i2c_write_word(struct adt7410_chip_info *chip, u8 reg, u16 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static int adt7410_i2c_read_byte(struct adt7410_chip_info *chip, u8 reg, u8 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	*data = (u8)ret;
+
+	return 0;
+}
+
+static int adt7410_i2c_write_byte(struct adt7410_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static ssize_t adt7410_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u8 config;
+
+	config = chip->config & ADT7410_MODE_MASK;
+
+	switch (config) {
+	case ADT7410_PD:
+		return sprintf(buf, "power-down\n");
+	case ADT7410_ONESHOT:
+		return sprintf(buf, "one-shot\n");
+	case ADT7410_SPS:
+		return sprintf(buf, "sps\n");
+	default:
+		return sprintf(buf, "full\n");
+	}
+}
+
+static ssize_t adt7410_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7410_MODE_MASK);
+	if (strcmp(buf, "power-down"))
+		config |= ADT7410_PD;
+	else if (strcmp(buf, "one-shot"))
+		config |= ADT7410_ONESHOT;
+	else if (strcmp(buf, "sps"))
+		config |= ADT7410_SPS;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt7410_show_mode,
+		adt7410_store_mode,
+		0);
+
+static ssize_t adt7410_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7410_show_available_modes, NULL, 0);
+
+static ssize_t adt7410_show_resolution(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+	int bits;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7410_RESOLUTION)
+		bits = 16;
+	else
+		bits = 13;
+
+	return sprintf(buf, "%d bits\n", bits);
+}
+
+static ssize_t adt7410_store_resolution(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u16 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7410_RESOLUTION);
+	if (data)
+		config |= ADT7410_RESOLUTION;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
+		adt7410_show_resolution,
+		adt7410_store_resolution,
+		0);
+
+static ssize_t adt7410_show_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u8 id;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
+			id & ADT7410_DEVICE_ID_MASK,
+			(id & ADT7410_MANUFACTORY_ID_MASK) >> ADT7410_MANUFACTORY_ID_OFFSET);
+}
+
+static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
+		adt7410_show_id,
+		NULL,
+		0);
+
+static ssize_t adt7410_convert_temperature(struct adt7410_chip_info *chip,
+		u16 data, char *buf)
+{
+	char sign = ' ';
+
+	if (chip->config & ADT7410_RESOLUTION) {
+		if (data & ADT7410_T16_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.7d\n", sign,
+				(data >> ADT7410_T16_VALUE_FLOAT_OFFSET),
+				(data & ADT7410_T16_VALUE_FLOAT_MASK) * 78125);
+	} else {
+		if (data & ADT7410_T13_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data >>= ADT7410_T13_VALUE_OFFSET;
+			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.4d\n", sign,
+				(data >> ADT7410_T13_VALUE_FLOAT_OFFSET),
+				(data & ADT7410_T13_VALUE_FLOAT_MASK) * 625);
+	}
+}
+
+static ssize_t adt7410_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u8 status;
+	u16 data;
+	int ret, i = 0;
+
+	do {
+		ret = adt7410_i2c_read_byte(chip, ADT7410_STATUS, &status);
+		if (ret)
+			return -EIO;
+		i++;
+		if (i == 10000)
+			return -EIO;
+	} while (status & ADT7410_STAT_NOT_RDY);
+
+	ret = adt7410_i2c_read_word(chip, ADT7410_TEMPERATURE, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7410_convert_temperature(chip, data, buf);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, adt7410_show_value, NULL, 0);
+
+static ssize_t adt7410_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt7410_show_name, NULL, 0);
+
+static struct attribute *adt7410_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_resolution.dev_attr.attr,
+	&iio_dev_attr_id.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt7410_attribute_group = {
+	.attrs = adt7410_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT7410_ABOVE_ALARM    IIO_BUFFER_EVENT_CODE(0)
+#define IIO_EVENT_CODE_ADT7410_BELLOW_ALARM   IIO_BUFFER_EVENT_CODE(1)
+#define IIO_EVENT_CODE_ADT7410_ABOVE_CRIT     IIO_BUFFER_EVENT_CODE(2)
+
+static void adt7410_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt7410_chip_info *chip =
+		container_of(work_s, struct adt7410_chip_info, thresh_work);
+	u8 status;
+
+	if (adt7410_i2c_read_byte(chip, ADT7410_STATUS, &status))
+		return;
+
+	enable_irq(chip->client->irq);
+
+	if (status & ADT7410_STAT_T_HIGH)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7410_ABOVE_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7410_STAT_T_LOW)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7410_BELLOW_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7410_STAT_T_CRIT)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7410_ABOVE_CRIT,
+			chip->last_timestamp);
+}
+
+static int adt7410_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt7410, &adt7410_interrupt);
+IIO_EVENT_SH(adt7410_ct, &adt7410_interrupt);
+
+static ssize_t adt7410_show_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7410_EVENT_MODE)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t adt7410_set_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config &= ~ADT7410_EVENT_MODE;
+	if (strcmp(buf, "comparator") != 0)
+		config |= ADT7410_EVENT_MODE;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt7410_show_available_event_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t adt7410_show_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", chip->config & ADT7410_FAULT_QUEUE_MASK);
+}
+
+static ssize_t adt7410_set_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > 3)
+		return -EINVAL;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT7410_FAULT_QUEUE_MASK;
+	config |= data;
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static inline ssize_t adt7410_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	int ret;
+
+	ret = adt7410_i2c_read_word(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7410_convert_temperature(chip, data, buf);
+}
+
+static inline ssize_t adt7410_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+
+		if (chip->config & ADT7410_RESOLUTION) {
+			if (len > ADT7410_T16_VALUE_FLOAT_OFFSET)
+				len = ADT7410_T16_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 78125) * 78125;
+		} else {
+			if (len > ADT7410_T13_VALUE_FLOAT_OFFSET)
+				len = ADT7410_T13_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 625) * 625;
+		}
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+
+	if (chip->config & ADT7410_RESOLUTION) {
+		data = (data << ADT7410_T16_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7410_T16_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
+	} else {
+		data = (data << ADT7410_T13_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7410_T13_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
+		data <<= ADT7410_T13_VALUE_OFFSET;
+	}
+
+	ret = adt7410_i2c_write_word(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t adt7410_show_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7410_show_t_bound(dev, attr,
+			ADT7410_T_ALARM_HIGH, buf);
+}
+
+static inline ssize_t adt7410_set_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7410_set_t_bound(dev, attr,
+			ADT7410_T_ALARM_HIGH, buf, len);
+}
+
+static ssize_t adt7410_show_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7410_show_t_bound(dev, attr,
+			ADT7410_T_ALARM_LOW, buf);
+}
+
+static inline ssize_t adt7410_set_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7410_set_t_bound(dev, attr,
+			ADT7410_T_ALARM_LOW, buf, len);
+}
+
+static ssize_t adt7410_show_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7410_show_t_bound(dev, attr,
+			ADT7410_T_CRIT, buf);
+}
+
+static inline ssize_t adt7410_set_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7410_set_t_bound(dev, attr,
+			ADT7410_T_CRIT, buf, len);
+}
+
+static ssize_t adt7410_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 t_hyst;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_T_HYST, &t_hyst);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", t_hyst & ADT7410_T_HYST_MASK);
+}
+
+static inline ssize_t adt7410_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+	unsigned long data;
+	u8 t_hyst;
+
+	ret = strict_strtol(buf, 10, &data);
+
+	if (ret || data > ADT7410_T_HYST_MASK)
+		return -EINVAL;
+
+	t_hyst = (u8)data;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_T_HYST, t_hyst);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7410,
+		adt7410_show_event_mode, adt7410_set_event_mode, 0);
+IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7410,
+		adt7410_show_available_event_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(fault_queue, iio_event_adt7410,
+		adt7410_show_fault_queue, adt7410_set_fault_queue, 0);
+IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7410,
+		adt7410_show_t_alarm_high, adt7410_set_t_alarm_high, 0);
+IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7410,
+		adt7410_show_t_alarm_low, adt7410_set_t_alarm_low, 0);
+IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7410_ct,
+		adt7410_show_t_crit, adt7410_set_t_crit, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7410,
+		adt7410_show_t_hyst, adt7410_set_t_hyst, 0);
+
+static struct attribute *adt7410_event_int_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_alarm_high.dev_attr.attr,
+	&iio_event_attr_t_alarm_low.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *adt7410_event_ct_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_crit.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt7410_event_attribute_group[ADT7410_IRQS] = {
+	{
+		.attrs = adt7410_event_int_attributes,
+	},
+	{
+		.attrs = adt7410_event_ct_attributes,
+	}
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt7410_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct adt7410_chip_info *chip;
+	int ret = 0;
+	unsigned long *adt7410_platform_data = client->dev.platform_data;
+
+	chip = kzalloc(sizeof(struct adt7410_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &adt7410_attribute_group;
+	chip->indio_dev->event_attrs = adt7410_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = ADT7410_IRQS;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	/* CT critcal temperature event. line 0 */
+	if (client->irq) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_LOW,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7410.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7410,
+				&chip->indio_dev->interrupts[0]->ev_list);
+	}
+
+	/* INT bound temperature alarm event. line 1 */
+	if (adt7410_platform_data[0]) {
+		ret = iio_register_interrupt_line(adt7410_platform_data[0],
+				chip->indio_dev,
+				1,
+				adt7410_platform_data[1],
+				chip->name);
+		if (ret)
+			goto error_unreg_ct_irq;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7410.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7410_ct,
+				&chip->indio_dev->interrupts[1]->ev_list);
+	}
+
+	if (client->irq && adt7410_platform_data[0]) {
+		INIT_WORK(&chip->thresh_work, adt7410_interrupt_bh);
+
+		ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+
+		/* set irq polarity low level */
+		chip->config &= ~ADT7410_CT_POLARITY;
+
+		if (adt7410_platform_data[1] & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT7410_INT_POLARITY;
+		else
+			chip->config &= ~ADT7410_INT_POLARITY;
+
+		ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+
+error_unreg_int_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 1);
+error_unreg_ct_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adt7410_remove(struct i2c_client *client)
+{
+	struct adt7410_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+	unsigned long *adt7410_platform_data = client->dev.platform_data;
+
+	if (adt7410_platform_data[0])
+		iio_unregister_interrupt_line(indio_dev, 1);
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id adt7410_id[] = {
+	{ "adt7410", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, adt7410_id);
+
+static struct i2c_driver adt7410_driver = {
+	.driver = {
+		.name = "adt7410",
+	},
+	.probe = adt7410_probe,
+	.remove = __devexit_p(adt7410_remove),
+	.id_table = adt7410_id,
+};
+
+static __init int adt7410_init(void)
+{
+	return i2c_add_driver(&adt7410_driver);
+}
+
+static __exit void adt7410_exit(void)
+{
+	i2c_del_driver(&adt7410_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT7410 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt7410_init);
+module_exit(adt7410_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 18/36] staging: iio: gyro: new driver for ADIS16251 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (16 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 17/36] staging: iio: adc: new driver for ADT7410 " Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 19/36] staging: iio: gyro: new driver for ADIS16060 digital output gyros Mike Frysinger
                   ` (18 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/gyro/Kconfig          |   10 +
 drivers/staging/iio/gyro/Makefile         |    3 +
 drivers/staging/iio/gyro/adis16251.h      |  185 +++++++
 drivers/staging/iio/gyro/adis16251_core.c |  777 +++++++++++++++++++++++++++++
 drivers/staging/iio/sysfs.h               |    6 +
 5 files changed, 981 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/gyro/adis16251.h
 create mode 100644 drivers/staging/iio/gyro/adis16251_core.c

diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
index d1dde82..c7a7d29 100644
--- a/drivers/staging/iio/gyro/Kconfig
+++ b/drivers/staging/iio/gyro/Kconfig
@@ -14,3 +14,13 @@ config ADIS16260
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called adis16260.
+
+config ADIS16251
+	tristate "Analog Devices ADIS16251 Digital Gyroscope Sensor SPI driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices adis16261 programmable
+	  digital gyroscope sensor.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called adis16251.
diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile
index b5f0dc0..8a592d8 100644
--- a/drivers/staging/iio/gyro/Makefile
+++ b/drivers/staging/iio/gyro/Makefile
@@ -5,3 +5,6 @@
 adis16260-y             := adis16260_core.o
 adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o
 obj-$(CONFIG_ADIS16260) += adis16260.o
+
+adis16251-y             := adis16251_core.o
+obj-$(CONFIG_ADIS16251) += adis16251.o
diff --git a/drivers/staging/iio/gyro/adis16251.h b/drivers/staging/iio/gyro/adis16251.h
new file mode 100644
index 0000000..999db49
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16251.h
@@ -0,0 +1,185 @@
+#ifndef SPI_ADIS16251_H_
+#define SPI_ADIS16251_H_
+
+#define ADIS16251_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16251_READ_REG(a)    a
+#define ADIS16251_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16251_ENDURANCE  0x00 /* Flash memory write count */
+#define ADIS16251_SUPPLY_OUT 0x02 /* Power supply measurement */
+#define ADIS16251_GYRO_OUT   0x04 /* X-axis gyroscope output */
+#define ADIS16251_AUX_ADC    0x0A /* analog input channel measurement */
+#define ADIS16251_TEMP_OUT   0x0C /* internal temperature measurement */
+#define ADIS16251_ANGL_OUT   0x0E /* angle displacement */
+#define ADIS16251_GYRO_OFF   0x14 /* Calibration, offset/bias adjustment */
+#define ADIS16251_GYRO_SCALE 0x16 /* Calibration, scale adjustment */
+#define ADIS16251_ALM_MAG1   0x20 /* Alarm 1 magnitude/polarity setting */
+#define ADIS16251_ALM_MAG2   0x22 /* Alarm 2 magnitude/polarity setting */
+#define ADIS16251_ALM_SMPL1  0x24 /* Alarm 1 dynamic rate of change setting */
+#define ADIS16251_ALM_SMPL2  0x26 /* Alarm 2 dynamic rate of change setting */
+#define ADIS16251_ALM_CTRL   0x28 /* Alarm control */
+#define ADIS16251_AUX_DAC    0x30 /* Auxiliary DAC data */
+#define ADIS16251_GPIO_CTRL  0x32 /* Control, digital I/O line */
+#define ADIS16251_MSC_CTRL   0x34 /* Control, data ready, self-test settings */
+#define ADIS16251_SMPL_PRD   0x36 /* Control, internal sample rate */
+#define ADIS16251_SENS_AVG   0x38 /* Control, dynamic range, filtering */
+#define ADIS16251_SLP_CNT    0x3A /* Control, sleep mode initiation */
+#define ADIS16251_DIAG_STAT  0x3C /* Diagnostic, error flags */
+#define ADIS16251_GLOB_CMD   0x3E /* Control, global commands */
+
+#define ADIS16251_ERROR_ACTIVE			(1<<14)
+#define ADIS16251_NEW_DATA			(1<<14)
+
+/* MSC_CTRL */
+#define ADIS16251_MSC_CTRL_INT_SELF_TEST	(1<<10) /* Internal self-test enable */
+#define ADIS16251_MSC_CTRL_NEG_SELF_TEST	(1<<9)
+#define ADIS16251_MSC_CTRL_POS_SELF_TEST	(1<<8)
+#define ADIS16251_MSC_CTRL_DATA_RDY_EN		(1<<2)
+#define ADIS16251_MSC_CTRL_DATA_RDY_POL_HIGH	(1<<1)
+#define ADIS16251_MSC_CTRL_DATA_RDY_DIO2	(1<<0)
+
+/* SMPL_PRD */
+#define ADIS16251_SMPL_PRD_TIME_BASE	(1<<7) /* Time base (tB): 0 = 1.953 ms, 1 = 60.54 ms */
+#define ADIS16251_SMPL_PRD_DIV_MASK	0x7F
+
+/* SLP_CNT */
+#define ADIS16251_SLP_CNT_POWER_OFF     0x80
+
+/* DIAG_STAT */
+#define ADIS16251_DIAG_STAT_ALARM2	(1<<9)
+#define ADIS16251_DIAG_STAT_ALARM1	(1<<8)
+#define ADIS16251_DIAG_STAT_SELF_TEST	(1<<5)
+#define ADIS16251_DIAG_STAT_OVERFLOW	(1<<4)
+#define ADIS16251_DIAG_STAT_SPI_FAIL	(1<<3)
+#define ADIS16251_DIAG_STAT_FLASH_UPT	(1<<2)
+#define ADIS16251_DIAG_STAT_POWER_HIGH	(1<<1)
+#define ADIS16251_DIAG_STAT_POWER_LOW	(1<<0)
+
+#define ADIS16251_DIAG_STAT_ERR_MASK (ADIS16261_DIAG_STAT_ALARM2 | \
+				      ADIS16261_DIAG_STAT_ALARM1 | \
+				      ADIS16261_DIAG_STAT_SELF_TEST | \
+				      ADIS16261_DIAG_STAT_OVERFLOW | \
+				      ADIS16261_DIAG_STAT_SPI_FAIL | \
+				      ADIS16261_DIAG_STAT_FLASH_UPT | \
+				      ADIS16261_DIAG_STAT_POWER_HIGH | \
+				      ADIS16261_DIAG_STAT_POWER_LOW)
+
+/* GLOB_CMD */
+#define ADIS16251_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16251_GLOB_CMD_FLASH_UPD	(1<<3)
+#define ADIS16251_GLOB_CMD_DAC_LATCH	(1<<2)
+#define ADIS16251_GLOB_CMD_FAC_CALIB	(1<<1)
+#define ADIS16251_GLOB_CMD_AUTO_NULL	(1<<0)
+
+#define ADIS16251_MAX_TX 24
+#define ADIS16251_MAX_RX 24
+
+#define ADIS16251_SPI_SLOW	(u32)(300 * 1000)
+#define ADIS16251_SPI_BURST	(u32)(1000 * 1000)
+#define ADIS16251_SPI_FAST	(u32)(2000 * 1000)
+
+/**
+ * struct adis16251_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct adis16251_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16251_spi_write_reg_8(struct device *dev,
+			      u8 reg_address,
+			      u8 val);
+
+int adis16251_spi_read_burst(struct device *dev, u8 *rx);
+
+int adis16251_spi_read_sequence(struct device *dev,
+				      u8 *tx, u8 *rx, int num);
+
+int adis16251_set_irq(struct device *dev, bool enable);
+
+int adis16251_reset(struct device *dev);
+
+int adis16251_stop_device(struct device *dev);
+
+int adis16251_check_status(struct device *dev);
+
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum adis16251_scan {
+	ADIS16251_SCAN_SUPPLY,
+	ADIS16251_SCAN_GYRO,
+	ADIS16251_SCAN_TEMP,
+	ADIS16251_SCAN_ADC_0,
+};
+
+void adis16251_remove_trigger(struct iio_dev *indio_dev);
+int adis16251_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16251_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+
+int adis16251_configure_ring(struct iio_dev *indio_dev);
+void adis16251_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16251_initialize_ring(struct iio_ring_buffer *ring);
+void adis16251_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16251_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16251_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16251_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16251_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16251_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16251_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16251_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16251_H_ */
diff --git a/drivers/staging/iio/gyro/adis16251_core.c b/drivers/staging/iio/gyro/adis16251_core.c
new file mode 100644
index 0000000..a0d400f
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16251_core.c
@@ -0,0 +1,777 @@
+/*
+ * ADIS16251 Programmable Digital Gyroscope Sensor Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16251.h"
+
+#define DRIVER_NAME		"adis16251"
+
+/* At the moment the spi framework doesn't allow global setting of cs_change.
+ * It's in the likely to be added comment at the top of spi.h.
+ * This means that use cannot be made of spi_write etc.
+ */
+
+/**
+ * adis16251_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+int adis16251_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16251_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16251_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16251_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16251_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16251_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16251_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16251_WRITE_REG(lower_reg_address + 1);
+	st->tx[3] = (value >> 8) & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16251_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16251_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16251_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16251_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+	st->tx[3] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+/**
+ * adis16251_spi_read_burst() - read all data registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read (min size is 24 bytes)
+ **/
+int adis16251_spi_read_burst(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16251_state *st = iio_dev_get_devdata(indio_dev);
+	u32 old_speed_hz = st->us->max_speed_hz;
+	int ret;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 0,
+		}, {
+			.rx_buf = rx,
+			.bits_per_word = 8,
+			.len = 24,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16251_READ_REG(ADIS16251_GLOB_CMD);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+
+	st->us->max_speed_hz = min(ADIS16251_SPI_BURST, old_speed_hz);
+	spi_setup(st->us);
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	st->us->max_speed_hz = old_speed_hz;
+	spi_setup(st->us);
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+/**
+ * adis16251_spi_read_sequence() - read a sequence of 16-bit registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @tx: register addresses in bytes 0,2,4,6... (min size is 2*num bytes)
+ * @rx: somewhere to pass back the value read (min size is 2*num bytes)
+ **/
+int adis16251_spi_read_sequence(struct device *dev,
+		u8 *tx, u8 *rx, int num)
+{
+	struct spi_message msg;
+	struct spi_transfer *xfers;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16251_state *st = iio_dev_get_devdata(indio_dev);
+	int ret, i;
+
+	xfers = kzalloc(num + 1, GFP_KERNEL);
+	if (xfers == NULL) {
+		dev_err(&st->us->dev, "memory alloc failed");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* tx: |add1|addr2|addr3|...|addrN |zero|
+	 * rx: |zero|res1 |res2 |...|resN-1|resN| */
+	spi_message_init(&msg);
+	for (i = 0; i < num + 1; i++) {
+		if (i > 0)
+			xfers[i].rx_buf = st->rx + 2*(i - 1);
+		if (i < num)
+			xfers[i].tx_buf = st->tx + 2*i;
+		xfers[i].bits_per_word = 8;
+		xfers[i].len = 2;
+		xfers[i].cs_change = 1;
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	mutex_lock(&st->buf_lock);
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when reading sequence");
+
+	mutex_unlock(&st->buf_lock);
+	kfree(xfers);
+
+error_ret:
+	return ret;
+}
+
+static ssize_t adis16251_spi_read_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf,
+		unsigned bits)
+{
+	int ret;
+	s16 val = 0;
+	unsigned shift = 16 - bits;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16251_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16251_ERROR_ACTIVE)
+		adis16251_check_status(dev);
+	val = ((s16)(val << shift) >> shift);
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t adis16251_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16251_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16251_ERROR_ACTIVE)
+		adis16251_check_status(dev);
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16251_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+	ret =  adis16251_spi_read_signed(dev, attr, buf, 14);
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16251_read_12bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+	ret =  adis16251_spi_read_signed(dev, attr, buf, 12);
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16251_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = adis16251_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t adis16251_read_frequency(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret, len = 0;
+	u16 t;
+	int sps;
+	ret = adis16251_spi_read_reg_16(dev,
+			ADIS16251_SMPL_PRD,
+			&t);
+	if (ret)
+		return ret;
+	sps =  (t & ADIS16251_SMPL_PRD_TIME_BASE) ? 8 : 256;
+	sps /= (t & ADIS16251_SMPL_PRD_DIV_MASK) + 1;
+	len = sprintf(buf, "%d SPS\n", sps);
+	return len;
+}
+
+static ssize_t adis16251_write_frequency(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16251_state *st = iio_dev_get_devdata(indio_dev);
+	long val;
+	int ret;
+	u8 t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	t = (256 / val);
+	if (t > 0)
+		t--;
+	t &= ADIS16251_SMPL_PRD_DIV_MASK;
+	if ((t & ADIS16251_SMPL_PRD_DIV_MASK) >= 0x0A)
+		st->us->max_speed_hz = ADIS16251_SPI_SLOW;
+	else
+		st->us->max_speed_hz = ADIS16251_SPI_FAST;
+
+	ret = adis16251_spi_write_reg_8(dev,
+			ADIS16251_SMPL_PRD,
+			t);
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t adis16251_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -1;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16251_reset(dev);
+	}
+	return -1;
+}
+
+
+
+int adis16251_set_irq(struct device *dev, bool enable)
+{
+	int ret;
+	u16 msc;
+	ret = adis16251_spi_read_reg_16(dev, ADIS16251_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16251_MSC_CTRL_DATA_RDY_POL_HIGH;
+	if (enable)
+		msc |= ADIS16251_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16251_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16251_spi_write_reg_16(dev, ADIS16251_MSC_CTRL, msc);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	return ret;
+}
+
+int adis16251_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16251_spi_write_reg_8(dev,
+			ADIS16251_GLOB_CMD,
+			ADIS16251_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+/* Power down the device */
+int adis16251_stop_device(struct device *dev)
+{
+	int ret;
+	u16 val = ADIS16251_SLP_CNT_POWER_OFF;
+
+	ret = adis16251_spi_write_reg_16(dev, ADIS16251_SLP_CNT, val);
+	if (ret)
+		dev_err(dev, "problem with turning device off: SLP_CNT");
+
+	return ret;
+}
+
+static int adis16251_self_test(struct device *dev)
+{
+	int ret;
+
+	ret = adis16251_spi_write_reg_16(dev,
+			ADIS16251_MSC_CTRL,
+			ADIS16251_MSC_CTRL_INT_SELF_TEST);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	adis16251_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+int adis16251_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16251_spi_read_reg_16(dev, ADIS16251_DIAG_STAT, &status);
+
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+
+	if (!(status & ADIS16251_DIAG_STAT_ERR_MASK)) {
+		ret = 0;
+		goto error_ret;
+	}
+
+	ret = -EFAULT;
+
+	if (status & ADIS16251_DIAG_STAT_ALARM2)
+		dev_err(dev, "Alarm 2 active\n");
+	if (status & ADIS16251_DIAG_STAT_ALARM1)
+		dev_err(dev, "Alarm 1 active\n");
+	if (status & ADIS16251_DIAG_STAT_SELF_TEST)
+		dev_err(dev, "Self test error\n");
+	if (status & ADIS16251_DIAG_STAT_OVERFLOW)
+		dev_err(dev, "Sensor overrange\n");
+	if (status & ADIS16251_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16251_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16251_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 5.25V\n");
+	if (status & ADIS16251_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 4.75V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16251_initial_setup(struct adis16251_state *st)
+{
+	int ret;
+	u16 smp_prd;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* use low spi speed for init */
+	st->us->max_speed_hz = ADIS16251_SPI_SLOW;
+	st->us->mode = SPI_MODE_3;
+	spi_setup(st->us);
+
+	/* Disable IRQ */
+	ret = adis16251_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+
+	/* Read status register to check the result */
+	ret = adis16251_check_status(dev);
+	if (ret) {
+		adis16251_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16251_STARTUP_DELAY);
+		ret = adis16251_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+	/* use high spi speed if possible */
+	ret = adis16251_spi_read_reg_16(dev, ADIS16251_SMPL_PRD, &smp_prd);
+	if (!ret && (smp_prd & ADIS16251_SMPL_PRD_DIV_MASK) < 0x0A) {
+		st->us->max_speed_hz = ADIS16251_SPI_SLOW;
+		spi_setup(st->us);
+	}
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16251_read_12bit_signed,
+		ADIS16251_SUPPLY_OUT);
+static IIO_CONST_ATTR(in0_supply_scale, "0.0018315");
+
+static IIO_DEV_ATTR_GYRO(adis16251_read_14bit_signed,
+		ADIS16251_GYRO_OUT);
+static IIO_DEV_ATTR_GYRO_SCALE(S_IWUSR | S_IRUGO,
+		adis16251_read_12bit_signed,
+		adis16251_write_16bit,
+		ADIS16251_GYRO_SCALE);
+static IIO_DEV_ATTR_GYRO_OFFSET(S_IWUSR | S_IRUGO,
+		adis16251_read_12bit_signed,
+		adis16251_write_16bit,
+		ADIS16251_GYRO_OFF);
+
+static IIO_DEV_ATTR_TEMP_RAW(adis16251_read_12bit_signed);
+static IIO_CONST_ATTR(temp_offset, "25 K");
+static IIO_CONST_ATTR(temp_scale, "0.1453 K");
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(1, aux, adis16251_read_12bit_unsigned,
+		ADIS16251_AUX_ADC);
+static IIO_CONST_ATTR(in1_aux_scale, "0.0006105");
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+		adis16251_read_frequency,
+		adis16251_write_frequency);
+static IIO_DEV_ATTR_ANGL(adis16251_read_14bit_signed,
+		ADIS16251_ANGL_OUT);
+
+static IIO_DEV_ATTR_RESET(adis16251_write_reset);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("0.129 ~ 256");
+
+static IIO_CONST_ATTR(name, "adis16251");
+
+static struct attribute *adis16251_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16251_event_attribute_group = {
+	.attrs = adis16251_event_attributes,
+};
+
+static struct attribute *adis16251_attributes[] = {
+	&iio_dev_attr_in0_supply_raw.dev_attr.attr,
+	&iio_const_attr_in0_supply_scale.dev_attr.attr,
+	&iio_dev_attr_gyro_raw.dev_attr.attr,
+	&iio_dev_attr_gyro_scale.dev_attr.attr,
+	&iio_dev_attr_gyro_offset.dev_attr.attr,
+	&iio_dev_attr_angl_raw.dev_attr.attr,
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_in1_aux_raw.dev_attr.attr,
+	&iio_const_attr_in1_aux_scale.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16251_attribute_group = {
+	.attrs = adis16251_attributes,
+};
+
+static int __devinit adis16251_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16251_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16251_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16251_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16251_event_attribute_group;
+	st->indio_dev->attrs = &adis16251_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16251_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16251_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16251");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16251_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16251_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		adis16251_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16251_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16251_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int adis16251_remove(struct spi_device *spi)
+{
+	int ret;
+	struct adis16251_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	ret = adis16251_stop_device(&(indio_dev->dev));
+	if (ret)
+		goto err_ret;
+
+	flush_scheduled_work();
+
+	adis16251_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16251_uninitialize_ring(indio_dev->ring);
+	adis16251_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+
+err_ret:
+	return ret;
+}
+
+static struct spi_driver adis16251_driver = {
+	.driver = {
+		.name = "adis16251",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16251_probe,
+	.remove = __devexit_p(adis16251_remove),
+};
+
+static __init int adis16251_init(void)
+{
+	return spi_register_driver(&adis16251_driver);
+}
+module_init(adis16251_init);
+
+static __exit void adis16251_exit(void)
+{
+	spi_unregister_driver(&adis16251_driver);
+}
+module_exit(adis16251_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16251 Digital Gyroscope Sensor SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/sysfs.h b/drivers/staging/iio/sysfs.h
index ee91a95..18bdaac 100644
--- a/drivers/staging/iio/sysfs.h
+++ b/drivers/staging/iio/sysfs.h
@@ -108,6 +108,12 @@ struct iio_const_attr {
 	IIO_DEVICE_ATTR(name, S_IRUGO, _show, NULL, 0)
 
 /**
+ * IIO_DEV_ATTR_RESET: resets the device
+ **/
+#define IIO_DEV_ATTR_RESET(_store)			\
+	IIO_DEVICE_ATTR(reset, S_IWUGO, NULL, _store, 0)
+
+/**
  * IIO_CONST_ATTR_NAME - constant identifier
  * @_string: the name
  **/
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 19/36] staging: iio: gyro: new driver for ADIS16060 digital output gyros
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (17 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 18/36] staging: iio: gyro: new driver for ADIS16251 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 20/36] staging: iio: gyro: new driver for ADIS16080 " Mike Frysinger
                   ` (17 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/gyro/Kconfig          |    7 +
 drivers/staging/iio/gyro/Makefile         |    3 +
 drivers/staging/iio/gyro/adis16060.h      |  101 +++++++++
 drivers/staging/iio/gyro/adis16060_core.c |  319 +++++++++++++++++++++++++++++
 4 files changed, 430 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/gyro/adis16060.h
 create mode 100644 drivers/staging/iio/gyro/adis16060_core.c

diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
index c7a7d29..77c0fe8 100644
--- a/drivers/staging/iio/gyro/Kconfig
+++ b/drivers/staging/iio/gyro/Kconfig
@@ -3,6 +3,13 @@
 #
 comment "Digital gyroscope sensors"
 
+config ADIS16060
+	tristate "Analog Devices ADIS16060 Yaw Rate Gyroscope with SPI driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices adis16060 wide bandwidth
+	  yaw rate gyroscope with SPI.
+
 config ADIS16260
 	tristate "Analog Devices ADIS16260 ADIS16265 Digital Gyroscope Sensor SPI driver"
 	depends on SPI
diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile
index 8a592d8..e2c4acb 100644
--- a/drivers/staging/iio/gyro/Makefile
+++ b/drivers/staging/iio/gyro/Makefile
@@ -2,6 +2,9 @@
 # Makefile for digital gyroscope sensor drivers
 #
 
+adis16060-y             := adis16060_core.o
+obj-$(CONFIG_ADIS16060) += adis16060.o
+
 adis16260-y             := adis16260_core.o
 adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o
 obj-$(CONFIG_ADIS16260) += adis16260.o
diff --git a/drivers/staging/iio/gyro/adis16060.h b/drivers/staging/iio/gyro/adis16060.h
new file mode 100644
index 0000000..5c00e53
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16060.h
@@ -0,0 +1,101 @@
+#ifndef SPI_ADIS16060_H_
+#define SPI_ADIS16060_H_
+
+#define ADIS16060_GYRO       0x20 /* Measure Angular Rate (Gyro) */
+#define ADIS16060_SUPPLY_OUT 0x10 /* Measure Temperature */
+#define ADIS16060_AIN2       0x80 /* Measure AIN2 */
+#define ADIS16060_AIN1       0x40 /* Measure AIN1 */
+#define ADIS16060_TEMP_OUT   0x22 /* Set Positive Self-Test and Output for Angular Rate */
+#define ADIS16060_ANGL_OUT   0x21 /* Set Negative Self-Test and Output for Angular Rate */
+
+#define ADIS16060_MAX_TX     3
+#define ADIS16060_MAX_RX     3
+
+/**
+ * struct adis16060_state - device instance specific data
+ * @us_w:			actual spi_device to write data
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct adis16060_state {
+	struct spi_device		*us_w;
+	struct spi_device		*us_r;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum adis16060_scan {
+	ADIS16060_SCAN_GYRO,
+	ADIS16060_SCAN_TEMP,
+	ADIS16060_SCAN_ADC_1,
+	ADIS16060_SCAN_ADC_2,
+};
+
+void adis16060_remove_trigger(struct iio_dev *indio_dev);
+int adis16060_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16060_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+
+int adis16060_configure_ring(struct iio_dev *indio_dev);
+void adis16060_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16060_initialize_ring(struct iio_ring_buffer *ring);
+void adis16060_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16060_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16060_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16060_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16060_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16060_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16060_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16060_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16060_H_ */
diff --git a/drivers/staging/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c
new file mode 100644
index 0000000..fc48aca
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16060_core.c
@@ -0,0 +1,319 @@
+/*
+ * ADIS16060 Wide Bandwidth Yaw Rate Gyroscope with SPI driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16060.h"
+
+#define DRIVER_NAME		"adis16060"
+
+struct adis16060_state *adis16060_st;
+
+int adis16060_spi_write(struct device *dev,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16060_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = 0;
+	st->tx[1] = 0;
+	st->tx[2] = val; /* The last 8 bits clocked in are latched */
+
+	ret = spi_write(st->us_w, st->tx, 3);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+int adis16060_spi_read(struct device *dev,
+		u16 *val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16060_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+
+	ret = spi_read(st->us_r, st->rx, 3);
+
+	/* The internal successive approximation ADC begins the conversion process
+	 * on the falling edge of MSEL1 and starts to place data MSB first on the
+	 * DOUT line at the 6th falling edge of SCLK
+	 */
+	if (ret == 0)
+		*val = ((st->rx[0] & 0x3) << 12) | (st->rx[1] << 4) | ((st->rx[2] >> 4) & 0xF);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static ssize_t adis16060_read(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	u16 val;
+	ssize_t ret;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+	ret =  adis16060_spi_read(dev, &val);
+	mutex_unlock(&indio_dev->mlock);
+
+	if (ret == 0)
+		return sprintf(buf, "%d\n", val);
+	else
+		return ret;
+}
+
+static ssize_t adis16060_write(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 16, &val);
+	if (ret)
+		goto error_ret;
+	ret = adis16060_spi_write(dev, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+#define IIO_DEV_ATTR_IN(_show)				\
+	IIO_DEVICE_ATTR(in, S_IRUGO, _show, NULL, 0)
+
+#define IIO_DEV_ATTR_OUT(_store)				\
+	IIO_DEVICE_ATTR(out, S_IRUGO, NULL, _store, 0)
+
+static IIO_DEV_ATTR_IN(adis16060_read);
+static IIO_DEV_ATTR_OUT(adis16060_write);
+
+static IIO_CONST_ATTR(name, "adis16060");
+
+static struct attribute *adis16060_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16060_event_attribute_group = {
+	.attrs = adis16060_event_attributes,
+};
+
+static struct attribute *adis16060_attributes[] = {
+	&iio_dev_attr_in.dev_attr.attr,
+	&iio_dev_attr_out.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16060_attribute_group = {
+	.attrs = adis16060_attributes,
+};
+
+static int __devinit adis16060_r_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16060_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16060_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16060_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us_r = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16060_event_attribute_group;
+	st->indio_dev->attrs = &adis16060_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16060_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16060_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16060");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16060_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	adis16060_st = st;
+	return 0;
+
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16060_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16060_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int adis16060_r_remove(struct spi_device *spi)
+{
+	struct adis16060_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16060_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16060_uninitialize_ring(indio_dev->ring);
+	adis16060_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static int __devinit adis16060_w_probe(struct spi_device *spi)
+{
+	int ret;
+	struct adis16060_state *st = adis16060_st;
+	if (!st) {
+		ret =  -ENODEV;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+	st->us_w = spi;
+	return 0;
+
+error_ret:
+	return ret;
+}
+
+static int adis16060_w_remove(struct spi_device *spi)
+{
+	return 0;
+}
+
+static struct spi_driver adis16060_r_driver = {
+	.driver = {
+		.name = "adis16060_r",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16060_r_probe,
+	.remove = __devexit_p(adis16060_r_remove),
+};
+
+static struct spi_driver adis16060_w_driver = {
+	.driver = {
+		.name = "adis16060_w",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16060_w_probe,
+	.remove = __devexit_p(adis16060_w_remove),
+};
+
+static __init int adis16060_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&adis16060_r_driver);
+	if (ret < 0)
+		return ret;
+
+	ret = spi_register_driver(&adis16060_w_driver);
+	if (ret < 0) {
+		spi_unregister_driver(&adis16060_r_driver);
+		return ret;
+	}
+
+	return 0;
+}
+module_init(adis16060_init);
+
+static __exit void adis16060_exit(void)
+{
+	spi_unregister_driver(&adis16060_w_driver);
+	spi_unregister_driver(&adis16060_r_driver);
+}
+module_exit(adis16060_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16060 Yaw Rate Gyroscope with SPI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 20/36] staging: iio: gyro: new driver for ADIS16080 digital output gyros
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (18 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 19/36] staging: iio: gyro: new driver for ADIS16060 digital output gyros Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 21/36] staging: iio: gyro: new driver for ADIS16130 " Mike Frysinger
                   ` (16 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/gyro/Kconfig          |    7 +
 drivers/staging/iio/gyro/Makefile         |    3 +
 drivers/staging/iio/gyro/adis16080.h      |  102 +++++++++++
 drivers/staging/iio/gyro/adis16080_core.c |  271 +++++++++++++++++++++++++++++
 4 files changed, 383 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/gyro/adis16080.h
 create mode 100644 drivers/staging/iio/gyro/adis16080_core.c

diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
index 77c0fe8..f219c94 100644
--- a/drivers/staging/iio/gyro/Kconfig
+++ b/drivers/staging/iio/gyro/Kconfig
@@ -10,6 +10,13 @@ config ADIS16060
 	  Say yes here to build support for Analog Devices adis16060 wide bandwidth
 	  yaw rate gyroscope with SPI.
 
+config ADIS16080
+	tristate "Analog Devices ADIS16080/100 Yaw Rate Gyroscope with SPI driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices adis16080/100 Yaw Rate
+	  Gyroscope with SPI.
+
 config ADIS16260
 	tristate "Analog Devices ADIS16260 ADIS16265 Digital Gyroscope Sensor SPI driver"
 	depends on SPI
diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile
index e2c4acb..747823f 100644
--- a/drivers/staging/iio/gyro/Makefile
+++ b/drivers/staging/iio/gyro/Makefile
@@ -5,6 +5,9 @@
 adis16060-y             := adis16060_core.o
 obj-$(CONFIG_ADIS16060) += adis16060.o
 
+adis16080-y             := adis16080_core.o
+obj-$(CONFIG_ADIS16080) += adis16080.o
+
 adis16260-y             := adis16260_core.o
 adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o
 obj-$(CONFIG_ADIS16260) += adis16260.o
diff --git a/drivers/staging/iio/gyro/adis16080.h b/drivers/staging/iio/gyro/adis16080.h
new file mode 100644
index 0000000..3fcbe67
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16080.h
@@ -0,0 +1,102 @@
+#ifndef SPI_ADIS16080_H_
+#define SPI_ADIS16080_H_
+
+#define ADIS16080_DIN_CODE   4 /* Output data format setting. 0: Twos complement. 1: Offset binary. */
+#define ADIS16080_DIN_GYRO   (0 << 10) /* Gyroscope output */
+#define ADIS16080_DIN_TEMP   (1 << 10) /* Temperature output */
+#define ADIS16080_DIN_AIN1   (2 << 10)
+#define ADIS16080_DIN_AIN2   (3 << 10)
+#define ADIS16080_DIN_WRITE  (1 << 15) /* 1: Write contents on DIN to control register.
+					* 0: No changes to control register.
+					*/
+
+#define ADIS16080_MAX_TX     2
+#define ADIS16080_MAX_RX     2
+
+/**
+ * struct adis16080_state - device instance specific data
+ * @us:			actual spi_device to write data
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct adis16080_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum adis16080_scan {
+	ADIS16080_SCAN_GYRO,
+	ADIS16080_SCAN_TEMP,
+	ADIS16080_SCAN_ADC_1,
+	ADIS16080_SCAN_ADC_2,
+};
+
+void adis16080_remove_trigger(struct iio_dev *indio_dev);
+int adis16080_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16080_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+
+int adis16080_configure_ring(struct iio_dev *indio_dev);
+void adis16080_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16080_initialize_ring(struct iio_ring_buffer *ring);
+void adis16080_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16080_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16080_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16080_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16080_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16080_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16080_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16080_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16080_H_ */
diff --git a/drivers/staging/iio/gyro/adis16080_core.c b/drivers/staging/iio/gyro/adis16080_core.c
new file mode 100644
index 0000000..0efb768
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16080_core.c
@@ -0,0 +1,271 @@
+/*
+ * ADIS16080/100 Yaw Rate Gyroscope with SPI driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16080.h"
+
+#define DRIVER_NAME		"adis16080"
+
+struct adis16080_state *adis16080_st;
+
+int adis16080_spi_write(struct device *dev,
+		u16 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16080_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = val >> 8;
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+int adis16080_spi_read(struct device *dev,
+		u16 *val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16080_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+
+	ret = spi_read(st->us, st->rx, 2);
+
+	if (ret == 0)
+		*val = ((st->rx[0] & 0xF) << 8) | st->rx[1];
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static ssize_t adis16080_read(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	u16 val;
+	ssize_t ret;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+	ret =  adis16080_spi_read(dev, &val);
+	mutex_unlock(&indio_dev->mlock);
+
+	if (ret == 0)
+		return sprintf(buf, "%d\n", val);
+	else
+		return ret;
+}
+
+static ssize_t adis16080_write(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 16, &val);
+	if (ret)
+		goto error_ret;
+	ret = adis16080_spi_write(dev, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+#define IIO_DEV_ATTR_IN(_show)				\
+	IIO_DEVICE_ATTR(in, S_IRUGO, _show, NULL, 0)
+
+#define IIO_DEV_ATTR_OUT(_store)				\
+	IIO_DEVICE_ATTR(out, S_IRUGO, NULL, _store, 0)
+
+static IIO_DEV_ATTR_IN(adis16080_read);
+static IIO_DEV_ATTR_OUT(adis16080_write);
+
+static IIO_CONST_ATTR(name, "adis16080");
+
+static struct attribute *adis16080_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16080_event_attribute_group = {
+	.attrs = adis16080_event_attributes,
+};
+
+static struct attribute *adis16080_attributes[] = {
+	&iio_dev_attr_in.dev_attr.attr,
+	&iio_dev_attr_out.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16080_attribute_group = {
+	.attrs = adis16080_attributes,
+};
+
+static int __devinit adis16080_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16080_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16080_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16080_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16080_event_attribute_group;
+	st->indio_dev->attrs = &adis16080_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16080_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16080_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16080");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16080_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	adis16080_st = st;
+	return 0;
+
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16080_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16080_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int adis16080_remove(struct spi_device *spi)
+{
+	struct adis16080_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16080_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16080_uninitialize_ring(indio_dev->ring);
+	adis16080_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16080_driver = {
+	.driver = {
+		.name = "adis16080",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16080_probe,
+	.remove = __devexit_p(adis16080_remove),
+};
+
+static __init int adis16080_init(void)
+{
+	return spi_register_driver(&adis16080_driver);
+}
+module_init(adis16080_init);
+
+static __exit void adis16080_exit(void)
+{
+	spi_unregister_driver(&adis16080_driver);
+}
+module_exit(adis16080_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope with SPI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 21/36] staging: iio: gyro: new driver for ADIS16130 digital output gyros
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (19 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 20/36] staging: iio: gyro: new driver for ADIS16080 " Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 22/36] staging: iio: dac: new driver for AD5624R devices Mike Frysinger
                   ` (15 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/gyro/Kconfig          |    7 +
 drivers/staging/iio/gyro/Makefile         |    3 +
 drivers/staging/iio/gyro/adis16130.h      |  108 ++++++++++
 drivers/staging/iio/gyro/adis16130_core.c |  313 +++++++++++++++++++++++++++++
 4 files changed, 431 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/gyro/adis16130.h
 create mode 100644 drivers/staging/iio/gyro/adis16130_core.c

diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
index f219c94..236f15f 100644
--- a/drivers/staging/iio/gyro/Kconfig
+++ b/drivers/staging/iio/gyro/Kconfig
@@ -17,6 +17,13 @@ config ADIS16080
 	  Say yes here to build support for Analog Devices adis16080/100 Yaw Rate
 	  Gyroscope with SPI.
 
+config ADIS16130
+	tristate "Analog Devices ADIS16130 High Precision Angular Rate Sensor driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices ADIS16130 High Precision
+	  Angular Rate Sensor driver.
+
 config ADIS16260
 	tristate "Analog Devices ADIS16260 ADIS16265 Digital Gyroscope Sensor SPI driver"
 	depends on SPI
diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile
index 747823f..2764c15 100644
--- a/drivers/staging/iio/gyro/Makefile
+++ b/drivers/staging/iio/gyro/Makefile
@@ -8,6 +8,9 @@ obj-$(CONFIG_ADIS16060) += adis16060.o
 adis16080-y             := adis16080_core.o
 obj-$(CONFIG_ADIS16080) += adis16080.o
 
+adis16130-y             := adis16130_core.o
+obj-$(CONFIG_ADIS16130) += adis16130.o
+
 adis16260-y             := adis16260_core.o
 adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o
 obj-$(CONFIG_ADIS16260) += adis16260.o
diff --git a/drivers/staging/iio/gyro/adis16130.h b/drivers/staging/iio/gyro/adis16130.h
new file mode 100644
index 0000000..ab80ef6
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16130.h
@@ -0,0 +1,108 @@
+#ifndef SPI_ADIS16130_H_
+#define SPI_ADIS16130_H_
+
+#define ADIS16130_CON         0x0
+#define ADIS16130_CON_RD      (1 << 6)
+#define ADIS16130_IOP         0x1
+#define ADIS16130_IOP_ALL_RDY (1 << 3) /* 1 = data-ready signal low when unread data on all channels; */
+#define ADIS16130_IOP_SYNC    (1 << 0) /* 1 = synchronization enabled */
+#define ADIS16130_RATEDATA    0x8 /* Gyroscope output, rate of rotation */
+#define ADIS16130_TEMPDATA    0xA /* Temperature output */
+#define ADIS16130_RATECS      0x28 /* Gyroscope channel setup */
+#define ADIS16130_RATECS_EN   (1 << 3) /* 1 = channel enable; */
+#define ADIS16130_TEMPCS      0x2A /* Temperature channel setup */
+#define ADIS16130_TEMPCS_EN   (1 << 3)
+#define ADIS16130_RATECONV    0x30
+#define ADIS16130_TEMPCONV    0x32
+#define ADIS16130_MODE        0x38
+#define ADIS16130_MODE_24BIT  (1 << 1) /* 1 = 24-bit resolution; */
+
+#define ADIS16130_MAX_TX     4
+#define ADIS16130_MAX_RX     4
+
+/**
+ * struct adis16130_state - device instance specific data
+ * @us:			actual spi_device to write data
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct adis16130_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	u32                             mode; /* 1: 24bits mode 0:16bits mode */
+	struct mutex			buf_lock;
+};
+
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum adis16130_scan {
+	ADIS16130_SCAN_GYRO,
+	ADIS16130_SCAN_TEMP,
+};
+
+void adis16130_remove_trigger(struct iio_dev *indio_dev);
+int adis16130_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16130_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+
+int adis16130_configure_ring(struct iio_dev *indio_dev);
+void adis16130_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16130_initialize_ring(struct iio_ring_buffer *ring);
+void adis16130_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16130_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16130_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16130_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16130_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16130_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16130_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16130_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16130_H_ */
diff --git a/drivers/staging/iio/gyro/adis16130_core.c b/drivers/staging/iio/gyro/adis16130_core.c
new file mode 100644
index 0000000..49ffc7b
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16130_core.c
@@ -0,0 +1,313 @@
+/*
+ * ADIS16130 Digital Output, High Precision Angular Rate Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16130.h"
+
+#define DRIVER_NAME		"adis16130"
+
+struct adis16130_state *adis16130_st;
+
+int adis16130_spi_write(struct device *dev, u8 reg_addr,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16130_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = reg_addr;
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+int adis16130_spi_read(struct device *dev, u8 reg_addr,
+		u32 *val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16130_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+
+	st->tx[0] = ADIS16130_CON_RD | reg_addr;
+	if (st->mode)
+		ret = spi_read(st->us, st->rx, 4);
+	else
+		ret = spi_read(st->us, st->rx, 3);
+
+	if (ret == 0) {
+		if (st->mode)
+			*val = (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3];
+		else
+			*val = (st->rx[1] << 8) | st->rx[2];
+	}
+
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static ssize_t adis16130_gyro_read(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	u32 val;
+	ssize_t ret;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+	ret =  adis16130_spi_read(dev, ADIS16130_RATEDATA, &val);
+	mutex_unlock(&indio_dev->mlock);
+
+	if (ret == 0)
+		return sprintf(buf, "%d\n", val);
+	else
+		return ret;
+}
+
+static ssize_t adis16130_temp_read(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	u32 val;
+	ssize_t ret;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+	ret =  adis16130_spi_read(dev, ADIS16130_TEMPDATA, &val);
+	mutex_unlock(&indio_dev->mlock);
+
+	if (ret == 0)
+		return sprintf(buf, "%d\n", val);
+	else
+		return ret;
+}
+
+static ssize_t adis16130_bitsmode_read(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16130_state *st = iio_dev_get_devdata(indio_dev);
+
+	return sprintf(buf, "%d\n", st->mode);
+}
+
+static ssize_t adis16130_bitsmode_write(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 16, &val);
+	if (ret)
+		goto error_ret;
+	ret = adis16130_spi_write(dev, ADIS16130_MODE, !!val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_TEMP_RAW(adis16130_temp_read);
+
+static IIO_CONST_ATTR(name, "adis16130");
+
+static IIO_DEV_ATTR_GYRO(adis16130_gyro_read,
+		ADIS16130_RATEDATA);
+
+#define IIO_DEV_ATTR_BITS_MODE(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(bits_mode, _mode, _show, _store, _addr)
+
+static IIO_DEV_ATTR_BITS_MODE(S_IWUSR | S_IRUGO, adis16130_bitsmode_read, adis16130_bitsmode_write,
+			ADIS16130_MODE);
+
+static struct attribute *adis16130_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16130_event_attribute_group = {
+	.attrs = adis16130_event_attributes,
+};
+
+static struct attribute *adis16130_attributes[] = {
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_gyro_raw.dev_attr.attr,
+	&iio_dev_attr_bits_mode.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16130_attribute_group = {
+	.attrs = adis16130_attributes,
+};
+
+static int __devinit adis16130_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16130_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16130_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16130_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16130_event_attribute_group;
+	st->indio_dev->attrs = &adis16130_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+	st->mode = 1;
+
+	ret = adis16130_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16130_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16130");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16130_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	adis16130_st = st;
+	return 0;
+
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16130_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16130_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int adis16130_remove(struct spi_device *spi)
+{
+	struct adis16130_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16130_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16130_uninitialize_ring(indio_dev->ring);
+	adis16130_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16130_driver = {
+	.driver = {
+		.name = "adis16130",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16130_probe,
+	.remove = __devexit_p(adis16130_remove),
+};
+
+static __init int adis16130_init(void)
+{
+	return spi_register_driver(&adis16130_driver);
+}
+module_init(adis16130_init);
+
+static __exit void adis16130_exit(void)
+{
+	spi_unregister_driver(&adis16130_driver);
+}
+module_exit(adis16130_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16130 High Precision Angular Rate Sensor driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 22/36] staging: iio: dac: new driver for AD5624R devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (20 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 21/36] staging: iio: gyro: new driver for ADIS16130 " Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 23/36] staging: iio: dds: new driver for AD5930/2 devices Mike Frysinger
                   ` (14 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

This is used to convert digital streams into voltages.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/Kconfig           |    1 +
 drivers/staging/iio/Makefile          |    1 +
 drivers/staging/iio/dac/Kconfig       |   11 ++
 drivers/staging/iio/dac/Makefile      |    5 +
 drivers/staging/iio/dac/ad5624r.h     |   21 +++
 drivers/staging/iio/dac/ad5624r_spi.c |  313 +++++++++++++++++++++++++++++++++
 drivers/staging/iio/dac/dac.h         |    6 +
 7 files changed, 358 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dac/Kconfig
 create mode 100644 drivers/staging/iio/dac/Makefile
 create mode 100644 drivers/staging/iio/dac/ad5624r.h
 create mode 100644 drivers/staging/iio/dac/ad5624r_spi.c
 create mode 100644 drivers/staging/iio/dac/dac.h

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index b8bb5f1..773c301 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -43,6 +43,7 @@ config IIO_TRIGGER
 source "drivers/staging/iio/accel/Kconfig"
 source "drivers/staging/iio/adc/Kconfig"
 source "drivers/staging/iio/addac/Kconfig"
+source "drivers/staging/iio/dac/Kconfig"
 source "drivers/staging/iio/gyro/Kconfig"
 source "drivers/staging/iio/imu/Kconfig"
 source "drivers/staging/iio/light/Kconfig"
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 0111647..15eba01 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 obj-y += accel/
 obj-y += adc/
 obj-y += addac/
+obj-y += dac/
 obj-y += gyro/
 obj-y += imu/
 obj-y += light/
diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kconfig
new file mode 100644
index 0000000..583df78
--- /dev/null
+++ b/drivers/staging/iio/dac/Kconfig
@@ -0,0 +1,11 @@
+#
+# DAC drivers
+#
+comment "Digital to analog convertors"
+
+config AD5624R_SPI
+	tristate "Analog Devices AD5624/44/64R DAC spi driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
+	  AD5664R convertors (DAC). This driver uses the common SPI interface.
diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/Makefile
new file mode 100644
index 0000000..7ddf05d
--- /dev/null
+++ b/drivers/staging/iio/dac/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O DAC drivers
+#
+
+obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
diff --git a/drivers/staging/iio/dac/ad5624r.h b/drivers/staging/iio/dac/ad5624r.h
new file mode 100644
index 0000000..ce518be
--- /dev/null
+++ b/drivers/staging/iio/dac/ad5624r.h
@@ -0,0 +1,21 @@
+#ifndef SPI_AD5624R_H_
+#define SPI_AD5624R_H_
+
+#define AD5624R_DAC_CHANNELS	4
+
+#define AD5624R_ADDR_DAC0	0x0
+#define AD5624R_ADDR_DAC1	0x1
+#define AD5624R_ADDR_DAC2	0x2
+#define AD5624R_ADDR_DAC3	0x3
+#define AD5624R_ADDR_ALL_DAC	0x7
+
+#define AD5624R_CMD_WRITE_INPUT_N             0x0
+#define AD5624R_CMD_UPDATE_DAC_N              0x1
+#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL  0x2
+#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N    0x3
+#define AD5624R_CMD_POWERDOWN_DAC             0x4
+#define AD5624R_CMD_RESET                     0x5
+#define AD5624R_CMD_LDAC_SETUP                0x6
+#define AD5624R_CMD_INTERNAL_REFER_SETUP      0x7
+
+#endif
diff --git a/drivers/staging/iio/dac/ad5624r_spi.c b/drivers/staging/iio/dac/ad5624r_spi.c
new file mode 100644
index 0000000..705ff50
--- /dev/null
+++ b/drivers/staging/iio/dac/ad5624r_spi.c
@@ -0,0 +1,313 @@
+/*
+ * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "dac.h"
+#include "ad5624r.h"
+
+/**
+ * struct ad5624r_state - device related storage
+ * @indio_dev:	associated industrial IO device
+ * @us:		spi device
+ **/
+struct ad5624r_state {
+	struct iio_dev *indio_dev;
+	struct spi_device *us;
+	int data_len;
+	int ldac_mode;
+	int dac_power_mode[AD5624R_DAC_CHANNELS];
+	int internal_ref;
+};
+
+static int ad5624r_spi_write(struct spi_device *spi, u8 cmd, u8 addr, u16 val, u8 len)
+{
+	struct spi_transfer t;
+	struct spi_message m;
+	u32 data;
+	u8 msg[3];
+
+	/*
+	 * The input shift register is 24 bits wide. The first two bits are don't care bits.
+	 * The next three are the command bits, C2 to C0, followed by the 3-bit DAC address,
+	 * A2 to A0, and then the 16-, 14-, 12-bit data-word. The data-word comprises the 16-,
+	 * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits, for the AD5664R,
+	 * AD5644R, and AD5624R, respectively.
+	 */
+	data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len));
+	msg[0] = data >> 16;
+	msg[1] = data >> 8;
+	msg[2] = data;
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+	t.tx_buf = &msg[0];
+	t.len = 3;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
+
+static ssize_t ad5624r_write_dac(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	long readin;
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ad5624r_state *st = indio_dev->dev_data;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = strict_strtol(buf, 10, &readin);
+	if (ret)
+		return ret;
+
+	ad5624r_spi_write(st->us, AD5624R_CMD_WRITE_INPUT_N_UPDATE_N,
+			this_attr->address, readin, st->data_len);
+	return ret ? ret : len;
+}
+
+static ssize_t ad5624r_read_ldac_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ad5624r_state *st = indio_dev->dev_data;
+
+	return sprintf(buf, "%x\n", st->ldac_mode);
+}
+
+static ssize_t ad5624r_write_ldac_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	long readin;
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ad5624r_state *st = indio_dev->dev_data;
+
+	ret = strict_strtol(buf, 16, &readin);
+	if (ret)
+		return ret;
+
+	ad5624r_spi_write(st->us, AD5624R_CMD_LDAC_SETUP, 0, readin & 0xF, 16);
+	st->ldac_mode = readin & 0xF;
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad5624r_read_dac_power_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ad5624r_state *st = indio_dev->dev_data;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	return sprintf(buf, "%d\n", st->dac_power_mode[this_attr->address]);
+}
+
+static ssize_t ad5624r_write_dac_power_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	long readin;
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ad5624r_state *st = indio_dev->dev_data;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = strict_strtol(buf, 10, &readin);
+	if (ret)
+		return ret;
+
+	ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0,
+			((readin & 0x3) << 4) | (1 << this_attr->address), 16);
+
+	st->dac_power_mode[this_attr->address] = readin & 0x3;
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad5624r_read_internal_ref_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ad5624r_state *st = indio_dev->dev_data;
+
+	return sprintf(buf, "%d\n", st->internal_ref);
+}
+
+static ssize_t ad5624r_write_internal_ref_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	long readin;
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ad5624r_state *st = indio_dev->dev_data;
+
+	ret = strict_strtol(buf, 10, &readin);
+	if (ret)
+		return ret;
+
+	ad5624r_spi_write(st->us, AD5624R_CMD_INTERNAL_REFER_SETUP, 0, !!readin, 16);
+
+	st->internal_ref = !!readin;
+
+	return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_DAC(0, ad5624r_write_dac, AD5624R_ADDR_DAC0);
+static IIO_DEV_ATTR_DAC(1, ad5624r_write_dac, AD5624R_ADDR_DAC1);
+static IIO_DEV_ATTR_DAC(2, ad5624r_write_dac, AD5624R_ADDR_DAC2);
+static IIO_DEV_ATTR_DAC(3, ad5624r_write_dac, AD5624R_ADDR_DAC3);
+
+static IIO_DEVICE_ATTR(ldac_mode, S_IRUGO | S_IWUSR, ad5624r_read_ldac_mode,
+		ad5624r_write_ldac_mode, 0);
+static IIO_DEVICE_ATTR(internal_ref, S_IRUGO | S_IWUSR, ad5624r_read_internal_ref_mode,
+		ad5624r_write_internal_ref_mode, 0);
+
+#define IIO_DEV_ATTR_DAC_POWER_MODE(_num, _show, _store, _addr)			\
+	IIO_DEVICE_ATTR(dac_power_mode_##_num, S_IRUGO | S_IWUSR, _show, _store, _addr)
+
+static IIO_DEV_ATTR_DAC_POWER_MODE(0, ad5624r_read_dac_power_mode, ad5624r_write_dac_power_mode, 0);
+static IIO_DEV_ATTR_DAC_POWER_MODE(1, ad5624r_read_dac_power_mode, ad5624r_write_dac_power_mode, 1);
+static IIO_DEV_ATTR_DAC_POWER_MODE(2, ad5624r_read_dac_power_mode, ad5624r_write_dac_power_mode, 2);
+static IIO_DEV_ATTR_DAC_POWER_MODE(3, ad5624r_read_dac_power_mode, ad5624r_write_dac_power_mode, 3);
+
+static struct attribute *ad5624r_attributes[] = {
+	&iio_dev_attr_dac_0.dev_attr.attr,
+	&iio_dev_attr_dac_1.dev_attr.attr,
+	&iio_dev_attr_dac_2.dev_attr.attr,
+	&iio_dev_attr_dac_3.dev_attr.attr,
+	&iio_dev_attr_dac_power_mode_0.dev_attr.attr,
+	&iio_dev_attr_dac_power_mode_1.dev_attr.attr,
+	&iio_dev_attr_dac_power_mode_2.dev_attr.attr,
+	&iio_dev_attr_dac_power_mode_3.dev_attr.attr,
+	&iio_dev_attr_ldac_mode.dev_attr.attr,
+	&iio_dev_attr_internal_ref.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad5624r_attribute_group = {
+	.attrs = ad5624r_attributes,
+};
+
+static int __devinit ad5624r_probe(struct spi_device *spi)
+{
+
+	struct ad5624r_state *st;
+	int ret = 0;
+	char *chip_name = spi->dev.platform_data;
+
+	if (!chip_name)
+		return -ENODEV;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	if (strcmp(chip_name, "ad5624r") == 0)
+		st->data_len = 12;
+	else if (strcmp(chip_name, "ad5644r") == 0)
+		st->data_len = 14;
+	else if (strcmp(chip_name, "ad5664r") == 0)
+		st->data_len = 16;
+	else {
+		dev_err(&spi->dev, "not supported chip type\n");
+		ret = -EINVAL;
+		goto error_ret;
+	}
+
+	st->us = spi;
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 0;
+	st->indio_dev->event_attrs = NULL;
+
+	st->indio_dev->attrs = &ad5624r_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	spi->mode = SPI_MODE_0;
+	spi_setup(spi);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->indio_dev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad5624r_remove(struct spi_device *spi)
+{
+	struct ad5624r_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->indio_dev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad5624r_driver = {
+	.driver = {
+		.name = "ad5624r",
+		.owner = THIS_MODULE,
+	},
+	.probe = ad5624r_probe,
+	.remove = __devexit_p(ad5624r_remove),
+};
+
+static __init int ad5624r_spi_init(void)
+{
+	return spi_register_driver(&ad5624r_driver);
+}
+module_init(ad5624r_spi_init);
+
+static __exit void ad5624r_spi_exit(void)
+{
+	spi_unregister_driver(&ad5624r_driver);
+}
+module_exit(ad5624r_spi_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/dac/dac.h b/drivers/staging/iio/dac/dac.h
new file mode 100644
index 0000000..55005ee
--- /dev/null
+++ b/drivers/staging/iio/dac/dac.h
@@ -0,0 +1,6 @@
+/*
+ * dac.h - sysfs attributes associated with DACs
+ */
+
+#define IIO_DEV_ATTR_DAC(_num, _store, _addr)			\
+	IIO_DEVICE_ATTR(dac_##_num, S_IWUSR, NULL, _store, _addr)
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 23/36] staging: iio: dds: new driver for AD5930/2 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (21 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 22/36] staging: iio: dac: new driver for AD5624R devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 24/36] staging: iio: dds: new driver for AD9832/3/4/5 devices Mike Frysinger
                   ` (13 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Cliff Cai, Michael Hennerich

From: Cliff Cai <cliff.cai@analog.com>

This is the initial driver in the new Direct Digital Synthesis section.

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/Kconfig      |    1 +
 drivers/staging/iio/Makefile     |    1 +
 drivers/staging/iio/dds/Kconfig  |   11 +++
 drivers/staging/iio/dds/Makefile |    5 +
 drivers/staging/iio/dds/ad5930.c |  170 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 188 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dds/Kconfig
 create mode 100644 drivers/staging/iio/dds/Makefile
 create mode 100644 drivers/staging/iio/dds/ad5930.c

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 773c301..5e5bc47 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -44,6 +44,7 @@ source "drivers/staging/iio/accel/Kconfig"
 source "drivers/staging/iio/adc/Kconfig"
 source "drivers/staging/iio/addac/Kconfig"
 source "drivers/staging/iio/dac/Kconfig"
+source "drivers/staging/iio/dds/Kconfig"
 source "drivers/staging/iio/gyro/Kconfig"
 source "drivers/staging/iio/imu/Kconfig"
 source "drivers/staging/iio/light/Kconfig"
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 15eba01..766a5d1 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -13,6 +13,7 @@ obj-y += accel/
 obj-y += adc/
 obj-y += addac/
 obj-y += dac/
+obj-y += dds/
 obj-y += gyro/
 obj-y += imu/
 obj-y += light/
diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig
new file mode 100644
index 0000000..d32bebc
--- /dev/null
+++ b/drivers/staging/iio/dds/Kconfig
@@ -0,0 +1,11 @@
+#
+# Direct Digital Synthesis drivers
+#
+comment "Direct Digital Synthesis"
+
+config AD5930
+	tristate "Analog Devices ad5930/5932 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices DDS chip
+	  ad5930/ad5932, provides direct access via sysfs.
diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile
new file mode 100644
index 0000000..110f289
--- /dev/null
+++ b/drivers/staging/iio/dds/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for Direct Digital Synthesis drivers
+#
+
+obj-$(CONFIG_AD5930) += ad5930.o
diff --git a/drivers/staging/iio/dds/ad5930.c b/drivers/staging/iio/dds/ad5930.c
new file mode 100644
index 0000000..f80039c
--- /dev/null
+++ b/drivers/staging/iio/dds/ad5930.c
@@ -0,0 +1,170 @@
+/*
+ * Driver for ADI Direct Digital Synthesis ad5930
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad5930"
+
+#define value_mask (u16)0xf000
+#define addr_shift 12
+
+/* Register format: 4 bits addr + 12 bits value */
+struct ad5903_config {
+	u16 control;
+	u16 incnum;
+	u16 frqdelt[2];
+	u16 incitvl;
+	u16 buritvl;
+	u16 strtfrq[2];
+};
+
+struct ad5930_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+};
+
+static ssize_t ad5930_set_parameter(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	struct ad5903_config *config = (struct ad5903_config *)buf;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad5930_state *st = idev->dev_data;
+
+	config->control = (config->control & ~value_mask);
+	config->incnum = (config->control & ~value_mask) | (1 << addr_shift);
+	config->frqdelt[0] = (config->control & ~value_mask) | (2 << addr_shift);
+	config->frqdelt[1] = (config->control & ~value_mask) | 3 << addr_shift;
+	config->incitvl = (config->control & ~value_mask) | 4 << addr_shift;
+	config->buritvl = (config->control & ~value_mask) | 8 << addr_shift;
+	config->strtfrq[0] = (config->control & ~value_mask) | 0xc << addr_shift;
+	config->strtfrq[1] = (config->control & ~value_mask) | 0xd << addr_shift;
+
+	xfer.len = len;
+	xfer.tx_buf = config;
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad5930_set_parameter, 0);
+
+static struct attribute *ad5930_attributes[] = {
+	&iio_dev_attr_dds.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad5930_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad5930_attributes,
+};
+
+static int __devinit ad5930_probe(struct spi_device *spi)
+{
+	struct ad5930_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad5930_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+	spi->max_speed_hz = 2000000;
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = 16;
+	spi_setup(spi);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad5930_remove(struct spi_device *spi)
+{
+	struct ad5930_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad5930_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad5930_probe,
+	.remove = __devexit_p(ad5930_remove),
+};
+
+static __init int ad5930_spi_init(void)
+{
+	return spi_register_driver(&ad5930_driver);
+}
+module_init(ad5930_spi_init);
+
+static __exit void ad5930_spi_exit(void)
+{
+	spi_unregister_driver(&ad5930_driver);
+}
+module_exit(ad5930_spi_exit);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("Analog Devices ad5930 driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 24/36] staging: iio: dds: new driver for AD9832/3/4/5 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (22 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 23/36] staging: iio: dds: new driver for AD5930/2 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 25/36] staging: iio: dds: new driver for AD9850/1 devices Mike Frysinger
                   ` (12 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Cliff Cai, Michael Hennerich

From: Cliff Cai <cliff.cai@analog.com>

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/dds/Kconfig  |    7 +
 drivers/staging/iio/dds/Makefile |    1 +
 drivers/staging/iio/dds/ad9832.c |  266 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 274 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dds/ad9832.c

diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig
index d32bebc..c9fae1d 100644
--- a/drivers/staging/iio/dds/Kconfig
+++ b/drivers/staging/iio/dds/Kconfig
@@ -9,3 +9,10 @@ config AD5930
 	help
 	  Say yes here to build support for Analog Devices DDS chip
 	  ad5930/ad5932, provides direct access via sysfs.
+
+config AD9832
+	tristate "Analog Devices ad9832/3/4/5 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices DDS chip
+	  ad9832/3/4/5, provides direct access via sysfs.
diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile
index 110f289..d0f17f0 100644
--- a/drivers/staging/iio/dds/Makefile
+++ b/drivers/staging/iio/dds/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_AD5930) += ad5930.o
+obj-$(CONFIG_AD9832) += ad9832.o
diff --git a/drivers/staging/iio/dds/ad9832.c b/drivers/staging/iio/dds/ad9832.c
new file mode 100644
index 0000000..a4bb048
--- /dev/null
+++ b/drivers/staging/iio/dds/ad9832.c
@@ -0,0 +1,266 @@
+/*
+ * Driver for ADI Direct Digital Synthesis ad9832
+ *
+ * Copyright (c) 2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad9832"
+
+#define value_mask (u16)0xf000
+#define cmd_shift 12
+#define add_shift 8
+#define AD9832_SYNC (1 << 13)
+#define AD9832_SELSRC (1 << 12)
+#define AD9832_SLEEP (1 << 13)
+#define AD9832_RESET (1 << 12)
+#define AD9832_CLR (1 << 11)
+
+#define ADD_FREQ0LL 0x0
+#define ADD_FREQ0HL 0x1
+#define ADD_FREQ0LM 0x2
+#define ADD_FREQ0HM 0x3
+#define ADD_FREQ1LL 0x4
+#define ADD_FREQ1HL 0x5
+#define ADD_FREQ1LM 0x6
+#define ADD_FREQ1HM 0x7
+#define ADD_PHASE0L 0x8
+#define ADD_PHASE0H 0x9
+#define ADD_PHASE1L 0xa
+#define ADD_PHASE1H 0xb
+#define ADD_PHASE2L 0xc
+#define ADD_PHASE2H 0xd
+#define ADD_PHASE3L 0xe
+#define ADD_PHASE3H 0xf
+
+#define CMD_PHA8BITSW 0x1
+#define CMD_PHA16BITSW 0x0
+#define CMD_FRE8BITSW 0x3
+#define CMD_FRE16BITSW 0x2
+#define CMD_SELBITSCTL 0x6
+
+struct ad9832_setting {
+	u16 freq0[4];
+	u16 freq1[4];
+	u16 phase0[2];
+	u16 phase1[2];
+	u16 phase2[2];
+	u16 phase3[2];
+};
+
+struct ad9832_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+};
+
+static ssize_t ad9832_set_parameter(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	struct ad9832_setting config;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad9832_state *st = idev->dev_data;
+
+	config.freq0[0] = (CMD_FRE8BITSW << add_shift | ADD_FREQ0LL << add_shift | buf[0]);
+	config.freq0[1] = (CMD_FRE16BITSW << add_shift | ADD_FREQ0HL << add_shift | buf[1]);
+	config.freq0[2] = (CMD_FRE8BITSW << add_shift | ADD_FREQ0LM << add_shift | buf[2]);
+	config.freq0[3] = (CMD_FRE16BITSW << add_shift | ADD_FREQ0HM << add_shift | buf[3]);
+	config.freq1[0] = (CMD_FRE8BITSW << add_shift | ADD_FREQ1LL << add_shift | buf[4]);
+	config.freq1[1] = (CMD_FRE16BITSW << add_shift | ADD_FREQ1HL << add_shift | buf[5]);
+	config.freq1[2] = (CMD_FRE8BITSW << add_shift | ADD_FREQ1LM << add_shift | buf[6]);
+	config.freq1[3] = (CMD_FRE16BITSW << add_shift | ADD_FREQ1HM << add_shift | buf[7]);
+
+	config.phase0[0] = (CMD_PHA8BITSW << add_shift | ADD_PHASE0L << add_shift | buf[9]);
+	config.phase0[1] = (CMD_PHA16BITSW << add_shift | ADD_PHASE0H << add_shift | buf[10]);
+	config.phase1[0] = (CMD_PHA8BITSW << add_shift | ADD_PHASE1L << add_shift | buf[11]);
+	config.phase1[1] = (CMD_PHA16BITSW << add_shift | ADD_PHASE1H << add_shift | buf[12]);
+	config.phase2[0] = (CMD_PHA8BITSW << add_shift | ADD_PHASE2L << add_shift | buf[13]);
+	config.phase2[1] = (CMD_PHA16BITSW << add_shift | ADD_PHASE2H << add_shift | buf[14]);
+	config.phase3[0] = (CMD_PHA8BITSW << add_shift | ADD_PHASE3L << add_shift | buf[15]);
+	config.phase3[1] = (CMD_PHA16BITSW << add_shift | ADD_PHASE3H << add_shift | buf[16]);
+
+	xfer.len = 2 * len;
+	xfer.tx_buf = &config;
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9832_set_parameter, 0);
+
+static struct attribute *ad9832_attributes[] = {
+	&iio_dev_attr_dds.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad9832_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad9832_attributes,
+};
+
+static void ad9832_init(struct ad9832_state *st)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	u16 config = 0;
+
+	config = 0x3 << 14 | AD9832_SLEEP | AD9832_RESET | AD9832_CLR;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 2;
+	xfer.tx_buf = &config;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	config = 0x2 << 14 | AD9832_SYNC | AD9832_SELSRC;
+	xfer.len = 2;
+	xfer.tx_buf = &config;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	config = CMD_SELBITSCTL << cmd_shift;
+	xfer.len = 2;
+	xfer.tx_buf = &config;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	config = 0x3 << 14;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 2;
+	xfer.tx_buf = &config;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+error_ret:
+	mutex_unlock(&st->lock);
+
+
+
+}
+
+static int __devinit ad9832_probe(struct spi_device *spi)
+{
+	struct ad9832_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad9832_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+	spi->max_speed_hz = 2000000;
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = 16;
+	spi_setup(spi);
+	ad9832_init(st);
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad9832_remove(struct spi_device *spi)
+{
+	struct ad9832_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad9832_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad9832_probe,
+	.remove = __devexit_p(ad9832_remove),
+};
+
+static __init int ad9832_spi_init(void)
+{
+	return spi_register_driver(&ad9832_driver);
+}
+module_init(ad9832_spi_init);
+
+static __exit void ad9832_spi_exit(void)
+{
+	spi_unregister_driver(&ad9832_driver);
+}
+module_exit(ad9832_spi_exit);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("Analog Devices ad9832 driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 25/36] staging: iio: dds: new driver for AD9850/1 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (23 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 24/36] staging: iio: dds: new driver for AD9832/3/4/5 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 26/36] staging: iio: dds: new driver for AD9852/4 devices Mike Frysinger
                   ` (11 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Cliff Cai, Michael Hennerich

From: Cliff Cai <cliff.cai@analog.com>

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/dds/Kconfig  |    7 ++
 drivers/staging/iio/dds/Makefile |    1 +
 drivers/staging/iio/dds/ad9850.c |  156 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 164 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dds/ad9850.c

diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig
index c9fae1d..d42bc4c 100644
--- a/drivers/staging/iio/dds/Kconfig
+++ b/drivers/staging/iio/dds/Kconfig
@@ -16,3 +16,10 @@ config AD9832
 	help
 	  Say yes here to build support for Analog Devices DDS chip
 	  ad9832/3/4/5, provides direct access via sysfs.
+
+config AD9850
+	tristate "Analog Devices ad9850/1 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices DDS chip
+	  ad9850/1, provides direct access via sysfs.
diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile
index d0f17f0..791db74 100644
--- a/drivers/staging/iio/dds/Makefile
+++ b/drivers/staging/iio/dds/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_AD5930) += ad5930.o
 obj-$(CONFIG_AD9832) += ad9832.o
+obj-$(CONFIG_AD9850) += ad9850.o
diff --git a/drivers/staging/iio/dds/ad9850.c b/drivers/staging/iio/dds/ad9850.c
new file mode 100644
index 0000000..b259bfe
--- /dev/null
+++ b/drivers/staging/iio/dds/ad9850.c
@@ -0,0 +1,156 @@
+/*
+ * Driver for ADI Direct Digital Synthesis ad9850
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad9850"
+
+#define value_mask (u16)0xf000
+#define addr_shift 12
+
+/* Register format: 4 bits addr + 12 bits value */
+struct ad9850_config {
+	u8 control[5];
+};
+
+struct ad9850_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+};
+
+static ssize_t ad9850_set_parameter(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	struct ad9850_config *config = (struct ad9850_config *)buf;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad9850_state *st = idev->dev_data;
+
+	xfer.len = len;
+	xfer.tx_buf = config;
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9850_set_parameter, 0);
+
+static struct attribute *ad9850_attributes[] = {
+	&iio_dev_attr_dds.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad9850_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad9850_attributes,
+};
+
+static int __devinit ad9850_probe(struct spi_device *spi)
+{
+	struct ad9850_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad9850_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+	spi->max_speed_hz = 2000000;
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = 16;
+	spi_setup(spi);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad9850_remove(struct spi_device *spi)
+{
+	struct ad9850_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad9850_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad9850_probe,
+	.remove = __devexit_p(ad9850_remove),
+};
+
+static __init int ad9850_spi_init(void)
+{
+	return spi_register_driver(&ad9850_driver);
+}
+module_init(ad9850_spi_init);
+
+static __exit void ad9850_spi_exit(void)
+{
+	spi_unregister_driver(&ad9850_driver);
+}
+module_exit(ad9850_spi_exit);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("Analog Devices ad9850 driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 26/36] staging: iio: dds: new driver for AD9852/4 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (24 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 25/36] staging: iio: dds: new driver for AD9850/1 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 27/36] staging: iio: dds: new driver for AD9910 devices Mike Frysinger
                   ` (10 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Cliff Cai, Michael Hennerich

From: Cliff Cai <cliff.cai@analog.com>

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/dds/Kconfig  |    7 +
 drivers/staging/iio/dds/Makefile |    1 +
 drivers/staging/iio/dds/ad9852.c |  314 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 322 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dds/ad9852.c

diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig
index d42bc4c..dee31eb 100644
--- a/drivers/staging/iio/dds/Kconfig
+++ b/drivers/staging/iio/dds/Kconfig
@@ -23,3 +23,10 @@ config AD9850
 	help
 	  Say yes here to build support for Analog Devices DDS chip
 	  ad9850/1, provides direct access via sysfs.
+
+config AD9852
+	tristate "Analog Devices ad9852/4 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices DDS chip
+	  ad9852/4, provides direct access via sysfs.
diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile
index 791db74..e344e8d 100644
--- a/drivers/staging/iio/dds/Makefile
+++ b/drivers/staging/iio/dds/Makefile
@@ -5,3 +5,4 @@
 obj-$(CONFIG_AD5930) += ad5930.o
 obj-$(CONFIG_AD9832) += ad9832.o
 obj-$(CONFIG_AD9850) += ad9850.o
+obj-$(CONFIG_AD9852) += ad9852.o
diff --git a/drivers/staging/iio/dds/ad9852.c b/drivers/staging/iio/dds/ad9852.c
new file mode 100644
index 0000000..0a41d25
--- /dev/null
+++ b/drivers/staging/iio/dds/ad9852.c
@@ -0,0 +1,314 @@
+/*
+ * Driver for ADI Direct Digital Synthesis ad9852
+ *
+ * Copyright (c) 2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad9852"
+
+#define addr_phaad1 0x0
+#define addr_phaad2 0x1
+#define addr_fretu1 0x2
+#define addr_fretu2 0x3
+#define addr_delfre 0x4
+#define addr_updclk 0x5
+#define addr_ramclk 0x6
+#define addr_contrl 0x7
+#define addr_optskm 0x8
+#define addr_optskr 0xa
+#define addr_dacctl 0xb
+
+#define COMPPD		(1 << 4)
+#define REFMULT2	(1 << 2)
+#define BYPPLL		(1 << 5)
+#define PLLRANG		(1 << 6)
+#define IEUPCLK		(1)
+#define OSKEN		(1 << 5)
+
+#define read_bit	(1 << 7)
+
+/* Register format: 1 byte addr + value */
+struct ad9852_config {
+	u8 phajst0[3];
+	u8 phajst1[3];
+	u8 fretun1[6];
+	u8 fretun2[6];
+	u8 dltafre[6];
+	u8 updtclk[5];
+	u8 ramprat[4];
+	u8 control[5];
+	u8 outpskm[3];
+	u8 outpskr[2];
+	u8 daccntl[3];
+};
+
+struct ad9852_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+};
+
+static ssize_t ad9852_set_parameter(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	struct ad9852_config *config = (struct ad9852_config *)buf;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad9852_state *st = idev->dev_data;
+
+	xfer.len = 3;
+	xfer.tx_buf = &config->phajst0[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 3;
+	xfer.tx_buf = &config->phajst1[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 6;
+	xfer.tx_buf = &config->fretun1[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 6;
+	xfer.tx_buf = &config->fretun2[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 6;
+	xfer.tx_buf = &config->dltafre[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->updtclk[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 4;
+	xfer.tx_buf = &config->ramprat[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->control[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 3;
+	xfer.tx_buf = &config->outpskm[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 2;
+	xfer.tx_buf = &config->outpskr[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 3;
+	xfer.tx_buf = &config->daccntl[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9852_set_parameter, 0);
+
+static void ad9852_init(struct ad9852_state *st)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	u8 config[5];
+
+	config[0] = addr_contrl;
+	config[1] = COMPPD;
+	config[2] = REFMULT2 | BYPPLL | PLLRANG;
+	config[3] = IEUPCLK;
+	config[4] = OSKEN;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 5;
+	xfer.tx_buf = &config;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	mutex_unlock(&st->lock);
+
+
+
+}
+
+static struct attribute *ad9852_attributes[] = {
+	&iio_dev_attr_dds.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad9852_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad9852_attributes,
+};
+
+static int __devinit ad9852_probe(struct spi_device *spi)
+{
+	struct ad9852_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad9852_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+	spi->max_speed_hz = 2000000;
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = 8;
+	spi_setup(spi);
+	ad9852_init(st);
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad9852_remove(struct spi_device *spi)
+{
+	struct ad9852_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad9852_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad9852_probe,
+	.remove = __devexit_p(ad9852_remove),
+};
+
+static __init int ad9852_spi_init(void)
+{
+	return spi_register_driver(&ad9852_driver);
+}
+module_init(ad9852_spi_init);
+
+static __exit void ad9852_spi_exit(void)
+{
+	spi_unregister_driver(&ad9852_driver);
+}
+module_exit(ad9852_spi_exit);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("Analog Devices ad9852 driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 27/36] staging: iio: dds: new driver for AD9910 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (25 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 26/36] staging: iio: dds: new driver for AD9852/4 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 28/36] staging: iio: dds: new driver for AD9951 devices Mike Frysinger
                   ` (9 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Cliff Cai, Michael Hennerich

From: Cliff Cai <cliff.cai@analog.com>

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/dds/Kconfig  |    7 +
 drivers/staging/iio/dds/Makefile |    1 +
 drivers/staging/iio/dds/ad9910.c |  454 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 462 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dds/ad9910.c

diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig
index dee31eb..518d0c1 100644
--- a/drivers/staging/iio/dds/Kconfig
+++ b/drivers/staging/iio/dds/Kconfig
@@ -30,3 +30,10 @@ config AD9852
 	help
 	  Say yes here to build support for Analog Devices DDS chip
 	  ad9852/4, provides direct access via sysfs.
+
+config AD9910
+	tristate "Analog Devices ad9910 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices DDS chip
+	  ad9910, provides direct access via sysfs.
diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile
index e344e8d..d249b81 100644
--- a/drivers/staging/iio/dds/Makefile
+++ b/drivers/staging/iio/dds/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_AD5930) += ad5930.o
 obj-$(CONFIG_AD9832) += ad9832.o
 obj-$(CONFIG_AD9850) += ad9850.o
 obj-$(CONFIG_AD9852) += ad9852.o
+obj-$(CONFIG_AD9910) += ad9910.o
diff --git a/drivers/staging/iio/dds/ad9910.c b/drivers/staging/iio/dds/ad9910.c
new file mode 100644
index 0000000..c59b4079
--- /dev/null
+++ b/drivers/staging/iio/dds/ad9910.c
@@ -0,0 +1,454 @@
+/*
+ * Driver for ADI Direct Digital Synthesis ad9910
+ *
+ * Copyright (c) 2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad9910"
+
+#define CFR1 0x0
+#define CFR2 0x1
+#define CFR3 0x2
+
+#define AUXDAC 0x3
+#define IOUPD 0x4
+#define FTW 0x7
+#define POW 0x8
+#define ASF 0x9
+#define MULTC 0x0A
+#define DIG_RAMPL 0x0B
+#define DIG_RAMPS 0x0C
+#define DIG_RAMPR 0x0D
+#define SIN_TONEP0 0x0E
+#define SIN_TONEP1 0x0F
+#define SIN_TONEP2 0x10
+#define SIN_TONEP3 0x11
+#define SIN_TONEP4 0x12
+#define SIN_TONEP5 0x13
+#define SIN_TONEP6 0x14
+#define SIN_TONEP7 0x15
+
+#define RAM_ENABLE	(1 << 7)
+
+#define MANUAL_OSK	(1 << 7)
+#define INVSIC		(1 << 6)
+#define DDS_SINEOP	(1)
+
+#define AUTO_OSK	(1)
+#define OSKEN		(1 << 1)
+#define LOAD_ARR	(1 << 2)
+#define CLR_PHA		(1 << 3)
+#define CLR_DIG		(1 << 4)
+#define ACLR_PHA	(1 << 5)
+#define ACLR_DIG	(1 << 6)
+#define LOAD_LRR	(1 << 7)
+
+#define LSB_FST		(1)
+#define SDIO_IPT	(1 << 1)
+#define EXT_PWD		(1 << 3)
+#define ADAC_PWD	(1 << 4)
+#define REFCLK_PWD	(1 << 5)
+#define DAC_PWD		(1 << 6)
+#define DIG_PWD		(1 << 7)
+
+#define ENA_AMP		(1)
+#define READ_FTW	(1)
+#define DIGR_LOW	(1 << 1)
+#define DIGR_HIGH	(1 << 2)
+#define DIGR_ENA	(1 << 3)
+#define SYNCCLK_ENA	(1 << 6)
+#define ITER_IOUPD	(1 << 7)
+
+#define TX_ENA		(1 << 1)
+#define PDCLK_INV	(1 << 2)
+#define PDCLK_ENB	(1 << 3)
+
+#define PARA_ENA	(1 << 4)
+#define SYNC_DIS	(1 << 5)
+#define DATA_ASS	(1 << 6)
+#define MATCH_ENA	(1 << 7)
+
+#define PLL_ENA		(1)
+#define PFD_RST		(1 << 2)
+#define REFCLK_RST	(1 << 6)
+#define REFCLK_BYP	(1 << 7)
+
+/* Register format: 1 byte addr + value */
+struct ad9910_config {
+	u8 auxdac[5];
+	u8 ioupd[5];
+	u8 ftw[5];
+	u8 pow[3];
+	u8 asf[5];
+	u8 multc[5];
+	u8 dig_rampl[9];
+	u8 dig_ramps[9];
+	u8 dig_rampr[5];
+	u8 sin_tonep0[9];
+	u8 sin_tonep1[9];
+	u8 sin_tonep2[9];
+	u8 sin_tonep3[9];
+	u8 sin_tonep4[9];
+	u8 sin_tonep5[9];
+	u8 sin_tonep6[9];
+	u8 sin_tonep7[9];
+};
+
+struct ad9910_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+};
+
+static ssize_t ad9910_set_parameter(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	struct ad9910_config *config = (struct ad9910_config *)buf;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad9910_state *st = idev->dev_data;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->auxdac[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->ioupd[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->ftw[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 3;
+	xfer.tx_buf = &config->pow[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->asf[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->multc[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 9;
+	xfer.tx_buf = &config->dig_rampl[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 9;
+	xfer.tx_buf = &config->dig_ramps[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->dig_rampr[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep0[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep1[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep2[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep3[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep4[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep5[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep6[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	xfer.len = 9;
+	xfer.tx_buf = &config->sin_tonep7[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9910_set_parameter, 0);
+
+static void ad9910_init(struct ad9910_state *st)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	u8 cfr[5];
+
+	cfr[0] = CFR1;
+	cfr[1] = 0;
+	cfr[2] = MANUAL_OSK | INVSIC | DDS_SINEOP;
+	cfr[3] = AUTO_OSK | OSKEN | ACLR_PHA | ACLR_DIG | LOAD_LRR;
+	cfr[4] = 0;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 5;
+	xfer.tx_buf = &cfr;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	cfr[0] = CFR2;
+	cfr[1] = ENA_AMP;
+	cfr[2] = READ_FTW | DIGR_ENA | ITER_IOUPD;
+	cfr[3] = TX_ENA | PDCLK_INV | PDCLK_ENB;
+	cfr[4] = PARA_ENA;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 5;
+	xfer.tx_buf = &cfr;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	cfr[0] = CFR3;
+	cfr[1] = PLL_ENA;
+	cfr[2] = 0;
+	cfr[3] = REFCLK_RST | REFCLK_BYP;
+	cfr[4] = 0;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 5;
+	xfer.tx_buf = &cfr;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	mutex_unlock(&st->lock);
+
+
+
+}
+
+static struct attribute *ad9910_attributes[] = {
+	&iio_dev_attr_dds.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad9910_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad9910_attributes,
+};
+
+static int __devinit ad9910_probe(struct spi_device *spi)
+{
+	struct ad9910_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad9910_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+	spi->max_speed_hz = 2000000;
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = 8;
+	spi_setup(spi);
+	ad9910_init(st);
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad9910_remove(struct spi_device *spi)
+{
+	struct ad9910_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad9910_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad9910_probe,
+	.remove = __devexit_p(ad9910_remove),
+};
+
+static __init int ad9910_spi_init(void)
+{
+	return spi_register_driver(&ad9910_driver);
+}
+module_init(ad9910_spi_init);
+
+static __exit void ad9910_spi_exit(void)
+{
+	spi_unregister_driver(&ad9910_driver);
+}
+module_exit(ad9910_spi_exit);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("Analog Devices ad9910 driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 28/36] staging: iio: dds: new driver for AD9951 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (26 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 27/36] staging: iio: dds: new driver for AD9910 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 29/36] staging: iio: meter: new driver for ADE7753/6 devices Mike Frysinger
                   ` (8 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Cliff Cai, Michael Hennerich

From: Cliff Cai <cliff.cai@analog.com>

Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/dds/Kconfig  |    7 +
 drivers/staging/iio/dds/Makefile |    1 +
 drivers/staging/iio/dds/ad9951.c |  254 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 262 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dds/ad9951.c

diff --git a/drivers/staging/iio/dds/Kconfig b/drivers/staging/iio/dds/Kconfig
index 518d0c1..d045ed6 100644
--- a/drivers/staging/iio/dds/Kconfig
+++ b/drivers/staging/iio/dds/Kconfig
@@ -37,3 +37,10 @@ config AD9910
 	help
 	  Say yes here to build support for Analog Devices DDS chip
 	  ad9910, provides direct access via sysfs.
+
+config AD9951
+	tristate "Analog Devices ad9951 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices DDS chip
+	  ad9951, provides direct access via sysfs.
diff --git a/drivers/staging/iio/dds/Makefile b/drivers/staging/iio/dds/Makefile
index d249b81..6f274ac 100644
--- a/drivers/staging/iio/dds/Makefile
+++ b/drivers/staging/iio/dds/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_AD9832) += ad9832.o
 obj-$(CONFIG_AD9850) += ad9850.o
 obj-$(CONFIG_AD9852) += ad9852.o
 obj-$(CONFIG_AD9910) += ad9910.o
+obj-$(CONFIG_AD9951) += ad9951.o
diff --git a/drivers/staging/iio/dds/ad9951.c b/drivers/staging/iio/dds/ad9951.c
new file mode 100644
index 0000000..bc3beff
--- /dev/null
+++ b/drivers/staging/iio/dds/ad9951.c
@@ -0,0 +1,254 @@
+/*
+ * Driver for ADI Direct Digital Synthesis ad9951
+ *
+ * Copyright (c) 2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad9951"
+
+#define CFR1 0x0
+#define CFR2 0x1
+
+#define AUTO_OSK	(1)
+#define OSKEN		(1 << 1)
+#define LOAD_ARR	(1 << 2)
+
+#define AUTO_SYNC	(1 << 7)
+
+#define LSB_FST		(1)
+#define SDIO_IPT	(1 << 1)
+#define CLR_PHA		(1 << 2)
+#define SINE_OPT	(1 << 4)
+#define ACLR_PHA	(1 << 5)
+
+#define VCO_RANGE	(1 << 2)
+
+#define CRS_OPT		(1 << 1)
+#define HMANU_SYNC	(1 << 2)
+#define HSPD_SYNC	(1 << 3)
+
+/* Register format: 1 byte addr + value */
+struct ad9951_config {
+	u8 asf[3];
+	u8 arr[2];
+	u8 ftw0[5];
+	u8 ftw1[3];
+};
+
+struct ad9951_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+};
+
+static ssize_t ad9951_set_parameter(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	struct ad9951_config *config = (struct ad9951_config *)buf;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad9951_state *st = idev->dev_data;
+
+	xfer.len = 3;
+	xfer.tx_buf = &config->asf[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 2;
+	xfer.tx_buf = &config->arr[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 5;
+	xfer.tx_buf = &config->ftw0[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	xfer.len = 3;
+	xfer.tx_buf = &config->ftw1[0];
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad9951_set_parameter, 0);
+
+static void ad9951_init(struct ad9951_state *st)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	u8 cfr[5];
+
+	cfr[0] = CFR1;
+	cfr[1] = 0;
+	cfr[2] = LSB_FST | CLR_PHA | SINE_OPT | ACLR_PHA;
+	cfr[3] = AUTO_OSK | OSKEN | LOAD_ARR;
+	cfr[4] = 0;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 5;
+	xfer.tx_buf = &cfr;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+	cfr[0] = CFR2;
+	cfr[1] = VCO_RANGE;
+	cfr[2] = HSPD_SYNC;
+	cfr[3] = 0;
+
+	mutex_lock(&st->lock);
+
+	xfer.len = 4;
+	xfer.tx_buf = &cfr;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	mutex_unlock(&st->lock);
+
+
+
+}
+
+static struct attribute *ad9951_attributes[] = {
+	&iio_dev_attr_dds.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad9951_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad9951_attributes,
+};
+
+static int __devinit ad9951_probe(struct spi_device *spi)
+{
+	struct ad9951_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad9951_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+	spi->max_speed_hz = 2000000;
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = 8;
+	spi_setup(spi);
+	ad9951_init(st);
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad9951_remove(struct spi_device *spi)
+{
+	struct ad9951_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad9951_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad9951_probe,
+	.remove = __devexit_p(ad9951_remove),
+};
+
+static __init int ad9951_spi_init(void)
+{
+	return spi_register_driver(&ad9951_driver);
+}
+module_init(ad9951_spi_init);
+
+static __exit void ad9951_spi_exit(void)
+{
+	spi_unregister_driver(&ad9951_driver);
+}
+module_exit(ad9951_spi_exit);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("Analog Devices ad9951 driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 29/36] staging: iio: meter: new driver for ADE7753/6 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (27 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 28/36] staging: iio: dds: new driver for AD9951 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 30/36] staging: iio: meter: new driver for ADE7754 devices Mike Frysinger
                   ` (7 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

This also kicks off the new meter subsection.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/Kconfig         |    2 +-
 drivers/staging/iio/Makefile        |    3 +-
 drivers/staging/iio/meter/Kconfig   |   11 +
 drivers/staging/iio/meter/Makefile  |    5 +
 drivers/staging/iio/meter/ade7753.c |  730 +++++++++++++++++++++++++++++++++++
 drivers/staging/iio/meter/ade7753.h |  140 +++++++
 drivers/staging/iio/meter/meter.h   |  396 +++++++++++++++++++
 7 files changed, 1285 insertions(+), 2 deletions(-)
 create mode 100644 drivers/staging/iio/meter/Kconfig
 create mode 100644 drivers/staging/iio/meter/Makefile
 create mode 100644 drivers/staging/iio/meter/ade7753.c
 create mode 100644 drivers/staging/iio/meter/ade7753.h
 create mode 100644 drivers/staging/iio/meter/meter.h

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 5e5bc47..ffe8912 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -49,7 +49,7 @@ source "drivers/staging/iio/gyro/Kconfig"
 source "drivers/staging/iio/imu/Kconfig"
 source "drivers/staging/iio/light/Kconfig"
 source "drivers/staging/iio/magnetometer/Kconfig"
-
+source "drivers/staging/iio/meter/Kconfig"
 source "drivers/staging/iio/trigger/Kconfig"
 
 endif # IIO
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 766a5d1..5b449a1 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -17,5 +17,6 @@ obj-y += dds/
 obj-y += gyro/
 obj-y += imu/
 obj-y += light/
-obj-y += trigger/
 obj-y += magnetometer/
+obj-y += meter/
+obj-y += trigger/
diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig
new file mode 100644
index 0000000..0c09c39
--- /dev/null
+++ b/drivers/staging/iio/meter/Kconfig
@@ -0,0 +1,11 @@
+#
+# IIO meter drivers configuration
+#
+comment "Active energy metering IC"
+
+config ADE7753
+	tristate "Analog Devices ADE7753/6 Single-Phase Multifunction Metering IC Driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices ADE7753 Single-Phase Multifunction
+	  Metering IC with di/dt Sensor Interface.
diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile
new file mode 100644
index 0000000..b4eac38
--- /dev/null
+++ b/drivers/staging/iio/meter/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for metering ic drivers
+#
+
+obj-$(CONFIG_ADE7753) += ade7753.o
diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c
new file mode 100644
index 0000000..e72afbd
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7753.c
@@ -0,0 +1,730 @@
+/*
+ * ADE7753 Single-Phase Multifunction Metering IC with di/dt Sensor Interface Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "meter.h"
+#include "ade7753.h"
+
+int ade7753_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7753_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7753_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7753_spi_write_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7753_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 3,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7753_WRITE_REG(reg_address);
+	st->tx[1] = (value >> 8) & 0xFF;
+	st->tx[2] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7753_spi_read_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7753_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7753_READ_REG(reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7753_spi_read_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7753_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 3,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7753_READ_REG(reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[1] << 8) | st->rx[2];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7753_spi_read_reg_24(struct device *dev,
+		u8 reg_address,
+		u32 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7753_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 4,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7753_READ_REG(reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+	st->tx[3] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t ade7753_read_8bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u8 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7753_spi_read_reg_8(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7753_read_16bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7753_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7753_read_24bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u32 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7753_spi_read_reg_24(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val & 0xFFFFFF);
+}
+
+static ssize_t ade7753_write_8bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7753_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t ade7753_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7753_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int ade7753_reset(struct device *dev)
+{
+	int ret;
+	u16 val;
+	ade7753_spi_read_reg_16(dev,
+			ADE7753_MODE,
+			&val);
+	val |= 1 << 6; /* Software Chip Reset */
+	ret = ade7753_spi_write_reg_16(dev,
+			ADE7753_MODE,
+			val);
+
+	return ret;
+}
+
+static ssize_t ade7753_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -1;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return ade7753_reset(dev);
+	}
+	return -1;
+}
+
+static IIO_DEV_ATTR_AENERGY(ade7753_read_24bit, ADE7753_AENERGY);
+static IIO_DEV_ATTR_LAENERGY(ade7753_read_24bit, ADE7753_LAENERGY);
+static IIO_DEV_ATTR_VAENERGY(ade7753_read_24bit, ADE7753_VAENERGY);
+static IIO_DEV_ATTR_LVAENERGY(ade7753_read_24bit, ADE7753_LVAENERGY);
+static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_CFDEN);
+static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_CFNUM);
+static IIO_DEV_ATTR_CHKSUM(ade7753_read_8bit, ADE7753_CHKSUM);
+static IIO_DEV_ATTR_PHCAL(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_PHCAL);
+static IIO_DEV_ATTR_APOS(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_APOS);
+static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_SAGCYC);
+static IIO_DEV_ATTR_SAGLVL(S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_SAGLVL);
+static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_LINECYC);
+static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_WDIV);
+static IIO_DEV_ATTR_IRMS(S_IWUSR | S_IRUGO,
+		ade7753_read_24bit,
+		NULL,
+		ADE7753_IRMS);
+static IIO_DEV_ATTR_VRMS(S_IRUGO,
+		ade7753_read_24bit,
+		NULL,
+		ADE7753_VRMS);
+static IIO_DEV_ATTR_IRMSOS(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_IRMSOS);
+static IIO_DEV_ATTR_VRMSOS(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_VRMSOS);
+static IIO_DEV_ATTR_WGAIN(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_WGAIN);
+static IIO_DEV_ATTR_VAGAIN(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_VAGAIN);
+static IIO_DEV_ATTR_PGA_GAIN(S_IWUSR | S_IRUGO,
+		ade7753_read_16bit,
+		ade7753_write_16bit,
+		ADE7753_GAIN);
+static IIO_DEV_ATTR_IPKLVL(S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_IPKLVL);
+static IIO_DEV_ATTR_VPKLVL(S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_VPKLVL);
+static IIO_DEV_ATTR_IPEAK(S_IRUGO,
+		ade7753_read_24bit,
+		NULL,
+		ADE7753_IPEAK);
+static IIO_DEV_ATTR_VPEAK(S_IRUGO,
+		ade7753_read_24bit,
+		NULL,
+		ADE7753_VPEAK);
+static IIO_DEV_ATTR_VPERIOD(S_IRUGO,
+		ade7753_read_16bit,
+		NULL,
+		ADE7753_PERIOD);
+static IIO_DEV_ATTR_CH_OFF(1, S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_CH1OS);
+static IIO_DEV_ATTR_CH_OFF(2, S_IWUSR | S_IRUGO,
+		ade7753_read_8bit,
+		ade7753_write_8bit,
+		ADE7753_CH2OS);
+
+static int ade7753_set_irq(struct device *dev, bool enable)
+{
+	int ret;
+	u8 irqen;
+	ret = ade7753_spi_read_reg_8(dev, ADE7753_IRQEN, &irqen);
+	if (ret)
+		goto error_ret;
+
+	if (enable)
+		irqen |= 1 << 3; /* Enables an interrupt when a data is
+				    present in the waveform register */
+	else
+		irqen &= ~(1 << 3);
+
+	ret = ade7753_spi_write_reg_8(dev, ADE7753_IRQEN, irqen);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	return ret;
+}
+
+/* Power down the device */
+int ade7753_stop_device(struct device *dev)
+{
+	int ret;
+	u16 val;
+	ade7753_spi_read_reg_16(dev,
+			ADE7753_MODE,
+			&val);
+	val |= 1 << 4;  /* AD converters can be turned off */
+	ret = ade7753_spi_write_reg_16(dev,
+			ADE7753_MODE,
+			val);
+
+	return ret;
+}
+
+static int ade7753_initial_setup(struct ade7753_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* use low spi speed for init */
+	st->us->mode = SPI_MODE_3;
+	spi_setup(st->us);
+
+	/* Disable IRQ */
+	ret = ade7753_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	ade7753_reset(dev);
+	msleep(ADE7753_STARTUP_DELAY);
+
+err_ret:
+	return ret;
+}
+
+static ssize_t ade7753_read_frequency(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret, len = 0;
+	u8 t;
+	int sps;
+	ret = ade7753_spi_read_reg_8(dev,
+			ADE7753_MODE,
+			&t);
+	if (ret)
+		return ret;
+
+	t = (t >> 11) & 0x3;
+	sps = 27900 / (1 + t);
+
+	len = sprintf(buf, "%d SPS\n", sps);
+	return len;
+}
+
+static ssize_t ade7753_write_frequency(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7753_state *st = iio_dev_get_devdata(indio_dev);
+	unsigned long val;
+	int ret;
+	u16 reg, t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	t = (27900 / val);
+	if (t > 0)
+		t--;
+
+	if (t > 1)
+		st->us->max_speed_hz = ADE7753_SPI_SLOW;
+	else
+		st->us->max_speed_hz = ADE7753_SPI_FAST;
+
+	ret = ade7753_spi_read_reg_16(dev,
+			ADE7753_MODE,
+			&reg);
+	if (ret)
+		goto out;
+
+	reg &= ~(3 << 11);
+	reg |= t << 11;
+
+	ret = ade7753_spi_write_reg_16(dev,
+			ADE7753_MODE,
+			reg);
+
+out:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+static IIO_DEV_ATTR_TEMP_RAW(ade7753_read_8bit);
+static IIO_CONST_ATTR(temp_offset, "-25 C");
+static IIO_CONST_ATTR(temp_scale, "0.67 C");
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+		ade7753_read_frequency,
+		ade7753_write_frequency);
+
+static IIO_DEV_ATTR_RESET(ade7753_write_reset);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500");
+
+static IIO_CONST_ATTR(name, "ade7753");
+
+static struct attribute *ade7753_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group ade7753_event_attribute_group = {
+	.attrs = ade7753_event_attributes,
+};
+
+static struct attribute *ade7753_attributes[] = {
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_phcal.dev_attr.attr,
+	&iio_dev_attr_cfden.dev_attr.attr,
+	&iio_dev_attr_aenergy.dev_attr.attr,
+	&iio_dev_attr_laenergy.dev_attr.attr,
+	&iio_dev_attr_vaenergy.dev_attr.attr,
+	&iio_dev_attr_lvaenergy.dev_attr.attr,
+	&iio_dev_attr_cfnum.dev_attr.attr,
+	&iio_dev_attr_apos.dev_attr.attr,
+	&iio_dev_attr_sagcyc.dev_attr.attr,
+	&iio_dev_attr_saglvl.dev_attr.attr,
+	&iio_dev_attr_linecyc.dev_attr.attr,
+	&iio_dev_attr_chksum.dev_attr.attr,
+	&iio_dev_attr_pga_gain.dev_attr.attr,
+	&iio_dev_attr_wgain.dev_attr.attr,
+	&iio_dev_attr_choff_1.dev_attr.attr,
+	&iio_dev_attr_choff_2.dev_attr.attr,
+	&iio_dev_attr_wdiv.dev_attr.attr,
+	&iio_dev_attr_irms.dev_attr.attr,
+	&iio_dev_attr_vrms.dev_attr.attr,
+	&iio_dev_attr_irmsos.dev_attr.attr,
+	&iio_dev_attr_vrmsos.dev_attr.attr,
+	&iio_dev_attr_vagain.dev_attr.attr,
+	&iio_dev_attr_ipklvl.dev_attr.attr,
+	&iio_dev_attr_vpklvl.dev_attr.attr,
+	&iio_dev_attr_ipeak.dev_attr.attr,
+	&iio_dev_attr_vpeak.dev_attr.attr,
+	&iio_dev_attr_vperiod.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ade7753_attribute_group = {
+	.attrs = ade7753_attributes,
+};
+
+static int __devinit ade7753_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct ade7753_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADE7753_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADE7753_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &ade7753_event_attribute_group;
+	st->indio_dev->attrs = &ade7753_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = ade7753_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = ade7753_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				"ade7753");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = ade7753_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = ade7753_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		ade7753_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	ade7753_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	ade7753_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int ade7753_remove(struct spi_device *spi)
+{
+	int ret;
+	struct ade7753_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	ret = ade7753_stop_device(&(indio_dev->dev));
+	if (ret)
+		goto err_ret;
+
+	flush_scheduled_work();
+
+	ade7753_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	ade7753_uninitialize_ring(indio_dev->ring);
+	ade7753_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+
+err_ret:
+	return ret;
+}
+
+static struct spi_driver ade7753_driver = {
+	.driver = {
+		.name = "ade7753",
+		.owner = THIS_MODULE,
+	},
+	.probe = ade7753_probe,
+	.remove = __devexit_p(ade7753_remove),
+};
+
+static __init int ade7753_init(void)
+{
+	return spi_register_driver(&ade7753_driver);
+}
+module_init(ade7753_init);
+
+static __exit void ade7753_exit(void)
+{
+	spi_unregister_driver(&ade7753_driver);
+}
+module_exit(ade7753_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7753/6 Single-Phase Multifunction Metering IC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7753.h b/drivers/staging/iio/meter/ade7753.h
new file mode 100644
index 0000000..a3722b8
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7753.h
@@ -0,0 +1,140 @@
+#ifndef _ADE7753_H
+#define _ADE7753_H
+
+#define ADE7753_WAVEFORM   0x01
+#define ADE7753_AENERGY    0x02
+#define ADE7753_RAENERGY   0x03
+#define ADE7753_LAENERGY   0x04
+#define ADE7753_VAENERGY   0x05
+#define ADE7753_RVAENERGY  0x06
+#define ADE7753_LVAENERGY  0x07
+#define ADE7753_LVARENERGY 0x08
+#define ADE7753_MODE       0x09
+#define ADE7753_IRQEN      0x0A
+#define ADE7753_STATUS     0x0B
+#define ADE7753_RSTSTATUS  0x0C
+#define ADE7753_CH1OS      0x0D
+#define ADE7753_CH2OS      0x0E
+#define ADE7753_GAIN       0x0F
+#define ADE7753_PHCAL      0x10
+#define ADE7753_APOS       0x11
+#define ADE7753_WGAIN      0x12
+#define ADE7753_WDIV       0x13
+#define ADE7753_CFNUM      0x14
+#define ADE7753_CFDEN      0x15
+#define ADE7753_IRMS       0x16
+#define ADE7753_VRMS       0x17
+#define ADE7753_IRMSOS     0x18
+#define ADE7753_VRMSOS     0x19
+#define ADE7753_VAGAIN     0x1A
+#define ADE7753_VADIV      0x1B
+#define ADE7753_LINECYC    0x1C
+#define ADE7753_ZXTOUT     0x1D
+#define ADE7753_SAGCYC     0x1E
+#define ADE7753_SAGLVL     0x1F
+#define ADE7753_IPKLVL     0x20
+#define ADE7753_VPKLVL     0x21
+#define ADE7753_IPEAK      0x22
+#define ADE7753_RSTIPEAK   0x23
+#define ADE7753_VPEAK      0x24
+#define ADE7753_RSTVPEAK   0x25
+#define ADE7753_TEMP       0x26
+#define ADE7753_PERIOD     0x27
+#define ADE7753_TMODE      0x3D
+#define ADE7753_CHKSUM     0x3E
+#define ADE7753_DIEREV     0x3F
+
+#define ADE7753_READ_REG(a)    a
+#define ADE7753_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7753_MAX_TX    4
+#define ADE7753_MAX_RX    4
+#define ADE7753_STARTUP_DELAY 1
+
+#define ADE7753_SPI_SLOW	(u32)(300 * 1000)
+#define ADE7753_SPI_BURST	(u32)(1000 * 1000)
+#define ADE7753_SPI_FAST	(u32)(2000 * 1000)
+
+#define DRIVER_NAME		"ade7753"
+
+/**
+ * struct ade7753_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct ade7753_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum ade7753_scan {
+	ADE7753_SCAN_ACTIVE_POWER,
+	ADE7753_SCAN_CH1,
+	ADE7753_SCAN_CH2,
+};
+
+void ade7753_remove_trigger(struct iio_dev *indio_dev);
+int ade7753_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t ade7753_read_data_from_ring(struct device *dev,
+		struct device_attribute *attr,
+		char *buf);
+
+
+int ade7753_configure_ring(struct iio_dev *indio_dev);
+void ade7753_unconfigure_ring(struct iio_dev *indio_dev);
+
+int ade7753_initialize_ring(struct iio_ring_buffer *ring);
+void ade7753_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void ade7753_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7753_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+ade7753_read_data_from_ring(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return 0;
+}
+
+static int ade7753_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void ade7753_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7753_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+static inline void ade7753_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif
diff --git a/drivers/staging/iio/meter/meter.h b/drivers/staging/iio/meter/meter.h
new file mode 100644
index 0000000..142c50d
--- /dev/null
+++ b/drivers/staging/iio/meter/meter.h
@@ -0,0 +1,396 @@
+#include "../sysfs.h"
+
+/* metering ic types of attribute */
+
+#define IIO_DEV_ATTR_CURRENT_A_OFFSET(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(current_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_B_OFFSET(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(current_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_C_OFFSET(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(current_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOLT_A_OFFSET(_mode, _show, _store, _addr)      \
+	IIO_DEVICE_ATTR(volt_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOLT_B_OFFSET(_mode, _show, _store, _addr)      \
+	IIO_DEVICE_ATTR(volt_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOLT_C_OFFSET(_mode, _show, _store, _addr)      \
+	IIO_DEVICE_ATTR(volt_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(reactive_power_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(reactive_power_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(reactive_power_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(active_power_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(active_power_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(active_power_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_A_GAIN(_mode, _show, _store, _addr)		\
+	IIO_DEVICE_ATTR(current_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_B_GAIN(_mode, _show, _store, _addr)		\
+	IIO_DEVICE_ATTR(current_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_C_GAIN(_mode, _show, _store, _addr)		\
+	IIO_DEVICE_ATTR(current_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(apparent_power_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(apparent_power_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(apparent_power_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(active_power_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(active_power_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(active_power_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(active_power_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(reactive_power_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(reactive_power_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(reactive_power_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_A(_show, _addr)			\
+	IIO_DEVICE_ATTR(current_a, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_B(_show, _addr)			\
+	IIO_DEVICE_ATTR(current_b, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_C(_show, _addr)			\
+	IIO_DEVICE_ATTR(current_c, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VOLT_A(_show, _addr)			\
+	IIO_DEVICE_ATTR(volt_a, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VOLT_B(_show, _addr)			\
+	IIO_DEVICE_ATTR(volt_b, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VOLT_C(_show, _addr)			\
+	IIO_DEVICE_ATTR(volt_c, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(aenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(lenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_RAENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(raenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LAENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(laenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VAENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(vaenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LVAENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(lvaenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_RVAENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(rvaenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LVARENERGY(_show, _addr)			\
+	IIO_DEVICE_ATTR(lvarenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CHKSUM(_show, _addr)                       \
+	IIO_DEVICE_ATTR(chksum, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ANGLE0(_show, _addr)                       \
+	IIO_DEVICE_ATTR(angle0, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ANGLE1(_show, _addr)                       \
+	IIO_DEVICE_ATTR(angle1, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ANGLE2(_show, _addr)                       \
+	IIO_DEVICE_ATTR(angle2, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AWATTHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(awatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BWATTHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(bwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CWATTHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(cwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AFWATTHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(afwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BFWATTHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(bfwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CFWATTHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(cfwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AVARHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(avarhr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BVARHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(bvarhr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CVARHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(cvarhr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AVAHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(avahr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BVAHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(bvahr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CVAHR(_show, _addr)			\
+	IIO_DEVICE_ATTR(cvahr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_IOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(ios, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(vos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_PHCAL(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(phcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APHCAL(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(aphcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BPHCAL(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(bphcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CPHCAL(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cphcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(apos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AAPOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(aapos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BAPOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(bapos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CAPOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(capos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVRMSGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(avrmsgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVRMSGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(bvrmsgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVRMSGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cvrmsgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AIGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(aigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BIGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(bigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CIGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_NIGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(nigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(avgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(bvgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cvgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_WGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(wgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_WDIV(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(wdiv, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CFNUM(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cfnum, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CFDEN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cfden, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CF1DEN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cf1den, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CF2DEN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cf2den, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CF3DEN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cf3den, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(irms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(vrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AIRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(airms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BIRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(birms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CIRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cirms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_NIRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(nirms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(avrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(bvrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVRMS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cvrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(irmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(vrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AIRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(airmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BIRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(birmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CIRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cirmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(avrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(bvrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVRMSOS(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cvrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VAGAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(vagain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_PGA_GAIN(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(pga_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VADIV(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(vadiv, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_LINECYC(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(linecyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_SAGCYC(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(sagcyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CFCYC(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(cfcyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_PEAKCYC(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(peakcyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_SAGLVL(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(saglvl, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IPKLVL(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(ipklvl, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VPKLVL(_mode, _show, _store, _addr)                \
+	IIO_DEVICE_ATTR(vpklvl, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IPEAK(_mode, _show, _store, _addr)			\
+	IIO_DEVICE_ATTR(ipeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_RIPEAK(_mode, _show, _store, _addr)			\
+	IIO_DEVICE_ATTR(ripeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VPEAK(_mode, _show, _store, _addr)			\
+	IIO_DEVICE_ATTR(vpeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_RVPEAK(_mode, _show, _store, _addr)			\
+	IIO_DEVICE_ATTR(rvpeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VPERIOD(_mode, _show, _store, _addr)			\
+	IIO_DEVICE_ATTR(vperiod, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CH_OFF(_num, _mode, _show, _store, _addr)			\
+  IIO_DEVICE_ATTR(choff_##_num, _mode, _show, _store, _addr)
+
+/* active energy register, AENERGY, is more than half full */
+#define IIO_EVENT_ATTR_AENERGY_HALF_FULL(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(aenergy_half_full, _evlist, _show, _store, _mask)
+
+/* a SAG on the line voltage */
+#define IIO_EVENT_ATTR_LINE_VOLT_SAG(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(line_volt_sag, _evlist, _show, _store, _mask)
+
+/*
+ * Indicates the end of energy accumulation over an integer number
+ * of half line cycles
+ */
+#define IIO_EVENT_ATTR_CYCEND(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(cycend, _evlist, _show, _store, _mask)
+
+/* on the rising and falling edge of the the voltage waveform */
+#define IIO_EVENT_ATTR_ZERO_CROSS(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(zero_cross, _evlist, _show, _store, _mask)
+
+/* the active energy register has overflowed */
+#define IIO_EVENT_ATTR_AENERGY_OVERFLOW(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(aenergy_overflow, _evlist, _show, _store, _mask)
+
+/* the apparent energy register has overflowed */
+#define IIO_EVENT_ATTR_VAENERGY_OVERFLOW(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(vaenergy_overflow, _evlist, _show, _store, _mask)
+
+/* the active energy register, VAENERGY, is more than half full */
+#define IIO_EVENT_ATTR_VAENERGY_HALF_FULL(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(vaenergy_half_full, _evlist, _show, _store, _mask)
+
+/* the power has gone from negative to positive */
+#define IIO_EVENT_ATTR_PPOS(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(ppos, _evlist, _show, _store, _mask)
+
+/* the power has gone from positive to negative */
+#define IIO_EVENT_ATTR_PNEG(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(pneg, _evlist, _show, _store, _mask)
+
+/* waveform sample from Channel 1 has exceeded the IPKLVL value */
+#define IIO_EVENT_ATTR_IPKLVL_EXC(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(ipklvl_exc, _evlist, _show, _store, _mask)
+
+/* waveform sample from Channel 2 has exceeded the VPKLVL value */
+#define IIO_EVENT_ATTR_VPKLVL_EXC(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(vpklvl_exc, _evlist, _show, _store, _mask)
+
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 30/36] staging: iio: meter: new driver for ADE7754 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (28 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 29/36] staging: iio: meter: new driver for ADE7753/6 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 31/36] staging: iio: meter: new driver for ADE7758 devices Mike Frysinger
                   ` (6 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/meter/Kconfig   |    7 +
 drivers/staging/iio/meter/Makefile  |    1 +
 drivers/staging/iio/meter/ade7754.c |  756 +++++++++++++++++++++++++++++++++++
 drivers/staging/iio/meter/ade7754.h |  161 ++++++++
 4 files changed, 925 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/meter/ade7754.c
 create mode 100644 drivers/staging/iio/meter/ade7754.h

diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig
index 0c09c39..19f20f4 100644
--- a/drivers/staging/iio/meter/Kconfig
+++ b/drivers/staging/iio/meter/Kconfig
@@ -9,3 +9,10 @@ config ADE7753
 	help
 	  Say yes here to build support for Analog Devices ADE7753 Single-Phase Multifunction
 	  Metering IC with di/dt Sensor Interface.
+
+config ADE7754
+	tristate "Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices ADE7754 Polyphase
+	  Multifunction Energy Metering IC Driver.
diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile
index b4eac38..7f65b99 100644
--- a/drivers/staging/iio/meter/Makefile
+++ b/drivers/staging/iio/meter/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_ADE7753) += ade7753.o
+obj-$(CONFIG_ADE7754) += ade7754.o
diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c
new file mode 100644
index 0000000..23dedfa
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7754.c
@@ -0,0 +1,756 @@
+/*
+ * ADE7754 Polyphase Multifunction Energy Metering IC Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "meter.h"
+#include "ade7754.h"
+
+static int ade7754_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7754_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7754_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7754_spi_write_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7754_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 3,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7754_WRITE_REG(reg_address);
+	st->tx[1] = (value >> 8) & 0xFF;
+	st->tx[2] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7754_spi_read_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7754_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7754_READ_REG(reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7754_spi_read_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7754_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 3,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7754_READ_REG(reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[1] << 8) | st->rx[2];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7754_spi_read_reg_24(struct device *dev,
+		u8 reg_address,
+		u32 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7754_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 4,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7754_READ_REG(reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+	st->tx[3] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t ade7754_read_8bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u8 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7754_spi_read_reg_8(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7754_read_16bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7754_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7754_read_24bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u32 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7754_spi_read_reg_24(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val & 0xFFFFFF);
+}
+
+static ssize_t ade7754_write_8bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7754_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t ade7754_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7754_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int ade7754_reset(struct device *dev)
+{
+	int ret;
+	u8 val;
+	ade7754_spi_read_reg_8(dev,
+			ADE7754_OPMODE,
+			&val);
+	val |= 1 << 6; /* Software Chip Reset */
+	ret = ade7754_spi_write_reg_8(dev,
+			ADE7754_OPMODE,
+			val);
+
+	return ret;
+}
+
+
+static ssize_t ade7754_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -1;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return ade7754_reset(dev);
+	}
+	return -1;
+}
+
+static IIO_DEV_ATTR_AENERGY(ade7754_read_24bit, ADE7754_AENERGY);
+static IIO_DEV_ATTR_LAENERGY(ade7754_read_24bit, ADE7754_LAENERGY);
+static IIO_DEV_ATTR_VAENERGY(ade7754_read_24bit, ADE7754_VAENERGY);
+static IIO_DEV_ATTR_LVAENERGY(ade7754_read_24bit, ADE7754_LVAENERGY);
+static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO,
+		ade7754_read_8bit,
+		ade7754_write_8bit,
+		ADE7754_VPEAK);
+static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO,
+		ade7754_read_8bit,
+		ade7754_write_8bit,
+		ADE7754_VPEAK);
+static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO,
+		ade7754_read_8bit,
+		ade7754_write_8bit,
+		ADE7754_APHCAL);
+static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO,
+		ade7754_read_8bit,
+		ade7754_write_8bit,
+		ADE7754_BPHCAL);
+static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO,
+		ade7754_read_8bit,
+		ade7754_write_8bit,
+		ADE7754_CPHCAL);
+static IIO_DEV_ATTR_AAPOS(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_AAPOS);
+static IIO_DEV_ATTR_BAPOS(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_BAPOS);
+static IIO_DEV_ATTR_CAPOS(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_CAPOS);
+static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO,
+		ade7754_read_8bit,
+		ade7754_write_8bit,
+		ADE7754_WDIV);
+static IIO_DEV_ATTR_VADIV(S_IWUSR | S_IRUGO,
+		ade7754_read_8bit,
+		ade7754_write_8bit,
+		ADE7754_VADIV);
+static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_CFNUM);
+static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_CFDEN);
+static IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_AAPGAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_BAPGAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(S_IWUSR | S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_CAPGAIN);
+static IIO_DEV_ATTR_AIRMS(S_IRUGO,
+		ade7754_read_24bit,
+		NULL,
+		ADE7754_AIRMS);
+static IIO_DEV_ATTR_BIRMS(S_IRUGO,
+		ade7754_read_24bit,
+		NULL,
+		ADE7754_BIRMS);
+static IIO_DEV_ATTR_CIRMS(S_IRUGO,
+		ade7754_read_24bit,
+		NULL,
+		ADE7754_CIRMS);
+static IIO_DEV_ATTR_AVRMS(S_IRUGO,
+		ade7754_read_24bit,
+		NULL,
+		ADE7754_AVRMS);
+static IIO_DEV_ATTR_BVRMS(S_IRUGO,
+		ade7754_read_24bit,
+		NULL,
+		ADE7754_BVRMS);
+static IIO_DEV_ATTR_CVRMS(S_IRUGO,
+		ade7754_read_24bit,
+		NULL,
+		ADE7754_CVRMS);
+static IIO_DEV_ATTR_AIRMSOS(S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_AIRMSOS);
+static IIO_DEV_ATTR_BIRMSOS(S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_BIRMSOS);
+static IIO_DEV_ATTR_CIRMSOS(S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_CIRMSOS);
+static IIO_DEV_ATTR_AVRMSOS(S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_AVRMSOS);
+static IIO_DEV_ATTR_BVRMSOS(S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_BVRMSOS);
+static IIO_DEV_ATTR_CVRMSOS(S_IRUGO,
+		ade7754_read_16bit,
+		ade7754_write_16bit,
+		ADE7754_CVRMSOS);
+
+static int ade7754_set_irq(struct device *dev, bool enable)
+{
+	int ret;
+	u16 irqen;
+	ret = ade7754_spi_read_reg_16(dev, ADE7754_IRQEN, &irqen);
+	if (ret)
+		goto error_ret;
+
+	if (enable)
+		irqen |= 1 << 14; /* Enables an interrupt when a data is
+				     present in the waveform register */
+	else
+		irqen &= ~(1 << 14);
+
+	ret = ade7754_spi_write_reg_16(dev, ADE7754_IRQEN, irqen);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	return ret;
+}
+
+/* Power down the device */
+static int ade7754_stop_device(struct device *dev)
+{
+	int ret;
+	u8 val;
+	ade7754_spi_read_reg_8(dev,
+			ADE7754_OPMODE,
+			&val);
+	val |= 7 << 3;  /* ADE7754 powered down */
+	ret = ade7754_spi_write_reg_8(dev,
+			ADE7754_OPMODE,
+			val);
+
+	return ret;
+}
+
+static int ade7754_initial_setup(struct ade7754_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* use low spi speed for init */
+	st->us->mode = SPI_MODE_3;
+	spi_setup(st->us);
+
+	/* Disable IRQ */
+	ret = ade7754_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	ade7754_reset(dev);
+	msleep(ADE7754_STARTUP_DELAY);
+
+err_ret:
+	return ret;
+}
+
+static ssize_t ade7754_read_frequency(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret, len = 0;
+	u8 t;
+	int sps;
+	ret = ade7754_spi_read_reg_8(dev,
+			ADE7754_WAVMODE,
+			&t);
+	if (ret)
+		return ret;
+
+	t = (t >> 3) & 0x3;
+	sps = 26000 / (1 + t);
+
+	len = sprintf(buf, "%d SPS\n", sps);
+	return len;
+}
+
+static ssize_t ade7754_write_frequency(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7754_state *st = iio_dev_get_devdata(indio_dev);
+	unsigned long val;
+	int ret;
+	u8 reg, t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	t = (26000 / val);
+	if (t > 0)
+		t--;
+
+	if (t > 1)
+		st->us->max_speed_hz = ADE7754_SPI_SLOW;
+	else
+		st->us->max_speed_hz = ADE7754_SPI_FAST;
+
+	ret = ade7754_spi_read_reg_8(dev,
+			ADE7754_WAVMODE,
+			&reg);
+	if (ret)
+		goto out;
+
+	reg &= ~(3 << 3);
+	reg |= t << 3;
+
+	ret = ade7754_spi_write_reg_8(dev,
+			ADE7754_WAVMODE,
+			reg);
+
+out:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+static IIO_DEV_ATTR_TEMP_RAW(ade7754_read_8bit);
+static IIO_CONST_ATTR(temp_offset, "129 C");
+static IIO_CONST_ATTR(temp_scale, "4 C");
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+		ade7754_read_frequency,
+		ade7754_write_frequency);
+
+static IIO_DEV_ATTR_RESET(ade7754_write_reset);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26000 13000 65000 33000");
+
+static IIO_CONST_ATTR(name, "ade7754");
+
+static struct attribute *ade7754_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group ade7754_event_attribute_group = {
+	.attrs = ade7754_event_attributes,
+};
+
+static struct attribute *ade7754_attributes[] = {
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_aenergy.dev_attr.attr,
+	&iio_dev_attr_laenergy.dev_attr.attr,
+	&iio_dev_attr_vaenergy.dev_attr.attr,
+	&iio_dev_attr_lvaenergy.dev_attr.attr,
+	&iio_dev_attr_vpeak.dev_attr.attr,
+	&iio_dev_attr_ipeak.dev_attr.attr,
+	&iio_dev_attr_aphcal.dev_attr.attr,
+	&iio_dev_attr_bphcal.dev_attr.attr,
+	&iio_dev_attr_cphcal.dev_attr.attr,
+	&iio_dev_attr_aapos.dev_attr.attr,
+	&iio_dev_attr_bapos.dev_attr.attr,
+	&iio_dev_attr_capos.dev_attr.attr,
+	&iio_dev_attr_wdiv.dev_attr.attr,
+	&iio_dev_attr_vadiv.dev_attr.attr,
+	&iio_dev_attr_cfnum.dev_attr.attr,
+	&iio_dev_attr_cfden.dev_attr.attr,
+	&iio_dev_attr_active_power_a_gain.dev_attr.attr,
+	&iio_dev_attr_active_power_b_gain.dev_attr.attr,
+	&iio_dev_attr_active_power_c_gain.dev_attr.attr,
+	&iio_dev_attr_airms.dev_attr.attr,
+	&iio_dev_attr_birms.dev_attr.attr,
+	&iio_dev_attr_cirms.dev_attr.attr,
+	&iio_dev_attr_avrms.dev_attr.attr,
+	&iio_dev_attr_bvrms.dev_attr.attr,
+	&iio_dev_attr_cvrms.dev_attr.attr,
+	&iio_dev_attr_airmsos.dev_attr.attr,
+	&iio_dev_attr_birmsos.dev_attr.attr,
+	&iio_dev_attr_cirmsos.dev_attr.attr,
+	&iio_dev_attr_avrmsos.dev_attr.attr,
+	&iio_dev_attr_bvrmsos.dev_attr.attr,
+	&iio_dev_attr_cvrmsos.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ade7754_attribute_group = {
+	.attrs = ade7754_attributes,
+};
+
+
+
+static int __devinit ade7754_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct ade7754_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADE7754_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADE7754_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &ade7754_event_attribute_group;
+	st->indio_dev->attrs = &ade7754_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = ade7754_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = ade7754_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				"ade7754");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = ade7754_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = ade7754_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		ade7754_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	ade7754_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	ade7754_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int ade7754_remove(struct spi_device *spi)
+{
+	int ret;
+	struct ade7754_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	ret = ade7754_stop_device(&(indio_dev->dev));
+	if (ret)
+		goto err_ret;
+
+	flush_scheduled_work();
+
+	ade7754_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	ade7754_uninitialize_ring(indio_dev->ring);
+	ade7754_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+
+err_ret:
+	return ret;
+}
+
+static struct spi_driver ade7754_driver = {
+	.driver = {
+		.name = "ade7754",
+		.owner = THIS_MODULE,
+	},
+	.probe = ade7754_probe,
+	.remove = __devexit_p(ade7754_remove),
+};
+
+static __init int ade7754_init(void)
+{
+	return spi_register_driver(&ade7754_driver);
+}
+module_init(ade7754_init);
+
+static __exit void ade7754_exit(void)
+{
+	spi_unregister_driver(&ade7754_driver);
+}
+module_exit(ade7754_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7754.h b/drivers/staging/iio/meter/ade7754.h
new file mode 100644
index 0000000..f6a3e4b
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7754.h
@@ -0,0 +1,161 @@
+#ifndef _ADE7754_H
+#define _ADE7754_H
+
+#define ADE7754_AENERGY   0x01
+#define ADE7754_RAENERGY  0x02
+#define ADE7754_LAENERGY  0x03
+#define ADE7754_VAENERGY  0x04
+#define ADE7754_RVAENERGY 0x05
+#define ADE7754_LVAENERGY 0x06
+#define ADE7754_PERIOD    0x07
+#define ADE7754_TEMP      0x08
+#define ADE7754_WFORM     0x09
+#define ADE7754_OPMODE    0x0A
+#define ADE7754_MMODE     0x0B
+#define ADE7754_WAVMODE   0x0C
+#define ADE7754_WATMODE   0x0D
+#define ADE7754_VAMODE    0x0E
+#define ADE7754_IRQEN     0x0F
+#define ADE7754_STATUS    0x10
+#define ADE7754_RSTATUS   0x11
+#define ADE7754_ZXTOUT    0x12
+#define ADE7754_LINCYC    0x13
+#define ADE7754_SAGCYC    0x14
+#define ADE7754_SAGLVL    0x15
+#define ADE7754_VPEAK     0x16
+#define ADE7754_IPEAK     0x17
+#define ADE7754_GAIN      0x18
+#define ADE7754_AWG       0x19
+#define ADE7754_BWG       0x1A
+#define ADE7754_CWG       0x1B
+#define ADE7754_AVAG      0x1C
+#define ADE7754_BVAG      0x1D
+#define ADE7754_CVAG      0x1E
+#define ADE7754_APHCAL    0x1F
+#define ADE7754_BPHCAL    0x20
+#define ADE7754_CPHCAL    0x21
+#define ADE7754_AAPOS     0x22
+#define ADE7754_BAPOS     0x23
+#define ADE7754_CAPOS     0x24
+#define ADE7754_CFNUM     0x25
+#define ADE7754_CFDEN     0x26
+#define ADE7754_WDIV      0x27
+#define ADE7754_VADIV     0x28
+#define ADE7754_AIRMS     0x29
+#define ADE7754_BIRMS     0x2A
+#define ADE7754_CIRMS     0x2B
+#define ADE7754_AVRMS     0x2C
+#define ADE7754_BVRMS     0x2D
+#define ADE7754_CVRMS     0x2E
+#define ADE7754_AIRMSOS   0x2F
+#define ADE7754_BIRMSOS   0x30
+#define ADE7754_CIRMSOS   0x31
+#define ADE7754_AVRMSOS   0x32
+#define ADE7754_BVRMSOS   0x33
+#define ADE7754_CVRMSOS   0x34
+#define ADE7754_AAPGAIN   0x35
+#define ADE7754_BAPGAIN   0x36
+#define ADE7754_CAPGAIN   0x37
+#define ADE7754_AVGAIN    0x38
+#define ADE7754_BVGAIN    0x39
+#define ADE7754_CVGAIN    0x3A
+#define ADE7754_CHKSUM    0x3E
+#define ADE7754_VERSION   0x3F
+
+#define ADE7754_READ_REG(a)    a
+#define ADE7754_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7754_MAX_TX    4
+#define ADE7754_MAX_RX    4
+#define ADE7754_STARTUP_DELAY 1
+
+#define ADE7754_SPI_SLOW	(u32)(300 * 1000)
+#define ADE7754_SPI_BURST	(u32)(1000 * 1000)
+#define ADE7754_SPI_FAST	(u32)(2000 * 1000)
+
+#define DRIVER_NAME		"ade7754"
+
+/**
+ * struct ade7754_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct ade7754_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum ade7754_scan {
+	ADE7754_SCAN_PHA_V,
+	ADE7754_SCAN_PHB_V,
+	ADE7754_SCAN_PHC_V,
+	ADE7754_SCAN_PHA_I,
+	ADE7754_SCAN_PHB_I,
+	ADE7754_SCAN_PHC_I,
+};
+
+void ade7754_remove_trigger(struct iio_dev *indio_dev);
+int ade7754_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t ade7754_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+
+int ade7754_configure_ring(struct iio_dev *indio_dev);
+void ade7754_unconfigure_ring(struct iio_dev *indio_dev);
+
+int ade7754_initialize_ring(struct iio_ring_buffer *ring);
+void ade7754_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void ade7754_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7754_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+ade7754_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int ade7754_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void ade7754_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7754_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+static inline void ade7754_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 31/36] staging: iio: meter: new driver for ADE7758 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (29 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 30/36] staging: iio: meter: new driver for ADE7754 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 32/36] staging: iio: meter: new driver for ADE7759 devices Mike Frysinger
                   ` (5 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/meter/Kconfig           |    9 +
 drivers/staging/iio/meter/Makefile          |    4 +
 drivers/staging/iio/meter/ade7758.h         |  171 ++++++
 drivers/staging/iio/meter/ade7758_core.c    |  866 +++++++++++++++++++++++++++
 drivers/staging/iio/meter/ade7758_ring.c    |  212 +++++++
 drivers/staging/iio/meter/ade7758_trigger.c |  125 ++++
 6 files changed, 1387 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/meter/ade7758.h
 create mode 100644 drivers/staging/iio/meter/ade7758_core.c
 create mode 100644 drivers/staging/iio/meter/ade7758_ring.c
 create mode 100644 drivers/staging/iio/meter/ade7758_trigger.c

diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig
index 19f20f4..be88bb8 100644
--- a/drivers/staging/iio/meter/Kconfig
+++ b/drivers/staging/iio/meter/Kconfig
@@ -16,3 +16,12 @@ config ADE7754
 	help
 	  Say yes here to build support for Analog Devices ADE7754 Polyphase
 	  Multifunction Energy Metering IC Driver.
+
+config ADE7758
+	tristate "Analog Devices ADE7758 Poly Phase Multifunction Energy Metering IC Driver"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices ADE7758 Polyphase
+	  Multifunction Energy Metering IC with Per Phase Information Driver.
diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile
index 7f65b99..8e1a71d 100644
--- a/drivers/staging/iio/meter/Makefile
+++ b/drivers/staging/iio/meter/Makefile
@@ -4,3 +4,7 @@
 
 obj-$(CONFIG_ADE7753) += ade7753.o
 obj-$(CONFIG_ADE7754) += ade7754.o
+
+ade7758-y             := ade7758_core.o
+ade7758-$(CONFIG_IIO_RING_BUFFER) += ade7758_ring.o ade7758_trigger.o
+obj-$(CONFIG_ADE7758) += ade7758.o
diff --git a/drivers/staging/iio/meter/ade7758.h b/drivers/staging/iio/meter/ade7758.h
new file mode 100644
index 0000000..df5bb7b
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758.h
@@ -0,0 +1,171 @@
+#ifndef _ADE7758_H
+#define _ADE7758_H
+
+#define ADE7758_AWATTHR   0x01
+#define ADE7758_BWATTHR   0x02
+#define ADE7758_CWATTHR   0x03
+#define ADE7758_AVARHR    0x04
+#define ADE7758_BVARHR    0x05
+#define ADE7758_CVARHR    0x06
+#define ADE7758_AVAHR     0x07
+#define ADE7758_BVAHR     0x08
+#define ADE7758_CVAHR     0x09
+#define ADE7758_AIRMS     0x0A
+#define ADE7758_BIRMS     0x0B
+#define ADE7758_CIRMS     0x0C
+#define ADE7758_AVRMS     0x0D
+#define ADE7758_BVRMS     0x0E
+#define ADE7758_CVRMS     0x0F
+#define ADE7758_FREQ      0x10
+#define ADE7758_TEMP      0x11
+#define ADE7758_WFORM     0x12
+#define ADE7758_OPMODE    0x13
+#define ADE7758_MMODE     0x14
+#define ADE7758_WAVMODE   0x15
+#define ADE7758_COMPMODE  0x16
+#define ADE7758_LCYCMODE  0x17
+#define ADE7758_MASK      0x18
+#define ADE7758_STATUS    0x19
+#define ADE7758_RSTATUS   0x1A
+#define ADE7758_ZXTOUT    0x1B
+#define ADE7758_LINECYC   0x1C
+#define ADE7758_SAGCYC    0x1D
+#define ADE7758_SAGLVL    0x1E
+#define ADE7758_VPINTLVL  0x1F
+#define ADE7758_IPINTLVL  0x20
+#define ADE7758_VPEAK     0x21
+#define ADE7758_IPEAK     0x22
+#define ADE7758_GAIN      0x23
+#define ADE7758_AVRMSGAIN 0x24
+#define ADE7758_BVRMSGAIN 0x25
+#define ADE7758_CVRMSGAIN 0x26
+#define ADE7758_AIGAIN    0x27
+#define ADE7758_BIGAIN    0x28
+#define ADE7758_CIGAIN    0x29
+#define ADE7758_AWG       0x2A
+#define ADE7758_BWG       0x2B
+#define ADE7758_CWG       0x2C
+#define ADE7758_AVARG     0x2D
+#define ADE7758_BVARG     0x2E
+#define ADE7758_CVARG     0x2F
+#define ADE7758_AVAG      0x30
+#define ADE7758_BVAG      0x31
+#define ADE7758_CVAG      0x32
+#define ADE7758_AVRMSOS   0x33
+#define ADE7758_BVRMSOS   0x34
+#define ADE7758_CVRMSOS   0x35
+#define ADE7758_AIRMSOS   0x36
+#define ADE7758_BIRMSOS   0x37
+#define ADE7758_CIRMSOS   0x38
+#define ADE7758_AWAITOS   0x39
+#define ADE7758_BWAITOS   0x3A
+#define ADE7758_CWAITOS   0x3B
+#define ADE7758_AVAROS    0x3C
+#define ADE7758_BVAROS    0x3D
+#define ADE7758_CVAROS    0x3E
+#define ADE7758_APHCAL    0x3F
+#define ADE7758_BPHCAL    0x40
+#define ADE7758_CPHCAL    0x41
+#define ADE7758_WDIV      0x42
+#define ADE7758_VADIV     0x44
+#define ADE7758_VARDIV    0x43
+#define ADE7758_APCFNUM   0x45
+#define ADE7758_APCFDEN   0x46
+#define ADE7758_VARCFNUM  0x47
+#define ADE7758_VARCFDEN  0x48
+#define ADE7758_CHKSUM    0x7E
+#define ADE7758_VERSION   0x7F
+
+#define ADE7758_READ_REG(a)    a
+#define ADE7758_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7758_MAX_TX    8
+#define ADE7758_MAX_RX    4
+#define ADE7758_STARTUP_DELAY 1
+
+#define ADE7758_SPI_SLOW	(u32)(300 * 1000)
+#define ADE7758_SPI_BURST	(u32)(1000 * 1000)
+#define ADE7758_SPI_FAST	(u32)(2000 * 1000)
+
+#define DRIVER_NAME		"ade7758"
+
+/**
+ * struct ade7758_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct ade7758_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+#ifdef CONFIG_IIO_RING_BUFFER
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum ade7758_scan {
+	ADE7758_SCAN_WFORM,
+};
+
+void ade7758_remove_trigger(struct iio_dev *indio_dev);
+int ade7758_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t ade7758_read_data_from_ring(struct device *dev,
+		struct device_attribute *attr,
+		char *buf);
+
+
+int ade7758_configure_ring(struct iio_dev *indio_dev);
+void ade7758_unconfigure_ring(struct iio_dev *indio_dev);
+
+int ade7758_initialize_ring(struct iio_ring_buffer *ring);
+void ade7758_uninitialize_ring(struct iio_ring_buffer *ring);
+int ade7758_set_irq(struct device *dev, bool enable);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void ade7758_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7758_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+ade7758_read_data_from_ring(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return 0;
+}
+
+static int ade7758_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7758_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+static inline void ade7758_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif
diff --git a/drivers/staging/iio/meter/ade7758_core.c b/drivers/staging/iio/meter/ade7758_core.c
new file mode 100644
index 0000000..b7634cb
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758_core.c
@@ -0,0 +1,866 @@
+/*
+ * ADE7758 Polyphase Multifunction Energy Metering IC Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "meter.h"
+#include "ade7758.h"
+
+int ade7758_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7758_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7758_spi_write_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 3,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7758_WRITE_REG(reg_address);
+	st->tx[1] = (value >> 8) & 0xFF;
+	st->tx[2] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7758_spi_write_reg_24(struct device *dev,
+		u8 reg_address,
+		u32 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 4,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7758_WRITE_REG(reg_address);
+	st->tx[1] = (value >> 16) & 0xFF;
+	st->tx[2] = (value >> 8) & 0xFF;
+	st->tx[3] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7758_spi_read_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7758_READ_REG(reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7758_spi_read_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 3,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7758_READ_REG(reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[1] << 8) | st->rx[2];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7758_spi_read_reg_24(struct device *dev,
+		u8 reg_address,
+		u32 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 4,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7758_READ_REG(reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+	st->tx[3] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t ade7758_read_8bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u8 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7758_spi_read_reg_8(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7758_read_16bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7758_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7758_read_24bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u32 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7758_spi_read_reg_24(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val & 0xFFFFFF);
+}
+
+static ssize_t ade7758_write_8bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7758_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t ade7758_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7758_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+int ade7758_reset(struct device *dev)
+{
+	int ret;
+	u8 val;
+	ade7758_spi_read_reg_8(dev,
+			ADE7758_OPMODE,
+			&val);
+	val |= 1 << 6; /* Software Chip Reset */
+	ret = ade7758_spi_write_reg_8(dev,
+			ADE7758_OPMODE,
+			val);
+
+	return ret;
+}
+
+static ssize_t ade7758_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -1;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return ade7758_reset(dev);
+	}
+	return -1;
+}
+
+static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO,
+		ade7758_read_8bit,
+		ade7758_write_8bit,
+		ADE7758_VPEAK);
+static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO,
+		ade7758_read_8bit,
+		ade7758_write_8bit,
+		ADE7758_VPEAK);
+static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO,
+		ade7758_read_8bit,
+		ade7758_write_8bit,
+		ADE7758_APHCAL);
+static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO,
+		ade7758_read_8bit,
+		ade7758_write_8bit,
+		ADE7758_BPHCAL);
+static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO,
+		ade7758_read_8bit,
+		ade7758_write_8bit,
+		ADE7758_CPHCAL);
+static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO,
+		ade7758_read_8bit,
+		ade7758_write_8bit,
+		ADE7758_WDIV);
+static IIO_DEV_ATTR_VADIV(S_IWUSR | S_IRUGO,
+		ade7758_read_8bit,
+		ade7758_write_8bit,
+		ADE7758_VADIV);
+static IIO_DEV_ATTR_AIRMS(S_IRUGO,
+		ade7758_read_24bit,
+		NULL,
+		ADE7758_AIRMS);
+static IIO_DEV_ATTR_BIRMS(S_IRUGO,
+		ade7758_read_24bit,
+		NULL,
+		ADE7758_BIRMS);
+static IIO_DEV_ATTR_CIRMS(S_IRUGO,
+		ade7758_read_24bit,
+		NULL,
+		ADE7758_CIRMS);
+static IIO_DEV_ATTR_AVRMS(S_IRUGO,
+		ade7758_read_24bit,
+		NULL,
+		ADE7758_AVRMS);
+static IIO_DEV_ATTR_BVRMS(S_IRUGO,
+		ade7758_read_24bit,
+		NULL,
+		ADE7758_BVRMS);
+static IIO_DEV_ATTR_CVRMS(S_IRUGO,
+		ade7758_read_24bit,
+		NULL,
+		ADE7758_CVRMS);
+static IIO_DEV_ATTR_AIRMSOS(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_AIRMSOS);
+static IIO_DEV_ATTR_BIRMSOS(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_BIRMSOS);
+static IIO_DEV_ATTR_CIRMSOS(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_CIRMSOS);
+static IIO_DEV_ATTR_AVRMSOS(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_AVRMSOS);
+static IIO_DEV_ATTR_BVRMSOS(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_BVRMSOS);
+static IIO_DEV_ATTR_CVRMSOS(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_CVRMSOS);
+static IIO_DEV_ATTR_AIGAIN(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_AIGAIN);
+static IIO_DEV_ATTR_BIGAIN(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_BIGAIN);
+static IIO_DEV_ATTR_CIGAIN(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_CIGAIN);
+static IIO_DEV_ATTR_AVRMSGAIN(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_AVRMSGAIN);
+static IIO_DEV_ATTR_BVRMSGAIN(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_BVRMSGAIN);
+static IIO_DEV_ATTR_CVRMSGAIN(S_IWUSR | S_IRUGO,
+		ade7758_read_16bit,
+		ade7758_write_16bit,
+		ADE7758_CVRMSGAIN);
+
+int ade7758_set_irq(struct device *dev, bool enable)
+{
+	int ret;
+	u32 irqen;
+	ret = ade7758_spi_read_reg_24(dev, ADE7758_MASK, &irqen);
+	if (ret)
+		goto error_ret;
+
+	if (enable)
+		irqen |= 1 << 16; /* Enables an interrupt when a data is
+				     present in the waveform register */
+	else
+		irqen &= ~(1 << 16);
+
+	ret = ade7758_spi_write_reg_24(dev, ADE7758_MASK, irqen);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	return ret;
+}
+
+/* Power down the device */
+static int ade7758_stop_device(struct device *dev)
+{
+	int ret;
+	u8 val;
+	ade7758_spi_read_reg_8(dev,
+			ADE7758_OPMODE,
+			&val);
+	val |= 7 << 3;  /* ADE7758 powered down */
+	ret = ade7758_spi_write_reg_8(dev,
+			ADE7758_OPMODE,
+			val);
+
+	return ret;
+}
+
+static int ade7758_initial_setup(struct ade7758_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* use low spi speed for init */
+	st->us->mode = SPI_MODE_3;
+	spi_setup(st->us);
+
+	/* Disable IRQ */
+	ret = ade7758_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	ade7758_reset(dev);
+	msleep(ADE7758_STARTUP_DELAY);
+
+err_ret:
+	return ret;
+}
+
+static ssize_t ade7758_read_frequency(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret, len = 0;
+	u8 t;
+	int sps;
+	ret = ade7758_spi_read_reg_8(dev,
+			ADE7758_WAVMODE,
+			&t);
+	if (ret)
+		return ret;
+
+	t = (t >> 5) & 0x3;
+	sps = 26040 / (1 << t);
+
+	len = sprintf(buf, "%d SPS\n", sps);
+	return len;
+}
+
+static ssize_t ade7758_write_frequency(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	unsigned long val;
+	int ret;
+	u8 reg, t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	t = (26040 / val);
+	if (t > 0)
+		t >>= 1;
+
+	if (t > 1)
+		st->us->max_speed_hz = ADE7758_SPI_SLOW;
+	else
+		st->us->max_speed_hz = ADE7758_SPI_FAST;
+
+	ret = ade7758_spi_read_reg_8(dev,
+			ADE7758_WAVMODE,
+			&reg);
+	if (ret)
+		goto out;
+
+	reg &= ~(5 << 3);
+	reg |= t << 5;
+
+	ret = ade7758_spi_write_reg_8(dev,
+			ADE7758_WAVMODE,
+			reg);
+
+out:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t ade7758_read_waveform_type(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret, len = 0;
+	u8 t;
+	ret = ade7758_spi_read_reg_8(dev,
+			ADE7758_WAVMODE,
+			&t);
+	if (ret)
+		return ret;
+
+	t = (t >> 2) & 0x7;
+
+	len = sprintf(buf, "%d\n", t);
+
+	return len;
+}
+
+static ssize_t ade7758_write_waveform_type(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+	u8 reg;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val > 4)
+		return -EINVAL;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = ade7758_spi_read_reg_8(dev,
+			ADE7758_WAVMODE,
+			&reg);
+	if (ret)
+		goto out;
+
+	reg &= ~(7 << 2);
+	reg |= val << 2;
+
+	ret = ade7758_spi_write_reg_8(dev,
+			ADE7758_WAVMODE,
+			reg);
+
+out:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_TEMP_RAW(ade7758_read_8bit);
+static IIO_CONST_ATTR(temp_offset, "129 C");
+static IIO_CONST_ATTR(temp_scale, "4 C");
+
+static IIO_DEV_ATTR_AWATTHR(ade7758_read_16bit,
+		ADE7758_AWATTHR);
+static IIO_DEV_ATTR_BWATTHR(ade7758_read_16bit,
+		ADE7758_BWATTHR);
+static IIO_DEV_ATTR_CWATTHR(ade7758_read_16bit,
+		ADE7758_CWATTHR);
+static IIO_DEV_ATTR_AVARHR(ade7758_read_16bit,
+		ADE7758_AVARHR);
+static IIO_DEV_ATTR_BVARHR(ade7758_read_16bit,
+		ADE7758_BVARHR);
+static IIO_DEV_ATTR_CVARHR(ade7758_read_16bit,
+		ADE7758_CVARHR);
+static IIO_DEV_ATTR_AVAHR(ade7758_read_16bit,
+		ADE7758_AVAHR);
+static IIO_DEV_ATTR_BVAHR(ade7758_read_16bit,
+		ADE7758_BVAHR);
+static IIO_DEV_ATTR_CVAHR(ade7758_read_16bit,
+		ADE7758_CVAHR);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+		ade7758_read_frequency,
+		ade7758_write_frequency);
+
+/**
+ * IIO_DEV_ATTR_WAVEFORM_TYPE - set the type of waveform.
+ * @_mode: sysfs file mode/permissions
+ * @_show: output method for the attribute
+ * @_store: input method for the attribute
+ **/
+#define IIO_DEV_ATTR_WAVEFORM_TYPE(_mode, _show, _store)			\
+	IIO_DEVICE_ATTR(waveform_type, _mode, _show, _store, 0)
+
+static IIO_DEV_ATTR_WAVEFORM_TYPE(S_IWUSR | S_IRUGO,
+		ade7758_read_waveform_type,
+		ade7758_write_waveform_type);
+
+static IIO_DEV_ATTR_RESET(ade7758_write_reset);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26000 13000 65000 33000");
+
+static IIO_CONST_ATTR(name, "ade7758");
+
+static struct attribute *ade7758_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group ade7758_event_attribute_group = {
+	.attrs = ade7758_event_attributes,
+};
+
+static struct attribute *ade7758_attributes[] = {
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_dev_attr_waveform_type.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_awatthr.dev_attr.attr,
+	&iio_dev_attr_bwatthr.dev_attr.attr,
+	&iio_dev_attr_cwatthr.dev_attr.attr,
+	&iio_dev_attr_avarhr.dev_attr.attr,
+	&iio_dev_attr_bvarhr.dev_attr.attr,
+	&iio_dev_attr_cvarhr.dev_attr.attr,
+	&iio_dev_attr_avahr.dev_attr.attr,
+	&iio_dev_attr_bvahr.dev_attr.attr,
+	&iio_dev_attr_cvahr.dev_attr.attr,
+	&iio_dev_attr_vpeak.dev_attr.attr,
+	&iio_dev_attr_ipeak.dev_attr.attr,
+	&iio_dev_attr_aphcal.dev_attr.attr,
+	&iio_dev_attr_bphcal.dev_attr.attr,
+	&iio_dev_attr_cphcal.dev_attr.attr,
+	&iio_dev_attr_wdiv.dev_attr.attr,
+	&iio_dev_attr_vadiv.dev_attr.attr,
+	&iio_dev_attr_airms.dev_attr.attr,
+	&iio_dev_attr_birms.dev_attr.attr,
+	&iio_dev_attr_cirms.dev_attr.attr,
+	&iio_dev_attr_avrms.dev_attr.attr,
+	&iio_dev_attr_bvrms.dev_attr.attr,
+	&iio_dev_attr_cvrms.dev_attr.attr,
+	&iio_dev_attr_aigain.dev_attr.attr,
+	&iio_dev_attr_bigain.dev_attr.attr,
+	&iio_dev_attr_cigain.dev_attr.attr,
+	&iio_dev_attr_avrmsgain.dev_attr.attr,
+	&iio_dev_attr_bvrmsgain.dev_attr.attr,
+	&iio_dev_attr_cvrmsgain.dev_attr.attr,
+	&iio_dev_attr_airmsos.dev_attr.attr,
+	&iio_dev_attr_birmsos.dev_attr.attr,
+	&iio_dev_attr_cirmsos.dev_attr.attr,
+	&iio_dev_attr_avrmsos.dev_attr.attr,
+	&iio_dev_attr_bvrmsos.dev_attr.attr,
+	&iio_dev_attr_cvrmsos.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ade7758_attribute_group = {
+	.attrs = ade7758_attributes,
+};
+
+
+
+static int __devinit ade7758_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct ade7758_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADE7758_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADE7758_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &ade7758_event_attribute_group;
+	st->indio_dev->attrs = &ade7758_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = ade7758_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = ade7758_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				"ade7758");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = ade7758_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = ade7758_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		ade7758_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	ade7758_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	ade7758_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int ade7758_remove(struct spi_device *spi)
+{
+	int ret;
+	struct ade7758_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	ret = ade7758_stop_device(&(indio_dev->dev));
+	if (ret)
+		goto err_ret;
+
+	flush_scheduled_work();
+
+	ade7758_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	ade7758_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	ade7758_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+
+err_ret:
+	return ret;
+}
+
+static struct spi_driver ade7758_driver = {
+	.driver = {
+		.name = "ade7758",
+		.owner = THIS_MODULE,
+	},
+	.probe = ade7758_probe,
+	.remove = __devexit_p(ade7758_remove),
+};
+
+static __init int ade7758_init(void)
+{
+	return spi_register_driver(&ade7758_driver);
+}
+module_init(ade7758_init);
+
+static __exit void ade7758_exit(void)
+{
+	spi_unregister_driver(&ade7758_driver);
+}
+module_exit(ade7758_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7758 Polyphase Multifunction Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c
new file mode 100644
index 0000000..274b4a0
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758_ring.c
@@ -0,0 +1,212 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "../accel/accel.h"
+#include "../trigger.h"
+#include "ade7758.h"
+
+/**
+ * combine_8_to_32() utility function to munge to u8s into u32
+ **/
+static inline u32 combine_8_to_32(u8 lower, u8 mid, u8 upper)
+{
+	u32 _lower = lower;
+	u32 _mid = mid;
+	u32 _upper = upper;
+
+	return _lower | (_mid << 8) | (_upper << 16);
+}
+
+static IIO_SCAN_EL_C(wform, ADE7758_SCAN_WFORM, ADE7758_WFORM, NULL);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(wform, s, 24, 32);
+static IIO_SCAN_EL_TIMESTAMP(1);
+static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64);
+
+static struct attribute *ade7758_scan_el_attrs[] = {
+	&iio_scan_el_wform.dev_attr.attr,
+	&iio_const_attr_wform_index.dev_attr.attr,
+	&iio_const_attr_wform_type.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	&iio_const_attr_timestamp_index.dev_attr.attr,
+	&iio_const_attr_timestamp_type.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ade7758_scan_el_group = {
+	.attrs = ade7758_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * ade7758_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void ade7758_poll_func_th(struct iio_dev *indio_dev, s64 time)
+{
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = time;
+	schedule_work(&st->work_trigger_to_ring);
+	/* Indicate that this interrupt is being handled */
+
+	/* Technically this is trigger related, but without this
+	 * handler running there is currently no way for the interrupt
+	 * to clear.
+	 */
+}
+
+/**
+ * ade7758_spi_read_burst() - read all data registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read (min size is 24 bytes)
+ **/
+static int ade7758_spi_read_burst(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = rx,
+			.bits_per_word = 8,
+			.len = 4,
+		}, {
+			.tx_buf = st->tx + 4,
+			.rx_buf = rx,
+			.bits_per_word = 8,
+			.len = 4,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7758_READ_REG(ADE7758_RSTATUS);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+	st->tx[3] = 0;
+	st->tx[4] = ADE7758_READ_REG(ADE7758_WFORM);
+	st->tx[5] = 0;
+	st->tx[6] = 0;
+	st->tx[7] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when reading WFORM value\n");
+
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
+ * specific to be rolled into the core.
+ */
+static void ade7758_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct ade7758_state *st
+		= container_of(work_s, struct ade7758_state,
+			       work_trigger_to_ring);
+	struct iio_ring_buffer *ring = st->indio_dev->ring;
+
+	int i = 0;
+	s32 *data;
+	size_t datasize = ring->access.get_bytes_per_datum(ring);
+
+	data = kmalloc(datasize, GFP_KERNEL);
+	if (data == NULL) {
+		dev_err(&st->us->dev, "memory alloc failed in ring bh");
+		return;
+	}
+
+	if (ring->scan_count)
+		if (ade7758_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < ring->scan_count; i++)
+				data[i] = combine_8_to_32(st->rx[i*2+2],
+						st->rx[i*2+1],
+						st->rx[i*2]);
+
+	/* Guaranteed to be aligned with 8 byte boundary */
+	if (ring->scan_timestamp)
+		*((s64 *)
+		(((u32)data + 4 * ring->scan_count + 4) & ~0x7)) =
+			st->last_timestamp;
+
+	ring->access.store_to(ring,
+			      (u8 *)data,
+			      st->last_timestamp);
+
+	iio_trigger_notify_done(st->indio_dev->trig);
+	kfree(data);
+
+	return;
+}
+
+void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int ade7758_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct ade7758_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, ade7758_trigger_bh_to_ring);
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->bpe = 4;
+	ring->scan_el_attrs = &ade7758_scan_el_group;
+	ring->scan_timestamp = true;
+	ring->preenable = &iio_sw_ring_preenable;
+	ring->postenable = &iio_triggered_ring_postenable;
+	ring->predisable = &iio_triggered_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	/* Set default scan mode */
+	iio_scan_mask_set(ring, iio_scan_el_wform.number);
+
+	ret = iio_alloc_pollfunc(indio_dev, NULL, &ade7758_poll_func_th);
+	if (ret)
+		goto error_iio_sw_rb_free;
+
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int ade7758_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void ade7758_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/meter/ade7758_trigger.c b/drivers/staging/iio/meter/ade7758_trigger.c
new file mode 100644
index 0000000..60abca0
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758_trigger.c
@@ -0,0 +1,125 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "ade7758.h"
+
+/**
+ * ade7758_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int ade7758_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct ade7758_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	iio_trigger_poll(trig, timestamp);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &ade7758_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *ade7758_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group ade7758_trigger_attr_group = {
+	.attrs = ade7758_trigger_attrs,
+};
+
+/**
+ * ade7758_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int ade7758_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct ade7758_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = ade7758_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		/* possible quirk with handler currently worked around
+		   by ensuring the work queue is empty */
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * ade7758_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int ade7758_trig_try_reen(struct iio_trigger *trig)
+{
+	struct ade7758_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	/* irq reenabled so success! */
+	return 0;
+}
+
+int ade7758_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct ade7758_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kasprintf(GFP_KERNEL,
+				"ade7758-dev%d",
+				indio_dev->id);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &ade7758_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &ade7758_trig_try_reen;
+	st->trig->control_attrs = &ade7758_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void ade7758_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct ade7758_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 32/36] staging: iio: meter: new driver for ADE7759 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (30 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 31/36] staging: iio: meter: new driver for ADE7758 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 33/36] staging: iio: meter: new driver for ADE7854/58/68/78 devices Mike Frysinger
                   ` (4 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/meter/Kconfig   |    7 +
 drivers/staging/iio/meter/Makefile  |    2 +
 drivers/staging/iio/meter/ade7759.c |  670 +++++++++++++++++++++++++++++++++++
 drivers/staging/iio/meter/ade7759.h |  122 +++++++
 4 files changed, 801 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/meter/ade7759.c
 create mode 100644 drivers/staging/iio/meter/ade7759.h

diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig
index be88bb8..ebc253e 100644
--- a/drivers/staging/iio/meter/Kconfig
+++ b/drivers/staging/iio/meter/Kconfig
@@ -25,3 +25,10 @@ config ADE7758
 	help
 	  Say yes here to build support for Analog Devices ADE7758 Polyphase
 	  Multifunction Energy Metering IC with Per Phase Information Driver.
+
+config ADE7759
+	tristate "Analog Devices ADE7759 Active Energy Metering IC Driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices ADE7758 Active Energy
+	  Metering IC with di/dt Sensor Interface.
diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile
index 8e1a71d..85e3021 100644
--- a/drivers/staging/iio/meter/Makefile
+++ b/drivers/staging/iio/meter/Makefile
@@ -8,3 +8,5 @@ obj-$(CONFIG_ADE7754) += ade7754.o
 ade7758-y             := ade7758_core.o
 ade7758-$(CONFIG_IIO_RING_BUFFER) += ade7758_ring.o ade7758_trigger.o
 obj-$(CONFIG_ADE7758) += ade7758.o
+
+obj-$(CONFIG_ADE7759) += ade7759.o
diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c
new file mode 100644
index 0000000..fafc3c1
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7759.c
@@ -0,0 +1,670 @@
+/*
+ * ADE7759 Active Energy Metering IC with di/dt Sensor Interface Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "meter.h"
+#include "ade7759.h"
+
+int ade7759_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7759_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7759_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7759_spi_write_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7759_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 3,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7759_WRITE_REG(reg_address);
+	st->tx[1] = (value >> 8) & 0xFF;
+	st->tx[2] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7759_spi_read_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7759_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7759_READ_REG(reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7759_spi_read_reg_16(struct device *dev,
+		u8 reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7759_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 3,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7759_READ_REG(reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[1] << 8) | st->rx[2];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7759_spi_read_reg_40(struct device *dev,
+		u8 reg_address,
+		u64 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7759_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 6,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7759_READ_REG(reg_address);
+	memset(&st->tx[1], 0 , 5);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 40 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = ((u64)st->rx[1] << 32) | (st->rx[2] << 24) |
+		(st->rx[3] << 16) | (st->rx[4] << 8) | st->rx[5];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t ade7759_read_8bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u8 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7759_spi_read_reg_8(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7759_read_16bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7759_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7759_read_40bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u64 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = ade7759_spi_read_reg_40(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%llu\n", val);
+}
+
+static ssize_t ade7759_write_8bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7759_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t ade7759_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = ade7759_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int ade7759_reset(struct device *dev)
+{
+	int ret;
+	u16 val;
+	ade7759_spi_read_reg_16(dev,
+			ADE7759_MODE,
+			&val);
+	val |= 1 << 6; /* Software Chip Reset */
+	ret = ade7759_spi_write_reg_16(dev,
+			ADE7759_MODE,
+			val);
+
+	return ret;
+}
+
+static ssize_t ade7759_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -1;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return ade7759_reset(dev);
+	}
+	return -1;
+}
+
+static IIO_DEV_ATTR_AENERGY(ade7759_read_40bit, ADE7759_AENERGY);
+static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO,
+		ade7759_read_16bit,
+		ade7759_write_16bit,
+		ADE7759_CFDEN);
+static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO,
+		ade7759_read_8bit,
+		ade7759_write_8bit,
+		ADE7759_CFNUM);
+static IIO_DEV_ATTR_CHKSUM(ade7759_read_8bit, ADE7759_CHKSUM);
+static IIO_DEV_ATTR_PHCAL(S_IWUSR | S_IRUGO,
+		ade7759_read_16bit,
+		ade7759_write_16bit,
+		ADE7759_PHCAL);
+static IIO_DEV_ATTR_APOS(S_IWUSR | S_IRUGO,
+		ade7759_read_16bit,
+		ade7759_write_16bit,
+		ADE7759_APOS);
+static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO,
+		ade7759_read_8bit,
+		ade7759_write_8bit,
+		ADE7759_SAGCYC);
+static IIO_DEV_ATTR_SAGLVL(S_IWUSR | S_IRUGO,
+		ade7759_read_8bit,
+		ade7759_write_8bit,
+		ADE7759_SAGLVL);
+static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO,
+		ade7759_read_8bit,
+		ade7759_write_8bit,
+		ADE7759_LINECYC);
+static IIO_DEV_ATTR_LENERGY(ade7759_read_40bit, ADE7759_LENERGY);
+static IIO_DEV_ATTR_PGA_GAIN(S_IWUSR | S_IRUGO,
+		ade7759_read_8bit,
+		ade7759_write_8bit,
+		ADE7759_GAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_GAIN(S_IWUSR | S_IRUGO,
+		ade7759_read_16bit,
+		ade7759_write_16bit,
+		ADE7759_APGAIN);
+static IIO_DEV_ATTR_CH_OFF(1, S_IWUSR | S_IRUGO,
+		ade7759_read_8bit,
+		ade7759_write_8bit,
+		ADE7759_CH1OS);
+static IIO_DEV_ATTR_CH_OFF(2, S_IWUSR | S_IRUGO,
+		ade7759_read_8bit,
+		ade7759_write_8bit,
+		ADE7759_CH2OS);
+
+static int ade7759_set_irq(struct device *dev, bool enable)
+{
+	int ret;
+	u8 irqen;
+	ret = ade7759_spi_read_reg_8(dev, ADE7759_IRQEN, &irqen);
+	if (ret)
+		goto error_ret;
+
+	if (enable)
+		irqen |= 1 << 3; /* Enables an interrupt when a data is
+				    present in the waveform register */
+	else
+		irqen &= ~(1 << 3);
+
+	ret = ade7759_spi_write_reg_8(dev, ADE7759_IRQEN, irqen);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	return ret;
+}
+
+/* Power down the device */
+int ade7759_stop_device(struct device *dev)
+{
+	int ret;
+	u16 val;
+	ade7759_spi_read_reg_16(dev,
+			ADE7759_MODE,
+			&val);
+	val |= 1 << 4;  /* AD converters can be turned off */
+	ret = ade7759_spi_write_reg_16(dev,
+			ADE7759_MODE,
+			val);
+
+	return ret;
+}
+
+static int ade7759_initial_setup(struct ade7759_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* use low spi speed for init */
+	st->us->mode = SPI_MODE_3;
+	spi_setup(st->us);
+
+	/* Disable IRQ */
+	ret = ade7759_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	ade7759_reset(dev);
+	msleep(ADE7759_STARTUP_DELAY);
+
+err_ret:
+	return ret;
+}
+
+static ssize_t ade7759_read_frequency(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret, len = 0;
+	u16 t;
+	int sps;
+	ret = ade7759_spi_read_reg_16(dev,
+			ADE7759_MODE,
+			&t);
+	if (ret)
+		return ret;
+
+	t = (t >> 3) & 0x3;
+	sps = 27900 / (1 + t);
+
+	len = sprintf(buf, "%d SPS\n", sps);
+	return len;
+}
+
+static ssize_t ade7759_write_frequency(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7759_state *st = iio_dev_get_devdata(indio_dev);
+	unsigned long val;
+	int ret;
+	u16 reg, t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	t = (27900 / val);
+	if (t > 0)
+		t--;
+
+	if (t > 1)
+		st->us->max_speed_hz = ADE7759_SPI_SLOW;
+	else
+		st->us->max_speed_hz = ADE7759_SPI_FAST;
+
+	ret = ade7759_spi_read_reg_16(dev,
+			ADE7759_MODE,
+			&reg);
+	if (ret)
+		goto out;
+
+	reg &= ~(3 << 13);
+	reg |= t << 13;
+
+	ret = ade7759_spi_write_reg_16(dev,
+			ADE7759_MODE,
+			reg);
+
+out:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+static IIO_DEV_ATTR_TEMP_RAW(ade7759_read_8bit);
+static IIO_CONST_ATTR(temp_offset, "70 C");
+static IIO_CONST_ATTR(temp_scale, "1 C");
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+		ade7759_read_frequency,
+		ade7759_write_frequency);
+
+static IIO_DEV_ATTR_RESET(ade7759_write_reset);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500");
+
+static IIO_CONST_ATTR(name, "ade7759");
+
+static struct attribute *ade7759_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group ade7759_event_attribute_group = {
+	.attrs = ade7759_event_attributes,
+};
+
+static struct attribute *ade7759_attributes[] = {
+	&iio_dev_attr_temp_raw.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_phcal.dev_attr.attr,
+	&iio_dev_attr_cfden.dev_attr.attr,
+	&iio_dev_attr_aenergy.dev_attr.attr,
+	&iio_dev_attr_cfnum.dev_attr.attr,
+	&iio_dev_attr_apos.dev_attr.attr,
+	&iio_dev_attr_sagcyc.dev_attr.attr,
+	&iio_dev_attr_saglvl.dev_attr.attr,
+	&iio_dev_attr_linecyc.dev_attr.attr,
+	&iio_dev_attr_lenergy.dev_attr.attr,
+	&iio_dev_attr_chksum.dev_attr.attr,
+	&iio_dev_attr_pga_gain.dev_attr.attr,
+	&iio_dev_attr_active_power_gain.dev_attr.attr,
+	&iio_dev_attr_choff_1.dev_attr.attr,
+	&iio_dev_attr_choff_2.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ade7759_attribute_group = {
+	.attrs = ade7759_attributes,
+};
+
+static int __devinit ade7759_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct ade7759_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADE7759_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADE7759_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &ade7759_event_attribute_group;
+	st->indio_dev->attrs = &ade7759_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = ade7759_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = ade7759_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				"ade7759");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = ade7759_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = ade7759_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		ade7759_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	ade7759_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	ade7759_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int ade7759_remove(struct spi_device *spi)
+{
+	int ret;
+	struct ade7759_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	ret = ade7759_stop_device(&(indio_dev->dev));
+	if (ret)
+		goto err_ret;
+
+	flush_scheduled_work();
+
+	ade7759_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	ade7759_uninitialize_ring(indio_dev->ring);
+	ade7759_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+
+err_ret:
+	return ret;
+}
+
+static struct spi_driver ade7759_driver = {
+	.driver = {
+		.name = "ade7759",
+		.owner = THIS_MODULE,
+	},
+	.probe = ade7759_probe,
+	.remove = __devexit_p(ade7759_remove),
+};
+
+static __init int ade7759_init(void)
+{
+	return spi_register_driver(&ade7759_driver);
+}
+module_init(ade7759_init);
+
+static __exit void ade7759_exit(void)
+{
+	spi_unregister_driver(&ade7759_driver);
+}
+module_exit(ade7759_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7759 Active Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7759.h b/drivers/staging/iio/meter/ade7759.h
new file mode 100644
index 0000000..813dea2
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7759.h
@@ -0,0 +1,122 @@
+#ifndef _ADE7759_H
+#define _ADE7759_H
+
+#define ADE7759_WAVEFORM  0x01
+#define ADE7759_AENERGY   0x02
+#define ADE7759_RSTENERGY 0x03
+#define ADE7759_STATUS    0x04
+#define ADE7759_RSTSTATUS 0x05
+#define ADE7759_MODE      0x06
+#define ADE7759_CFDEN     0x07
+#define ADE7759_CH1OS     0x08
+#define ADE7759_CH2OS     0x09
+#define ADE7759_GAIN      0x0A
+#define ADE7759_APGAIN    0x0B
+#define ADE7759_PHCAL     0x0C
+#define ADE7759_APOS      0x0D
+#define ADE7759_ZXTOUT    0x0E
+#define ADE7759_SAGCYC    0x0F
+#define ADE7759_IRQEN     0x10
+#define ADE7759_SAGLVL    0x11
+#define ADE7759_TEMP      0x12
+#define ADE7759_LINECYC   0x13
+#define ADE7759_LENERGY   0x14
+#define ADE7759_CFNUM     0x15
+#define ADE7759_CHKSUM    0x1E
+#define ADE7759_DIEREV    0x1F
+
+#define ADE7759_READ_REG(a)    a
+#define ADE7759_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7759_MAX_TX    6
+#define ADE7759_MAX_RX    6
+#define ADE7759_STARTUP_DELAY 1
+
+#define ADE7759_SPI_SLOW	(u32)(300 * 1000)
+#define ADE7759_SPI_BURST	(u32)(1000 * 1000)
+#define ADE7759_SPI_FAST	(u32)(2000 * 1000)
+
+#define DRIVER_NAME		"ade7759"
+
+/**
+ * struct ade7759_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct ade7759_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum ade7759_scan {
+	ADE7759_SCAN_ACTIVE_POWER,
+	ADE7759_SCAN_CH1_CH2,
+	ADE7759_SCAN_CH1,
+	ADE7759_SCAN_CH2,
+};
+
+void ade7759_remove_trigger(struct iio_dev *indio_dev);
+int ade7759_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t ade7759_read_data_from_ring(struct device *dev,
+		struct device_attribute *attr,
+		char *buf);
+
+
+int ade7759_configure_ring(struct iio_dev *indio_dev);
+void ade7759_unconfigure_ring(struct iio_dev *indio_dev);
+
+int ade7759_initialize_ring(struct iio_ring_buffer *ring);
+void ade7759_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void ade7759_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7759_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+ade7759_read_data_from_ring(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return 0;
+}
+
+static int ade7759_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void ade7759_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7759_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+static inline void ade7759_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 33/36] staging: iio: meter: new driver for ADE7854/58/68/78 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (31 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 32/36] staging: iio: meter: new driver for ADE7759 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 34/36] staging: iio: resolver: new driver for AD2S90 devices Mike Frysinger
                   ` (3 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Barry Song, Michael Hennerich

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/meter/Kconfig       |   27 ++
 drivers/staging/iio/meter/Makefile      |    3 +
 drivers/staging/iio/meter/ade7854-i2c.c |  272 ++++++++++++
 drivers/staging/iio/meter/ade7854-spi.c |  360 ++++++++++++++++
 drivers/staging/iio/meter/ade7854.c     |  680 +++++++++++++++++++++++++++++++
 drivers/staging/iio/meter/ade7854.h     |  245 +++++++++++
 6 files changed, 1587 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/meter/ade7854-i2c.c
 create mode 100644 drivers/staging/iio/meter/ade7854-spi.c
 create mode 100644 drivers/staging/iio/meter/ade7854.c
 create mode 100644 drivers/staging/iio/meter/ade7854.h

diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig
index ebc253e..12e36e4 100644
--- a/drivers/staging/iio/meter/Kconfig
+++ b/drivers/staging/iio/meter/Kconfig
@@ -32,3 +32,30 @@ config ADE7759
 	help
 	  Say yes here to build support for Analog Devices ADE7758 Active Energy
 	  Metering IC with di/dt Sensor Interface.
+
+config ADE7854
+	tristate "Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver"
+	depends on SPI || I2C
+	help
+	  Say yes here to build support for Analog Devices ADE7854/58/68/78 Polyphase
+	  Multifunction Energy Metering IC Driver.
+
+config ADE7854_I2C
+	tristate "support I2C bus connection"
+	depends on ADE7854 && I2C
+	default y
+	help
+	  Say Y here if you have ADE7854/58/68/78 hooked to an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ade7854-i2c.
+
+config ADE7854_SPI
+	tristate "support SPI bus connection"
+	depends on ADE7854 && SPI
+	default y
+	help
+	  Say Y here if you have ADE7854/58/68/78 hooked to a SPI bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ade7854-spi.
diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile
index 85e3021..0cc7d51 100644
--- a/drivers/staging/iio/meter/Makefile
+++ b/drivers/staging/iio/meter/Makefile
@@ -10,3 +10,6 @@ ade7758-$(CONFIG_IIO_RING_BUFFER) += ade7758_ring.o ade7758_trigger.o
 obj-$(CONFIG_ADE7758) += ade7758.o
 
 obj-$(CONFIG_ADE7759) += ade7759.o
+obj-$(CONFIG_ADE7854) += ade7854.o
+obj-$(CONFIG_ADE7854_I2C) += ade7854-i2c.o
+obj-$(CONFIG_ADE7854_SPI) += ade7854-spi.o
diff --git a/drivers/staging/iio/meter/ade7854-i2c.c b/drivers/staging/iio/meter/ade7854-i2c.c
new file mode 100644
index 0000000..4578e7b
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854-i2c.c
@@ -0,0 +1,272 @@
+/*
+ * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (I2C Bus)
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include "../iio.h"
+#include "ade7854.h"
+
+static int ade7854_i2c_write_reg_8(struct device *dev,
+		u16 reg_address,
+		u8 value)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+	st->tx[2] = value;
+
+	ret = i2c_master_send(st->i2c, st->tx, 3);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_i2c_write_reg_16(struct device *dev,
+		u16 reg_address,
+		u16 value)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+	st->tx[2] = (value >> 8) & 0xFF;
+	st->tx[3] = value & 0xFF;
+
+	ret = i2c_master_send(st->i2c, st->tx, 4);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_i2c_write_reg_24(struct device *dev,
+		u16 reg_address,
+		u32 value)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+	st->tx[2] = (value >> 16) & 0xFF;
+	st->tx[3] = (value >> 8) & 0xFF;
+	st->tx[4] = value & 0xFF;
+
+	ret = i2c_master_send(st->i2c, st->tx, 5);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_i2c_write_reg_32(struct device *dev,
+		u16 reg_address,
+		u32 value)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+	st->tx[2] = (value >> 24) & 0xFF;
+	st->tx[3] = (value >> 16) & 0xFF;
+	st->tx[4] = (value >> 8) & 0xFF;
+	st->tx[5] = value & 0xFF;
+
+	ret = i2c_master_send(st->i2c, st->tx, 6);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_i2c_read_reg_8(struct device *dev,
+		u16 reg_address,
+		u8 *val)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+
+	ret = i2c_master_send(st->i2c, st->tx, 2);
+	if (ret)
+		goto out;
+
+	ret = i2c_master_recv(st->i2c, st->rx, 1);
+	if (ret)
+		goto out;
+
+	*val = st->rx[0];
+out:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7854_i2c_read_reg_16(struct device *dev,
+		u16 reg_address,
+		u16 *val)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+
+	ret = i2c_master_send(st->i2c, st->tx, 2);
+	if (ret)
+		goto out;
+
+	ret = i2c_master_recv(st->i2c, st->rx, 2);
+	if (ret)
+		goto out;
+
+	*val = (st->rx[0] << 8) | st->rx[1];
+out:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7854_i2c_read_reg_24(struct device *dev,
+		u16 reg_address,
+		u32 *val)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+
+	ret = i2c_master_send(st->i2c, st->tx, 2);
+	if (ret)
+		goto out;
+
+	ret = i2c_master_recv(st->i2c, st->rx, 3);
+	if (ret)
+		goto out;
+
+	*val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2];
+out:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7854_i2c_read_reg_32(struct device *dev,
+		u16 reg_address,
+		u32 *val)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = (reg_address >> 8) & 0xFF;
+	st->tx[1] = reg_address & 0xFF;
+
+	ret = i2c_master_send(st->i2c, st->tx, 2);
+	if (ret)
+		goto out;
+
+	ret = i2c_master_recv(st->i2c, st->rx, 3);
+	if (ret)
+		goto out;
+
+	*val = (st->rx[0] << 24) | (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3];
+out:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int __devinit ade7854_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret;
+	struct ade7854_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		return ret;
+	}
+
+	i2c_set_clientdata(client, st);
+	st->read_reg_8 = ade7854_i2c_read_reg_8;
+	st->read_reg_16 = ade7854_i2c_read_reg_16;
+	st->read_reg_24 = ade7854_i2c_read_reg_24;
+	st->read_reg_32 = ade7854_i2c_read_reg_32;
+	st->write_reg_8 = ade7854_i2c_write_reg_8;
+	st->write_reg_16 = ade7854_i2c_write_reg_16;
+	st->write_reg_24 = ade7854_i2c_write_reg_24;
+	st->write_reg_32 = ade7854_i2c_write_reg_32;
+	st->i2c = client;
+	st->irq = client->irq;
+
+	ret = ade7854_probe(st, &client->dev);
+	if (ret) {
+		kfree(st);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int __devexit ade7854_i2c_remove(struct i2c_client *client)
+{
+	return ade7854_remove(i2c_get_clientdata(client));
+}
+
+static const struct i2c_device_id ade7854_id[] = {
+	{ "ade7854", 0 },
+	{ "ade7858", 0 },
+	{ "ade7868", 0 },
+	{ "ade7878", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ade7854_id);
+
+static struct i2c_driver ade7854_i2c_driver = {
+	.driver = {
+		.name = "ade7854",
+	},
+	.probe    = ade7854_i2c_probe,
+	.remove   = __devexit_p(ade7854_i2c_remove),
+	.id_table = ade7854_id,
+};
+
+static __init int ade7854_i2c_init(void)
+{
+	return i2c_add_driver(&ade7854_i2c_driver);
+}
+module_init(ade7854_i2c_init);
+
+static __exit void ade7854_i2c_exit(void)
+{
+	i2c_del_driver(&ade7854_i2c_driver);
+}
+module_exit(ade7854_i2c_exit);
+
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC I2C Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7854-spi.c b/drivers/staging/iio/meter/ade7854-spi.c
new file mode 100644
index 0000000..fe58103e
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854-spi.c
@@ -0,0 +1,360 @@
+/*
+ * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (SPI Bus)
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+
+#include "../iio.h"
+#include "ade7854.h"
+
+static int ade7854_spi_write_reg_8(struct device *dev,
+		u16 reg_address,
+		u8 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 4,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7854_WRITE_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_spi_write_reg_16(struct device *dev,
+		u16 reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 5,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7854_WRITE_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = (value >> 8) & 0xFF;
+	st->tx[4] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_spi_write_reg_24(struct device *dev,
+		u16 reg_address,
+		u32 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 6,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7854_WRITE_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = (value >> 16) & 0xFF;
+	st->tx[4] = (value >> 8) & 0xFF;
+	st->tx[5] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_spi_write_reg_32(struct device *dev,
+		u16 reg_address,
+		u32 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 7,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7854_WRITE_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = (value >> 24) & 0xFF;
+	st->tx[4] = (value >> 16) & 0xFF;
+	st->tx[5] = (value >> 8) & 0xFF;
+	st->tx[6] = value & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+static int ade7854_spi_read_reg_8(struct device *dev,
+		u16 reg_address,
+		u8 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 4,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+
+	st->tx[0] = ADE7854_READ_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	if (ret) {
+		dev_err(&st->spi->dev, "problem when reading 8 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = st->rx[3];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7854_spi_read_reg_16(struct device *dev,
+		u16 reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 5,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADE7854_READ_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = 0;
+	st->tx[4] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	if (ret) {
+		dev_err(&st->spi->dev, "problem when reading 16 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[3] << 8) | st->rx[4];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7854_spi_read_reg_24(struct device *dev,
+		u16 reg_address,
+		u32 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 6,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+
+	st->tx[0] = ADE7854_READ_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = 0;
+	st->tx[4] = 0;
+	st->tx[5] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	if (ret) {
+		dev_err(&st->spi->dev, "problem when reading 24 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[3] << 16) | (st->rx[4] << 8) | st->rx[5];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int ade7854_spi_read_reg_32(struct device *dev,
+		u16 reg_address,
+		u32 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 7,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+
+	st->tx[0] = ADE7854_READ_REG;
+	st->tx[1] = (reg_address >> 8) & 0xFF;
+	st->tx[2] = reg_address & 0xFF;
+	st->tx[3] = 0;
+	st->tx[4] = 0;
+	st->tx[5] = 0;
+	st->tx[6] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(xfers, &msg);
+	ret = spi_sync(st->spi, &msg);
+	if (ret) {
+		dev_err(&st->spi->dev, "problem when reading 32 bit register 0x%02X",
+				reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[3] << 24) | (st->rx[4] << 16) | (st->rx[5] << 8) | st->rx[6];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static int __devinit ade7854_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	struct ade7854_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		return ret;
+	}
+
+	spi_set_drvdata(spi, st);
+	st->read_reg_8 = ade7854_spi_read_reg_8;
+	st->read_reg_16 = ade7854_spi_read_reg_16;
+	st->read_reg_24 = ade7854_spi_read_reg_24;
+	st->read_reg_32 = ade7854_spi_read_reg_32;
+	st->write_reg_8 = ade7854_spi_write_reg_8;
+	st->write_reg_16 = ade7854_spi_write_reg_16;
+	st->write_reg_24 = ade7854_spi_write_reg_24;
+	st->write_reg_32 = ade7854_spi_write_reg_32;
+	st->irq = spi->irq;
+	st->spi = spi;
+
+	ret = ade7854_probe(st, &spi->dev);
+	if (ret) {
+		kfree(st);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ade7854_spi_remove(struct spi_device *spi)
+{
+	ade7854_remove(spi_get_drvdata(spi));
+
+	return 0;
+}
+
+static struct spi_driver ade7854_driver = {
+	.driver = {
+		.name = "ade7854",
+		.owner = THIS_MODULE,
+	},
+	.probe = ade7854_spi_probe,
+	.remove = __devexit_p(ade7854_spi_remove),
+};
+
+static __init int ade7854_init(void)
+{
+	return spi_register_driver(&ade7854_driver);
+}
+module_init(ade7854_init);
+
+static __exit void ade7854_exit(void)
+{
+	spi_unregister_driver(&ade7854_driver);
+}
+module_exit(ade7854_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC SPI Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c
new file mode 100644
index 0000000..a13d504
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854.c
@@ -0,0 +1,680 @@
+/*
+ * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "meter.h"
+#include "ade7854.h"
+
+static ssize_t ade7854_read_8bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u8 val = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = st->read_reg_8(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7854_read_16bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = st->read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7854_read_24bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u32 val = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = st->read_reg_24(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val & 0xFFFFFF);
+}
+
+static ssize_t ade7854_read_32bit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u32 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	ret = st->read_reg_32(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7854_write_8bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = st->write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t ade7854_write_16bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = st->write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t ade7854_write_24bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = st->write_reg_24(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t ade7854_write_32bit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = st->write_reg_32(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int ade7854_reset(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	int ret;
+	u16 val;
+
+	st->read_reg_16(dev, ADE7854_CONFIG, &val);
+	val |= 1 << 7; /* Software Chip Reset */
+	ret = st->write_reg_16(dev, ADE7854_CONFIG, val);
+
+	return ret;
+}
+
+
+static ssize_t ade7854_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -1;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return ade7854_reset(dev);
+	}
+	return -1;
+}
+
+static IIO_DEV_ATTR_AIGAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_AIGAIN);
+static IIO_DEV_ATTR_BIGAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_BIGAIN);
+static IIO_DEV_ATTR_CIGAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_CIGAIN);
+static IIO_DEV_ATTR_NIGAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_NIGAIN);
+static IIO_DEV_ATTR_AVGAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_AVGAIN);
+static IIO_DEV_ATTR_BVGAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_BVGAIN);
+static IIO_DEV_ATTR_CVGAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_CVGAIN);
+static IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_AVAGAIN);
+static IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_BVAGAIN);
+static IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_CVAGAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_AWATTOS);
+static IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_BWATTOS);
+static IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_CWATTOS);
+static IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_AVARGAIN);
+static IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_BVARGAIN);
+static IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_CVARGAIN);
+static IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_AVAROS);
+static IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_BVAROS);
+static IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(S_IWUSR | S_IRUGO,
+		ade7854_read_24bit,
+		ade7854_write_24bit,
+		ADE7854_CVAROS);
+static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO,
+		ade7854_read_32bit,
+		ade7854_write_32bit,
+		ADE7854_VPEAK);
+static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO,
+		ade7854_read_32bit,
+		ade7854_write_32bit,
+		ADE7854_VPEAK);
+static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_APHCAL);
+static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_BPHCAL);
+static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_CPHCAL);
+static IIO_DEV_ATTR_CF1DEN(S_IWUSR | S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_CF1DEN);
+static IIO_DEV_ATTR_CF2DEN(S_IWUSR | S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_CF2DEN);
+static IIO_DEV_ATTR_CF3DEN(S_IWUSR | S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_CF3DEN);
+static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_LINECYC);
+static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO,
+		ade7854_read_8bit,
+		ade7854_write_8bit,
+		ADE7854_SAGCYC);
+static IIO_DEV_ATTR_CFCYC(S_IWUSR | S_IRUGO,
+		ade7854_read_8bit,
+		ade7854_write_8bit,
+		ADE7854_CFCYC);
+static IIO_DEV_ATTR_PEAKCYC(S_IWUSR | S_IRUGO,
+		ade7854_read_8bit,
+		ade7854_write_8bit,
+		ADE7854_PEAKCYC);
+static IIO_DEV_ATTR_CHKSUM(ade7854_read_24bit,
+		ADE7854_CHECKSUM);
+static IIO_DEV_ATTR_ANGLE0(ade7854_read_24bit,
+		ADE7854_ANGLE0);
+static IIO_DEV_ATTR_ANGLE1(ade7854_read_24bit,
+		ADE7854_ANGLE1);
+static IIO_DEV_ATTR_ANGLE2(ade7854_read_24bit,
+		ADE7854_ANGLE2);
+static IIO_DEV_ATTR_AIRMS(S_IRUGO,
+		ade7854_read_24bit,
+		NULL,
+		ADE7854_AIRMS);
+static IIO_DEV_ATTR_BIRMS(S_IRUGO,
+		ade7854_read_24bit,
+		NULL,
+		ADE7854_BIRMS);
+static IIO_DEV_ATTR_CIRMS(S_IRUGO,
+		ade7854_read_24bit,
+		NULL,
+		ADE7854_CIRMS);
+static IIO_DEV_ATTR_NIRMS(S_IRUGO,
+		ade7854_read_24bit,
+		NULL,
+		ADE7854_NIRMS);
+static IIO_DEV_ATTR_AVRMS(S_IRUGO,
+		ade7854_read_24bit,
+		NULL,
+		ADE7854_AVRMS);
+static IIO_DEV_ATTR_BVRMS(S_IRUGO,
+		ade7854_read_24bit,
+		NULL,
+		ADE7854_BVRMS);
+static IIO_DEV_ATTR_CVRMS(S_IRUGO,
+		ade7854_read_24bit,
+		NULL,
+		ADE7854_CVRMS);
+static IIO_DEV_ATTR_AIRMSOS(S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_AIRMSOS);
+static IIO_DEV_ATTR_BIRMSOS(S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_BIRMSOS);
+static IIO_DEV_ATTR_CIRMSOS(S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_CIRMSOS);
+static IIO_DEV_ATTR_AVRMSOS(S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_AVRMSOS);
+static IIO_DEV_ATTR_BVRMSOS(S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_BVRMSOS);
+static IIO_DEV_ATTR_CVRMSOS(S_IRUGO,
+		ade7854_read_16bit,
+		ade7854_write_16bit,
+		ADE7854_CVRMSOS);
+static IIO_DEV_ATTR_VOLT_A(ade7854_read_24bit,
+		ADE7854_VAWV);
+static IIO_DEV_ATTR_VOLT_B(ade7854_read_24bit,
+		ADE7854_VBWV);
+static IIO_DEV_ATTR_VOLT_C(ade7854_read_24bit,
+		ADE7854_VCWV);
+static IIO_DEV_ATTR_CURRENT_A(ade7854_read_24bit,
+		ADE7854_IAWV);
+static IIO_DEV_ATTR_CURRENT_B(ade7854_read_24bit,
+		ADE7854_IBWV);
+static IIO_DEV_ATTR_CURRENT_C(ade7854_read_24bit,
+		ADE7854_ICWV);
+static IIO_DEV_ATTR_AWATTHR(ade7854_read_32bit,
+		ADE7854_AWATTHR);
+static IIO_DEV_ATTR_BWATTHR(ade7854_read_32bit,
+		ADE7854_BWATTHR);
+static IIO_DEV_ATTR_CWATTHR(ade7854_read_32bit,
+		ADE7854_CWATTHR);
+static IIO_DEV_ATTR_AFWATTHR(ade7854_read_32bit,
+		ADE7854_AFWATTHR);
+static IIO_DEV_ATTR_BFWATTHR(ade7854_read_32bit,
+		ADE7854_BFWATTHR);
+static IIO_DEV_ATTR_CFWATTHR(ade7854_read_32bit,
+		ADE7854_CFWATTHR);
+static IIO_DEV_ATTR_AVARHR(ade7854_read_32bit,
+		ADE7854_AVARHR);
+static IIO_DEV_ATTR_BVARHR(ade7854_read_32bit,
+		ADE7854_BVARHR);
+static IIO_DEV_ATTR_CVARHR(ade7854_read_32bit,
+		ADE7854_CVARHR);
+static IIO_DEV_ATTR_AVAHR(ade7854_read_32bit,
+		ADE7854_AVAHR);
+static IIO_DEV_ATTR_BVAHR(ade7854_read_32bit,
+		ADE7854_BVAHR);
+static IIO_DEV_ATTR_CVAHR(ade7854_read_32bit,
+		ADE7854_CVAHR);
+
+static int ade7854_set_irq(struct device *dev, bool enable)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ade7854_state *st = iio_dev_get_devdata(indio_dev);
+
+	int ret;
+	u32 irqen;
+
+	ret = st->read_reg_32(dev, ADE7854_MASK0, &irqen);
+	if (ret)
+		goto error_ret;
+
+	if (enable)
+		irqen |= 1 << 17; /* 1: interrupt enabled when all periodical
+				     (at 8 kHz rate) DSP computations finish. */
+	else
+		irqen &= ~(1 << 17);
+
+	ret = st->write_reg_32(dev, ADE7854_MASK0, irqen);
+	if (ret)
+		goto error_ret;
+
+error_ret:
+	return ret;
+}
+
+static int ade7854_initial_setup(struct ade7854_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = ade7854_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	ade7854_reset(dev);
+	msleep(ADE7854_STARTUP_DELAY);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_RESET(ade7854_write_reset);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("8000");
+
+static IIO_CONST_ATTR(name, "ade7854");
+
+static struct attribute *ade7854_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group ade7854_event_attribute_group = {
+	.attrs = ade7854_event_attributes,
+};
+
+static struct attribute *ade7854_attributes[] = {
+	&iio_dev_attr_aigain.dev_attr.attr,
+	&iio_dev_attr_bigain.dev_attr.attr,
+	&iio_dev_attr_cigain.dev_attr.attr,
+	&iio_dev_attr_nigain.dev_attr.attr,
+	&iio_dev_attr_avgain.dev_attr.attr,
+	&iio_dev_attr_bvgain.dev_attr.attr,
+	&iio_dev_attr_cvgain.dev_attr.attr,
+	&iio_dev_attr_linecyc.dev_attr.attr,
+	&iio_dev_attr_sagcyc.dev_attr.attr,
+	&iio_dev_attr_cfcyc.dev_attr.attr,
+	&iio_dev_attr_peakcyc.dev_attr.attr,
+	&iio_dev_attr_chksum.dev_attr.attr,
+	&iio_dev_attr_apparent_power_a_gain.dev_attr.attr,
+	&iio_dev_attr_apparent_power_b_gain.dev_attr.attr,
+	&iio_dev_attr_apparent_power_c_gain.dev_attr.attr,
+	&iio_dev_attr_active_power_a_offset.dev_attr.attr,
+	&iio_dev_attr_active_power_b_offset.dev_attr.attr,
+	&iio_dev_attr_active_power_c_offset.dev_attr.attr,
+	&iio_dev_attr_reactive_power_a_gain.dev_attr.attr,
+	&iio_dev_attr_reactive_power_b_gain.dev_attr.attr,
+	&iio_dev_attr_reactive_power_c_gain.dev_attr.attr,
+	&iio_dev_attr_reactive_power_a_offset.dev_attr.attr,
+	&iio_dev_attr_reactive_power_b_offset.dev_attr.attr,
+	&iio_dev_attr_reactive_power_c_offset.dev_attr.attr,
+	&iio_dev_attr_awatthr.dev_attr.attr,
+	&iio_dev_attr_bwatthr.dev_attr.attr,
+	&iio_dev_attr_cwatthr.dev_attr.attr,
+	&iio_dev_attr_afwatthr.dev_attr.attr,
+	&iio_dev_attr_bfwatthr.dev_attr.attr,
+	&iio_dev_attr_cfwatthr.dev_attr.attr,
+	&iio_dev_attr_avarhr.dev_attr.attr,
+	&iio_dev_attr_bvarhr.dev_attr.attr,
+	&iio_dev_attr_cvarhr.dev_attr.attr,
+	&iio_dev_attr_angle0.dev_attr.attr,
+	&iio_dev_attr_angle1.dev_attr.attr,
+	&iio_dev_attr_angle2.dev_attr.attr,
+	&iio_dev_attr_avahr.dev_attr.attr,
+	&iio_dev_attr_bvahr.dev_attr.attr,
+	&iio_dev_attr_cvahr.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_vpeak.dev_attr.attr,
+	&iio_dev_attr_ipeak.dev_attr.attr,
+	&iio_dev_attr_aphcal.dev_attr.attr,
+	&iio_dev_attr_bphcal.dev_attr.attr,
+	&iio_dev_attr_cphcal.dev_attr.attr,
+	&iio_dev_attr_cf1den.dev_attr.attr,
+	&iio_dev_attr_cf2den.dev_attr.attr,
+	&iio_dev_attr_cf3den.dev_attr.attr,
+	&iio_dev_attr_airms.dev_attr.attr,
+	&iio_dev_attr_birms.dev_attr.attr,
+	&iio_dev_attr_cirms.dev_attr.attr,
+	&iio_dev_attr_nirms.dev_attr.attr,
+	&iio_dev_attr_avrms.dev_attr.attr,
+	&iio_dev_attr_bvrms.dev_attr.attr,
+	&iio_dev_attr_cvrms.dev_attr.attr,
+	&iio_dev_attr_airmsos.dev_attr.attr,
+	&iio_dev_attr_birmsos.dev_attr.attr,
+	&iio_dev_attr_cirmsos.dev_attr.attr,
+	&iio_dev_attr_avrmsos.dev_attr.attr,
+	&iio_dev_attr_bvrmsos.dev_attr.attr,
+	&iio_dev_attr_cvrmsos.dev_attr.attr,
+	&iio_dev_attr_volt_a.dev_attr.attr,
+	&iio_dev_attr_volt_b.dev_attr.attr,
+	&iio_dev_attr_volt_c.dev_attr.attr,
+	&iio_dev_attr_current_a.dev_attr.attr,
+	&iio_dev_attr_current_b.dev_attr.attr,
+	&iio_dev_attr_current_c.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ade7854_attribute_group = {
+	.attrs = ade7854_attributes,
+};
+
+int ade7854_probe(struct ade7854_state *st, struct device *dev)
+{
+	int ret, regdone = 0;
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADE7854_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADE7854_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &ade7854_event_attribute_group;
+	st->indio_dev->attrs = &ade7854_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = ade7854_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = ade7854_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (st->irq) {
+		ret = iio_register_interrupt_line(st->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				"ade7854");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = ade7854_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+	/* Get the device into a sane initial state */
+	ret = ade7854_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+
+	return 0;
+
+error_remove_trigger:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		ade7854_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	ade7854_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	ade7854_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+	return ret;
+
+}
+EXPORT_SYMBOL(ade7854_probe);
+
+int ade7854_remove(struct ade7854_state *st)
+{
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	ade7854_remove_trigger(indio_dev);
+	if (st->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	ade7854_uninitialize_ring(indio_dev->ring);
+	ade7854_unconfigure_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+EXPORT_SYMBOL(ade7854_remove);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7854.h b/drivers/staging/iio/meter/ade7854.h
new file mode 100644
index 0000000..47690e5
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854.h
@@ -0,0 +1,245 @@
+#ifndef _ADE7854_H
+#define _ADE7854_H
+
+#define ADE7854_AIGAIN    0x4380
+#define ADE7854_AVGAIN    0x4381
+#define ADE7854_BIGAIN    0x4382
+#define ADE7854_BVGAIN    0x4383
+#define ADE7854_CIGAIN    0x4384
+#define ADE7854_CVGAIN    0x4385
+#define ADE7854_NIGAIN    0x4386
+#define ADE7854_AIRMSOS   0x4387
+#define ADE7854_AVRMSOS   0x4388
+#define ADE7854_BIRMSOS   0x4389
+#define ADE7854_BVRMSOS   0x438A
+#define ADE7854_CIRMSOS   0x438B
+#define ADE7854_CVRMSOS   0x438C
+#define ADE7854_NIRMSOS   0x438D
+#define ADE7854_AVAGAIN   0x438E
+#define ADE7854_BVAGAIN   0x438F
+#define ADE7854_CVAGAIN   0x4390
+#define ADE7854_AWGAIN    0x4391
+#define ADE7854_AWATTOS   0x4392
+#define ADE7854_BWGAIN    0x4393
+#define ADE7854_BWATTOS   0x4394
+#define ADE7854_CWGAIN    0x4395
+#define ADE7854_CWATTOS   0x4396
+#define ADE7854_AVARGAIN  0x4397
+#define ADE7854_AVAROS    0x4398
+#define ADE7854_BVARGAIN  0x4399
+#define ADE7854_BVAROS    0x439A
+#define ADE7854_CVARGAIN  0x439B
+#define ADE7854_CVAROS    0x439C
+#define ADE7854_AFWGAIN   0x439D
+#define ADE7854_AFWATTOS  0x439E
+#define ADE7854_BFWGAIN   0x439F
+#define ADE7854_BFWATTOS  0x43A0
+#define ADE7854_CFWGAIN   0x43A1
+#define ADE7854_CFWATTOS  0x43A2
+#define ADE7854_AFVARGAIN 0x43A3
+#define ADE7854_AFVAROS   0x43A4
+#define ADE7854_BFVARGAIN 0x43A5
+#define ADE7854_BFVAROS   0x43A6
+#define ADE7854_CFVARGAIN 0x43A7
+#define ADE7854_CFVAROS   0x43A8
+#define ADE7854_VATHR1    0x43A9
+#define ADE7854_VATHR0    0x43AA
+#define ADE7854_WTHR1     0x43AB
+#define ADE7854_WTHR0     0x43AC
+#define ADE7854_VARTHR1   0x43AD
+#define ADE7854_VARTHR0   0x43AE
+#define ADE7854_RSV       0x43AF
+#define ADE7854_VANOLOAD  0x43B0
+#define ADE7854_APNOLOAD  0x43B1
+#define ADE7854_VARNOLOAD 0x43B2
+#define ADE7854_VLEVEL    0x43B3
+#define ADE7854_DICOEFF   0x43B5
+#define ADE7854_HPFDIS    0x43B6
+#define ADE7854_ISUMLVL   0x43B8
+#define ADE7854_ISUM      0x43BF
+#define ADE7854_AIRMS     0x43C0
+#define ADE7854_AVRMS     0x43C1
+#define ADE7854_BIRMS     0x43C2
+#define ADE7854_BVRMS     0x43C3
+#define ADE7854_CIRMS     0x43C4
+#define ADE7854_CVRMS     0x43C5
+#define ADE7854_NIRMS     0x43C6
+#define ADE7854_RUN       0xE228
+#define ADE7854_AWATTHR   0xE400
+#define ADE7854_BWATTHR   0xE401
+#define ADE7854_CWATTHR   0xE402
+#define ADE7854_AFWATTHR  0xE403
+#define ADE7854_BFWATTHR  0xE404
+#define ADE7854_CFWATTHR  0xE405
+#define ADE7854_AVARHR    0xE406
+#define ADE7854_BVARHR    0xE407
+#define ADE7854_CVARHR    0xE408
+#define ADE7854_AFVARHR   0xE409
+#define ADE7854_BFVARHR   0xE40A
+#define ADE7854_CFVARHR   0xE40B
+#define ADE7854_AVAHR     0xE40C
+#define ADE7854_BVAHR     0xE40D
+#define ADE7854_CVAHR     0xE40E
+#define ADE7854_IPEAK     0xE500
+#define ADE7854_VPEAK     0xE501
+#define ADE7854_STATUS0   0xE502
+#define ADE7854_STATUS1   0xE503
+#define ADE7854_OILVL     0xE507
+#define ADE7854_OVLVL     0xE508
+#define ADE7854_SAGLVL    0xE509
+#define ADE7854_MASK0     0xE50A
+#define ADE7854_MASK1     0xE50B
+#define ADE7854_IAWV      0xE50C
+#define ADE7854_IBWV      0xE50D
+#define ADE7854_ICWV      0xE50E
+#define ADE7854_VAWV      0xE510
+#define ADE7854_VBWV      0xE511
+#define ADE7854_VCWV      0xE512
+#define ADE7854_AWATT     0xE513
+#define ADE7854_BWATT     0xE514
+#define ADE7854_CWATT     0xE515
+#define ADE7854_AVA       0xE519
+#define ADE7854_BVA       0xE51A
+#define ADE7854_CVA       0xE51B
+#define ADE7854_CHECKSUM  0xE51F
+#define ADE7854_VNOM      0xE520
+#define ADE7854_PHSTATUS  0xE600
+#define ADE7854_ANGLE0    0xE601
+#define ADE7854_ANGLE1    0xE602
+#define ADE7854_ANGLE2    0xE603
+#define ADE7854_PERIOD    0xE607
+#define ADE7854_PHNOLOAD  0xE608
+#define ADE7854_LINECYC   0xE60C
+#define ADE7854_ZXTOUT    0xE60D
+#define ADE7854_COMPMODE  0xE60E
+#define ADE7854_GAIN      0xE60F
+#define ADE7854_CFMODE    0xE610
+#define ADE7854_CF1DEN    0xE611
+#define ADE7854_CF2DEN    0xE612
+#define ADE7854_CF3DEN    0xE613
+#define ADE7854_APHCAL    0xE614
+#define ADE7854_BPHCAL    0xE615
+#define ADE7854_CPHCAL    0xE616
+#define ADE7854_PHSIGN    0xE617
+#define ADE7854_CONFIG    0xE618
+#define ADE7854_MMODE     0xE700
+#define ADE7854_ACCMODE   0xE701
+#define ADE7854_LCYCMODE  0xE702
+#define ADE7854_PEAKCYC   0xE703
+#define ADE7854_SAGCYC    0xE704
+#define ADE7854_CFCYC     0xE705
+#define ADE7854_HSDC_CFG  0xE706
+#define ADE7854_CONFIG2   0xEC01
+
+#define ADE7854_READ_REG   0x1
+#define ADE7854_WRITE_REG  0x0
+
+#define ADE7854_MAX_TX    7
+#define ADE7854_MAX_RX    7
+#define ADE7854_STARTUP_DELAY 1
+
+#define ADE7854_SPI_SLOW	(u32)(300 * 1000)
+#define ADE7854_SPI_BURST	(u32)(1000 * 1000)
+#define ADE7854_SPI_FAST	(u32)(2000 * 1000)
+
+#define DRIVER_NAME		"ade7854"
+
+/**
+ * struct ade7854_state - device instance specific data
+ * @spi:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct ade7854_state {
+	struct spi_device		*spi;
+	struct i2c_client               *i2c;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	int				(*read_reg_8) (struct device *, u16, u8 *);
+	int				(*read_reg_16) (struct device *, u16, u16 *);
+	int				(*read_reg_24) (struct device *, u16, u32 *);
+	int				(*read_reg_32) (struct device *, u16, u32 *);
+	int				(*write_reg_8) (struct device *, u16, u8);
+	int				(*write_reg_16) (struct device *, u16, u16);
+	int				(*write_reg_24) (struct device *, u16, u32);
+	int				(*write_reg_32) (struct device *, u16, u32);
+	int                             irq;
+	struct mutex			buf_lock;
+};
+
+extern int ade7854_probe(struct ade7854_state *st, struct device *dev);
+extern int ade7854_remove(struct ade7854_state *st);
+
+#if defined(CONFIG_IIO_RING_BUFFER) && defined(THIS_HAS_RING_BUFFER_SUPPORT)
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum ade7854_scan {
+	ADE7854_SCAN_PHA_V,
+	ADE7854_SCAN_PHB_V,
+	ADE7854_SCAN_PHC_V,
+	ADE7854_SCAN_PHA_I,
+	ADE7854_SCAN_PHB_I,
+	ADE7854_SCAN_PHC_I,
+};
+
+void ade7854_remove_trigger(struct iio_dev *indio_dev);
+int ade7854_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t ade7854_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+
+int ade7854_configure_ring(struct iio_dev *indio_dev);
+void ade7854_unconfigure_ring(struct iio_dev *indio_dev);
+
+int ade7854_initialize_ring(struct iio_ring_buffer *ring);
+void ade7854_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void ade7854_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7854_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+ade7854_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static inline int ade7854_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void ade7854_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7854_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+static inline void ade7854_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 34/36] staging: iio: resolver: new driver for AD2S90 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (32 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 33/36] staging: iio: meter: new driver for ADE7854/58/68/78 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 35/36] staging: iio: resolver: new driver for AD2S1200/1205 devices Mike Frysinger
                   ` (2 subsequent siblings)
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Graf Yang, Michael Hennerich

From: Graf Yang <graf.yang@analog.com>

This also kicks off the new resolver subsection.

Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/Kconfig           |    1 +
 drivers/staging/iio/Makefile          |    1 +
 drivers/staging/iio/resolver/Kconfig  |   11 +++
 drivers/staging/iio/resolver/Makefile |    5 +
 drivers/staging/iio/resolver/ad2s90.c |  159 +++++++++++++++++++++++++++++++++
 5 files changed, 177 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/resolver/Kconfig
 create mode 100644 drivers/staging/iio/resolver/Makefile
 create mode 100644 drivers/staging/iio/resolver/ad2s90.c

diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index ffe8912..e2ac07d 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -50,6 +50,7 @@ source "drivers/staging/iio/imu/Kconfig"
 source "drivers/staging/iio/light/Kconfig"
 source "drivers/staging/iio/magnetometer/Kconfig"
 source "drivers/staging/iio/meter/Kconfig"
+source "drivers/staging/iio/resolver/Kconfig"
 source "drivers/staging/iio/trigger/Kconfig"
 
 endif # IIO
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 5b449a1..f9b5fb2 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -19,4 +19,5 @@ obj-y += imu/
 obj-y += light/
 obj-y += magnetometer/
 obj-y += meter/
+obj-y += resolver/
 obj-y += trigger/
diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig
new file mode 100644
index 0000000..05b15a9
--- /dev/null
+++ b/drivers/staging/iio/resolver/Kconfig
@@ -0,0 +1,11 @@
+#
+# Resolver/Synchro drivers
+#
+comment "Resolver to digital converters"
+
+config AD2S90
+	tristate "Analog Devices ad2s90 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices spi resolver
+	  to digital converters, ad2s90, provides direct access via sysfs.
diff --git a/drivers/staging/iio/resolver/Makefile b/drivers/staging/iio/resolver/Makefile
new file mode 100644
index 0000000..067fa70
--- /dev/null
+++ b/drivers/staging/iio/resolver/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for Resolver/Synchro drivers
+#
+
+obj-$(CONFIG_AD2S90) += ad2s90.o
diff --git a/drivers/staging/iio/resolver/ad2s90.c b/drivers/staging/iio/resolver/ad2s90.c
new file mode 100644
index 0000000..4143535
--- /dev/null
+++ b/drivers/staging/iio/resolver/ad2s90.c
@@ -0,0 +1,159 @@
+/*
+ * ad2s90.c simple support for the ADI Resolver to Digital Converters: AD2S90
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad2s90"
+
+struct ad2s90_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+	u8 rx[2];
+	u8 tx[2];
+};
+
+static ssize_t ad2s90_show_angular(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret;
+	ssize_t len = 0;
+	u16 val;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s90_state *st = idev->dev_data;
+
+	xfer.len = 1;
+	xfer.tx_buf = st->tx;
+	xfer.rx_buf = st->rx;
+	mutex_lock(&st->lock);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+	len = sprintf(buf, "%d\n", val);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+#define IIO_DEV_ATTR_SIMPLE_RESOLVER(_show) \
+	IIO_DEVICE_ATTR(angular, S_IRUGO, _show, NULL, 0)
+
+static IIO_CONST_ATTR(description,
+	"Low Cost, Complete 12-Bit Resolver-to-Digital Converter");
+static IIO_DEV_ATTR_SIMPLE_RESOLVER(ad2s90_show_angular);
+
+static struct attribute *ad2s90_attributes[] = {
+	&iio_const_attr_description.dev_attr.attr,
+	&iio_dev_attr_angular.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad2s90_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad2s90_attributes,
+};
+
+static int __devinit ad2s90_probe(struct spi_device *spi)
+{
+	struct ad2s90_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad2s90_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+
+	/* need 600ns between CS and the first falling edge of SCLK */
+	spi->max_speed_hz = 830000;
+	spi->mode = SPI_MODE_3;
+	spi_setup(spi);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad2s90_remove(struct spi_device *spi)
+{
+	struct ad2s90_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad2s90_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad2s90_probe,
+	.remove = __devexit_p(ad2s90_remove),
+};
+
+static __init int ad2s90_spi_init(void)
+{
+	return spi_register_driver(&ad2s90_driver);
+}
+module_init(ad2s90_spi_init);
+
+static __exit void ad2s90_spi_exit(void)
+{
+	spi_unregister_driver(&ad2s90_driver);
+}
+module_exit(ad2s90_spi_exit);
+
+MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD2S90 Resolver to Digital SPI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 35/36] staging: iio: resolver: new driver for AD2S1200/1205 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (33 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 34/36] staging: iio: resolver: new driver for AD2S90 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-26 18:17 ` [PATCH 36/36] staging: iio: resolver: new driver for AD2S1210 devices Mike Frysinger
  2010-10-28  1:44 ` [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Graf Yang, Michael Hennerich

From: Graf Yang <graf.yang@analog.com>

Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/resolver/Kconfig    |    8 +
 drivers/staging/iio/resolver/Makefile   |    1 +
 drivers/staging/iio/resolver/ad2s120x.c |  310 +++++++++++++++++++++++++++++++
 3 files changed, 319 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/resolver/ad2s120x.c

diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig
index 05b15a9..32df73a 100644
--- a/drivers/staging/iio/resolver/Kconfig
+++ b/drivers/staging/iio/resolver/Kconfig
@@ -9,3 +9,11 @@ config AD2S90
 	help
 	  Say yes here to build support for Analog Devices spi resolver
 	  to digital converters, ad2s90, provides direct access via sysfs.
+
+config AD2S120X
+	tristate "Analog Devices ad2s120x driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices spi resolver
+	  to digital converters, ad2s1200 and ad2s1205, provides direct access
+	  via sysfs.
diff --git a/drivers/staging/iio/resolver/Makefile b/drivers/staging/iio/resolver/Makefile
index 067fa70..b2b3cc1 100644
--- a/drivers/staging/iio/resolver/Makefile
+++ b/drivers/staging/iio/resolver/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_AD2S90) += ad2s90.o
+obj-$(CONFIG_AD2S120X) += ad2s120x.o
diff --git a/drivers/staging/iio/resolver/ad2s120x.c b/drivers/staging/iio/resolver/ad2s120x.c
new file mode 100644
index 0000000..8f497a2
--- /dev/null
+++ b/drivers/staging/iio/resolver/ad2s120x.c
@@ -0,0 +1,310 @@
+/*
+ * ad2s120x.c simple support for the ADI Resolver to Digital Converters: AD2S1200/1205
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad2s120x"
+
+/* input pin sample and rdvel is controlled by driver */
+#define AD2S120X_PN	2
+
+/* input clock on serial interface */
+#define AD2S120X_HZ	8192000
+/* clock period in nano second */
+#define AD2S120X_TSCLK	(1000000000/AD2S120X_HZ)
+
+struct ad2s120x_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+	unsigned short sample;
+	unsigned short rdvel;
+	u8 rx[2];
+	u8 tx[2];
+};
+
+static ssize_t ad2s120x_show_pos_vel(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret = 0;
+	ssize_t len = 0;
+	u16 pos;
+	s16 vel;
+	u8 status;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s120x_state *st = idev->dev_data;
+
+	xfer.len = 1;
+	xfer.tx_buf = st->tx;
+	xfer.rx_buf = st->rx;
+	mutex_lock(&st->lock);
+
+	gpio_set_value(st->sample, 0);
+	/* delay (6 * AD2S120X_TSCLK + 20) nano seconds */
+	udelay(1);
+	gpio_set_value(st->sample, 1);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	status = st->rx[1];
+	pos = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+	len = sprintf(buf, "%d %c%c%c%c ", pos,
+				(status & 0x8) ? 'P' : 'V',
+				(status & 0x4) ? 'd' : '_',
+				(status & 0x2) ? 'l' : '_',
+				(status & 0x1) ? '1' : '0');
+
+	/* delay 18 ns */
+	/* ndelay(18); */
+
+	gpio_set_value(st->rdvel, 0);
+	/* ndelay(5);*/
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	status = st->rx[1];
+	vel = (st->rx[0] & 0x80) ? 0xf000 : 0;
+	vel |= (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+	len += sprintf(buf + len, "%d %c%c%c%c\n", vel,
+				(status & 0x8) ? 'P' : 'V',
+				(status & 0x4) ? 'd' : '_',
+				(status & 0x2) ? 'l' : '_',
+				(status & 0x1) ? '1' : '0');
+error_ret:
+	gpio_set_value(st->rdvel, 1);
+	/* delay (2 * AD2S120X_TSCLK + 20) ns for sample pulse */
+	udelay(1);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad2s120x_show_pos(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret = 0;
+	ssize_t len = 0;
+	u16 pos;
+	u8 status;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s120x_state *st = idev->dev_data;
+
+	xfer.len = 1;
+	xfer.tx_buf = st->tx;
+	xfer.rx_buf = st->rx;
+	mutex_lock(&st->lock);
+
+	gpio_set_value(st->sample, 0);
+	/* delay (6 * AD2S120X_TSCLK + 20) nano seconds */
+	udelay(1);
+	gpio_set_value(st->sample, 1);
+	gpio_set_value(st->rdvel, 1);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	status = st->rx[1];
+	pos = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+	len = sprintf(buf, "%d %c%c%c%c ", pos,
+				(status & 0x8) ? 'P' : 'V',
+				(status & 0x4) ? 'd' : '_',
+				(status & 0x2) ? 'l' : '_',
+				(status & 0x1) ? '1' : '0');
+error_ret:
+	/* delay (2 * AD2S120X_TSCLK + 20) ns for sample pulse */
+	udelay(1);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad2s120x_show_vel(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int ret = 0;
+	ssize_t len = 0;
+	s16 vel;
+	u8 status;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s120x_state *st = idev->dev_data;
+
+	xfer.len = 1;
+	xfer.tx_buf = st->tx;
+	xfer.rx_buf = st->rx;
+	mutex_lock(&st->lock);
+
+	gpio_set_value(st->sample, 0);
+	/* delay (6 * AD2S120X_TSCLK + 20) nano seconds */
+	udelay(1);
+	gpio_set_value(st->sample, 1);
+
+	gpio_set_value(st->rdvel, 0);
+	/* ndelay(5);*/
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	status = st->rx[1];
+	vel = (st->rx[0] & 0x80) ? 0xf000 : 0;
+	vel |= (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+	len += sprintf(buf + len, "%d %c%c%c%c\n", vel,
+				(status & 0x8) ? 'P' : 'V',
+				(status & 0x4) ? 'd' : '_',
+				(status & 0x2) ? 'l' : '_',
+				(status & 0x1) ? '1' : '0');
+error_ret:
+	gpio_set_value(st->rdvel, 1);
+	/* delay (2 * AD2S120X_TSCLK + 20) ns for sample pulse */
+	udelay(1);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_CONST_ATTR(description,
+	"12-Bit R/D Converter with Reference Oscillator");
+static IIO_DEVICE_ATTR(pos_vel, S_IRUGO, ad2s120x_show_pos_vel, NULL, 0);
+static IIO_DEVICE_ATTR(pos, S_IRUGO, ad2s120x_show_pos, NULL, 0);
+static IIO_DEVICE_ATTR(vel, S_IRUGO, ad2s120x_show_vel, NULL, 0);
+
+static struct attribute *ad2s120x_attributes[] = {
+	&iio_const_attr_description.dev_attr.attr,
+	&iio_dev_attr_pos_vel.dev_attr.attr,
+	&iio_dev_attr_pos.dev_attr.attr,
+	&iio_dev_attr_vel.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad2s120x_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad2s120x_attributes,
+};
+
+static int __devinit ad2s120x_probe(struct spi_device *spi)
+{
+	struct ad2s120x_state *st;
+	int pn, ret = 0;
+	unsigned short *pins = spi->dev.platform_data;
+
+	for (pn = 0; pn < AD2S120X_PN; pn++) {
+		if (gpio_request(pins[pn], DRV_NAME)) {
+			pr_err("%s: request gpio pin %d failed\n",
+						DRV_NAME, pins[pn]);
+			goto error_ret;
+		}
+		gpio_direction_output(pins[pn], 1);
+	}
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+	st->sample = pins[0];
+	st->rdvel = pins[1];
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad2s120x_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+
+	spi->max_speed_hz = AD2S120X_HZ;
+	spi->mode = SPI_MODE_3;
+	spi_setup(spi);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	for (--pn; pn >= 0; pn--)
+		gpio_free(pins[pn]);
+	return ret;
+}
+
+static int __devexit ad2s120x_remove(struct spi_device *spi)
+{
+	struct ad2s120x_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad2s120x_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad2s120x_probe,
+	.remove = __devexit_p(ad2s120x_remove),
+};
+
+static __init int ad2s120x_spi_init(void)
+{
+	return spi_register_driver(&ad2s120x_driver);
+}
+module_init(ad2s120x_spi_init);
+
+static __exit void ad2s120x_spi_exit(void)
+{
+	spi_unregister_driver(&ad2s120x_driver);
+}
+module_exit(ad2s120x_spi_exit);
+
+MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD2S1200/1205 Resolver to Digital SPI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 36/36] staging: iio: resolver: new driver for AD2S1210 devices
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (34 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 35/36] staging: iio: resolver: new driver for AD2S1200/1205 devices Mike Frysinger
@ 2010-10-26 18:17 ` Mike Frysinger
  2010-10-28  1:44 ` [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
  36 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-10-26 18:17 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron
  Cc: device-drivers-devel, Graf Yang, Michael Hennerich

From: Graf Yang <graf.yang@analog.com>

Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/resolver/Kconfig    |   35 ++
 drivers/staging/iio/resolver/Makefile   |    1 +
 drivers/staging/iio/resolver/ad2s1210.c |  872 +++++++++++++++++++++++++++++++
 3 files changed, 908 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/resolver/ad2s1210.c

diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig
index 32df73a..a4a3634 100644
--- a/drivers/staging/iio/resolver/Kconfig
+++ b/drivers/staging/iio/resolver/Kconfig
@@ -17,3 +17,38 @@ config AD2S120X
 	  Say yes here to build support for Analog Devices spi resolver
 	  to digital converters, ad2s1200 and ad2s1205, provides direct access
 	  via sysfs.
+
+config AD2S1210
+	tristate "Analog Devices ad2s1210 driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices spi resolver
+	  to digital converters, ad2s1210, provides direct access via sysfs.
+
+choice
+	prompt "Resolution Control"
+	depends on AD2S1210
+	default AD2S1210_GPIO_NONE
+	help
+	  In normal mode, the resolution of the digital output is selected
+	  using the RES0 and RES1 input pins. In configuration mode, the
+	  resolution is selected by setting the RES0 and RES1 bits in the
+	  control regsiter. When switching between normal mode and configuration
+	  mode, there are some schemes to keep them matchs.
+
+config AD2S1210_GPIO_INPUT
+	bool "read resolution from gpio pins"
+	help
+	  GPIO pins are sampling RES0 and RES1 pins, read the resolution
+	  settings from the GPIO pins.
+
+config AD2S1210_GPIO_OUTPUT
+	bool "set gpio pins to set resolution"
+	help
+	  RES0 and RES1 pins are controlled by GPIOs, setting GPIO pins to
+	  set the resolution.
+
+config AD2S1210_GPIO_NONE
+	bool "take the responsibility by user"
+
+endchoice
diff --git a/drivers/staging/iio/resolver/Makefile b/drivers/staging/iio/resolver/Makefile
index b2b3cc1..0b84a89 100644
--- a/drivers/staging/iio/resolver/Makefile
+++ b/drivers/staging/iio/resolver/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_AD2S90) += ad2s90.o
 obj-$(CONFIG_AD2S120X) += ad2s120x.o
+obj-$(CONFIG_AD2S1210) += ad2s1210.o
diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
new file mode 100644
index 0000000..34fb21a
--- /dev/null
+++ b/drivers/staging/iio/resolver/ad2s1210.c
@@ -0,0 +1,872 @@
+/*
+ * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#define DRV_NAME "ad2s1210"
+
+#define DEF_CONTROL		0x7E
+
+#define MSB_IS_HIGH		0x80
+#define MSB_IS_LOW		0x7F
+#define PHASE_LOCK_RANGE_44	0x20
+#define ENABLE_HYSTERESIS	0x10
+#define SET_ENRES1		0x08
+#define SET_ENRES0		0x04
+#define SET_RES1		0x02
+#define SET_RES0		0x01
+
+#define SET_ENRESOLUTION	(SET_ENRES1 | SET_ENRES0)
+#define SET_RESOLUTION		(SET_RES1 | SET_RES0)
+
+#define REG_POSITION		0x80
+#define REG_VELOCITY		0x82
+#define REG_LOS_THRD		0x88
+#define REG_DOS_OVR_THRD	0x89
+#define REG_DOS_MIS_THRD	0x8A
+#define REG_DOS_RST_MAX_THRD	0x8B
+#define REG_DOS_RST_MIN_THRD	0x8C
+#define REG_LOT_HIGH_THRD	0x8D
+#define REG_LOT_LOW_THRD	0x8E
+#define REG_EXCIT_FREQ		0x91
+#define REG_CONTROL		0x92
+#define REG_SOFT_RESET		0xF0
+#define REG_FAULT		0xFF
+
+/* pin SAMPLE, A0, A1, RES0, RES1, is controlled by driver */
+#define AD2S1210_SAA		3
+#if defined(CONFIG_AD2S1210_GPIO_INPUT) || defined(CONFIG_AD2S1210_GPIO_OUTPUT)
+# define AD2S1210_RES		2
+#else
+# define AD2S1210_RES		0
+#endif
+#define AD2S1210_PN		(AD2S1210_SAA + AD2S1210_RES)
+
+#define AD2S1210_MIN_CLKIN	6144000
+#define AD2S1210_MAX_CLKIN	10240000
+#define AD2S1210_MIN_EXCIT	2000
+#define AD2S1210_MAX_EXCIT	20000
+#define AD2S1210_MIN_FCW	0x4
+#define AD2S1210_MAX_FCW	0x50
+
+/* default input clock on serial interface */
+#define AD2S1210_DEF_CLKIN	8192000
+/* clock period in nano second */
+#define AD2S1210_DEF_TCK	(1000000000/AD2S1210_DEF_CLKIN)
+#define AD2S1210_DEF_EXCIT	10000
+
+enum ad2s1210_mode {
+	MOD_POS = 0,
+	MOD_VEL,
+	MOD_RESERVED,
+	MOD_CONFIG,
+};
+
+enum ad2s1210_res {
+	RES_10 = 10,
+	RES_12 = 12,
+	RES_14 = 14,
+	RES_16 = 16,
+};
+
+static unsigned int resolution_value[] = {
+		RES_10, RES_12, RES_14, RES_16};
+
+struct ad2s1210_state {
+	struct mutex lock;
+	struct iio_dev *idev;
+	struct spi_device *sdev;
+	struct spi_transfer xfer;
+	unsigned int hysteresis;
+	unsigned int old_data;
+	enum ad2s1210_mode mode;
+	enum ad2s1210_res resolution;
+	unsigned int fclkin;
+	unsigned int fexcit;
+	unsigned short sample;
+	unsigned short a0;
+	unsigned short a1;
+	unsigned short res0;
+	unsigned short res1;
+	u8 rx[3];
+	u8 tx[3];
+};
+
+static inline void start_sample(struct ad2s1210_state *st)
+{
+	gpio_set_value(st->sample, 0);
+}
+
+static inline void stop_sample(struct ad2s1210_state *st)
+{
+	gpio_set_value(st->sample, 1);
+}
+
+static inline void set_mode(enum ad2s1210_mode mode, struct ad2s1210_state *st)
+{
+	switch (mode) {
+	case MOD_POS:
+		gpio_set_value(st->a0, 0);
+		gpio_set_value(st->a1, 0);
+		break;
+	case MOD_VEL:
+		gpio_set_value(st->a0, 0);
+		gpio_set_value(st->a1, 1);
+		break;
+	case MOD_CONFIG:
+		gpio_set_value(st->a0, 1);
+		gpio_set_value(st->a1, 1);
+		break;
+	default:
+		/* set to reserved mode */
+		gpio_set_value(st->a0, 1);
+		gpio_set_value(st->a1, 0);
+	}
+	st->mode = mode;
+}
+
+/* write 1 bytes (address or data) to the chip */
+static int config_write(struct ad2s1210_state *st,
+					unsigned char data)
+{
+	struct spi_message msg;
+	int ret = 0;
+
+	st->xfer.len = 1;
+	set_mode(MOD_CONFIG, st);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&st->xfer, &msg);
+	st->tx[0] = data;
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		return ret;
+	st->old_data = 1;
+	return ret;
+}
+
+/* read value from one of the registers */
+static int config_read(struct ad2s1210_state *st,
+				unsigned char address,
+					unsigned char *data)
+{
+	struct spi_message msg;
+	int ret = 0;
+
+	st->xfer.len = 2;
+	set_mode(MOD_CONFIG, st);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&st->xfer, &msg);
+	st->tx[0] = address | MSB_IS_HIGH;
+	st->tx[1] = REG_FAULT;
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		return ret;
+	*data = st->rx[1];
+	st->old_data = 1;
+	return ret;
+}
+
+static inline void update_frequency_control_word(struct ad2s1210_state *st)
+{
+	unsigned char fcw;
+	fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin);
+	if (fcw >= AD2S1210_MIN_FCW && fcw <= AD2S1210_MAX_FCW) {
+		config_write(st, REG_EXCIT_FREQ);
+		config_write(st, fcw);
+	} else
+		pr_err("ad2s1210: FCW out of range\n");
+}
+
+#if defined(CONFIG_AD2S1210_GPIO_INPUT)
+static inline unsigned char read_resolution_pin(struct ad2s1210_state *st)
+{
+	unsigned int data;
+	data = (gpio_get_value(st->res0) << 1)  |
+			gpio_get_value(st->res1);
+	return resolution_value[data];
+}
+#elif defined(CONFIG_AD2S1210_GPIO_OUTPUT)
+static inline void set_resolution_pin(struct ad2s1210_state *st)
+{
+	switch (st->resolution) {
+	case RES_10:
+		gpio_set_value(st->res0, 0);
+		gpio_set_value(st->res1, 0);
+		break;
+	case RES_12:
+		gpio_set_value(st->res0, 0);
+		gpio_set_value(st->res1, 1);
+		break;
+	case RES_14:
+		gpio_set_value(st->res0, 1);
+		gpio_set_value(st->res1, 0);
+		break;
+	case RES_16:
+		gpio_set_value(st->res0, 1);
+		gpio_set_value(st->res1, 1);
+		break;
+	}
+}
+#endif
+
+static inline void soft_reset(struct ad2s1210_state *st)
+{
+	config_write(st, REG_SOFT_RESET);
+	config_write(st, 0x0);
+}
+
+
+/* return the OLD DATA since last spi bus write */
+static ssize_t ad2s1210_show_raw(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	int ret;
+
+	mutex_lock(&st->lock);
+	if (st->old_data) {
+		ret = sprintf(buf, "0x%x\n", st->rx[0]);
+		st->old_data = 0;
+	} else
+		ret = 0;
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+static ssize_t ad2s1210_store_raw(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned long udata;
+	unsigned char data;
+	int ret;
+
+	ret = strict_strtoul(buf, 16, &udata);
+	if (ret)
+		return -EINVAL;
+	data = udata & 0xff;
+	mutex_lock(&st->lock);
+	config_write(st, data);
+	mutex_unlock(&st->lock);
+	return 1;
+}
+
+static ssize_t ad2s1210_store_softreset(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	mutex_lock(&st->lock);
+	soft_reset(st);
+	mutex_unlock(&st->lock);
+	return len;
+}
+
+static ssize_t ad2s1210_show_fclkin(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	return sprintf(buf, "%d\n", st->fclkin);
+}
+
+static ssize_t ad2s1210_store_fclkin(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned long fclkin;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &fclkin);
+	if (!ret && fclkin >= AD2S1210_MIN_CLKIN &&
+				fclkin <= AD2S1210_MAX_CLKIN) {
+		mutex_lock(&st->lock);
+		st->fclkin = fclkin;
+	} else {
+		pr_err("ad2s1210: fclkin out of range\n");
+		return -EINVAL;
+	}
+	update_frequency_control_word(st);
+	soft_reset(st);
+	mutex_unlock(&st->lock);
+	return len;
+}
+
+static ssize_t ad2s1210_show_fexcit(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	return sprintf(buf, "%d\n", st->fexcit);
+}
+
+static ssize_t ad2s1210_store_fexcit(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned long fexcit;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &fexcit);
+	if (!ret && fexcit >= AD2S1210_MIN_EXCIT &&
+				fexcit <= AD2S1210_MAX_EXCIT) {
+		mutex_lock(&st->lock);
+		st->fexcit = fexcit;
+	} else {
+		pr_err("ad2s1210: excitation frequency out of range\n");
+		return -EINVAL;
+	}
+	update_frequency_control_word(st);
+	soft_reset(st);
+	mutex_unlock(&st->lock);
+	return len;
+}
+
+static ssize_t ad2s1210_show_control(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned char data;
+	mutex_lock(&st->lock);
+	config_read(st, REG_CONTROL, &data);
+	mutex_unlock(&st->lock);
+	return sprintf(buf, "0x%x\n", data);
+}
+
+static ssize_t ad2s1210_store_control(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned long udata;
+	unsigned char data;
+	int ret;
+
+	ret = strict_strtoul(buf, 16, &udata);
+	if (ret) {
+		ret = -EINVAL;
+		goto error_ret;
+	}
+	mutex_lock(&st->lock);
+	config_write(st, REG_CONTROL);
+	data = udata & MSB_IS_LOW;
+	config_write(st, data);
+	config_read(st, REG_CONTROL, &data);
+	if (data & MSB_IS_HIGH) {
+		ret = -EIO;
+		pr_err("ad2s1210: write control register fail\n");
+		goto error_ret;
+	}
+	st->resolution = resolution_value[data & SET_RESOLUTION];
+#if defined(CONFIG_AD2S1210_GPIO_INPUT)
+	data = read_resolution_pin(st);
+	if (data != st->resolution)
+		pr_warning("ad2s1210: resolution settings not match\n");
+#elif defined(CONFIG_AD2S1210_GPIO_OUTPUT)
+	set_resolution_pin(st);
+#endif
+	ret = len;
+	if (data & ENABLE_HYSTERESIS)
+		st->hysteresis = 1;
+	else
+		st->hysteresis = 0;
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+static ssize_t ad2s1210_show_resolution(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	return sprintf(buf, "%d\n", st->resolution);
+}
+
+static ssize_t ad2s1210_store_resolution(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned char data;
+	unsigned long udata;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &udata);
+	if (ret || udata < RES_10 || udata > RES_16) {
+		pr_err("ad2s1210: resolution out of range\n");
+		return -EINVAL;
+	}
+	mutex_lock(&st->lock);
+	config_read(st, REG_CONTROL, &data);
+	data &= ~SET_RESOLUTION;
+	data |= (udata - RES_10) >> 1;
+	config_write(st, REG_CONTROL);
+	config_write(st, data & MSB_IS_LOW);
+	config_read(st, REG_CONTROL, &data);
+	if (data & MSB_IS_HIGH) {
+		ret = -EIO;
+		pr_err("ad2s1210: setting resolution fail\n");
+		goto error_ret;
+	}
+	st->resolution = resolution_value[data & SET_RESOLUTION];
+#if defined(CONFIG_AD2S1210_GPIO_INPUT)
+	data = read_resolution_pin(st);
+	if (data != st->resolution)
+		pr_warning("ad2s1210: resolution settings not match\n");
+#elif defined(CONFIG_AD2S1210_GPIO_OUTPUT)
+	set_resolution_pin(st);
+#endif
+	ret = len;
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+/* read the fault register since last sample */
+static ssize_t ad2s1210_show_fault(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	ssize_t len = 0;
+	unsigned char data;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+
+	mutex_lock(&st->lock);
+	ret = config_read(st, REG_FAULT, &data);
+
+	if (ret)
+		goto error_ret;
+	len = sprintf(buf, "0x%x\n", data);
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret ? ret : len;
+}
+
+static ssize_t ad2s1210_clear_fault(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned char data;
+
+	mutex_lock(&st->lock);
+	start_sample(st);
+	/* delay (2 * tck + 20) nano seconds */
+	udelay(1);
+	stop_sample(st);
+	config_read(st, REG_FAULT, &data);
+	start_sample(st);
+	stop_sample(st);
+	mutex_unlock(&st->lock);
+
+	return 0;
+}
+
+static ssize_t ad2s1210_show_reg(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned char data;
+	struct iio_dev_attr *iattr = to_iio_dev_attr(attr);
+
+	mutex_lock(&st->lock);
+	config_read(st, iattr->address, &data);
+	mutex_unlock(&st->lock);
+	return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t ad2s1210_store_reg(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+	unsigned long data;
+	int ret;
+	struct iio_dev_attr *iattr = to_iio_dev_attr(attr);
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+	mutex_lock(&st->lock);
+	config_write(st, iattr->address);
+	config_write(st, data & MSB_IS_LOW);
+	mutex_unlock(&st->lock);
+	return len;
+}
+
+static ssize_t ad2s1210_show_pos(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct spi_message msg;
+	int ret = 0;
+	ssize_t len = 0;
+	u16 pos;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+
+	st->xfer.len = 2;
+	mutex_lock(&st->lock);
+	start_sample(st);
+	/* delay (6 * tck + 20) nano seconds */
+	udelay(1);
+
+	set_mode(MOD_POS, st);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&st->xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	pos = ((((u16)(st->rx[0])) << 8) | (st->rx[1]));
+	if (st->hysteresis)
+		pos >>= 16 - st->resolution;
+	len = sprintf(buf, "%d\n", pos);
+error_ret:
+	stop_sample(st);
+	/* delay (2 * tck + 20) nano seconds */
+	udelay(1);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad2s1210_show_vel(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct spi_message msg;
+	unsigned short negative;
+	int ret = 0;
+	ssize_t len = 0;
+	s16 vel;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+
+	st->xfer.len = 2;
+	mutex_lock(&st->lock);
+	start_sample(st);
+	/* delay (6 * tck + 20) nano seconds */
+	udelay(1);
+
+	set_mode(MOD_VEL, st);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&st->xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	negative = st->rx[0] & 0x80;
+	vel = ((((s16)(st->rx[0])) << 8) | (st->rx[1]));
+	vel >>= 16 - st->resolution;
+	if (negative) {
+		negative = (0xffff >> st->resolution) << st->resolution;
+		vel |= negative;
+	}
+	len = sprintf(buf, "%d\n", vel);
+error_ret:
+	stop_sample(st);
+	/* delay (2 * tck + 20) nano seconds */
+	udelay(1);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad2s1210_show_pos_vel(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct spi_message msg;
+	unsigned short negative;
+	int ret = 0;
+	ssize_t len = 0;
+	u16 pos;
+	s16 vel;
+	struct iio_dev *idev = dev_get_drvdata(dev);
+	struct ad2s1210_state *st = idev->dev_data;
+
+	st->xfer.len = 2;
+	mutex_lock(&st->lock);
+	start_sample(st);
+	/* delay (6 * tck + 20) nano seconds */
+	udelay(1);
+
+	set_mode(MOD_POS, st);
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&st->xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	pos = ((((u16)(st->rx[0])) << 8) | (st->rx[1]));
+	if (st->hysteresis)
+		pos >>= 16 - st->resolution;
+	len = sprintf(buf, "%d ", pos);
+
+	st->xfer.len = 2;
+	set_mode(MOD_VEL, st);
+	spi_message_init(&msg);
+	spi_message_add_tail(&st->xfer, &msg);
+	ret = spi_sync(st->sdev, &msg);
+	if (ret)
+		goto error_ret;
+	negative = st->rx[0] & 0x80;
+	vel = ((((s16)(st->rx[0])) << 8) | (st->rx[1]));
+	vel >>= 16 - st->resolution;
+	if (negative) {
+		negative = (0xffff >> st->resolution) << st->resolution;
+		vel |= negative;
+	}
+	len += sprintf(buf + len, "%d\n", vel);
+error_ret:
+	stop_sample(st);
+	/* delay (2 * tck + 20) nano seconds */
+	udelay(1);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_CONST_ATTR(description,
+	"Variable Resolution, 10-Bit to 16Bit R/D\n\
+Converter with Reference Oscillator");
+static IIO_DEVICE_ATTR(raw_io, S_IRUGO | S_IWUGO,
+		ad2s1210_show_raw, ad2s1210_store_raw, 0);
+static IIO_DEVICE_ATTR(reset, S_IWUGO,
+		NULL, ad2s1210_store_softreset, 0);
+static IIO_DEVICE_ATTR(fclkin, S_IRUGO | S_IWUGO,
+		ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0);
+static IIO_DEVICE_ATTR(fexcit, S_IRUGO | S_IWUGO,
+		ad2s1210_show_fexcit,	ad2s1210_store_fexcit, 0);
+static IIO_DEVICE_ATTR(control, S_IRUGO | S_IWUGO,
+		ad2s1210_show_control, ad2s1210_store_control, 0);
+static IIO_DEVICE_ATTR(bits, S_IRUGO | S_IWUGO,
+		ad2s1210_show_resolution, ad2s1210_store_resolution, 0);
+static IIO_DEVICE_ATTR(fault, S_IRUGO | S_IWUGO,
+		ad2s1210_show_fault, ad2s1210_clear_fault, 0);
+static IIO_DEVICE_ATTR(pos, S_IRUGO,
+		ad2s1210_show_pos, NULL, 0);
+static IIO_DEVICE_ATTR(vel, S_IRUGO,
+		ad2s1210_show_vel, NULL, 0);
+static IIO_DEVICE_ATTR(pos_vel, S_IRUGO,
+		ad2s1210_show_pos_vel, NULL, 0);
+static IIO_DEVICE_ATTR(los_thrd, S_IRUGO | S_IWUGO,
+		ad2s1210_show_reg, ad2s1210_store_reg, REG_LOS_THRD);
+static IIO_DEVICE_ATTR(dos_ovr_thrd, S_IRUGO | S_IWUGO,
+		ad2s1210_show_reg, ad2s1210_store_reg, REG_DOS_OVR_THRD);
+static IIO_DEVICE_ATTR(dos_mis_thrd, S_IRUGO | S_IWUGO,
+		ad2s1210_show_reg, ad2s1210_store_reg, REG_DOS_MIS_THRD);
+static IIO_DEVICE_ATTR(dos_rst_max_thrd, S_IRUGO | S_IWUGO,
+		ad2s1210_show_reg, ad2s1210_store_reg, REG_DOS_RST_MAX_THRD);
+static IIO_DEVICE_ATTR(dos_rst_min_thrd, S_IRUGO | S_IWUGO,
+		ad2s1210_show_reg, ad2s1210_store_reg, REG_DOS_RST_MIN_THRD);
+static IIO_DEVICE_ATTR(lot_high_thrd, S_IRUGO | S_IWUGO,
+		ad2s1210_show_reg, ad2s1210_store_reg, REG_LOT_HIGH_THRD);
+static IIO_DEVICE_ATTR(lot_low_thrd, S_IRUGO | S_IWUGO,
+		ad2s1210_show_reg, ad2s1210_store_reg, REG_LOT_LOW_THRD);
+
+static struct attribute *ad2s1210_attributes[] = {
+	&iio_const_attr_description.dev_attr.attr,
+	&iio_dev_attr_raw_io.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_dev_attr_fclkin.dev_attr.attr,
+	&iio_dev_attr_fexcit.dev_attr.attr,
+	&iio_dev_attr_control.dev_attr.attr,
+	&iio_dev_attr_bits.dev_attr.attr,
+	&iio_dev_attr_fault.dev_attr.attr,
+	&iio_dev_attr_pos.dev_attr.attr,
+	&iio_dev_attr_vel.dev_attr.attr,
+	&iio_dev_attr_pos_vel.dev_attr.attr,
+	&iio_dev_attr_los_thrd.dev_attr.attr,
+	&iio_dev_attr_dos_ovr_thrd.dev_attr.attr,
+	&iio_dev_attr_dos_mis_thrd.dev_attr.attr,
+	&iio_dev_attr_dos_rst_max_thrd.dev_attr.attr,
+	&iio_dev_attr_dos_rst_min_thrd.dev_attr.attr,
+	&iio_dev_attr_lot_high_thrd.dev_attr.attr,
+	&iio_dev_attr_lot_low_thrd.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad2s1210_attribute_group = {
+	.name = DRV_NAME,
+	.attrs = ad2s1210_attributes,
+};
+
+static int __devinit ad2s1210_initial(struct ad2s1210_state *st)
+{
+	unsigned char data;
+	int ret;
+
+	mutex_lock(&st->lock);
+#if defined(CONFIG_AD2S1210_GPIO_INPUT)
+	st->resolution = read_resolution_pin(st);
+#elif defined(CONFIG_AD2S1210_GPIO_OUTPUT)
+	set_resolution_pin(st);
+#endif
+
+	config_write(st, REG_CONTROL);
+	data = DEF_CONTROL & ~(SET_RESOLUTION);
+	data |= (st->resolution - RES_10) >> 1;
+	config_write(st, data);
+	ret = config_read(st, REG_CONTROL, &data);
+	if (ret)
+		goto error_ret;
+
+	if (data & MSB_IS_HIGH) {
+		ret = -EIO;
+		goto error_ret;
+	}
+
+	update_frequency_control_word(st);
+	soft_reset(st);
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+static int __devinit ad2s1210_probe(struct spi_device *spi)
+{
+	struct ad2s1210_state *st;
+	int pn, ret = 0;
+	unsigned short *pins = spi->dev.platform_data;
+
+	for (pn = 0; pn < AD2S1210_PN; pn++) {
+		if (gpio_request(pins[pn], DRV_NAME)) {
+			pr_err("%s: request gpio pin %d failed\n",
+						DRV_NAME, pins[pn]);
+			goto error_ret;
+		}
+		if (pn < AD2S1210_SAA)
+			gpio_direction_output(pins[pn], 1);
+		else {
+#if defined(CONFIG_AD2S1210_GPIO_INPUT)
+			gpio_direction_input(pins[pn]);
+#elif defined(CONFIG_AD2S1210_GPIO_OUTPUT)
+			gpio_direction_output(pins[pn], 1);
+#endif
+		}
+	}
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	mutex_init(&st->lock);
+	st->sdev = spi;
+	st->xfer.tx_buf = st->tx;
+	st->xfer.rx_buf = st->rx;
+	st->hysteresis = 1;
+	st->mode = MOD_CONFIG;
+	st->resolution = RES_12;
+	st->fclkin = AD2S1210_DEF_CLKIN;
+	st->fexcit = AD2S1210_DEF_EXCIT;
+	st->sample = pins[0];
+	st->a0 = pins[1];
+	st->a1 = pins[2];
+	st->res0 = pins[3];
+	st->res1 = pins[4];
+
+	st->idev = iio_allocate_device();
+	if (st->idev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->idev->dev.parent = &spi->dev;
+	st->idev->num_interrupt_lines = 0;
+	st->idev->event_attrs = NULL;
+
+	st->idev->attrs = &ad2s1210_attribute_group;
+	st->idev->dev_data = (void *)(st);
+	st->idev->driver_module = THIS_MODULE;
+	st->idev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->idev);
+	if (ret)
+		goto error_free_dev;
+
+	if (spi->max_speed_hz != AD2S1210_DEF_CLKIN)
+		st->fclkin = spi->max_speed_hz;
+	spi->mode = SPI_MODE_3;
+	spi_setup(spi);
+
+	ad2s1210_initial(st);
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->idev);
+error_free_st:
+	kfree(st);
+error_ret:
+	for (--pn; pn >= 0; pn--)
+		gpio_free(pins[pn]);
+	return ret;
+}
+
+static int __devexit ad2s1210_remove(struct spi_device *spi)
+{
+	struct ad2s1210_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->idev);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver ad2s1210_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad2s1210_probe,
+	.remove = __devexit_p(ad2s1210_remove),
+};
+
+static __init int ad2s1210_spi_init(void)
+{
+	return spi_register_driver(&ad2s1210_driver);
+}
+module_init(ad2s1210_spi_init);
+
+static __exit void ad2s1210_spi_exit(void)
+{
+	spi_unregister_driver(&ad2s1210_driver);
+}
+module_exit(ad2s1210_spi_exit);
+
+MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next
  2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
                   ` (35 preceding siblings ...)
  2010-10-26 18:17 ` [PATCH 36/36] staging: iio: resolver: new driver for AD2S1210 devices Mike Frysinger
@ 2010-10-28  1:44 ` Mike Frysinger
  2010-11-01 12:18   ` Jonathan Cameron
  36 siblings, 1 reply; 41+ messages in thread
From: Mike Frysinger @ 2010-10-28  1:44 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel

On Tue, Oct 26, 2010 at 14:17, Mike Frysinger wrote:
> Hopefully there shouldn't be any roadblocks now before pushing to GregKH.
> I'll do so in a day or two if there aren't any issues.

ive bounced it over to him now
-mike

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next
  2010-10-28  1:44 ` [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
@ 2010-11-01 12:18   ` Jonathan Cameron
  2010-11-02  7:40     ` Mike Frysinger
  0 siblings, 1 reply; 41+ messages in thread
From: Jonathan Cameron @ 2010-11-01 12:18 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel

On 10/28/10 02:44, Mike Frysinger wrote:
> On Tue, Oct 26, 2010 at 14:17, Mike Frysinger wrote:
>> Hopefully there shouldn't be any roadblocks now before pushing to GregKH.
>> I'll do so in a day or two if there aren't any issues.
> 
> ive bounced it over to him now
> -mike
> 
I'm guessing this lot *just* missed the merge window.  Despite all Mike's
hard work they were proposed for merging very late so we can't really complain!

Anyhow, this is not necessarily a bad thing as it will mean they sit in
staging-next for a couple of months and we can do a lot of needed cleaning up
before the next merge window.  The trick is probably going to be to synchronize
this chunk of the blackfin tree against staging-next so that we can apply any
accepted/reviewed patches to both trees as we move forward.

I can set up an additional 'in between' tree if that is helpful? (kernel.org hosted)
It would contain whatever we have sent on to Greg KH but based against mainline
rather than linux-next (with no rebasing but with merges to move with the release
candidates as they appear). Hence it would effectively be iio-next though formally
that role will still be handled by Greg's staging-next tree.

Jonathan


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next
  2010-11-01 12:18   ` Jonathan Cameron
@ 2010-11-02  7:40     ` Mike Frysinger
  2010-11-12 18:02       ` Mike Frysinger
  0 siblings, 1 reply; 41+ messages in thread
From: Mike Frysinger @ 2010-11-02  7:40 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel

On Mon, Nov 1, 2010 at 08:18, Jonathan Cameron wrote:
> On 10/28/10 02:44, Mike Frysinger wrote:
>> On Tue, Oct 26, 2010 at 14:17, Mike Frysinger wrote:
>>> Hopefully there shouldn't be any roadblocks now before pushing to GregK=
H.
>>> I'll do so in a day or two if there aren't any issues.
>>
>> ive bounced it over to him now
>>
> I'm guessing this lot *just* missed the merge window. =C2=A0Despite all M=
ike's
> hard work they were proposed for merging very late so we can't really com=
plain!
>
> Anyhow, this is not necessarily a bad thing as it will mean they sit in
> staging-next for a couple of months and we can do a lot of needed cleanin=
g up
> before the next merge window. =C2=A0The trick is probably going to be to =
synchronize
> this chunk of the blackfin tree against staging-next so that we can apply=
 any
> accepted/reviewed patches to both trees as we move forward.
>
> I can set up an additional 'in between' tree if that is helpful? (kernel.=
org hosted)
> It would contain whatever we have sent on to Greg KH but based against ma=
inline
> rather than linux-next (with no rebasing but with merges to move with the=
 release
> candidates as they appear). Hence it would effectively be iio-next though=
 formally
> that role will still be handled by Greg's staging-next tree.

as long as the patches are in staging-next, i think things are fine.
with the bulk of the code merged, what we have left should be a lot
easier to sync/work on.
-mike

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next
  2010-11-02  7:40     ` Mike Frysinger
@ 2010-11-12 18:02       ` Mike Frysinger
  0 siblings, 0 replies; 41+ messages in thread
From: Mike Frysinger @ 2010-11-12 18:02 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel

On Tue, Nov 2, 2010 at 03:40, Mike Frysinger wrote:
> On Mon, Nov 1, 2010 at 08:18, Jonathan Cameron wrote:
>> On 10/28/10 02:44, Mike Frysinger wrote:
>>> On Tue, Oct 26, 2010 at 14:17, Mike Frysinger wrote:
>>>> Hopefully there shouldn't be any roadblocks now before pushing to Greg=
KH.
>>>> I'll do so in a day or two if there aren't any issues.
>>>
>>> ive bounced it over to him now
>>
>> I'm guessing this lot *just* missed the merge window. =C2=A0Despite all =
Mike's
>> hard work they were proposed for merging very late so we can't really co=
mplain!
>>
>> Anyhow, this is not necessarily a bad thing as it will mean they sit in
>> staging-next for a couple of months and we can do a lot of needed cleani=
ng up
>> before the next merge window. =C2=A0The trick is probably going to be to=
 synchronize
>> this chunk of the blackfin tree against staging-next so that we can appl=
y any
>> accepted/reviewed patches to both trees as we move forward.
>>
>> I can set up an additional 'in between' tree if that is helpful? (kernel=
.org hosted)
>> It would contain whatever we have sent on to Greg KH but based against m=
ainline
>> rather than linux-next (with no rebasing but with merges to move with th=
e release
>> candidates as they appear). Hence it would effectively be iio-next thoug=
h formally
>> that role will still be handled by Greg's staging-next tree.
>
> as long as the patches are in staging-next, i think things are fine.
> with the bulk of the code merged, what we have left should be a lot
> easier to sync/work on.

greg has pulled things into his staging-next tree, so hopefully we can
get back to focusing on technical details and not API thrashing now :)
-mike

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2010-11-12 18:02 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-26 18:17 [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
2010-10-26 18:17 ` [PATCH 01/36] staging: iio: adis16350: add missing reference to temp offset Mike Frysinger
2010-10-26 18:17 ` [PATCH 02/36] staging: iio: gyro: make sure grep can find the ADIS16265 support Mike Frysinger
2010-10-26 18:17 ` [PATCH 03/36] staging: iio: add ADI info to TODO Mike Frysinger
2010-10-26 18:17 ` [PATCH 04/36] staging: iio: new adis16201 driver Mike Frysinger
2010-10-26 18:17 ` [PATCH 05/36] staging: iio: new adis16203 driver Mike Frysinger
2010-10-26 18:17 ` [PATCH 06/36] staging: iio: new adis16204 driver Mike Frysinger
2010-10-26 18:17 ` [PATCH 07/36] staging: iio: new ADT7316/7/8 and ADT7516/7/9 driver Mike Frysinger
2010-10-26 18:17 ` [PATCH 08/36] staging: iio: adc: new driver for AD7150/1/6 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 09/36] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 10/36] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 11/36] staging: iio: adc: new driver for AD7298 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 12/36] staging: iio: adc: new driver for AD7314 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 13/36] staging: iio: adc: new driver for AD7745/6/7 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 14/36] staging: iio: adc: new driver for AD7816 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 15/36] staging: iio: adc: new driver for ADT75 temperature sensors Mike Frysinger
2010-10-26 18:17 ` [PATCH 16/36] staging: iio: adc: new driver for ADT7310 " Mike Frysinger
2010-10-26 18:17 ` [PATCH 17/36] staging: iio: adc: new driver for ADT7410 " Mike Frysinger
2010-10-26 18:17 ` [PATCH 18/36] staging: iio: gyro: new driver for ADIS16251 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 19/36] staging: iio: gyro: new driver for ADIS16060 digital output gyros Mike Frysinger
2010-10-26 18:17 ` [PATCH 20/36] staging: iio: gyro: new driver for ADIS16080 " Mike Frysinger
2010-10-26 18:17 ` [PATCH 21/36] staging: iio: gyro: new driver for ADIS16130 " Mike Frysinger
2010-10-26 18:17 ` [PATCH 22/36] staging: iio: dac: new driver for AD5624R devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 23/36] staging: iio: dds: new driver for AD5930/2 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 24/36] staging: iio: dds: new driver for AD9832/3/4/5 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 25/36] staging: iio: dds: new driver for AD9850/1 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 26/36] staging: iio: dds: new driver for AD9852/4 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 27/36] staging: iio: dds: new driver for AD9910 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 28/36] staging: iio: dds: new driver for AD9951 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 29/36] staging: iio: meter: new driver for ADE7753/6 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 30/36] staging: iio: meter: new driver for ADE7754 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 31/36] staging: iio: meter: new driver for ADE7758 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 32/36] staging: iio: meter: new driver for ADE7759 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 33/36] staging: iio: meter: new driver for ADE7854/58/68/78 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 34/36] staging: iio: resolver: new driver for AD2S90 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 35/36] staging: iio: resolver: new driver for AD2S1200/1205 devices Mike Frysinger
2010-10-26 18:17 ` [PATCH 36/36] staging: iio: resolver: new driver for AD2S1210 devices Mike Frysinger
2010-10-28  1:44 ` [Device-drivers-devel] [PATCH 00/36 v2] staging: iio: ADI drivers for staging-next Mike Frysinger
2010-11-01 12:18   ` Jonathan Cameron
2010-11-02  7:40     ` Mike Frysinger
2010-11-12 18:02       ` Mike Frysinger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.