Browse Source

Merge tag 'staging-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging/IIO updates from Greg KH:
 "Here's the "big" staging/iio pull request for 4.10-rc1.

  Not as big as 4.9 was, but still just over a thousand changes. We
  almost broke even of lines added vs. removed, as the slicoss driver
  was removed (got a "clean" driver for the same hardware through the
  netdev tree), and some iio drivers were also dropped, but I think we
  ended up adding a few thousand lines to the source tree in the end.
  Other than that it's a lot of minor fixes all over the place, nothing
  major stands out at all.

  All of these have been in linux-next for a while. There will be a
  merge conflict with Al's vfs tree in the lustre code, but the
  resolution for that should be pretty simple, that too has been in
  linux-next"

* tag 'staging-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1002 commits)
  staging: comedi: comedidev.h: Document usage of 'detach' handler
  staging: fsl-mc: remove unnecessary info prints from bus driver
  staging: fsl-mc: add sysfs ABI doc
  staging/lustre/o2iblnd: Fix misspelled attemps->attempts
  staging/lustre/o2iblnd: Fix misspelling intialized->intialized
  staging/lustre: Convert all bare unsigned to unsigned int
  staging/lustre/socklnd: Fix whitespace problem
  staging/lustre/o2iblnd: Add missing space
  staging/lustre/lnetselftest: Fix potential integer overflow
  staging: greybus: audio_module: remove redundant OOM message
  staging: dgnc: Fix lines longer than 80 characters
  staging: dgnc: fix blank line after '{' warnings.
  staging/android: remove Sync Framework tasks from TODO
  staging/lustre/osc: Revert erroneous list_for_each_entry_safe use
  staging: slicoss: remove the staging driver
  staging: lustre: libcfs: remove lnet upcall code
  staging: lustre: remove set but unused variables
  staging: lustre: osc: set lock data for readahead lock
  staging: lustre: import: don't reconnect during connect interpret
  staging: lustre: clio: remove mtime check in vvp_io_fault_start()
  ...
Linus Torvalds 8 years ago
parent
commit
72cca7baf4
100 changed files with 12583 additions and 1122 deletions
  1. 21 0
      Documentation/ABI/testing/sysfs-bus-fsl-mc
  2. 18 0
      Documentation/ABI/testing/sysfs-bus-iio
  3. 36 0
      Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
  4. 125 0
      Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8
  5. 18 0
      Documentation/ABI/testing/sysfs-bus-iio-cros-ec
  6. 8 0
      Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
  7. 19 0
      Documentation/ABI/testing/sysfs-bus-iio-light-isl29018
  8. 7 7
      Documentation/ABI/testing/sysfs-bus-iio-light-tsl2583
  9. 8 0
      Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
  10. 7 0
      Documentation/devicetree/bindings/i2c/trivial-devices.txt
  11. 54 0
      Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
  12. 83 0
      Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
  13. 2 0
      Documentation/devicetree/bindings/iio/adc/ti-adc161s626.txt
  14. 41 0
      Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
  15. 35 0
      Documentation/devicetree/bindings/iio/dac/mcp4725.txt
  16. 46 0
      Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
  17. 22 0
      Documentation/devicetree/bindings/iio/humidity/hts221.txt
  18. 28 0
      Documentation/devicetree/bindings/iio/light/isl29018.txt
  19. 26 0
      Documentation/devicetree/bindings/iio/light/tsl2583.txt
  20. 30 0
      Documentation/devicetree/bindings/iio/potentiostat/lmp91000.txt
  21. 1 0
      Documentation/devicetree/bindings/iio/st-sensors.txt
  22. 4 0
      Documentation/devicetree/bindings/vendor-prefixes.txt
  23. 38 14
      MAINTAINERS
  24. 2 0
      drivers/iio/Kconfig
  25. 2 0
      drivers/iio/Makefile
  26. 44 1
      drivers/iio/accel/Kconfig
  27. 5 0
      drivers/iio/accel/Makefile
  28. 183 0
      drivers/iio/accel/da280.c
  29. 305 0
      drivers/iio/accel/da311.c
  30. 266 0
      drivers/iio/accel/dmard10.c
  31. 1 1
      drivers/iio/accel/mma7660.c
  32. 43 36
      drivers/iio/accel/mma8452.c
  33. 1576 0
      drivers/iio/accel/sca3000.c
  34. 1 0
      drivers/iio/accel/st_accel.h
  35. 244 361
      drivers/iio/accel/st_accel_core.c
  36. 5 0
      drivers/iio/accel/st_accel_i2c.c
  37. 1 0
      drivers/iio/accel/st_accel_spi.c
  38. 46 0
      drivers/iio/adc/Kconfig
  39. 4 0
      drivers/iio/adc/Makefile
  40. 330 0
      drivers/iio/adc/ad7766.c
  41. 28 0
      drivers/iio/adc/at91_adc.c
  42. 422 0
      drivers/iio/adc/envelope-detector.c
  43. 4 13
      drivers/iio/adc/max1027.c
  44. 303 0
      drivers/iio/adc/stm32-adc-core.c
  45. 52 0
      drivers/iio/adc/stm32-adc-core.h
  46. 518 0
      drivers/iio/adc/stm32-adc.c
  47. 87 19
      drivers/iio/adc/ti-adc0832.c
  48. 44 11
      drivers/iio/adc/ti-adc161s626.c
  49. 145 3
      drivers/iio/adc/ti_am335x_adc.c
  50. 1 0
      drivers/iio/common/Kconfig
  51. 1 0
      drivers/iio/common/Makefile
  52. 22 0
      drivers/iio/common/cros_ec_sensors/Kconfig
  53. 6 0
      drivers/iio/common/cros_ec_sensors/Makefile
  54. 322 0
      drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
  55. 450 0
      drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
  56. 175 0
      drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
  57. 4 1
      drivers/iio/common/hid-sensors/hid-sensor-attributes.c
  58. 593 0
      drivers/iio/counter/104-quad-8.c
  59. 24 0
      drivers/iio/counter/Kconfig
  60. 7 0
      drivers/iio/counter/Makefile
  61. 10 0
      drivers/iio/dac/Kconfig
  62. 1 0
      drivers/iio/dac/Makefile
  63. 1 1
      drivers/iio/dac/ad5592r.c
  64. 266 0
      drivers/iio/dac/dpot-dac.c
  65. 160 16
      drivers/iio/dac/mcp4725.c
  66. 18 0
      drivers/iio/gyro/Kconfig
  67. 5 0
      drivers/iio/gyro/Makefile
  68. 1306 0
      drivers/iio/gyro/mpu3050-core.c
  69. 124 0
      drivers/iio/gyro/mpu3050-i2c.c
  70. 96 0
      drivers/iio/gyro/mpu3050.h
  71. 66 139
      drivers/iio/gyro/st_gyro_core.c
  72. 24 0
      drivers/iio/humidity/Kconfig
  73. 7 0
      drivers/iio/humidity/Makefile
  74. 128 2
      drivers/iio/humidity/hdc100x.c
  75. 73 0
      drivers/iio/humidity/hts221.h
  76. 168 0
      drivers/iio/humidity/hts221_buffer.c
  77. 687 0
      drivers/iio/humidity/hts221_core.c
  78. 110 0
      drivers/iio/humidity/hts221_i2c.c
  79. 125 0
      drivers/iio/humidity/hts221_spi.c
  80. 10 1
      drivers/iio/humidity/si7020.c
  81. 2 1
      drivers/iio/imu/bmi160/bmi160_core.c
  82. 1 1
      drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
  83. 3 4
      drivers/iio/industrialio-buffer.c
  84. 228 33
      drivers/iio/industrialio-core.c
  85. 21 0
      drivers/iio/industrialio-trigger.c
  86. 120 3
      drivers/iio/inkern.c
  87. 19 0
      drivers/iio/light/Kconfig
  88. 2 0
      drivers/iio/light/Makefile
  89. 86 73
      drivers/iio/light/isl29018.c
  90. 71 40
      drivers/iio/light/ltr501.c
  91. 3 2
      drivers/iio/light/max44000.c
  92. 913 0
      drivers/iio/light/tsl2583.c
  93. 4 4
      drivers/iio/magnetometer/ak8974.c
  94. 9 7
      drivers/iio/magnetometer/ak8975.c
  95. 112 35
      drivers/iio/magnetometer/hid-sensor-magn-3d.c
  96. 125 251
      drivers/iio/magnetometer/st_magn_core.c
  97. 62 42
      drivers/iio/potentiometer/mcp4531.c
  98. 22 0
      drivers/iio/potentiostat/Kconfig
  99. 6 0
      drivers/iio/potentiostat/Makefile
  100. 446 0
      drivers/iio/potentiostat/lmp91000.c

+ 21 - 0
Documentation/ABI/testing/sysfs-bus-fsl-mc

@@ -0,0 +1,21 @@
+What:		/sys/bus/fsl-mc/drivers/.../bind
+Date:		December 2016
+Contact:	stuart.yoder@nxp.com
+Description:
+		Writing a device location to this file will cause
+		the driver to attempt to bind to the device found at
+		this location. The format for the location is Object.Id
+		and is the same as found in /sys/bus/fsl-mc/devices/.
+                For example:
+		# echo dpni.2 > /sys/bus/fsl-mc/drivers/fsl_dpaa2_eth/bind
+
+What:		/sys/bus/fsl-mc/drivers/.../unbind
+Date:		December 2016
+Contact:	stuart.yoder@nxp.com
+Description:
+		Writing a device location to this file will cause the
+		driver to attempt to unbind from the device found at
+		this location. The format for the location is Object.Id
+		and is the same as found in /sys/bus/fsl-mc/devices/.
+                For example:
+		# echo dpni.2 > /sys/bus/fsl-mc/drivers/fsl_dpaa2_eth/unbind

+ 18 - 0
Documentation/ABI/testing/sysfs-bus-iio

@@ -329,6 +329,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_illuminance_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_scale
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -1579,3 +1580,20 @@ Contact:	linux-iio@vger.kernel.org
 Description:
 		Raw (unscaled no offset etc.) electric conductivity reading that
 		can be processed to siemens per meter.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_raw
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw counter device counts from channel Y. For quadrature
+		counters, multiplication by an available [Y]_scale results in
+		the counts of a single quadrature signal phase from channel Y.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_indexY_raw
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw counter device index value from channel Y. This attribute
+		provides an absolute positional reference (e.g. a pulse once per
+		revolution) which may be used to home positional systems as
+		required.

+ 36 - 0
Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector

@@ -0,0 +1,36 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_altvoltageY_invert
+Date:		October 2016
+KernelVersion:	4.9
+Contact:	Peter Rosin <peda@axentia.se>
+Description:
+		The DAC is used to find the peak level of an alternating
+		voltage input signal by a binary search using the output
+		of a comparator wired to an interrupt pin. Like so:
+		                           _
+		                          | \
+		     input +------>-------|+ \
+		                          |   \
+		            .-------.     |    }---.
+		            |       |     |   /    |
+		            |    dac|-->--|- /     |
+		            |       |     |_/      |
+		            |       |              |
+		            |       |              |
+		            |    irq|------<-------'
+		            |       |
+		            '-------'
+		The boolean invert attribute (0/1) should be set when the
+		input signal is centered around the maximum value of the
+		dac instead of zero. The envelope detector will search
+		from below in this case and will also invert the result.
+		The edge/level of the interrupt is also switched to its
+		opposite value.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_altvoltageY_compare_interval
+Date:		October 2016
+KernelVersion:	4.9
+Contact:	Peter Rosin <peda@axentia.se>
+Description:
+		Number of milliseconds to wait for the comparator in each
+		step of the binary search for the input peak level. Needs
+		to relate to the frequency of the input signal.

+ 125 - 0
Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8

@@ -0,0 +1,125 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Discrete set of available values for the respective counter
+		configuration are listed in this file.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_count_direction
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Read-only attribute that indicates whether the counter for
+		channel Y is counting up or down.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_count_mode
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Count mode for channel Y. Four count modes are available:
+		normal, range limit, non-recycle, and modulo-n. The preset value
+		for channel Y is used by the count mode where required.
+
+		Normal:
+			Counting is continuous in either direction.
+
+		Range Limit:
+			An upper or lower limit is set, mimicking limit switches
+			in the mechanical counterpart. The upper limit is set to
+			the preset value, while the lower limit is set to 0. The
+			counter freezes at count = preset when counting up, and
+			at count = 0 when counting down. At either of these
+			limits, the counting is resumed only when the count
+			direction is reversed.
+
+		Non-recycle:
+			Counter is disabled whenever a 24-bit count overflow or
+			underflow takes place. The counter is re-enabled when a
+			new count value is loaded to the counter via a preset
+			operation or write to raw.
+
+		Modulo-N:
+			A count boundary is set between 0 and the preset value.
+			The counter is reset to 0 at count = preset when
+			counting up, while the counter is set to the preset
+			value at count = 0 when counting down; the counter does
+			not freeze at the bundary points, but counts
+			continuously throughout.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_noise_error
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Read-only attribute that indicates whether excessive noise is
+		present at the channel Y count inputs in quadrature clock mode;
+		irrelevant in non-quadrature clock mode.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_preset
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		If the counter device supports preset registers, the preset
+		count for channel Y is provided by this attribute.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Configure channel Y counter for non-quadrature or quadrature
+		clock mode. Selecting non-quadrature clock mode will disable
+		synchronous load mode. In quadrature clock mode, the channel Y
+		scale attribute selects the encoder phase division (scale of 1
+		selects full-cycle, scale of 0.5 selects half-cycle, scale of
+		0.25 selects quarter-cycle) processed by the channel Y counter.
+
+		Non-quadrature:
+			The filter and decoder circuit are bypassed. Encoder A
+			input serves as the count input and B as the UP/DOWN
+			direction control input, with B = 1 selecting UP Count
+			mode and B = 0 selecting Down Count mode.
+
+		Quadrature:
+			Encoder A and B inputs are digitally filtered and
+			decoded for UP/DN clock.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Whether to set channel Y counter with channel Y preset value
+		when channel Y index input is active, or continuously count.
+		Valid attribute values are boolean.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Active level of channel Y index input; irrelevant in
+		non-synchronous load mode.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode
+KernelVersion:	4.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Configure channel Y counter for non-synchronous or synchronous
+		load mode. Synchronous load mode cannot be selected in
+		non-quadrature clock mode.
+
+		Non-synchronous:
+			A logic low level is the active level at this index
+			input. The index function (as enabled via
+			set_to_preset_on_index) is performed directly on the
+			active level of the index input.
+
+		Synchronous:
+			Intended for interfacing with encoder Index output in
+			quadrature clock mode. The active level is configured
+			via index_polarity. The index function (as enabled via
+			set_to_preset_on_index) is performed synchronously with
+			the quadrature clock on the active level of the index
+			input.

+ 18 - 0
Documentation/ABI/testing/sysfs-bus-iio-cros-ec

@@ -0,0 +1,18 @@
+What:		/sys/bus/iio/devices/iio:deviceX/calibrate
+Date:		July 2015
+KernelVersion:	4.7
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Writing '1' will perform a FOC (Fast Online Calibration). The
+                corresponding calibration offsets can be read from *_calibbias
+                entries.
+
+What:		/sys/bus/iio/devices/iio:deviceX/location
+Date:		July 2015
+KernelVersion:	4.7
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This attribute returns a string with the physical location where
+                the motion sensor is placed. For example, in a laptop a motion
+                sensor can be located on the base or on the lid. Current valid
+		values are 'base' and 'lid'.

+ 8 - 0
Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac

@@ -0,0 +1,8 @@
+What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_raw_available
+Date:		October 2016
+KernelVersion:	4.9
+Contact:	Peter Rosin <peda@axentia.se>
+Description:
+		The range of available values represented as the minimum value,
+		the step and the maximum value, all enclosed in square brackets.
+		Example: [0 1 256]

+ 19 - 0
Documentation/ABI/testing/sysfs-bus-iio-light-isl29018

@@ -0,0 +1,19 @@
+What:		/sys/bus/iio/devices/iio:deviceX/proximity_on_chip_ambient_infrared_suppression
+Date:		January 2011
+KernelVersion:	2.6.37
+Contact:	linux-iio@vger.kernel.org
+Description:
+		From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
+		infrared suppression:
+
+		Scheme 0, makes full n (4, 8, 12, 16) bits (unsigned) proximity
+		detection. The range of Scheme 0 proximity count is from 0 to
+		2^n. Logic 1 of this bit, Scheme 1, makes n-1 (3, 7, 11, 15)
+		bits (2's complementary) proximity_less_ambient detection. The
+		range of Scheme 1 proximity count is from -2^(n-1) to 2^(n-1).
+		The sign bit is extended for resolutions less than 16. While
+		Scheme 0 has wider dynamic range, Scheme 1 proximity detection
+		is less affected by the ambient IR noise variation.
+
+		0 Sensing IR from LED and ambient
+		1 Sensing IR from LED with ambient IR rejection

+ 7 - 7
drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 → Documentation/ABI/testing/sysfs-bus-iio-light-tsl2583

@@ -1,18 +1,18 @@
-What:		/sys/bus/iio/devices/device[n]/lux_table
+What:		/sys/bus/iio/devices/device[n]/in_illuminance_calibrate
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
-		This property gets/sets the table of coefficients
-		used in calculating illuminance in lux.
+		This property causes an internal calibration of the als gain trim
+		value which is later used in calculating illuminance in lux.
 
-What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
+What:		/sys/bus/iio/devices/device[n]/in_illuminance_lux_table
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
-		This property causes an internal calibration of the als gain trim
-		value which is later used in calculating illuminance in lux.
+		This property gets/sets the table of coefficients
+		used in calculating illuminance in lux.
 
-What:		/sys/bus/iio/devices/device[n]/illuminance0_input_target
+What:		/sys/bus/iio/devices/device[n]/in_illuminance_input_target
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:

+ 8 - 0
Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531

@@ -0,0 +1,8 @@
+What:		/sys/bus/iio/devices/iio:deviceX/out_resistance_raw_available
+Date:		October 2016
+KernelVersion:	4.9
+Contact:	Peter Rosin <peda@axentia.se>
+Description:
+		The range of available values represented as the minimum value,
+		the step and the maximum value, all enclosed in square brackets.
+		Example: [0 1 256]

+ 7 - 0
Documentation/devicetree/bindings/i2c/trivial-devices.txt

@@ -39,11 +39,13 @@ dallas,ds75		Digital Thermometer and Thermostat
 dlg,da9053		DA9053: flexible system level PMIC with multicore support
 dlg,da9063		DA9063: system PMIC for quad-core application processors
 domintech,dmard09	DMARD09: 3-axis Accelerometer
+domintech,dmard10	DMARD10: 3-axis Accelerometer
 epson,rx8010		I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 epson,rx8025		High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
 epson,rx8581		I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 fsl,mag3110		MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
 fsl,mc13892		MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
+fsl,mma7660		MMA7660FC: 3-Axis Orientation/Motion Detection Sensor
 fsl,mma8450		MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
 fsl,mpl3115		MPL3115: Absolute Digital Pressure Sensor
 fsl,mpr121		MPR121: Proximity Capacitive Touch Sensor Controller
@@ -57,6 +59,7 @@ maxim,max1237		Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
 maxim,max6625		9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
 mc,rv3029c2		Real Time Clock Module with I2C-Bus
 mcube,mc3230		mCube 3-axis 8-bit digital accelerometer
+memsic,mxc6225		MEMSIC 2-axis 8-bit digital accelerometer
 microchip,mcp4531-502	Microchip 7-bit Single I2C Digital Potentiometer (5k)
 microchip,mcp4531-103	Microchip 7-bit Single I2C Digital Potentiometer (10k)
 microchip,mcp4531-503	Microchip 7-bit Single I2C Digital Potentiometer (50k)
@@ -121,6 +124,9 @@ microchip,mcp4662-502	Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
 microchip,mcp4662-103	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
 microchip,mcp4662-503	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
 microchip,mcp4662-104	Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
+miramems,da226		MiraMEMS DA226 2-axis 14-bit digital accelerometer
+miramems,da280		MiraMEMS DA280 3-axis 14-bit digital accelerometer
+miramems,da311		MiraMEMS DA311 3-axis 12-bit digital accelerometer
 national,lm63		Temperature sensor with integrated fan control
 national,lm75		I2C TEMP SENSOR
 national,lm80		Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
@@ -146,6 +152,7 @@ ricoh,rv5c387a		I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 samsung,24ad0xd1	S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
 sgx,vz89x		SGX Sensortech VZ89X Sensors
 sii,s35390a		2-wire CMOS real-time clock
+silabs,si7020		Relative Humidity and Temperature Sensors
 skyworks,sky81452	Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
 st,24c256		i2c serial eeprom  (24cxx)
 st,m41t00		Serial real-time clock (RTC)

+ 54 - 0
Documentation/devicetree/bindings/iio/adc/envelope-detector.txt

@@ -0,0 +1,54 @@
+Bindings for ADC envelope detector using a DAC and a comparator
+
+The DAC is used to find the peak level of an alternating voltage input
+signal by a binary search using the output of a comparator wired to
+an interrupt pin. Like so:
+                          _
+                         | \
+    input +------>-------|+ \
+                         |   \
+           .-------.     |    }---.
+           |       |     |   /    |
+           |    dac|-->--|- /     |
+           |       |     |_/      |
+           |       |              |
+           |       |              |
+           |    irq|------<-------'
+           |       |
+           '-------'
+
+Required properties:
+- compatible: Should be "axentia,tse850-envelope-detector"
+- io-channels: Channel node of the dac to be used for comparator input.
+- io-channel-names: Should be "dac".
+- interrupt specification for one client interrupt,
+  see ../../interrupt-controller/interrupts.txt for details.
+- interrupt-names: Should be "comp".
+
+Example:
+
+	&i2c {
+		dpot: mcp4651-104@28 {
+			compatible = "microchip,mcp4651-104";
+			reg = <0x28>;
+			#io-channel-cells = <1>;
+		};
+	};
+
+	dac: dac {
+		compatible = "dpot-dac";
+		vref-supply = <&reg_3v3>;
+		io-channels = <&dpot 0>;
+		io-channel-names = "dpot";
+		#io-channel-cells = <1>;
+	};
+
+	envelope-detector {
+		compatible = "axentia,tse850-envelope-detector";
+		io-channels = <&dac 0>;
+		io-channel-names = "dac";
+
+		interrupt-parent = <&gpio>;
+		interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+		interrupt-names = "comp";
+	};

+ 83 - 0
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt

@@ -0,0 +1,83 @@
+STMicroelectronics STM32 ADC device driver
+
+STM32 ADC is a successive approximation analog-to-digital converter.
+It has several multiplexed input channels. Conversions can be performed
+in single, continuous, scan or discontinuous mode. Result of the ADC is
+stored in a left-aligned or right-aligned 32-bit data register.
+Conversions can be launched in software or using hardware triggers.
+
+The analog watchdog feature allows the application to detect if the input
+voltage goes beyond the user-defined, higher or lower thresholds.
+
+Each STM32 ADC block can have up to 3 ADC instances.
+
+Each instance supports two contexts to manage conversions, each one has its
+own configurable sequence and trigger:
+- regular conversion can be done in sequence, running in background
+- injected conversions have higher priority, and so have the ability to
+  interrupt regular conversion sequence (either triggered in SW or HW).
+  Regular sequence is resumed, in case it has been interrupted.
+
+Contents of a stm32 adc root node:
+-----------------------------------
+Required properties:
+- compatible: Should be "st,stm32f4-adc-core".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC block.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- interrupt-controller: Identifies the controller node as interrupt-parent
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #interrupt-cells = <1>;
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- A pinctrl state named "default" for each ADC channel may be defined to set
+  inX ADC pins in mode of operation for analog input on external pin.
+
+Contents of a stm32 adc child node:
+-----------------------------------
+An ADC block node should contain at least one subnode, representing an
+ADC instance available on the machine.
+
+Required properties:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- clocks: Input clock private to this ADC instance.
+- interrupt-parent: Phandle to the parent interrupt controller.
+- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or
+  2 for adc@200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+  It can have up to 16 channels, numbered from 0 to 15 (resp. for in0..in15).
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+  Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Example:
+	adc: adc@40012000 {
+		compatible = "st,stm32f4-adc-core";
+		reg = <0x40012000 0x400>;
+		interrupts = <18>;
+		clocks = <&rcc 0 168>;
+		clock-names = "adc";
+		vref-supply = <&reg_vref>;
+		interrupt-controller;
+		pinctrl-names = "default";
+		pinctrl-0 = <&adc3_in8_pin>;
+
+		#interrupt-cells = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		adc@0 {
+			compatible = "st,stm32f4-adc";
+			#io-channel-cells = <1>;
+			reg = <0x0>;
+			clocks = <&rcc 0 168>;
+			interrupt-parent = <&adc>;
+			interrupts = <0>;
+			st,adc-channels = <8>;
+		};
+		...
+		other adc child nodes follow...
+	};

+ 2 - 0
Documentation/devicetree/bindings/iio/adc/ti-adc161s626.txt

@@ -3,6 +3,7 @@
 Required properties:
  - compatible: Should be "ti,adc141s626" or "ti,adc161s626"
  - reg: spi chip select number for the device
+ - vdda-supply: supply voltage to VDDA pin
 
 Recommended properties:
  - spi-max-frequency: Definition as per
@@ -11,6 +12,7 @@ Recommended properties:
 Example:
 adc@0 {
 	compatible = "ti,adc161s626";
+	vdda-supply = <&vdda_fixed>;
 	reg = <0>;
 	spi-max-frequency = <4300000>;
 };

+ 41 - 0
Documentation/devicetree/bindings/iio/dac/dpot-dac.txt

@@ -0,0 +1,41 @@
+Bindings for DAC emulation using a digital potentiometer
+
+It is assumed that the dpot is used as a voltage divider between the
+current dpot wiper setting and the maximum resistance of the dpot. The
+divided voltage is provided by a vref regulator.
+
+                  .------.
+   .-----------.  |      |
+   | vref      |--'    .---.
+   | regulator |--.    |   |
+   '-----------'  |    | d |
+                  |    | p |
+                  |    | o |  wiper
+                  |    | t |<---------+
+                  |    |   |
+                  |    '---'       dac output voltage
+                  |      |
+                  '------+------------+
+
+Required properties:
+- compatible: Should be "dpot-dac"
+- vref-supply: The regulator supplying the voltage divider.
+- io-channels: Channel node of the dpot to be used for the voltage division.
+- io-channel-names: Should be "dpot".
+
+Example:
+
+	&i2c {
+		dpot: mcp4651-503@28 {
+			compatible = "microchip,mcp4651-503";
+			reg = <0x28>;
+			#io-channel-cells = <1>;
+		};
+	};
+
+	dac {
+		compatible = "dpot-dac";
+		vref-supply = <&reg_3v3>;
+		io-channels = <&dpot 0>;
+		io-channel-names = "dpot";
+	};

+ 35 - 0
Documentation/devicetree/bindings/iio/dac/mcp4725.txt

@@ -0,0 +1,35 @@
+Microchip mcp4725 and mcp4726 DAC device driver
+
+Required properties:
+	- compatible: Must be "microchip,mcp4725" or "microchip,mcp4726"
+	- reg: Should contain the DAC I2C address
+	- vdd-supply: Phandle to the Vdd power supply. This supply is used as a
+	  voltage reference on mcp4725. It is used as a voltage reference on
+	  mcp4726 if there is no vref-supply specified.
+
+Optional properties (valid only for mcp4726):
+	- vref-supply: Optional phandle to the Vref power supply. Vref pin is
+	  used as a voltage reference when this supply is specified.
+	- microchip,vref-buffered: Boolean to enable buffering of the external
+	  Vref pin. This boolean is not valid without the vref-supply. Quoting
+	  the datasheet: This is offered in cases where the reference voltage
+	  does not have the current capability not to drop its voltage when
+	  connected to the internal resistor ladder circuit.
+
+Examples:
+
+	/* simple mcp4725 */
+	mcp4725@60 {
+		compatible = "microchip,mcp4725";
+		reg = <0x60>;
+		vdd-supply = <&vdac_vdd>;
+	};
+
+	/* mcp4726 with the buffered external reference voltage */
+	mcp4726@60 {
+		compatible = "microchip,mcp4726";
+		reg = <0x60>;
+		vdd-supply = <&vdac_vdd>;
+		vref-supply = <&vdac_vref>;
+		microchip,vref-buffered;
+	};

+ 46 - 0
Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt

@@ -0,0 +1,46 @@
+Invensense MPU-3050 Gyroscope device tree bindings
+
+Required properties:
+  - compatible : should be "invensense,mpu3050"
+  - reg : the I2C address of the sensor
+
+Optional properties:
+  - interrupt-parent : should be the phandle for the interrupt controller
+  - interrupts : interrupt mapping for the trigger interrupt from the
+    internal oscillator. The following IRQ modes are supported:
+    IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
+    IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
+    for the desired interrupt type.
+  - vdd-supply : supply regulator for the main power voltage.
+  - vlogic-supply : supply regulator for the signal voltage.
+  - mount-matrix : see iio/mount-matrix.txt
+
+Optional subnodes:
+  - The MPU-3050 will pass through and forward the I2C signals from the
+    incoming I2C bus, alternatively drive traffic to a slave device (usually
+    an accelerometer) on its own initiative. Therefore is supports a subnode
+    i2c gate node. For details see: i2c/i2c-gate.txt
+
+Example:
+
+mpu3050@68 {
+	compatible = "invensense,mpu3050";
+	reg = <0x68>;
+	interrupt-parent = <&foo>;
+	interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
+	vdd-supply = <&bar>;
+	vlogic-supply = <&baz>;
+
+	/* External I2C interface */
+	i2c-gate {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		fnord@18 {
+			compatible = "fnord";
+			reg = <0x18>;
+			interrupt-parent = <&foo>;
+			interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
+		};
+	};
+};

+ 22 - 0
Documentation/devicetree/bindings/iio/humidity/hts221.txt

@@ -0,0 +1,22 @@
+* HTS221 STM humidity + temperature sensor
+
+Required properties:
+- compatible: should be "st,hts221"
+- reg: i2c address of the sensor / spi cs line
+
+Optional properties:
+- interrupt-parent: should be the phandle for the interrupt controller
+- interrupts: interrupt mapping for IRQ. It should be configured with
+  flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
+
+  Refer to interrupt-controller/interrupts.txt for generic interrupt
+  client node bindings.
+
+Example:
+
+hts221@5f {
+	compatible = "st,hts221";
+	reg = <0x5f>;
+	interrupt-parent = <&gpio0>;
+	interrupts = <0 IRQ_TYPE_EDGE_RISING>;
+};

+ 28 - 0
Documentation/devicetree/bindings/iio/light/isl29018.txt

@@ -0,0 +1,28 @@
+* ISL 29018/29023/29035 I2C ALS, Proximity, and Infrared sensor
+
+Required properties:
+
+  - compatible: Should be one of
+		"isil,isl29018"
+		"isil,isl29023"
+		"isil,isl29035"
+  - reg: the I2C address of the device
+
+Optional properties:
+
+  - interrupt-parent: should be the phandle for the interrupt controller
+  - interrupts: the sole interrupt generated by the device
+
+  Refer to interrupt-controller/interrupts.txt for generic interrupt client
+  node bindings.
+
+  - vcc-supply: phandle to the regulator that provides power to the sensor.
+
+Example:
+
+isl29018@44 {
+	compatible = "isil,isl29018";
+	reg = <0x44>;
+	interrupt-parent = <&gpio>;
+	interrupts = <TEGRA_GPIO(Z, 2) IRQ_TYPE_LEVEL_HIGH>;
+};

+ 26 - 0
Documentation/devicetree/bindings/iio/light/tsl2583.txt

@@ -0,0 +1,26 @@
+* TAOS TSL 2580/2581/2583 ALS sensor
+
+Required properties:
+
+  - compatible: Should be one of
+		"amstaos,tsl2580"
+		"amstaos,tsl2581"
+		"amstaos,tsl2583"
+  - reg: the I2C address of the device
+
+Optional properties:
+
+  - interrupt-parent: should be the phandle for the interrupt controller
+  - interrupts: the sole interrupt generated by the device
+
+  Refer to interrupt-controller/interrupts.txt for generic interrupt client
+  node bindings.
+
+  - vcc-supply: phandle to the regulator that provides power to the sensor.
+
+Example:
+
+tsl2581@29 {
+	compatible = "amstaos,tsl2581";
+	reg = <0x29>;
+};

+ 30 - 0
Documentation/devicetree/bindings/iio/potentiostat/lmp91000.txt

@@ -0,0 +1,30 @@
+* Texas Instruments LMP91000 potentiostat
+
+http://www.ti.com/lit/ds/symlink/lmp91000.pdf
+
+Required properties:
+
+  - compatible: should be "ti,lmp91000"
+  - reg: the I2C address of the device
+  - io-channels: the phandle of the iio provider
+
+  - ti,external-tia-resistor: if the property ti,tia-gain-ohm is not defined this
+    needs to be set to signal that an external resistor value is being used.
+
+Optional properties:
+
+  - ti,tia-gain-ohm: ohm value of the internal resistor for the transimpedance
+    amplifier. Must be 2750, 3500, 7000, 14000, 35000, 120000, or 350000 ohms.
+
+  - ti,rload-ohm: ohm value of the internal resistor load applied to the gas
+    sensor. Must be 10, 33, 50, or 100 (default) ohms.
+
+Example:
+
+lmp91000@48 {
+	compatible = "ti,lmp91000";
+	reg = <0x48>;
+	ti,tia-gain-ohm = <7500>;
+	ti,rload = <100>;
+	io-channels = <&adc>;
+};

+ 1 - 0
Documentation/devicetree/bindings/iio/st-sensors.txt

@@ -42,6 +42,7 @@ Accelerometers:
 - st,lsm303agr-accel
 - st,lis2dh12-accel
 - st,h3lis331dl-accel
+- st,lng2dm-accel
 
 Gyroscopes:
 - st,l3g4200d-gyro

+ 4 - 0
Documentation/devicetree/bindings/vendor-prefixes.txt

@@ -39,6 +39,7 @@ auo	AU Optronics Corporation
 auvidea Auvidea GmbH
 avago	Avago Technologies
 avic	Shanghai AVIC Optoelectronics Co., Ltd.
+axentia	Axentia Technologies AB
 axis	Axis Communications AB
 boe	BOE Technology Group Co., Ltd.
 bosch	Bosch Sensortec GmbH
@@ -160,16 +161,19 @@ lltc	Linear Technology Corporation
 lsi	LSI Corp. (LSI Logic)
 marvell	Marvell Technology Group Ltd.
 maxim	Maxim Integrated Products
+mcube	mCube
 meas	Measurement Specialties
 mediatek	MediaTek Inc.
 melexis	Melexis N.V.
 melfas	MELFAS Inc.
+memsic	MEMSIC Inc.
 merrii	Merrii Technology Co., Ltd.
 micrel	Micrel Inc.
 microchip	Microchip Technology Inc.
 microcrystal	Micro Crystal AG
 micron	Micron Technology Inc.
 minix	MINIX Technology Ltd.
+miramems	MiraMEMS Sensing Technology Co., Ltd.
 mitsubishi	Mitsubishi Electric Corporation
 mosaixtech	Mosaix Technologies, Inc.
 moxa	Moxa

+ 38 - 14
MAINTAINERS

@@ -260,6 +260,12 @@ L:	linux-gpio@vger.kernel.org
 S:	Maintained
 F:	drivers/gpio/gpio-104-idio-16.c
 
+ACCES 104-QUAD-8 IIO DRIVER
+M:	William Breathitt Gray <vilhelm.gray@gmail.com>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	drivers/iio/counter/104-quad-8.c
+
 ACENIC DRIVER
 M:	Jes Sorensen <jes@trained-monkey.org>
 L:	linux-acenic@sunsite.dk
@@ -803,7 +809,7 @@ S:	Supported
 F:	drivers/iio/*/ad*
 X:	drivers/iio/*/adjd*
 F:	drivers/staging/iio/*/ad*
-F:	staging/iio/trigger/iio-trig-bfin-timer.c
+F:	drivers/staging/iio/trigger/iio-trig-bfin-timer.c
 
 ANALOG DEVICES INC DMA DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
@@ -2612,6 +2618,7 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git
 S:	Maintained
 N:	bcm2835
+F:	drivers/staging/vc04_services
 
 BROADCOM BCM47XX MIPS ARCHITECTURE
 M:	Hauke Mehrtens <hauke@hauke-m.de>
@@ -5192,13 +5199,6 @@ F:	sound/soc/fsl/fsl*
 F:	sound/soc/fsl/imx*
 F:	sound/soc/fsl/mpc8610_hpcd.c
 
-FREESCALE QORIQ MANAGEMENT COMPLEX DRIVER
-M:	"J. German Rivera" <German.Rivera@freescale.com>
-M:	Stuart Yoder <stuart.yoder@nxp.com>
-L:	linux-kernel@vger.kernel.org
-S:	Maintained
-F:	drivers/staging/fsl-mc/
-
 FREEVXFS FILESYSTEM
 M:	Christoph Hellwig <hch@infradead.org>
 W:	ftp://ftp.openlinux.org/pub/people/hch/vxfs
@@ -6215,6 +6215,22 @@ L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	drivers/media/rc/iguanair.c
 
+IIO DIGITAL POTENTIOMETER DAC
+M:	Peter Rosin <peda@axentia.se>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
+F:	Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
+F:	drivers/iio/dac/dpot-dac.c
+
+IIO ENVELOPE DETECTOR
+M:	Peter Rosin <peda@axentia.se>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
+F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
+F:	drivers/iio/adc/envelope-detector.c
+
 IIO SUBSYSTEM AND DRIVERS
 M:	Jonathan Cameron <jic23@kernel.org>
 R:	Hartmut Knaack <knaack.h@gmx.de>
@@ -6596,6 +6612,13 @@ S:	Maintained
 F:	arch/x86/include/asm/pmc_core.h
 F:	drivers/platform/x86/intel_pmc_core*
 
+INVENSENSE MPU-3050 GYROSCOPE DRIVER
+M:	Linus Walleij <linus.walleij@linaro.org>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	drivers/iio/gyro/mpu3050*
+F:	Documentation/devicetree/bindings/iio/gyroscope/inv,mpu3050.txt
+
 IOC3 ETHERNET DRIVER
 M:	Ralf Baechle <ralf@linux-mips.org>
 L:	linux-mips@linux-mips.org
@@ -7803,6 +7826,7 @@ MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER
 M:	Peter Rosin <peda@axentia.se>
 L:	linux-iio@vger.kernel.org
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
 F:	drivers/iio/potentiometer/mcp4531.c
 
 MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
@@ -10056,6 +10080,12 @@ F:	fs/qnx4/
 F:	include/uapi/linux/qnx4_fs.h
 F:	include/uapi/linux/qnxtypes.h
 
+QORIQ DPAA2 FSL-MC BUS DRIVER
+M:	Stuart Yoder <stuart.yoder@nxp.com>
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+F:	drivers/staging/fsl-mc/
+
 QT1010 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
 L:	linux-media@vger.kernel.org
@@ -11647,12 +11677,6 @@ L:	linux-fbdev@vger.kernel.org
 S:	Maintained
 F:	drivers/staging/sm750fb/
 
-STAGING - SLICOSS
-M:	Lior Dotan <liodot@gmail.com>
-M:	Christopher Harrer <charrer@alacritech.com>
-S:	Odd Fixes
-F:	drivers/staging/slicoss/
-
 STAGING - SPEAKUP CONSOLE SPEECH DRIVER
 M:	William Hubbs <w.d.hubbs@gmail.com>
 M:	Chris Brannon <chris@the-brannons.com>

+ 2 - 0
drivers/iio/Kconfig

@@ -73,6 +73,7 @@ source "drivers/iio/adc/Kconfig"
 source "drivers/iio/amplifiers/Kconfig"
 source "drivers/iio/chemical/Kconfig"
 source "drivers/iio/common/Kconfig"
+source "drivers/iio/counter/Kconfig"
 source "drivers/iio/dac/Kconfig"
 source "drivers/iio/dummy/Kconfig"
 source "drivers/iio/frequency/Kconfig"
@@ -87,6 +88,7 @@ if IIO_TRIGGER
    source "drivers/iio/trigger/Kconfig"
 endif #IIO_TRIGGER
 source "drivers/iio/potentiometer/Kconfig"
+source "drivers/iio/potentiostat/Kconfig"
 source "drivers/iio/pressure/Kconfig"
 source "drivers/iio/proximity/Kconfig"
 source "drivers/iio/temperature/Kconfig"

+ 2 - 0
drivers/iio/Makefile

@@ -18,6 +18,7 @@ obj-y += amplifiers/
 obj-y += buffer/
 obj-y += chemical/
 obj-y += common/
+obj-y += counter/
 obj-y += dac/
 obj-y += dummy/
 obj-y += gyro/
@@ -29,6 +30,7 @@ obj-y += light/
 obj-y += magnetometer/
 obj-y += orientation/
 obj-y += potentiometer/
+obj-y += potentiostat/
 obj-y += pressure/
 obj-y += proximity/
 obj-y += temperature/

+ 44 - 1
drivers/iio/accel/Kconfig

@@ -52,6 +52,26 @@ config BMC150_ACCEL_SPI
 	tristate
 	select REGMAP_SPI
 
+config DA280
+	tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver"
+	depends on I2C
+	help
+	  Say yes here to build support for the MiraMEMS DA280 3-axis 14-bit
+	  digital accelerometer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called da280.
+
+config DA311
+	tristate "MiraMEMS DA311 3-axis 12-bit digital accelerometer driver"
+	depends on I2C
+	help
+	  Say yes here to build support for the MiraMEMS DA311 3-axis 12-bit
+	  digital accelerometer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called da311.
+
 config DMARD06
 	tristate "Domintech DMARD06 Digital Accelerometer Driver"
 	depends on OF || COMPILE_TEST
@@ -73,6 +93,16 @@ config DMARD09
 	  Choosing M will build the driver as a module. If so, the module
 	  will be called dmard09.
 
+config DMARD10
+	tristate "Domintech DMARD10 3-axis Accelerometer Driver"
+	depends on I2C
+	help
+	  Say yes here to get support for the Domintech DMARD10 3-axis
+	  accelerometer.
+
+	  Choosing M will build the driver as a module. If so, the module
+	  will be called dmard10.
+
 config HID_SENSOR_ACCEL_3D
 	depends on HID_SENSOR_HUB
 	select IIO_BUFFER
@@ -97,7 +127,8 @@ config IIO_ST_ACCEL_3AXIS
 	help
 	  Say yes here to build support for STMicroelectronics accelerometers:
 	  LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
-	  LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL.
+	  LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
+	  LNG2DM
 
 	  This driver can also be built as a module. If so, these modules
 	  will be created:
@@ -273,6 +304,18 @@ config MXC6255
 	  To compile this driver as a module, choose M here: the module will be
 	  called mxc6255.
 
+config SCA3000
+	select IIO_BUFFER
+	select IIO_KFIFO_BUF
+	depends on SPI
+	tristate "VTI SCA3000 series accelerometers"
+	help
+	  Say Y here to build support for the VTI SCA3000 series of SPI
+	  accelerometers. These devices use a hardware ring buffer.
+
+	  To compile this driver as a module, say M here: the module will be
+	  called sca3000.
+
 config STK8312
 	tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
 	depends on I2C

+ 5 - 0
drivers/iio/accel/Makefile

@@ -8,8 +8,11 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
 obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
 obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
 obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
+obj-$(CONFIG_DA280)	+= da280.o
+obj-$(CONFIG_DA311)	+= da311.o
 obj-$(CONFIG_DMARD06)	+= dmard06.o
 obj-$(CONFIG_DMARD09)	+= dmard09.o
+obj-$(CONFIG_DMARD10)	+= dmard10.o
 obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
 obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
 obj-$(CONFIG_KXSD9)	+= kxsd9.o
@@ -32,6 +35,8 @@ obj-$(CONFIG_MMA9553)		+= mma9553.o
 obj-$(CONFIG_MXC4005)		+= mxc4005.o
 obj-$(CONFIG_MXC6255)		+= mxc6255.o
 
+obj-$(CONFIG_SCA3000)		+= sca3000.o
+
 obj-$(CONFIG_STK8312)		+= stk8312.o
 obj-$(CONFIG_STK8BA50)		+= stk8ba50.o
 

+ 183 - 0
drivers/iio/accel/da280.c

@@ -0,0 +1,183 @@
+/**
+ * IIO driver for the MiraMEMS DA280 3-axis accelerometer and
+ * IIO driver for the MiraMEMS DA226 2-axis accelerometer
+ *
+ * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/byteorder/generic.h>
+
+#define DA280_REG_CHIP_ID		0x01
+#define DA280_REG_ACC_X_LSB		0x02
+#define DA280_REG_ACC_Y_LSB		0x04
+#define DA280_REG_ACC_Z_LSB		0x06
+#define DA280_REG_MODE_BW		0x11
+
+#define DA280_CHIP_ID			0x13
+#define DA280_MODE_ENABLE		0x1e
+#define DA280_MODE_DISABLE		0x9e
+
+enum { da226, da280 };
+
+/*
+ * a value of + or -4096 corresponds to + or - 1G
+ * scale = 9.81 / 4096 = 0.002395019
+ */
+
+static const int da280_nscale = 2395019;
+
+#define DA280_CHANNEL(reg, axis) {	\
+	.type = IIO_ACCEL,	\
+	.address = reg,	\
+	.modified = 1,	\
+	.channel2 = IIO_MOD_##axis,	\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+}
+
+static const struct iio_chan_spec da280_channels[] = {
+	DA280_CHANNEL(DA280_REG_ACC_X_LSB, X),
+	DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y),
+	DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),
+};
+
+struct da280_data {
+	struct i2c_client *client;
+};
+
+static int da280_enable(struct i2c_client *client, bool enable)
+{
+	u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE;
+
+	return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, data);
+}
+
+static int da280_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val, int *val2, long mask)
+{
+	struct da280_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = i2c_smbus_read_word_data(data->client, chan->address);
+		if (ret < 0)
+			return ret;
+		/*
+		 * Values are 14 bits, stored as 16 bits with the 2
+		 * least significant bits always 0.
+		 */
+		*val = (short)ret >> 2;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = da280_nscale;
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info da280_info = {
+	.driver_module	= THIS_MODULE,
+	.read_raw	= da280_read_raw,
+};
+
+static int da280_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret;
+	struct iio_dev *indio_dev;
+	struct da280_data *data;
+
+	ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
+	if (ret != DA280_CHIP_ID)
+		return (ret < 0) ? ret : -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	data->client = client;
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &da280_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = da280_channels;
+	if (id->driver_data == da226) {
+		indio_dev->name = "da226";
+		indio_dev->num_channels = 2;
+	} else {
+		indio_dev->name = "da280";
+		indio_dev->num_channels = 3;
+	}
+
+	ret = da280_enable(client, true);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "device_register failed\n");
+		da280_enable(client, false);
+	}
+
+	return ret;
+}
+
+static int da280_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+
+	return da280_enable(client, false);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int da280_suspend(struct device *dev)
+{
+	return da280_enable(to_i2c_client(dev), false);
+}
+
+static int da280_resume(struct device *dev)
+{
+	return da280_enable(to_i2c_client(dev), true);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
+
+static const struct i2c_device_id da280_i2c_id[] = {
+	{ "da226", da226 },
+	{ "da280", da280 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
+
+static struct i2c_driver da280_driver = {
+	.driver = {
+		.name = "da280",
+		.pm = &da280_pm_ops,
+	},
+	.probe		= da280_probe,
+	.remove		= da280_remove,
+	.id_table	= da280_i2c_id,
+};
+
+module_i2c_driver(da280_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");

+ 305 - 0
drivers/iio/accel/da311.c

@@ -0,0 +1,305 @@
+/**
+ * IIO driver for the MiraMEMS DA311 3-axis accelerometer
+ *
+ * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (c) 2011-2013 MiraMEMS Sensing Technology Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/byteorder/generic.h>
+
+#define DA311_CHIP_ID			0x13
+
+/*
+ * Note register addressed go from 0 - 0x3f and then wrap.
+ * For some reason there are 2 banks with 0 - 0x3f addresses,
+ * rather then a single 0-0x7f bank.
+ */
+
+/* Bank 0 regs */
+#define DA311_REG_BANK			0x0000
+#define DA311_REG_LDO_REG		0x0006
+#define DA311_REG_CHIP_ID		0x000f
+#define DA311_REG_TEMP_CFG_REG		0x001f
+#define DA311_REG_CTRL_REG1		0x0020
+#define DA311_REG_CTRL_REG3		0x0022
+#define DA311_REG_CTRL_REG4		0x0023
+#define DA311_REG_CTRL_REG5		0x0024
+#define DA311_REG_CTRL_REG6		0x0025
+#define DA311_REG_STATUS_REG		0x0027
+#define DA311_REG_OUT_X_L		0x0028
+#define DA311_REG_OUT_X_H		0x0029
+#define DA311_REG_OUT_Y_L		0x002a
+#define DA311_REG_OUT_Y_H		0x002b
+#define DA311_REG_OUT_Z_L		0x002c
+#define DA311_REG_OUT_Z_H		0x002d
+#define DA311_REG_INT1_CFG		0x0030
+#define DA311_REG_INT1_SRC		0x0031
+#define DA311_REG_INT1_THS		0x0032
+#define DA311_REG_INT1_DURATION		0x0033
+#define DA311_REG_INT2_CFG		0x0034
+#define DA311_REG_INT2_SRC		0x0035
+#define DA311_REG_INT2_THS		0x0036
+#define DA311_REG_INT2_DURATION		0x0037
+#define DA311_REG_CLICK_CFG		0x0038
+#define DA311_REG_CLICK_SRC		0x0039
+#define DA311_REG_CLICK_THS		0x003a
+#define DA311_REG_TIME_LIMIT		0x003b
+#define DA311_REG_TIME_LATENCY		0x003c
+#define DA311_REG_TIME_WINDOW		0x003d
+
+/* Bank 1 regs */
+#define DA311_REG_SOFT_RESET		0x0105
+#define DA311_REG_OTP_XOFF_L		0x0110
+#define DA311_REG_OTP_XOFF_H		0x0111
+#define DA311_REG_OTP_YOFF_L		0x0112
+#define DA311_REG_OTP_YOFF_H		0x0113
+#define DA311_REG_OTP_ZOFF_L		0x0114
+#define DA311_REG_OTP_ZOFF_H		0x0115
+#define DA311_REG_OTP_XSO		0x0116
+#define DA311_REG_OTP_YSO		0x0117
+#define DA311_REG_OTP_ZSO		0x0118
+#define DA311_REG_OTP_TRIM_OSC		0x011b
+#define DA311_REG_LPF_ABSOLUTE		0x011c
+#define DA311_REG_TEMP_OFF1		0x0127
+#define DA311_REG_TEMP_OFF2		0x0128
+#define DA311_REG_TEMP_OFF3		0x0129
+#define DA311_REG_OTP_TRIM_THERM_H	0x011a
+
+/*
+ * a value of + or -1024 corresponds to + or - 1G
+ * scale = 9.81 / 1024 = 0.009580078
+ */
+
+static const int da311_nscale = 9580078;
+
+#define DA311_CHANNEL(reg, axis) {	\
+	.type = IIO_ACCEL,	\
+	.address = reg,	\
+	.modified = 1,	\
+	.channel2 = IIO_MOD_##axis,	\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+}
+
+static const struct iio_chan_spec da311_channels[] = {
+	/* | 0x80 comes from the android driver */
+	DA311_CHANNEL(DA311_REG_OUT_X_L | 0x80, X),
+	DA311_CHANNEL(DA311_REG_OUT_Y_L | 0x80, Y),
+	DA311_CHANNEL(DA311_REG_OUT_Z_L | 0x80, Z),
+};
+
+struct da311_data {
+	struct i2c_client *client;
+};
+
+static int da311_register_mask_write(struct i2c_client *client, u16 addr,
+				     u8 mask, u8 data)
+{
+	int ret;
+	u8 tmp_data = 0;
+
+	if (addr & 0xff00) {
+		/* Select bank 1 */
+		ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x01);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (mask != 0xff) {
+		ret = i2c_smbus_read_byte_data(client, addr);
+		if (ret < 0)
+			return ret;
+		tmp_data = ret;
+	}
+
+	tmp_data &= ~mask;
+	tmp_data |= data & mask;
+	ret = i2c_smbus_write_byte_data(client, addr & 0xff, tmp_data);
+	if (ret < 0)
+		return ret;
+
+	if (addr & 0xff00) {
+		/* Back to bank 0 */
+		ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x00);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/* Init sequence taken from the android driver */
+static int da311_reset(struct i2c_client *client)
+{
+	const struct {
+		u16 addr;
+		u8 mask;
+		u8 data;
+	} init_data[] = {
+		{ DA311_REG_TEMP_CFG_REG,       0xff,   0x08 },
+		{ DA311_REG_CTRL_REG5,          0xff,   0x80 },
+		{ DA311_REG_CTRL_REG4,          0x30,   0x00 },
+		{ DA311_REG_CTRL_REG1,          0xff,   0x6f },
+		{ DA311_REG_TEMP_CFG_REG,       0xff,   0x88 },
+		{ DA311_REG_LDO_REG,            0xff,   0x02 },
+		{ DA311_REG_OTP_TRIM_OSC,       0xff,   0x27 },
+		{ DA311_REG_LPF_ABSOLUTE,       0xff,   0x30 },
+		{ DA311_REG_TEMP_OFF1,          0xff,   0x3f },
+		{ DA311_REG_TEMP_OFF2,          0xff,   0xff },
+		{ DA311_REG_TEMP_OFF3,          0xff,   0x0f },
+	};
+	int i, ret;
+
+	/* Reset */
+	ret = da311_register_mask_write(client, DA311_REG_SOFT_RESET,
+					0xff, 0xaa);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+		ret = da311_register_mask_write(client,
+						init_data[i].addr,
+						init_data[i].mask,
+						init_data[i].data);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int da311_enable(struct i2c_client *client, bool enable)
+{
+	u8 data = enable ? 0x00 : 0x20;
+
+	return da311_register_mask_write(client, DA311_REG_TEMP_CFG_REG,
+					 0x20, data);
+}
+
+static int da311_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val, int *val2, long mask)
+{
+	struct da311_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = i2c_smbus_read_word_data(data->client, chan->address);
+		if (ret < 0)
+			return ret;
+		/*
+		 * Values are 12 bits, stored as 16 bits with the 4
+		 * least significant bits always 0.
+		 */
+		*val = (short)ret >> 4;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = da311_nscale;
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info da311_info = {
+	.driver_module	= THIS_MODULE,
+	.read_raw	= da311_read_raw,
+};
+
+static int da311_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret;
+	struct iio_dev *indio_dev;
+	struct da311_data *data;
+
+	ret = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
+	if (ret != DA311_CHIP_ID)
+		return (ret < 0) ? ret : -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	data->client = client;
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &da311_info;
+	indio_dev->name = "da311";
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = da311_channels;
+	indio_dev->num_channels = ARRAY_SIZE(da311_channels);
+
+	ret = da311_reset(client);
+	if (ret < 0)
+		return ret;
+
+	ret = da311_enable(client, true);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "device_register failed\n");
+		da311_enable(client, false);
+	}
+
+	return ret;
+}
+
+static int da311_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+
+	return da311_enable(client, false);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int da311_suspend(struct device *dev)
+{
+	return da311_enable(to_i2c_client(dev), false);
+}
+
+static int da311_resume(struct device *dev)
+{
+	return da311_enable(to_i2c_client(dev), true);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
+
+static const struct i2c_device_id da311_i2c_id[] = {
+	{"da311", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
+
+static struct i2c_driver da311_driver = {
+	.driver = {
+		.name = "da311",
+		.pm = &da311_pm_ops,
+	},
+	.probe		= da311_probe,
+	.remove		= da311_remove,
+	.id_table	= da311_i2c_id,
+};
+
+module_i2c_driver(da311_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("MiraMEMS DA311 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");

+ 266 - 0
drivers/iio/accel/dmard10.c

@@ -0,0 +1,266 @@
+/**
+ * IIO driver for the 3-axis accelerometer Domintech ARD10.
+ *
+ * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (c) 2012 Domintech Technology Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/byteorder/generic.h>
+
+#define DMARD10_REG_ACTR			0x00
+#define DMARD10_REG_AFEM			0x0c
+#define DMARD10_REG_STADR			0x12
+#define DMARD10_REG_STAINT			0x1c
+#define DMARD10_REG_MISC2			0x1f
+#define DMARD10_REG_PD				0x21
+
+#define DMARD10_MODE_OFF			0x00
+#define DMARD10_MODE_STANDBY			0x02
+#define DMARD10_MODE_ACTIVE			0x06
+#define DMARD10_MODE_READ_OTP			0x12
+#define DMARD10_MODE_RESET_DATA_PATH		0x82
+
+/* AFEN set 1, ATM[2:0]=b'000 (normal), EN_Z/Y/X/T=1 */
+#define DMARD10_VALUE_AFEM_AFEN_NORMAL		0x8f
+/* ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) */
+#define DMARD10_VALUE_CKSEL_ODR_100_204		0x74
+/* INTC[6:5]=b'00 */
+#define DMARD10_VALUE_INTC			0x00
+/* TAP1/TAP2 Average 2 */
+#define DMARD10_VALUE_TAPNS_AVE_2		0x11
+
+#define DMARD10_VALUE_STADR			0x55
+#define DMARD10_VALUE_STAINT			0xaa
+#define DMARD10_VALUE_MISC2_OSCA_EN		0x08
+#define DMARD10_VALUE_PD_RST			0x52
+
+/* Offsets into the buffer read in dmard10_read_raw() */
+#define DMARD10_X_OFFSET			1
+#define DMARD10_Y_OFFSET			2
+#define DMARD10_Z_OFFSET			3
+
+/*
+ * a value of + or -128 corresponds to + or - 1G
+ * scale = 9.81 / 128 = 0.076640625
+ */
+
+static const int dmard10_nscale = 76640625;
+
+#define DMARD10_CHANNEL(reg, axis) {	\
+	.type = IIO_ACCEL,	\
+	.address = reg,	\
+	.modified = 1,	\
+	.channel2 = IIO_MOD_##axis,	\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+}
+
+static const struct iio_chan_spec dmard10_channels[] = {
+	DMARD10_CHANNEL(DMARD10_X_OFFSET, X),
+	DMARD10_CHANNEL(DMARD10_Y_OFFSET, Y),
+	DMARD10_CHANNEL(DMARD10_Z_OFFSET, Z),
+};
+
+struct dmard10_data {
+	struct i2c_client *client;
+};
+
+/* Init sequence taken from the android driver */
+static int dmard10_reset(struct i2c_client *client)
+{
+	unsigned char buffer[7];
+	int ret;
+
+	/* 1. Powerdown reset */
+	ret = i2c_smbus_write_byte_data(client, DMARD10_REG_PD,
+						DMARD10_VALUE_PD_RST);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * 2. ACTR => Standby mode => Download OTP to parameter reg =>
+	 *    Standby mode => Reset data path => Standby mode
+	 */
+	buffer[0] = DMARD10_REG_ACTR;
+	buffer[1] = DMARD10_MODE_STANDBY;
+	buffer[2] = DMARD10_MODE_READ_OTP;
+	buffer[3] = DMARD10_MODE_STANDBY;
+	buffer[4] = DMARD10_MODE_RESET_DATA_PATH;
+	buffer[5] = DMARD10_MODE_STANDBY;
+	ret = i2c_master_send(client, buffer, 6);
+	if (ret < 0)
+		return ret;
+
+	/* 3. OSCA_EN = 1, TSTO = b'000 (INT1 = normal, TEST0 = normal) */
+	ret = i2c_smbus_write_byte_data(client, DMARD10_REG_MISC2,
+						DMARD10_VALUE_MISC2_OSCA_EN);
+	if (ret < 0)
+		return ret;
+
+	/* 4. AFEN = 1 (AFE will powerdown after ADC) */
+	buffer[0] = DMARD10_REG_AFEM;
+	buffer[1] = DMARD10_VALUE_AFEM_AFEN_NORMAL;
+	buffer[2] = DMARD10_VALUE_CKSEL_ODR_100_204;
+	buffer[3] = DMARD10_VALUE_INTC;
+	buffer[4] = DMARD10_VALUE_TAPNS_AVE_2;
+	buffer[5] = 0x00; /* DLYC, no delay timing */
+	buffer[6] = 0x07; /* INTD=1 push-pull, INTA=1 active high, AUTOT=1 */
+	ret = i2c_master_send(client, buffer, 7);
+	if (ret < 0)
+		return ret;
+
+	/* 5. Activation mode */
+	ret = i2c_smbus_write_byte_data(client, DMARD10_REG_ACTR,
+						DMARD10_MODE_ACTIVE);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/* Shutdown sequence taken from the android driver */
+static int dmard10_shutdown(struct i2c_client *client)
+{
+	unsigned char buffer[3];
+
+	buffer[0] = DMARD10_REG_ACTR;
+	buffer[1] = DMARD10_MODE_STANDBY;
+	buffer[2] = DMARD10_MODE_OFF;
+
+	return i2c_master_send(client, buffer, 3);
+}
+
+static int dmard10_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val, int *val2, long mask)
+{
+	struct dmard10_data *data = iio_priv(indio_dev);
+	__le16 buf[4];
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		/*
+		 * Read 8 bytes starting at the REG_STADR register, trying to
+		 * read the individual X, Y, Z registers will always read 0.
+		 */
+		ret = i2c_smbus_read_i2c_block_data(data->client,
+						    DMARD10_REG_STADR,
+						    sizeof(buf), (u8 *)buf);
+		if (ret < 0)
+			return ret;
+		ret = le16_to_cpu(buf[chan->address]);
+		*val = sign_extend32(ret, 12);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = dmard10_nscale;
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info dmard10_info = {
+	.driver_module	= THIS_MODULE,
+	.read_raw	= dmard10_read_raw,
+};
+
+static int dmard10_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret;
+	struct iio_dev *indio_dev;
+	struct dmard10_data *data;
+
+	/* These 2 registers have special POR reset values used for id */
+	ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
+	if (ret != DMARD10_VALUE_STADR)
+		return (ret < 0) ? ret : -ENODEV;
+
+	ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
+	if (ret != DMARD10_VALUE_STAINT)
+		return (ret < 0) ? ret : -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev) {
+		dev_err(&client->dev, "iio allocation failed!\n");
+		return -ENOMEM;
+	}
+
+	data = iio_priv(indio_dev);
+	data->client = client;
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &dmard10_info;
+	indio_dev->name = "dmard10";
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = dmard10_channels;
+	indio_dev->num_channels = ARRAY_SIZE(dmard10_channels);
+
+	ret = dmard10_reset(client);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "device_register failed\n");
+		dmard10_shutdown(client);
+	}
+
+	return ret;
+}
+
+static int dmard10_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+
+	return dmard10_shutdown(client);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dmard10_suspend(struct device *dev)
+{
+	return dmard10_shutdown(to_i2c_client(dev));
+}
+
+static int dmard10_resume(struct device *dev)
+{
+	return dmard10_reset(to_i2c_client(dev));
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume);
+
+static const struct i2c_device_id dmard10_i2c_id[] = {
+	{"dmard10", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
+
+static struct i2c_driver dmard10_driver = {
+	.driver = {
+		.name = "dmard10",
+		.pm = &dmard10_pm_ops,
+	},
+	.probe		= dmard10_probe,
+	.remove		= dmard10_remove,
+	.id_table	= dmard10_i2c_id,
+};
+
+module_i2c_driver(dmard10_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Domintech ARD10 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");

+ 1 - 1
drivers/iio/accel/mma7660.c

@@ -39,7 +39,7 @@
 
 #define MMA7660_SCALE_AVAIL	"0.467142857"
 
-const int mma7660_nscale = 467142857;
+static const int mma7660_nscale = 467142857;
 
 #define MMA7660_CHANNEL(reg, axis) {	\
 	.type = IIO_ACCEL,	\

+ 43 - 36
drivers/iio/accel/mma8452.c

@@ -459,12 +459,14 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		if (iio_buffer_enabled(indio_dev))
-			return -EBUSY;
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
 
 		mutex_lock(&data->lock);
 		ret = mma8452_read(data, buffer);
 		mutex_unlock(&data->lock);
+		iio_device_release_direct_mode(indio_dev);
 		if (ret < 0)
 			return ret;
 
@@ -664,37 +666,46 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
 	struct mma8452_data *data = iio_priv(indio_dev);
 	int i, ret;
 
-	if (iio_buffer_enabled(indio_dev))
-		return -EBUSY;
+	ret = iio_device_claim_direct_mode(indio_dev);
+	if (ret)
+		return ret;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		i = mma8452_get_samp_freq_index(data, val, val2);
-		if (i < 0)
-			return i;
-
+		if (i < 0) {
+			ret = i;
+			break;
+		}
 		data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
 		data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
 
-		return mma8452_change_config(data, MMA8452_CTRL_REG1,
-					     data->ctrl_reg1);
+		ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
+					    data->ctrl_reg1);
+		break;
 	case IIO_CHAN_INFO_SCALE:
 		i = mma8452_get_scale_index(data, val, val2);
-		if (i < 0)
-			return i;
+		if (i < 0) {
+			ret = i;
+			break;
+		}
 
 		data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
 		data->data_cfg |= i;
 
-		return mma8452_change_config(data, MMA8452_DATA_CFG,
-					     data->data_cfg);
+		ret = mma8452_change_config(data, MMA8452_DATA_CFG,
+					    data->data_cfg);
+		break;
 	case IIO_CHAN_INFO_CALIBBIAS:
-		if (val < -128 || val > 127)
-			return -EINVAL;
+		if (val < -128 || val > 127) {
+			ret = -EINVAL;
+			break;
+		}
 
-		return mma8452_change_config(data,
-					     MMA8452_OFF_X + chan->scan_index,
-					     val);
+		ret = mma8452_change_config(data,
+					    MMA8452_OFF_X + chan->scan_index,
+					    val);
+		break;
 
 	case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
 		if (val == 0 && val2 == 0) {
@@ -703,23 +714,30 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
 			data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
 			ret = mma8452_set_hp_filter_frequency(data, val, val2);
 			if (ret < 0)
-				return ret;
+				break;
 		}
 
-		return mma8452_change_config(data, MMA8452_DATA_CFG,
+		ret = mma8452_change_config(data, MMA8452_DATA_CFG,
 					     data->data_cfg);
+		break;
 
 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
 		ret = mma8452_get_odr_index(data);
 
 		for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
-			if (mma8452_os_ratio[i][ret] == val)
-				return mma8452_set_power_mode(data, i);
+			if (mma8452_os_ratio[i][ret] == val) {
+				ret = mma8452_set_power_mode(data, i);
+				break;
+			}
 		}
-
+		break;
 	default:
-		return -EINVAL;
+		ret = -EINVAL;
+		break;
 	}
+
+	iio_device_release_direct_mode(indio_dev);
+	return ret;
 }
 
 static int mma8452_read_thresh(struct iio_dev *indio_dev,
@@ -1347,20 +1365,9 @@ static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
 	return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
 }
 
-static int mma8452_validate_device(struct iio_trigger *trig,
-				   struct iio_dev *indio_dev)
-{
-	struct iio_dev *indio = iio_trigger_get_drvdata(trig);
-
-	if (indio != indio_dev)
-		return -EINVAL;
-
-	return 0;
-}
-
 static const struct iio_trigger_ops mma8452_trigger_ops = {
 	.set_trigger_state = mma8452_data_rdy_trigger_set_state,
-	.validate_device = mma8452_validate_device,
+	.validate_device = iio_trigger_validate_own_device,
 	.owner = THIS_MODULE,
 };
 

+ 1576 - 0
drivers/iio/accel/sca3000.c

@@ -0,0 +1,1576 @@
+/*
+ * sca3000_core.c -- support VTI sca3000 series accelerometers via SPI
+ *
+ * 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.
+ *
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ * See industrialio/accels/sca3000.h for comments.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define SCA3000_WRITE_REG(a) (((a) << 2) | 0x02)
+#define SCA3000_READ_REG(a) ((a) << 2)
+
+#define SCA3000_REG_REVID_ADDR				0x00
+#define   SCA3000_REG_REVID_MAJOR_MASK			GENMASK(8, 4)
+#define   SCA3000_REG_REVID_MINOR_MASK			GENMASK(3, 0)
+
+#define SCA3000_REG_STATUS_ADDR				0x02
+#define   SCA3000_LOCKED				BIT(5)
+#define   SCA3000_EEPROM_CS_ERROR			BIT(1)
+#define   SCA3000_SPI_FRAME_ERROR			BIT(0)
+ 
+/* All reads done using register decrement so no need to directly access LSBs */
+#define SCA3000_REG_X_MSB_ADDR				0x05
+#define SCA3000_REG_Y_MSB_ADDR				0x07
+#define SCA3000_REG_Z_MSB_ADDR				0x09
+
+#define SCA3000_REG_RING_OUT_ADDR			0x0f
+
+/* Temp read untested - the e05 doesn't have the sensor */
+#define SCA3000_REG_TEMP_MSB_ADDR			0x13
+
+#define SCA3000_REG_MODE_ADDR				0x14
+#define SCA3000_MODE_PROT_MASK				0x28
+#define   SCA3000_REG_MODE_RING_BUF_ENABLE		BIT(7)
+#define   SCA3000_REG_MODE_RING_BUF_8BIT		BIT(6)
+
+/*
+ * Free fall detection triggers an interrupt if the acceleration
+ * is below a threshold for equivalent of 25cm drop
+ */
+#define   SCA3000_REG_MODE_FREE_FALL_DETECT		BIT(4)
+#define   SCA3000_REG_MODE_MEAS_MODE_NORMAL		0x00
+#define   SCA3000_REG_MODE_MEAS_MODE_OP_1		0x01
+#define   SCA3000_REG_MODE_MEAS_MODE_OP_2		0x02
+
+/*
+ * In motion detection mode the accelerations are band pass filtered
+ * (approx 1 - 25Hz) and then a programmable threshold used to trigger
+ * and interrupt.
+ */
+#define   SCA3000_REG_MODE_MEAS_MODE_MOT_DET		0x03
+#define   SCA3000_REG_MODE_MODE_MASK			0x03
+
+#define SCA3000_REG_BUF_COUNT_ADDR			0x15
+
+#define SCA3000_REG_INT_STATUS_ADDR			0x16
+#define   SCA3000_REG_INT_STATUS_THREE_QUARTERS		BIT(7)
+#define   SCA3000_REG_INT_STATUS_HALF			BIT(6)
+	
+#define SCA3000_INT_STATUS_FREE_FALL			BIT(3)
+#define SCA3000_INT_STATUS_Y_TRIGGER			BIT(2)
+#define SCA3000_INT_STATUS_X_TRIGGER			BIT(1)
+#define SCA3000_INT_STATUS_Z_TRIGGER			BIT(0)
+
+/* Used to allow access to multiplexed registers */
+#define SCA3000_REG_CTRL_SEL_ADDR			0x18
+/* Only available for SCA3000-D03 and SCA3000-D01 */
+#define   SCA3000_REG_CTRL_SEL_I2C_DISABLE		0x01
+#define   SCA3000_REG_CTRL_SEL_MD_CTRL			0x02
+#define   SCA3000_REG_CTRL_SEL_MD_Y_TH			0x03
+#define   SCA3000_REG_CTRL_SEL_MD_X_TH			0x04
+#define   SCA3000_REG_CTRL_SEL_MD_Z_TH			0x05
+/*
+ * BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device
+ * will not function
+ */
+#define   SCA3000_REG_CTRL_SEL_OUT_CTRL			0x0B
+
+#define     SCA3000_REG_OUT_CTRL_PROT_MASK		0xE0
+#define     SCA3000_REG_OUT_CTRL_BUF_X_EN		0x10
+#define     SCA3000_REG_OUT_CTRL_BUF_Y_EN		0x08
+#define     SCA3000_REG_OUT_CTRL_BUF_Z_EN		0x04
+#define     SCA3000_REG_OUT_CTRL_BUF_DIV_MASK		0x03
+#define     SCA3000_REG_OUT_CTRL_BUF_DIV_4		0x02
+#define     SCA3000_REG_OUT_CTRL_BUF_DIV_2		0x01
+
+
+/*
+ * Control which motion detector interrupts are on.
+ * For now only OR combinations are supported.
+ */
+#define SCA3000_MD_CTRL_PROT_MASK			0xC0
+#define SCA3000_MD_CTRL_OR_Y				BIT(0)
+#define SCA3000_MD_CTRL_OR_X				BIT(1)
+#define SCA3000_MD_CTRL_OR_Z				BIT(2)
+/* Currently unsupported */
+#define SCA3000_MD_CTRL_AND_Y				BIT(3)
+#define SCA3000_MD_CTRL_AND_X				BIT(4)
+#define SAC3000_MD_CTRL_AND_Z				BIT(5)
+
+/*
+ * Some control registers of complex access methods requiring this register to
+ * be used to remove a lock.
+ */
+#define SCA3000_REG_UNLOCK_ADDR				0x1e
+
+#define SCA3000_REG_INT_MASK_ADDR			0x21
+#define   SCA3000_REG_INT_MASK_PROT_MASK		0x1C
+ 
+#define   SCA3000_REG_INT_MASK_RING_THREE_QUARTER	BIT(7)
+#define   SCA3000_REG_INT_MASK_RING_HALF		BIT(6)
+
+#define SCA3000_REG_INT_MASK_ALL_INTS			0x02
+#define SCA3000_REG_INT_MASK_ACTIVE_HIGH		0x01
+#define SCA3000_REG_INT_MASK_ACTIVE_LOW			0x00
+/* Values of multiplexed registers (write to ctrl_data after select) */
+#define SCA3000_REG_CTRL_DATA_ADDR			0x22
+
+/*
+ * Measurement modes available on some sca3000 series chips. Code assumes others
+ * may become available in the future.
+ *
+ * Bypass - Bypass the low-pass filter in the signal channel so as to increase
+ *          signal bandwidth.
+ *
+ * Narrow - Narrow low-pass filtering of the signal channel and half output
+ *          data rate by decimation.
+ *
+ * Wide - Widen low-pass filtering of signal channel to increase bandwidth
+ */
+#define SCA3000_OP_MODE_BYPASS				0x01
+#define SCA3000_OP_MODE_NARROW				0x02
+#define SCA3000_OP_MODE_WIDE				0x04
+#define SCA3000_MAX_TX 6
+#define SCA3000_MAX_RX 2
+
+/**
+ * struct sca3000_state - device instance state information
+ * @us:			the associated spi device
+ * @info:			chip variant information
+ * @last_timestamp:		the timestamp of the last event
+ * @mo_det_use_count:		reference counter for the motion detection unit
+ * @lock:			lock used to protect elements of sca3000_state
+ *				and the underlying device state.
+ * @tx:			dma-able transmit buffer
+ * @rx:			dma-able receive buffer
+ **/
+struct sca3000_state {
+	struct spi_device		*us;
+	const struct sca3000_chip_info	*info;
+	s64				last_timestamp;
+	int				mo_det_use_count;
+	struct mutex			lock;
+	/* Can these share a cacheline ? */
+	u8				rx[384] ____cacheline_aligned;
+	u8				tx[6] ____cacheline_aligned;
+};
+
+/**
+ * struct sca3000_chip_info - model dependent parameters
+ * @scale:			scale * 10^-6
+ * @temp_output:		some devices have temperature sensors.
+ * @measurement_mode_freq:	normal mode sampling frequency
+ * @measurement_mode_3db_freq:	3db cutoff frequency of the low pass filter for
+ * the normal measurement mode.
+ * @option_mode_1:		first optional mode. Not all models have one
+ * @option_mode_1_freq:		option mode 1 sampling frequency
+ * @option_mode_1_3db_freq:	3db cutoff frequency of the low pass filter for
+ * the first option mode.
+ * @option_mode_2:		second optional mode. Not all chips have one
+ * @option_mode_2_freq:		option mode 2 sampling frequency
+ * @option_mode_2_3db_freq:	3db cutoff frequency of the low pass filter for
+ * the second option mode.
+ * @mod_det_mult_xz:		Bit wise multipliers to calculate the threshold
+ * for motion detection in the x and z axis.
+ * @mod_det_mult_y:		Bit wise multipliers to calculate the threshold
+ * for motion detection in the y axis.
+ *
+ * This structure is used to hold information about the functionality of a given
+ * sca3000 variant.
+ **/
+struct sca3000_chip_info {
+	unsigned int		scale;
+	bool			temp_output;
+	int			measurement_mode_freq;
+	int			measurement_mode_3db_freq;
+	int			option_mode_1;
+	int			option_mode_1_freq;
+	int			option_mode_1_3db_freq;
+	int			option_mode_2;
+	int			option_mode_2_freq;
+	int			option_mode_2_3db_freq;
+	int			mot_det_mult_xz[6];
+	int			mot_det_mult_y[7];
+};
+
+enum sca3000_variant {
+	d01,
+	e02,
+	e04,
+	e05,
+};
+
+/*
+ * Note where option modes are not defined, the chip simply does not
+ * support any.
+ * Other chips in the sca3000 series use i2c and are not included here.
+ *
+ * Some of these devices are only listed in the family data sheet and
+ * do not actually appear to be available.
+ */
+static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = {
+	[d01] = {
+		.scale = 7357,
+		.temp_output = true,
+		.measurement_mode_freq = 250,
+		.measurement_mode_3db_freq = 45,
+		.option_mode_1 = SCA3000_OP_MODE_BYPASS,
+		.option_mode_1_freq = 250,
+		.option_mode_1_3db_freq = 70,
+		.mot_det_mult_xz = {50, 100, 200, 350, 650, 1300},
+		.mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750},
+	},
+	[e02] = {
+		.scale = 9810,
+		.measurement_mode_freq = 125,
+		.measurement_mode_3db_freq = 40,
+		.option_mode_1 = SCA3000_OP_MODE_NARROW,
+		.option_mode_1_freq = 63,
+		.option_mode_1_3db_freq = 11,
+		.mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050},
+		.mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700},
+	},
+	[e04] = {
+		.scale = 19620,
+		.measurement_mode_freq = 100,
+		.measurement_mode_3db_freq = 38,
+		.option_mode_1 = SCA3000_OP_MODE_NARROW,
+		.option_mode_1_freq = 50,
+		.option_mode_1_3db_freq = 9,
+		.option_mode_2 = SCA3000_OP_MODE_WIDE,
+		.option_mode_2_freq = 400,
+		.option_mode_2_3db_freq = 70,
+		.mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100},
+		.mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000},
+	},
+	[e05] = {
+		.scale = 61313,
+		.measurement_mode_freq = 200,
+		.measurement_mode_3db_freq = 60,
+		.option_mode_1 = SCA3000_OP_MODE_NARROW,
+		.option_mode_1_freq = 50,
+		.option_mode_1_3db_freq = 9,
+		.option_mode_2 = SCA3000_OP_MODE_WIDE,
+		.option_mode_2_freq = 400,
+		.option_mode_2_3db_freq = 75,
+		.mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900},
+		.mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600},
+	},
+};
+
+static int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val)
+{
+	st->tx[0] = SCA3000_WRITE_REG(address);
+	st->tx[1] = val;
+	return spi_write(st->us, st->tx, 2);
+}
+
+static int sca3000_read_data_short(struct sca3000_state *st,
+				   u8 reg_address_high,
+				   int len)
+{
+	struct spi_transfer xfer[2] = {
+		{
+			.len = 1,
+			.tx_buf = st->tx,
+		}, {
+			.len = len,
+			.rx_buf = st->rx,
+		}
+	};
+	st->tx[0] = SCA3000_READ_REG(reg_address_high);
+
+	return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_reg_lock_on() - test if the ctrl register lock is on
+ * @st: Driver specific device instance data.
+ *
+ * Lock must be held.
+ **/
+static int sca3000_reg_lock_on(struct sca3000_state *st)
+{
+	int ret;
+
+	ret = sca3000_read_data_short(st, SCA3000_REG_STATUS_ADDR, 1);
+	if (ret < 0)
+		return ret;
+
+	return !(st->rx[0] & SCA3000_LOCKED);
+}
+
+/**
+ * __sca3000_unlock_reg_lock() - unlock the control registers
+ * @st: Driver specific device instance data.
+ *
+ * Note the device does not appear to support doing this in a single transfer.
+ * This should only ever be used as part of ctrl reg read.
+ * Lock must be held before calling this
+ */
+static int __sca3000_unlock_reg_lock(struct sca3000_state *st)
+{
+	struct spi_transfer xfer[3] = {
+		{
+			.len = 2,
+			.cs_change = 1,
+			.tx_buf = st->tx,
+		}, {
+			.len = 2,
+			.cs_change = 1,
+			.tx_buf = st->tx + 2,
+		}, {
+			.len = 2,
+			.tx_buf = st->tx + 4,
+		},
+	};
+	st->tx[0] = SCA3000_WRITE_REG(SCA3000_REG_UNLOCK_ADDR);
+	st->tx[1] = 0x00;
+	st->tx[2] = SCA3000_WRITE_REG(SCA3000_REG_UNLOCK_ADDR);
+	st->tx[3] = 0x50;
+	st->tx[4] = SCA3000_WRITE_REG(SCA3000_REG_UNLOCK_ADDR);
+	st->tx[5] = 0xA0;
+
+	return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_write_ctrl_reg() write to a lock protect ctrl register
+ * @st: Driver specific device instance data.
+ * @sel: selects which registers we wish to write to
+ * @val: the value to be written
+ *
+ * Certain control registers are protected against overwriting by the lock
+ * register and use a shared write address. This function allows writing of
+ * these registers.
+ * Lock must be held.
+ */
+static int sca3000_write_ctrl_reg(struct sca3000_state *st,
+				  u8 sel,
+				  uint8_t val)
+{
+	int ret;
+
+	ret = sca3000_reg_lock_on(st);
+	if (ret < 0)
+		goto error_ret;
+	if (ret) {
+		ret = __sca3000_unlock_reg_lock(st);
+		if (ret)
+			goto error_ret;
+	}
+
+	/* Set the control select register */
+	ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, sel);
+	if (ret)
+		goto error_ret;
+
+	/* Write the actual value into the register */
+	ret = sca3000_write_reg(st, SCA3000_REG_CTRL_DATA_ADDR, val);
+
+error_ret:
+	return ret;
+}
+
+/**
+ * sca3000_read_ctrl_reg() read from lock protected control register.
+ * @st: Driver specific device instance data.
+ * @ctrl_reg: Which ctrl register do we want to read.
+ *
+ * Lock must be held.
+ */
+static int sca3000_read_ctrl_reg(struct sca3000_state *st,
+				 u8 ctrl_reg)
+{
+	int ret;
+
+	ret = sca3000_reg_lock_on(st);
+	if (ret < 0)
+		goto error_ret;
+	if (ret) {
+		ret = __sca3000_unlock_reg_lock(st);
+		if (ret)
+			goto error_ret;
+	}
+	/* Set the control select register */
+	ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, ctrl_reg);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_read_data_short(st, SCA3000_REG_CTRL_DATA_ADDR, 1);
+	if (ret)
+		goto error_ret;
+	return st->rx[0];
+error_ret:
+	return ret;
+}
+
+/**
+ * sca3000_show_rev() - sysfs interface to read the chip revision number
+ * @indio_dev: Device instance specific generic IIO data.
+ * Driver specific device instance data can be obtained via
+ * via iio_priv(indio_dev)
+ */
+static int sca3000_print_rev(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct sca3000_state *st = iio_priv(indio_dev);
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data_short(st, SCA3000_REG_REVID_ADDR, 1);
+	if (ret < 0)
+		goto error_ret;
+	dev_info(&indio_dev->dev,
+		 "sca3000 revision major=%lu, minor=%lu\n",
+		 st->rx[0] & SCA3000_REG_REVID_MAJOR_MASK,
+		 st->rx[0] & SCA3000_REG_REVID_MINOR_MASK);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static ssize_t
+sca3000_show_available_3db_freqs(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int len;
+
+	len = sprintf(buf, "%d", st->info->measurement_mode_3db_freq);
+	if (st->info->option_mode_1)
+		len += sprintf(buf + len, " %d",
+			       st->info->option_mode_1_3db_freq);
+	if (st->info->option_mode_2)
+		len += sprintf(buf + len, " %d",
+			       st->info->option_mode_2_3db_freq);
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available,
+		       S_IRUGO, sca3000_show_available_3db_freqs,
+		       NULL, 0);
+
+static const struct iio_event_spec sca3000_event = {
+	.type = IIO_EV_TYPE_MAG,
+	.dir = IIO_EV_DIR_RISING,
+	.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+};
+
+/*
+ * Note the hack in the number of bits to pretend we have 2 more than
+ * we do in the fifo.
+ */
+#define SCA3000_CHAN(index, mod)				\
+	{							\
+		.type = IIO_ACCEL,				\
+		.modified = 1,					\
+		.channel2 = mod,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |\
+			BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),\
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+		.address = index,				\
+		.scan_index = index,				\
+		.scan_type = {					\
+			.sign = 's',				\
+			.realbits = 13,				\
+			.storagebits = 16,			\
+			.shift = 3,				\
+			.endianness = IIO_BE,			\
+		},						\
+		.event_spec = &sca3000_event,			\
+		.num_event_specs = 1,				\
+	}
+
+static const struct iio_event_spec sca3000_freefall_event_spec = {
+	.type = IIO_EV_TYPE_MAG,
+	.dir = IIO_EV_DIR_FALLING,
+	.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+		BIT(IIO_EV_INFO_PERIOD),
+};
+
+static const struct iio_chan_spec sca3000_channels[] = {
+	SCA3000_CHAN(0, IIO_MOD_X),
+	SCA3000_CHAN(1, IIO_MOD_Y),
+	SCA3000_CHAN(2, IIO_MOD_Z),
+	{
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X_AND_Y_AND_Z,
+		.scan_index = -1, /* Fake channel */
+		.event_spec = &sca3000_freefall_event_spec,
+		.num_event_specs = 1,
+	},
+};
+
+static const struct iio_chan_spec sca3000_channels_with_temp[] = {
+	SCA3000_CHAN(0, IIO_MOD_X),
+	SCA3000_CHAN(1, IIO_MOD_Y),
+	SCA3000_CHAN(2, IIO_MOD_Z),
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+			BIT(IIO_CHAN_INFO_OFFSET),
+		/* No buffer support */
+		.scan_index = -1,
+	},
+	{
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X_AND_Y_AND_Z,
+		.scan_index = -1, /* Fake channel */
+		.event_spec = &sca3000_freefall_event_spec,
+		.num_event_specs = 1,
+	},
+};
+
+static u8 sca3000_addresses[3][3] = {
+	[0] = {SCA3000_REG_X_MSB_ADDR, SCA3000_REG_CTRL_SEL_MD_X_TH,
+	       SCA3000_MD_CTRL_OR_X},
+	[1] = {SCA3000_REG_Y_MSB_ADDR, SCA3000_REG_CTRL_SEL_MD_Y_TH,
+	       SCA3000_MD_CTRL_OR_Y},
+	[2] = {SCA3000_REG_Z_MSB_ADDR, SCA3000_REG_CTRL_SEL_MD_Z_TH,
+	       SCA3000_MD_CTRL_OR_Z},
+};
+
+/**
+ * __sca3000_get_base_freq() - obtain mode specific base frequency
+ * @st: Private driver specific device instance specific state.
+ * @info: chip type specific information.
+ * @base_freq: Base frequency for the current measurement mode.
+ *
+ * lock must be held
+ */
+static inline int __sca3000_get_base_freq(struct sca3000_state *st,
+					  const struct sca3000_chip_info *info,
+					  int *base_freq)
+{
+	int ret;
+
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		goto error_ret;
+	switch (SCA3000_REG_MODE_MODE_MASK & st->rx[0]) {
+	case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
+		*base_freq = info->measurement_mode_freq;
+		break;
+	case SCA3000_REG_MODE_MEAS_MODE_OP_1:
+		*base_freq = info->option_mode_1_freq;
+		break;
+	case SCA3000_REG_MODE_MEAS_MODE_OP_2:
+		*base_freq = info->option_mode_2_freq;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+error_ret:
+	return ret;
+}
+
+/**
+ * sca3000_read_raw_samp_freq() - read_raw handler for IIO_CHAN_INFO_SAMP_FREQ
+ * @st: Private driver specific device instance specific state.
+ * @val: The frequency read back.
+ *
+ * lock must be held
+ **/
+static int sca3000_read_raw_samp_freq(struct sca3000_state *st, int *val)
+{
+	int ret;
+
+	ret = __sca3000_get_base_freq(st, st->info, val);
+	if (ret)
+		return ret;
+
+	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+	if (ret < 0)
+		return ret;
+
+	if (*val > 0) {
+		ret &= SCA3000_REG_OUT_CTRL_BUF_DIV_MASK;
+		switch (ret) {
+		case SCA3000_REG_OUT_CTRL_BUF_DIV_2:
+			*val /= 2;
+			break;
+		case SCA3000_REG_OUT_CTRL_BUF_DIV_4:
+			*val /= 4;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * sca3000_write_raw_samp_freq() - write_raw handler for IIO_CHAN_INFO_SAMP_FREQ
+ * @st: Private driver specific device instance specific state.
+ * @val: The frequency desired.
+ *
+ * lock must be held
+ */
+static int sca3000_write_raw_samp_freq(struct sca3000_state *st, int val)
+{
+	int ret, base_freq, ctrlval;
+
+	ret = __sca3000_get_base_freq(st, st->info, &base_freq);
+	if (ret)
+		return ret;
+
+	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+	if (ret < 0)
+		return ret;
+
+	ctrlval = ret & ~SCA3000_REG_OUT_CTRL_BUF_DIV_MASK;
+
+	if (val == base_freq / 2)
+		ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_2;
+	if (val == base_freq / 4)
+		ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_4;
+	else if (val != base_freq)
+		return -EINVAL;
+
+	return sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+				     ctrlval);
+}
+
+static int sca3000_read_3db_freq(struct sca3000_state *st, int *val)
+{
+	int ret;
+
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		return ret;
+
+	/* mask bottom 2 bits - only ones that are relevant */
+	st->rx[0] &= SCA3000_REG_MODE_MODE_MASK;
+	switch (st->rx[0]) {
+	case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
+		*val = st->info->measurement_mode_3db_freq;
+		return IIO_VAL_INT;
+	case SCA3000_REG_MODE_MEAS_MODE_MOT_DET:
+		return -EBUSY;
+	case SCA3000_REG_MODE_MEAS_MODE_OP_1:
+		*val = st->info->option_mode_1_3db_freq;
+		return IIO_VAL_INT;
+	case SCA3000_REG_MODE_MEAS_MODE_OP_2:
+		*val = st->info->option_mode_2_3db_freq;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int sca3000_write_3db_freq(struct sca3000_state *st, int val)
+{
+	int ret;
+	int mode;
+
+	if (val == st->info->measurement_mode_3db_freq)
+		mode = SCA3000_REG_MODE_MEAS_MODE_NORMAL;
+	else if (st->info->option_mode_1 &&
+		 (val == st->info->option_mode_1_3db_freq))
+		mode = SCA3000_REG_MODE_MEAS_MODE_OP_1;
+	else if (st->info->option_mode_2 &&
+		 (val == st->info->option_mode_2_3db_freq))
+		mode = SCA3000_REG_MODE_MEAS_MODE_OP_2;
+	else
+		return -EINVAL;
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		return ret;
+
+	st->rx[0] &= ~SCA3000_REG_MODE_MODE_MASK;
+	st->rx[0] |= (mode & SCA3000_REG_MODE_MODE_MASK);
+
+	return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR, st->rx[0]);
+}
+
+static int sca3000_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long mask)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret;
+	u8 address;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&st->lock);
+		if (chan->type == IIO_ACCEL) {
+			if (st->mo_det_use_count) {
+				mutex_unlock(&st->lock);
+				return -EBUSY;
+			}
+			address = sca3000_addresses[chan->address][0];
+			ret = sca3000_read_data_short(st, address, 2);
+			if (ret < 0) {
+				mutex_unlock(&st->lock);
+				return ret;
+			}
+			*val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF;
+			*val = ((*val) << (sizeof(*val) * 8 - 13)) >>
+				(sizeof(*val) * 8 - 13);
+		} else {
+			/* get the temperature when available */
+			ret = sca3000_read_data_short(st,
+						      SCA3000_REG_TEMP_MSB_ADDR,
+						      2);
+			if (ret < 0) {
+				mutex_unlock(&st->lock);
+				return ret;
+			}
+			*val = ((st->rx[0] & 0x3F) << 3) |
+			       ((st->rx[1] & 0xE0) >> 5);
+		}
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		if (chan->type == IIO_ACCEL)
+			*val2 = st->info->scale;
+		else /* temperature */
+			*val2 = 555556;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = -214;
+		*val2 = 600000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		mutex_lock(&st->lock);
+		ret = sca3000_read_raw_samp_freq(st, val);
+		mutex_unlock(&st->lock);
+		return ret ? ret : IIO_VAL_INT;
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		mutex_lock(&st->lock);
+		ret = sca3000_read_3db_freq(st, val);
+		mutex_unlock(&st->lock);
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int sca3000_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (val2)
+			return -EINVAL;
+		mutex_lock(&st->lock);
+		ret = sca3000_write_raw_samp_freq(st, val);
+		mutex_unlock(&st->lock);
+		return ret;
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		if (val2)
+			return -EINVAL;
+		mutex_lock(&st->lock);
+		ret = sca3000_write_3db_freq(st, val);
+		mutex_unlock(&st->lock);
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * sca3000_read_av_freq() - sysfs function to get available frequencies
+ * @dev: Device structure for this device.
+ * @attr: Description of the attribute.
+ * @buf: Incoming string
+ *
+ * The later modes are only relevant to the ring buffer - and depend on current
+ * mode. Note that data sheet gives rather wide tolerances for these so integer
+ * division will give good enough answer and not all chips have them specified
+ * at all.
+ **/
+static ssize_t sca3000_read_av_freq(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int len = 0, ret, val;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	val = st->rx[0];
+	mutex_unlock(&st->lock);
+	if (ret)
+		goto error_ret;
+
+	switch (val & SCA3000_REG_MODE_MODE_MASK) {
+	case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
+		len += sprintf(buf + len, "%d %d %d\n",
+			       st->info->measurement_mode_freq,
+			       st->info->measurement_mode_freq / 2,
+			       st->info->measurement_mode_freq / 4);
+		break;
+	case SCA3000_REG_MODE_MEAS_MODE_OP_1:
+		len += sprintf(buf + len, "%d %d %d\n",
+			       st->info->option_mode_1_freq,
+			       st->info->option_mode_1_freq / 2,
+			       st->info->option_mode_1_freq / 4);
+		break;
+	case SCA3000_REG_MODE_MEAS_MODE_OP_2:
+		len += sprintf(buf + len, "%d %d %d\n",
+			       st->info->option_mode_2_freq,
+			       st->info->option_mode_2_freq / 2,
+			       st->info->option_mode_2_freq / 4);
+		break;
+	}
+	return len;
+error_ret:
+	return ret;
+}
+
+/*
+ * Should only really be registered if ring buffer support is compiled in.
+ * Does no harm however and doing it right would add a fair bit of complexity
+ */
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq);
+
+/**
+ * sca3000_read_event_value() - query of a threshold or period
+ **/
+static int sca3000_read_event_value(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan,
+				    enum iio_event_type type,
+				    enum iio_event_direction dir,
+				    enum iio_event_info info,
+				    int *val, int *val2)
+{
+	int ret, i;
+	struct sca3000_state *st = iio_priv(indio_dev);
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		mutex_lock(&st->lock);
+		ret = sca3000_read_ctrl_reg(st,
+					    sca3000_addresses[chan->address][1]);
+		mutex_unlock(&st->lock);
+		if (ret < 0)
+			return ret;
+		*val = 0;
+		if (chan->channel2 == IIO_MOD_Y)
+			for_each_set_bit(i, (unsigned long *)&ret,
+					 ARRAY_SIZE(st->info->mot_det_mult_y))
+				*val += st->info->mot_det_mult_y[i];
+		else
+			for_each_set_bit(i, (unsigned long *)&ret,
+					 ARRAY_SIZE(st->info->mot_det_mult_xz))
+				*val += st->info->mot_det_mult_xz[i];
+
+		return IIO_VAL_INT;
+	case IIO_EV_INFO_PERIOD:
+		*val = 0;
+		*val2 = 226000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * sca3000_write_value() - control of threshold and period
+ * @indio_dev: Device instance specific IIO information.
+ * @chan: Description of the channel for which the event is being
+ * configured.
+ * @type: The type of event being configured, here magnitude rising
+ * as everything else is read only.
+ * @dir: Direction of the event (here rising)
+ * @info: What information about the event are we configuring.
+ * Here the threshold only.
+ * @val: Integer part of the value being written..
+ * @val2: Non integer part of the value being written. Here always 0.
+ */
+static int sca3000_write_event_value(struct iio_dev *indio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir,
+				     enum iio_event_info info,
+				     int val, int val2)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret;
+	int i;
+	u8 nonlinear = 0;
+
+	if (chan->channel2 == IIO_MOD_Y) {
+		i = ARRAY_SIZE(st->info->mot_det_mult_y);
+		while (i > 0)
+			if (val >= st->info->mot_det_mult_y[--i]) {
+				nonlinear |= (1 << i);
+				val -= st->info->mot_det_mult_y[i];
+			}
+	} else {
+		i = ARRAY_SIZE(st->info->mot_det_mult_xz);
+		while (i > 0)
+			if (val >= st->info->mot_det_mult_xz[--i]) {
+				nonlinear |= (1 << i);
+				val -= st->info->mot_det_mult_xz[i];
+			}
+	}
+
+	mutex_lock(&st->lock);
+	ret = sca3000_write_ctrl_reg(st,
+				     sca3000_addresses[chan->address][1],
+				     nonlinear);
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static struct attribute *sca3000_attributes[] = {
+	&iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group sca3000_attribute_group = {
+	.attrs = sca3000_attributes,
+};
+
+static int sca3000_read_data(struct sca3000_state *st,
+			     u8 reg_address_high,
+			     u8 *rx,
+			     int len)
+{
+	int ret;
+	struct spi_transfer xfer[2] = {
+		{
+			.len = 1,
+			.tx_buf = st->tx,
+		}, {
+			.len = len,
+			.rx_buf = rx,
+		}
+	};
+
+	st->tx[0] = SCA3000_READ_REG(reg_address_high);
+	ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+	if (ret) {
+		dev_err(get_device(&st->us->dev), "problem reading register");
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * sca3000_ring_int_process() - ring specific interrupt handling.
+ * @val: Value of the interrupt status register.
+ * @indio_dev: Device instance specific IIO device structure.
+ */
+static void sca3000_ring_int_process(u8 val, struct iio_dev *indio_dev)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret, i, num_available;
+
+	mutex_lock(&st->lock);
+
+	if (val & SCA3000_REG_INT_STATUS_HALF) {
+		ret = sca3000_read_data_short(st, SCA3000_REG_BUF_COUNT_ADDR,
+					      1);
+		if (ret)
+			goto error_ret;
+		num_available = st->rx[0];
+		/*
+		 * num_available is the total number of samples available
+		 * i.e. number of time points * number of channels.
+		 */
+		ret = sca3000_read_data(st, SCA3000_REG_RING_OUT_ADDR, st->rx,
+					num_available * 2);
+		if (ret)
+			goto error_ret;
+		for (i = 0; i < num_available / 3; i++) {
+			/*
+			 * Dirty hack to cover for 11 bit in fifo, 13 bit
+			 * direct reading.
+			 *
+			 * In theory the bottom two bits are undefined.
+			 * In reality they appear to always be 0.
+			 */
+			iio_push_to_buffers(indio_dev, st->rx + i * 3 * 2);
+		}
+	}
+error_ret:
+	mutex_unlock(&st->lock);
+}
+
+/**
+ * sca3000_event_handler() - handling ring and non ring events
+ * @irq: The irq being handled.
+ * @private: struct iio_device pointer for the device.
+ *
+ * Ring related interrupt handler. Depending on event, push to
+ * the ring buffer event chrdev or the event one.
+ *
+ * This function is complicated by the fact that the devices can signify ring
+ * and non ring events via the same interrupt line and they can only
+ * be distinguished via a read of the relevant status register.
+ */
+static irqreturn_t sca3000_event_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret, val;
+	s64 last_timestamp = iio_get_time_ns(indio_dev);
+
+	/*
+	 * Could lead if badly timed to an extra read of status reg,
+	 * but ensures no interrupt is missed.
+	 */
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data_short(st, SCA3000_REG_INT_STATUS_ADDR, 1);
+	val = st->rx[0];
+	mutex_unlock(&st->lock);
+	if (ret)
+		goto done;
+
+	sca3000_ring_int_process(val, indio_dev);
+
+	if (val & SCA3000_INT_STATUS_FREE_FALL)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+						  0,
+						  IIO_MOD_X_AND_Y_AND_Z,
+						  IIO_EV_TYPE_MAG,
+						  IIO_EV_DIR_FALLING),
+			       last_timestamp);
+
+	if (val & SCA3000_INT_STATUS_Y_TRIGGER)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+						  0,
+						  IIO_MOD_Y,
+						  IIO_EV_TYPE_MAG,
+						  IIO_EV_DIR_RISING),
+			       last_timestamp);
+
+	if (val & SCA3000_INT_STATUS_X_TRIGGER)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+						  0,
+						  IIO_MOD_X,
+						  IIO_EV_TYPE_MAG,
+						  IIO_EV_DIR_RISING),
+			       last_timestamp);
+
+	if (val & SCA3000_INT_STATUS_Z_TRIGGER)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+						  0,
+						  IIO_MOD_Z,
+						  IIO_EV_TYPE_MAG,
+						  IIO_EV_DIR_RISING),
+			       last_timestamp);
+
+done:
+	return IRQ_HANDLED;
+}
+
+/**
+ * sca3000_read_event_config() what events are enabled
+ **/
+static int sca3000_read_event_config(struct iio_dev *indio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret;
+	/* read current value of mode register */
+	mutex_lock(&st->lock);
+
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		goto error_ret;
+
+	switch (chan->channel2) {
+	case IIO_MOD_X_AND_Y_AND_Z:
+		ret = !!(st->rx[0] & SCA3000_REG_MODE_FREE_FALL_DETECT);
+		break;
+	case IIO_MOD_X:
+	case IIO_MOD_Y:
+	case IIO_MOD_Z:
+		/*
+		 * Motion detection mode cannot run at the same time as
+		 * acceleration data being read.
+		 */
+		if ((st->rx[0] & SCA3000_REG_MODE_MODE_MASK)
+		    != SCA3000_REG_MODE_MEAS_MODE_MOT_DET) {
+			ret = 0;
+		} else {
+			ret = sca3000_read_ctrl_reg(st,
+						SCA3000_REG_CTRL_SEL_MD_CTRL);
+			if (ret < 0)
+				goto error_ret;
+			/* only supporting logical or's for now */
+			ret = !!(ret & sca3000_addresses[chan->address][2]);
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int sca3000_freefall_set_state(struct iio_dev *indio_dev, int state)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret;
+
+	/* read current value of mode register */
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		return ret;
+
+	/* if off and should be on */
+	if (state && !(st->rx[0] & SCA3000_REG_MODE_FREE_FALL_DETECT))
+		return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+					 st->rx[0] | SCA3000_REG_MODE_FREE_FALL_DETECT);
+	/* if on and should be off */
+	else if (!state && (st->rx[0] & SCA3000_REG_MODE_FREE_FALL_DETECT))
+		return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+					 st->rx[0] & ~SCA3000_REG_MODE_FREE_FALL_DETECT);
+	else
+		return 0;
+}
+
+static int sca3000_motion_detect_set_state(struct iio_dev *indio_dev, int axis,
+					   int state)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret, ctrlval;
+
+	/*
+	 * First read the motion detector config to find out if
+	 * this axis is on
+	 */
+	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+	if (ret < 0)
+		return ret;
+	ctrlval = ret;
+	/* if off and should be on */
+	if (state && !(ctrlval & sca3000_addresses[axis][2])) {
+		ret = sca3000_write_ctrl_reg(st,
+					     SCA3000_REG_CTRL_SEL_MD_CTRL,
+					     ctrlval |
+					     sca3000_addresses[axis][2]);
+		if (ret)
+			return ret;
+		st->mo_det_use_count++;
+	} else if (!state && (ctrlval & sca3000_addresses[axis][2])) {
+		ret = sca3000_write_ctrl_reg(st,
+					     SCA3000_REG_CTRL_SEL_MD_CTRL,
+					     ctrlval &
+					     ~(sca3000_addresses[axis][2]));
+		if (ret)
+			return ret;
+		st->mo_det_use_count--;
+	}
+
+	/* read current value of mode register */
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		return ret;
+	/* if off and should be on */
+	if ((st->mo_det_use_count) &&
+	    ((st->rx[0] & SCA3000_REG_MODE_MODE_MASK)
+	     != SCA3000_REG_MODE_MEAS_MODE_MOT_DET))
+		return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+			(st->rx[0] & ~SCA3000_REG_MODE_MODE_MASK)
+			| SCA3000_REG_MODE_MEAS_MODE_MOT_DET);
+	/* if on and should be off */
+	else if (!(st->mo_det_use_count) &&
+		 ((st->rx[0] & SCA3000_REG_MODE_MODE_MASK)
+		  == SCA3000_REG_MODE_MEAS_MODE_MOT_DET))
+		return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+			st->rx[0] & SCA3000_REG_MODE_MODE_MASK);
+	else
+		return 0;
+}
+
+/**
+ * sca3000_write_event_config() - simple on off control for motion detector
+ * @indio_dev: IIO device instance specific structure. Data specific to this
+ * particular driver may be accessed via iio_priv(indio_dev).
+ * @chan: Description of the channel whose event we are configuring.
+ * @type: The type of event.
+ * @dir: The direction of the event.
+ * @state: Desired state of event being configured.
+ *
+ * This is a per axis control, but enabling any will result in the
+ * motion detector unit being enabled.
+ * N.B. enabling motion detector stops normal data acquisition.
+ * There is a complexity in knowing which mode to return to when
+ * this mode is disabled.  Currently normal mode is assumed.
+ **/
+static int sca3000_write_event_config(struct iio_dev *indio_dev,
+				      const struct iio_chan_spec *chan,
+				      enum iio_event_type type,
+				      enum iio_event_direction dir,
+				      int state)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&st->lock);
+	switch (chan->channel2) {
+	case IIO_MOD_X_AND_Y_AND_Z:
+		ret = sca3000_freefall_set_state(indio_dev, state);
+		break;
+
+	case IIO_MOD_X:
+	case IIO_MOD_Y:
+	case IIO_MOD_Z:
+		ret = sca3000_motion_detect_set_state(indio_dev,
+						      chan->address,
+						      state);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int sca3000_configure_ring(struct iio_dev *indio_dev)
+{
+	struct iio_buffer *buffer;
+
+	buffer = iio_kfifo_allocate();
+	if (!buffer)
+		return -ENOMEM;
+
+	iio_device_attach_buffer(indio_dev, buffer);
+	indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+
+	return 0;
+}
+
+static void sca3000_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	iio_kfifo_free(indio_dev->buffer);
+}
+
+static inline
+int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
+{
+	struct sca3000_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		goto error_ret;
+	if (state) {
+		dev_info(&indio_dev->dev, "supposedly enabling ring buffer\n");
+		ret = sca3000_write_reg(st,
+			SCA3000_REG_MODE_ADDR,
+			(st->rx[0] | SCA3000_REG_MODE_RING_BUF_ENABLE));
+	} else
+		ret = sca3000_write_reg(st,
+			SCA3000_REG_MODE_ADDR,
+			(st->rx[0] & ~SCA3000_REG_MODE_RING_BUF_ENABLE));
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+/**
+ * sca3000_hw_ring_preenable() - hw ring buffer preenable function
+ * @indio_dev: structure representing the IIO device. Device instance
+ * specific state can be accessed via iio_priv(indio_dev).
+ *
+ * Very simple enable function as the chip will allows normal reads
+ * during ring buffer operation so as long as it is indeed running
+ * before we notify the core, the precise ordering does not matter.
+ */
+static int sca3000_hw_ring_preenable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct sca3000_state *st = iio_priv(indio_dev);
+
+	mutex_lock(&st->lock);
+
+	/* Enable the 50% full interrupt */
+	ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+	if (ret)
+		goto error_unlock;
+	ret = sca3000_write_reg(st,
+				SCA3000_REG_INT_MASK_ADDR,
+				st->rx[0] | SCA3000_REG_INT_MASK_RING_HALF);
+	if (ret)
+		goto error_unlock;
+
+	mutex_unlock(&st->lock);
+
+	return __sca3000_hw_ring_state_set(indio_dev, 1);
+
+error_unlock:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct sca3000_state *st = iio_priv(indio_dev);
+
+	ret = __sca3000_hw_ring_state_set(indio_dev, 0);
+	if (ret)
+		return ret;
+
+	/* Disable the 50% full interrupt */
+	mutex_lock(&st->lock);
+
+	ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+	if (ret)
+		goto unlock;
+	ret = sca3000_write_reg(st,
+				SCA3000_REG_INT_MASK_ADDR,
+				st->rx[0] & ~SCA3000_REG_INT_MASK_RING_HALF);
+unlock:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops sca3000_ring_setup_ops = {
+	.preenable = &sca3000_hw_ring_preenable,
+	.postdisable = &sca3000_hw_ring_postdisable,
+};
+
+/**
+ * sca3000_clean_setup() - get the device into a predictable state
+ * @st: Device instance specific private data structure
+ *
+ * Devices use flash memory to store many of the register values
+ * and hence can come up in somewhat unpredictable states.
+ * Hence reset everything on driver load.
+ */
+static int sca3000_clean_setup(struct sca3000_state *st)
+{
+	int ret;
+
+	mutex_lock(&st->lock);
+	/* Ensure all interrupts have been acknowledged */
+	ret = sca3000_read_data_short(st, SCA3000_REG_INT_STATUS_ADDR, 1);
+	if (ret)
+		goto error_ret;
+
+	/* Turn off all motion detection channels */
+	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+	if (ret < 0)
+		goto error_ret;
+	ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL,
+				     ret & SCA3000_MD_CTRL_PROT_MASK);
+	if (ret)
+		goto error_ret;
+
+	/* Disable ring buffer */
+	ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+	if (ret < 0)
+		goto error_ret;
+	ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+				     (ret & SCA3000_REG_OUT_CTRL_PROT_MASK)
+				     | SCA3000_REG_OUT_CTRL_BUF_X_EN
+				     | SCA3000_REG_OUT_CTRL_BUF_Y_EN
+				     | SCA3000_REG_OUT_CTRL_BUF_Z_EN
+				     | SCA3000_REG_OUT_CTRL_BUF_DIV_4);
+	if (ret)
+		goto error_ret;
+	/* Enable interrupts, relevant to mode and set up as active low */
+	ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_write_reg(st,
+				SCA3000_REG_INT_MASK_ADDR,
+				(ret & SCA3000_REG_INT_MASK_PROT_MASK)
+				| SCA3000_REG_INT_MASK_ACTIVE_LOW);
+	if (ret)
+		goto error_ret;
+	/*
+	 * Select normal measurement mode, free fall off, ring off
+	 * Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5
+	 * as that occurs in one of the example on the datasheet
+	 */
+	ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+				(st->rx[0] & SCA3000_MODE_PROT_MASK));
+
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+static const struct iio_info sca3000_info = {
+	.attrs = &sca3000_attribute_group,
+	.read_raw = &sca3000_read_raw,
+	.write_raw = &sca3000_write_raw,
+	.read_event_value = &sca3000_read_event_value,
+	.write_event_value = &sca3000_write_event_value,
+	.read_event_config = &sca3000_read_event_config,
+	.write_event_config = &sca3000_write_event_config,
+	.driver_module = THIS_MODULE,
+};
+
+static int sca3000_probe(struct spi_device *spi)
+{
+	int ret;
+	struct sca3000_state *st;
+	struct iio_dev *indio_dev;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	spi_set_drvdata(spi, indio_dev);
+	st->us = spi;
+	mutex_init(&st->lock);
+	st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi)
+					      ->driver_data];
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->info = &sca3000_info;
+	if (st->info->temp_output) {
+		indio_dev->channels = sca3000_channels_with_temp;
+		indio_dev->num_channels =
+			ARRAY_SIZE(sca3000_channels_with_temp);
+	} else {
+		indio_dev->channels = sca3000_channels;
+		indio_dev->num_channels = ARRAY_SIZE(sca3000_channels);
+	}
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	sca3000_configure_ring(indio_dev);
+
+	if (spi->irq) {
+		ret = request_threaded_irq(spi->irq,
+					   NULL,
+					   &sca3000_event_handler,
+					   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					   "sca3000",
+					   indio_dev);
+		if (ret)
+			return ret;
+	}
+	indio_dev->setup_ops = &sca3000_ring_setup_ops;
+	ret = sca3000_clean_setup(st);
+	if (ret)
+		goto error_free_irq;
+
+	ret = sca3000_print_rev(indio_dev);
+	if (ret)
+		goto error_free_irq;
+
+	return iio_device_register(indio_dev);
+
+error_free_irq:
+	if (spi->irq)
+		free_irq(spi->irq, indio_dev);
+
+	return ret;
+}
+
+static int sca3000_stop_all_interrupts(struct sca3000_state *st)
+{
+	int ret;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR,
+				(st->rx[0] &
+				 ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER |
+				   SCA3000_REG_INT_MASK_RING_HALF |
+				   SCA3000_REG_INT_MASK_ALL_INTS)));
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+static int sca3000_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct sca3000_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	/* Must ensure no interrupts can be generated after this! */
+	sca3000_stop_all_interrupts(st);
+	if (spi->irq)
+		free_irq(spi->irq, indio_dev);
+
+	sca3000_unconfigure_ring(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id sca3000_id[] = {
+	{"sca3000_d01", d01},
+	{"sca3000_e02", e02},
+	{"sca3000_e04", e04},
+	{"sca3000_e05", e05},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, sca3000_id);
+
+static struct spi_driver sca3000_driver = {
+	.driver = {
+		.name = "sca3000",
+	},
+	.probe = sca3000_probe,
+	.remove = sca3000_remove,
+	.id_table = sca3000_id,
+};
+module_spi_driver(sca3000_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");
+MODULE_LICENSE("GPL v2");

+ 1 - 0
drivers/iio/accel/st_accel.h

@@ -30,6 +30,7 @@
 #define LSM303AGR_ACCEL_DEV_NAME	"lsm303agr_accel"
 #define LIS2DH12_ACCEL_DEV_NAME		"lis2dh12_accel"
 #define LIS3L02DQ_ACCEL_DEV_NAME	"lis3l02dq"
+#define LNG2DM_ACCEL_DEV_NAME		"lng2dm"
 
 /**
 * struct st_sensors_platform_data - default accel platform data

+ 244 - 361
drivers/iio/accel/st_accel_core.c

@@ -43,194 +43,6 @@
 #define ST_ACCEL_FS_AVL_200G			200
 #define ST_ACCEL_FS_AVL_400G			400
 
-/* CUSTOM VALUES FOR SENSOR 1 */
-#define ST_ACCEL_1_WAI_EXP			0x33
-#define ST_ACCEL_1_ODR_ADDR			0x20
-#define ST_ACCEL_1_ODR_MASK			0xf0
-#define ST_ACCEL_1_ODR_AVL_1HZ_VAL		0x01
-#define ST_ACCEL_1_ODR_AVL_10HZ_VAL		0x02
-#define ST_ACCEL_1_ODR_AVL_25HZ_VAL		0x03
-#define ST_ACCEL_1_ODR_AVL_50HZ_VAL		0x04
-#define ST_ACCEL_1_ODR_AVL_100HZ_VAL		0x05
-#define ST_ACCEL_1_ODR_AVL_200HZ_VAL		0x06
-#define ST_ACCEL_1_ODR_AVL_400HZ_VAL		0x07
-#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL		0x08
-#define ST_ACCEL_1_FS_ADDR			0x23
-#define ST_ACCEL_1_FS_MASK			0x30
-#define ST_ACCEL_1_FS_AVL_2_VAL			0x00
-#define ST_ACCEL_1_FS_AVL_4_VAL			0x01
-#define ST_ACCEL_1_FS_AVL_8_VAL			0x02
-#define ST_ACCEL_1_FS_AVL_16_VAL		0x03
-#define ST_ACCEL_1_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
-#define ST_ACCEL_1_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
-#define ST_ACCEL_1_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(4000)
-#define ST_ACCEL_1_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(12000)
-#define ST_ACCEL_1_BDU_ADDR			0x23
-#define ST_ACCEL_1_BDU_MASK			0x80
-#define ST_ACCEL_1_DRDY_IRQ_ADDR		0x22
-#define ST_ACCEL_1_DRDY_IRQ_INT1_MASK		0x10
-#define ST_ACCEL_1_DRDY_IRQ_INT2_MASK		0x08
-#define ST_ACCEL_1_IHL_IRQ_ADDR			0x25
-#define ST_ACCEL_1_IHL_IRQ_MASK			0x02
-#define ST_ACCEL_1_MULTIREAD_BIT		true
-
-/* CUSTOM VALUES FOR SENSOR 2 */
-#define ST_ACCEL_2_WAI_EXP			0x32
-#define ST_ACCEL_2_ODR_ADDR			0x20
-#define ST_ACCEL_2_ODR_MASK			0x18
-#define ST_ACCEL_2_ODR_AVL_50HZ_VAL		0x00
-#define ST_ACCEL_2_ODR_AVL_100HZ_VAL		0x01
-#define ST_ACCEL_2_ODR_AVL_400HZ_VAL		0x02
-#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL		0x03
-#define ST_ACCEL_2_PW_ADDR			0x20
-#define ST_ACCEL_2_PW_MASK			0xe0
-#define ST_ACCEL_2_FS_ADDR			0x23
-#define ST_ACCEL_2_FS_MASK			0x30
-#define ST_ACCEL_2_FS_AVL_2_VAL			0X00
-#define ST_ACCEL_2_FS_AVL_4_VAL			0X01
-#define ST_ACCEL_2_FS_AVL_8_VAL			0x03
-#define ST_ACCEL_2_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
-#define ST_ACCEL_2_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
-#define ST_ACCEL_2_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(3900)
-#define ST_ACCEL_2_BDU_ADDR			0x23
-#define ST_ACCEL_2_BDU_MASK			0x80
-#define ST_ACCEL_2_DRDY_IRQ_ADDR		0x22
-#define ST_ACCEL_2_DRDY_IRQ_INT1_MASK		0x02
-#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK		0x10
-#define ST_ACCEL_2_IHL_IRQ_ADDR			0x22
-#define ST_ACCEL_2_IHL_IRQ_MASK			0x80
-#define ST_ACCEL_2_OD_IRQ_ADDR			0x22
-#define ST_ACCEL_2_OD_IRQ_MASK			0x40
-#define ST_ACCEL_2_MULTIREAD_BIT		true
-
-/* CUSTOM VALUES FOR SENSOR 3 */
-#define ST_ACCEL_3_WAI_EXP			0x40
-#define ST_ACCEL_3_ODR_ADDR			0x20
-#define ST_ACCEL_3_ODR_MASK			0xf0
-#define ST_ACCEL_3_ODR_AVL_3HZ_VAL		0x01
-#define ST_ACCEL_3_ODR_AVL_6HZ_VAL		0x02
-#define ST_ACCEL_3_ODR_AVL_12HZ_VAL		0x03
-#define ST_ACCEL_3_ODR_AVL_25HZ_VAL		0x04
-#define ST_ACCEL_3_ODR_AVL_50HZ_VAL		0x05
-#define ST_ACCEL_3_ODR_AVL_100HZ_VAL		0x06
-#define ST_ACCEL_3_ODR_AVL_200HZ_VAL		0x07
-#define ST_ACCEL_3_ODR_AVL_400HZ_VAL		0x08
-#define ST_ACCEL_3_ODR_AVL_800HZ_VAL		0x09
-#define ST_ACCEL_3_ODR_AVL_1600HZ_VAL		0x0a
-#define ST_ACCEL_3_FS_ADDR			0x24
-#define ST_ACCEL_3_FS_MASK			0x38
-#define ST_ACCEL_3_FS_AVL_2_VAL			0X00
-#define ST_ACCEL_3_FS_AVL_4_VAL			0X01
-#define ST_ACCEL_3_FS_AVL_6_VAL			0x02
-#define ST_ACCEL_3_FS_AVL_8_VAL			0x03
-#define ST_ACCEL_3_FS_AVL_16_VAL		0x04
-#define ST_ACCEL_3_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(61)
-#define ST_ACCEL_3_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(122)
-#define ST_ACCEL_3_FS_AVL_6_GAIN		IIO_G_TO_M_S_2(183)
-#define ST_ACCEL_3_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(244)
-#define ST_ACCEL_3_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(732)
-#define ST_ACCEL_3_BDU_ADDR			0x20
-#define ST_ACCEL_3_BDU_MASK			0x08
-#define ST_ACCEL_3_DRDY_IRQ_ADDR		0x23
-#define ST_ACCEL_3_DRDY_IRQ_INT1_MASK		0x80
-#define ST_ACCEL_3_DRDY_IRQ_INT2_MASK		0x00
-#define ST_ACCEL_3_IHL_IRQ_ADDR			0x23
-#define ST_ACCEL_3_IHL_IRQ_MASK			0x40
-#define ST_ACCEL_3_IG1_EN_ADDR			0x23
-#define ST_ACCEL_3_IG1_EN_MASK			0x08
-#define ST_ACCEL_3_MULTIREAD_BIT		false
-
-/* CUSTOM VALUES FOR SENSOR 4 */
-#define ST_ACCEL_4_WAI_EXP			0x3a
-#define ST_ACCEL_4_ODR_ADDR			0x20
-#define ST_ACCEL_4_ODR_MASK			0x30 /* DF1 and DF0 */
-#define ST_ACCEL_4_ODR_AVL_40HZ_VAL		0x00
-#define ST_ACCEL_4_ODR_AVL_160HZ_VAL		0x01
-#define ST_ACCEL_4_ODR_AVL_640HZ_VAL		0x02
-#define ST_ACCEL_4_ODR_AVL_2560HZ_VAL		0x03
-#define ST_ACCEL_4_PW_ADDR			0x20
-#define ST_ACCEL_4_PW_MASK			0xc0
-#define ST_ACCEL_4_FS_ADDR			0x21
-#define ST_ACCEL_4_FS_MASK			0x80
-#define ST_ACCEL_4_FS_AVL_2_VAL			0X00
-#define ST_ACCEL_4_FS_AVL_6_VAL			0X01
-#define ST_ACCEL_4_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1024)
-#define ST_ACCEL_4_FS_AVL_6_GAIN		IIO_G_TO_M_S_2(340)
-#define ST_ACCEL_4_BDU_ADDR			0x21
-#define ST_ACCEL_4_BDU_MASK			0x40
-#define ST_ACCEL_4_DRDY_IRQ_ADDR		0x21
-#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK		0x04
-#define ST_ACCEL_4_MULTIREAD_BIT		true
-
-/* CUSTOM VALUES FOR SENSOR 5 */
-#define ST_ACCEL_5_WAI_EXP			0x3b
-#define ST_ACCEL_5_ODR_ADDR			0x20
-#define ST_ACCEL_5_ODR_MASK			0x80
-#define ST_ACCEL_5_ODR_AVL_100HZ_VAL		0x00
-#define ST_ACCEL_5_ODR_AVL_400HZ_VAL		0x01
-#define ST_ACCEL_5_PW_ADDR			0x20
-#define ST_ACCEL_5_PW_MASK			0x40
-#define ST_ACCEL_5_FS_ADDR			0x20
-#define ST_ACCEL_5_FS_MASK			0x20
-#define ST_ACCEL_5_FS_AVL_2_VAL			0X00
-#define ST_ACCEL_5_FS_AVL_8_VAL			0X01
-/* TODO: check these resulting gain settings, these are not in the datsheet */
-#define ST_ACCEL_5_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(18000)
-#define ST_ACCEL_5_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(72000)
-#define ST_ACCEL_5_DRDY_IRQ_ADDR		0x22
-#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK		0x04
-#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK		0x20
-#define ST_ACCEL_5_IHL_IRQ_ADDR			0x22
-#define ST_ACCEL_5_IHL_IRQ_MASK			0x80
-#define ST_ACCEL_5_OD_IRQ_ADDR			0x22
-#define ST_ACCEL_5_OD_IRQ_MASK			0x40
-#define ST_ACCEL_5_IG1_EN_ADDR			0x21
-#define ST_ACCEL_5_IG1_EN_MASK			0x08
-#define ST_ACCEL_5_MULTIREAD_BIT		false
-
-/* CUSTOM VALUES FOR SENSOR 6 */
-#define ST_ACCEL_6_WAI_EXP			0x32
-#define ST_ACCEL_6_ODR_ADDR			0x20
-#define ST_ACCEL_6_ODR_MASK			0x18
-#define ST_ACCEL_6_ODR_AVL_50HZ_VAL		0x00
-#define ST_ACCEL_6_ODR_AVL_100HZ_VAL		0x01
-#define ST_ACCEL_6_ODR_AVL_400HZ_VAL		0x02
-#define ST_ACCEL_6_ODR_AVL_1000HZ_VAL		0x03
-#define ST_ACCEL_6_PW_ADDR			0x20
-#define ST_ACCEL_6_PW_MASK			0x20
-#define ST_ACCEL_6_FS_ADDR			0x23
-#define ST_ACCEL_6_FS_MASK			0x30
-#define ST_ACCEL_6_FS_AVL_100_VAL		0x00
-#define ST_ACCEL_6_FS_AVL_200_VAL		0x01
-#define ST_ACCEL_6_FS_AVL_400_VAL		0x03
-#define ST_ACCEL_6_FS_AVL_100_GAIN		IIO_G_TO_M_S_2(49000)
-#define ST_ACCEL_6_FS_AVL_200_GAIN		IIO_G_TO_M_S_2(98000)
-#define ST_ACCEL_6_FS_AVL_400_GAIN		IIO_G_TO_M_S_2(195000)
-#define ST_ACCEL_6_BDU_ADDR			0x23
-#define ST_ACCEL_6_BDU_MASK			0x80
-#define ST_ACCEL_6_DRDY_IRQ_ADDR		0x22
-#define ST_ACCEL_6_DRDY_IRQ_INT1_MASK		0x02
-#define ST_ACCEL_6_DRDY_IRQ_INT2_MASK		0x10
-#define ST_ACCEL_6_IHL_IRQ_ADDR			0x22
-#define ST_ACCEL_6_IHL_IRQ_MASK			0x80
-#define ST_ACCEL_6_MULTIREAD_BIT		true
-
-/* CUSTOM VALUES FOR SENSOR 7 */
-#define ST_ACCEL_7_ODR_ADDR			0x20
-#define ST_ACCEL_7_ODR_MASK			0x30
-#define ST_ACCEL_7_ODR_AVL_280HZ_VAL		0x00
-#define ST_ACCEL_7_ODR_AVL_560HZ_VAL		0x01
-#define ST_ACCEL_7_ODR_AVL_1120HZ_VAL		0x02
-#define ST_ACCEL_7_ODR_AVL_4480HZ_VAL		0x03
-#define ST_ACCEL_7_PW_ADDR			0x20
-#define ST_ACCEL_7_PW_MASK			0xc0
-#define ST_ACCEL_7_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(488)
-#define ST_ACCEL_7_BDU_ADDR			0x21
-#define ST_ACCEL_7_BDU_MASK			0x40
-#define ST_ACCEL_7_DRDY_IRQ_ADDR		0x21
-#define ST_ACCEL_7_DRDY_IRQ_INT1_MASK		0x04
-#define ST_ACCEL_7_MULTIREAD_BIT		false
-
 static const struct iio_chan_spec st_accel_8bit_channels[] = {
 	ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
 			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@@ -281,7 +93,7 @@ static const struct iio_chan_spec st_accel_16bit_channels[] = {
 
 static const struct st_sensor_settings st_accel_sensors_settings[] = {
 	{
-		.wai = ST_ACCEL_1_WAI_EXP,
+		.wai = 0x33,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = LIS3DH_ACCEL_DEV_NAME,
@@ -294,22 +106,22 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 		},
 		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
 		.odr = {
-			.addr = ST_ACCEL_1_ODR_ADDR,
-			.mask = ST_ACCEL_1_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0xf0,
 			.odr_avl = {
-				{ 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
-				{ 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
-				{ 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
-				{ 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
-				{ 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
-				{ 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
-				{ 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
-				{ 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+				{ .hz = 1, .value = 0x01, },
+				{ .hz = 10, .value = 0x02, },
+				{ .hz = 25, .value = 0x03, },
+				{ .hz = 50, .value = 0x04, },
+				{ .hz = 100, .value = 0x05, },
+				{ .hz = 200, .value = 0x06, },
+				{ .hz = 400, .value = 0x07, },
+				{ .hz = 1600, .value = 0x08, },
 			},
 		},
 		.pw = {
-			.addr = ST_ACCEL_1_ODR_ADDR,
-			.mask = ST_ACCEL_1_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0xf0,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
 		.enable_axis = {
@@ -317,48 +129,48 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_ACCEL_1_FS_ADDR,
-			.mask = ST_ACCEL_1_FS_MASK,
+			.addr = 0x23,
+			.mask = 0x30,
 			.fs_avl = {
 				[0] = {
 					.num = ST_ACCEL_FS_AVL_2G,
-					.value = ST_ACCEL_1_FS_AVL_2_VAL,
-					.gain = ST_ACCEL_1_FS_AVL_2_GAIN,
+					.value = 0x00,
+					.gain = IIO_G_TO_M_S_2(1000),
 				},
 				[1] = {
 					.num = ST_ACCEL_FS_AVL_4G,
-					.value = ST_ACCEL_1_FS_AVL_4_VAL,
-					.gain = ST_ACCEL_1_FS_AVL_4_GAIN,
+					.value = 0x01,
+					.gain = IIO_G_TO_M_S_2(2000),
 				},
 				[2] = {
 					.num = ST_ACCEL_FS_AVL_8G,
-					.value = ST_ACCEL_1_FS_AVL_8_VAL,
-					.gain = ST_ACCEL_1_FS_AVL_8_GAIN,
+					.value = 0x02,
+					.gain = IIO_G_TO_M_S_2(4000),
 				},
 				[3] = {
 					.num = ST_ACCEL_FS_AVL_16G,
-					.value = ST_ACCEL_1_FS_AVL_16_VAL,
-					.gain = ST_ACCEL_1_FS_AVL_16_GAIN,
+					.value = 0x03,
+					.gain = IIO_G_TO_M_S_2(12000),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_ACCEL_1_BDU_ADDR,
-			.mask = ST_ACCEL_1_BDU_MASK,
+			.addr = 0x23,
+			.mask = 0x80,
 		},
 		.drdy_irq = {
-			.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
-			.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
-			.addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
-			.mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
+			.addr = 0x22,
+			.mask_int1 = 0x10,
+			.mask_int2 = 0x08,
+			.addr_ihl = 0x25,
+			.mask_ihl = 0x02,
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_ACCEL_2_WAI_EXP,
+		.wai = 0x32,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = LIS331DLH_ACCEL_DEV_NAME,
@@ -368,18 +180,18 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 		},
 		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
 		.odr = {
-			.addr = ST_ACCEL_2_ODR_ADDR,
-			.mask = ST_ACCEL_2_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0x18,
 			.odr_avl = {
-				{ 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
-				{ 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
-				{ 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
-				{ 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, },
+				{ .hz = 50, .value = 0x00, },
+				{ .hz = 100, .value = 0x01, },
+				{ .hz = 400, .value = 0x02, },
+				{ .hz = 1000, .value = 0x03, },
 			},
 		},
 		.pw = {
-			.addr = ST_ACCEL_2_PW_ADDR,
-			.mask = ST_ACCEL_2_PW_MASK,
+			.addr = 0x20,
+			.mask = 0xe0,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -388,69 +200,69 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_ACCEL_2_FS_ADDR,
-			.mask = ST_ACCEL_2_FS_MASK,
+			.addr = 0x23,
+			.mask = 0x30,
 			.fs_avl = {
 				[0] = {
 					.num = ST_ACCEL_FS_AVL_2G,
-					.value = ST_ACCEL_2_FS_AVL_2_VAL,
-					.gain = ST_ACCEL_2_FS_AVL_2_GAIN,
+					.value = 0x00,
+					.gain = IIO_G_TO_M_S_2(1000),
 				},
 				[1] = {
 					.num = ST_ACCEL_FS_AVL_4G,
-					.value = ST_ACCEL_2_FS_AVL_4_VAL,
-					.gain = ST_ACCEL_2_FS_AVL_4_GAIN,
+					.value = 0x01,
+					.gain = IIO_G_TO_M_S_2(2000),
 				},
 				[2] = {
 					.num = ST_ACCEL_FS_AVL_8G,
-					.value = ST_ACCEL_2_FS_AVL_8_VAL,
-					.gain = ST_ACCEL_2_FS_AVL_8_GAIN,
+					.value = 0x03,
+					.gain = IIO_G_TO_M_S_2(3900),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_ACCEL_2_BDU_ADDR,
-			.mask = ST_ACCEL_2_BDU_MASK,
+			.addr = 0x23,
+			.mask = 0x80,
 		},
 		.drdy_irq = {
-			.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK,
-			.mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK,
-			.addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR,
-			.mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK,
-			.addr_od = ST_ACCEL_2_OD_IRQ_ADDR,
-			.mask_od = ST_ACCEL_2_OD_IRQ_MASK,
+			.addr = 0x22,
+			.mask_int1 = 0x02,
+			.mask_int2 = 0x10,
+			.addr_ihl = 0x22,
+			.mask_ihl = 0x80,
+			.addr_od = 0x22,
+			.mask_od = 0x40,
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_ACCEL_3_WAI_EXP,
+		.wai = 0x40,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = LSM330_ACCEL_DEV_NAME,
 		},
 		.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
 		.odr = {
-			.addr = ST_ACCEL_3_ODR_ADDR,
-			.mask = ST_ACCEL_3_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0xf0,
 			.odr_avl = {
-				{ 3, ST_ACCEL_3_ODR_AVL_3HZ_VAL },
-				{ 6, ST_ACCEL_3_ODR_AVL_6HZ_VAL, },
-				{ 12, ST_ACCEL_3_ODR_AVL_12HZ_VAL, },
-				{ 25, ST_ACCEL_3_ODR_AVL_25HZ_VAL, },
-				{ 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
-				{ 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
-				{ 200, ST_ACCEL_3_ODR_AVL_200HZ_VAL, },
-				{ 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
-				{ 800, ST_ACCEL_3_ODR_AVL_800HZ_VAL, },
-				{ 1600, ST_ACCEL_3_ODR_AVL_1600HZ_VAL, },
+				{ .hz = 3, .value = 0x01, },
+				{ .hz = 6, .value = 0x02, },
+				{ .hz = 12, .value = 0x03, },
+				{ .hz = 25, .value = 0x04, },
+				{ .hz = 50, .value = 0x05, },
+				{ .hz = 100, .value = 0x06, },
+				{ .hz = 200, .value = 0x07, },
+				{ .hz = 400, .value = 0x08, },
+				{ .hz = 800, .value = 0x09, },
+				{ .hz = 1600, .value = 0x0a, },
 			},
 		},
 		.pw = {
-			.addr = ST_ACCEL_3_ODR_ADDR,
-			.mask = ST_ACCEL_3_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0xf0,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
 		.enable_axis = {
@@ -458,75 +270,75 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_ACCEL_3_FS_ADDR,
-			.mask = ST_ACCEL_3_FS_MASK,
+			.addr = 0x24,
+			.mask = 0x38,
 			.fs_avl = {
 				[0] = {
 					.num = ST_ACCEL_FS_AVL_2G,
-					.value = ST_ACCEL_3_FS_AVL_2_VAL,
-					.gain = ST_ACCEL_3_FS_AVL_2_GAIN,
+					.value = 0x00,
+					.gain = IIO_G_TO_M_S_2(61),
 				},
 				[1] = {
 					.num = ST_ACCEL_FS_AVL_4G,
-					.value = ST_ACCEL_3_FS_AVL_4_VAL,
-					.gain = ST_ACCEL_3_FS_AVL_4_GAIN,
+					.value = 0x01,
+					.gain = IIO_G_TO_M_S_2(122),
 				},
 				[2] = {
 					.num = ST_ACCEL_FS_AVL_6G,
-					.value = ST_ACCEL_3_FS_AVL_6_VAL,
-					.gain = ST_ACCEL_3_FS_AVL_6_GAIN,
+					.value = 0x02,
+					.gain = IIO_G_TO_M_S_2(183),
 				},
 				[3] = {
 					.num = ST_ACCEL_FS_AVL_8G,
-					.value = ST_ACCEL_3_FS_AVL_8_VAL,
-					.gain = ST_ACCEL_3_FS_AVL_8_GAIN,
+					.value = 0x03,
+					.gain = IIO_G_TO_M_S_2(244),
 				},
 				[4] = {
 					.num = ST_ACCEL_FS_AVL_16G,
-					.value = ST_ACCEL_3_FS_AVL_16_VAL,
-					.gain = ST_ACCEL_3_FS_AVL_16_GAIN,
+					.value = 0x04,
+					.gain = IIO_G_TO_M_S_2(732),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_ACCEL_3_BDU_ADDR,
-			.mask = ST_ACCEL_3_BDU_MASK,
+			.addr = 0x20,
+			.mask = 0x08,
 		},
 		.drdy_irq = {
-			.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK,
-			.mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK,
-			.addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR,
-			.mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK,
+			.addr = 0x23,
+			.mask_int1 = 0x80,
+			.mask_int2 = 0x00,
+			.addr_ihl = 0x23,
+			.mask_ihl = 0x40,
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 			.ig1 = {
-				.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
-				.en_mask = ST_ACCEL_3_IG1_EN_MASK,
+				.en_addr = 0x23,
+				.en_mask = 0x08,
 			},
 		},
-		.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
+		.multi_read_bit = false,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_ACCEL_4_WAI_EXP,
+		.wai = 0x3a,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = LIS3LV02DL_ACCEL_DEV_NAME,
 		},
 		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
 		.odr = {
-			.addr = ST_ACCEL_4_ODR_ADDR,
-			.mask = ST_ACCEL_4_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0x30, /* DF1 and DF0 */
 			.odr_avl = {
-				{ 40, ST_ACCEL_4_ODR_AVL_40HZ_VAL },
-				{ 160, ST_ACCEL_4_ODR_AVL_160HZ_VAL, },
-				{ 640, ST_ACCEL_4_ODR_AVL_640HZ_VAL, },
-				{ 2560, ST_ACCEL_4_ODR_AVL_2560HZ_VAL, },
+				{ .hz = 40, .value = 0x00, },
+				{ .hz = 160, .value = 0x01, },
+				{ .hz = 640, .value = 0x02, },
+				{ .hz = 2560, .value = 0x03, },
 			},
 		},
 		.pw = {
-			.addr = ST_ACCEL_4_PW_ADDR,
-			.mask = ST_ACCEL_4_PW_MASK,
+			.addr = 0x20,
+			.mask = 0xc0,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -535,51 +347,51 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_ACCEL_4_FS_ADDR,
-			.mask = ST_ACCEL_4_FS_MASK,
+			.addr = 0x21,
+			.mask = 0x80,
 			.fs_avl = {
 				[0] = {
 					.num = ST_ACCEL_FS_AVL_2G,
-					.value = ST_ACCEL_4_FS_AVL_2_VAL,
-					.gain = ST_ACCEL_4_FS_AVL_2_GAIN,
+					.value = 0x00,
+					.gain = IIO_G_TO_M_S_2(1024),
 				},
 				[1] = {
 					.num = ST_ACCEL_FS_AVL_6G,
-					.value = ST_ACCEL_4_FS_AVL_6_VAL,
-					.gain = ST_ACCEL_4_FS_AVL_6_GAIN,
+					.value = 0x01,
+					.gain = IIO_G_TO_M_S_2(340),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_ACCEL_4_BDU_ADDR,
-			.mask = ST_ACCEL_4_BDU_MASK,
+			.addr = 0x21,
+			.mask = 0x40,
 		},
 		.drdy_irq = {
-			.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK,
+			.addr = 0x21,
+			.mask_int1 = 0x04,
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2, /* guess */
 	},
 	{
-		.wai = ST_ACCEL_5_WAI_EXP,
+		.wai = 0x3b,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = LIS331DL_ACCEL_DEV_NAME,
 		},
 		.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
 		.odr = {
-			.addr = ST_ACCEL_5_ODR_ADDR,
-			.mask = ST_ACCEL_5_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0x80,
 			.odr_avl = {
-				{ 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL },
-				{ 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, },
+				{ .hz = 100, .value = 0x00, },
+				{ .hz = 400, .value = 0x01, },
 			},
 		},
 		.pw = {
-			.addr = ST_ACCEL_5_PW_ADDR,
-			.mask = ST_ACCEL_5_PW_MASK,
+			.addr = 0x20,
+			.mask = 0x40,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -588,54 +400,58 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_ACCEL_5_FS_ADDR,
-			.mask = ST_ACCEL_5_FS_MASK,
+			.addr = 0x20,
+			.mask = 0x20,
+			/*
+			 * TODO: check these resulting gain settings, these are
+			 * not in the datsheet
+			 */
 			.fs_avl = {
 				[0] = {
 					.num = ST_ACCEL_FS_AVL_2G,
-					.value = ST_ACCEL_5_FS_AVL_2_VAL,
-					.gain = ST_ACCEL_5_FS_AVL_2_GAIN,
+					.value = 0x00,
+					.gain = IIO_G_TO_M_S_2(18000),
 				},
 				[1] = {
 					.num = ST_ACCEL_FS_AVL_8G,
-					.value = ST_ACCEL_5_FS_AVL_8_VAL,
-					.gain = ST_ACCEL_5_FS_AVL_8_GAIN,
+					.value = 0x01,
+					.gain = IIO_G_TO_M_S_2(72000),
 				},
 			},
 		},
 		.drdy_irq = {
-			.addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
-			.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
-			.addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR,
-			.mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK,
-			.addr_od = ST_ACCEL_5_OD_IRQ_ADDR,
-			.mask_od = ST_ACCEL_5_OD_IRQ_MASK,
+			.addr = 0x22,
+			.mask_int1 = 0x04,
+			.mask_int2 = 0x20,
+			.addr_ihl = 0x22,
+			.mask_ihl = 0x80,
+			.addr_od = 0x22,
+			.mask_od = 0x40,
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
+		.multi_read_bit = false,
 		.bootime = 2, /* guess */
 	},
 	{
-		.wai = ST_ACCEL_6_WAI_EXP,
+		.wai = 0x32,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = H3LIS331DL_DRIVER_NAME,
 		},
 		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
 		.odr = {
-			.addr = ST_ACCEL_6_ODR_ADDR,
-			.mask = ST_ACCEL_6_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0x18,
 			.odr_avl = {
-				{ 50, ST_ACCEL_6_ODR_AVL_50HZ_VAL },
-				{ 100, ST_ACCEL_6_ODR_AVL_100HZ_VAL, },
-				{ 400, ST_ACCEL_6_ODR_AVL_400HZ_VAL, },
-				{ 1000, ST_ACCEL_6_ODR_AVL_1000HZ_VAL, },
+				{ .hz = 50, .value = 0x00, },
+				{ .hz = 100, .value = 0x01, },
+				{ .hz = 400, .value = 0x02, },
+				{ .hz = 1000, .value = 0x03, },
 			},
 		},
 		.pw = {
-			.addr = ST_ACCEL_6_PW_ADDR,
-			.mask = ST_ACCEL_6_PW_MASK,
+			.addr = 0x20,
+			.mask = 0x20,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -644,38 +460,38 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_ACCEL_6_FS_ADDR,
-			.mask = ST_ACCEL_6_FS_MASK,
+			.addr = 0x23,
+			.mask = 0x30,
 			.fs_avl = {
 				[0] = {
 					.num = ST_ACCEL_FS_AVL_100G,
-					.value = ST_ACCEL_6_FS_AVL_100_VAL,
-					.gain = ST_ACCEL_6_FS_AVL_100_GAIN,
+					.value = 0x00,
+					.gain = IIO_G_TO_M_S_2(49000),
 				},
 				[1] = {
 					.num = ST_ACCEL_FS_AVL_200G,
-					.value = ST_ACCEL_6_FS_AVL_200_VAL,
-					.gain = ST_ACCEL_6_FS_AVL_200_GAIN,
+					.value = 0x01,
+					.gain = IIO_G_TO_M_S_2(98000),
 				},
 				[2] = {
 					.num = ST_ACCEL_FS_AVL_400G,
-					.value = ST_ACCEL_6_FS_AVL_400_VAL,
-					.gain = ST_ACCEL_6_FS_AVL_400_GAIN,
+					.value = 0x03,
+					.gain = IIO_G_TO_M_S_2(195000),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_ACCEL_6_BDU_ADDR,
-			.mask = ST_ACCEL_6_BDU_MASK,
+			.addr = 0x23,
+			.mask = 0x80,
 		},
 		.drdy_irq = {
-			.addr = ST_ACCEL_6_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_ACCEL_6_DRDY_IRQ_INT1_MASK,
-			.mask_int2 = ST_ACCEL_6_DRDY_IRQ_INT2_MASK,
-			.addr_ihl = ST_ACCEL_6_IHL_IRQ_ADDR,
-			.mask_ihl = ST_ACCEL_6_IHL_IRQ_MASK,
+			.addr = 0x22,
+			.mask_int1 = 0x02,
+			.mask_int2 = 0x10,
+			.addr_ihl = 0x22,
+			.mask_ihl = 0x80,
 		},
-		.multi_read_bit = ST_ACCEL_6_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2,
 	},
 	{
@@ -685,18 +501,18 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 		},
 		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
 		.odr = {
-			.addr = ST_ACCEL_7_ODR_ADDR,
-			.mask = ST_ACCEL_7_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0x30,
 			.odr_avl = {
-				{ 280, ST_ACCEL_7_ODR_AVL_280HZ_VAL, },
-				{ 560, ST_ACCEL_7_ODR_AVL_560HZ_VAL, },
-				{ 1120, ST_ACCEL_7_ODR_AVL_1120HZ_VAL, },
-				{ 4480, ST_ACCEL_7_ODR_AVL_4480HZ_VAL, },
+				{ .hz = 280, .value = 0x00, },
+				{ .hz = 560, .value = 0x01, },
+				{ .hz = 1120, .value = 0x02, },
+				{ .hz = 4480, .value = 0x03, },
 			},
 		},
 		.pw = {
-			.addr = ST_ACCEL_7_PW_ADDR,
-			.mask = ST_ACCEL_7_PW_MASK,
+			.addr = 0x20,
+			.mask = 0xc0,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -708,7 +524,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 			.fs_avl = {
 				[0] = {
 					.num = ST_ACCEL_FS_AVL_2G,
-					.gain = ST_ACCEL_7_FS_AVL_2_GAIN,
+					.gain = IIO_G_TO_M_S_2(488),
 				},
 			},
 		},
@@ -719,11 +535,78 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
 		.bdu = {
 		},
 		.drdy_irq = {
-			.addr = ST_ACCEL_7_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_ACCEL_7_DRDY_IRQ_INT1_MASK,
+			.addr = 0x21,
+			.mask_int1 = 0x04,
+			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
+		},
+		.multi_read_bit = false,
+		.bootime = 2,
+	},
+	{
+		.wai = 0x33,
+		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+		.sensors_supported = {
+			[0] = LNG2DM_ACCEL_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
+		.odr = {
+			.addr = 0x20,
+			.mask = 0xf0,
+			.odr_avl = {
+				{ .hz = 1, .value = 0x01, },
+				{ .hz = 10, .value = 0x02, },
+				{ .hz = 25, .value = 0x03, },
+				{ .hz = 50, .value = 0x04, },
+				{ .hz = 100, .value = 0x05, },
+				{ .hz = 200, .value = 0x06, },
+				{ .hz = 400, .value = 0x07, },
+				{ .hz = 1600, .value = 0x08, },
+			},
+		},
+		.pw = {
+			.addr = 0x20,
+			.mask = 0xf0,
+			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = 0x23,
+			.mask = 0x30,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = 0x00,
+					.gain = IIO_G_TO_M_S_2(15600),
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = 0x01,
+					.gain = IIO_G_TO_M_S_2(31200),
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = 0x02,
+					.gain = IIO_G_TO_M_S_2(62500),
+				},
+				[3] = {
+					.num = ST_ACCEL_FS_AVL_16G,
+					.value = 0x03,
+					.gain = IIO_G_TO_M_S_2(187500),
+				},
+			},
+		},
+		.drdy_irq = {
+			.addr = 0x22,
+			.mask_int1 = 0x10,
+			.mask_int2 = 0x08,
+			.addr_ihl = 0x25,
+			.mask_ihl = 0x02,
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_ACCEL_7_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2,
 	},
 };

+ 5 - 0
drivers/iio/accel/st_accel_i2c.c

@@ -84,6 +84,10 @@ static const struct of_device_id st_accel_of_match[] = {
 		.compatible = "st,lis3l02dq",
 		.data = LIS3L02DQ_ACCEL_DEV_NAME,
 	},
+	{
+		.compatible = "st,lng2dm-accel",
+		.data = LNG2DM_ACCEL_DEV_NAME,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
 	{ LSM303AGR_ACCEL_DEV_NAME },
 	{ LIS2DH12_ACCEL_DEV_NAME },
 	{ LIS3L02DQ_ACCEL_DEV_NAME },
+	{ LNG2DM_ACCEL_DEV_NAME },
 	{},
 };
 MODULE_DEVICE_TABLE(i2c, st_accel_id_table);

+ 1 - 0
drivers/iio/accel/st_accel_spi.c

@@ -60,6 +60,7 @@ static const struct spi_device_id st_accel_id_table[] = {
 	{ LSM303AGR_ACCEL_DEV_NAME },
 	{ LIS2DH12_ACCEL_DEV_NAME },
 	{ LIS3L02DQ_ACCEL_DEV_NAME },
+	{ LNG2DM_ACCEL_DEV_NAME },
 	{},
 };
 MODULE_DEVICE_TABLE(spi, st_accel_id_table);

+ 46 - 0
drivers/iio/adc/Kconfig

@@ -58,6 +58,18 @@ config AD7476
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7476.
 
+config AD7766
+	tristate "Analog Devices AD7766/AD7767 ADC driver"
+	depends on SPI_MASTER
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for Analog Devices AD7766, AD7766-1,
+	  AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ad7766.
+
 config AD7791
 	tristate "Analog Devices AD7791 ADC driver"
 	depends on SPI
@@ -195,6 +207,16 @@ config DA9150_GPADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called berlin2-adc.
 
+config ENVELOPE_DETECTOR
+	tristate "Envelope detector using a DAC and a comparator"
+	depends on OF
+	help
+	  Say yes here to build support for an envelope detector using a DAC
+	  and a comparator.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called envelope-detector.
+
 config EXYNOS_ADC
 	tristate "Exynos ADC driver support"
 	depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@@ -419,6 +441,28 @@ config ROCKCHIP_SARADC
 	  To compile this driver as a module, choose M here: the
 	  module will be called rockchip_saradc.
 
+config STM32_ADC_CORE
+	tristate "STMicroelectronics STM32 adc core"
+	depends on ARCH_STM32 || COMPILE_TEST
+	depends on OF
+	depends on REGULATOR
+	help
+	  Select this option to enable the core driver for STMicroelectronics
+	  STM32 analog-to-digital converter (ADC).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-adc-core.
+
+config STM32_ADC
+	tristate "STMicroelectronics STM32 adc"
+	depends on STM32_ADC_CORE
+	help
+	  Say yes here to build support for STMicroelectronics stm32 Analog
+	  to Digital Converter (ADC).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-adc.
+
 config STX104
 	tristate "Apex Embedded Systems STX104 driver"
 	depends on X86 && ISA_BUS_API
@@ -449,6 +493,8 @@ config TI_ADC081C
 config TI_ADC0832
 	tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
 	depends on SPI
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
 	help
 	  If you say yes here you get support for Texas Instruments ADC0831,
 	  ADC0832, ADC0834, ADC0838 ADC chips.

+ 4 - 0
drivers/iio/adc/Makefile

@@ -9,6 +9,7 @@ obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7298) += ad7298.o
 obj-$(CONFIG_AD7923) += ad7923.o
 obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7766) += ad7766.o
 obj-$(CONFIG_AD7791) += ad7791.o
 obj-$(CONFIG_AD7793) += ad7793.o
 obj-$(CONFIG_AD7887) += ad7887.o
@@ -20,6 +21,7 @@ obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
 obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
@@ -41,6 +43,8 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
 obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
 obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
+obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
 obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o

+ 330 - 0
drivers/iio/adc/ad7766.c

@@ -0,0 +1,330 @@
+/*
+ * AD7766/AD7767 SPI ADC driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+struct ad7766_chip_info {
+	unsigned int decimation_factor;
+};
+
+enum {
+	AD7766_SUPPLY_AVDD = 0,
+	AD7766_SUPPLY_DVDD = 1,
+	AD7766_SUPPLY_VREF = 2,
+	AD7766_NUM_SUPPLIES = 3
+};
+
+struct ad7766 {
+	const struct ad7766_chip_info *chip_info;
+	struct spi_device *spi;
+	struct clk *mclk;
+	struct gpio_desc *pd_gpio;
+	struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES];
+
+	struct iio_trigger *trig;
+
+	struct spi_transfer xfer;
+	struct spi_message msg;
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 * Make the buffer large enough for one 24 bit sample and one 64 bit
+	 * aligned 64 bit timestamp.
+	 */
+	unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)]
+			____cacheline_aligned;
+};
+
+/*
+ * AD7766 and AD7767 variations are interface compatible, the main difference is
+ * analog performance. Both parts will use the same ID.
+ */
+enum ad7766_device_ids {
+	ID_AD7766,
+	ID_AD7766_1,
+	ID_AD7766_2,
+};
+
+static irqreturn_t ad7766_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct ad7766 *ad7766 = iio_priv(indio_dev);
+	int ret;
+
+	ret = spi_sync(ad7766->spi, &ad7766->msg);
+	if (ret < 0)
+		goto done;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data,
+		pf->timestamp);
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int ad7766_preenable(struct iio_dev *indio_dev)
+{
+	struct ad7766 *ad7766 = iio_priv(indio_dev);
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+	if (ret < 0) {
+		dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(ad7766->mclk);
+	if (ret < 0) {
+		dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret);
+		regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+		return ret;
+	}
+
+	if (ad7766->pd_gpio)
+		gpiod_set_value(ad7766->pd_gpio, 0);
+
+	return 0;
+}
+
+static int ad7766_postdisable(struct iio_dev *indio_dev)
+{
+	struct ad7766 *ad7766 = iio_priv(indio_dev);
+
+	if (ad7766->pd_gpio)
+		gpiod_set_value(ad7766->pd_gpio, 1);
+
+	/*
+	 * The PD pin is synchronous to the clock, so give it some time to
+	 * notice the change before we disable the clock.
+	 */
+	msleep(20);
+
+	clk_disable_unprepare(ad7766->mclk);
+	regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+
+	return 0;
+}
+
+static int ad7766_read_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+	struct ad7766 *ad7766 = iio_priv(indio_dev);
+	struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer;
+	int scale_uv;
+
+	switch (info) {
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = regulator_get_voltage(vref);
+		if (scale_uv < 0)
+			return scale_uv;
+		*val = scale_uv / 1000;
+		*val2 = chan->scan_type.realbits;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = clk_get_rate(ad7766->mclk) /
+			ad7766->chip_info->decimation_factor;
+		return IIO_VAL_INT;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_chan_spec ad7766_channels[] = {
+	{
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.scan_type = {
+			.sign = 's',
+			.realbits = 24,
+			.storagebits = 32,
+			.endianness = IIO_BE,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7766_chip_info ad7766_chip_info[] = {
+	[ID_AD7766] = {
+		.decimation_factor = 8,
+	},
+	[ID_AD7766_1] = {
+		.decimation_factor = 16,
+	},
+	[ID_AD7766_2] = {
+		.decimation_factor = 32,
+	},
+};
+
+static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = {
+	.preenable = &ad7766_preenable,
+	.postenable = &iio_triggered_buffer_postenable,
+	.predisable = &iio_triggered_buffer_predisable,
+	.postdisable = &ad7766_postdisable,
+};
+
+static const struct iio_info ad7766_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &ad7766_read_raw,
+};
+
+static irqreturn_t ad7766_irq(int irq, void *private)
+{
+	iio_trigger_poll(private);
+	return IRQ_HANDLED;
+}
+
+static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
+{
+	struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
+
+	if (enable)
+		enable_irq(ad7766->spi->irq);
+	else
+		disable_irq(ad7766->spi->irq);
+
+	return 0;
+}
+
+static const struct iio_trigger_ops ad7766_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = ad7766_set_trigger_state,
+	.validate_device = iio_trigger_validate_own_device,
+};
+
+static int ad7766_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct iio_dev *indio_dev;
+	struct ad7766 *ad7766;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	ad7766 = iio_priv(indio_dev);
+	ad7766->chip_info = &ad7766_chip_info[id->driver_data];
+
+	ad7766->mclk = devm_clk_get(&spi->dev, "mclk");
+	if (IS_ERR(ad7766->mclk))
+		return PTR_ERR(ad7766->mclk);
+
+	ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd";
+	ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd";
+	ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref";
+
+	ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg),
+		ad7766->reg);
+	if (ret)
+		return ret;
+
+	ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
+		GPIOD_OUT_HIGH);
+	if (IS_ERR(ad7766->pd_gpio))
+		return PTR_ERR(ad7766->pd_gpio);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = ad7766_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ad7766_channels);
+	indio_dev->info = &ad7766_info;
+
+	if (spi->irq > 0) {
+		ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+			indio_dev->name, indio_dev->id);
+		if (!ad7766->trig)
+			return -ENOMEM;
+
+		ad7766->trig->ops = &ad7766_trigger_ops;
+		ad7766->trig->dev.parent = &spi->dev;
+		iio_trigger_set_drvdata(ad7766->trig, ad7766);
+
+		ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
+			IRQF_TRIGGER_FALLING, dev_name(&spi->dev),
+			ad7766->trig);
+		if (ret < 0)
+			return ret;
+
+		/*
+		 * The device generates interrupts as long as it is powered up.
+		 * Some platforms might not allow the option to power it down so
+		 * disable the interrupt to avoid extra load on the system
+		 */
+		disable_irq(spi->irq);
+
+		ret = devm_iio_trigger_register(&spi->dev, ad7766->trig);
+		if (ret)
+			return ret;
+	}
+
+	spi_set_drvdata(spi, indio_dev);
+
+	ad7766->spi = spi;
+
+	/* First byte always 0 */
+	ad7766->xfer.rx_buf = &ad7766->data[1];
+	ad7766->xfer.len = 3;
+
+	spi_message_init(&ad7766->msg);
+	spi_message_add_tail(&ad7766->xfer, &ad7766->msg);
+
+	ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+		&iio_pollfunc_store_time, &ad7766_trigger_handler,
+		&ad7766_buffer_setup_ops);
+	if (ret)
+		return ret;
+
+	ret = devm_iio_device_register(&spi->dev, indio_dev);
+	if (ret)
+		return ret;
+	return 0;
+}
+
+static const struct spi_device_id ad7766_id[] = {
+	{"ad7766", ID_AD7766},
+	{"ad7766-1", ID_AD7766_1},
+	{"ad7766-2", ID_AD7766_2},
+	{"ad7767", ID_AD7766},
+	{"ad7767-1", ID_AD7766_1},
+	{"ad7767-2", ID_AD7766_2},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, ad7766_id);
+
+static struct spi_driver ad7766_driver = {
+	.driver = {
+		.name	= "ad7766",
+	},
+	.probe		= ad7766_probe,
+	.id_table	= ad7766_id,
+};
+module_spi_driver(ad7766_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support");
+MODULE_LICENSE("GPL v2");

+ 28 - 0
drivers/iio/adc/at91_adc.c

@@ -30,6 +30,7 @@
 #include <linux/iio/trigger.h>
 #include <linux/iio/trigger_consumer.h>
 #include <linux/iio/triggered_buffer.h>
+#include <linux/pinctrl/consumer.h>
 
 /* Registers */
 #define AT91_ADC_CR		0x00		/* Control Register */
@@ -1347,6 +1348,32 @@ static int at91_adc_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int at91_adc_suspend(struct device *dev)
+{
+	struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+	struct at91_adc_state *st = iio_priv(idev);
+
+	pinctrl_pm_select_sleep_state(dev);
+	clk_disable_unprepare(st->clk);
+
+	return 0;
+}
+
+static int at91_adc_resume(struct device *dev)
+{
+	struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+	struct at91_adc_state *st = iio_priv(idev);
+
+	clk_prepare_enable(st->clk);
+	pinctrl_pm_select_default_state(dev);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
+
 static struct at91_adc_caps at91sam9260_caps = {
 	.calc_startup_ticks = calc_startup_ticks_9260,
 	.num_channels = 4,
@@ -1441,6 +1468,7 @@ static struct platform_driver at91_adc_driver = {
 	.driver = {
 		   .name = DRIVER_NAME,
 		   .of_match_table = of_match_ptr(at91_adc_dt_ids),
+		   .pm = &at91_adc_pm_ops,
 	},
 };
 

+ 422 - 0
drivers/iio/adc/envelope-detector.c

@@ -0,0 +1,422 @@
+/*
+ * Driver for an envelope detector using a DAC and a comparator
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * 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.
+ */
+
+/*
+ * The DAC is used to find the peak level of an alternating voltage input
+ * signal by a binary search using the output of a comparator wired to
+ * an interrupt pin. Like so:
+ *                           _
+ *                          | \
+ *     input +------>-------|+ \
+ *                          |   \
+ *            .-------.     |    }---.
+ *            |       |     |   /    |
+ *            |    dac|-->--|- /     |
+ *            |       |     |_/      |
+ *            |       |              |
+ *            |       |              |
+ *            |    irq|------<-------'
+ *            |       |
+ *            '-------'
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+struct envelope {
+	spinlock_t comp_lock; /* protects comp */
+	int comp;
+
+	struct mutex read_lock; /* protects everything else */
+
+	int comp_irq;
+	u32 comp_irq_trigger;
+	u32 comp_irq_trigger_inv;
+
+	struct iio_channel *dac;
+	struct delayed_work comp_timeout;
+
+	unsigned int comp_interval;
+	bool invert;
+	u32 dac_max;
+
+	int high;
+	int level;
+	int low;
+
+	struct completion done;
+};
+
+/*
+ * The envelope_detector_comp_latch function works together with the compare
+ * interrupt service routine below (envelope_detector_comp_isr) as a latch
+ * (one-bit memory) for if the interrupt has triggered since last calling
+ * this function.
+ * The ..._comp_isr function disables the interrupt so that the cpu does not
+ * need to service a possible interrupt flood from the comparator when no-one
+ * cares anyway, and this ..._comp_latch function reenables them again if
+ * needed.
+ */
+static int envelope_detector_comp_latch(struct envelope *env)
+{
+	int comp;
+
+	spin_lock_irq(&env->comp_lock);
+	comp = env->comp;
+	env->comp = 0;
+	spin_unlock_irq(&env->comp_lock);
+
+	if (!comp)
+		return 0;
+
+	/*
+	 * The irq was disabled, and is reenabled just now.
+	 * But there might have been a pending irq that
+	 * happened while the irq was disabled that fires
+	 * just as the irq is reenabled. That is not what
+	 * is desired.
+	 */
+	enable_irq(env->comp_irq);
+
+	/* So, synchronize this possibly pending irq... */
+	synchronize_irq(env->comp_irq);
+
+	/* ...and redo the whole dance. */
+	spin_lock_irq(&env->comp_lock);
+	comp = env->comp;
+	env->comp = 0;
+	spin_unlock_irq(&env->comp_lock);
+
+	if (comp)
+		enable_irq(env->comp_irq);
+
+	return 1;
+}
+
+static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx)
+{
+	struct envelope *env = ctx;
+
+	spin_lock(&env->comp_lock);
+	env->comp = 1;
+	disable_irq_nosync(env->comp_irq);
+	spin_unlock(&env->comp_lock);
+
+	return IRQ_HANDLED;
+}
+
+static void envelope_detector_setup_compare(struct envelope *env)
+{
+	int ret;
+
+	/*
+	 * Do a binary search for the peak input level, and stop
+	 * when that level is "trapped" between two adjacent DAC
+	 * values.
+	 * When invert is active, use the midpoint floor so that
+	 * env->level ends up as env->low when the termination
+	 * criteria below is fulfilled, and use the midpoint
+	 * ceiling when invert is not active so that env->level
+	 * ends up as env->high in that case.
+	 */
+	env->level = (env->high + env->low + !env->invert) / 2;
+
+	if (env->high == env->low + 1) {
+		complete(&env->done);
+		return;
+	}
+
+	/* Set a "safe" DAC level (if there is such a thing)... */
+	ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max);
+	if (ret < 0)
+		goto err;
+
+	/* ...clear the comparison result... */
+	envelope_detector_comp_latch(env);
+
+	/* ...set the real DAC level... */
+	ret = iio_write_channel_raw(env->dac, env->level);
+	if (ret < 0)
+		goto err;
+
+	/* ...and wait for a bit to see if the latch catches anything. */
+	schedule_delayed_work(&env->comp_timeout,
+			      msecs_to_jiffies(env->comp_interval));
+	return;
+
+err:
+	env->level = ret;
+	complete(&env->done);
+}
+
+static void envelope_detector_timeout(struct work_struct *work)
+{
+	struct envelope *env = container_of(work, struct envelope,
+					    comp_timeout.work);
+
+	/* Adjust low/high depending on the latch content... */
+	if (!envelope_detector_comp_latch(env) ^ !env->invert)
+		env->low = env->level;
+	else
+		env->high = env->level;
+
+	/* ...and continue the search. */
+	envelope_detector_setup_compare(env);
+}
+
+static int envelope_detector_read_raw(struct iio_dev *indio_dev,
+				      struct iio_chan_spec const *chan,
+				      int *val, int *val2, long mask)
+{
+	struct envelope *env = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		/*
+		 * When invert is active, start with high=max+1 and low=0
+		 * since we will end up with the low value when the
+		 * termination criteria is fulfilled (rounding down). And
+		 * start with high=max and low=-1 when invert is not active
+		 * since we will end up with the high value in that case.
+		 * This ensures that the returned value in both cases are
+		 * in the same range as the DAC and is a value that has not
+		 * triggered the comparator.
+		 */
+		mutex_lock(&env->read_lock);
+		env->high = env->dac_max + env->invert;
+		env->low = -1 + env->invert;
+		envelope_detector_setup_compare(env);
+		wait_for_completion(&env->done);
+		if (env->level < 0) {
+			ret = env->level;
+			goto err_unlock;
+		}
+		*val = env->invert ? env->dac_max - env->level : env->level;
+		mutex_unlock(&env->read_lock);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		return iio_read_channel_scale(env->dac, val, val2);
+	}
+
+	return -EINVAL;
+
+err_unlock:
+	mutex_unlock(&env->read_lock);
+	return ret;
+}
+
+static ssize_t envelope_show_invert(struct iio_dev *indio_dev,
+				    uintptr_t private,
+				    struct iio_chan_spec const *ch, char *buf)
+{
+	struct envelope *env = iio_priv(indio_dev);
+
+	return sprintf(buf, "%u\n", env->invert);
+}
+
+static ssize_t envelope_store_invert(struct iio_dev *indio_dev,
+				     uintptr_t private,
+				     struct iio_chan_spec const *ch,
+				     const char *buf, size_t len)
+{
+	struct envelope *env = iio_priv(indio_dev);
+	unsigned long invert;
+	int ret;
+	u32 trigger;
+
+	ret = kstrtoul(buf, 0, &invert);
+	if (ret < 0)
+		return ret;
+	if (invert > 1)
+		return -EINVAL;
+
+	trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger;
+
+	mutex_lock(&env->read_lock);
+	if (invert != env->invert)
+		ret = irq_set_irq_type(env->comp_irq, trigger);
+	if (!ret) {
+		env->invert = invert;
+		ret = len;
+	}
+	mutex_unlock(&env->read_lock);
+
+	return ret;
+}
+
+static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev,
+					   uintptr_t private,
+					   struct iio_chan_spec const *ch,
+					   char *buf)
+{
+	struct envelope *env = iio_priv(indio_dev);
+
+	return sprintf(buf, "%u\n", env->comp_interval);
+}
+
+static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev,
+					    uintptr_t private,
+					    struct iio_chan_spec const *ch,
+					    const char *buf, size_t len)
+{
+	struct envelope *env = iio_priv(indio_dev);
+	unsigned long interval;
+	int ret;
+
+	ret = kstrtoul(buf, 0, &interval);
+	if (ret < 0)
+		return ret;
+	if (interval > 1000)
+		return -EINVAL;
+
+	mutex_lock(&env->read_lock);
+	env->comp_interval = interval;
+	mutex_unlock(&env->read_lock);
+
+	return len;
+}
+
+static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = {
+	{ .name = "invert",
+	  .read = envelope_show_invert,
+	  .write = envelope_store_invert, },
+	{ .name = "compare_interval",
+	  .read = envelope_show_comp_interval,
+	  .write = envelope_store_comp_interval, },
+	{ /* sentinel */ }
+};
+
+static const struct iio_chan_spec envelope_detector_iio_channel = {
+	.type = IIO_ALTVOLTAGE,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+			    | BIT(IIO_CHAN_INFO_SCALE),
+	.ext_info = envelope_detector_ext_info,
+	.indexed = 1,
+};
+
+static const struct iio_info envelope_detector_info = {
+	.read_raw = &envelope_detector_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int envelope_detector_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *indio_dev;
+	struct envelope *env;
+	enum iio_chan_type type;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*env));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, indio_dev);
+	env = iio_priv(indio_dev);
+	env->comp_interval = 50; /* some sensible default? */
+
+	spin_lock_init(&env->comp_lock);
+	mutex_init(&env->read_lock);
+	init_completion(&env->done);
+	INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout);
+
+	indio_dev->name = dev_name(dev);
+	indio_dev->dev.parent = dev;
+	indio_dev->dev.of_node = dev->of_node;
+	indio_dev->info = &envelope_detector_info;
+	indio_dev->channels = &envelope_detector_iio_channel;
+	indio_dev->num_channels = 1;
+
+	env->dac = devm_iio_channel_get(dev, "dac");
+	if (IS_ERR(env->dac)) {
+		if (PTR_ERR(env->dac) != -EPROBE_DEFER)
+			dev_err(dev, "failed to get dac input channel\n");
+		return PTR_ERR(env->dac);
+	}
+
+	env->comp_irq = platform_get_irq_byname(pdev, "comp");
+	if (env->comp_irq < 0) {
+		if (env->comp_irq != -EPROBE_DEFER)
+			dev_err(dev, "failed to get compare interrupt\n");
+		return env->comp_irq;
+	}
+
+	ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
+			       0, "envelope-detector", env);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to request interrupt\n");
+		return ret;
+	}
+	env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq);
+	if (env->comp_irq_trigger & IRQF_TRIGGER_RISING)
+		env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING;
+	if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING)
+		env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING;
+	if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH)
+		env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW;
+	if (env->comp_irq_trigger & IRQF_TRIGGER_LOW)
+		env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH;
+
+	ret = iio_get_channel_type(env->dac, &type);
+	if (ret < 0)
+		return ret;
+
+	if (type != IIO_VOLTAGE) {
+		dev_err(dev, "dac is of the wrong type\n");
+		return -EINVAL;
+	}
+
+	ret = iio_read_max_channel_raw(env->dac, &env->dac_max);
+	if (ret < 0) {
+		dev_err(dev, "dac does not indicate its raw maximum value\n");
+		return ret;
+	}
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id envelope_detector_match[] = {
+	{ .compatible = "axentia,tse850-envelope-detector", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, envelope_detector_match);
+
+static struct platform_driver envelope_detector_driver = {
+	.probe = envelope_detector_probe,
+	.driver = {
+		.name = "iio-envelope-detector",
+		.of_match_table = envelope_detector_match,
+	},
+};
+module_platform_driver(envelope_detector_driver);
+
+MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");

+ 4 - 13
drivers/iio/adc/max1027.c

@@ -238,7 +238,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
 
 	/* Configure conversion register with the requested chan */
 	st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
-		  MAX1027_NOSCAN | !!(chan->type == IIO_TEMP);
+		  MAX1027_NOSCAN;
+	if (chan->type == IIO_TEMP)
+		st->reg |= MAX1027_TEMP;
 	ret = spi_write(st->spi, &st->reg, 1);
 	if (ret < 0) {
 		dev_err(&indio_dev->dev,
@@ -360,17 +362,6 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
 	return 0;
 }
 
-static int max1027_validate_device(struct iio_trigger *trig,
-				   struct iio_dev *indio_dev)
-{
-	struct iio_dev *indio = iio_trigger_get_drvdata(trig);
-
-	if (indio != indio_dev)
-		return -EINVAL;
-
-	return 0;
-}
-
 static irqreturn_t max1027_trigger_handler(int irq, void *private)
 {
 	struct iio_poll_func *pf = (struct iio_poll_func *)private;
@@ -391,7 +382,7 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private)
 
 static const struct iio_trigger_ops max1027_trigger_ops = {
 	.owner = THIS_MODULE,
-	.validate_device = &max1027_validate_device,
+	.validate_device = &iio_trigger_validate_own_device,
 	.set_trigger_state = &max1027_set_trigger_state,
 };
 

+ 303 - 0
drivers/iio/adc/stm32-adc-core.c

@@ -0,0 +1,303 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * Inspired from: fsl-imx25-tsadc
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include "stm32-adc-core.h"
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CSR			(STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_EOC3			BIT(17)
+#define STM32F4_EOC2			BIT(9)
+#define STM32F4_EOC1			BIT(1)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT	16
+#define STM32F4_ADC_ADCPRE_MASK		GENMASK(17, 16)
+
+/* STM32 F4 maximum analog clock rate (from datasheet) */
+#define STM32F4_ADC_MAX_CLK_RATE	36000000
+
+/**
+ * struct stm32_adc_priv - stm32 ADC core private data
+ * @irq:		irq for ADC block
+ * @domain:		irq domain reference
+ * @aclk:		clock reference for the analog circuitry
+ * @vref:		regulator reference
+ * @common:		common data for all ADC instances
+ */
+struct stm32_adc_priv {
+	int				irq;
+	struct irq_domain		*domain;
+	struct clk			*aclk;
+	struct regulator		*vref;
+	struct stm32_adc_common		common;
+};
+
+static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
+{
+	return container_of(com, struct stm32_adc_priv, common);
+}
+
+/* STM32F4 ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
+ * @priv: stm32 ADC core private data
+ * Select clock prescaler used for analog conversions, before using ADC.
+ */
+static int stm32f4_adc_clk_sel(struct platform_device *pdev,
+			       struct stm32_adc_priv *priv)
+{
+	unsigned long rate;
+	u32 val;
+	int i;
+
+	rate = clk_get_rate(priv->aclk);
+	for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+		if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
+			break;
+	}
+	if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+		return -EINVAL;
+
+	val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
+	val &= ~STM32F4_ADC_ADCPRE_MASK;
+	val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+	writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
+
+	dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
+		rate / (stm32f4_pclk_div[i] * 1000));
+
+	return 0;
+}
+
+/* ADC common interrupt for all instances */
+static void stm32_adc_irq_handler(struct irq_desc *desc)
+{
+	struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 status;
+
+	chained_irq_enter(chip, desc);
+	status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
+
+	if (status & STM32F4_EOC1)
+		generic_handle_irq(irq_find_mapping(priv->domain, 0));
+
+	if (status & STM32F4_EOC2)
+		generic_handle_irq(irq_find_mapping(priv->domain, 1));
+
+	if (status & STM32F4_EOC3)
+		generic_handle_irq(irq_find_mapping(priv->domain, 2));
+
+	chained_irq_exit(chip, desc);
+};
+
+static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
+				irq_hw_number_t hwirq)
+{
+	irq_set_chip_data(irq, d->host_data);
+	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
+
+	return 0;
+}
+
+static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+	irq_set_chip_and_handler(irq, NULL, NULL);
+	irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops stm32_adc_domain_ops = {
+	.map = stm32_adc_domain_map,
+	.unmap  = stm32_adc_domain_unmap,
+	.xlate = irq_domain_xlate_onecell,
+};
+
+static int stm32_adc_irq_probe(struct platform_device *pdev,
+			       struct stm32_adc_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq < 0) {
+		dev_err(&pdev->dev, "failed to get irq\n");
+		return priv->irq;
+	}
+
+	priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
+					     &stm32_adc_domain_ops,
+					     priv);
+	if (!priv->domain) {
+		dev_err(&pdev->dev, "Failed to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
+	irq_set_handler_data(priv->irq, priv);
+
+	return 0;
+}
+
+static void stm32_adc_irq_remove(struct platform_device *pdev,
+				 struct stm32_adc_priv *priv)
+{
+	int hwirq;
+
+	for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
+		irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
+	irq_domain_remove(priv->domain);
+	irq_set_chained_handler(priv->irq, NULL);
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+	struct stm32_adc_priv *priv;
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	int ret;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->common.base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->common.base))
+		return PTR_ERR(priv->common.base);
+
+	priv->vref = devm_regulator_get(&pdev->dev, "vref");
+	if (IS_ERR(priv->vref)) {
+		ret = PTR_ERR(priv->vref);
+		dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_enable(priv->vref);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "vref enable failed\n");
+		return ret;
+	}
+
+	ret = regulator_get_voltage(priv->vref);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+		goto err_regulator_disable;
+	}
+	priv->common.vref_mv = ret / 1000;
+	dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+	priv->aclk = devm_clk_get(&pdev->dev, "adc");
+	if (IS_ERR(priv->aclk)) {
+		ret = PTR_ERR(priv->aclk);
+		dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+		goto err_regulator_disable;
+	}
+
+	ret = clk_prepare_enable(priv->aclk);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "adc clk enable failed\n");
+		goto err_regulator_disable;
+	}
+
+	ret = stm32f4_adc_clk_sel(pdev, priv);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "adc clk selection failed\n");
+		goto err_clk_disable;
+	}
+
+	ret = stm32_adc_irq_probe(pdev, priv);
+	if (ret < 0)
+		goto err_clk_disable;
+
+	platform_set_drvdata(pdev, &priv->common);
+
+	ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to populate DT children\n");
+		goto err_irq_remove;
+	}
+
+	return 0;
+
+err_irq_remove:
+	stm32_adc_irq_remove(pdev, priv);
+
+err_clk_disable:
+	clk_disable_unprepare(priv->aclk);
+
+err_regulator_disable:
+	regulator_disable(priv->vref);
+
+	return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+	struct stm32_adc_common *common = platform_get_drvdata(pdev);
+	struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+	of_platform_depopulate(&pdev->dev);
+	stm32_adc_irq_remove(pdev, priv);
+	clk_disable_unprepare(priv->aclk);
+	regulator_disable(priv->vref);
+
+	return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+	{ .compatible = "st,stm32f4-adc-core" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+	.probe = stm32_adc_probe,
+	.remove = stm32_adc_remove,
+	.driver = {
+		.name = "stm32-adc-core",
+		.of_match_table = stm32_adc_of_match,
+	},
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc-core");

+ 52 - 0
drivers/iio/adc/stm32-adc-core.h

@@ -0,0 +1,52 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset |                 Register                    |
+ * --------------------------------------------------------
+ * | 0x000  |                Master ADC1                  |
+ * --------------------------------------------------------
+ * | 0x100  |                Slave ADC2                   |
+ * --------------------------------------------------------
+ * | 0x200  |                Slave ADC3                   |
+ * --------------------------------------------------------
+ * | 0x300  |         Master & Slave common regs          |
+ * --------------------------------------------------------
+ */
+#define STM32_ADC_MAX_ADCS		3
+#define STM32_ADCX_COMN_OFFSET		0x300
+
+/**
+ * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
+ * @base:		control registers base cpu addr
+ * @vref_mv:		vref voltage (mv)
+ */
+struct stm32_adc_common {
+	void __iomem			*base;
+	int				vref_mv;
+};
+
+#endif

+ 518 - 0
drivers/iio/adc/stm32-adc.c

@@ -0,0 +1,518 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include "stm32-adc-core.h"
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADC_SR			0x00
+#define STM32F4_ADC_CR1			0x04
+#define STM32F4_ADC_CR2			0x08
+#define STM32F4_ADC_SMPR1		0x0C
+#define STM32F4_ADC_SMPR2		0x10
+#define STM32F4_ADC_HTR			0x24
+#define STM32F4_ADC_LTR			0x28
+#define STM32F4_ADC_SQR1		0x2C
+#define STM32F4_ADC_SQR2		0x30
+#define STM32F4_ADC_SQR3		0x34
+#define STM32F4_ADC_JSQR		0x38
+#define STM32F4_ADC_JDR1		0x3C
+#define STM32F4_ADC_JDR2		0x40
+#define STM32F4_ADC_JDR3		0x44
+#define STM32F4_ADC_JDR4		0x48
+#define STM32F4_ADC_DR			0x4C
+
+/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_STRT			BIT(4)
+#define STM32F4_EOC			BIT(1)
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_SCAN			BIT(8)
+#define STM32F4_EOCIE			BIT(5)
+
+/* STM32F4_ADC_CR2 - bit fields */
+#define STM32F4_SWSTART			BIT(30)
+#define STM32F4_EXTEN_MASK		GENMASK(29, 28)
+#define STM32F4_EOCS			BIT(10)
+#define STM32F4_ADON			BIT(0)
+
+/* STM32F4_ADC_SQR1 - bit fields */
+#define STM32F4_L_SHIFT			20
+#define STM32F4_L_MASK			GENMASK(23, 20)
+
+/* STM32F4_ADC_SQR3 - bit fields */
+#define STM32F4_SQ1_SHIFT		0
+#define STM32F4_SQ1_MASK		GENMASK(4, 0)
+
+#define STM32_ADC_TIMEOUT_US		100000
+#define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common:		reference to ADC block common data
+ * @offset:		ADC instance register offset in ADC block
+ * @completion:		end of single conversion completion
+ * @buffer:		data buffer
+ * @clk:		clock for this adc instance
+ * @irq:		interrupt for this adc instance
+ * @lock:		spinlock
+ */
+struct stm32_adc {
+	struct stm32_adc_common	*common;
+	u32			offset;
+	struct completion	completion;
+	u16			*buffer;
+	struct clk		*clk;
+	int			irq;
+	spinlock_t		lock;		/* interrupt lock */
+};
+
+/**
+ * struct stm32_adc_chan_spec - specification of stm32 adc channel
+ * @type:	IIO channel type
+ * @channel:	channel number (single ended)
+ * @name:	channel name (single ended)
+ */
+struct stm32_adc_chan_spec {
+	enum iio_chan_type	type;
+	int			channel;
+	const char		*name;
+};
+
+/* Input definitions common for all STM32F4 instances */
+static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
+	{ IIO_VOLTAGE, 0, "in0" },
+	{ IIO_VOLTAGE, 1, "in1" },
+	{ IIO_VOLTAGE, 2, "in2" },
+	{ IIO_VOLTAGE, 3, "in3" },
+	{ IIO_VOLTAGE, 4, "in4" },
+	{ IIO_VOLTAGE, 5, "in5" },
+	{ IIO_VOLTAGE, 6, "in6" },
+	{ IIO_VOLTAGE, 7, "in7" },
+	{ IIO_VOLTAGE, 8, "in8" },
+	{ IIO_VOLTAGE, 9, "in9" },
+	{ IIO_VOLTAGE, 10, "in10" },
+	{ IIO_VOLTAGE, 11, "in11" },
+	{ IIO_VOLTAGE, 12, "in12" },
+	{ IIO_VOLTAGE, 13, "in13" },
+	{ IIO_VOLTAGE, 14, "in14" },
+	{ IIO_VOLTAGE, 15, "in15" },
+};
+
+/**
+ * STM32 ADC registers access routines
+ * @adc: stm32 adc instance
+ * @reg: reg offset in adc instance
+ *
+ * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
+ * for adc1, adc2 and adc3.
+ */
+static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+	return readl_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
+{
+	return readw_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+	writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adc->lock, flags);
+	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
+	spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adc->lock, flags);
+	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
+	spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+/**
+ * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+};
+
+/**
+ * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+	stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+}
+
+/**
+ * stm32_adc_start_conv() - Start conversions for regular channels.
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_start_conv(struct stm32_adc *adc)
+{
+	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+	stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
+
+	/* Wait for Power-up time (tSTAB from datasheet) */
+	usleep_range(2, 3);
+
+	/* Software start ? (e.g. trigger detection disabled ?) */
+	if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
+		stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
+}
+
+static void stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+	stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
+
+	stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
+}
+
+/**
+ * stm32_adc_single_conv() - Performs a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @res: conversion result
+ *
+ * The function performs a single conversion on a given channel:
+ * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
+ * - Use SW trigger
+ * - Start conversion, then wait for interrupt completion.
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+				 const struct iio_chan_spec *chan,
+				 int *res)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	u32 val;
+	u16 result;
+	int ret;
+
+	reinit_completion(&adc->completion);
+
+	adc->buffer = &result;
+
+	/* Program chan number in regular sequence */
+	val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
+	val &= ~STM32F4_SQ1_MASK;
+	val |= chan->channel << STM32F4_SQ1_SHIFT;
+	stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
+
+	/* Set regular sequence len (0 for 1 conversion) */
+	stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
+
+	/* Trigger detection disabled (conversion can be launched in SW) */
+	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+
+	stm32_adc_conv_irq_enable(adc);
+
+	stm32_adc_start_conv(adc);
+
+	timeout = wait_for_completion_interruptible_timeout(
+					&adc->completion, STM32_ADC_TIMEOUT);
+	if (timeout == 0) {
+		ret = -ETIMEDOUT;
+	} else if (timeout < 0) {
+		ret = timeout;
+	} else {
+		*res = result;
+		ret = IIO_VAL_INT;
+	}
+
+	stm32_adc_stop_conv(adc);
+
+	stm32_adc_conv_irq_disable(adc);
+
+	return ret;
+}
+
+static int stm32_adc_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2, long mask)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+		if (chan->type == IIO_VOLTAGE)
+			ret = stm32_adc_single_conv(indio_dev, chan, val);
+		else
+			ret = -EINVAL;
+		iio_device_release_direct_mode(indio_dev);
+		return ret;
+
+	case IIO_CHAN_INFO_SCALE:
+		*val = adc->common->vref_mv;
+		*val2 = chan->scan_type.realbits;
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t stm32_adc_isr(int irq, void *data)
+{
+	struct stm32_adc *adc = data;
+	u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
+
+	if (status & STM32F4_EOC) {
+		*adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
+		complete(&adc->completion);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
+			      const struct of_phandle_args *iiospec)
+{
+	int i;
+
+	for (i = 0; i < indio_dev->num_channels; i++)
+		if (indio_dev->channels[i].channel == iiospec->args[0])
+			return i;
+
+	return -EINVAL;
+}
+
+/**
+ * stm32_adc_debugfs_reg_access - read or write register value
+ *
+ * To read a value from an ADC register:
+ *   echo [ADC reg offset] > direct_reg_access
+ *   cat direct_reg_access
+ *
+ * To write a value in a ADC register:
+ *   echo [ADC_reg_offset] [value] > direct_reg_access
+ */
+static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
+					unsigned reg, unsigned writeval,
+					unsigned *readval)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	if (!readval)
+		stm32_adc_writel(adc, reg, writeval);
+	else
+		*readval = stm32_adc_readl(adc, reg);
+
+	return 0;
+}
+
+static const struct iio_info stm32_adc_iio_info = {
+	.read_raw = stm32_adc_read_raw,
+	.debugfs_reg_access = stm32_adc_debugfs_reg_access,
+	.of_xlate = stm32_adc_of_xlate,
+	.driver_module = THIS_MODULE,
+};
+
+static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
+				    struct iio_chan_spec *chan,
+				    const struct stm32_adc_chan_spec *channel,
+				    int scan_index)
+{
+	chan->type = channel->type;
+	chan->channel = channel->channel;
+	chan->datasheet_name = channel->name;
+	chan->scan_index = scan_index;
+	chan->indexed = 1;
+	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+	chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+	chan->scan_type.sign = 'u';
+	chan->scan_type.realbits = 12;
+	chan->scan_type.storagebits = 16;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	struct property *prop;
+	const __be32 *cur;
+	struct iio_chan_spec *channels;
+	int scan_index = 0, num_channels;
+	u32 val;
+
+	num_channels = of_property_count_u32_elems(node, "st,adc-channels");
+	if (num_channels < 0 ||
+	    num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+		return num_channels < 0 ? num_channels : -EINVAL;
+	}
+
+	channels = devm_kcalloc(&indio_dev->dev, num_channels,
+				sizeof(struct iio_chan_spec), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
+		if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+			return -EINVAL;
+		}
+		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+					&stm32f4_adc123_channels[val],
+					scan_index);
+		scan_index++;
+	}
+
+	indio_dev->num_channels = scan_index;
+	indio_dev->channels = channels;
+
+	return 0;
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct stm32_adc *adc;
+	int ret;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	adc = iio_priv(indio_dev);
+	adc->common = dev_get_drvdata(pdev->dev.parent);
+	spin_lock_init(&adc->lock);
+	init_completion(&adc->completion);
+
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	indio_dev->info = &stm32_adc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	platform_set_drvdata(pdev, adc);
+
+	ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	adc->irq = platform_get_irq(pdev, 0);
+	if (adc->irq < 0) {
+		dev_err(&pdev->dev, "failed to get irq\n");
+		return adc->irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
+			       0, pdev->name, adc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		return ret;
+	}
+
+	adc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(adc->clk)) {
+		dev_err(&pdev->dev, "Can't get clock\n");
+		return PTR_ERR(adc->clk);
+	}
+
+	ret = clk_prepare_enable(adc->clk);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "clk enable failed\n");
+		return ret;
+	}
+
+	ret = stm32_adc_chan_of_init(indio_dev);
+	if (ret < 0)
+		goto err_clk_disable;
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "iio dev register failed\n");
+		goto err_clk_disable;
+	}
+
+	return 0;
+
+err_clk_disable:
+	clk_disable_unprepare(adc->clk);
+
+	return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+	struct stm32_adc *adc = platform_get_drvdata(pdev);
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+	iio_device_unregister(indio_dev);
+	clk_disable_unprepare(adc->clk);
+
+	return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+	{ .compatible = "st,stm32f4-adc" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+	.probe = stm32_adc_probe,
+	.remove = stm32_adc_remove,
+	.driver = {
+		.name = "stm32-adc",
+		.of_match_table = stm32_adc_of_match,
+	},
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc");

+ 87 - 19
drivers/iio/adc/ti-adc0832.c

@@ -14,6 +14,10 @@
 #include <linux/spi/spi.h>
 #include <linux/iio/iio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
 
 enum {
 	adc0831,
@@ -38,10 +42,16 @@ struct adc0832 {
 		.indexed = 1,						\
 		.channel = chan,					\
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)	\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+		.scan_index = chan,					\
+		.scan_type = {						\
+			.sign = 'u',					\
+			.realbits = 8,					\
+			.storagebits = 8,				\
+		},							\
 	}
 
-#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2)			\
+#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si)			\
 	{								\
 		.type = IIO_VOLTAGE,					\
 		.indexed = 1,						\
@@ -49,18 +59,26 @@ struct adc0832 {
 		.channel2 = (chan2),					\
 		.differential = 1,					\
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)	\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+		.scan_index = si,					\
+		.scan_type = {						\
+			.sign = 'u',					\
+			.realbits = 8,					\
+			.storagebits = 8,				\
+		},							\
 	}
 
 static const struct iio_chan_spec adc0831_channels[] = {
-	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0),
+	IIO_CHAN_SOFT_TIMESTAMP(1),
 };
 
 static const struct iio_chan_spec adc0832_channels[] = {
 	ADC0832_VOLTAGE_CHANNEL(0),
 	ADC0832_VOLTAGE_CHANNEL(1),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
+	IIO_CHAN_SOFT_TIMESTAMP(4),
 };
 
 static const struct iio_chan_spec adc0834_channels[] = {
@@ -68,10 +86,11 @@ static const struct iio_chan_spec adc0834_channels[] = {
 	ADC0832_VOLTAGE_CHANNEL(1),
 	ADC0832_VOLTAGE_CHANNEL(2),
 	ADC0832_VOLTAGE_CHANNEL(3),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7),
+	IIO_CHAN_SOFT_TIMESTAMP(8),
 };
 
 static const struct iio_chan_spec adc0838_channels[] = {
@@ -83,14 +102,15 @@ static const struct iio_chan_spec adc0838_channels[] = {
 	ADC0832_VOLTAGE_CHANNEL(5),
 	ADC0832_VOLTAGE_CHANNEL(6),
 	ADC0832_VOLTAGE_CHANNEL(7),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7),
-	ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
+	ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+	IIO_CHAN_SOFT_TIMESTAMP(16),
 };
 
 static int adc0831_adc_conversion(struct adc0832 *adc)
@@ -178,6 +198,42 @@ static const struct iio_info adc0832_info = {
 	.driver_module = THIS_MODULE,
 };
 
+static irqreturn_t adc0832_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct adc0832 *adc = iio_priv(indio_dev);
+	u8 data[24] = { }; /* 16x 1 byte ADC data + 8 bytes timestamp */
+	int scan_index;
+	int i = 0;
+
+	mutex_lock(&adc->lock);
+
+	for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		const struct iio_chan_spec *scan_chan =
+				&indio_dev->channels[scan_index];
+		int ret = adc0832_adc_conversion(adc, scan_chan->channel,
+						 scan_chan->differential);
+		if (ret < 0) {
+			dev_warn(&adc->spi->dev,
+				 "failed to get conversion data\n");
+			goto out;
+		}
+
+		data[i] = ret;
+		i++;
+	}
+	iio_push_to_buffers_with_timestamp(indio_dev, data,
+					   iio_get_time_ns(indio_dev));
+out:
+	mutex_unlock(&adc->lock);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
 static int adc0832_probe(struct spi_device *spi)
 {
 	struct iio_dev *indio_dev;
@@ -233,9 +289,20 @@ static int adc0832_probe(struct spi_device *spi)
 
 	spi_set_drvdata(spi, indio_dev);
 
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 adc0832_trigger_handler, NULL);
+	if (ret)
+		goto err_reg_disable;
+
 	ret = iio_device_register(indio_dev);
 	if (ret)
-		regulator_disable(adc->reg);
+		goto err_buffer_cleanup;
+
+	return 0;
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_reg_disable:
+	regulator_disable(adc->reg);
 
 	return ret;
 }
@@ -246,6 +313,7 @@ static int adc0832_remove(struct spi_device *spi)
 	struct adc0832 *adc = iio_priv(indio_dev);
 
 	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
 	regulator_disable(adc->reg);
 
 	return 0;

+ 44 - 11
drivers/iio/adc/ti-adc161s626.c

@@ -27,6 +27,7 @@
 #include <linux/iio/buffer.h>
 #include <linux/iio/trigger_consumer.h>
 #include <linux/iio/triggered_buffer.h>
+#include <linux/regulator/consumer.h>
 
 #define TI_ADC_DRV_NAME	"ti-adc161s626"
 
@@ -39,7 +40,9 @@ static const struct iio_chan_spec ti_adc141s626_channels[] = {
 	{
 		.type = IIO_VOLTAGE,
 		.channel = 0,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE) |
+				      BIT(IIO_CHAN_INFO_OFFSET),
 		.scan_index = 0,
 		.scan_type = {
 			.sign = 's',
@@ -54,7 +57,9 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
 	{
 		.type = IIO_VOLTAGE,
 		.channel = 0,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE) |
+				      BIT(IIO_CHAN_INFO_OFFSET),
 		.scan_index = 0,
 		.scan_type = {
 			.sign = 's',
@@ -68,6 +73,8 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
 struct ti_adc_data {
 	struct iio_dev *indio_dev;
 	struct spi_device *spi;
+	struct regulator *ref;
+
 	u8 read_size;
 	u8 shift;
 
@@ -135,18 +142,32 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
 	struct ti_adc_data *data = iio_priv(indio_dev);
 	int ret;
 
-	if (mask != IIO_CHAN_INFO_RAW)
-		return -EINVAL;
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
 
-	ret = iio_device_claim_direct_mode(indio_dev);
-	if (ret)
-		return ret;
+		ret = ti_adc_read_measurement(data, chan, val);
+		iio_device_release_direct_mode(indio_dev);
 
-	ret = ti_adc_read_measurement(data, chan, val);
-	iio_device_release_direct_mode(indio_dev);
+		if (ret)
+			return ret;
 
-	if (!ret)
 		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		ret = regulator_get_voltage(data->ref);
+		if (ret < 0)
+			return ret;
+
+		*val = ret / 1000;
+		*val2 = chan->scan_type.realbits;
+
+		return IIO_VAL_FRACTIONAL_LOG2;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = 1 << (chan->scan_type.realbits - 1);
+		return IIO_VAL_INT;
+	}
 
 	return 0;
 }
@@ -191,10 +212,17 @@ static int ti_adc_probe(struct spi_device *spi)
 		break;
 	}
 
+	data->ref = devm_regulator_get(&spi->dev, "vdda");
+	if (!IS_ERR(data->ref)) {
+		ret = regulator_enable(data->ref);
+		if (ret < 0)
+			return ret;
+	}
+
 	ret = iio_triggered_buffer_setup(indio_dev, NULL,
 					 ti_adc_trigger_handler, NULL);
 	if (ret)
-		return ret;
+		goto error_regulator_disable;
 
 	ret = iio_device_register(indio_dev);
 	if (ret)
@@ -205,15 +233,20 @@ static int ti_adc_probe(struct spi_device *spi)
 error_unreg_buffer:
 	iio_triggered_buffer_cleanup(indio_dev);
 
+error_regulator_disable:
+	regulator_disable(data->ref);
+
 	return ret;
 }
 
 static int ti_adc_remove(struct spi_device *spi)
 {
 	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct ti_adc_data *data = iio_priv(indio_dev);
 
 	iio_device_unregister(indio_dev);
 	iio_triggered_buffer_cleanup(indio_dev);
+	regulator_disable(data->ref);
 
 	return 0;
 }

+ 145 - 3
drivers/iio/adc/ti_am335x_adc.c

@@ -30,10 +30,28 @@
 #include <linux/iio/buffer.h>
 #include <linux/iio/kfifo_buf.h>
 
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#define DMA_BUFFER_SIZE		SZ_2K
+
+struct tiadc_dma {
+	struct dma_slave_config	conf;
+	struct dma_chan		*chan;
+	dma_addr_t		addr;
+	dma_cookie_t		cookie;
+	u8			*buf;
+	int			current_period;
+	int			period_size;
+	u8			fifo_thresh;
+};
+
 struct tiadc_device {
 	struct ti_tscadc_dev *mfd_tscadc;
+	struct tiadc_dma dma;
 	struct mutex fifo1_lock; /* to protect fifo access */
 	int channels;
+	int total_ch_enabled;
 	u8 channel_line[8];
 	u8 channel_step[8];
 	int buffer_en_ch_steps;
@@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
 	return IRQ_HANDLED;
 }
 
+static void tiadc_dma_rx_complete(void *param)
+{
+	struct iio_dev *indio_dev = param;
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
+	u8 *data;
+	int i;
+
+	data = dma->buf + dma->current_period * dma->period_size;
+	dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
+
+	for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
+		iio_push_to_buffers(indio_dev, data);
+		data += indio_dev->scan_bytes;
+	}
+}
+
+static int tiadc_start_dma(struct iio_dev *indio_dev)
+{
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
+	struct dma_async_tx_descriptor *desc;
+
+	dma->current_period = 0; /* We start to fill period 0 */
+	/*
+	 * Make the fifo thresh as the multiple of total number of
+	 * channels enabled, so make sure that cyclic DMA period
+	 * length is also a multiple of total number of channels
+	 * enabled. This ensures that no invalid data is reported
+	 * to the stack via iio_push_to_buffers().
+	 */
+	dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
+				     adc_dev->total_ch_enabled) - 1;
+	/* Make sure that period length is multiple of fifo thresh level */
+	dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
+				    (dma->fifo_thresh + 1) * sizeof(u16));
+
+	dma->conf.src_maxburst = dma->fifo_thresh + 1;
+	dmaengine_slave_config(dma->chan, &dma->conf);
+
+	desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
+					 dma->period_size * 2,
+					 dma->period_size, DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT);
+	if (!desc)
+		return -EBUSY;
+
+	desc->callback = tiadc_dma_rx_complete;
+	desc->callback_param = indio_dev;
+
+	dma->cookie = dmaengine_submit(desc);
+
+	dma_async_issue_pending(dma->chan);
+
+	tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
+	tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
+	tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
+
+	return 0;
+}
+
 static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
 {
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
@@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
 static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
 {
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
+	unsigned int irq_enable;
 	unsigned int enb = 0;
 	u8 bit;
 
 	tiadc_step_config(indio_dev);
-	for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
+	for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
 		enb |= (get_adc_step_bit(adc_dev, bit) << 1);
+		adc_dev->total_ch_enabled++;
+	}
 	adc_dev->buffer_en_ch_steps = enb;
 
+	if (dma->chan)
+		tiadc_start_dma(indio_dev);
+
 	am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
 
 	tiadc_writel(adc_dev,  REG_IRQSTATUS, IRQENB_FIFO1THRES
 				| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
-	tiadc_writel(adc_dev,  REG_IRQENABLE, IRQENB_FIFO1THRES
-				| IRQENB_FIFO1OVRRUN);
+
+	irq_enable = IRQENB_FIFO1OVRRUN;
+	if (!dma->chan)
+		irq_enable |= IRQENB_FIFO1THRES;
+	tiadc_writel(adc_dev,  REG_IRQENABLE, irq_enable);
 
 	return 0;
 }
@@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
 static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
 {
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
 	int fifo1count, i, read;
 
 	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
 				IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
 	am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
 	adc_dev->buffer_en_ch_steps = 0;
+	adc_dev->total_ch_enabled = 0;
+	if (dma->chan) {
+		tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
+		dmaengine_terminate_async(dma->chan);
+	}
 
 	/* Flush FIFO of leftover data in the time it takes to disable adc */
 	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
@@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
 	.driver_module = THIS_MODULE,
 };
 
+static int tiadc_request_dma(struct platform_device *pdev,
+			     struct tiadc_device *adc_dev)
+{
+	struct tiadc_dma	*dma = &adc_dev->dma;
+	dma_cap_mask_t		mask;
+
+	/* Default slave configuration parameters */
+	dma->conf.direction = DMA_DEV_TO_MEM;
+	dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+
+	/* Get a channel for RX */
+	dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
+	if (IS_ERR(dma->chan)) {
+		int ret = PTR_ERR(dma->chan);
+
+		dma->chan = NULL;
+		return ret;
+	}
+
+	/* RX buffer */
+	dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+				      &dma->addr, GFP_KERNEL);
+	if (!dma->buf)
+		goto err;
+
+	return 0;
+err:
+	dma_release_channel(dma->chan);
+	return -ENOMEM;
+}
+
 static int tiadc_parse_dt(struct platform_device *pdev,
 			  struct tiadc_device *adc_dev)
 {
@@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, indio_dev);
 
+	err = tiadc_request_dma(pdev, adc_dev);
+	if (err && err == -EPROBE_DEFER)
+		goto err_dma;
+
 	return 0;
 
+err_dma:
+	iio_device_unregister(indio_dev);
 err_buffer_unregister:
 	tiadc_iio_buffered_hardware_remove(indio_dev);
 err_free_channels:
@@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
 {
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
 	u32 step_en;
 
+	if (dma->chan) {
+		dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+				  dma->buf, dma->addr);
+		dma_release_channel(dma->chan);
+	}
 	iio_device_unregister(indio_dev);
 	tiadc_iio_buffered_hardware_remove(indio_dev);
 	tiadc_channels_remove(indio_dev);

+ 1 - 0
drivers/iio/common/Kconfig

@@ -2,6 +2,7 @@
 # IIO common modules
 #
 
+source "drivers/iio/common/cros_ec_sensors/Kconfig"
 source "drivers/iio/common/hid-sensors/Kconfig"
 source "drivers/iio/common/ms_sensors/Kconfig"
 source "drivers/iio/common/ssp_sensors/Kconfig"

+ 1 - 0
drivers/iio/common/Makefile

@@ -7,6 +7,7 @@
 #
 
 # When adding new entries keep the list in alphabetical order
+obj-y += cros_ec_sensors/
 obj-y += hid-sensors/
 obj-y += ms_sensors/
 obj-y += ssp_sensors/

+ 22 - 0
drivers/iio/common/cros_ec_sensors/Kconfig

@@ -0,0 +1,22 @@
+#
+# Chrome OS Embedded Controller managed sensors library
+#
+config IIO_CROS_EC_SENSORS_CORE
+	tristate "ChromeOS EC Sensors Core"
+	depends on SYSFS && MFD_CROS_EC
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Base module for the ChromeOS EC Sensors module.
+	  Contains core functions used by other IIO CrosEC sensor
+	  drivers.
+	  Define common attributes and sysfs interrupt handler.
+
+config IIO_CROS_EC_SENSORS
+	tristate "ChromeOS EC Contiguous Sensors"
+	depends on IIO_CROS_EC_SENSORS_CORE
+	help
+	  Module to handle 3d contiguous sensors like
+	  Accelerometers, Gyroscope and Magnetometer that are
+	  presented by the ChromeOS EC Sensor hub.
+	  Creates an IIO device for each functions.

+ 6 - 0
drivers/iio/common/cros_ec_sensors/Makefile

@@ -0,0 +1,6 @@
+#
+# Makefile for sensors seen through the ChromeOS EC sensor hub.
+#
+
+obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
+obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o

+ 322 - 0
drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c

@@ -0,0 +1,322 @@
+/*
+ * cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the cros-ec interface to communicate with the Chrome OS
+ * EC about sensors data. Data access is presented through iio sysfs.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "cros_ec_sensors_core.h"
+
+#define CROS_EC_SENSORS_MAX_CHANNELS 4
+
+/* State data for ec_sensors iio driver. */
+struct cros_ec_sensors_state {
+	/* Shared by all sensors */
+	struct cros_ec_sensors_core_state core;
+
+	struct iio_chan_spec channels[CROS_EC_SENSORS_MAX_CHANNELS];
+};
+
+static int cros_ec_sensors_read(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val, int *val2, long mask)
+{
+	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
+	s16 data = 0;
+	s64 val64;
+	int i;
+	int ret;
+	int idx = chan->scan_index;
+
+	mutex_lock(&st->core.cmd_lock);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = st->core.read_ec_sensors_data(indio_dev, 1 << idx, &data);
+		if (ret < 0)
+			break;
+
+		*val = data;
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
+		st->core.param.sensor_offset.flags = 0;
+
+		ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+		if (ret < 0)
+			break;
+
+		/* Save values */
+		for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+			st->core.calib[i] =
+				st->core.resp->sensor_offset.offset[i];
+
+		*val = st->core.calib[idx];
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
+		st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
+
+		ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+		if (ret < 0)
+			break;
+
+		val64 = st->core.resp->sensor_range.ret;
+		switch (st->core.type) {
+		case MOTIONSENSE_TYPE_ACCEL:
+			/*
+			 * EC returns data in g, iio exepects m/s^2.
+			 * Do not use IIO_G_TO_M_S_2 to avoid precision loss.
+			 */
+			*val = div_s64(val64 * 980665, 10);
+			*val2 = 10000 << (CROS_EC_SENSOR_BITS - 1);
+			ret = IIO_VAL_FRACTIONAL;
+			break;
+		case MOTIONSENSE_TYPE_GYRO:
+			/*
+			 * EC returns data in dps, iio expects rad/s.
+			 * Do not use IIO_DEGREE_TO_RAD to avoid precision
+			 * loss. Round to the nearest integer.
+			 */
+			*val = div_s64(val64 * 314159 + 9000000ULL, 1000);
+			*val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
+			ret = IIO_VAL_FRACTIONAL;
+			break;
+		case MOTIONSENSE_TYPE_MAG:
+			/*
+			 * EC returns data in 16LSB / uT,
+			 * iio expects Gauss
+			 */
+			*val = val64;
+			*val2 = 100 << (CROS_EC_SENSOR_BITS - 1);
+			ret = IIO_VAL_FRACTIONAL;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
+						mask);
+		break;
+	}
+	mutex_unlock(&st->core.cmd_lock);
+
+	return ret;
+}
+
+static int cros_ec_sensors_write(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask)
+{
+	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
+	int i;
+	int ret;
+	int idx = chan->scan_index;
+
+	mutex_lock(&st->core.cmd_lock);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		st->core.calib[idx] = val;
+
+		/* Send to EC for each axis, even if not complete */
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
+		st->core.param.sensor_offset.flags =
+			MOTION_SENSE_SET_OFFSET;
+		for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+			st->core.param.sensor_offset.offset[i] =
+				st->core.calib[i];
+		st->core.param.sensor_offset.temp =
+			EC_MOTION_SENSE_INVALID_CALIB_TEMP;
+
+		ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		if (st->core.type == MOTIONSENSE_TYPE_MAG) {
+			ret = -EINVAL;
+			break;
+		}
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
+		st->core.param.sensor_range.data = val;
+
+		/* Always roundup, so caller gets at least what it asks for. */
+		st->core.param.sensor_range.roundup = 1;
+
+		ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+		break;
+	default:
+		ret = cros_ec_sensors_core_write(
+				&st->core, chan, val, val2, mask);
+		break;
+	}
+
+	mutex_unlock(&st->core.cmd_lock);
+
+	return ret;
+}
+
+static const struct iio_info ec_sensors_info = {
+	.read_raw = &cros_ec_sensors_read,
+	.write_raw = &cros_ec_sensors_write,
+	.driver_module = THIS_MODULE,
+};
+
+static int cros_ec_sensors_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+	struct cros_ec_device *ec_device;
+	struct iio_dev *indio_dev;
+	struct cros_ec_sensors_state *state;
+	struct iio_chan_spec *channel;
+	int ret, i;
+
+	if (!ec_dev || !ec_dev->ec_dev) {
+		dev_warn(&pdev->dev, "No CROS EC device found.\n");
+		return -EINVAL;
+	}
+	ec_device = ec_dev->ec_dev;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+	if (ret)
+		return ret;
+
+	indio_dev->info = &ec_sensors_info;
+	state = iio_priv(indio_dev);
+	for (channel = state->channels, i = CROS_EC_SENSOR_X;
+	     i < CROS_EC_SENSOR_MAX_AXIS; i++, channel++) {
+		/* Common part */
+		channel->info_mask_separate =
+			BIT(IIO_CHAN_INFO_RAW) |
+			BIT(IIO_CHAN_INFO_CALIBBIAS);
+		channel->info_mask_shared_by_all =
+			BIT(IIO_CHAN_INFO_SCALE) |
+			BIT(IIO_CHAN_INFO_FREQUENCY) |
+			BIT(IIO_CHAN_INFO_SAMP_FREQ);
+		channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
+		channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
+		channel->scan_index = i;
+		channel->ext_info = cros_ec_sensors_ext_info;
+		channel->modified = 1;
+		channel->channel2 = IIO_MOD_X + i;
+		channel->scan_type.sign = 's';
+
+		/* Sensor specific */
+		switch (state->core.type) {
+		case MOTIONSENSE_TYPE_ACCEL:
+			channel->type = IIO_ACCEL;
+			break;
+		case MOTIONSENSE_TYPE_GYRO:
+			channel->type = IIO_ANGL_VEL;
+			break;
+		case MOTIONSENSE_TYPE_MAG:
+			channel->type = IIO_MAGN;
+			break;
+		default:
+			dev_err(&pdev->dev, "Unknown motion sensor\n");
+			return -EINVAL;
+		}
+	}
+
+	/* Timestamp */
+	channel->type = IIO_TIMESTAMP;
+	channel->channel = -1;
+	channel->scan_index = CROS_EC_SENSOR_MAX_AXIS;
+	channel->scan_type.sign = 's';
+	channel->scan_type.realbits = 64;
+	channel->scan_type.storagebits = 64;
+
+	indio_dev->channels = state->channels;
+	indio_dev->num_channels = CROS_EC_SENSORS_MAX_CHANNELS;
+
+	/* There is only enough room for accel and gyro in the io space */
+	if ((state->core.ec->cmd_readmem != NULL) &&
+	    (state->core.type != MOTIONSENSE_TYPE_MAG))
+		state->core.read_ec_sensors_data = cros_ec_sensors_read_lpc;
+	else
+		state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 cros_ec_sensors_capture, NULL);
+	if (ret)
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_uninit_buffer;
+
+	return 0;
+
+error_uninit_buffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	return ret;
+}
+
+static int cros_ec_sensors_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	return 0;
+}
+
+static const struct platform_device_id cros_ec_sensors_ids[] = {
+	{
+		.name = "cros-ec-accel",
+	},
+	{
+		.name = "cros-ec-gyro",
+	},
+	{
+		.name = "cros-ec-mag",
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
+
+static struct platform_driver cros_ec_sensors_platform_driver = {
+	.driver = {
+		.name	= "cros-ec-sensors",
+	},
+	.probe		= cros_ec_sensors_probe,
+	.remove		= cros_ec_sensors_remove,
+	.id_table	= cros_ec_sensors_ids,
+};
+module_platform_driver(cros_ec_sensors_platform_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC 3-axis sensors driver");
+MODULE_LICENSE("GPL v2");

+ 450 - 0
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c

@@ -0,0 +1,450 @@
+/*
+ * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+
+#include "cros_ec_sensors_core.h"
+
+static char *cros_ec_loc[] = {
+	[MOTIONSENSE_LOC_BASE] = "base",
+	[MOTIONSENSE_LOC_LID] = "lid",
+	[MOTIONSENSE_LOC_MAX] = "unknown",
+};
+
+int cros_ec_sensors_core_init(struct platform_device *pdev,
+			      struct iio_dev *indio_dev,
+			      bool physical_device)
+{
+	struct device *dev = &pdev->dev;
+	struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
+	struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
+	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	state->ec = ec->ec_dev;
+	state->msg = devm_kzalloc(&pdev->dev,
+				max((u16)sizeof(struct ec_params_motion_sense),
+				state->ec->max_response), GFP_KERNEL);
+	if (!state->msg)
+		return -ENOMEM;
+
+	state->resp = (struct ec_response_motion_sense *)state->msg->data;
+
+	mutex_init(&state->cmd_lock);
+
+	/* Set up the host command structure. */
+	state->msg->version = 2;
+	state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+	state->msg->outsize = sizeof(struct ec_params_motion_sense);
+
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->name = pdev->name;
+
+	if (physical_device) {
+		indio_dev->modes = INDIO_DIRECT_MODE;
+
+		state->param.cmd = MOTIONSENSE_CMD_INFO;
+		state->param.info.sensor_num = sensor_platform->sensor_num;
+		if (cros_ec_motion_send_host_cmd(state, 0)) {
+			dev_warn(dev, "Can not access sensor info\n");
+			return -EIO;
+		}
+		state->type = state->resp->info.type;
+		state->loc = state->resp->info.location;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
+
+int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
+				 u16 opt_length)
+{
+	int ret;
+
+	if (opt_length)
+		state->msg->insize = min(opt_length, state->ec->max_response);
+	else
+		state->msg->insize = state->ec->max_response;
+
+	memcpy(state->msg->data, &state->param, sizeof(state->param));
+
+	ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
+	if (ret < 0)
+		return -EIO;
+
+	if (ret &&
+	    state->resp != (struct ec_response_motion_sense *)state->msg->data)
+		memcpy(state->resp, state->msg->data, ret);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
+
+static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
+		uintptr_t private, const struct iio_chan_spec *chan,
+		const char *buf, size_t len)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int ret, i;
+	bool calibrate;
+
+	ret = strtobool(buf, &calibrate);
+	if (ret < 0)
+		return ret;
+	if (!calibrate)
+		return -EINVAL;
+
+	mutex_lock(&st->cmd_lock);
+	st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
+	ret = cros_ec_motion_send_host_cmd(st, 0);
+	if (ret != 0) {
+		dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
+	} else {
+		/* Save values */
+		for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+			st->calib[i] = st->resp->perform_calib.offset[i];
+	}
+	mutex_unlock(&st->cmd_lock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
+		uintptr_t private, const struct iio_chan_spec *chan,
+		char *buf)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", cros_ec_loc[st->loc]);
+}
+
+const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
+	{
+		.name = "calibrate",
+		.shared = IIO_SHARED_BY_ALL,
+		.write = cros_ec_sensors_calibrate
+	},
+	{
+		.name = "location",
+		.shared = IIO_SHARED_BY_ALL,
+		.read = cros_ec_sensors_loc
+	},
+	{ },
+};
+EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
+
+/**
+ * cros_ec_sensors_idx_to_reg - convert index into offset in shared memory
+ * @st:		pointer to state information for device
+ * @idx:	sensor index (should be element of enum sensor_index)
+ *
+ * Return:	address to read at
+ */
+static unsigned int cros_ec_sensors_idx_to_reg(
+					struct cros_ec_sensors_core_state *st,
+					unsigned int idx)
+{
+	/*
+	 * When using LPC interface, only space for 2 Accel and one Gyro.
+	 * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
+	 */
+	if (st->type == MOTIONSENSE_TYPE_ACCEL)
+		return EC_MEMMAP_ACC_DATA + sizeof(u16) *
+			(1 + idx + st->param.info.sensor_num *
+			 CROS_EC_SENSOR_MAX_AXIS);
+
+	return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
+}
+
+static int cros_ec_sensors_cmd_read_u8(struct cros_ec_device *ec,
+				       unsigned int offset, u8 *dest)
+{
+	return ec->cmd_readmem(ec, offset, 1, dest);
+}
+
+static int cros_ec_sensors_cmd_read_u16(struct cros_ec_device *ec,
+					 unsigned int offset, u16 *dest)
+{
+	__le16 tmp;
+	int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
+
+	if (ret >= 0)
+		*dest = le16_to_cpu(tmp);
+
+	return ret;
+}
+
+/**
+ * cros_ec_sensors_read_until_not_busy() - read until is not busy
+ *
+ * @st:	pointer to state information for device
+ *
+ * Read from EC status byte until it reads not busy.
+ * Return: 8-bit status if ok, -errno on failure.
+ */
+static int cros_ec_sensors_read_until_not_busy(
+					struct cros_ec_sensors_core_state *st)
+{
+	struct cros_ec_device *ec = st->ec;
+	u8 status;
+	int ret, attempts = 0;
+
+	ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
+	if (ret < 0)
+		return ret;
+
+	while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
+		/* Give up after enough attempts, return error. */
+		if (attempts++ >= 50)
+			return -EIO;
+
+		/* Small delay every so often. */
+		if (attempts % 5 == 0)
+			msleep(25);
+
+		ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
+						  &status);
+		if (ret < 0)
+			return ret;
+	}
+
+	return status;
+}
+
+/**
+ * read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory
+ * @indio_dev:	pointer to IIO device
+ * @scan_mask:	bitmap of the sensor indices to scan
+ * @data:	location to store data
+ *
+ * This is the unsafe function for reading the EC data. It does not guarantee
+ * that the EC will not modify the data as it is being read in.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev,
+			 unsigned long scan_mask, s16 *data)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	struct cros_ec_device *ec = st->ec;
+	unsigned int i;
+	int ret;
+
+	/* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
+	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+		ret = cros_ec_sensors_cmd_read_u16(ec,
+					     cros_ec_sensors_idx_to_reg(st, i),
+					     data);
+		if (ret < 0)
+			return ret;
+
+		data++;
+	}
+
+	return 0;
+}
+
+int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
+			     unsigned long scan_mask, s16 *data)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	struct cros_ec_device *ec = st->ec;
+	u8 samp_id = 0xff, status = 0;
+	int ret, attempts = 0;
+
+	/*
+	 * Continually read all data from EC until the status byte after
+	 * all reads reflects that the EC is not busy and the sample id
+	 * matches the sample id from before all reads. This guarantees
+	 * that data read in was not modified by the EC while reading.
+	 */
+	while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
+			  EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
+		/* If we have tried to read too many times, return error. */
+		if (attempts++ >= 5)
+			return -EIO;
+
+		/* Read status byte until EC is not busy. */
+		ret = cros_ec_sensors_read_until_not_busy(st);
+		if (ret < 0)
+			return ret;
+
+		/*
+		 * Store the current sample id so that we can compare to the
+		 * sample id after reading the data.
+		 */
+		samp_id = ret & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
+
+		/* Read all EC data, format it, and store it into data. */
+		ret = cros_ec_sensors_read_data_unsafe(indio_dev, scan_mask,
+						       data);
+		if (ret < 0)
+			return ret;
+
+		/* Read status byte. */
+		ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
+						  &status);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
+
+int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
+			     unsigned long scan_mask, s16 *data)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int ret;
+	unsigned int i;
+
+	/* Read all sensor data through a command. */
+	st->param.cmd = MOTIONSENSE_CMD_DATA;
+	ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
+	if (ret != 0) {
+		dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
+		return ret;
+	}
+
+	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+		*data = st->resp->data.data[i];
+		data++;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
+
+irqreturn_t cros_ec_sensors_capture(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&st->cmd_lock);
+
+	/* Clear capture data. */
+	memset(st->samples, 0, indio_dev->scan_bytes);
+
+	/* Read data based on which channels are enabled in scan mask. */
+	ret = st->read_ec_sensors_data(indio_dev,
+				       *(indio_dev->active_scan_mask),
+				       (s16 *)st->samples);
+	if (ret < 0)
+		goto done;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
+					   iio_get_time_ns(indio_dev));
+
+done:
+	/*
+	 * Tell the core we are done with this trigger and ready for the
+	 * next one.
+	 */
+	iio_trigger_notify_done(indio_dev->trig);
+
+	mutex_unlock(&st->cmd_lock);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
+
+int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
+			  struct iio_chan_spec const *chan,
+			  int *val, int *val2, long mask)
+{
+	int ret = IIO_VAL_INT;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+		st->param.ec_rate.data =
+			EC_MOTION_SENSE_NO_VALUE;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		else
+			*val = st->resp->ec_rate.ret;
+		break;
+	case IIO_CHAN_INFO_FREQUENCY:
+		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
+		st->param.sensor_odr.data =
+			EC_MOTION_SENSE_NO_VALUE;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		else
+			*val = st->resp->sensor_odr.ret;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
+
+int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask)
+{
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_FREQUENCY:
+		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
+		st->param.sensor_odr.data = val;
+
+		/* Always roundup, so caller gets at least what it asks for. */
+		st->param.sensor_odr.roundup = 1;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+		st->param.ec_rate.data = val;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		else
+			st->curr_sampl_freq = val;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
+
+MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
+MODULE_LICENSE("GPL v2");

+ 175 - 0
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h

@@ -0,0 +1,175 @@
+/*
+ * ChromeOS EC sensor hub
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CROS_EC_SENSORS_CORE_H
+#define __CROS_EC_SENSORS_CORE_H
+
+#include <linux/irqreturn.h>
+
+enum {
+	CROS_EC_SENSOR_X,
+	CROS_EC_SENSOR_Y,
+	CROS_EC_SENSOR_Z,
+	CROS_EC_SENSOR_MAX_AXIS,
+};
+
+/* EC returns sensor values using signed 16 bit registers */
+#define CROS_EC_SENSOR_BITS 16
+
+/*
+ * 4 16 bit channels are allowed.
+ * Good enough for current sensors, they use up to 3 16 bit vectors.
+ */
+#define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
+
+/* Minimum sampling period to use when device is suspending */
+#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
+
+/**
+ * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
+ * @ec:				cros EC device structure
+ * @cmd_lock:			lock used to prevent simultaneous access to the
+ *				commands.
+ * @msg:			cros EC command structure
+ * @param:			motion sensor parameters structure
+ * @resp:			motion sensor response structure
+ * @type:			type of motion sensor
+ * @loc:			location where the motion sensor is placed
+ * @calib:			calibration parameters. Note that trigger
+ *				captured data will always provide the calibrated
+ *				data
+ * @samples:			static array to hold data from a single capture.
+ *				For each channel we need 2 bytes, except for
+ *				the timestamp. The timestamp is always last and
+ *				is always 8-byte aligned.
+ * @read_ec_sensors_data:	function used for accessing sensors values
+ * @cuur_sampl_freq:		current sampling period
+ */
+struct cros_ec_sensors_core_state {
+	struct cros_ec_device *ec;
+	struct mutex cmd_lock;
+
+	struct cros_ec_command *msg;
+	struct ec_params_motion_sense param;
+	struct ec_response_motion_sense *resp;
+
+	enum motionsensor_type type;
+	enum motionsensor_location loc;
+
+	s16 calib[CROS_EC_SENSOR_MAX_AXIS];
+
+	u8 samples[CROS_EC_SAMPLE_SIZE];
+
+	int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
+				    unsigned long scan_mask, s16 *data);
+
+	int curr_sampl_freq;
+};
+
+/**
+ * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory
+ * @indio_dev:	pointer to IIO device
+ * @scan_mask:	bitmap of the sensor indices to scan
+ * @data:	location to store data
+ *
+ * This is the safe function for reading the EC data. It guarantees that the
+ * data sampled was not modified by the EC while being read.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
+			     s16 *data);
+
+/**
+ * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
+ * @indio_dev:	pointer to IIO device
+ * @scan_mask:	bitmap of the sensor indices to scan
+ * @data:	location to store data
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
+			     s16 *data);
+
+/**
+ * cros_ec_sensors_core_init() - basic initialization of the core structure
+ * @pdev:		platform device created for the sensors
+ * @indio_dev:		iio device structure of the device
+ * @physical_device:	true if the device refers to a physical device
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_sensors_core_init(struct platform_device *pdev,
+			      struct iio_dev *indio_dev, bool physical_device);
+
+/**
+ * cros_ec_sensors_capture() - the trigger handler function
+ * @irq:	the interrupt number.
+ * @p:		a pointer to the poll function.
+ *
+ * On a trigger event occurring, if the pollfunc is attached then this
+ * handler is called as a threaded interrupt (and hence may sleep). It
+ * is responsible for grabbing data from the device and pushing it into
+ * the associated buffer.
+ *
+ * Return: IRQ_HANDLED
+ */
+irqreturn_t cros_ec_sensors_capture(int irq, void *p);
+
+/**
+ * cros_ec_motion_send_host_cmd() - send motion sense host command
+ * @st:		pointer to state information for device
+ * @opt_length:	optional length to reduce the response size, useful on the data
+ *		path. Otherwise, the maximal allowed response size is used
+ *
+ * When called, the sub-command is assumed to be set in param->cmd.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
+				 u16 opt_length);
+
+/**
+ * cros_ec_sensors_core_read() - function to request a value from the sensor
+ * @st:		pointer to state information for device
+ * @chan:	channel specification structure table
+ * @val:	will contain one element making up the returned value
+ * @val2:	will contain another element making up the returned value
+ * @mask:	specifies which values to be requested
+ *
+ * Return:	the type of value returned by the device
+ */
+int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2, long mask);
+
+/**
+ * cros_ec_sensors_core_write() - function to write a value to the sensor
+ * @st:		pointer to state information for device
+ * @chan:	channel specification structure table
+ * @val:	first part of value to write
+ * @val2:	second part of value to write
+ * @mask:	specifies which values to write
+ *
+ * Return:	the type of value returned by the device
+ */
+int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask);
+
+/* List of extended channel specification for all sensors */
+extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
+
+#endif  /* __CROS_EC_SENSORS_CORE_H */

+ 4 - 1
drivers/iio/common/hid-sensors/hid-sensor-attributes.c

@@ -201,7 +201,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
 	int ret;
 
 	if (val1 < 0 || val2 < 0)
-		ret = -EINVAL;
+		return -EINVAL;
 
 	value = val1 * pow_10(6) + val2;
 	if (value) {
@@ -250,6 +250,9 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
 	s32 value;
 	int ret;
 
+	if (val1 < 0 || val2 < 0)
+		return -EINVAL;
+
 	value = convert_to_vtf_format(st->sensitivity.size,
 				st->sensitivity.unit_expo,
 				val1, val2);

+ 593 - 0
drivers/iio/counter/104-quad-8.c

@@ -0,0 +1,593 @@
+/*
+ * IIO driver for the ACCES 104-QUAD-8
+ * Copyright (C) 2016 William Breathitt Gray
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/isa.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+
+#define QUAD8_EXTENT 32
+
+static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
+static unsigned int num_quad8;
+module_param_array(base, uint, &num_quad8, 0);
+MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
+
+#define QUAD8_NUM_COUNTERS 8
+
+/**
+ * struct quad8_iio - IIO device private data structure
+ * @preset:		array of preset values
+ * @count_mode:		array of count mode configurations
+ * @quadrature_mode:	array of quadrature mode configurations
+ * @quadrature_scale:	array of quadrature mode scale configurations
+ * @ab_enable:		array of A and B inputs enable configurations
+ * @preset_enable:	array of set_to_preset_on_index attribute configurations
+ * @synchronous_mode:	array of index function synchronous mode configurations
+ * @index_polarity:	array of index function polarity configurations
+ * @base:		base port address of the IIO device
+ */
+struct quad8_iio {
+	unsigned int preset[QUAD8_NUM_COUNTERS];
+	unsigned int count_mode[QUAD8_NUM_COUNTERS];
+	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
+	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
+	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
+	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
+	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
+	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
+	unsigned int base;
+};
+
+static int quad8_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const int base_offset = priv->base + 2 * chan->channel;
+	unsigned int flags;
+	unsigned int borrow;
+	unsigned int carry;
+	int i;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (chan->type == IIO_INDEX) {
+			*val = !!(inb(priv->base + 0x16) & BIT(chan->channel));
+			return IIO_VAL_INT;
+		}
+
+		flags = inb(base_offset);
+		borrow = flags & BIT(0);
+		carry = !!(flags & BIT(1));
+
+		/* Borrow XOR Carry effectively doubles count range */
+		*val = (borrow ^ carry) << 24;
+
+		/* Reset Byte Pointer; transfer Counter to Output Latch */
+		outb(0x11, base_offset + 1);
+
+		for (i = 0; i < 3; i++)
+			*val |= (unsigned int)inb(base_offset) << (8 * i);
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_ENABLE:
+		*val = priv->ab_enable[chan->channel];
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 1;
+		*val2 = priv->quadrature_scale[chan->channel];
+		return IIO_VAL_FRACTIONAL_LOG2;
+	}
+
+	return -EINVAL;
+}
+
+static int quad8_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const int base_offset = priv->base + 2 * chan->channel;
+	int i;
+	unsigned int ior_cfg;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (chan->type == IIO_INDEX)
+			return -EINVAL;
+
+		/* Only 24-bit values are supported */
+		if ((unsigned int)val > 0xFFFFFF)
+			return -EINVAL;
+
+		/* Reset Byte Pointer */
+		outb(0x01, base_offset + 1);
+
+		/* Counter can only be set via Preset Register */
+		for (i = 0; i < 3; i++)
+			outb(val >> (8 * i), base_offset);
+
+		/* Transfer Preset Register to Counter */
+		outb(0x08, base_offset + 1);
+
+		/* Reset Byte Pointer */
+		outb(0x01, base_offset + 1);
+
+		/* Set Preset Register back to original value */
+		val = priv->preset[chan->channel];
+		for (i = 0; i < 3; i++)
+			outb(val >> (8 * i), base_offset);
+
+		/* Reset Borrow, Carry, Compare, and Sign flags */
+		outb(0x02, base_offset + 1);
+		/* Reset Error flag */
+		outb(0x06, base_offset + 1);
+
+		return 0;
+	case IIO_CHAN_INFO_ENABLE:
+		/* only boolean values accepted */
+		if (val < 0 || val > 1)
+			return -EINVAL;
+
+		priv->ab_enable[chan->channel] = val;
+
+		ior_cfg = val | priv->preset_enable[chan->channel] << 1;
+
+		/* Load I/O control configuration */
+		outb(0x40 | ior_cfg, base_offset);
+
+		return 0;
+	case IIO_CHAN_INFO_SCALE:
+		/* Quadrature scaling only available in quadrature mode */
+		if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
+			return -EINVAL;
+
+		/* Only three gain states (1, 0.5, 0.25) */
+		if (val == 1 && !val2)
+			priv->quadrature_scale[chan->channel] = 0;
+		else if (!val)
+			switch (val2) {
+			case 500000:
+				priv->quadrature_scale[chan->channel] = 1;
+				break;
+			case 250000:
+				priv->quadrature_scale[chan->channel] = 2;
+				break;
+			default:
+				return -EINVAL;
+			}
+		else
+			return -EINVAL;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info quad8_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = quad8_read_raw,
+	.write_raw = quad8_write_raw
+};
+
+static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
+	const struct iio_chan_spec *chan, char *buf)
+{
+	const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
+}
+
+static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
+	const struct iio_chan_spec *chan, const char *buf, size_t len)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const int base_offset = priv->base + 2 * chan->channel;
+	unsigned int preset;
+	int ret;
+	int i;
+
+	ret = kstrtouint(buf, 0, &preset);
+	if (ret)
+		return ret;
+
+	/* Only 24-bit values are supported */
+	if (preset > 0xFFFFFF)
+		return -EINVAL;
+
+	priv->preset[chan->channel] = preset;
+
+	/* Reset Byte Pointer */
+	outb(0x01, base_offset + 1);
+
+	/* Set Preset Register */
+	for (i = 0; i < 3; i++)
+		outb(preset >> (8 * i), base_offset);
+
+	return len;
+}
+
+static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
+	uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+	const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+		priv->preset_enable[chan->channel]);
+}
+
+static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
+	uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+	size_t len)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const int base_offset = priv->base + 2 * chan->channel;
+	bool preset_enable;
+	int ret;
+	unsigned int ior_cfg;
+
+	ret = kstrtobool(buf, &preset_enable);
+	if (ret)
+		return ret;
+
+	priv->preset_enable[chan->channel] = preset_enable;
+
+	ior_cfg = priv->ab_enable[chan->channel] |
+		(unsigned int)preset_enable << 1;
+
+	/* Load I/O control configuration to Input / Output Control Register */
+	outb(0x40 | ior_cfg, base_offset);
+
+	return len;
+}
+
+static const char *const quad8_noise_error_states[] = {
+	"No excessive noise is present at the count inputs",
+	"Excessive noise is present at the count inputs"
+};
+
+static int quad8_get_noise_error(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const int base_offset = priv->base + 2 * chan->channel + 1;
+
+	return !!(inb(base_offset) & BIT(4));
+}
+
+static const struct iio_enum quad8_noise_error_enum = {
+	.items = quad8_noise_error_states,
+	.num_items = ARRAY_SIZE(quad8_noise_error_states),
+	.get = quad8_get_noise_error
+};
+
+static const char *const quad8_count_direction_states[] = {
+	"down",
+	"up"
+};
+
+static int quad8_get_count_direction(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const int base_offset = priv->base + 2 * chan->channel + 1;
+
+	return !!(inb(base_offset) & BIT(5));
+}
+
+static const struct iio_enum quad8_count_direction_enum = {
+	.items = quad8_count_direction_states,
+	.num_items = ARRAY_SIZE(quad8_count_direction_states),
+	.get = quad8_get_count_direction
+};
+
+static const char *const quad8_count_modes[] = {
+	"normal",
+	"range limit",
+	"non-recycle",
+	"modulo-n"
+};
+
+static int quad8_set_count_mode(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int count_mode)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	unsigned int mode_cfg = count_mode << 1;
+	const int base_offset = priv->base + 2 * chan->channel + 1;
+
+	priv->count_mode[chan->channel] = count_mode;
+
+	/* Add quadrature mode configuration */
+	if (priv->quadrature_mode[chan->channel])
+		mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
+
+	/* Load mode configuration to Counter Mode Register */
+	outb(0x20 | mode_cfg, base_offset);
+
+	return 0;
+}
+
+static int quad8_get_count_mode(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+	return priv->count_mode[chan->channel];
+}
+
+static const struct iio_enum quad8_count_mode_enum = {
+	.items = quad8_count_modes,
+	.num_items = ARRAY_SIZE(quad8_count_modes),
+	.set = quad8_set_count_mode,
+	.get = quad8_get_count_mode
+};
+
+static const char *const quad8_synchronous_modes[] = {
+	"non-synchronous",
+	"synchronous"
+};
+
+static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int synchronous_mode)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const unsigned int idr_cfg = synchronous_mode |
+		priv->index_polarity[chan->channel] << 1;
+	const int base_offset = priv->base + 2 * chan->channel + 1;
+
+	/* Index function must be non-synchronous in non-quadrature mode */
+	if (synchronous_mode && !priv->quadrature_mode[chan->channel])
+		return -EINVAL;
+
+	priv->synchronous_mode[chan->channel] = synchronous_mode;
+
+	/* Load Index Control configuration to Index Control Register */
+	outb(0x40 | idr_cfg, base_offset);
+
+	return 0;
+}
+
+static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+	return priv->synchronous_mode[chan->channel];
+}
+
+static const struct iio_enum quad8_synchronous_mode_enum = {
+	.items = quad8_synchronous_modes,
+	.num_items = ARRAY_SIZE(quad8_synchronous_modes),
+	.set = quad8_set_synchronous_mode,
+	.get = quad8_get_synchronous_mode
+};
+
+static const char *const quad8_quadrature_modes[] = {
+	"non-quadrature",
+	"quadrature"
+};
+
+static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int quadrature_mode)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
+	const int base_offset = priv->base + 2 * chan->channel + 1;
+
+	if (quadrature_mode)
+		mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
+	else {
+		/* Quadrature scaling only available in quadrature mode */
+		priv->quadrature_scale[chan->channel] = 0;
+
+		/* Synchronous function not supported in non-quadrature mode */
+		if (priv->synchronous_mode[chan->channel])
+			quad8_set_synchronous_mode(indio_dev, chan, 0);
+	}
+
+	priv->quadrature_mode[chan->channel] = quadrature_mode;
+
+	/* Load mode configuration to Counter Mode Register */
+	outb(0x20 | mode_cfg, base_offset);
+
+	return 0;
+}
+
+static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+	return priv->quadrature_mode[chan->channel];
+}
+
+static const struct iio_enum quad8_quadrature_mode_enum = {
+	.items = quad8_quadrature_modes,
+	.num_items = ARRAY_SIZE(quad8_quadrature_modes),
+	.set = quad8_set_quadrature_mode,
+	.get = quad8_get_quadrature_mode
+};
+
+static const char *const quad8_index_polarity_modes[] = {
+	"negative",
+	"positive"
+};
+
+static int quad8_set_index_polarity(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int index_polarity)
+{
+	struct quad8_iio *const priv = iio_priv(indio_dev);
+	const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
+		index_polarity << 1;
+	const int base_offset = priv->base + 2 * chan->channel + 1;
+
+	priv->index_polarity[chan->channel] = index_polarity;
+
+	/* Load Index Control configuration to Index Control Register */
+	outb(0x40 | idr_cfg, base_offset);
+
+	return 0;
+}
+
+static int quad8_get_index_polarity(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+	return priv->index_polarity[chan->channel];
+}
+
+static const struct iio_enum quad8_index_polarity_enum = {
+	.items = quad8_index_polarity_modes,
+	.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
+	.set = quad8_set_index_polarity,
+	.get = quad8_get_index_polarity
+};
+
+static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
+	{
+		.name = "preset",
+		.shared = IIO_SEPARATE,
+		.read = quad8_read_preset,
+		.write = quad8_write_preset
+	},
+	{
+		.name = "set_to_preset_on_index",
+		.shared = IIO_SEPARATE,
+		.read = quad8_read_set_to_preset_on_index,
+		.write = quad8_write_set_to_preset_on_index
+	},
+	IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
+	IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
+	IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
+	IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
+	IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
+	IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
+	IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
+	IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
+	{}
+};
+
+static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
+	IIO_ENUM("synchronous_mode", IIO_SEPARATE,
+		&quad8_synchronous_mode_enum),
+	IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
+	IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
+	IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
+	{}
+};
+
+#define QUAD8_COUNT_CHAN(_chan) {					\
+	.type = IIO_COUNT,						\
+	.channel = (_chan),						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+		BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE),	\
+	.ext_info = quad8_count_ext_info,				\
+	.indexed = 1							\
+}
+
+#define QUAD8_INDEX_CHAN(_chan) {			\
+	.type = IIO_INDEX,				\
+	.channel = (_chan),				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+	.ext_info = quad8_index_ext_info,		\
+	.indexed = 1					\
+}
+
+static const struct iio_chan_spec quad8_channels[] = {
+	QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
+	QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
+	QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
+	QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
+	QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
+	QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
+	QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
+	QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
+};
+
+static int quad8_probe(struct device *dev, unsigned int id)
+{
+	struct iio_dev *indio_dev;
+	struct quad8_iio *priv;
+	int i, j;
+	unsigned int base_offset;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
+		dev_name(dev))) {
+		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+			base[id], base[id] + QUAD8_EXTENT);
+		return -EBUSY;
+	}
+
+	indio_dev->info = &quad8_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
+	indio_dev->channels = quad8_channels;
+	indio_dev->name = dev_name(dev);
+
+	priv = iio_priv(indio_dev);
+	priv->base = base[id];
+
+	/* Reset all counters and disable interrupt function */
+	outb(0x01, base[id] + 0x11);
+	/* Set initial configuration for all counters */
+	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
+		base_offset = base[id] + 2 * i;
+		/* Reset Byte Pointer */
+		outb(0x01, base_offset + 1);
+		/* Reset Preset Register */
+		for (j = 0; j < 3; j++)
+			outb(0x00, base_offset);
+		/* Reset Borrow, Carry, Compare, and Sign flags */
+		outb(0x04, base_offset + 1);
+		/* Reset Error flag */
+		outb(0x06, base_offset + 1);
+		/* Binary encoding; Normal count; non-quadrature mode */
+		outb(0x20, base_offset + 1);
+		/* Disable A and B inputs; preset on index; FLG1 as Carry */
+		outb(0x40, base_offset + 1);
+		/* Disable index function; negative index polarity */
+		outb(0x60, base_offset + 1);
+	}
+	/* Enable all counters */
+	outb(0x00, base[id] + 0x11);
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct isa_driver quad8_driver = {
+	.probe = quad8_probe,
+	.driver = {
+		.name = "104-quad-8"
+	}
+};
+
+module_isa_driver(quad8_driver, num_quad8);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
+MODULE_LICENSE("GPL v2");

+ 24 - 0
drivers/iio/counter/Kconfig

@@ -0,0 +1,24 @@
+#
+# Counter devices
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Counters"
+
+config 104_QUAD_8
+	tristate "ACCES 104-QUAD-8 driver"
+	depends on X86 && ISA_BUS_API
+	help
+	  Say yes here to build support for the ACCES 104-QUAD-8 quadrature
+	  encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
+
+	  Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and
+	  also clears the counter's respective error flag. Although the counters
+	  have a 25-bit range, only the lower 24 bits may be set, either directly
+	  or via a counter's preset attribute. Interrupts are not supported by
+	  this driver.
+
+	  The base port addresses for the devices may be configured via the base
+	  array module parameter.
+
+endmenu

+ 7 - 0
drivers/iio/counter/Makefile

@@ -0,0 +1,7 @@
+#
+# Makefile for IIO counter devices
+#
+
+# When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o

+ 10 - 0
drivers/iio/dac/Kconfig

@@ -200,6 +200,16 @@ config AD8801
 	  To compile this driver as a module choose M here: the module will be called
 	  ad8801.
 
+config DPOT_DAC
+	tristate "DAC emulation using a DPOT"
+	depends on OF
+	help
+	  Say yes here to build support for DAC emulation using a digital
+	  potentiometer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called dpot-dac.
+
 config LPC18XX_DAC
 	tristate "NXP LPC18xx DAC driver"
 	depends on ARCH_LPC18XX || COMPILE_TEST

+ 1 - 0
drivers/iio/dac/Makefile

@@ -22,6 +22,7 @@ obj-$(CONFIG_AD5686) += ad5686.o
 obj-$(CONFIG_AD7303) += ad7303.o
 obj-$(CONFIG_AD8801) += ad8801.o
 obj-$(CONFIG_CIO_DAC) += cio-dac.o
+obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
 obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
 obj-$(CONFIG_M62332) += m62332.o
 obj-$(CONFIG_MAX517) += max517.o

+ 1 - 1
drivers/iio/dac/ad5592r.c

@@ -17,7 +17,7 @@
 #define AD5592R_GPIO_READBACK_EN	BIT(10)
 #define AD5592R_LDAC_READBACK_EN	BIT(6)
 
-static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, u16 *buf)
+static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf)
 {
 	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
 	struct spi_transfer t = {

+ 266 - 0
drivers/iio/dac/dpot-dac.c

@@ -0,0 +1,266 @@
+/*
+ * IIO DAC emulation driver using a digital potentiometer
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * 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.
+ */
+
+/*
+ * It is assumed that the dpot is used as a voltage divider between the
+ * current dpot wiper setting and the maximum resistance of the dpot. The
+ * divided voltage is provided by a vref regulator.
+ *
+ *                   .------.
+ *    .-----------.  |      |
+ *    | vref      |--'    .---.
+ *    | regulator |--.    |   |
+ *    '-----------'  |    | d |
+ *                   |    | p |
+ *                   |    | o |  wiper
+ *                   |    | t |<---------+
+ *                   |    |   |
+ *                   |    '---'       dac output voltage
+ *                   |      |
+ *                   '------+------------+
+ */
+
+#include <linux/err.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+struct dpot_dac {
+	struct regulator *vref;
+	struct iio_channel *dpot;
+	u32 max_ohms;
+};
+
+static const struct iio_chan_spec dpot_dac_iio_channel = {
+	.type = IIO_VOLTAGE,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+			    | BIT(IIO_CHAN_INFO_SCALE),
+	.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
+	.output = 1,
+	.indexed = 1,
+};
+
+static int dpot_dac_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val, int *val2, long mask)
+{
+	struct dpot_dac *dac = iio_priv(indio_dev);
+	int ret;
+	unsigned long long tmp;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return iio_read_channel_raw(dac->dpot, val);
+
+	case IIO_CHAN_INFO_SCALE:
+		ret = iio_read_channel_scale(dac->dpot, val, val2);
+		switch (ret) {
+		case IIO_VAL_FRACTIONAL_LOG2:
+			tmp = *val * 1000000000LL;
+			do_div(tmp, dac->max_ohms);
+			tmp *= regulator_get_voltage(dac->vref) / 1000;
+			do_div(tmp, 1000000000LL);
+			*val = tmp;
+			return ret;
+		case IIO_VAL_INT:
+			/*
+			 * Convert integer scale to fractional scale by
+			 * setting the denominator (val2) to one...
+			 */
+			*val2 = 1;
+			ret = IIO_VAL_FRACTIONAL;
+			/* ...and fall through. */
+		case IIO_VAL_FRACTIONAL:
+			*val *= regulator_get_voltage(dac->vref) / 1000;
+			*val2 *= dac->max_ohms;
+			break;
+		}
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static int dpot_dac_read_avail(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       const int **vals, int *type, int *length,
+			       long mask)
+{
+	struct dpot_dac *dac = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		*type = IIO_VAL_INT;
+		return iio_read_avail_channel_raw(dac->dpot, vals, length);
+	}
+
+	return -EINVAL;
+}
+
+static int dpot_dac_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val, int val2, long mask)
+{
+	struct dpot_dac *dac = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return iio_write_channel_raw(dac->dpot, val);
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info dpot_dac_info = {
+	.read_raw = dpot_dac_read_raw,
+	.read_avail = dpot_dac_read_avail,
+	.write_raw = dpot_dac_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev)
+{
+	struct device *dev = &indio_dev->dev;
+	struct dpot_dac *dac = iio_priv(indio_dev);
+	unsigned long long tmp;
+	int ret;
+	int val;
+	int val2;
+	int max;
+
+	ret = iio_read_max_channel_raw(dac->dpot, &max);
+	if (ret < 0) {
+		dev_err(dev, "dpot does not indicate its raw maximum value\n");
+		return ret;
+	}
+
+	switch (iio_read_channel_scale(dac->dpot, &val, &val2)) {
+	case IIO_VAL_INT:
+		return max * val;
+	case IIO_VAL_FRACTIONAL:
+		tmp = (unsigned long long)max * val;
+		do_div(tmp, val2);
+		return tmp;
+	case IIO_VAL_FRACTIONAL_LOG2:
+		tmp = val * 1000000000LL * max >> val2;
+		do_div(tmp, 1000000000LL);
+		return tmp;
+	default:
+		dev_err(dev, "dpot has a scale that is too weird\n");
+	}
+
+	return -EINVAL;
+}
+
+static int dpot_dac_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *indio_dev;
+	struct dpot_dac *dac;
+	enum iio_chan_type type;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, indio_dev);
+	dac = iio_priv(indio_dev);
+
+	indio_dev->name = dev_name(dev);
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dpot_dac_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = &dpot_dac_iio_channel;
+	indio_dev->num_channels = 1;
+
+	dac->vref = devm_regulator_get(dev, "vref");
+	if (IS_ERR(dac->vref)) {
+		if (PTR_ERR(dac->vref) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "failed to get vref regulator\n");
+		return PTR_ERR(dac->vref);
+	}
+
+	dac->dpot = devm_iio_channel_get(dev, "dpot");
+	if (IS_ERR(dac->dpot)) {
+		if (PTR_ERR(dac->dpot) != -EPROBE_DEFER)
+			dev_err(dev, "failed to get dpot input channel\n");
+		return PTR_ERR(dac->dpot);
+	}
+
+	ret = iio_get_channel_type(dac->dpot, &type);
+	if (ret < 0)
+		return ret;
+
+	if (type != IIO_RESISTANCE) {
+		dev_err(dev, "dpot is of the wrong type\n");
+		return -EINVAL;
+	}
+
+	ret = dpot_dac_channel_max_ohms(indio_dev);
+	if (ret < 0)
+		return ret;
+	dac->max_ohms = ret;
+
+	ret = regulator_enable(dac->vref);
+	if (ret) {
+		dev_err(dev, "failed to enable the vref regulator\n");
+		return ret;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "failed to register iio device\n");
+		goto disable_reg;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(dac->vref);
+	return ret;
+}
+
+static int dpot_dac_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct dpot_dac *dac = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	regulator_disable(dac->vref);
+
+	return 0;
+}
+
+static const struct of_device_id dpot_dac_match[] = {
+	{ .compatible = "dpot-dac" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dpot_dac_match);
+
+static struct platform_driver dpot_dac_driver = {
+	.probe = dpot_dac_probe,
+	.remove = dpot_dac_remove,
+	.driver = {
+		.name = "iio-dpot-dac",
+		.of_match_table = dpot_dac_match,
+	},
+};
+module_platform_driver(dpot_dac_driver);
+
+MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");

+ 160 - 16
drivers/iio/dac/mcp4725.c

@@ -18,6 +18,8 @@
 #include <linux/i2c.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
@@ -26,12 +28,20 @@
 
 #define MCP4725_DRV_NAME "mcp4725"
 
+#define MCP472X_REF_VDD			0x00
+#define MCP472X_REF_VREF_UNBUFFERED	0x02
+#define MCP472X_REF_VREF_BUFFERED	0x03
+
 struct mcp4725_data {
 	struct i2c_client *client;
-	u16 vref_mv;
+	int id;
+	unsigned ref_mode;
+	bool vref_buffered;
 	u16 dac_value;
 	bool powerdown;
 	unsigned powerdown_mode;
+	struct regulator *vdd_reg;
+	struct regulator *vref_reg;
 };
 
 static int mcp4725_suspend(struct device *dev)
@@ -86,6 +96,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
 		return 0;
 
 	inoutbuf[0] = 0x60; /* write EEPROM */
+	inoutbuf[0] |= data->ref_mode << 3;
 	inoutbuf[1] = data->dac_value >> 4;
 	inoutbuf[2] = (data->dac_value & 0xf) << 4;
 
@@ -278,18 +289,49 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
 		return 0;
 }
 
+static int mcp4726_set_cfg(struct iio_dev *indio_dev)
+{
+	struct mcp4725_data *data = iio_priv(indio_dev);
+	u8 outbuf[3];
+	int ret;
+
+	outbuf[0] = 0x40;
+	outbuf[0] |= data->ref_mode << 3;
+	if (data->powerdown)
+		outbuf[0] |= data->powerdown << 1;
+	outbuf[1] = data->dac_value >> 4;
+	outbuf[2] = (data->dac_value & 0xf) << 4;
+
+	ret = i2c_master_send(data->client, outbuf, 3);
+	if (ret < 0)
+		return ret;
+	else if (ret != 3)
+		return -EIO;
+	else
+		return 0;
+}
+
 static int mcp4725_read_raw(struct iio_dev *indio_dev,
 			   struct iio_chan_spec const *chan,
 			   int *val, int *val2, long mask)
 {
 	struct mcp4725_data *data = iio_priv(indio_dev);
+	int ret;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
 		*val = data->dac_value;
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
-		*val = data->vref_mv;
+		if (data->ref_mode == MCP472X_REF_VDD)
+			ret = regulator_get_voltage(data->vdd_reg);
+		else
+			ret = regulator_get_voltage(data->vref_reg);
+
+		if (ret < 0)
+			return ret;
+
+		*val = ret / 1000;
 		*val2 = 12;
 		return IIO_VAL_FRACTIONAL_LOG2;
 	}
@@ -323,27 +365,98 @@ static const struct iio_info mcp4725_info = {
 	.driver_module = THIS_MODULE,
 };
 
+#ifdef CONFIG_OF
+static int mcp4725_probe_dt(struct device *dev,
+			    struct mcp4725_platform_data *pdata)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!np)
+		return -ENODEV;
+
+	/* check if is the vref-supply defined */
+	pdata->use_vref = of_property_read_bool(np, "vref-supply");
+	pdata->vref_buffered =
+		of_property_read_bool(np, "microchip,vref-buffered");
+
+	return 0;
+}
+#else
+static int mcp4725_probe_dt(struct device *dev,
+			    struct mcp4725_platform_data *platform_data)
+{
+	return -ENODEV;
+}
+#endif
+
 static int mcp4725_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
 	struct mcp4725_data *data;
 	struct iio_dev *indio_dev;
-	struct mcp4725_platform_data *platform_data = client->dev.platform_data;
-	u8 inbuf[3];
+	struct mcp4725_platform_data *pdata, pdata_dt;
+	u8 inbuf[4];
 	u8 pd;
+	u8 ref;
 	int err;
 
-	if (!platform_data || !platform_data->vref_mv) {
-		dev_err(&client->dev, "invalid platform data");
-		return -EINVAL;
-	}
-
 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 	if (indio_dev == NULL)
 		return -ENOMEM;
 	data = iio_priv(indio_dev);
 	i2c_set_clientdata(client, indio_dev);
 	data->client = client;
+	data->id = id->driver_data;
+	pdata = dev_get_platdata(&client->dev);
+
+	if (!pdata) {
+		err = mcp4725_probe_dt(&client->dev, &pdata_dt);
+		if (err) {
+			dev_err(&client->dev,
+				"invalid platform or devicetree data");
+			return err;
+		}
+		pdata = &pdata_dt;
+	}
+
+	if (data->id == MCP4725 && pdata->use_vref) {
+		dev_err(&client->dev,
+			"external reference is unavailable on MCP4725");
+		return -EINVAL;
+	}
+
+	if (!pdata->use_vref && pdata->vref_buffered) {
+		dev_err(&client->dev,
+			"buffering is unavailable on the internal reference");
+		return -EINVAL;
+	}
+
+	if (!pdata->use_vref)
+		data->ref_mode = MCP472X_REF_VDD;
+	else
+		data->ref_mode = pdata->vref_buffered ?
+			MCP472X_REF_VREF_BUFFERED :
+			MCP472X_REF_VREF_UNBUFFERED;
+
+	data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
+	if (IS_ERR(data->vdd_reg))
+		return PTR_ERR(data->vdd_reg);
+
+	err = regulator_enable(data->vdd_reg);
+	if (err)
+		return err;
+
+	if (pdata->use_vref) {
+		data->vref_reg = devm_regulator_get(&client->dev, "vref");
+		if (IS_ERR(data->vref_reg)) {
+			err = PTR_ERR(data->vref_reg);
+			goto err_disable_vdd_reg;
+		}
+
+		err = regulator_enable(data->vref_reg);
+		if (err)
+			goto err_disable_vdd_reg;
+	}
 
 	indio_dev->dev.parent = &client->dev;
 	indio_dev->name = id->name;
@@ -352,25 +465,56 @@ static int mcp4725_probe(struct i2c_client *client,
 	indio_dev->num_channels = 1;
 	indio_dev->modes = INDIO_DIRECT_MODE;
 
-	data->vref_mv = platform_data->vref_mv;
+	/* read current DAC value and settings */
+	err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4);
 
-	/* read current DAC value */
-	err = i2c_master_recv(client, inbuf, 3);
 	if (err < 0) {
 		dev_err(&client->dev, "failed to read DAC value");
-		return err;
+		goto err_disable_vref_reg;
 	}
 	pd = (inbuf[0] >> 1) & 0x3;
 	data->powerdown = pd > 0 ? true : false;
-	data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
+	data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
 	data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
+	if (data->id == MCP4726)
+		ref = (inbuf[3] >> 3) & 0x3;
+
+	if (data->id == MCP4726 && ref != data->ref_mode) {
+		dev_info(&client->dev,
+			"voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
+			data->ref_mode, ref, data->ref_mode);
+		err = mcp4726_set_cfg(indio_dev);
+		if (err < 0)
+			goto err_disable_vref_reg;
+	}
+ 
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto err_disable_vref_reg;
+
+	return 0;
+
+err_disable_vref_reg:
+	if (data->vref_reg)
+		regulator_disable(data->vref_reg);
 
-	return iio_device_register(indio_dev);
+err_disable_vdd_reg:
+	regulator_disable(data->vdd_reg);
+
+	return err;
 }
 
 static int mcp4725_remove(struct i2c_client *client)
 {
-	iio_device_unregister(i2c_get_clientdata(client));
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct mcp4725_data *data = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	if (data->vref_reg)
+		regulator_disable(data->vref_reg);
+	regulator_disable(data->vdd_reg);
+
 	return 0;
 }
 

+ 18 - 0
drivers/iio/gyro/Kconfig

@@ -84,6 +84,24 @@ config HID_SENSOR_GYRO_3D
 	  Say yes here to build support for the HID SENSOR
 	  Gyroscope 3D.
 
+config MPU3050
+	tristate
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select REGMAP
+
+config MPU3050_I2C
+	tristate "Invensense MPU3050 devices on I2C"
+	depends on !(INPUT_MPU3050=y || INPUT_MPU3050=m)
+	depends on I2C
+	select MPU3050
+	select REGMAP_I2C
+	select I2C_MUX
+	help
+	  This driver supports the Invensense MPU3050 gyroscope over I2C.
+	  This driver can be built as a module. The module will be called
+	  inv-mpu3050-i2c.
+
 config IIO_ST_GYRO_3AXIS
 	tristate "STMicroelectronics gyroscopes 3-Axis Driver"
 	depends on (I2C || SPI_MASTER) && SYSFS

+ 5 - 0
drivers/iio/gyro/Makefile

@@ -14,6 +14,11 @@ obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
 
 obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
 
+# Currently this is rolled into one module, split it if
+# we ever create a separate SPI interface for MPU-3050
+obj-$(CONFIG_MPU3050) += mpu3050.o
+mpu3050-objs := mpu3050-core.o mpu3050-i2c.o
+
 itg3200-y               := itg3200_core.o
 itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
 obj-$(CONFIG_ITG3200)   += itg3200.o

+ 1306 - 0
drivers/iio/gyro/mpu3050-core.c

@@ -0,0 +1,1306 @@
+/*
+ * MPU3050 gyroscope driver
+ *
+ * Copyright (C) 2016 Linaro Ltd.
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Based on the input subsystem driver, Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com> and trimmed down by
+ * Alan Cox <alan@linux.intel.com> in turn based on bma023.c.
+ * Device behaviour based on a misc driver posted by Nathan Royer in 2011.
+ *
+ * TODO: add support for setting up the low pass 3dB frequency.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#include "mpu3050.h"
+
+#define MPU3050_CHIP_ID		0x69
+
+/*
+ * Register map: anything suffixed *_H is a big-endian high byte and always
+ * followed by the corresponding low byte (*_L) even though these are not
+ * explicitly included in the register definitions.
+ */
+#define MPU3050_CHIP_ID_REG	0x00
+#define MPU3050_PRODUCT_ID_REG	0x01
+#define MPU3050_XG_OFFS_TC	0x05
+#define MPU3050_YG_OFFS_TC	0x08
+#define MPU3050_ZG_OFFS_TC	0x0B
+#define MPU3050_X_OFFS_USR_H	0x0C
+#define MPU3050_Y_OFFS_USR_H	0x0E
+#define MPU3050_Z_OFFS_USR_H	0x10
+#define MPU3050_FIFO_EN		0x12
+#define MPU3050_AUX_VDDIO	0x13
+#define MPU3050_SLV_ADDR	0x14
+#define MPU3050_SMPLRT_DIV	0x15
+#define MPU3050_DLPF_FS_SYNC	0x16
+#define MPU3050_INT_CFG		0x17
+#define MPU3050_AUX_ADDR	0x18
+#define MPU3050_INT_STATUS	0x1A
+#define MPU3050_TEMP_H		0x1B
+#define MPU3050_XOUT_H		0x1D
+#define MPU3050_YOUT_H		0x1F
+#define MPU3050_ZOUT_H		0x21
+#define MPU3050_DMP_CFG1	0x35
+#define MPU3050_DMP_CFG2	0x36
+#define MPU3050_BANK_SEL	0x37
+#define MPU3050_MEM_START_ADDR	0x38
+#define MPU3050_MEM_R_W		0x39
+#define MPU3050_FIFO_COUNT_H	0x3A
+#define MPU3050_FIFO_R		0x3C
+#define MPU3050_USR_CTRL	0x3D
+#define MPU3050_PWR_MGM		0x3E
+
+/* MPU memory bank read options */
+#define MPU3050_MEM_PRFTCH	BIT(5)
+#define MPU3050_MEM_USER_BANK	BIT(4)
+/* Bits 8-11 select memory bank */
+#define MPU3050_MEM_RAM_BANK_0	0
+#define MPU3050_MEM_RAM_BANK_1	1
+#define MPU3050_MEM_RAM_BANK_2	2
+#define MPU3050_MEM_RAM_BANK_3	3
+#define MPU3050_MEM_OTP_BANK_0	4
+
+#define MPU3050_AXIS_REGS(axis) (MPU3050_XOUT_H + (axis * 2))
+
+/* Register bits */
+
+/* FIFO Enable */
+#define MPU3050_FIFO_EN_FOOTER		BIT(0)
+#define MPU3050_FIFO_EN_AUX_ZOUT	BIT(1)
+#define MPU3050_FIFO_EN_AUX_YOUT	BIT(2)
+#define MPU3050_FIFO_EN_AUX_XOUT	BIT(3)
+#define MPU3050_FIFO_EN_GYRO_ZOUT	BIT(4)
+#define MPU3050_FIFO_EN_GYRO_YOUT	BIT(5)
+#define MPU3050_FIFO_EN_GYRO_XOUT	BIT(6)
+#define MPU3050_FIFO_EN_TEMP_OUT	BIT(7)
+
+/*
+ * Digital Low Pass filter (DLPF)
+ * Full Scale (FS)
+ * and Synchronization
+ */
+#define MPU3050_EXT_SYNC_NONE		0x00
+#define MPU3050_EXT_SYNC_TEMP		0x20
+#define MPU3050_EXT_SYNC_GYROX		0x40
+#define MPU3050_EXT_SYNC_GYROY		0x60
+#define MPU3050_EXT_SYNC_GYROZ		0x80
+#define MPU3050_EXT_SYNC_ACCELX	0xA0
+#define MPU3050_EXT_SYNC_ACCELY	0xC0
+#define MPU3050_EXT_SYNC_ACCELZ	0xE0
+#define MPU3050_EXT_SYNC_MASK		0xE0
+#define MPU3050_EXT_SYNC_SHIFT		5
+
+#define MPU3050_FS_250DPS		0x00
+#define MPU3050_FS_500DPS		0x08
+#define MPU3050_FS_1000DPS		0x10
+#define MPU3050_FS_2000DPS		0x18
+#define MPU3050_FS_MASK			0x18
+#define MPU3050_FS_SHIFT		3
+
+#define MPU3050_DLPF_CFG_256HZ_NOLPF2	0x00
+#define MPU3050_DLPF_CFG_188HZ		0x01
+#define MPU3050_DLPF_CFG_98HZ		0x02
+#define MPU3050_DLPF_CFG_42HZ		0x03
+#define MPU3050_DLPF_CFG_20HZ		0x04
+#define MPU3050_DLPF_CFG_10HZ		0x05
+#define MPU3050_DLPF_CFG_5HZ		0x06
+#define MPU3050_DLPF_CFG_2100HZ_NOLPF	0x07
+#define MPU3050_DLPF_CFG_MASK		0x07
+#define MPU3050_DLPF_CFG_SHIFT		0
+
+/* Interrupt config */
+#define MPU3050_INT_RAW_RDY_EN		BIT(0)
+#define MPU3050_INT_DMP_DONE_EN		BIT(1)
+#define MPU3050_INT_MPU_RDY_EN		BIT(2)
+#define MPU3050_INT_ANYRD_2CLEAR	BIT(4)
+#define MPU3050_INT_LATCH_EN		BIT(5)
+#define MPU3050_INT_OPEN		BIT(6)
+#define MPU3050_INT_ACTL		BIT(7)
+/* Interrupt status */
+#define MPU3050_INT_STATUS_RAW_RDY	BIT(0)
+#define MPU3050_INT_STATUS_DMP_DONE	BIT(1)
+#define MPU3050_INT_STATUS_MPU_RDY	BIT(2)
+#define MPU3050_INT_STATUS_FIFO_OVFLW	BIT(7)
+/* USR_CTRL */
+#define MPU3050_USR_CTRL_FIFO_EN	BIT(6)
+#define MPU3050_USR_CTRL_AUX_IF_EN	BIT(5)
+#define MPU3050_USR_CTRL_AUX_IF_RST	BIT(3)
+#define MPU3050_USR_CTRL_FIFO_RST	BIT(1)
+#define MPU3050_USR_CTRL_GYRO_RST	BIT(0)
+/* PWR_MGM */
+#define MPU3050_PWR_MGM_PLL_X		0x01
+#define MPU3050_PWR_MGM_PLL_Y		0x02
+#define MPU3050_PWR_MGM_PLL_Z		0x03
+#define MPU3050_PWR_MGM_CLKSEL_MASK	0x07
+#define MPU3050_PWR_MGM_STBY_ZG		BIT(3)
+#define MPU3050_PWR_MGM_STBY_YG		BIT(4)
+#define MPU3050_PWR_MGM_STBY_XG		BIT(5)
+#define MPU3050_PWR_MGM_SLEEP		BIT(6)
+#define MPU3050_PWR_MGM_RESET		BIT(7)
+#define MPU3050_PWR_MGM_MASK		0xff
+
+/*
+ * Fullscale precision is (for finest precision) +/- 250 deg/s, so the full
+ * scale is actually 500 deg/s. All 16 bits are then used to cover this scale,
+ * in two's complement.
+ */
+static unsigned int mpu3050_fs_precision[] = {
+	IIO_DEGREE_TO_RAD(250),
+	IIO_DEGREE_TO_RAD(500),
+	IIO_DEGREE_TO_RAD(1000),
+	IIO_DEGREE_TO_RAD(2000)
+};
+
+/*
+ * Regulator names
+ */
+static const char mpu3050_reg_vdd[] = "vdd";
+static const char mpu3050_reg_vlogic[] = "vlogic";
+
+static unsigned int mpu3050_get_freq(struct mpu3050 *mpu3050)
+{
+	unsigned int freq;
+
+	if (mpu3050->lpf == MPU3050_DLPF_CFG_256HZ_NOLPF2)
+		freq = 8000;
+	else
+		freq = 1000;
+	freq /= (mpu3050->divisor + 1);
+
+	return freq;
+}
+
+static int mpu3050_start_sampling(struct mpu3050 *mpu3050)
+{
+	__be16 raw_val[3];
+	int ret;
+	int i;
+
+	/* Reset */
+	ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+				 MPU3050_PWR_MGM_RESET, MPU3050_PWR_MGM_RESET);
+	if (ret)
+		return ret;
+
+	/* Turn on the Z-axis PLL */
+	ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+				 MPU3050_PWR_MGM_CLKSEL_MASK,
+				 MPU3050_PWR_MGM_PLL_Z);
+	if (ret)
+		return ret;
+
+	/* Write calibration offset registers */
+	for (i = 0; i < 3; i++)
+		raw_val[i] = cpu_to_be16(mpu3050->calibration[i]);
+
+	ret = regmap_bulk_write(mpu3050->map, MPU3050_X_OFFS_USR_H, raw_val,
+				sizeof(raw_val));
+	if (ret)
+		return ret;
+
+	/* Set low pass filter (sample rate), sync and full scale */
+	ret = regmap_write(mpu3050->map, MPU3050_DLPF_FS_SYNC,
+			   MPU3050_EXT_SYNC_NONE << MPU3050_EXT_SYNC_SHIFT |
+			   mpu3050->fullscale << MPU3050_FS_SHIFT |
+			   mpu3050->lpf << MPU3050_DLPF_CFG_SHIFT);
+	if (ret)
+		return ret;
+
+	/* Set up sampling frequency */
+	ret = regmap_write(mpu3050->map, MPU3050_SMPLRT_DIV, mpu3050->divisor);
+	if (ret)
+		return ret;
+
+	/*
+	 * Max 50 ms start-up time after setting DLPF_FS_SYNC
+	 * according to the data sheet, then wait for the next sample
+	 * at this frequency T = 1000/f ms.
+	 */
+	msleep(50 + 1000 / mpu3050_get_freq(mpu3050));
+
+	return 0;
+}
+
+static int mpu3050_set_8khz_samplerate(struct mpu3050 *mpu3050)
+{
+	int ret;
+	u8 divisor;
+	enum mpu3050_lpf lpf;
+
+	lpf = mpu3050->lpf;
+	divisor = mpu3050->divisor;
+
+	mpu3050->lpf = LPF_256_HZ_NOLPF; /* 8 kHz base frequency */
+	mpu3050->divisor = 0; /* Divide by 1 */
+	ret = mpu3050_start_sampling(mpu3050);
+
+	mpu3050->lpf = lpf;
+	mpu3050->divisor = divisor;
+
+	return ret;
+}
+
+static int mpu3050_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2,
+			    long mask)
+{
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+	int ret;
+	__be16 raw_val;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OFFSET:
+		switch (chan->type) {
+		case IIO_TEMP:
+			/* The temperature scaling is (x+23000)/280 Celsius */
+			*val = 23000;
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_CALIBBIAS:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = mpu3050->calibration[chan->scan_index-1];
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = mpu3050_get_freq(mpu3050);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_TEMP:
+			/* Millidegrees, see about temperature scaling above */
+			*val = 1000;
+			*val2 = 280;
+			return IIO_VAL_FRACTIONAL;
+		case IIO_ANGL_VEL:
+			/*
+			 * Convert to the corresponding full scale in
+			 * radians. All 16 bits are used with sign to
+			 * span the available scale: to account for the one
+			 * missing value if we multiply by 1/S16_MAX, instead
+			 * multiply with 2/U16_MAX.
+			 */
+			*val = mpu3050_fs_precision[mpu3050->fullscale] * 2;
+			*val2 = U16_MAX;
+			return IIO_VAL_FRACTIONAL;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_RAW:
+		/* Resume device */
+		pm_runtime_get_sync(mpu3050->dev);
+		mutex_lock(&mpu3050->lock);
+
+		ret = mpu3050_set_8khz_samplerate(mpu3050);
+		if (ret)
+			goto out_read_raw_unlock;
+
+		switch (chan->type) {
+		case IIO_TEMP:
+			ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H,
+					       &raw_val, sizeof(raw_val));
+			if (ret) {
+				dev_err(mpu3050->dev,
+					"error reading temperature\n");
+				goto out_read_raw_unlock;
+			}
+
+			*val = be16_to_cpu(raw_val);
+			ret = IIO_VAL_INT;
+
+			goto out_read_raw_unlock;
+		case IIO_ANGL_VEL:
+			ret = regmap_bulk_read(mpu3050->map,
+				       MPU3050_AXIS_REGS(chan->scan_index-1),
+				       &raw_val,
+				       sizeof(raw_val));
+			if (ret) {
+				dev_err(mpu3050->dev,
+					"error reading axis data\n");
+				goto out_read_raw_unlock;
+			}
+
+			*val = be16_to_cpu(raw_val);
+			ret = IIO_VAL_INT;
+
+			goto out_read_raw_unlock;
+		default:
+			ret = -EINVAL;
+			goto out_read_raw_unlock;
+		}
+	default:
+		break;
+	}
+
+	return -EINVAL;
+
+out_read_raw_unlock:
+	mutex_unlock(&mpu3050->lock);
+	pm_runtime_mark_last_busy(mpu3050->dev);
+	pm_runtime_put_autosuspend(mpu3050->dev);
+
+	return ret;
+}
+
+static int mpu3050_write_raw(struct iio_dev *indio_dev,
+			     const struct iio_chan_spec *chan,
+			     int val, int val2, long mask)
+{
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+	/*
+	 * Couldn't figure out a way to precalculate these at compile time.
+	 */
+	unsigned int fs250 =
+		DIV_ROUND_CLOSEST(mpu3050_fs_precision[0] * 1000000 * 2,
+				  U16_MAX);
+	unsigned int fs500 =
+		DIV_ROUND_CLOSEST(mpu3050_fs_precision[1] * 1000000 * 2,
+				  U16_MAX);
+	unsigned int fs1000 =
+		DIV_ROUND_CLOSEST(mpu3050_fs_precision[2] * 1000000 * 2,
+				  U16_MAX);
+	unsigned int fs2000 =
+		DIV_ROUND_CLOSEST(mpu3050_fs_precision[3] * 1000000 * 2,
+				  U16_MAX);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (chan->type != IIO_ANGL_VEL)
+			return -EINVAL;
+		mpu3050->calibration[chan->scan_index-1] = val;
+		return 0;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		/*
+		 * The max samplerate is 8000 Hz, the minimum
+		 * 1000 / 256 ~= 4 Hz
+		 */
+		if (val < 4 || val > 8000)
+			return -EINVAL;
+
+		/*
+		 * Above 1000 Hz we must turn off the digital low pass filter
+		 * so we get a base frequency of 8kHz to the divider
+		 */
+		if (val > 1000) {
+			mpu3050->lpf = LPF_256_HZ_NOLPF;
+			mpu3050->divisor = DIV_ROUND_CLOSEST(8000, val) - 1;
+			return 0;
+		}
+
+		mpu3050->lpf = LPF_188_HZ;
+		mpu3050->divisor = DIV_ROUND_CLOSEST(1000, val) - 1;
+		return 0;
+	case IIO_CHAN_INFO_SCALE:
+		if (chan->type != IIO_ANGL_VEL)
+			return -EINVAL;
+		/*
+		 * We support +/-250, +/-500, +/-1000 and +/2000 deg/s
+		 * which means we need to round to the closest radians
+		 * which will be roughly +/-4.3, +/-8.7, +/-17.5, +/-35
+		 * rad/s. The scale is then for the 16 bits used to cover
+		 * it 2/(2^16) of that.
+		 */
+
+		/* Just too large, set the max range */
+		if (val != 0) {
+			mpu3050->fullscale = FS_2000_DPS;
+			return 0;
+		}
+
+		/*
+		 * Now we're dealing with fractions below zero in millirad/s
+		 * do some integer interpolation and match with the closest
+		 * fullscale in the table.
+		 */
+		if (val2 <= fs250 ||
+		    val2 < ((fs500 + fs250) / 2))
+			mpu3050->fullscale = FS_250_DPS;
+		else if (val2 <= fs500 ||
+			 val2 < ((fs1000 + fs500) / 2))
+			mpu3050->fullscale = FS_500_DPS;
+		else if (val2 <= fs1000 ||
+			 val2 < ((fs2000 + fs1000) / 2))
+			mpu3050->fullscale = FS_1000_DPS;
+		else
+			/* Catch-all */
+			mpu3050->fullscale = FS_2000_DPS;
+		return 0;
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static irqreturn_t mpu3050_trigger_handler(int irq, void *p)
+{
+	const struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+	int ret;
+	/*
+	 * Temperature 1*16 bits
+	 * Three axes 3*16 bits
+	 * Timestamp 64 bits (4*16 bits)
+	 * Sum total 8*16 bits
+	 */
+	__be16 hw_values[8];
+	s64 timestamp;
+	unsigned int datums_from_fifo = 0;
+
+	/*
+	 * If we're using the hardware trigger, get the precise timestamp from
+	 * the top half of the threaded IRQ handler. Otherwise get the
+	 * timestamp here so it will be close in time to the actual values
+	 * read from the registers.
+	 */
+	if (iio_trigger_using_own(indio_dev))
+		timestamp = mpu3050->hw_timestamp;
+	else
+		timestamp = iio_get_time_ns(indio_dev);
+
+	mutex_lock(&mpu3050->lock);
+
+	/* Using the hardware IRQ trigger? Check the buffer then. */
+	if (mpu3050->hw_irq_trigger) {
+		__be16 raw_fifocnt;
+		u16 fifocnt;
+		/* X, Y, Z + temperature */
+		unsigned int bytes_per_datum = 8;
+		bool fifo_overflow = false;
+
+		ret = regmap_bulk_read(mpu3050->map,
+				       MPU3050_FIFO_COUNT_H,
+				       &raw_fifocnt,
+				       sizeof(raw_fifocnt));
+		if (ret)
+			goto out_trigger_unlock;
+		fifocnt = be16_to_cpu(raw_fifocnt);
+
+		if (fifocnt == 512) {
+			dev_info(mpu3050->dev,
+				 "FIFO overflow! Emptying and resetting FIFO\n");
+			fifo_overflow = true;
+			/* Reset and enable the FIFO */
+			ret = regmap_update_bits(mpu3050->map,
+						 MPU3050_USR_CTRL,
+						 MPU3050_USR_CTRL_FIFO_EN |
+						 MPU3050_USR_CTRL_FIFO_RST,
+						 MPU3050_USR_CTRL_FIFO_EN |
+						 MPU3050_USR_CTRL_FIFO_RST);
+			if (ret) {
+				dev_info(mpu3050->dev, "error resetting FIFO\n");
+				goto out_trigger_unlock;
+			}
+			mpu3050->pending_fifo_footer = false;
+		}
+
+		if (fifocnt)
+			dev_dbg(mpu3050->dev,
+				"%d bytes in the FIFO\n",
+				fifocnt);
+
+		while (!fifo_overflow && fifocnt > bytes_per_datum) {
+			unsigned int toread;
+			unsigned int offset;
+			__be16 fifo_values[5];
+
+			/*
+			 * If there is a FIFO footer in the pipe, first clear
+			 * that out. This follows the complex algorithm in the
+			 * datasheet that states that you may never leave the
+			 * FIFO empty after the first reading: you have to
+			 * always leave two footer bytes in it. The footer is
+			 * in practice just two zero bytes.
+			 */
+			if (mpu3050->pending_fifo_footer) {
+				toread = bytes_per_datum + 2;
+				offset = 0;
+			} else {
+				toread = bytes_per_datum;
+				offset = 1;
+				/* Put in some dummy value */
+				fifo_values[0] = 0xAAAA;
+			}
+
+			ret = regmap_bulk_read(mpu3050->map,
+					       MPU3050_FIFO_R,
+					       &fifo_values[offset],
+					       toread);
+
+			dev_dbg(mpu3050->dev,
+				"%04x %04x %04x %04x %04x\n",
+				fifo_values[0],
+				fifo_values[1],
+				fifo_values[2],
+				fifo_values[3],
+				fifo_values[4]);
+
+			/* Index past the footer (fifo_values[0]) and push */
+			iio_push_to_buffers_with_timestamp(indio_dev,
+							   &fifo_values[1],
+							   timestamp);
+
+			fifocnt -= toread;
+			datums_from_fifo++;
+			mpu3050->pending_fifo_footer = true;
+
+			/*
+			 * If we're emptying the FIFO, just make sure to
+			 * check if something new appeared.
+			 */
+			if (fifocnt < bytes_per_datum) {
+				ret = regmap_bulk_read(mpu3050->map,
+						       MPU3050_FIFO_COUNT_H,
+						       &raw_fifocnt,
+						       sizeof(raw_fifocnt));
+				if (ret)
+					goto out_trigger_unlock;
+				fifocnt = be16_to_cpu(raw_fifocnt);
+			}
+
+			if (fifocnt < bytes_per_datum)
+				dev_dbg(mpu3050->dev,
+					"%d bytes left in the FIFO\n",
+					fifocnt);
+
+			/*
+			 * At this point, the timestamp that triggered the
+			 * hardware interrupt is no longer valid for what
+			 * we are reading (the interrupt likely fired for
+			 * the value on the top of the FIFO), so set the
+			 * timestamp to zero and let userspace deal with it.
+			 */
+			timestamp = 0;
+		}
+	}
+
+	/*
+	 * If we picked some datums from the FIFO that's enough, else
+	 * fall through and just read from the current value registers.
+	 * This happens in two cases:
+	 *
+	 * - We are using some other trigger (external, like an HRTimer)
+	 *   than the sensor's own sample generator. In this case the
+	 *   sensor is just set to the max sampling frequency and we give
+	 *   the trigger a copy of the latest value every time we get here.
+	 *
+	 * - The hardware trigger is active but unused and we actually use
+	 *   another trigger which calls here with a frequency higher
+	 *   than what the device provides data. We will then just read
+	 *   duplicate values directly from the hardware registers.
+	 */
+	if (datums_from_fifo) {
+		dev_dbg(mpu3050->dev,
+			"read %d datums from the FIFO\n",
+			datums_from_fifo);
+		goto out_trigger_unlock;
+	}
+
+	ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H, &hw_values,
+			       sizeof(hw_values));
+	if (ret) {
+		dev_err(mpu3050->dev,
+			"error reading axis data\n");
+		goto out_trigger_unlock;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, hw_values, timestamp);
+
+out_trigger_unlock:
+	mutex_unlock(&mpu3050->lock);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int mpu3050_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+	pm_runtime_get_sync(mpu3050->dev);
+
+	/* Unless we have OUR trigger active, run at full speed */
+	if (!mpu3050->hw_irq_trigger)
+		return mpu3050_set_8khz_samplerate(mpu3050);
+
+	return 0;
+}
+
+static int mpu3050_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+	pm_runtime_mark_last_busy(mpu3050->dev);
+	pm_runtime_put_autosuspend(mpu3050->dev);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops mpu3050_buffer_setup_ops = {
+	.preenable = mpu3050_buffer_preenable,
+	.postenable = iio_triggered_buffer_postenable,
+	.predisable = iio_triggered_buffer_predisable,
+	.postdisable = mpu3050_buffer_postdisable,
+};
+
+static const struct iio_mount_matrix *
+mpu3050_get_mount_matrix(const struct iio_dev *indio_dev,
+			 const struct iio_chan_spec *chan)
+{
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+	return &mpu3050->orientation;
+}
+
+static const struct iio_chan_spec_ext_info mpu3050_ext_info[] = {
+	IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mpu3050_get_mount_matrix),
+	{ },
+};
+
+#define MPU3050_AXIS_CHANNEL(axis, index)				\
+	{								\
+		.type = IIO_ANGL_VEL,					\
+		.modified = 1,						\
+		.channel2 = IIO_MOD_##axis,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
+			BIT(IIO_CHAN_INFO_CALIBBIAS),			\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+		.ext_info = mpu3050_ext_info,				\
+		.scan_index = index,					\
+		.scan_type = {						\
+			.sign = 's',					\
+			.realbits = 16,					\
+			.storagebits = 16,				\
+			.endianness = IIO_BE,				\
+		},							\
+	}
+
+static const struct iio_chan_spec mpu3050_channels[] = {
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE) |
+				      BIT(IIO_CHAN_INFO_OFFSET),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.scan_index = 0,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_BE,
+		},
+	},
+	MPU3050_AXIS_CHANNEL(X, 1),
+	MPU3050_AXIS_CHANNEL(Y, 2),
+	MPU3050_AXIS_CHANNEL(Z, 3),
+	IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+/* Four channels apart from timestamp, scan mask = 0x0f */
+static const unsigned long mpu3050_scan_masks[] = { 0xf, 0 };
+
+/*
+ * These are just the hardcoded factors resulting from the more elaborate
+ * calculations done with fractions in the scale raw get/set functions.
+ */
+static IIO_CONST_ATTR(anglevel_scale_available,
+		      "0.000122070 "
+		      "0.000274658 "
+		      "0.000518798 "
+		      "0.001068115");
+
+static struct attribute *mpu3050_attributes[] = {
+	&iio_const_attr_anglevel_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group mpu3050_attribute_group = {
+	.attrs = mpu3050_attributes,
+};
+
+static const struct iio_info mpu3050_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = mpu3050_read_raw,
+	.write_raw = mpu3050_write_raw,
+	.attrs = &mpu3050_attribute_group,
+};
+
+/**
+ * mpu3050_read_mem() - read MPU-3050 internal memory
+ * @mpu3050: device to read from
+ * @bank: target bank
+ * @addr: target address
+ * @len: number of bytes
+ * @buf: the buffer to store the read bytes in
+ */
+static int mpu3050_read_mem(struct mpu3050 *mpu3050,
+			    u8 bank,
+			    u8 addr,
+			    u8 len,
+			    u8 *buf)
+{
+	int ret;
+
+	ret = regmap_write(mpu3050->map,
+			   MPU3050_BANK_SEL,
+			   bank);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(mpu3050->map,
+			   MPU3050_MEM_START_ADDR,
+			   addr);
+	if (ret)
+		return ret;
+
+	return regmap_bulk_read(mpu3050->map,
+				MPU3050_MEM_R_W,
+				buf,
+				len);
+}
+
+static int mpu3050_hw_init(struct mpu3050 *mpu3050)
+{
+	int ret;
+	u8 otp[8];
+
+	/* Reset */
+	ret = regmap_update_bits(mpu3050->map,
+				 MPU3050_PWR_MGM,
+				 MPU3050_PWR_MGM_RESET,
+				 MPU3050_PWR_MGM_RESET);
+	if (ret)
+		return ret;
+
+	/* Turn on the PLL */
+	ret = regmap_update_bits(mpu3050->map,
+				 MPU3050_PWR_MGM,
+				 MPU3050_PWR_MGM_CLKSEL_MASK,
+				 MPU3050_PWR_MGM_PLL_Z);
+	if (ret)
+		return ret;
+
+	/* Disable IRQs */
+	ret = regmap_write(mpu3050->map,
+			   MPU3050_INT_CFG,
+			   0);
+	if (ret)
+		return ret;
+
+	/* Read out the 8 bytes of OTP (one-time-programmable) memory */
+	ret = mpu3050_read_mem(mpu3050,
+			       (MPU3050_MEM_PRFTCH |
+				MPU3050_MEM_USER_BANK |
+				MPU3050_MEM_OTP_BANK_0),
+			       0,
+			       sizeof(otp),
+			       otp);
+	if (ret)
+		return ret;
+
+	/* This is device-unique data so it goes into the entropy pool */
+	add_device_randomness(otp, sizeof(otp));
+
+	dev_info(mpu3050->dev,
+		 "die ID: %04X, wafer ID: %02X, A lot ID: %04X, "
+		 "W lot ID: %03X, WP ID: %01X, rev ID: %02X\n",
+		 /* Die ID, bits 0-12 */
+		 (otp[1] << 8 | otp[0]) & 0x1fff,
+		 /* Wafer ID, bits 13-17 */
+		 ((otp[2] << 8 | otp[1]) & 0x03e0) >> 5,
+		 /* A lot ID, bits 18-33 */
+		 ((otp[4] << 16 | otp[3] << 8 | otp[2]) & 0x3fffc) >> 2,
+		 /* W lot ID, bits 34-45 */
+		 ((otp[5] << 8 | otp[4]) & 0x3ffc) >> 2,
+		 /* WP ID, bits 47-49 */
+		 ((otp[6] << 8 | otp[5]) & 0x0380) >> 7,
+		 /* rev ID, bits 50-55 */
+		 otp[6] >> 2);
+
+	return 0;
+}
+
+static int mpu3050_power_up(struct mpu3050 *mpu3050)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(mpu3050->regs), mpu3050->regs);
+	if (ret) {
+		dev_err(mpu3050->dev, "cannot enable regulators\n");
+		return ret;
+	}
+	/*
+	 * 20-100 ms start-up time for register read/write according to
+	 * the datasheet, be on the safe side and wait 200 ms.
+	 */
+	msleep(200);
+
+	/* Take device out of sleep mode */
+	ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+				 MPU3050_PWR_MGM_SLEEP, 0);
+	if (ret) {
+		dev_err(mpu3050->dev, "error setting power mode\n");
+		return ret;
+	}
+	msleep(10);
+
+	return 0;
+}
+
+static int mpu3050_power_down(struct mpu3050 *mpu3050)
+{
+	int ret;
+
+	/*
+	 * Put MPU-3050 into sleep mode before cutting regulators.
+	 * This is important, because we may not be the sole user
+	 * of the regulator so the power may stay on after this, and
+	 * then we would be wasting power unless we go to sleep mode
+	 * first.
+	 */
+	ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+				 MPU3050_PWR_MGM_SLEEP, MPU3050_PWR_MGM_SLEEP);
+	if (ret)
+		dev_err(mpu3050->dev, "error putting to sleep\n");
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(mpu3050->regs), mpu3050->regs);
+	if (ret)
+		dev_err(mpu3050->dev, "error disabling regulators\n");
+
+	return 0;
+}
+
+static irqreturn_t mpu3050_irq_handler(int irq, void *p)
+{
+	struct iio_trigger *trig = p;
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+	if (!mpu3050->hw_irq_trigger)
+		return IRQ_NONE;
+
+	/* Get the time stamp as close in time as possible */
+	mpu3050->hw_timestamp = iio_get_time_ns(indio_dev);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t mpu3050_irq_thread(int irq, void *p)
+{
+	struct iio_trigger *trig = p;
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+	unsigned int val;
+	int ret;
+
+	/* ACK IRQ and check if it was from us */
+	ret = regmap_read(mpu3050->map, MPU3050_INT_STATUS, &val);
+	if (ret) {
+		dev_err(mpu3050->dev, "error reading IRQ status\n");
+		return IRQ_HANDLED;
+	}
+	if (!(val & MPU3050_INT_STATUS_RAW_RDY))
+		return IRQ_NONE;
+
+	iio_trigger_poll_chained(p);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * mpu3050_drdy_trigger_set_state() - set data ready interrupt state
+ * @trig: trigger instance
+ * @enable: true if trigger should be enabled, false to disable
+ */
+static int mpu3050_drdy_trigger_set_state(struct iio_trigger *trig,
+					  bool enable)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+	unsigned int val;
+	int ret;
+
+	/* Disabling trigger: disable interrupt and return */
+	if (!enable) {
+		/* Disable all interrupts */
+		ret = regmap_write(mpu3050->map,
+				   MPU3050_INT_CFG,
+				   0);
+		if (ret)
+			dev_err(mpu3050->dev, "error disabling IRQ\n");
+
+		/* Clear IRQ flag */
+		ret = regmap_read(mpu3050->map, MPU3050_INT_STATUS, &val);
+		if (ret)
+			dev_err(mpu3050->dev, "error clearing IRQ status\n");
+
+		/* Disable all things in the FIFO and reset it */
+		ret = regmap_write(mpu3050->map, MPU3050_FIFO_EN, 0);
+		if (ret)
+			dev_err(mpu3050->dev, "error disabling FIFO\n");
+
+		ret = regmap_write(mpu3050->map, MPU3050_USR_CTRL,
+				   MPU3050_USR_CTRL_FIFO_RST);
+		if (ret)
+			dev_err(mpu3050->dev, "error resetting FIFO\n");
+
+		pm_runtime_mark_last_busy(mpu3050->dev);
+		pm_runtime_put_autosuspend(mpu3050->dev);
+		mpu3050->hw_irq_trigger = false;
+
+		return 0;
+	} else {
+		/* Else we're enabling the trigger from this point */
+		pm_runtime_get_sync(mpu3050->dev);
+		mpu3050->hw_irq_trigger = true;
+
+		/* Disable all things in the FIFO */
+		ret = regmap_write(mpu3050->map, MPU3050_FIFO_EN, 0);
+		if (ret)
+			return ret;
+
+		/* Reset and enable the FIFO */
+		ret = regmap_update_bits(mpu3050->map, MPU3050_USR_CTRL,
+					 MPU3050_USR_CTRL_FIFO_EN |
+					 MPU3050_USR_CTRL_FIFO_RST,
+					 MPU3050_USR_CTRL_FIFO_EN |
+					 MPU3050_USR_CTRL_FIFO_RST);
+		if (ret)
+			return ret;
+
+		mpu3050->pending_fifo_footer = false;
+
+		/* Turn on the FIFO for temp+X+Y+Z */
+		ret = regmap_write(mpu3050->map, MPU3050_FIFO_EN,
+				   MPU3050_FIFO_EN_TEMP_OUT |
+				   MPU3050_FIFO_EN_GYRO_XOUT |
+				   MPU3050_FIFO_EN_GYRO_YOUT |
+				   MPU3050_FIFO_EN_GYRO_ZOUT |
+				   MPU3050_FIFO_EN_FOOTER);
+		if (ret)
+			return ret;
+
+		/* Configure the sample engine */
+		ret = mpu3050_start_sampling(mpu3050);
+		if (ret)
+			return ret;
+
+		/* Clear IRQ flag */
+		ret = regmap_read(mpu3050->map, MPU3050_INT_STATUS, &val);
+		if (ret)
+			dev_err(mpu3050->dev, "error clearing IRQ status\n");
+
+		/* Give us interrupts whenever there is new data ready */
+		val = MPU3050_INT_RAW_RDY_EN;
+
+		if (mpu3050->irq_actl)
+			val |= MPU3050_INT_ACTL;
+		if (mpu3050->irq_latch)
+			val |= MPU3050_INT_LATCH_EN;
+		if (mpu3050->irq_opendrain)
+			val |= MPU3050_INT_OPEN;
+
+		ret = regmap_write(mpu3050->map, MPU3050_INT_CFG, val);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct iio_trigger_ops mpu3050_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = mpu3050_drdy_trigger_set_state,
+};
+
+static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq)
+{
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+	unsigned long irq_trig;
+	int ret;
+
+	mpu3050->trig = devm_iio_trigger_alloc(&indio_dev->dev,
+					       "%s-dev%d",
+					       indio_dev->name,
+					       indio_dev->id);
+	if (!mpu3050->trig)
+		return -ENOMEM;
+
+	/* Check if IRQ is open drain */
+	if (of_property_read_bool(mpu3050->dev->of_node, "drive-open-drain"))
+		mpu3050->irq_opendrain = true;
+
+	irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
+	/*
+	 * Configure the interrupt generator hardware to supply whatever
+	 * the interrupt is configured for, edges low/high level low/high,
+	 * we can provide it all.
+	 */
+	switch (irq_trig) {
+	case IRQF_TRIGGER_RISING:
+		dev_info(&indio_dev->dev,
+			 "pulse interrupts on the rising edge\n");
+		if (mpu3050->irq_opendrain) {
+			dev_info(&indio_dev->dev,
+				 "rising edge incompatible with open drain\n");
+			mpu3050->irq_opendrain = false;
+		}
+		break;
+	case IRQF_TRIGGER_FALLING:
+		mpu3050->irq_actl = true;
+		dev_info(&indio_dev->dev,
+			 "pulse interrupts on the falling edge\n");
+		break;
+	case IRQF_TRIGGER_HIGH:
+		mpu3050->irq_latch = true;
+		dev_info(&indio_dev->dev,
+			 "interrupts active high level\n");
+		if (mpu3050->irq_opendrain) {
+			dev_info(&indio_dev->dev,
+				 "active high incompatible with open drain\n");
+			mpu3050->irq_opendrain = false;
+		}
+		/*
+		 * With level IRQs, we mask the IRQ until it is processed,
+		 * but with edge IRQs (pulses) we can queue several interrupts
+		 * in the top half.
+		 */
+		irq_trig |= IRQF_ONESHOT;
+		break;
+	case IRQF_TRIGGER_LOW:
+		mpu3050->irq_latch = true;
+		mpu3050->irq_actl = true;
+		irq_trig |= IRQF_ONESHOT;
+		dev_info(&indio_dev->dev,
+			 "interrupts active low level\n");
+		break;
+	default:
+		/* This is the most preferred mode, if possible */
+		dev_err(&indio_dev->dev,
+			"unsupported IRQ trigger specified (%lx), enforce "
+			"rising edge\n", irq_trig);
+		irq_trig = IRQF_TRIGGER_RISING;
+		break;
+	}
+
+	/* An open drain line can be shared with several devices */
+	if (mpu3050->irq_opendrain)
+		irq_trig |= IRQF_SHARED;
+
+	ret = request_threaded_irq(irq,
+				   mpu3050_irq_handler,
+				   mpu3050_irq_thread,
+				   irq_trig,
+				   mpu3050->trig->name,
+				   mpu3050->trig);
+	if (ret) {
+		dev_err(mpu3050->dev,
+			"can't get IRQ %d, error %d\n", irq, ret);
+		return ret;
+	}
+
+	mpu3050->irq = irq;
+	mpu3050->trig->dev.parent = mpu3050->dev;
+	mpu3050->trig->ops = &mpu3050_trigger_ops;
+	iio_trigger_set_drvdata(mpu3050->trig, indio_dev);
+
+	ret = iio_trigger_register(mpu3050->trig);
+	if (ret)
+		return ret;
+
+	indio_dev->trig = iio_trigger_get(mpu3050->trig);
+
+	return 0;
+}
+
+int mpu3050_common_probe(struct device *dev,
+			 struct regmap *map,
+			 int irq,
+			 const char *name)
+{
+	struct iio_dev *indio_dev;
+	struct mpu3050 *mpu3050;
+	unsigned int val;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*mpu3050));
+	if (!indio_dev)
+		return -ENOMEM;
+	mpu3050 = iio_priv(indio_dev);
+
+	mpu3050->dev = dev;
+	mpu3050->map = map;
+	mutex_init(&mpu3050->lock);
+	/* Default fullscale: 2000 degrees per second */
+	mpu3050->fullscale = FS_2000_DPS;
+	/* 1 kHz, divide by 100, default frequency = 10 Hz */
+	mpu3050->lpf = MPU3050_DLPF_CFG_188HZ;
+	mpu3050->divisor = 99;
+
+	/* Read the mounting matrix, if present */
+	ret = of_iio_read_mount_matrix(dev, "mount-matrix",
+				       &mpu3050->orientation);
+	if (ret)
+		return ret;
+
+	/* Fetch and turn on regulators */
+	mpu3050->regs[0].supply = mpu3050_reg_vdd;
+	mpu3050->regs[1].supply = mpu3050_reg_vlogic;
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(mpu3050->regs),
+				      mpu3050->regs);
+	if (ret) {
+		dev_err(dev, "Cannot get regulators\n");
+		return ret;
+	}
+
+	ret = mpu3050_power_up(mpu3050);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(map, MPU3050_CHIP_ID_REG, &val);
+	if (ret) {
+		dev_err(dev, "could not read device ID\n");
+		ret = -ENODEV;
+
+		goto err_power_down;
+	}
+
+	if (val != MPU3050_CHIP_ID) {
+		dev_err(dev, "unsupported chip id %02x\n", (u8)val);
+		ret = -ENODEV;
+		goto err_power_down;
+	}
+
+	ret = regmap_read(map, MPU3050_PRODUCT_ID_REG, &val);
+	if (ret) {
+		dev_err(dev, "could not read device ID\n");
+		ret = -ENODEV;
+
+		goto err_power_down;
+	}
+	dev_info(dev, "found MPU-3050 part no: %d, version: %d\n",
+		 ((val >> 4) & 0xf), (val & 0xf));
+
+	ret = mpu3050_hw_init(mpu3050);
+	if (ret)
+		goto err_power_down;
+
+	indio_dev->dev.parent = dev;
+	indio_dev->channels = mpu3050_channels;
+	indio_dev->num_channels = ARRAY_SIZE(mpu3050_channels);
+	indio_dev->info = &mpu3050_info;
+	indio_dev->available_scan_masks = mpu3050_scan_masks;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->name = name;
+
+	ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
+					 mpu3050_trigger_handler,
+					 &mpu3050_buffer_setup_ops);
+	if (ret) {
+		dev_err(dev, "triggered buffer setup failed\n");
+		goto err_power_down;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(dev, "device register failed\n");
+		goto err_cleanup_buffer;
+	}
+
+	dev_set_drvdata(dev, indio_dev);
+
+	/* Check if we have an assigned IRQ to use as trigger */
+	if (irq) {
+		ret = mpu3050_trigger_probe(indio_dev, irq);
+		if (ret)
+			dev_err(dev, "failed to register trigger\n");
+	}
+
+	/* Enable runtime PM */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	/*
+	 * Set autosuspend to two orders of magnitude larger than the
+	 * start-up time. 100ms start-up time means 10000ms autosuspend,
+	 * i.e. 10 seconds.
+	 */
+	pm_runtime_set_autosuspend_delay(dev, 10000);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_put(dev);
+
+	return 0;
+
+err_cleanup_buffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_power_down:
+	mpu3050_power_down(mpu3050);
+
+	return ret;
+}
+EXPORT_SYMBOL(mpu3050_common_probe);
+
+int mpu3050_common_remove(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+	pm_runtime_get_sync(dev);
+	pm_runtime_put_noidle(dev);
+	pm_runtime_disable(dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	if (mpu3050->irq)
+		free_irq(mpu3050->irq, mpu3050);
+	iio_device_unregister(indio_dev);
+	mpu3050_power_down(mpu3050);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpu3050_common_remove);
+
+#ifdef CONFIG_PM
+static int mpu3050_runtime_suspend(struct device *dev)
+{
+	return mpu3050_power_down(iio_priv(dev_get_drvdata(dev)));
+}
+
+static int mpu3050_runtime_resume(struct device *dev)
+{
+	return mpu3050_power_up(iio_priv(dev_get_drvdata(dev)));
+}
+#endif /* CONFIG_PM */
+
+const struct dev_pm_ops mpu3050_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(mpu3050_runtime_suspend,
+			   mpu3050_runtime_resume, NULL)
+};
+EXPORT_SYMBOL(mpu3050_dev_pm_ops);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("MPU3050 gyroscope driver");
+MODULE_LICENSE("GPL");

+ 124 - 0
drivers/iio/gyro/mpu3050-i2c.c

@@ -0,0 +1,124 @@
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mpu3050.h"
+
+static const struct regmap_config mpu3050_i2c_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int mpu3050_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
+{
+	struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
+
+	/* Just power up the device, that is all that is needed */
+	pm_runtime_get_sync(mpu3050->dev);
+	return 0;
+}
+
+static int mpu3050_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
+{
+	struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
+
+	pm_runtime_mark_last_busy(mpu3050->dev);
+	pm_runtime_put_autosuspend(mpu3050->dev);
+	return 0;
+}
+
+static int mpu3050_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+	const char *name;
+	struct mpu3050 *mpu3050;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_I2C_BLOCK))
+		return -EOPNOTSUPP;
+
+	if (id)
+		name = id->name;
+	else
+		return -ENODEV;
+
+	regmap = devm_regmap_init_i2c(client, &mpu3050_i2c_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+
+	ret = mpu3050_common_probe(&client->dev, regmap, client->irq, name);
+	if (ret)
+		return ret;
+
+	/* The main driver is up, now register the I2C mux */
+	mpu3050 = iio_priv(dev_get_drvdata(&client->dev));
+	mpu3050->i2cmux = i2c_mux_alloc(client->adapter, &client->dev,
+					1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
+					mpu3050_i2c_bypass_select,
+					mpu3050_i2c_bypass_deselect);
+	/* Just fail the mux, there is no point in killing the driver */
+	if (!mpu3050->i2cmux)
+		dev_err(&client->dev, "failed to allocate I2C mux\n");
+	else {
+		mpu3050->i2cmux->priv = mpu3050;
+		ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
+		if (ret)
+			dev_err(&client->dev, "failed to add I2C mux\n");
+	}
+
+	return 0;
+}
+
+static int mpu3050_i2c_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
+	struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+	if (mpu3050->i2cmux)
+		i2c_mux_del_adapters(mpu3050->i2cmux);
+
+	return mpu3050_common_remove(&client->dev);
+}
+
+/*
+ * device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id mpu3050_i2c_id[] = {
+	{ "mpu3050" },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, mpu3050_i2c_id);
+
+static const struct of_device_id mpu3050_i2c_of_match[] = {
+	{ .compatible = "invensense,mpu3050", .data = "mpu3050" },
+	/* Deprecated vendor ID from the Input driver */
+	{ .compatible = "invn,mpu3050", .data = "mpu3050" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, mpu3050_i2c_of_match);
+
+static struct i2c_driver mpu3050_i2c_driver = {
+	.probe = mpu3050_i2c_probe,
+	.remove = mpu3050_i2c_remove,
+	.id_table = mpu3050_i2c_id,
+	.driver = {
+		.of_match_table = mpu3050_i2c_of_match,
+		.name = "mpu3050-i2c",
+		.pm = &mpu3050_dev_pm_ops,
+	},
+};
+module_i2c_driver(mpu3050_i2c_driver);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("Invensense MPU3050 gyroscope driver");
+MODULE_LICENSE("GPL");

+ 96 - 0
drivers/iio/gyro/mpu3050.h

@@ -0,0 +1,96 @@
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+
+/**
+ * enum mpu3050_fullscale - indicates the full range of the sensor in deg/sec
+ */
+enum mpu3050_fullscale {
+	FS_250_DPS = 0,
+	FS_500_DPS,
+	FS_1000_DPS,
+	FS_2000_DPS,
+};
+
+/**
+ * enum mpu3050_lpf - indicates the low pass filter width
+ */
+enum mpu3050_lpf {
+	/* This implicity sets sample frequency to 8 kHz */
+	LPF_256_HZ_NOLPF = 0,
+	/* All others sets the sample frequency to 1 kHz */
+	LPF_188_HZ,
+	LPF_98_HZ,
+	LPF_42_HZ,
+	LPF_20_HZ,
+	LPF_10_HZ,
+	LPF_5_HZ,
+	LPF_2100_HZ_NOLPF,
+};
+
+enum mpu3050_axis {
+	AXIS_X = 0,
+	AXIS_Y,
+	AXIS_Z,
+	AXIS_MAX,
+};
+
+/**
+ * struct mpu3050 - instance state container for the device
+ * @dev: parent device for this instance
+ * @orientation: mounting matrix, flipped axis etc
+ * @map: regmap to reach the registers
+ * @lock: serialization lock to marshal all requests
+ * @irq: the IRQ used for this device
+ * @regs: the regulators to power this device
+ * @fullscale: the current fullscale setting for the device
+ * @lpf: digital low pass filter setting for the device
+ * @divisor: base frequency divider: divides 8 or 1 kHz
+ * @calibration: the three signed 16-bit calibration settings that
+ * get written into the offset registers for each axis to compensate
+ * for DC offsets
+ * @trig: trigger for the MPU-3050 interrupt, if present
+ * @hw_irq_trigger: hardware interrupt trigger is in use
+ * @irq_actl: interrupt is active low
+ * @irq_latch: latched IRQ, this means that it is a level IRQ
+ * @irq_opendrain: the interrupt line shall be configured open drain
+ * @pending_fifo_footer: tells us if there is a pending footer in the FIFO
+ * that we have to read out first when handling the FIFO
+ * @hw_timestamp: latest hardware timestamp from the trigger IRQ, when in
+ * use
+ * @i2cmux: an I2C mux reflecting the fact that this sensor is a hub with
+ * a pass-through I2C interface coming out of it: this device needs to be
+ * powered up in order to reach devices on the other side of this mux
+ */
+struct mpu3050 {
+	struct device *dev;
+	struct iio_mount_matrix orientation;
+	struct regmap *map;
+	struct mutex lock;
+	int irq;
+	struct regulator_bulk_data regs[2];
+	enum mpu3050_fullscale fullscale;
+	enum mpu3050_lpf lpf;
+	u8 divisor;
+	s16 calibration[3];
+	struct iio_trigger *trig;
+	bool hw_irq_trigger;
+	bool irq_actl;
+	bool irq_latch;
+	bool irq_opendrain;
+	bool pending_fifo_footer;
+	s64 hw_timestamp;
+	struct i2c_mux_core *i2cmux;
+};
+
+/* Probe called from different transports */
+int mpu3050_common_probe(struct device *dev,
+			 struct regmap *map,
+			 int irq,
+			 const char *name);
+int mpu3050_common_remove(struct device *dev);
+
+/* PM ops */
+extern const struct dev_pm_ops mpu3050_dev_pm_ops;

+ 66 - 139
drivers/iio/gyro/st_gyro_core.c

@@ -39,79 +39,6 @@
 #define ST_GYRO_FS_AVL_500DPS			500
 #define ST_GYRO_FS_AVL_2000DPS			2000
 
-/* CUSTOM VALUES FOR SENSOR 1 */
-#define ST_GYRO_1_WAI_EXP			0xd3
-#define ST_GYRO_1_ODR_ADDR			0x20
-#define ST_GYRO_1_ODR_MASK			0xc0
-#define ST_GYRO_1_ODR_AVL_100HZ_VAL		0x00
-#define ST_GYRO_1_ODR_AVL_200HZ_VAL		0x01
-#define ST_GYRO_1_ODR_AVL_400HZ_VAL		0x02
-#define ST_GYRO_1_ODR_AVL_800HZ_VAL		0x03
-#define ST_GYRO_1_PW_ADDR			0x20
-#define ST_GYRO_1_PW_MASK			0x08
-#define ST_GYRO_1_FS_ADDR			0x23
-#define ST_GYRO_1_FS_MASK			0x30
-#define ST_GYRO_1_FS_AVL_250_VAL		0x00
-#define ST_GYRO_1_FS_AVL_500_VAL		0x01
-#define ST_GYRO_1_FS_AVL_2000_VAL		0x02
-#define ST_GYRO_1_FS_AVL_250_GAIN		IIO_DEGREE_TO_RAD(8750)
-#define ST_GYRO_1_FS_AVL_500_GAIN		IIO_DEGREE_TO_RAD(17500)
-#define ST_GYRO_1_FS_AVL_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
-#define ST_GYRO_1_BDU_ADDR			0x23
-#define ST_GYRO_1_BDU_MASK			0x80
-#define ST_GYRO_1_DRDY_IRQ_ADDR			0x22
-#define ST_GYRO_1_DRDY_IRQ_INT2_MASK		0x08
-#define ST_GYRO_1_MULTIREAD_BIT			true
-
-/* CUSTOM VALUES FOR SENSOR 2 */
-#define ST_GYRO_2_WAI_EXP			0xd4
-#define ST_GYRO_2_ODR_ADDR			0x20
-#define ST_GYRO_2_ODR_MASK			0xc0
-#define ST_GYRO_2_ODR_AVL_95HZ_VAL		0x00
-#define ST_GYRO_2_ODR_AVL_190HZ_VAL		0x01
-#define ST_GYRO_2_ODR_AVL_380HZ_VAL		0x02
-#define ST_GYRO_2_ODR_AVL_760HZ_VAL		0x03
-#define ST_GYRO_2_PW_ADDR			0x20
-#define ST_GYRO_2_PW_MASK			0x08
-#define ST_GYRO_2_FS_ADDR			0x23
-#define ST_GYRO_2_FS_MASK			0x30
-#define ST_GYRO_2_FS_AVL_250_VAL		0x00
-#define ST_GYRO_2_FS_AVL_500_VAL		0x01
-#define ST_GYRO_2_FS_AVL_2000_VAL		0x02
-#define ST_GYRO_2_FS_AVL_250_GAIN		IIO_DEGREE_TO_RAD(8750)
-#define ST_GYRO_2_FS_AVL_500_GAIN		IIO_DEGREE_TO_RAD(17500)
-#define ST_GYRO_2_FS_AVL_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
-#define ST_GYRO_2_BDU_ADDR			0x23
-#define ST_GYRO_2_BDU_MASK			0x80
-#define ST_GYRO_2_DRDY_IRQ_ADDR			0x22
-#define ST_GYRO_2_DRDY_IRQ_INT2_MASK		0x08
-#define ST_GYRO_2_MULTIREAD_BIT			true
-
-/* CUSTOM VALUES FOR SENSOR 3 */
-#define ST_GYRO_3_WAI_EXP			0xd7
-#define ST_GYRO_3_ODR_ADDR			0x20
-#define ST_GYRO_3_ODR_MASK			0xc0
-#define ST_GYRO_3_ODR_AVL_95HZ_VAL		0x00
-#define ST_GYRO_3_ODR_AVL_190HZ_VAL		0x01
-#define ST_GYRO_3_ODR_AVL_380HZ_VAL		0x02
-#define ST_GYRO_3_ODR_AVL_760HZ_VAL		0x03
-#define ST_GYRO_3_PW_ADDR			0x20
-#define ST_GYRO_3_PW_MASK			0x08
-#define ST_GYRO_3_FS_ADDR			0x23
-#define ST_GYRO_3_FS_MASK			0x30
-#define ST_GYRO_3_FS_AVL_250_VAL		0x00
-#define ST_GYRO_3_FS_AVL_500_VAL		0x01
-#define ST_GYRO_3_FS_AVL_2000_VAL		0x02
-#define ST_GYRO_3_FS_AVL_250_GAIN		IIO_DEGREE_TO_RAD(8750)
-#define ST_GYRO_3_FS_AVL_500_GAIN		IIO_DEGREE_TO_RAD(17500)
-#define ST_GYRO_3_FS_AVL_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
-#define ST_GYRO_3_BDU_ADDR			0x23
-#define ST_GYRO_3_BDU_MASK			0x80
-#define ST_GYRO_3_DRDY_IRQ_ADDR			0x22
-#define ST_GYRO_3_DRDY_IRQ_INT2_MASK		0x08
-#define ST_GYRO_3_MULTIREAD_BIT			true
-
-
 static const struct iio_chan_spec st_gyro_16bit_channels[] = {
 	ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
 			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@@ -130,7 +57,7 @@ static const struct iio_chan_spec st_gyro_16bit_channels[] = {
 
 static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 	{
-		.wai = ST_GYRO_1_WAI_EXP,
+		.wai = 0xd3,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = L3G4200D_GYRO_DEV_NAME,
@@ -138,18 +65,18 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 		},
 		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
 		.odr = {
-			.addr = ST_GYRO_1_ODR_ADDR,
-			.mask = ST_GYRO_1_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0xc0,
 			.odr_avl = {
-				{ 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, },
-				{ 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, },
-				{ 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, },
-				{ 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, },
+				{ .hz = 100, .value = 0x00, },
+				{ .hz = 200, .value = 0x01, },
+				{ .hz = 400, .value = 0x02, },
+				{ .hz = 800, .value = 0x03, },
 			},
 		},
 		.pw = {
-			.addr = ST_GYRO_1_PW_ADDR,
-			.mask = ST_GYRO_1_PW_MASK,
+			.addr = 0x20,
+			.mask = 0x08,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -158,33 +85,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_GYRO_1_FS_ADDR,
-			.mask = ST_GYRO_1_FS_MASK,
+			.addr = 0x23,
+			.mask = 0x30,
 			.fs_avl = {
 				[0] = {
 					.num = ST_GYRO_FS_AVL_250DPS,
-					.value = ST_GYRO_1_FS_AVL_250_VAL,
-					.gain = ST_GYRO_1_FS_AVL_250_GAIN,
+					.value = 0x00,
+					.gain = IIO_DEGREE_TO_RAD(8750),
 				},
 				[1] = {
 					.num = ST_GYRO_FS_AVL_500DPS,
-					.value = ST_GYRO_1_FS_AVL_500_VAL,
-					.gain = ST_GYRO_1_FS_AVL_500_GAIN,
+					.value = 0x01,
+					.gain = IIO_DEGREE_TO_RAD(17500),
 				},
 				[2] = {
 					.num = ST_GYRO_FS_AVL_2000DPS,
-					.value = ST_GYRO_1_FS_AVL_2000_VAL,
-					.gain = ST_GYRO_1_FS_AVL_2000_GAIN,
+					.value = 0x02,
+					.gain = IIO_DEGREE_TO_RAD(70000),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_GYRO_1_BDU_ADDR,
-			.mask = ST_GYRO_1_BDU_MASK,
+			.addr = 0x23,
+			.mask = 0x80,
 		},
 		.drdy_irq = {
-			.addr = ST_GYRO_1_DRDY_IRQ_ADDR,
-			.mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK,
+			.addr = 0x22,
+			.mask_int2 = 0x08,
 			/*
 			 * The sensor has IHL (active low) and open
 			 * drain settings, but only for INT1 and not
@@ -192,11 +119,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 			 */
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_GYRO_2_WAI_EXP,
+		.wai = 0xd4,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = L3GD20_GYRO_DEV_NAME,
@@ -208,18 +135,18 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 		},
 		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
 		.odr = {
-			.addr = ST_GYRO_2_ODR_ADDR,
-			.mask = ST_GYRO_2_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0xc0,
 			.odr_avl = {
-				{ 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, },
-				{ 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, },
-				{ 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, },
-				{ 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, },
+				{ .hz = 95, .value = 0x00, },
+				{ .hz = 190, .value = 0x01, },
+				{ .hz = 380, .value = 0x02, },
+				{ .hz = 760, .value = 0x03, },
 			},
 		},
 		.pw = {
-			.addr = ST_GYRO_2_PW_ADDR,
-			.mask = ST_GYRO_2_PW_MASK,
+			.addr = 0x20,
+			.mask = 0x08,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -228,33 +155,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_GYRO_2_FS_ADDR,
-			.mask = ST_GYRO_2_FS_MASK,
+			.addr = 0x23,
+			.mask = 0x30,
 			.fs_avl = {
 				[0] = {
 					.num = ST_GYRO_FS_AVL_250DPS,
-					.value = ST_GYRO_2_FS_AVL_250_VAL,
-					.gain = ST_GYRO_2_FS_AVL_250_GAIN,
+					.value = 0x00,
+					.gain = IIO_DEGREE_TO_RAD(8750),
 				},
 				[1] = {
 					.num = ST_GYRO_FS_AVL_500DPS,
-					.value = ST_GYRO_2_FS_AVL_500_VAL,
-					.gain = ST_GYRO_2_FS_AVL_500_GAIN,
+					.value = 0x01,
+					.gain = IIO_DEGREE_TO_RAD(17500),
 				},
 				[2] = {
 					.num = ST_GYRO_FS_AVL_2000DPS,
-					.value = ST_GYRO_2_FS_AVL_2000_VAL,
-					.gain = ST_GYRO_2_FS_AVL_2000_GAIN,
+					.value = 0x02,
+					.gain = IIO_DEGREE_TO_RAD(70000),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_GYRO_2_BDU_ADDR,
-			.mask = ST_GYRO_2_BDU_MASK,
+			.addr = 0x23,
+			.mask = 0x80,
 		},
 		.drdy_irq = {
-			.addr = ST_GYRO_2_DRDY_IRQ_ADDR,
-			.mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK,
+			.addr = 0x22,
+			.mask_int2 = 0x08,
 			/*
 			 * The sensor has IHL (active low) and open
 			 * drain settings, but only for INT1 and not
@@ -262,29 +189,29 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 			 */
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_GYRO_3_WAI_EXP,
+		.wai = 0xd7,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = L3GD20_GYRO_DEV_NAME,
 		},
 		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
 		.odr = {
-			.addr = ST_GYRO_3_ODR_ADDR,
-			.mask = ST_GYRO_3_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0xc0,
 			.odr_avl = {
-				{ 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, },
-				{ 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, },
-				{ 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, },
-				{ 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, },
+				{ .hz = 95, .value = 0x00, },
+				{ .hz = 190, .value = 0x01, },
+				{ .hz = 380, .value = 0x02, },
+				{ .hz = 760, .value = 0x03, },
 			},
 		},
 		.pw = {
-			.addr = ST_GYRO_3_PW_ADDR,
-			.mask = ST_GYRO_3_PW_MASK,
+			.addr = 0x20,
+			.mask = 0x08,
 			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
 			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
 		},
@@ -293,33 +220,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
 		},
 		.fs = {
-			.addr = ST_GYRO_3_FS_ADDR,
-			.mask = ST_GYRO_3_FS_MASK,
+			.addr = 0x23,
+			.mask = 0x30,
 			.fs_avl = {
 				[0] = {
 					.num = ST_GYRO_FS_AVL_250DPS,
-					.value = ST_GYRO_3_FS_AVL_250_VAL,
-					.gain = ST_GYRO_3_FS_AVL_250_GAIN,
+					.value = 0x00,
+					.gain = IIO_DEGREE_TO_RAD(8750),
 				},
 				[1] = {
 					.num = ST_GYRO_FS_AVL_500DPS,
-					.value = ST_GYRO_3_FS_AVL_500_VAL,
-					.gain = ST_GYRO_3_FS_AVL_500_GAIN,
+					.value = 0x01,
+					.gain = IIO_DEGREE_TO_RAD(17500),
 				},
 				[2] = {
 					.num = ST_GYRO_FS_AVL_2000DPS,
-					.value = ST_GYRO_3_FS_AVL_2000_VAL,
-					.gain = ST_GYRO_3_FS_AVL_2000_GAIN,
+					.value = 0x02,
+					.gain = IIO_DEGREE_TO_RAD(70000),
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_GYRO_3_BDU_ADDR,
-			.mask = ST_GYRO_3_BDU_MASK,
+			.addr = 0x23,
+			.mask = 0x80,
 		},
 		.drdy_irq = {
-			.addr = ST_GYRO_3_DRDY_IRQ_ADDR,
-			.mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
+			.addr = 0x22,
+			.mask_int2 = 0x08,
 			/*
 			 * The sensor has IHL (active low) and open
 			 * drain settings, but only for INT1 and not
@@ -327,7 +254,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
 			 */
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
+		.multi_read_bit = true,
 		.bootime = 2,
 	},
 };

+ 24 - 0
drivers/iio/humidity/Kconfig

@@ -27,6 +27,8 @@ config DHT11
 config HDC100X
 	tristate "TI HDC100x relative humidity and temperature sensor"
 	depends on I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
 	help
 	  Say yes here to build support for the Texas Instruments
 	  HDC1000 and HDC1008 relative humidity and temperature sensors.
@@ -34,6 +36,28 @@ config HDC100X
 	  To compile this driver as a module, choose M here: the module
 	  will be called hdc100x.
 
+config HTS221
+	tristate "STMicroelectronics HTS221 sensor Driver"
+	depends on (I2C || SPI)
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select HTS221_I2C if (I2C)
+	select HTS221_SPI if (SPI_MASTER)
+	help
+	  Say yes here to build support for STMicroelectronics HTS221
+	  temperature-humidity sensor
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called hts221.
+
+config HTS221_I2C
+	tristate
+	depends on HTS221
+
+config HTS221_SPI
+	tristate
+	depends on HTS221
+
 config HTU21
 	tristate "Measurement Specialties HTU21 humidity & temperature sensor"
 	depends on I2C

+ 7 - 0
drivers/iio/humidity/Makefile

@@ -5,6 +5,13 @@
 obj-$(CONFIG_AM2315) += am2315.o
 obj-$(CONFIG_DHT11) += dht11.o
 obj-$(CONFIG_HDC100X) += hdc100x.o
+
+hts221-y := hts221_core.o \
+	    hts221_buffer.o
+obj-$(CONFIG_HTS221) += hts221.o
+obj-$(CONFIG_HTS221_I2C) += hts221_i2c.o
+obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
+
 obj-$(CONFIG_HTU21) += htu21.o
 obj-$(CONFIG_SI7005) += si7005.o
 obj-$(CONFIG_SI7020) += si7020.o

+ 128 - 2
drivers/iio/humidity/hdc100x.c

@@ -22,11 +22,15 @@
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
 
 #define HDC100X_REG_TEMP			0x00
 #define HDC100X_REG_HUMIDITY			0x01
 
 #define HDC100X_REG_CONFIG			0x02
+#define HDC100X_REG_CONFIG_ACQ_MODE		BIT(12)
 #define HDC100X_REG_CONFIG_HEATER_EN		BIT(13)
 
 struct hdc100x_data {
@@ -87,22 +91,40 @@ static const struct iio_chan_spec hdc100x_channels[] = {
 			BIT(IIO_CHAN_INFO_SCALE) |
 			BIT(IIO_CHAN_INFO_INT_TIME) |
 			BIT(IIO_CHAN_INFO_OFFSET),
+		.scan_index = 0,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_BE,
+		},
 	},
 	{
 		.type = IIO_HUMIDITYRELATIVE,
 		.address = HDC100X_REG_HUMIDITY,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 			BIT(IIO_CHAN_INFO_SCALE) |
-			BIT(IIO_CHAN_INFO_INT_TIME)
+			BIT(IIO_CHAN_INFO_INT_TIME),
+		.scan_index = 1,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_BE,
+		},
 	},
 	{
 		.type = IIO_CURRENT,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 		.extend_name = "heater",
 		.output = 1,
+		.scan_index = -1,
 	},
+	IIO_CHAN_SOFT_TIMESTAMP(2),
 };
 
+static const unsigned long hdc100x_scan_masks[] = {0x3, 0};
+
 static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
 {
 	int tmp = (~mask & data->config) | val;
@@ -183,7 +205,14 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
 			*val = hdc100x_get_heater_status(data);
 			ret = IIO_VAL_INT;
 		} else {
+			ret = iio_device_claim_direct_mode(indio_dev);
+			if (ret) {
+				mutex_unlock(&data->lock);
+				return ret;
+			}
+
 			ret = hdc100x_get_measurement(data, chan);
+			iio_device_release_direct_mode(indio_dev);
 			if (ret >= 0) {
 				*val = ret;
 				ret = IIO_VAL_INT;
@@ -246,6 +275,78 @@ static int hdc100x_write_raw(struct iio_dev *indio_dev,
 	}
 }
 
+static int hdc100x_buffer_postenable(struct iio_dev *indio_dev)
+{
+	struct hdc100x_data *data = iio_priv(indio_dev);
+	int ret;
+
+	/* Buffer is enabled. First set ACQ Mode, then attach poll func */
+	mutex_lock(&data->lock);
+	ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE,
+				    HDC100X_REG_CONFIG_ACQ_MODE);
+	mutex_unlock(&data->lock);
+	if (ret)
+		return ret;
+
+	return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int hdc100x_buffer_predisable(struct iio_dev *indio_dev)
+{
+	struct hdc100x_data *data = iio_priv(indio_dev);
+	int ret;
+
+	/* First detach poll func, then reset ACQ mode. OK to disable buffer */
+	ret = iio_triggered_buffer_predisable(indio_dev);
+	if (ret)
+		return ret;
+
+	mutex_lock(&data->lock);
+	ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = {
+	.postenable  = hdc100x_buffer_postenable,
+	.predisable  = hdc100x_buffer_predisable,
+};
+
+static irqreturn_t hdc100x_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct hdc100x_data *data = iio_priv(indio_dev);
+	struct i2c_client *client = data->client;
+	int delay = data->adc_int_us[0] + data->adc_int_us[1];
+	int ret;
+	s16 buf[8];  /* 2x s16 + padding + 8 byte timestamp */
+
+	/* dual read starts at temp register */
+	mutex_lock(&data->lock);
+	ret = i2c_smbus_write_byte(client, HDC100X_REG_TEMP);
+	if (ret < 0) {
+		dev_err(&client->dev, "cannot start measurement\n");
+		goto err;
+	}
+	usleep_range(delay, delay + 1000);
+
+	ret = i2c_master_recv(client, (u8 *)buf, 4);
+	if (ret < 0) {
+		dev_err(&client->dev, "cannot read sensor data\n");
+		goto err;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, buf,
+					   iio_get_time_ns(indio_dev));
+err:
+	mutex_unlock(&data->lock);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
 static const struct iio_info hdc100x_info = {
 	.read_raw = hdc100x_read_raw,
 	.write_raw = hdc100x_write_raw,
@@ -258,6 +359,7 @@ static int hdc100x_probe(struct i2c_client *client,
 {
 	struct iio_dev *indio_dev;
 	struct hdc100x_data *data;
+	int ret;
 
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
 				     I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
@@ -279,12 +381,35 @@ static int hdc100x_probe(struct i2c_client *client,
 
 	indio_dev->channels = hdc100x_channels;
 	indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
+	indio_dev->available_scan_masks = hdc100x_scan_masks;
 
 	/* be sure we are in a known state */
 	hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]);
 	hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
+	hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 hdc100x_trigger_handler,
+					 &hdc_buffer_setup_ops);
+	if (ret < 0) {
+		dev_err(&client->dev, "iio triggered buffer setup failed\n");
+		return ret;
+	}
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		iio_triggered_buffer_cleanup(indio_dev);
+
+	return ret;
+}
+
+static int hdc100x_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
 
-	return devm_iio_device_register(&client->dev, indio_dev);
+	return 0;
 }
 
 static const struct i2c_device_id hdc100x_id[] = {
@@ -298,6 +423,7 @@ static struct i2c_driver hdc100x_driver = {
 		.name	= "hdc100x",
 	},
 	.probe = hdc100x_probe,
+	.remove = hdc100x_remove,
 	.id_table = hdc100x_id,
 };
 module_i2c_driver(hdc100x_driver);

+ 73 - 0
drivers/iio/humidity/hts221.h

@@ -0,0 +1,73 @@
+/*
+ * STMicroelectronics hts221 sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef HTS221_H
+#define HTS221_H
+
+#define HTS221_DEV_NAME		"hts221"
+
+#include <linux/iio/iio.h>
+
+#define HTS221_RX_MAX_LENGTH	8
+#define HTS221_TX_MAX_LENGTH	8
+
+#define HTS221_DATA_SIZE	2
+
+struct hts221_transfer_buffer {
+	u8 rx_buf[HTS221_RX_MAX_LENGTH];
+	u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
+};
+
+struct hts221_transfer_function {
+	int (*read)(struct device *dev, u8 addr, int len, u8 *data);
+	int (*write)(struct device *dev, u8 addr, int len, u8 *data);
+};
+
+#define HTS221_AVG_DEPTH	8
+struct hts221_avg_avl {
+	u16 avg;
+	u8 val;
+};
+
+enum hts221_sensor_type {
+	HTS221_SENSOR_H,
+	HTS221_SENSOR_T,
+	HTS221_SENSOR_MAX,
+};
+
+struct hts221_sensor {
+	u8 cur_avg_idx;
+	int slope, b_gen;
+};
+
+struct hts221_hw {
+	const char *name;
+	struct device *dev;
+
+	struct mutex lock;
+	struct iio_trigger *trig;
+	int irq;
+
+	struct hts221_sensor sensors[HTS221_SENSOR_MAX];
+
+	u8 odr;
+
+	const struct hts221_transfer_function *tf;
+	struct hts221_transfer_buffer tb;
+};
+
+int hts221_config_drdy(struct hts221_hw *hw, bool enable);
+int hts221_probe(struct iio_dev *iio_dev);
+int hts221_power_on(struct hts221_hw *hw);
+int hts221_power_off(struct hts221_hw *hw);
+int hts221_allocate_buffers(struct hts221_hw *hw);
+int hts221_allocate_trigger(struct hts221_hw *hw);
+
+#endif /* HTS221_H */

+ 168 - 0
drivers/iio/humidity/hts221_buffer.c

@@ -0,0 +1,168 @@
+/*
+ * STMicroelectronics hts221 sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+
+#include "hts221.h"
+
+#define HTS221_REG_STATUS_ADDR		0x27
+#define HTS221_RH_DRDY_MASK		BIT(1)
+#define HTS221_TEMP_DRDY_MASK		BIT(0)
+
+static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
+	struct hts221_hw *hw = iio_priv(iio_dev);
+
+	return hts221_config_drdy(hw, state);
+}
+
+static const struct iio_trigger_ops hts221_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = hts221_trig_set_state,
+};
+
+static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
+{
+	struct hts221_hw *hw = (struct hts221_hw *)private;
+	u8 status;
+	int err;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
+			   &status);
+	if (err < 0)
+		return IRQ_HANDLED;
+
+	/* 
+	 * H_DA bit (humidity data available) is routed to DRDY line.
+	 * Humidity sample is computed after temperature one.
+	 * Here we can assume data channels are both available if H_DA bit
+	 * is set in status register
+	 */
+	if (!(status & HTS221_RH_DRDY_MASK))
+		return IRQ_NONE;
+
+	iio_trigger_poll_chained(hw->trig);
+
+	return IRQ_HANDLED;
+}
+
+int hts221_allocate_trigger(struct hts221_hw *hw)
+{
+	struct iio_dev *iio_dev = iio_priv_to_dev(hw);
+	unsigned long irq_type;
+	int err;
+
+	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
+
+	switch (irq_type) {
+	case IRQF_TRIGGER_HIGH:
+	case IRQF_TRIGGER_RISING:
+		break;
+	default:
+		dev_info(hw->dev,
+			 "mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
+			 irq_type);
+		irq_type = IRQF_TRIGGER_RISING;
+		break;
+	}
+
+	err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
+					hts221_trigger_handler_thread,
+					irq_type | IRQF_ONESHOT,
+					hw->name, hw);
+	if (err) {
+		dev_err(hw->dev, "failed to request trigger irq %d\n",
+			hw->irq);
+		return err;
+	}
+
+	hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
+					  iio_dev->name);
+	if (!hw->trig)
+		return -ENOMEM;
+
+	iio_trigger_set_drvdata(hw->trig, iio_dev);
+	hw->trig->ops = &hts221_trigger_ops;
+	hw->trig->dev.parent = hw->dev;
+	iio_dev->trig = iio_trigger_get(hw->trig);
+
+	return devm_iio_trigger_register(hw->dev, hw->trig);
+}
+
+static int hts221_buffer_preenable(struct iio_dev *iio_dev)
+{
+	return hts221_power_on(iio_priv(iio_dev));
+}
+
+static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
+{
+	return hts221_power_off(iio_priv(iio_dev));
+}
+
+static const struct iio_buffer_setup_ops hts221_buffer_ops = {
+	.preenable = hts221_buffer_preenable,
+	.postenable = iio_triggered_buffer_postenable,
+	.predisable = iio_triggered_buffer_predisable,
+	.postdisable = hts221_buffer_postdisable,
+};
+
+static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
+{
+	u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio_dev = pf->indio_dev;
+	struct hts221_hw *hw = iio_priv(iio_dev);
+	struct iio_chan_spec const *ch;
+	int err;
+
+	/* humidity data */
+	ch = &iio_dev->channels[HTS221_SENSOR_H];
+	err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
+			   buffer);
+	if (err < 0)
+		goto out;
+
+	/* temperature data */
+	ch = &iio_dev->channels[HTS221_SENSOR_T];
+	err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
+			   buffer + HTS221_DATA_SIZE);
+	if (err < 0)
+		goto out;
+
+	iio_push_to_buffers_with_timestamp(iio_dev, buffer,
+					   iio_get_time_ns(iio_dev));
+
+out:
+	iio_trigger_notify_done(hw->trig);
+
+	return IRQ_HANDLED;
+}
+
+int hts221_allocate_buffers(struct hts221_hw *hw)
+{
+	return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
+					NULL, hts221_buffer_handler_thread,
+					&hts221_buffer_ops);
+}
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
+MODULE_LICENSE("GPL v2");

+ 687 - 0
drivers/iio/humidity/hts221_core.c

@@ -0,0 +1,687 @@
+/*
+ * STMicroelectronics hts221 sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/iio/sysfs.h>
+#include <linux/delay.h>
+#include <asm/unaligned.h>
+
+#include "hts221.h"
+
+#define HTS221_REG_WHOAMI_ADDR		0x0f
+#define HTS221_REG_WHOAMI_VAL		0xbc
+
+#define HTS221_REG_CNTRL1_ADDR		0x20
+#define HTS221_REG_CNTRL2_ADDR		0x21
+#define HTS221_REG_CNTRL3_ADDR		0x22
+
+#define HTS221_REG_AVG_ADDR		0x10
+#define HTS221_REG_H_OUT_L		0x28
+#define HTS221_REG_T_OUT_L		0x2a
+
+#define HTS221_HUMIDITY_AVG_MASK	0x07
+#define HTS221_TEMP_AVG_MASK		0x38
+
+#define HTS221_ODR_MASK			0x87
+#define HTS221_BDU_MASK			BIT(2)
+
+#define HTS221_DRDY_MASK		BIT(2)
+
+#define HTS221_ENABLE_SENSOR		BIT(7)
+
+#define HTS221_HUMIDITY_AVG_4		0x00 /* 0.4 %RH */
+#define HTS221_HUMIDITY_AVG_8		0x01 /* 0.3 %RH */
+#define HTS221_HUMIDITY_AVG_16		0x02 /* 0.2 %RH */
+#define HTS221_HUMIDITY_AVG_32		0x03 /* 0.15 %RH */
+#define HTS221_HUMIDITY_AVG_64		0x04 /* 0.1 %RH */
+#define HTS221_HUMIDITY_AVG_128		0x05 /* 0.07 %RH */
+#define HTS221_HUMIDITY_AVG_256		0x06 /* 0.05 %RH */
+#define HTS221_HUMIDITY_AVG_512		0x07 /* 0.03 %RH */
+
+#define HTS221_TEMP_AVG_2		0x00 /* 0.08 degC */
+#define HTS221_TEMP_AVG_4		0x08 /* 0.05 degC */
+#define HTS221_TEMP_AVG_8		0x10 /* 0.04 degC */
+#define HTS221_TEMP_AVG_16		0x18 /* 0.03 degC */
+#define HTS221_TEMP_AVG_32		0x20 /* 0.02 degC */
+#define HTS221_TEMP_AVG_64		0x28 /* 0.015 degC */
+#define HTS221_TEMP_AVG_128		0x30 /* 0.01 degC */
+#define HTS221_TEMP_AVG_256		0x38 /* 0.007 degC */
+
+/* calibration registers */
+#define HTS221_REG_0RH_CAL_X_H		0x36
+#define HTS221_REG_1RH_CAL_X_H		0x3a
+#define HTS221_REG_0RH_CAL_Y_H		0x30
+#define HTS221_REG_1RH_CAL_Y_H		0x31
+#define HTS221_REG_0T_CAL_X_L		0x3c
+#define HTS221_REG_1T_CAL_X_L		0x3e
+#define HTS221_REG_0T_CAL_Y_H		0x32
+#define HTS221_REG_1T_CAL_Y_H		0x33
+#define HTS221_REG_T1_T0_CAL_Y_H	0x35
+
+struct hts221_odr {
+	u8 hz;
+	u8 val;
+};
+
+struct hts221_avg {
+	u8 addr;
+	u8 mask;
+	struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
+};
+
+static const struct hts221_odr hts221_odr_table[] = {
+	{  1, 0x01 },	/* 1Hz */
+	{  7, 0x02 },	/* 7Hz */
+	{ 13, 0x03 },	/* 12.5Hz */
+};
+
+static const struct hts221_avg hts221_avg_list[] = {
+	{
+		.addr = HTS221_REG_AVG_ADDR,
+		.mask = HTS221_HUMIDITY_AVG_MASK,
+		.avg_avl = {
+			{ 4, HTS221_HUMIDITY_AVG_4 },
+			{ 8, HTS221_HUMIDITY_AVG_8 },
+			{ 16, HTS221_HUMIDITY_AVG_16 },
+			{ 32, HTS221_HUMIDITY_AVG_32 },
+			{ 64, HTS221_HUMIDITY_AVG_64 },
+			{ 128, HTS221_HUMIDITY_AVG_128 },
+			{ 256, HTS221_HUMIDITY_AVG_256 },
+			{ 512, HTS221_HUMIDITY_AVG_512 },
+		},
+	},
+	{
+		.addr = HTS221_REG_AVG_ADDR,
+		.mask = HTS221_TEMP_AVG_MASK,
+		.avg_avl = {
+			{ 2, HTS221_TEMP_AVG_2 },
+			{ 4, HTS221_TEMP_AVG_4 },
+			{ 8, HTS221_TEMP_AVG_8 },
+			{ 16, HTS221_TEMP_AVG_16 },
+			{ 32, HTS221_TEMP_AVG_32 },
+			{ 64, HTS221_TEMP_AVG_64 },
+			{ 128, HTS221_TEMP_AVG_128 },
+			{ 256, HTS221_TEMP_AVG_256 },
+		},
+	},
+};
+
+static const struct iio_chan_spec hts221_channels[] = {
+	{
+		.type = IIO_HUMIDITYRELATIVE,
+		.address = HTS221_REG_H_OUT_L,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_OFFSET) |
+				      BIT(IIO_CHAN_INFO_SCALE) |
+				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.scan_index = 0,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
+	},
+	{
+		.type = IIO_TEMP,
+		.address = HTS221_REG_T_OUT_L,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_OFFSET) |
+				      BIT(IIO_CHAN_INFO_SCALE) |
+				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.scan_index = 1,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
+				  u8 val)
+{
+	u8 data;
+	int err;
+
+	mutex_lock(&hw->lock);
+
+	err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
+	if (err < 0) {
+		dev_err(hw->dev, "failed to read %02x register\n", addr);
+		goto unlock;
+	}
+
+	data = (data & ~mask) | (val & mask);
+
+	err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
+	if (err < 0)
+		dev_err(hw->dev, "failed to write %02x register\n", addr);
+
+unlock:
+	mutex_unlock(&hw->lock);
+
+	return err;
+}
+
+static int hts221_check_whoami(struct hts221_hw *hw)
+{
+	u8 data;
+	int err;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
+			   &data);
+	if (err < 0) {
+		dev_err(hw->dev, "failed to read whoami register\n");
+		return err;
+	}
+
+	if (data != HTS221_REG_WHOAMI_VAL) {
+		dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
+			data, HTS221_REG_WHOAMI_VAL);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+int hts221_config_drdy(struct hts221_hw *hw, bool enable)
+{
+	u8 val = enable ? BIT(2) : 0;
+	int err;
+
+	err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
+				     HTS221_DRDY_MASK, val);
+
+	return err < 0 ? err : 0;
+}
+
+static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
+{
+	int i, err;
+	u8 val;
+
+	for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
+		if (hts221_odr_table[i].hz == odr)
+			break;
+
+	if (i == ARRAY_SIZE(hts221_odr_table))
+		return -EINVAL;
+
+	val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
+	err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
+				     HTS221_ODR_MASK, val);
+	if (err < 0)
+		return err;
+
+	hw->odr = odr;
+
+	return 0;
+}
+
+static int hts221_update_avg(struct hts221_hw *hw,
+			     enum hts221_sensor_type type,
+			     u16 val)
+{
+	int i, err;
+	const struct hts221_avg *avg = &hts221_avg_list[type];
+
+	for (i = 0; i < HTS221_AVG_DEPTH; i++)
+		if (avg->avg_avl[i].avg == val)
+			break;
+
+	if (i == HTS221_AVG_DEPTH)
+		return -EINVAL;
+
+	err = hts221_write_with_mask(hw, avg->addr, avg->mask,
+				     avg->avg_avl[i].val);
+	if (err < 0)
+		return err;
+
+	hw->sensors[type].cur_avg_idx = i;
+
+	return 0;
+}
+
+static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	int i;
+	ssize_t len = 0;
+
+	for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+				 hts221_odr_table[i].hz);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static ssize_t
+hts221_sysfs_rh_oversampling_avail(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
+	ssize_t len = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+				 avg->avg_avl[i].avg);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static ssize_t
+hts221_sysfs_temp_oversampling_avail(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
+	ssize_t len = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+				 avg->avg_avl[i].avg);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+int hts221_power_on(struct hts221_hw *hw)
+{
+	return hts221_update_odr(hw, hw->odr);
+}
+
+int hts221_power_off(struct hts221_hw *hw)
+{
+	u8 data[] = {0x00, 0x00};
+
+	return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
+			     data);
+}
+
+static int hts221_parse_temp_caldata(struct hts221_hw *hw)
+{
+	int err, *slope, *b_gen;
+	s16 cal_x0, cal_x1, cal_y0, cal_y1;
+	u8 cal0, cal1;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
+			   sizeof(cal0), &cal0);
+	if (err < 0)
+		return err;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
+			   sizeof(cal1), &cal1);
+	if (err < 0)
+		return err;
+	cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
+			   sizeof(cal0), &cal0);
+	if (err < 0)
+		return err;
+	cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
+			   (u8 *)&cal_x0);
+	if (err < 0)
+		return err;
+	cal_x0 = le16_to_cpu(cal_x0);
+
+	err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
+			   (u8 *)&cal_x1);
+	if (err < 0)
+		return err;
+	cal_x1 = le16_to_cpu(cal_x1);
+
+	slope = &hw->sensors[HTS221_SENSOR_T].slope;
+	b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
+
+	*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+	*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
+		 (cal_x1 - cal_x0);
+	*b_gen *= 8;
+
+	return 0;
+}
+
+static int hts221_parse_rh_caldata(struct hts221_hw *hw)
+{
+	int err, *slope, *b_gen;
+	s16 cal_x0, cal_x1, cal_y0, cal_y1;
+	u8 data;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
+			   &data);
+	if (err < 0)
+		return err;
+	cal_y0 = data;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
+			   &data);
+	if (err < 0)
+		return err;
+	cal_y1 = data;
+
+	err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
+			   (u8 *)&cal_x0);
+	if (err < 0)
+		return err;
+	cal_x0 = le16_to_cpu(cal_x0);
+
+	err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
+			   (u8 *)&cal_x1);
+	if (err < 0)
+		return err;
+	cal_x1 = le16_to_cpu(cal_x1);
+
+	slope = &hw->sensors[HTS221_SENSOR_H].slope;
+	b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
+
+	*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+	*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
+		 (cal_x1 - cal_x0);
+	*b_gen *= 8;
+
+	return 0;
+}
+
+static int hts221_get_sensor_scale(struct hts221_hw *hw,
+				   enum iio_chan_type ch_type,
+				   int *val, int *val2)
+{
+	s64 tmp;
+	s32 rem, div, data;
+
+	switch (ch_type) {
+	case IIO_HUMIDITYRELATIVE:
+		data = hw->sensors[HTS221_SENSOR_H].slope;
+		div = (1 << 4) * 1000;
+		break;
+	case IIO_TEMP:
+		data = hw->sensors[HTS221_SENSOR_T].slope;
+		div = (1 << 6) * 1000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tmp = div_s64(data * 1000000000LL, div);
+	tmp = div_s64_rem(tmp, 1000000000LL, &rem);
+
+	*val = tmp;
+	*val2 = rem;
+
+	return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int hts221_get_sensor_offset(struct hts221_hw *hw,
+				    enum iio_chan_type ch_type,
+				    int *val, int *val2)
+{
+	s64 tmp;
+	s32 rem, div, data;
+
+	switch (ch_type) {
+	case IIO_HUMIDITYRELATIVE:
+		data = hw->sensors[HTS221_SENSOR_H].b_gen;
+		div = hw->sensors[HTS221_SENSOR_H].slope;
+		break;
+	case IIO_TEMP:
+		data = hw->sensors[HTS221_SENSOR_T].b_gen;
+		div = hw->sensors[HTS221_SENSOR_T].slope;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tmp = div_s64(data * 1000000000LL, div);
+	tmp = div_s64_rem(tmp, 1000000000LL, &rem);
+
+	*val = tmp;
+	*val2 = rem;
+
+	return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
+{
+	u8 data[HTS221_DATA_SIZE];
+	int err;
+
+	err = hts221_power_on(hw);
+	if (err < 0)
+		return err;
+
+	msleep(50);
+
+	err = hw->tf->read(hw->dev, addr, sizeof(data), data);
+	if (err < 0)
+		return err;
+
+	hts221_power_off(hw);
+
+	*val = (s16)get_unaligned_le16(data);
+
+	return IIO_VAL_INT;
+}
+
+static int hts221_read_raw(struct iio_dev *iio_dev,
+			   struct iio_chan_spec const *ch,
+			   int *val, int *val2, long mask)
+{
+	struct hts221_hw *hw = iio_priv(iio_dev);
+	int ret;
+
+	ret = iio_device_claim_direct_mode(iio_dev);
+	if (ret)
+		return ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = hts221_read_oneshot(hw, ch->address, val);
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = hw->odr;
+		ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+		u8 idx;
+		const struct hts221_avg *avg;
+
+		switch (ch->type) {
+		case IIO_HUMIDITYRELATIVE:
+			avg = &hts221_avg_list[HTS221_SENSOR_H];
+			idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
+			*val = avg->avg_avl[idx].avg;
+			ret = IIO_VAL_INT;
+			break;
+		case IIO_TEMP:
+			avg = &hts221_avg_list[HTS221_SENSOR_T];
+			idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
+			*val = avg->avg_avl[idx].avg;
+			ret = IIO_VAL_INT;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	}
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	iio_device_release_direct_mode(iio_dev);
+
+	return ret;
+}
+
+static int hts221_write_raw(struct iio_dev *iio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct hts221_hw *hw = iio_priv(iio_dev);
+	int ret;
+
+	ret = iio_device_claim_direct_mode(iio_dev);
+	if (ret)
+		return ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hts221_update_odr(hw, val);
+		break;
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		switch (chan->type) {
+		case IIO_HUMIDITYRELATIVE:
+			ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
+			break;
+		case IIO_TEMP:
+			ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	iio_device_release_direct_mode(iio_dev);
+
+	return ret;
+}
+
+static int hts221_validate_trigger(struct iio_dev *iio_dev,
+				   struct iio_trigger *trig)
+{
+	struct hts221_hw *hw = iio_priv(iio_dev);
+
+	return hw->trig == trig ? 0 : -EINVAL;
+}
+
+static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
+		       hts221_sysfs_rh_oversampling_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
+		       hts221_sysfs_temp_oversampling_avail, NULL, 0);
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
+
+static struct attribute *hts221_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
+	&iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group hts221_attribute_group = {
+	.attrs = hts221_attributes,
+};
+
+static const struct iio_info hts221_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &hts221_attribute_group,
+	.read_raw = hts221_read_raw,
+	.write_raw = hts221_write_raw,
+	.validate_trigger = hts221_validate_trigger,
+};
+
+static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
+
+int hts221_probe(struct iio_dev *iio_dev)
+{
+	struct hts221_hw *hw = iio_priv(iio_dev);
+	int err;
+	u8 data;
+
+	mutex_init(&hw->lock);
+
+	err = hts221_check_whoami(hw);
+	if (err < 0)
+		return err;
+
+	hw->odr = hts221_odr_table[0].hz;
+
+	iio_dev->modes = INDIO_DIRECT_MODE;
+	iio_dev->dev.parent = hw->dev;
+	iio_dev->available_scan_masks = hts221_scan_masks;
+	iio_dev->channels = hts221_channels;
+	iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
+	iio_dev->name = HTS221_DEV_NAME;
+	iio_dev->info = &hts221_info;
+
+	/* configure humidity sensor */
+	err = hts221_parse_rh_caldata(hw);
+	if (err < 0) {
+		dev_err(hw->dev, "failed to get rh calibration data\n");
+		return err;
+	}
+
+	data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
+	err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
+	if (err < 0) {
+		dev_err(hw->dev, "failed to set rh oversampling ratio\n");
+		return err;
+	}
+
+	/* configure temperature sensor */
+	err = hts221_parse_temp_caldata(hw);
+	if (err < 0) {
+		dev_err(hw->dev,
+			"failed to get temperature calibration data\n");
+		return err;
+	}
+
+	data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
+	err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
+	if (err < 0) {
+		dev_err(hw->dev,
+			"failed to set temperature oversampling ratio\n");
+		return err;
+	}
+
+	if (hw->irq > 0) {
+		err = hts221_allocate_buffers(hw);
+		if (err < 0)
+			return err;
+
+		err = hts221_allocate_trigger(hw);
+		if (err)
+			return err;
+	}
+
+	return devm_iio_device_register(hw->dev, iio_dev);
+}
+EXPORT_SYMBOL(hts221_probe);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
+MODULE_LICENSE("GPL v2");

+ 110 - 0
drivers/iio/humidity/hts221_i2c.c

@@ -0,0 +1,110 @@
+/*
+ * STMicroelectronics hts221 i2c driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include "hts221.h"
+
+#define I2C_AUTO_INCREMENT	0x80
+
+static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
+{
+	struct i2c_msg msg[2];
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (len > 1)
+		addr |= I2C_AUTO_INCREMENT;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags;
+	msg[0].len = 1;
+	msg[0].buf = &addr;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = client->flags | I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = data;
+
+	return i2c_transfer(client->adapter, msg, 2);
+}
+
+static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
+{
+	u8 send[len + 1];
+	struct i2c_msg msg;
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (len > 1)
+		addr |= I2C_AUTO_INCREMENT;
+
+	send[0] = addr;
+	memcpy(&send[1], data, len * sizeof(u8));
+
+	msg.addr = client->addr;
+	msg.flags = client->flags;
+	msg.len = len + 1;
+	msg.buf = send;
+
+	return i2c_transfer(client->adapter, &msg, 1);
+}
+
+static const struct hts221_transfer_function hts221_transfer_fn = {
+	.read = hts221_i2c_read,
+	.write = hts221_i2c_write,
+};
+
+static int hts221_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct hts221_hw *hw;
+	struct iio_dev *iio_dev;
+
+	iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*hw));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, iio_dev);
+
+	hw = iio_priv(iio_dev);
+	hw->name = client->name;
+	hw->dev = &client->dev;
+	hw->irq = client->irq;
+	hw->tf = &hts221_transfer_fn;
+
+	return hts221_probe(iio_dev);
+}
+
+static const struct of_device_id hts221_i2c_of_match[] = {
+	{ .compatible = "st,hts221", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hts221_i2c_of_match);
+
+static const struct i2c_device_id hts221_i2c_id_table[] = {
+	{ HTS221_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table);
+
+static struct i2c_driver hts221_driver = {
+	.driver = {
+		.name = "hts221_i2c",
+		.of_match_table = of_match_ptr(hts221_i2c_of_match),
+	},
+	.probe = hts221_i2c_probe,
+	.id_table = hts221_i2c_id_table,
+};
+module_i2c_driver(hts221_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 i2c driver");
+MODULE_LICENSE("GPL v2");

+ 125 - 0
drivers/iio/humidity/hts221_spi.c

@@ -0,0 +1,125 @@
+/*
+ * STMicroelectronics hts221 spi driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include "hts221.h"
+
+#define SENSORS_SPI_READ	0x80
+#define SPI_AUTO_INCREMENT	0x40
+
+static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
+{
+	int err;
+	struct spi_device *spi = to_spi_device(dev);
+	struct iio_dev *iio_dev = spi_get_drvdata(spi);
+	struct hts221_hw *hw = iio_priv(iio_dev);
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = hw->tb.tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = hw->tb.rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	if (len > 1)
+		addr |= SPI_AUTO_INCREMENT;
+	hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
+
+	err = spi_sync_transfer(spi, xfers,  ARRAY_SIZE(xfers));
+	if (err < 0)
+		return err;
+
+	memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
+
+	return len;
+}
+
+static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct iio_dev *iio_dev = spi_get_drvdata(spi);
+	struct hts221_hw *hw = iio_priv(iio_dev);
+
+	struct spi_transfer xfers = {
+		.tx_buf = hw->tb.tx_buf,
+		.bits_per_word = 8,
+		.len = len + 1,
+	};
+
+	if (len >= HTS221_TX_MAX_LENGTH)
+		return -ENOMEM;
+
+	if (len > 1)
+		addr |= SPI_AUTO_INCREMENT;
+	hw->tb.tx_buf[0] = addr;
+	memcpy(&hw->tb.tx_buf[1], data, len);
+
+	return spi_sync_transfer(spi, &xfers, 1);
+}
+
+static const struct hts221_transfer_function hts221_transfer_fn = {
+	.read = hts221_spi_read,
+	.write = hts221_spi_write,
+};
+
+static int hts221_spi_probe(struct spi_device *spi)
+{
+	struct hts221_hw *hw;
+	struct iio_dev *iio_dev;
+
+	iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*hw));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, iio_dev);
+
+	hw = iio_priv(iio_dev);
+	hw->name = spi->modalias;
+	hw->dev = &spi->dev;
+	hw->irq = spi->irq;
+	hw->tf = &hts221_transfer_fn;
+
+	return hts221_probe(iio_dev);
+}
+
+static const struct of_device_id hts221_spi_of_match[] = {
+	{ .compatible = "st,hts221", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
+
+static const struct spi_device_id hts221_spi_id_table[] = {
+	{ HTS221_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
+
+static struct spi_driver hts221_driver = {
+	.driver = {
+		.name = "hts221_spi",
+		.of_match_table = of_match_ptr(hts221_spi_of_match),
+	},
+	.probe = hts221_spi_probe,
+	.id_table = hts221_spi_id_table,
+};
+module_spi_driver(hts221_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 spi driver");
+MODULE_LICENSE("GPL v2");

+ 10 - 1
drivers/iio/humidity/si7020.c

@@ -154,8 +154,17 @@ static const struct i2c_device_id si7020_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, si7020_id);
 
+static const struct of_device_id si7020_dt_ids[] = {
+	{ .compatible = "silabs,si7020" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, si7020_dt_ids);
+
 static struct i2c_driver si7020_driver = {
-	.driver.name	= "si7020",
+	.driver = {
+		.name = "si7020",
+		.of_match_table = of_match_ptr(si7020_dt_ids),
+	},
 	.probe		= si7020_probe,
 	.id_table	= si7020_id,
 };

+ 2 - 1
drivers/iio/imu/bmi160/bmi160_core.c

@@ -398,7 +398,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
 	struct iio_poll_func *pf = p;
 	struct iio_dev *indio_dev = pf->indio_dev;
 	struct bmi160_data *data = iio_priv(indio_dev);
-	s16 buf[16]; /* 3 sens x 3 axis x s16 + 3 x s16 pad + 4 x s16 tstamp */
+	__le16 buf[16];
+	/* 3 sens x 3 axis x __le16 + 3 x __le16 pad + 4 x __le16 tstamp */
 	int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
 	__le16 sample;
 

+ 1 - 1
drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c

@@ -126,7 +126,7 @@ static int inv_mpu_probe(struct i2c_client *client,
 
 	st = iio_priv(dev_get_drvdata(&client->dev));
 	st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
-				 1, 0, I2C_MUX_LOCKED,
+				 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
 				 inv_mpu6050_select_bypass,
 				 inv_mpu6050_deselect_bypass);
 	if (!st->muxc) {

+ 3 - 4
drivers/iio/industrialio-buffer.c

@@ -307,10 +307,9 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
 	const unsigned long *mask;
 	unsigned long *trialmask;
 
-	trialmask = kmalloc(sizeof(*trialmask)*
-			    BITS_TO_LONGS(indio_dev->masklength),
-			    GFP_KERNEL);
-
+	trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
+				  sizeof(*trialmask),
+				  GFP_KERNEL);
 	if (trialmask == NULL)
 		return -ENOMEM;
 	if (!indio_dev->masklength) {

+ 228 - 33
drivers/iio/industrialio-core.c

@@ -81,6 +81,8 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_PH] = "ph",
 	[IIO_UVINDEX] = "uvindex",
 	[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
+	[IIO_COUNT] = "count",
+	[IIO_INDEX] = "index",
 };
 
 static const char * const iio_modifier_names[] = {
@@ -575,66 +577,82 @@ int of_iio_read_mount_matrix(const struct device *dev,
 #endif
 EXPORT_SYMBOL(of_iio_read_mount_matrix);
 
-/**
- * iio_format_value() - Formats a IIO value into its string representation
- * @buf:	The buffer to which the formatted value gets written
- * @type:	One of the IIO_VAL_... constants. This decides how the val
- *		and val2 parameters are formatted.
- * @size:	Number of IIO value entries contained in vals
- * @vals:	Pointer to the values, exact meaning depends on the
- *		type parameter.
- *
- * Return: 0 by default, a negative number on failure or the
- *	   total number of characters written for a type that belongs
- *	   to the IIO_VAL_... constant.
- */
-ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
+static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
+				  int size, const int *vals)
 {
 	unsigned long long tmp;
+	int tmp0, tmp1;
 	bool scale_db = false;
 
 	switch (type) {
 	case IIO_VAL_INT:
-		return sprintf(buf, "%d\n", vals[0]);
+		return snprintf(buf, len, "%d", vals[0]);
 	case IIO_VAL_INT_PLUS_MICRO_DB:
 		scale_db = true;
 	case IIO_VAL_INT_PLUS_MICRO:
 		if (vals[1] < 0)
-			return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]),
-				       -vals[1], scale_db ? " dB" : "");
+			return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]),
+					-vals[1], scale_db ? " dB" : "");
 		else
-			return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
-				scale_db ? " dB" : "");
+			return snprintf(buf, len, "%d.%06u%s", vals[0], vals[1],
+					scale_db ? " dB" : "");
 	case IIO_VAL_INT_PLUS_NANO:
 		if (vals[1] < 0)
-			return sprintf(buf, "-%d.%09u\n", abs(vals[0]),
-				       -vals[1]);
+			return snprintf(buf, len, "-%d.%09u", abs(vals[0]),
+					-vals[1]);
 		else
-			return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
+			return snprintf(buf, len, "%d.%09u", vals[0], vals[1]);
 	case IIO_VAL_FRACTIONAL:
 		tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
-		vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]);
-		return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1]));
+		tmp1 = vals[1];
+		tmp0 = (int)div_s64_rem(tmp, 1000000000, &tmp1);
+		return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1));
 	case IIO_VAL_FRACTIONAL_LOG2:
 		tmp = (s64)vals[0] * 1000000000LL >> vals[1];
-		vals[1] = do_div(tmp, 1000000000LL);
-		vals[0] = tmp;
-		return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
+		tmp1 = do_div(tmp, 1000000000LL);
+		tmp0 = tmp;
+		return snprintf(buf, len, "%d.%09u", tmp0, tmp1);
 	case IIO_VAL_INT_MULTIPLE:
 	{
 		int i;
-		int len = 0;
+		int l = 0;
 
-		for (i = 0; i < size; ++i)
-			len += snprintf(&buf[len], PAGE_SIZE - len, "%d ",
-								vals[i]);
-		len += snprintf(&buf[len], PAGE_SIZE - len, "\n");
-		return len;
+		for (i = 0; i < size; ++i) {
+			l += snprintf(&buf[l], len - l, "%d ", vals[i]);
+			if (l >= len)
+				break;
+		}
+		return l;
 	}
 	default:
 		return 0;
 	}
 }
+
+/**
+ * iio_format_value() - Formats a IIO value into its string representation
+ * @buf:	The buffer to which the formatted value gets written
+ *		which is assumed to be big enough (i.e. PAGE_SIZE).
+ * @type:	One of the IIO_VAL_... constants. This decides how the val
+ *		and val2 parameters are formatted.
+ * @size:	Number of IIO value entries contained in vals
+ * @vals:	Pointer to the values, exact meaning depends on the
+ *		type parameter.
+ *
+ * Return: 0 by default, a negative number on failure or the
+ *	   total number of characters written for a type that belongs
+ *	   to the IIO_VAL_... constant.
+ */
+ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
+{
+	ssize_t len;
+
+	len = __iio_format_value(buf, PAGE_SIZE, type, size, vals);
+	if (len >= PAGE_SIZE - 1)
+		return -EFBIG;
+
+	return len + sprintf(buf + len, "\n");
+}
 EXPORT_SYMBOL_GPL(iio_format_value);
 
 static ssize_t iio_read_channel_info(struct device *dev,
@@ -662,6 +680,119 @@ static ssize_t iio_read_channel_info(struct device *dev,
 	return iio_format_value(buf, ret, val_len, vals);
 }
 
+static ssize_t iio_format_avail_list(char *buf, const int *vals,
+				     int type, int length)
+{
+	int i;
+	ssize_t len = 0;
+
+	switch (type) {
+	case IIO_VAL_INT:
+		for (i = 0; i < length; i++) {
+			len += __iio_format_value(buf + len, PAGE_SIZE - len,
+						  type, 1, &vals[i]);
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+			if (i < length - 1)
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						"\n");
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+		}
+		break;
+	default:
+		for (i = 0; i < length / 2; i++) {
+			len += __iio_format_value(buf + len, PAGE_SIZE - len,
+						  type, 2, &vals[i * 2]);
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+			if (i < length / 2 - 1)
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						"\n");
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+		}
+	}
+
+	return len;
+}
+
+static ssize_t iio_format_avail_range(char *buf, const int *vals, int type)
+{
+	int i;
+	ssize_t len;
+
+	len = snprintf(buf, PAGE_SIZE, "[");
+	switch (type) {
+	case IIO_VAL_INT:
+		for (i = 0; i < 3; i++) {
+			len += __iio_format_value(buf + len, PAGE_SIZE - len,
+						  type, 1, &vals[i]);
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+			if (i < 2)
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						"]\n");
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+		}
+		break;
+	default:
+		for (i = 0; i < 3; i++) {
+			len += __iio_format_value(buf + len, PAGE_SIZE - len,
+						  type, 2, &vals[i * 2]);
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+			if (i < 2)
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len, PAGE_SIZE - len,
+						"]\n");
+			if (len >= PAGE_SIZE)
+				return -EFBIG;
+		}
+	}
+
+	return len;
+}
+
+static ssize_t iio_read_channel_info_avail(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	const int *vals;
+	int ret;
+	int length;
+	int type;
+
+	ret = indio_dev->info->read_avail(indio_dev, this_attr->c,
+					  &vals, &type, &length,
+					  this_attr->address);
+
+	if (ret < 0)
+		return ret;
+	switch (ret) {
+	case IIO_AVAIL_LIST:
+		return iio_format_avail_list(buf, vals, type, length);
+	case IIO_AVAIL_RANGE:
+		return iio_format_avail_range(buf, vals, type);
+	default:
+		return -EINVAL;
+	}
+}
+
 /**
  * iio_str_to_fixpoint() - Parse a fixed-point number from a string
  * @str: The string to parse
@@ -978,6 +1109,40 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
 	return attrcount;
 }
 
+static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
+					       struct iio_chan_spec const *chan,
+					       enum iio_shared_by shared_by,
+					       const long *infomask)
+{
+	int i, ret, attrcount = 0;
+	char *avail_postfix;
+
+	for_each_set_bit(i, infomask, sizeof(infomask) * 8) {
+		avail_postfix = kasprintf(GFP_KERNEL,
+					  "%s_available",
+					  iio_chan_info_postfix[i]);
+		if (!avail_postfix)
+			return -ENOMEM;
+
+		ret = __iio_add_chan_devattr(avail_postfix,
+					     chan,
+					     &iio_read_channel_info_avail,
+					     NULL,
+					     i,
+					     shared_by,
+					     &indio_dev->dev,
+					     &indio_dev->channel_attr_list);
+		kfree(avail_postfix);
+		if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
+			continue;
+		else if (ret < 0)
+			return ret;
+		attrcount++;
+	}
+
+	return attrcount;
+}
+
 static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
 					struct iio_chan_spec const *chan)
 {
@@ -993,6 +1158,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
 		return ret;
 	attrcount += ret;
 
+	ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+						  IIO_SEPARATE,
+						  &chan->
+						  info_mask_separate_available);
+	if (ret < 0)
+		return ret;
+	attrcount += ret;
+
 	ret = iio_device_add_info_mask_type(indio_dev, chan,
 					    IIO_SHARED_BY_TYPE,
 					    &chan->info_mask_shared_by_type);
@@ -1000,6 +1173,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
 		return ret;
 	attrcount += ret;
 
+	ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+						  IIO_SHARED_BY_TYPE,
+						  &chan->
+						  info_mask_shared_by_type_available);
+	if (ret < 0)
+		return ret;
+	attrcount += ret;
+
 	ret = iio_device_add_info_mask_type(indio_dev, chan,
 					    IIO_SHARED_BY_DIR,
 					    &chan->info_mask_shared_by_dir);
@@ -1007,6 +1188,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
 		return ret;
 	attrcount += ret;
 
+	ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+						  IIO_SHARED_BY_DIR,
+						  &chan->info_mask_shared_by_dir_available);
+	if (ret < 0)
+		return ret;
+	attrcount += ret;
+
 	ret = iio_device_add_info_mask_type(indio_dev, chan,
 					    IIO_SHARED_BY_ALL,
 					    &chan->info_mask_shared_by_all);
@@ -1014,6 +1202,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
 		return ret;
 	attrcount += ret;
 
+	ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+						  IIO_SHARED_BY_ALL,
+						  &chan->info_mask_shared_by_all_available);
+	if (ret < 0)
+		return ret;
+	attrcount += ret;
+
 	if (chan->ext_info) {
 		unsigned int i = 0;
 		for (ext_info = chan->ext_info; ext_info->name; ext_info++) {

+ 21 - 0
drivers/iio/industrialio-trigger.c

@@ -717,6 +717,27 @@ bool iio_trigger_using_own(struct iio_dev *indio_dev)
 }
 EXPORT_SYMBOL(iio_trigger_using_own);
 
+/**
+ * iio_trigger_validate_own_device - Check if a trigger and IIO device belong to
+ *  the same device
+ * @trig: The IIO trigger to check
+ * @indio_dev: the IIO device to check
+ *
+ * This function can be used as the validate_device callback for triggers that
+ * can only be attached to their own device.
+ *
+ * Return: 0 if both the trigger and the IIO device belong to the same
+ * device, -EINVAL otherwise.
+ */
+int iio_trigger_validate_own_device(struct iio_trigger *trig,
+	struct iio_dev *indio_dev)
+{
+	if (indio_dev->dev.parent != trig->dev.parent)
+		return -EINVAL;
+	return 0;
+}
+EXPORT_SYMBOL(iio_trigger_validate_own_device);
+
 void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
 {
 	indio_dev->groups[indio_dev->groupcounter++] =

+ 120 - 3
drivers/iio/inkern.c

@@ -658,6 +658,31 @@ err_unlock:
 }
 EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
 
+static int iio_read_channel_attribute(struct iio_channel *chan,
+				      int *val, int *val2,
+				      enum iio_chan_info_enum attribute)
+{
+	int ret;
+
+	mutex_lock(&chan->indio_dev->info_exist_lock);
+	if (chan->indio_dev->info == NULL) {
+		ret = -ENODEV;
+		goto err_unlock;
+	}
+
+	ret = iio_channel_read(chan, val, val2, attribute);
+err_unlock:
+	mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+	return ret;
+}
+
+int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
+{
+	return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET);
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_offset);
+
 int iio_read_channel_processed(struct iio_channel *chan, int *val)
 {
 	int ret;
@@ -686,22 +711,114 @@ err_unlock:
 EXPORT_SYMBOL_GPL(iio_read_channel_processed);
 
 int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
+{
+	return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE);
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_scale);
+
+static int iio_channel_read_avail(struct iio_channel *chan,
+				  const int **vals, int *type, int *length,
+				  enum iio_chan_info_enum info)
+{
+	if (!iio_channel_has_available(chan->channel, info))
+		return -EINVAL;
+
+	return chan->indio_dev->info->read_avail(chan->indio_dev, chan->channel,
+						 vals, type, length, info);
+}
+
+int iio_read_avail_channel_raw(struct iio_channel *chan,
+			       const int **vals, int *length)
 {
 	int ret;
+	int type;
 
 	mutex_lock(&chan->indio_dev->info_exist_lock);
-	if (chan->indio_dev->info == NULL) {
+	if (!chan->indio_dev->info) {
 		ret = -ENODEV;
 		goto err_unlock;
 	}
 
-	ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
+	ret = iio_channel_read_avail(chan,
+				     vals, &type, length, IIO_CHAN_INFO_RAW);
 err_unlock:
 	mutex_unlock(&chan->indio_dev->info_exist_lock);
 
+	if (ret >= 0 && type != IIO_VAL_INT) {
+		/* raw values are assumed to be IIO_VAL_INT */
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
 	return ret;
 }
-EXPORT_SYMBOL_GPL(iio_read_channel_scale);
+EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw);
+
+static int iio_channel_read_max(struct iio_channel *chan,
+				int *val, int *val2, int *type,
+				enum iio_chan_info_enum info)
+{
+	int unused;
+	const int *vals;
+	int length;
+	int ret;
+
+	if (!val2)
+		val2 = &unused;
+
+	ret = iio_channel_read_avail(chan, &vals, type, &length, info);
+	switch (ret) {
+	case IIO_AVAIL_RANGE:
+		switch (*type) {
+		case IIO_VAL_INT:
+			*val = vals[2];
+			break;
+		default:
+			*val = vals[4];
+			*val2 = vals[5];
+		}
+		return 0;
+
+	case IIO_AVAIL_LIST:
+		if (length <= 0)
+			return -EINVAL;
+		switch (*type) {
+		case IIO_VAL_INT:
+			*val = vals[--length];
+			while (length) {
+				if (vals[--length] > *val)
+					*val = vals[length];
+			}
+			break;
+		default:
+			/* FIXME: learn about max for other iio values */
+			return -EINVAL;
+		}
+		return 0;
+
+	default:
+		return ret;
+	}
+}
+
+int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
+{
+	int ret;
+	int type;
+
+	mutex_lock(&chan->indio_dev->info_exist_lock);
+	if (!chan->indio_dev->info) {
+		ret = -ENODEV;
+		goto err_unlock;
+	}
+
+	ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
+err_unlock:
+	mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);
 
 int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
 {

+ 19 - 0
drivers/iio/light/Kconfig

@@ -140,6 +140,18 @@ config GP2AP020A00F
 	  To compile this driver as a module, choose M here: the
 	  module will be called gp2ap020a00f.
 
+config SENSORS_ISL29018
+	tristate "Intersil 29018 light and proximity sensor"
+	depends on I2C
+	select REGMAP_I2C
+	default n
+	help
+	 If you say yes here you get support for ambient light sensing and
+	 proximity infrared sensing from Intersil ISL29018.
+	 This driver will provide the measurements of ambient light intensity
+	 in lux, proximity infrared sensing and normal infrared sensing.
+	 Data from sensor is accessible via sysfs.
+
 config ISL29125
 	tristate "Intersil ISL29125 digital color light sensor"
 	depends on I2C
@@ -326,6 +338,13 @@ config SENSORS_TSL2563
 	 This driver can also be built as a module.  If so, the module
 	 will be called tsl2563.
 
+config TSL2583
+	tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters"
+	depends on I2C
+	help
+	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
+	 Access ALS data via iio, sysfs.
+
 config TSL4531
 	tristate "TAOS TSL4531 ambient light sensors"
 	depends on I2C

+ 2 - 0
drivers/iio/light/Makefile

@@ -17,6 +17,7 @@ obj-$(CONFIG_CM36651)		+= cm36651.o
 obj-$(CONFIG_GP2AP020A00F)	+= gp2ap020a00f.o
 obj-$(CONFIG_HID_SENSOR_ALS)	+= hid-sensor-als.o
 obj-$(CONFIG_HID_SENSOR_PROX)	+= hid-sensor-prox.o
+obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
 obj-$(CONFIG_ISL29125)		+= isl29125.o
 obj-$(CONFIG_JSA1212)		+= jsa1212.o
 obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_SI1145)		+= si1145.o
 obj-$(CONFIG_STK3310)          += stk3310.o
 obj-$(CONFIG_TCS3414)		+= tcs3414.o
 obj-$(CONFIG_TCS3472)		+= tcs3472.o
+obj-$(CONFIG_TSL2583)		+= tsl2583.o
 obj-$(CONFIG_TSL4531)		+= tsl4531.o
 obj-$(CONFIG_US5182D)		+= us5182d.o
 obj-$(CONFIG_VCNL4000)		+= vcnl4000.o

+ 86 - 73
drivers/staging/iio/light/isl29018.c → drivers/iio/light/isl29018.c

@@ -62,16 +62,6 @@
 #define ISL29035_BOUT_SHIFT		0x07
 #define ISL29035_BOUT_MASK		(0x01 << ISL29035_BOUT_SHIFT)
 
-#define ISL29018_INT_TIME_AVAIL		"0.090000 0.005630 0.000351 0.000021"
-#define ISL29023_INT_TIME_AVAIL		"0.090000 0.005600 0.000352 0.000022"
-#define ISL29035_INT_TIME_AVAIL		"0.105000 0.006500 0.000410 0.000025"
-
-static const char * const int_time_avail[] = {
-	ISL29018_INT_TIME_AVAIL,
-	ISL29023_INT_TIME_AVAIL,
-	ISL29035_INT_TIME_AVAIL,
-};
-
 enum isl29018_int_time {
 	ISL29018_INT_TIME_16,
 	ISL29018_INT_TIME_12,
@@ -110,7 +100,8 @@ struct isl29018_chip {
 static int isl29018_set_integration_time(struct isl29018_chip *chip,
 					 unsigned int utime)
 {
-	int i, ret;
+	unsigned int i;
+	int ret;
 	unsigned int int_time, new_int_time;
 
 	for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
@@ -145,7 +136,8 @@ static int isl29018_set_integration_time(struct isl29018_chip *chip,
 
 static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
 {
-	int i, ret;
+	unsigned int i;
+	int ret;
 	struct isl29018_scale new_scale;
 
 	for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
@@ -276,29 +268,35 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
 	return 0;
 }
 
-static ssize_t isl29018_show_scale_available(struct device *dev,
-				    struct device_attribute *attr, char *buf)
+static ssize_t in_illuminance_scale_available_show
+			(struct device *dev, struct device_attribute *attr,
+			 char *buf)
 {
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct isl29018_chip *chip = iio_priv(indio_dev);
-	int i, len = 0;
+	unsigned int i;
+	int len = 0;
 
+	mutex_lock(&chip->lock);
 	for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
 		len += sprintf(buf + len, "%d.%06d ",
 			       isl29018_scales[chip->int_time][i].scale,
 			       isl29018_scales[chip->int_time][i].uscale);
+	mutex_unlock(&chip->lock);
 
 	buf[len - 1] = '\n';
 
 	return len;
 }
 
-static ssize_t isl29018_show_int_time_available(struct device *dev,
-				       struct device_attribute *attr, char *buf)
+static ssize_t in_illuminance_integration_time_available_show
+			(struct device *dev, struct device_attribute *attr,
+			 char *buf)
 {
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct isl29018_chip *chip = iio_priv(indio_dev);
-	int i, len = 0;
+	unsigned int i;
+	int len = 0;
 
 	for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
 		len += sprintf(buf + len, "0.%06d ",
@@ -309,9 +307,27 @@ static ssize_t isl29018_show_int_time_available(struct device *dev,
 	return len;
 }
 
-static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
-					      struct device_attribute *attr,
-					      char *buf)
+/*
+ * From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
+ * infrared suppression:
+ *
+ *   Proximity Sensing Scheme: Bit 7. This bit programs the function
+ * of the proximity detection. Logic 0 of this bit, Scheme 0, makes
+ * full n (4, 8, 12, 16) bits (unsigned) proximity detection. The range
+ * of Scheme 0 proximity count is from 0 to 2^n. Logic 1 of this bit,
+ * Scheme 1, makes n-1 (3, 7, 11, 15) bits (2's complementary)
+ * proximity_less_ambient detection. The range of Scheme 1
+ * proximity count is from -2^(n-1) to 2^(n-1) . The sign bit is extended
+ * for resolutions less than 16. While Scheme 0 has wider dynamic
+ * range, Scheme 1 proximity detection is less affected by the
+ * ambient IR noise variation.
+ *
+ * 0 Sensing IR from LED and ambient
+ * 1 Sensing IR from LED with ambient IR rejection
+ */
+static ssize_t proximity_on_chip_ambient_infrared_suppression_show
+			(struct device *dev, struct device_attribute *attr,
+			 char *buf)
 {
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct isl29018_chip *chip = iio_priv(indio_dev);
@@ -323,9 +339,9 @@ static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
 	return sprintf(buf, "%d\n", chip->prox_scheme);
 }
 
-static ssize_t isl29018_store_prox_infrared_suppression(struct device *dev,
-					       struct device_attribute *attr,
-					       const char *buf, size_t count)
+static ssize_t proximity_on_chip_ambient_infrared_suppression_store
+			(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
 {
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct isl29018_chip *chip = iio_priv(indio_dev);
@@ -357,6 +373,10 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
 	int ret = -EINVAL;
 
 	mutex_lock(&chip->lock);
+	if (chip->suspended) {
+		ret = -EBUSY;
+		goto write_done;
+	}
 	switch (mask) {
 	case IIO_CHAN_INFO_CALIBSCALE:
 		if (chan->type == IIO_LIGHT) {
@@ -366,13 +386,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
 		}
 		break;
 	case IIO_CHAN_INFO_INT_TIME:
-		if (chan->type == IIO_LIGHT) {
-			if (val) {
-				mutex_unlock(&chip->lock);
-				return -EINVAL;
-			}
+		if (chan->type == IIO_LIGHT && !val)
 			ret = isl29018_set_integration_time(chip, val2);
-		}
 		break;
 	case IIO_CHAN_INFO_SCALE:
 		if (chan->type == IIO_LIGHT)
@@ -381,6 +396,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
 	default:
 		break;
 	}
+
+write_done:
 	mutex_unlock(&chip->lock);
 
 	return ret;
@@ -397,8 +414,8 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
 
 	mutex_lock(&chip->lock);
 	if (chip->suspended) {
-		mutex_unlock(&chip->lock);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto read_done;
 	}
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
@@ -445,7 +462,10 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
 	default:
 		break;
 	}
+
+read_done:
 	mutex_unlock(&chip->lock);
+
 	return ret;
 }
 
@@ -482,14 +502,9 @@ static const struct iio_chan_spec isl29023_channels[] = {
 	ISL29018_IR_CHANNEL,
 };
 
-static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO,
-		       isl29018_show_int_time_available, NULL, 0);
-static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO,
-		      isl29018_show_scale_available, NULL, 0);
-static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
-					S_IRUGO | S_IWUSR,
-					isl29018_show_prox_infrared_suppression,
-					isl29018_store_prox_infrared_suppression, 0);
+static IIO_DEVICE_ATTR_RO(in_illuminance_integration_time_available, 0);
+static IIO_DEVICE_ATTR_RO(in_illuminance_scale_available, 0);
+static IIO_DEVICE_ATTR_RW(proximity_on_chip_ambient_infrared_suppression, 0);
 
 #define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
 
@@ -514,30 +529,6 @@ static const struct attribute_group isl29023_group = {
 	.attrs = isl29023_attributes,
 };
 
-static int isl29035_detect(struct isl29018_chip *chip)
-{
-	int status;
-	unsigned int id;
-	struct device *dev = regmap_get_device(chip->regmap);
-
-	status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
-	if (status < 0) {
-		dev_err(dev,
-			"Error reading ID register with error %d\n",
-			status);
-		return status;
-	}
-
-	id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
-
-	if (id != ISL29035_DEVICE_ID)
-		return -ENODEV;
-
-	/* Clear brownout bit */
-	return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID,
-				  ISL29035_BOUT_MASK, 0);
-}
-
 enum {
 	isl29018,
 	isl29023,
@@ -550,12 +541,31 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
 	struct device *dev = regmap_get_device(chip->regmap);
 
 	if (chip->type == isl29035) {
-		status = isl29035_detect(chip);
+		unsigned int id;
+
+		status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
+		if (status < 0) {
+			dev_err(dev,
+				"Error reading ID register with error %d\n",
+				status);
+			return status;
+		}
+
+		id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
+
+		if (id != ISL29035_DEVICE_ID)
+			return -ENODEV;
+
+		/* Clear brownout bit */
+		status = regmap_update_bits(chip->regmap,
+					    ISL29035_REG_DEVICE_ID,
+					    ISL29035_BOUT_MASK, 0);
 		if (status < 0)
 			return status;
 	}
 
-	/* Code added per Intersil Application Note 1534:
+	/*
+	 * Code added per Intersil Application Note 1534:
 	 *     When VDD sinks to approximately 1.8V or below, some of
 	 * the part's registers may change their state. When VDD
 	 * recovers to 2.25V (or greater), the part may thus be in an
@@ -582,7 +592,8 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
 		return status;
 	}
 
-	/* See Intersil AN1534 comments above.
+	/*
+	 * See Intersil AN1534 comments above.
 	 * "Operating Mode" (COMMAND1) register is reprogrammed when
 	 * data is read from the device.
 	 */
@@ -605,12 +616,10 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
 
 	status = isl29018_set_integration_time(chip,
 			isl29018_int_utimes[chip->type][chip->int_time]);
-	if (status < 0) {
+	if (status < 0)
 		dev_err(dev, "Init of isl29018 fails\n");
-		return status;
-	}
 
-	return 0;
+	return status;
 }
 
 static const struct iio_info isl29018_info = {
@@ -713,6 +722,7 @@ static int isl29018_probe(struct i2c_client *client,
 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
 	if (!indio_dev)
 		return -ENOMEM;
+
 	chip = iio_priv(indio_dev);
 
 	i2c_set_clientdata(client, indio_dev);
@@ -752,6 +762,7 @@ static int isl29018_probe(struct i2c_client *client,
 	indio_dev->name = name;
 	indio_dev->dev.parent = &client->dev;
 	indio_dev->modes = INDIO_DIRECT_MODE;
+
 	return devm_iio_device_register(&client->dev, indio_dev);
 }
 
@@ -762,13 +773,15 @@ static int isl29018_suspend(struct device *dev)
 
 	mutex_lock(&chip->lock);
 
-	/* Since this driver uses only polling commands, we are by default in
+	/*
+	 * Since this driver uses only polling commands, we are by default in
 	 * auto shutdown (ie, power-down) mode.
 	 * So we do not have much to do here.
 	 */
 	chip->suspended = true;
 
 	mutex_unlock(&chip->lock);
+
 	return 0;
 }
 
@@ -784,6 +797,7 @@ static int isl29018_resume(struct device *dev)
 		chip->suspended = false;
 
 	mutex_unlock(&chip->lock);
+
 	return err;
 }
 
@@ -807,7 +821,6 @@ static const struct i2c_device_id isl29018_id[] = {
 	{"isl29035", isl29035},
 	{}
 };
-
 MODULE_DEVICE_TABLE(i2c, isl29018_id);
 
 static const struct of_device_id isl29018_of_match[] = {

+ 71 - 40
drivers/iio/light/ltr501.c

@@ -631,14 +631,16 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
 
 	switch (mask) {
 	case IIO_CHAN_INFO_PROCESSED:
-		if (iio_buffer_enabled(indio_dev))
-			return -EBUSY;
-
 		switch (chan->type) {
 		case IIO_LIGHT:
+			ret = iio_device_claim_direct_mode(indio_dev);
+			if (ret)
+				return ret;
+
 			mutex_lock(&data->lock_als);
 			ret = ltr501_read_als(data, buf);
 			mutex_unlock(&data->lock_als);
+			iio_device_release_direct_mode(indio_dev);
 			if (ret < 0)
 				return ret;
 			*val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
@@ -648,8 +650,9 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
 			return -EINVAL;
 		}
 	case IIO_CHAN_INFO_RAW:
-		if (iio_buffer_enabled(indio_dev))
-			return -EBUSY;
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
 
 		switch (chan->type) {
 		case IIO_INTENSITY:
@@ -657,21 +660,28 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
 			ret = ltr501_read_als(data, buf);
 			mutex_unlock(&data->lock_als);
 			if (ret < 0)
-				return ret;
+				break;
 			*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
 					   buf[0] : buf[1]);
-			return IIO_VAL_INT;
+			ret = IIO_VAL_INT;
+			break;
 		case IIO_PROXIMITY:
 			mutex_lock(&data->lock_ps);
 			ret = ltr501_read_ps(data);
 			mutex_unlock(&data->lock_ps);
 			if (ret < 0)
-				return ret;
+				break;
 			*val = ret & LTR501_PS_DATA_MASK;
-			return IIO_VAL_INT;
+			ret = IIO_VAL_INT;
+			break;
 		default:
-			return -EINVAL;
+			ret = -EINVAL;
+			break;
 		}
+
+		iio_device_release_direct_mode(indio_dev);
+		return ret;
+
 	case IIO_CHAN_INFO_SCALE:
 		switch (chan->type) {
 		case IIO_INTENSITY:
@@ -729,8 +739,9 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
 	int i, ret, freq_val, freq_val2;
 	struct ltr501_chip_info *info = data->chip_info;
 
-	if (iio_buffer_enabled(indio_dev))
-		return -EBUSY;
+	ret = iio_device_claim_direct_mode(indio_dev);
+	if (ret)
+		return ret;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_SCALE:
@@ -739,85 +750,105 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
 			i = ltr501_get_gain_index(info->als_gain,
 						  info->als_gain_tbl_size,
 						  val, val2);
-			if (i < 0)
-				return -EINVAL;
+			if (i < 0) {
+				ret = -EINVAL;
+				break;
+			}
 
 			data->als_contr &= ~info->als_gain_mask;
 			data->als_contr |= i << info->als_gain_shift;
 
-			return regmap_write(data->regmap, LTR501_ALS_CONTR,
-					    data->als_contr);
+			ret = regmap_write(data->regmap, LTR501_ALS_CONTR,
+					   data->als_contr);
+			break;
 		case IIO_PROXIMITY:
 			i = ltr501_get_gain_index(info->ps_gain,
 						  info->ps_gain_tbl_size,
 						  val, val2);
-			if (i < 0)
-				return -EINVAL;
+			if (i < 0) {
+				ret = -EINVAL;
+				break;
+			}
 			data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
 			data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
 
-			return regmap_write(data->regmap, LTR501_PS_CONTR,
-					    data->ps_contr);
+			ret = regmap_write(data->regmap, LTR501_PS_CONTR,
+					   data->ps_contr);
+			break;
 		default:
-			return -EINVAL;
+			ret = -EINVAL;
+			break;
 		}
+		break;
+
 	case IIO_CHAN_INFO_INT_TIME:
 		switch (chan->type) {
 		case IIO_INTENSITY:
-			if (val != 0)
-				return -EINVAL;
+			if (val != 0) {
+				ret = -EINVAL;
+				break;
+			}
 			mutex_lock(&data->lock_als);
-			i = ltr501_set_it_time(data, val2);
+			ret = ltr501_set_it_time(data, val2);
 			mutex_unlock(&data->lock_als);
-			return i;
+			break;
 		default:
-			return -EINVAL;
+			ret = -EINVAL;
+			break;
 		}
+		break;
+
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		switch (chan->type) {
 		case IIO_INTENSITY:
 			ret = ltr501_als_read_samp_freq(data, &freq_val,
 							&freq_val2);
 			if (ret < 0)
-				return ret;
+				break;
 
 			ret = ltr501_als_write_samp_freq(data, val, val2);
 			if (ret < 0)
-				return ret;
+				break;
 
 			/* update persistence count when changing frequency */
 			ret = ltr501_write_intr_prst(data, chan->type,
 						     0, data->als_period);
 
 			if (ret < 0)
-				return ltr501_als_write_samp_freq(data,
-								  freq_val,
-								  freq_val2);
-			return ret;
+				ret = ltr501_als_write_samp_freq(data, freq_val,
+								 freq_val2);
+			break;
 		case IIO_PROXIMITY:
 			ret = ltr501_ps_read_samp_freq(data, &freq_val,
 						       &freq_val2);
 			if (ret < 0)
-				return ret;
+				break;
 
 			ret = ltr501_ps_write_samp_freq(data, val, val2);
 			if (ret < 0)
-				return ret;
+				break;
 
 			/* update persistence count when changing frequency */
 			ret = ltr501_write_intr_prst(data, chan->type,
 						     0, data->ps_period);
 
 			if (ret < 0)
-				return ltr501_ps_write_samp_freq(data,
-								 freq_val,
-								 freq_val2);
-			return ret;
+				ret = ltr501_ps_write_samp_freq(data, freq_val,
+								freq_val2);
+			break;
 		default:
-			return -EINVAL;
+			ret = -EINVAL;
+			break;
 		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
 	}
-	return -EINVAL;
+
+	iio_device_release_direct_mode(indio_dev);
+	return ret;
 }
 
 static int ltr501_read_thresh(struct iio_dev *indio_dev,

+ 3 - 2
drivers/iio/light/max44000.c

@@ -204,17 +204,18 @@ static int max44000_write_alspga(struct max44000_data *data, int val)
 static int max44000_read_alsval(struct max44000_data *data)
 {
 	u16 regval;
+	__be16 val;
 	int alstim, ret;
 
 	ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
-			       &regval, sizeof(regval));
+			       &val, sizeof(val));
 	if (ret < 0)
 		return ret;
 	alstim = ret = max44000_read_alstim(data);
 	if (ret < 0)
 		return ret;
 
-	regval = be16_to_cpu(regval);
+	regval = be16_to_cpu(val);
 
 	/*
 	 * Overflow is explained on datasheet page 17.

+ 913 - 0
drivers/iio/light/tsl2583.c

@@ -0,0 +1,913 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * within the TAOS tsl258x family of devices (tsl2580, tsl2581, tsl2583).
+ *
+ * Copyright (c) 2011, TAOS Corporation.
+ * Copyright (c) 2016 Brian Masney <masneyb@onstation.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Device Registers and Masks */
+#define TSL2583_CNTRL			0x00
+#define TSL2583_ALS_TIME		0X01
+#define TSL2583_INTERRUPT		0x02
+#define TSL2583_GAIN			0x07
+#define TSL2583_REVID			0x11
+#define TSL2583_CHIPID			0x12
+#define TSL2583_ALS_CHAN0LO		0x14
+#define TSL2583_ALS_CHAN0HI		0x15
+#define TSL2583_ALS_CHAN1LO		0x16
+#define TSL2583_ALS_CHAN1HI		0x17
+#define TSL2583_TMR_LO			0x18
+#define TSL2583_TMR_HI			0x19
+
+/* tsl2583 cmd reg masks */
+#define TSL2583_CMD_REG			0x80
+#define TSL2583_CMD_SPL_FN		0x60
+#define TSL2583_CMD_ALS_INT_CLR		0x01
+
+/* tsl2583 cntrl reg masks */
+#define TSL2583_CNTL_ADC_ENBL		0x02
+#define TSL2583_CNTL_PWR_OFF		0x00
+#define TSL2583_CNTL_PWR_ON		0x01
+
+/* tsl2583 status reg masks */
+#define TSL2583_STA_ADC_VALID		0x01
+#define TSL2583_STA_ADC_INTR		0x10
+
+/* Lux calculation constants */
+#define TSL2583_LUX_CALC_OVER_FLOW	65535
+
+#define TSL2583_INTERRUPT_DISABLED	0x00
+
+#define TSL2583_CHIP_ID			0x90
+#define TSL2583_CHIP_ID_MASK		0xf0
+
+/* Per-device data */
+struct tsl2583_als_info {
+	u16 als_ch0;
+	u16 als_ch1;
+	u16 lux;
+};
+
+struct tsl2583_lux {
+	unsigned int ratio;
+	unsigned int ch0;
+	unsigned int ch1;
+};
+
+static const struct tsl2583_lux tsl2583_default_lux[] = {
+	{  9830,  8520, 15729 },
+	{ 12452, 10807, 23344 },
+	{ 14746,  6383, 11705 },
+	{ 17695,  4063,  6554 },
+	{     0,     0,     0 }  /* Termination segment */
+};
+
+#define TSL2583_MAX_LUX_TABLE_ENTRIES 11
+
+struct tsl2583_settings {
+	int als_time;
+	int als_gain;
+	int als_gain_trim;
+	int als_cal_target;
+
+	/*
+	 * This structure is intentionally large to accommodate updates via
+	 * sysfs. Sized to 11 = max 10 segments + 1 termination segment.
+	 * Assumption is that one and only one type of glass used.
+	 */
+	struct tsl2583_lux als_device_lux[TSL2583_MAX_LUX_TABLE_ENTRIES];
+};
+
+struct tsl2583_chip {
+	struct mutex als_mutex;
+	struct i2c_client *client;
+	struct tsl2583_als_info als_cur_info;
+	struct tsl2583_settings als_settings;
+	int als_time_scale;
+	int als_saturation;
+	bool suspended;
+};
+
+struct gainadj {
+	s16 ch0;
+	s16 ch1;
+	s16 mean;
+};
+
+/* Index = (0 - 3) Used to validate the gain selection index */
+static const struct gainadj gainadj[] = {
+	{ 1, 1, 1 },
+	{ 8, 8, 8 },
+	{ 16, 16, 16 },
+	{ 107, 115, 111 }
+};
+
+/*
+ * Provides initial operational parameter defaults.
+ * These defaults may be changed through the device's sysfs files.
+ */
+static void tsl2583_defaults(struct tsl2583_chip *chip)
+{
+	/*
+	 * The integration time must be a multiple of 50ms and within the
+	 * range [50, 600] ms.
+	 */
+	chip->als_settings.als_time = 100;
+
+	/*
+	 * This is an index into the gainadj table. Assume clear glass as the
+	 * default.
+	 */
+	chip->als_settings.als_gain = 0;
+
+	/* Default gain trim to account for aperture effects */
+	chip->als_settings.als_gain_trim = 1000;
+
+	/* Known external ALS reading used for calibration */
+	chip->als_settings.als_cal_target = 130;
+
+	/* Default lux table. */
+	memcpy(chip->als_settings.als_device_lux, tsl2583_default_lux,
+	       sizeof(tsl2583_default_lux));
+}
+
+/*
+ * Reads and calculates current lux value.
+ * The raw ch0 and ch1 values of the ambient light sensed in the last
+ * integration cycle are read from the device.
+ * Time scale factor array values are adjusted based on the integration time.
+ * The raw values are multiplied by a scale factor, and device gain is obtained
+ * using gain index. Limit checks are done next, then the ratio of a multiple
+ * of ch1 value, to the ch0 value, is calculated. The array als_device_lux[]
+ * declared above is then scanned to find the first ratio value that is just
+ * above the ratio we just calculated. The ch0 and ch1 multiplier constants in
+ * the array are then used along with the time scale factor array values, to
+ * calculate the lux.
+ */
+static int tsl2583_get_lux(struct iio_dev *indio_dev)
+{
+	u16 ch0, ch1; /* separated ch0/ch1 data from device */
+	u32 lux; /* raw lux calculated from device data */
+	u64 lux64;
+	u32 ratio;
+	u8 buf[5];
+	struct tsl2583_lux *p;
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int i, ret;
+
+	ret = i2c_smbus_read_byte_data(chip->client, TSL2583_CMD_REG);
+	if (ret < 0) {
+		dev_err(&chip->client->dev, "%s: failed to read CMD_REG register\n",
+			__func__);
+		goto done;
+	}
+
+	/* is data new & valid */
+	if (!(ret & TSL2583_STA_ADC_INTR)) {
+		dev_err(&chip->client->dev, "%s: data not valid; returning last value\n",
+			__func__);
+		ret = chip->als_cur_info.lux; /* return LAST VALUE */
+		goto done;
+	}
+
+	for (i = 0; i < 4; i++) {
+		int reg = TSL2583_CMD_REG | (TSL2583_ALS_CHAN0LO + i);
+
+		ret = i2c_smbus_read_byte_data(chip->client, reg);
+		if (ret < 0) {
+			dev_err(&chip->client->dev, "%s: failed to read register %x\n",
+				__func__, reg);
+			goto done;
+		}
+		buf[i] = ret;
+	}
+
+	/*
+	 * Clear the pending interrupt status bit on the chip to allow the next
+	 * integration cycle to start. This has to be done even though this
+	 * driver currently does not support interrupts.
+	 */
+	ret = i2c_smbus_write_byte(chip->client,
+				   (TSL2583_CMD_REG | TSL2583_CMD_SPL_FN |
+				    TSL2583_CMD_ALS_INT_CLR));
+	if (ret < 0) {
+		dev_err(&chip->client->dev, "%s: failed to clear the interrupt bit\n",
+			__func__);
+		goto done; /* have no data, so return failure */
+	}
+
+	/* extract ALS/lux data */
+	ch0 = le16_to_cpup((const __le16 *)&buf[0]);
+	ch1 = le16_to_cpup((const __le16 *)&buf[2]);
+
+	chip->als_cur_info.als_ch0 = ch0;
+	chip->als_cur_info.als_ch1 = ch1;
+
+	if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation))
+		goto return_max;
+
+	if (!ch0) {
+		/*
+		 * The sensor appears to be in total darkness so set the
+		 * calculated lux to 0 and return early to avoid a division by
+		 * zero below when calculating the ratio.
+		 */
+		ret = 0;
+		chip->als_cur_info.lux = 0;
+		goto done;
+	}
+
+	/* calculate ratio */
+	ratio = (ch1 << 15) / ch0;
+
+	/* convert to unscaled lux using the pointer to the table */
+	for (p = (struct tsl2583_lux *)chip->als_settings.als_device_lux;
+	     p->ratio != 0 && p->ratio < ratio; p++)
+		;
+
+	if (p->ratio == 0) {
+		lux = 0;
+	} else {
+		u32 ch0lux, ch1lux;
+
+		ch0lux = ((ch0 * p->ch0) +
+			  (gainadj[chip->als_settings.als_gain].ch0 >> 1))
+			 / gainadj[chip->als_settings.als_gain].ch0;
+		ch1lux = ((ch1 * p->ch1) +
+			  (gainadj[chip->als_settings.als_gain].ch1 >> 1))
+			 / gainadj[chip->als_settings.als_gain].ch1;
+
+		/* note: lux is 31 bit max at this point */
+		if (ch1lux > ch0lux) {
+			dev_dbg(&chip->client->dev, "%s: No Data - Returning 0\n",
+				__func__);
+			ret = 0;
+			chip->als_cur_info.lux = 0;
+			goto done;
+		}
+
+		lux = ch0lux - ch1lux;
+	}
+
+	/* adjust for active time scale */
+	if (chip->als_time_scale == 0)
+		lux = 0;
+	else
+		lux = (lux + (chip->als_time_scale >> 1)) /
+			chip->als_time_scale;
+
+	/*
+	 * Adjust for active gain scale.
+	 * The tsl2583_default_lux tables above have a factor of 8192 built in,
+	 * so we need to shift right.
+	 * User-specified gain provides a multiplier.
+	 * Apply user-specified gain before shifting right to retain precision.
+	 * Use 64 bits to avoid overflow on multiplication.
+	 * Then go back to 32 bits before division to avoid using div_u64().
+	 */
+	lux64 = lux;
+	lux64 = lux64 * chip->als_settings.als_gain_trim;
+	lux64 >>= 13;
+	lux = lux64;
+	lux = (lux + 500) / 1000;
+
+	if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */
+return_max:
+		lux = TSL2583_LUX_CALC_OVER_FLOW;
+	}
+
+	/* Update the structure with the latest VALID lux. */
+	chip->als_cur_info.lux = lux;
+	ret = lux;
+
+done:
+	return ret;
+}
+
+/*
+ * Obtain single reading and calculate the als_gain_trim (later used
+ * to derive actual lux).
+ * Return updated gain_trim value.
+ */
+static int tsl2583_als_calibrate(struct iio_dev *indio_dev)
+{
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	unsigned int gain_trim_val;
+	int ret;
+	int lux_val;
+
+	ret = i2c_smbus_read_byte_data(chip->client,
+				       TSL2583_CMD_REG | TSL2583_CNTRL);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed to read from the CNTRL register\n",
+			__func__);
+		return ret;
+	}
+
+	if ((ret & (TSL2583_CNTL_ADC_ENBL | TSL2583_CNTL_PWR_ON))
+			!= (TSL2583_CNTL_ADC_ENBL | TSL2583_CNTL_PWR_ON)) {
+		dev_err(&chip->client->dev,
+			"%s: Device is not powered on and/or ADC is not enabled\n",
+			__func__);
+		return -EINVAL;
+	} else if ((ret & TSL2583_STA_ADC_VALID) != TSL2583_STA_ADC_VALID) {
+		dev_err(&chip->client->dev,
+			"%s: The two ADC channels have not completed an integration cycle\n",
+			__func__);
+		return -ENODATA;
+	}
+
+	lux_val = tsl2583_get_lux(indio_dev);
+	if (lux_val < 0) {
+		dev_err(&chip->client->dev, "%s: failed to get lux\n",
+			__func__);
+		return lux_val;
+	}
+
+	gain_trim_val = (unsigned int)(((chip->als_settings.als_cal_target)
+			* chip->als_settings.als_gain_trim) / lux_val);
+	if ((gain_trim_val < 250) || (gain_trim_val > 4000)) {
+		dev_err(&chip->client->dev,
+			"%s: trim_val of %d is not within the range [250, 4000]\n",
+			__func__, gain_trim_val);
+		return -ENODATA;
+	}
+
+	chip->als_settings.als_gain_trim = (int)gain_trim_val;
+
+	return 0;
+}
+
+static int tsl2583_set_als_time(struct tsl2583_chip *chip)
+{
+	int als_count, als_time, ret;
+	u8 val;
+
+	/* determine als integration register */
+	als_count = (chip->als_settings.als_time * 100 + 135) / 270;
+	if (!als_count)
+		als_count = 1; /* ensure at least one cycle */
+
+	/* convert back to time (encompasses overrides) */
+	als_time = (als_count * 27 + 5) / 10;
+
+	val = 256 - als_count;
+	ret = i2c_smbus_write_byte_data(chip->client,
+					TSL2583_CMD_REG | TSL2583_ALS_TIME,
+					val);
+	if (ret < 0) {
+		dev_err(&chip->client->dev, "%s: failed to set the als time to %d\n",
+			__func__, val);
+		return ret;
+	}
+
+	/* set chip struct re scaling and saturation */
+	chip->als_saturation = als_count * 922; /* 90% of full scale */
+	chip->als_time_scale = (als_time + 25) / 50;
+
+	return ret;
+}
+
+static int tsl2583_set_als_gain(struct tsl2583_chip *chip)
+{
+	int ret;
+
+	/* Set the gain based on als_settings struct */
+	ret = i2c_smbus_write_byte_data(chip->client,
+					TSL2583_CMD_REG | TSL2583_GAIN,
+					chip->als_settings.als_gain);
+	if (ret < 0)
+		dev_err(&chip->client->dev,
+			"%s: failed to set the gain to %d\n", __func__,
+			chip->als_settings.als_gain);
+
+	return ret;
+}
+
+static int tsl2583_set_power_state(struct tsl2583_chip *chip, u8 state)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(chip->client,
+					TSL2583_CMD_REG | TSL2583_CNTRL, state);
+	if (ret < 0)
+		dev_err(&chip->client->dev,
+			"%s: failed to set the power state to %d\n", __func__,
+			state);
+
+	return ret;
+}
+
+/*
+ * Turn the device on.
+ * Configuration must be set before calling this function.
+ */
+static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev)
+{
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int ret;
+
+	/* Power on the device; ADC off. */
+	ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(chip->client,
+					TSL2583_CMD_REG | TSL2583_INTERRUPT,
+					TSL2583_INTERRUPT_DISABLED);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed to disable interrupts\n", __func__);
+		return ret;
+	}
+
+	ret = tsl2583_set_als_time(chip);
+	if (ret < 0)
+		return ret;
+
+	ret = tsl2583_set_als_gain(chip);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(3000, 3500);
+
+	ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON |
+					    TSL2583_CNTL_ADC_ENBL);
+	if (ret < 0)
+		return ret;
+
+	chip->suspended = false;
+
+	return ret;
+}
+
+/* Sysfs Interface Functions */
+
+static ssize_t in_illuminance_input_target_show(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&chip->als_mutex);
+	ret = sprintf(buf, "%d\n", chip->als_settings.als_cal_target);
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+static ssize_t in_illuminance_input_target_store(struct device *dev,
+						 struct device_attribute *attr,
+						 const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int value;
+
+	if (kstrtoint(buf, 0, &value) || !value)
+		return -EINVAL;
+
+	mutex_lock(&chip->als_mutex);
+	chip->als_settings.als_cal_target = value;
+	mutex_unlock(&chip->als_mutex);
+
+	return len;
+}
+
+static ssize_t in_illuminance_calibrate_store(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int value, ret;
+
+	if (kstrtoint(buf, 0, &value) || value != 1)
+		return -EINVAL;
+
+	mutex_lock(&chip->als_mutex);
+
+	if (chip->suspended) {
+		ret = -EBUSY;
+		goto done;
+	}
+
+	ret = tsl2583_als_calibrate(indio_dev);
+	if (ret < 0)
+		goto done;
+
+	ret = len;
+done:
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+static ssize_t in_illuminance_lux_table_show(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	unsigned int i;
+	int offset = 0;
+
+	for (i = 0; i < ARRAY_SIZE(chip->als_settings.als_device_lux); i++) {
+		offset += sprintf(buf + offset, "%u,%u,%u,",
+				  chip->als_settings.als_device_lux[i].ratio,
+				  chip->als_settings.als_device_lux[i].ch0,
+				  chip->als_settings.als_device_lux[i].ch1);
+		if (chip->als_settings.als_device_lux[i].ratio == 0) {
+			/*
+			 * We just printed the first "0" entry.
+			 * Now get rid of the extra "," and break.
+			 */
+			offset--;
+			break;
+		}
+	}
+
+	offset += sprintf(buf + offset, "\n");
+
+	return offset;
+}
+
+static ssize_t in_illuminance_lux_table_store(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	const unsigned int max_ints = TSL2583_MAX_LUX_TABLE_ENTRIES * 3;
+	int value[TSL2583_MAX_LUX_TABLE_ENTRIES * 3 + 1];
+	int ret = -EINVAL;
+	unsigned int n;
+
+	mutex_lock(&chip->als_mutex);
+
+	get_options(buf, ARRAY_SIZE(value), value);
+
+	/*
+	 * We now have an array of ints starting at value[1], and
+	 * enumerated by value[0].
+	 * We expect each group of three ints is one table entry,
+	 * and the last table entry is all 0.
+	 */
+	n = value[0];
+	if ((n % 3) || n < 6 || n > max_ints) {
+		dev_err(dev,
+			"%s: The number of entries in the lux table must be a multiple of 3 and within the range [6, %d]\n",
+			__func__, max_ints);
+		goto done;
+	}
+	if ((value[n - 2] | value[n - 1] | value[n]) != 0) {
+		dev_err(dev, "%s: The last 3 entries in the lux table must be zeros.\n",
+			__func__);
+		goto done;
+	}
+
+	memcpy(chip->als_settings.als_device_lux, &value[1],
+	       value[0] * sizeof(value[1]));
+
+	ret = len;
+
+done:
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+static IIO_CONST_ATTR(in_illuminance_calibscale_available, "1 8 16 111");
+static IIO_CONST_ATTR(in_illuminance_integration_time_available,
+		      "0.000050 0.000100 0.000150 0.000200 0.000250 0.000300 0.000350 0.000400 0.000450 0.000500 0.000550 0.000600 0.000650");
+static IIO_DEVICE_ATTR_RW(in_illuminance_input_target, 0);
+static IIO_DEVICE_ATTR_WO(in_illuminance_calibrate, 0);
+static IIO_DEVICE_ATTR_RW(in_illuminance_lux_table, 0);
+
+static struct attribute *sysfs_attrs_ctrl[] = {
+	&iio_const_attr_in_illuminance_calibscale_available.dev_attr.attr,
+	&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
+	&iio_dev_attr_in_illuminance_input_target.dev_attr.attr,
+	&iio_dev_attr_in_illuminance_calibrate.dev_attr.attr,
+	&iio_dev_attr_in_illuminance_lux_table.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group tsl2583_attribute_group = {
+	.attrs = sysfs_attrs_ctrl,
+};
+
+static const struct iio_chan_spec tsl2583_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_IR,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	},
+	{
+		.type = IIO_LIGHT,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_BOTH,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	},
+	{
+		.type = IIO_LIGHT,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+				      BIT(IIO_CHAN_INFO_CALIBBIAS) |
+				      BIT(IIO_CHAN_INFO_CALIBSCALE) |
+				      BIT(IIO_CHAN_INFO_INT_TIME),
+	},
+};
+
+static int tsl2583_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int ret = -EINVAL;
+
+	mutex_lock(&chip->als_mutex);
+
+	if (chip->suspended) {
+		ret = -EBUSY;
+		goto read_done;
+	}
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (chan->type == IIO_LIGHT) {
+			ret = tsl2583_get_lux(indio_dev);
+			if (ret < 0)
+				goto read_done;
+
+			/*
+			 * From page 20 of the TSL2581, TSL2583 data
+			 * sheet (TAOS134 − MARCH 2011):
+			 *
+			 * One of the photodiodes (channel 0) is
+			 * sensitive to both visible and infrared light,
+			 * while the second photodiode (channel 1) is
+			 * sensitive primarily to infrared light.
+			 */
+			if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
+				*val = chip->als_cur_info.als_ch0;
+			else
+				*val = chip->als_cur_info.als_ch1;
+
+			ret = IIO_VAL_INT;
+		}
+		break;
+	case IIO_CHAN_INFO_PROCESSED:
+		if (chan->type == IIO_LIGHT) {
+			ret = tsl2583_get_lux(indio_dev);
+			if (ret < 0)
+				goto read_done;
+
+			*val = ret;
+			ret = IIO_VAL_INT;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (chan->type == IIO_LIGHT) {
+			*val = chip->als_settings.als_gain_trim;
+			ret = IIO_VAL_INT;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (chan->type == IIO_LIGHT) {
+			*val = gainadj[chip->als_settings.als_gain].mean;
+			ret = IIO_VAL_INT;
+		}
+		break;
+	case IIO_CHAN_INFO_INT_TIME:
+		if (chan->type == IIO_LIGHT) {
+			*val = 0;
+			*val2 = chip->als_settings.als_time;
+			ret = IIO_VAL_INT_PLUS_MICRO;
+		}
+		break;
+	default:
+		break;
+	}
+
+read_done:
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+static int tsl2583_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int ret = -EINVAL;
+
+	mutex_lock(&chip->als_mutex);
+
+	if (chip->suspended) {
+		ret = -EBUSY;
+		goto write_done;
+	}
+
+	switch (mask) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (chan->type == IIO_LIGHT) {
+			chip->als_settings.als_gain_trim = val;
+			ret = 0;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (chan->type == IIO_LIGHT) {
+			unsigned int i;
+
+			for (i = 0; i < ARRAY_SIZE(gainadj); i++) {
+				if (gainadj[i].mean == val) {
+					chip->als_settings.als_gain = i;
+					ret = tsl2583_set_als_gain(chip);
+					break;
+				}
+			}
+		}
+		break;
+	case IIO_CHAN_INFO_INT_TIME:
+		if (chan->type == IIO_LIGHT && !val && val2 >= 50 &&
+		    val2 <= 650 && !(val2 % 50)) {
+			chip->als_settings.als_time = val2;
+			ret = tsl2583_set_als_time(chip);
+		}
+		break;
+	default:
+		break;
+	}
+
+write_done:
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+static const struct iio_info tsl2583_info = {
+	.attrs = &tsl2583_attribute_group,
+	.driver_module = THIS_MODULE,
+	.read_raw = tsl2583_read_raw,
+	.write_raw = tsl2583_write_raw,
+};
+
+static int tsl2583_probe(struct i2c_client *clientp,
+			 const struct i2c_device_id *idp)
+{
+	int ret;
+	struct tsl2583_chip *chip;
+	struct iio_dev *indio_dev;
+
+	if (!i2c_check_functionality(clientp->adapter,
+				     I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&clientp->dev, "%s: i2c smbus byte data functionality is unsupported\n",
+			__func__);
+		return -EOPNOTSUPP;
+	}
+
+	indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	chip = iio_priv(indio_dev);
+	chip->client = clientp;
+	i2c_set_clientdata(clientp, indio_dev);
+
+	mutex_init(&chip->als_mutex);
+	chip->suspended = true;
+
+	ret = i2c_smbus_read_byte_data(clientp,
+				       TSL2583_CMD_REG | TSL2583_CHIPID);
+	if (ret < 0) {
+		dev_err(&clientp->dev,
+			"%s: failed to read the chip ID register\n", __func__);
+		return ret;
+	}
+
+	if ((ret & TSL2583_CHIP_ID_MASK) != TSL2583_CHIP_ID) {
+		dev_err(&clientp->dev, "%s: received an unknown chip ID %x\n",
+			__func__, ret);
+		return -EINVAL;
+	}
+
+	indio_dev->info = &tsl2583_info;
+	indio_dev->channels = tsl2583_channels;
+	indio_dev->num_channels = ARRAY_SIZE(tsl2583_channels);
+	indio_dev->dev.parent = &clientp->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->name = chip->client->name;
+
+	ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
+	if (ret) {
+		dev_err(&clientp->dev, "%s: iio registration failed\n",
+			__func__);
+		return ret;
+	}
+
+	/* Load up the V2 defaults (these are hard coded defaults for now) */
+	tsl2583_defaults(chip);
+
+	/* Make sure the chip is on */
+	ret = tsl2583_chip_init_and_power_on(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	dev_info(&clientp->dev, "Light sensor found.\n");
+
+	return 0;
+}
+
+static int __maybe_unused tsl2583_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&chip->als_mutex);
+
+	ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_OFF);
+	chip->suspended = true;
+
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+static int __maybe_unused tsl2583_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct tsl2583_chip *chip = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&chip->als_mutex);
+
+	ret = tsl2583_chip_init_and_power_on(indio_dev);
+
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(tsl2583_pm_ops, tsl2583_suspend, tsl2583_resume);
+
+static struct i2c_device_id tsl2583_idtable[] = {
+	{ "tsl2580", 0 },
+	{ "tsl2581", 1 },
+	{ "tsl2583", 2 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tsl2583_idtable);
+
+static const struct of_device_id tsl2583_of_match[] = {
+	{ .compatible = "amstaos,tsl2580", },
+	{ .compatible = "amstaos,tsl2581", },
+	{ .compatible = "amstaos,tsl2583", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, tsl2583_of_match);
+
+/* Driver definition */
+static struct i2c_driver tsl2583_driver = {
+	.driver = {
+		.name = "tsl2583",
+		.pm = &tsl2583_pm_ops,
+		.of_match_table = tsl2583_of_match,
+	},
+	.id_table = tsl2583_idtable,
+	.probe = tsl2583_probe,
+};
+module_i2c_driver(tsl2583_driver);
+
+MODULE_AUTHOR("J. August Brenner <jbrenner@taosinc.com>");
+MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
+MODULE_DESCRIPTION("TAOS tsl2583 ambient light sensor driver");
+MODULE_LICENSE("GPL");

+ 4 - 4
drivers/iio/magnetometer/ak8974.c

@@ -287,7 +287,7 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
 	return 0;
 }
 
-static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
+static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
 {
 	unsigned int src;
 	int ret;
@@ -395,7 +395,7 @@ static int ak8974_selftest(struct ak8974 *ak8974)
 static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
 {
 	int ret;
-	u16 bulk;
+	__le16 bulk;
 
 	ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
 	if (ret)
@@ -453,7 +453,7 @@ static int ak8974_read_raw(struct iio_dev *indio_dev,
 			   long mask)
 {
 	struct ak8974 *ak8974 = iio_priv(indio_dev);
-	s16 hw_values[3];
+	__le16 hw_values[3];
 	int ret = -EINVAL;
 
 	pm_runtime_get_sync(&ak8974->i2c->dev);
@@ -494,7 +494,7 @@ static void ak8974_fill_buffer(struct iio_dev *indio_dev)
 {
 	struct ak8974 *ak8974 = iio_priv(indio_dev);
 	int ret;
-	s16 hw_values[8]; /* Three axes + 64bit padding */
+	__le16 hw_values[8]; /* Three axes + 64bit padding */
 
 	pm_runtime_get_sync(&ak8974->i2c->dev);
 	mutex_lock(&ak8974->lock);

+ 9 - 7
drivers/iio/magnetometer/ak8975.c

@@ -690,6 +690,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
 	struct ak8975_data *data = iio_priv(indio_dev);
 	const struct i2c_client *client = data->client;
 	const struct ak_def *def = data->def;
+	__le16 rval;
 	u16 buff;
 	int ret;
 
@@ -703,7 +704,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
 
 	ret = i2c_smbus_read_i2c_block_data_or_emulated(
 			client, def->data_regs[index],
-			sizeof(buff), (u8*)&buff);
+			sizeof(rval), (u8*)&rval);
 	if (ret < 0)
 		goto exit;
 
@@ -713,7 +714,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
 	pm_runtime_put_autosuspend(&data->client->dev);
 
 	/* Swap bytes and convert to valid range. */
-	buff = le16_to_cpu(buff);
+	buff = le16_to_cpu(rval);
 	*val = clamp_t(s16, buff, -def->range, def->range);
 	return IIO_VAL_INT;
 
@@ -813,6 +814,7 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
 	const struct ak_def *def = data->def;
 	int ret;
 	s16 buff[8]; /* 3 x 16 bits axis values + 1 aligned 64 bits timestamp */
+	__le16 fval[3];
 
 	mutex_lock(&data->lock);
 
@@ -826,17 +828,17 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
 	 */
 	ret = i2c_smbus_read_i2c_block_data_or_emulated(client,
 							def->data_regs[0],
-							3 * sizeof(buff[0]),
-							(u8 *)buff);
+							3 * sizeof(fval[0]),
+							(u8 *)fval);
 	if (ret < 0)
 		goto unlock;
 
 	mutex_unlock(&data->lock);
 
 	/* Clamp to valid range. */
-	buff[0] = clamp_t(s16, le16_to_cpu(buff[0]), -def->range, def->range);
-	buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range);
-	buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range);
+	buff[0] = clamp_t(s16, le16_to_cpu(fval[0]), -def->range, def->range);
+	buff[1] = clamp_t(s16, le16_to_cpu(fval[1]), -def->range, def->range);
+	buff[2] = clamp_t(s16, le16_to_cpu(fval[2]), -def->range, def->range);
 
 	iio_push_to_buffers_with_timestamp(indio_dev, buff,
 					   iio_get_time_ns(indio_dev));

+ 112 - 35
drivers/iio/magnetometer/hid-sensor-magn-3d.c

@@ -42,9 +42,17 @@ enum magn_3d_channel {
 	MAGN_3D_CHANNEL_MAX,
 };
 
+struct common_attributes {
+	int scale_pre_decml;
+	int scale_post_decml;
+	int scale_precision;
+	int value_offset;
+};
+
 struct magn_3d_state {
 	struct hid_sensor_hub_callbacks callbacks;
-	struct hid_sensor_common common_attributes;
+	struct hid_sensor_common magn_flux_attributes;
+	struct hid_sensor_common rot_attributes;
 	struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
 
 	/* dynamically sized array to hold sensor values */
@@ -52,10 +60,8 @@ struct magn_3d_state {
 	/* array of pointers to sensor value */
 	u32 *magn_val_addr[MAGN_3D_CHANNEL_MAX];
 
-	int scale_pre_decml;
-	int scale_post_decml;
-	int scale_precision;
-	int value_offset;
+	struct common_attributes magn_flux_attr;
+	struct common_attributes rot_attr;
 };
 
 static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
@@ -162,41 +168,74 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
 	*val2 = 0;
 	switch (mask) {
 	case 0:
-		hid_sensor_power_state(&magn_state->common_attributes, true);
+		hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
 		report_id =
 			magn_state->magn[chan->address].report_id;
 		address = magn_3d_addresses[chan->address];
 		if (report_id >= 0)
 			*val = sensor_hub_input_attr_get_raw_value(
-				magn_state->common_attributes.hsdev,
+				magn_state->magn_flux_attributes.hsdev,
 				HID_USAGE_SENSOR_COMPASS_3D, address,
 				report_id,
 				SENSOR_HUB_SYNC);
 		else {
 			*val = 0;
-			hid_sensor_power_state(&magn_state->common_attributes,
-						false);
+			hid_sensor_power_state(
+				&magn_state->magn_flux_attributes,
+				false);
 			return -EINVAL;
 		}
-		hid_sensor_power_state(&magn_state->common_attributes, false);
+		hid_sensor_power_state(&magn_state->magn_flux_attributes,
+					false);
 		ret_type = IIO_VAL_INT;
 		break;
 	case IIO_CHAN_INFO_SCALE:
-		*val = magn_state->scale_pre_decml;
-		*val2 = magn_state->scale_post_decml;
-		ret_type = magn_state->scale_precision;
+		switch (chan->type) {
+		case IIO_MAGN:
+			*val = magn_state->magn_flux_attr.scale_pre_decml;
+			*val2 = magn_state->magn_flux_attr.scale_post_decml;
+			ret_type = magn_state->magn_flux_attr.scale_precision;
+			break;
+		case IIO_ROT:
+			*val = magn_state->rot_attr.scale_pre_decml;
+			*val2 = magn_state->rot_attr.scale_post_decml;
+			ret_type = magn_state->rot_attr.scale_precision;
+			break;
+		default:
+			ret_type = -EINVAL;
+		}
 		break;
 	case IIO_CHAN_INFO_OFFSET:
-		*val = magn_state->value_offset;
-		ret_type = IIO_VAL_INT;
+		switch (chan->type) {
+		case IIO_MAGN:
+			*val = magn_state->magn_flux_attr.value_offset;
+			ret_type = IIO_VAL_INT;
+			break;
+		case IIO_ROT:
+			*val = magn_state->rot_attr.value_offset;
+			ret_type = IIO_VAL_INT;
+			break;
+		default:
+			ret_type = -EINVAL;
+		}
 		break;
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		ret_type = hid_sensor_read_samp_freq_value(
-			&magn_state->common_attributes, val, val2);
+			&magn_state->magn_flux_attributes, val, val2);
 		break;
 	case IIO_CHAN_INFO_HYSTERESIS:
-		ret_type = hid_sensor_read_raw_hyst_value(
-			&magn_state->common_attributes, val, val2);
+		switch (chan->type) {
+		case IIO_MAGN:
+			ret_type = hid_sensor_read_raw_hyst_value(
+				&magn_state->magn_flux_attributes, val, val2);
+			break;
+		case IIO_ROT:
+			ret_type = hid_sensor_read_raw_hyst_value(
+				&magn_state->rot_attributes, val, val2);
+			break;
+		default:
+			ret_type = -EINVAL;
+		}
 		break;
 	default:
 		ret_type = -EINVAL;
@@ -219,11 +258,21 @@ static int magn_3d_write_raw(struct iio_dev *indio_dev,
 	switch (mask) {
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		ret = hid_sensor_write_samp_freq_value(
-				&magn_state->common_attributes, val, val2);
+				&magn_state->magn_flux_attributes, val, val2);
 		break;
 	case IIO_CHAN_INFO_HYSTERESIS:
-		ret = hid_sensor_write_raw_hyst_value(
-				&magn_state->common_attributes, val, val2);
+		switch (chan->type) {
+		case IIO_MAGN:
+			ret = hid_sensor_write_raw_hyst_value(
+				&magn_state->magn_flux_attributes, val, val2);
+			break;
+		case IIO_ROT:
+			ret = hid_sensor_write_raw_hyst_value(
+				&magn_state->rot_attributes, val, val2);
+			break;
+		default:
+			ret = -EINVAL;
+		}
 		break;
 	default:
 		ret = -EINVAL;
@@ -254,7 +303,7 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
 	struct magn_3d_state *magn_state = iio_priv(indio_dev);
 
 	dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
-	if (atomic_read(&magn_state->common_attributes.data_ready))
+	if (atomic_read(&magn_state->magn_flux_attributes.data_ready))
 		hid_sensor_push_data(indio_dev, magn_state->iio_vals);
 
 	return 0;
@@ -389,21 +438,48 @@ static int magn_3d_parse_report(struct platform_device *pdev,
 	dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",
 			*chan_count);
 
-	st->scale_precision = hid_sensor_format_scale(
+	st->magn_flux_attr.scale_precision = hid_sensor_format_scale(
 				HID_USAGE_SENSOR_COMPASS_3D,
 				&st->magn[CHANNEL_SCAN_INDEX_X],
-				&st->scale_pre_decml, &st->scale_post_decml);
+				&st->magn_flux_attr.scale_pre_decml,
+				&st->magn_flux_attr.scale_post_decml);
+	st->rot_attr.scale_precision
+		= hid_sensor_format_scale(
+			HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
+			&st->magn[CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP],
+			&st->rot_attr.scale_pre_decml,
+			&st->rot_attr.scale_post_decml);
 
 	/* Set Sensitivity field ids, when there is no individual modifier */
-	if (st->common_attributes.sensitivity.index < 0) {
+	if (st->magn_flux_attributes.sensitivity.index < 0) {
 		sensor_hub_input_get_attribute_info(hsdev,
 			HID_FEATURE_REPORT, usage_id,
 			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 			HID_USAGE_SENSOR_DATA_ORIENTATION,
-			&st->common_attributes.sensitivity);
+			&st->magn_flux_attributes.sensitivity);
+		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
+			st->magn_flux_attributes.sensitivity.index,
+			st->magn_flux_attributes.sensitivity.report_id);
+	}
+	if (st->magn_flux_attributes.sensitivity.index < 0) {
+		sensor_hub_input_get_attribute_info(hsdev,
+			HID_FEATURE_REPORT, usage_id,
+			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
+			HID_USAGE_SENSOR_ORIENT_MAGN_FLUX,
+			&st->magn_flux_attributes.sensitivity);
+		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
+			st->magn_flux_attributes.sensitivity.index,
+			st->magn_flux_attributes.sensitivity.report_id);
+	}
+	if (st->rot_attributes.sensitivity.index < 0) {
+		sensor_hub_input_get_attribute_info(hsdev,
+			HID_FEATURE_REPORT, usage_id,
+			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
+			HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
+			&st->rot_attributes.sensitivity);
 		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
-			st->common_attributes.sensitivity.index,
-			st->common_attributes.sensitivity.report_id);
+			st->rot_attributes.sensitivity.index,
+			st->rot_attributes.sensitivity.report_id);
 	}
 
 	return 0;
@@ -428,16 +504,17 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, indio_dev);
 
 	magn_state = iio_priv(indio_dev);
-	magn_state->common_attributes.hsdev = hsdev;
-	magn_state->common_attributes.pdev = pdev;
+	magn_state->magn_flux_attributes.hsdev = hsdev;
+	magn_state->magn_flux_attributes.pdev = pdev;
 
 	ret = hid_sensor_parse_common_attributes(hsdev,
 				HID_USAGE_SENSOR_COMPASS_3D,
-				&magn_state->common_attributes);
+				&magn_state->magn_flux_attributes);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to setup common attributes\n");
 		return ret;
 	}
+	magn_state->rot_attributes = magn_state->magn_flux_attributes;
 
 	ret = magn_3d_parse_report(pdev, hsdev,
 				&channels, &chan_count,
@@ -460,9 +537,9 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
 		return ret;
 	}
-	atomic_set(&magn_state->common_attributes.data_ready, 0);
+	atomic_set(&magn_state->magn_flux_attributes.data_ready, 0);
 	ret = hid_sensor_setup_trigger(indio_dev, name,
-					&magn_state->common_attributes);
+					&magn_state->magn_flux_attributes);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "trigger setup failed\n");
 		goto error_unreg_buffer_funcs;
@@ -489,7 +566,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
 error_iio_unreg:
 	iio_device_unregister(indio_dev);
 error_remove_trigger:
-	hid_sensor_remove_trigger(&magn_state->common_attributes);
+	hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
 error_unreg_buffer_funcs:
 	iio_triggered_buffer_cleanup(indio_dev);
 	return ret;
@@ -504,7 +581,7 @@ static int hid_magn_3d_remove(struct platform_device *pdev)
 
 	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
 	iio_device_unregister(indio_dev);
-	hid_sensor_remove_trigger(&magn_state->common_attributes);
+	hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
 	iio_triggered_buffer_cleanup(indio_dev);
 
 	return 0;

+ 125 - 251
drivers/iio/magnetometer/st_magn_core.c

@@ -46,139 +46,12 @@
 #define ST_MAGN_FS_AVL_15000MG			15000
 #define ST_MAGN_FS_AVL_16000MG			16000
 
-/* CUSTOM VALUES FOR SENSOR 0 */
-#define ST_MAGN_0_ODR_ADDR			0x00
-#define ST_MAGN_0_ODR_MASK			0x1c
-#define ST_MAGN_0_ODR_AVL_1HZ_VAL		0x00
-#define ST_MAGN_0_ODR_AVL_2HZ_VAL		0x01
-#define ST_MAGN_0_ODR_AVL_3HZ_VAL		0x02
-#define ST_MAGN_0_ODR_AVL_8HZ_VAL		0x03
-#define ST_MAGN_0_ODR_AVL_15HZ_VAL		0x04
-#define ST_MAGN_0_ODR_AVL_30HZ_VAL		0x05
-#define ST_MAGN_0_ODR_AVL_75HZ_VAL		0x06
-#define ST_MAGN_0_ODR_AVL_220HZ_VAL		0x07
-#define ST_MAGN_0_PW_ADDR			0x02
-#define ST_MAGN_0_PW_MASK			0x03
-#define ST_MAGN_0_PW_ON				0x00
-#define ST_MAGN_0_PW_OFF			0x03
-#define ST_MAGN_0_FS_ADDR			0x01
-#define ST_MAGN_0_FS_MASK			0xe0
-#define ST_MAGN_0_FS_AVL_1300_VAL		0x01
-#define ST_MAGN_0_FS_AVL_1900_VAL		0x02
-#define ST_MAGN_0_FS_AVL_2500_VAL		0x03
-#define ST_MAGN_0_FS_AVL_4000_VAL		0x04
-#define ST_MAGN_0_FS_AVL_4700_VAL		0x05
-#define ST_MAGN_0_FS_AVL_5600_VAL		0x06
-#define ST_MAGN_0_FS_AVL_8100_VAL		0x07
-#define ST_MAGN_0_FS_AVL_1300_GAIN_XY		1100
-#define ST_MAGN_0_FS_AVL_1900_GAIN_XY		855
-#define ST_MAGN_0_FS_AVL_2500_GAIN_XY		670
-#define ST_MAGN_0_FS_AVL_4000_GAIN_XY		450
-#define ST_MAGN_0_FS_AVL_4700_GAIN_XY		400
-#define ST_MAGN_0_FS_AVL_5600_GAIN_XY		330
-#define ST_MAGN_0_FS_AVL_8100_GAIN_XY		230
-#define ST_MAGN_0_FS_AVL_1300_GAIN_Z		980
-#define ST_MAGN_0_FS_AVL_1900_GAIN_Z		760
-#define ST_MAGN_0_FS_AVL_2500_GAIN_Z		600
-#define ST_MAGN_0_FS_AVL_4000_GAIN_Z		400
-#define ST_MAGN_0_FS_AVL_4700_GAIN_Z		355
-#define ST_MAGN_0_FS_AVL_5600_GAIN_Z		295
-#define ST_MAGN_0_FS_AVL_8100_GAIN_Z		205
-#define ST_MAGN_0_MULTIREAD_BIT			false
-
-/* CUSTOM VALUES FOR SENSOR 1 */
-#define ST_MAGN_1_WAI_EXP			0x3c
-#define ST_MAGN_1_ODR_ADDR			0x00
-#define ST_MAGN_1_ODR_MASK			0x1c
-#define ST_MAGN_1_ODR_AVL_1HZ_VAL		0x00
-#define ST_MAGN_1_ODR_AVL_2HZ_VAL		0x01
-#define ST_MAGN_1_ODR_AVL_3HZ_VAL		0x02
-#define ST_MAGN_1_ODR_AVL_8HZ_VAL		0x03
-#define ST_MAGN_1_ODR_AVL_15HZ_VAL		0x04
-#define ST_MAGN_1_ODR_AVL_30HZ_VAL		0x05
-#define ST_MAGN_1_ODR_AVL_75HZ_VAL		0x06
-#define ST_MAGN_1_ODR_AVL_220HZ_VAL		0x07
-#define ST_MAGN_1_PW_ADDR			0x02
-#define ST_MAGN_1_PW_MASK			0x03
-#define ST_MAGN_1_PW_ON				0x00
-#define ST_MAGN_1_PW_OFF			0x03
-#define ST_MAGN_1_FS_ADDR			0x01
-#define ST_MAGN_1_FS_MASK			0xe0
-#define ST_MAGN_1_FS_AVL_1300_VAL		0x01
-#define ST_MAGN_1_FS_AVL_1900_VAL		0x02
-#define ST_MAGN_1_FS_AVL_2500_VAL		0x03
-#define ST_MAGN_1_FS_AVL_4000_VAL		0x04
-#define ST_MAGN_1_FS_AVL_4700_VAL		0x05
-#define ST_MAGN_1_FS_AVL_5600_VAL		0x06
-#define ST_MAGN_1_FS_AVL_8100_VAL		0x07
-#define ST_MAGN_1_FS_AVL_1300_GAIN_XY		909
-#define ST_MAGN_1_FS_AVL_1900_GAIN_XY		1169
-#define ST_MAGN_1_FS_AVL_2500_GAIN_XY		1492
-#define ST_MAGN_1_FS_AVL_4000_GAIN_XY		2222
-#define ST_MAGN_1_FS_AVL_4700_GAIN_XY		2500
-#define ST_MAGN_1_FS_AVL_5600_GAIN_XY		3030
-#define ST_MAGN_1_FS_AVL_8100_GAIN_XY		4347
-#define ST_MAGN_1_FS_AVL_1300_GAIN_Z		1020
-#define ST_MAGN_1_FS_AVL_1900_GAIN_Z		1315
-#define ST_MAGN_1_FS_AVL_2500_GAIN_Z		1666
-#define ST_MAGN_1_FS_AVL_4000_GAIN_Z		2500
-#define ST_MAGN_1_FS_AVL_4700_GAIN_Z		2816
-#define ST_MAGN_1_FS_AVL_5600_GAIN_Z		3389
-#define ST_MAGN_1_FS_AVL_8100_GAIN_Z		4878
-#define ST_MAGN_1_MULTIREAD_BIT			false
-
-/* CUSTOM VALUES FOR SENSOR 2 */
-#define ST_MAGN_2_WAI_EXP			0x3d
-#define ST_MAGN_2_ODR_ADDR			0x20
-#define ST_MAGN_2_ODR_MASK			0x1c
-#define ST_MAGN_2_ODR_AVL_1HZ_VAL		0x00
-#define ST_MAGN_2_ODR_AVL_2HZ_VAL		0x01
-#define ST_MAGN_2_ODR_AVL_3HZ_VAL		0x02
-#define ST_MAGN_2_ODR_AVL_5HZ_VAL		0x03
-#define ST_MAGN_2_ODR_AVL_10HZ_VAL		0x04
-#define ST_MAGN_2_ODR_AVL_20HZ_VAL		0x05
-#define ST_MAGN_2_ODR_AVL_40HZ_VAL		0x06
-#define ST_MAGN_2_ODR_AVL_80HZ_VAL		0x07
-#define ST_MAGN_2_PW_ADDR			0x22
-#define ST_MAGN_2_PW_MASK			0x03
-#define ST_MAGN_2_PW_ON				0x00
-#define ST_MAGN_2_PW_OFF			0x03
-#define ST_MAGN_2_FS_ADDR			0x21
-#define ST_MAGN_2_FS_MASK			0x60
-#define ST_MAGN_2_FS_AVL_4000_VAL		0x00
-#define ST_MAGN_2_FS_AVL_8000_VAL		0x01
-#define ST_MAGN_2_FS_AVL_12000_VAL		0x02
-#define ST_MAGN_2_FS_AVL_16000_VAL		0x03
-#define ST_MAGN_2_FS_AVL_4000_GAIN		146
-#define ST_MAGN_2_FS_AVL_8000_GAIN		292
-#define ST_MAGN_2_FS_AVL_12000_GAIN		438
-#define ST_MAGN_2_FS_AVL_16000_GAIN		584
-#define ST_MAGN_2_MULTIREAD_BIT			false
+/* Special L addresses for Sensor 2 */
 #define ST_MAGN_2_OUT_X_L_ADDR			0x28
 #define ST_MAGN_2_OUT_Y_L_ADDR			0x2a
 #define ST_MAGN_2_OUT_Z_L_ADDR			0x2c
 
-/* CUSTOM VALUES FOR SENSOR 3 */
-#define ST_MAGN_3_WAI_ADDR			0x4f
-#define ST_MAGN_3_WAI_EXP			0x40
-#define ST_MAGN_3_ODR_ADDR			0x60
-#define ST_MAGN_3_ODR_MASK			0x0c
-#define ST_MAGN_3_ODR_AVL_10HZ_VAL		0x00
-#define ST_MAGN_3_ODR_AVL_20HZ_VAL		0x01
-#define ST_MAGN_3_ODR_AVL_50HZ_VAL		0x02
-#define ST_MAGN_3_ODR_AVL_100HZ_VAL		0x03
-#define ST_MAGN_3_PW_ADDR			0x60
-#define ST_MAGN_3_PW_MASK			0x03
-#define ST_MAGN_3_PW_ON				0x00
-#define ST_MAGN_3_PW_OFF			0x03
-#define ST_MAGN_3_BDU_ADDR			0x62
-#define ST_MAGN_3_BDU_MASK			0x10
-#define ST_MAGN_3_DRDY_IRQ_ADDR			0x62
-#define ST_MAGN_3_DRDY_INT_MASK			0x01
-#define ST_MAGN_3_IHL_IRQ_ADDR			0x63
-#define ST_MAGN_3_IHL_IRQ_MASK			0x04
-#define ST_MAGN_3_FS_AVL_15000_GAIN		1500
-#define ST_MAGN_3_MULTIREAD_BIT			false
+/* Special L addresses for sensor 3 */
 #define ST_MAGN_3_OUT_X_L_ADDR			0x68
 #define ST_MAGN_3_OUT_Y_L_ADDR			0x6a
 #define ST_MAGN_3_OUT_Z_L_ADDR			0x6c
@@ -240,77 +113,78 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
 		},
 		.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
 		.odr = {
-			.addr = ST_MAGN_0_ODR_ADDR,
-			.mask = ST_MAGN_0_ODR_MASK,
+			.addr = 0x00,
+			.mask = 0x1c,
 			.odr_avl = {
-				{ 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
-				{ 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
-				{ 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
-				{ 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
-				{ 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
-				{ 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
-				{ 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
+				{ .hz = 1, .value = 0x00 },
+				{ .hz = 2, .value = 0x01 },
+				{ .hz = 3, .value = 0x02 },
+				{ .hz = 8, .value = 0x03 },
+				{ .hz = 15, .value = 0x04 },
+				{ .hz = 30, .value = 0x05 },
+				{ .hz = 75, .value = 0x06 },
+				/* 220 Hz, 0x07 reportedly exist */
 			},
 		},
 		.pw = {
-			.addr = ST_MAGN_0_PW_ADDR,
-			.mask = ST_MAGN_0_PW_MASK,
-			.value_on = ST_MAGN_0_PW_ON,
-			.value_off = ST_MAGN_0_PW_OFF,
+			.addr = 0x02,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
 		},
 		.fs = {
-			.addr = ST_MAGN_0_FS_ADDR,
-			.mask = ST_MAGN_0_FS_MASK,
+			.addr = 0x01,
+			.mask = 0xe0,
 			.fs_avl = {
 				[0] = {
 					.num = ST_MAGN_FS_AVL_1300MG,
-					.value = ST_MAGN_0_FS_AVL_1300_VAL,
-					.gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
-					.gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
+					.value = 0x01,
+					.gain = 1100,
+					.gain2 = 980,
 				},
 				[1] = {
 					.num = ST_MAGN_FS_AVL_1900MG,
-					.value = ST_MAGN_0_FS_AVL_1900_VAL,
-					.gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
-					.gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
+					.value = 0x02,
+					.gain = 855,
+					.gain2 = 760,
 				},
 				[2] = {
 					.num = ST_MAGN_FS_AVL_2500MG,
-					.value = ST_MAGN_0_FS_AVL_2500_VAL,
-					.gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
-					.gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
+					.value = 0x03,
+					.gain = 670,
+					.gain2 = 600,
 				},
 				[3] = {
 					.num = ST_MAGN_FS_AVL_4000MG,
-					.value = ST_MAGN_0_FS_AVL_4000_VAL,
-					.gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
-					.gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
+					.value = 0x04,
+					.gain = 450,
+					.gain2 = 400,
 				},
 				[4] = {
 					.num = ST_MAGN_FS_AVL_4700MG,
-					.value = ST_MAGN_0_FS_AVL_4700_VAL,
-					.gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
-					.gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
+					.value = 0x05,
+					.gain = 400,
+					.gain2 = 355,
 				},
 				[5] = {
 					.num = ST_MAGN_FS_AVL_5600MG,
-					.value = ST_MAGN_0_FS_AVL_5600_VAL,
-					.gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
-					.gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
+					.value = 0x06,
+					.gain = 330,
+					.gain2 = 295,
 				},
 				[6] = {
 					.num = ST_MAGN_FS_AVL_8100MG,
-					.value = ST_MAGN_0_FS_AVL_8100_VAL,
-					.gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
-					.gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
+					.value = 0x07,
+					.gain = 230,
+					.gain2 = 205,
 				},
 			},
 		},
-		.multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
+		.multi_read_bit = false,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_MAGN_1_WAI_EXP,
+		.wai = 0x3c,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = LSM303DLHC_MAGN_DEV_NAME,
@@ -318,175 +192,175 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
 		},
 		.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
 		.odr = {
-			.addr = ST_MAGN_1_ODR_ADDR,
-			.mask = ST_MAGN_1_ODR_MASK,
+			.addr = 0x00,
+			.mask = 0x1c,
 			.odr_avl = {
-				{ 1, ST_MAGN_1_ODR_AVL_1HZ_VAL, },
-				{ 2, ST_MAGN_1_ODR_AVL_2HZ_VAL, },
-				{ 3, ST_MAGN_1_ODR_AVL_3HZ_VAL, },
-				{ 8, ST_MAGN_1_ODR_AVL_8HZ_VAL, },
-				{ 15, ST_MAGN_1_ODR_AVL_15HZ_VAL, },
-				{ 30, ST_MAGN_1_ODR_AVL_30HZ_VAL, },
-				{ 75, ST_MAGN_1_ODR_AVL_75HZ_VAL, },
-				{ 220, ST_MAGN_1_ODR_AVL_220HZ_VAL, },
+				{ .hz = 1, .value = 0x00 },
+				{ .hz = 2, .value = 0x01 },
+				{ .hz = 3, .value = 0x02 },
+				{ .hz = 8, .value = 0x03 },
+				{ .hz = 15, .value = 0x04 },
+				{ .hz = 30, .value = 0x05 },
+				{ .hz = 75, .value = 0x06 },
+				{ .hz = 220, .value = 0x07 },
 			},
 		},
 		.pw = {
-			.addr = ST_MAGN_1_PW_ADDR,
-			.mask = ST_MAGN_1_PW_MASK,
-			.value_on = ST_MAGN_1_PW_ON,
-			.value_off = ST_MAGN_1_PW_OFF,
+			.addr = 0x02,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
 		},
 		.fs = {
-			.addr = ST_MAGN_1_FS_ADDR,
-			.mask = ST_MAGN_1_FS_MASK,
+			.addr = 0x01,
+			.mask = 0xe0,
 			.fs_avl = {
 				[0] = {
 					.num = ST_MAGN_FS_AVL_1300MG,
-					.value = ST_MAGN_1_FS_AVL_1300_VAL,
-					.gain = ST_MAGN_1_FS_AVL_1300_GAIN_XY,
-					.gain2 = ST_MAGN_1_FS_AVL_1300_GAIN_Z,
+					.value = 0x01,
+					.gain = 909,
+					.gain2 = 1020,
 				},
 				[1] = {
 					.num = ST_MAGN_FS_AVL_1900MG,
-					.value = ST_MAGN_1_FS_AVL_1900_VAL,
-					.gain = ST_MAGN_1_FS_AVL_1900_GAIN_XY,
-					.gain2 = ST_MAGN_1_FS_AVL_1900_GAIN_Z,
+					.value = 0x02,
+					.gain = 1169,
+					.gain2 = 1315,
 				},
 				[2] = {
 					.num = ST_MAGN_FS_AVL_2500MG,
-					.value = ST_MAGN_1_FS_AVL_2500_VAL,
-					.gain = ST_MAGN_1_FS_AVL_2500_GAIN_XY,
-					.gain2 = ST_MAGN_1_FS_AVL_2500_GAIN_Z,
+					.value = 0x03,
+					.gain = 1492,
+					.gain2 = 1666,
 				},
 				[3] = {
 					.num = ST_MAGN_FS_AVL_4000MG,
-					.value = ST_MAGN_1_FS_AVL_4000_VAL,
-					.gain = ST_MAGN_1_FS_AVL_4000_GAIN_XY,
-					.gain2 = ST_MAGN_1_FS_AVL_4000_GAIN_Z,
+					.value = 0x04,
+					.gain = 2222,
+					.gain2 = 2500,
 				},
 				[4] = {
 					.num = ST_MAGN_FS_AVL_4700MG,
-					.value = ST_MAGN_1_FS_AVL_4700_VAL,
-					.gain = ST_MAGN_1_FS_AVL_4700_GAIN_XY,
-					.gain2 = ST_MAGN_1_FS_AVL_4700_GAIN_Z,
+					.value = 0x05,
+					.gain = 2500,
+					.gain2 = 2816,
 				},
 				[5] = {
 					.num = ST_MAGN_FS_AVL_5600MG,
-					.value = ST_MAGN_1_FS_AVL_5600_VAL,
-					.gain = ST_MAGN_1_FS_AVL_5600_GAIN_XY,
-					.gain2 = ST_MAGN_1_FS_AVL_5600_GAIN_Z,
+					.value = 0x06,
+					.gain = 3030,
+					.gain2 = 3389,
 				},
 				[6] = {
 					.num = ST_MAGN_FS_AVL_8100MG,
-					.value = ST_MAGN_1_FS_AVL_8100_VAL,
-					.gain = ST_MAGN_1_FS_AVL_8100_GAIN_XY,
-					.gain2 = ST_MAGN_1_FS_AVL_8100_GAIN_Z,
+					.value = 0x07,
+					.gain = 4347,
+					.gain2 = 4878,
 				},
 			},
 		},
-		.multi_read_bit = ST_MAGN_1_MULTIREAD_BIT,
+		.multi_read_bit = false,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_MAGN_2_WAI_EXP,
+		.wai = 0x3d,
 		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
 		.sensors_supported = {
 			[0] = LIS3MDL_MAGN_DEV_NAME,
 		},
 		.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
 		.odr = {
-			.addr = ST_MAGN_2_ODR_ADDR,
-			.mask = ST_MAGN_2_ODR_MASK,
+			.addr = 0x20,
+			.mask = 0x1c,
 			.odr_avl = {
-				{ 1, ST_MAGN_2_ODR_AVL_1HZ_VAL, },
-				{ 2, ST_MAGN_2_ODR_AVL_2HZ_VAL, },
-				{ 3, ST_MAGN_2_ODR_AVL_3HZ_VAL, },
-				{ 5, ST_MAGN_2_ODR_AVL_5HZ_VAL, },
-				{ 10, ST_MAGN_2_ODR_AVL_10HZ_VAL, },
-				{ 20, ST_MAGN_2_ODR_AVL_20HZ_VAL, },
-				{ 40, ST_MAGN_2_ODR_AVL_40HZ_VAL, },
-				{ 80, ST_MAGN_2_ODR_AVL_80HZ_VAL, },
+				{ .hz = 1, .value = 0x00 },
+				{ .hz = 2, .value = 0x01 },
+				{ .hz = 3, .value = 0x02 },
+				{ .hz = 5, .value = 0x03 },
+				{ .hz = 10, .value = 0x04 },
+				{ .hz = 20, .value = 0x05 },
+				{ .hz = 40, .value = 0x06 },
+				{ .hz = 80, .value = 0x07 },
 			},
 		},
 		.pw = {
-			.addr = ST_MAGN_2_PW_ADDR,
-			.mask = ST_MAGN_2_PW_MASK,
-			.value_on = ST_MAGN_2_PW_ON,
-			.value_off = ST_MAGN_2_PW_OFF,
+			.addr = 0x22,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
 		},
 		.fs = {
-			.addr = ST_MAGN_2_FS_ADDR,
-			.mask = ST_MAGN_2_FS_MASK,
+			.addr = 0x21,
+			.mask = 0x60,
 			.fs_avl = {
 				[0] = {
 					.num = ST_MAGN_FS_AVL_4000MG,
-					.value = ST_MAGN_2_FS_AVL_4000_VAL,
-					.gain = ST_MAGN_2_FS_AVL_4000_GAIN,
+					.value = 0x00,
+					.gain = 146,
 				},
 				[1] = {
 					.num = ST_MAGN_FS_AVL_8000MG,
-					.value = ST_MAGN_2_FS_AVL_8000_VAL,
-					.gain = ST_MAGN_2_FS_AVL_8000_GAIN,
+					.value = 0x01,
+					.gain = 292,
 				},
 				[2] = {
 					.num = ST_MAGN_FS_AVL_12000MG,
-					.value = ST_MAGN_2_FS_AVL_12000_VAL,
-					.gain = ST_MAGN_2_FS_AVL_12000_GAIN,
+					.value = 0x02,
+					.gain = 438,
 				},
 				[3] = {
 					.num = ST_MAGN_FS_AVL_16000MG,
-					.value = ST_MAGN_2_FS_AVL_16000_VAL,
-					.gain = ST_MAGN_2_FS_AVL_16000_GAIN,
+					.value = 0x03,
+					.gain = 584,
 				},
 			},
 		},
-		.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
+		.multi_read_bit = false,
 		.bootime = 2,
 	},
 	{
-		.wai = ST_MAGN_3_WAI_EXP,
-		.wai_addr = ST_MAGN_3_WAI_ADDR,
+		.wai = 0x40,
+		.wai_addr = 0x4f,
 		.sensors_supported = {
 			[0] = LSM303AGR_MAGN_DEV_NAME,
 		},
 		.ch = (struct iio_chan_spec *)st_magn_3_16bit_channels,
 		.odr = {
-			.addr = ST_MAGN_3_ODR_ADDR,
-			.mask = ST_MAGN_3_ODR_MASK,
+			.addr = 0x60,
+			.mask = 0x0c,
 			.odr_avl = {
-				{ 10, ST_MAGN_3_ODR_AVL_10HZ_VAL, },
-				{ 20, ST_MAGN_3_ODR_AVL_20HZ_VAL, },
-				{ 50, ST_MAGN_3_ODR_AVL_50HZ_VAL, },
-				{ 100, ST_MAGN_3_ODR_AVL_100HZ_VAL, },
+				{ .hz = 10, .value = 0x00 },
+				{ .hz = 20, .value = 0x01 },
+				{ .hz = 50, .value = 0x02 },
+				{ .hz = 100, .value = 0x03 },
 			},
 		},
 		.pw = {
-			.addr = ST_MAGN_3_PW_ADDR,
-			.mask = ST_MAGN_3_PW_MASK,
-			.value_on = ST_MAGN_3_PW_ON,
-			.value_off = ST_MAGN_3_PW_OFF,
+			.addr = 0x60,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
 		},
 		.fs = {
 			.fs_avl = {
 				[0] = {
 					.num = ST_MAGN_FS_AVL_15000MG,
-					.gain = ST_MAGN_3_FS_AVL_15000_GAIN,
+					.gain = 1500,
 				},
 			},
 		},
 		.bdu = {
-			.addr = ST_MAGN_3_BDU_ADDR,
-			.mask = ST_MAGN_3_BDU_MASK,
+			.addr = 0x62,
+			.mask = 0x10,
 		},
 		.drdy_irq = {
-			.addr = ST_MAGN_3_DRDY_IRQ_ADDR,
-			.mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
-			.addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR,
-			.mask_ihl = ST_MAGN_3_IHL_IRQ_MASK,
+			.addr = 0x62,
+			.mask_int1 = 0x01,
+			.addr_ihl = 0x63,
+			.mask_ihl = 0x04,
 			.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
 		},
-		.multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
+		.multi_read_bit = false,
 		.bootime = 2,
 	},
 };

+ 62 - 42
drivers/iio/potentiometer/mcp4531.c

@@ -38,7 +38,7 @@
 
 struct mcp4531_cfg {
 	int wipers;
-	int max_pos;
+	int avail[3];
 	int kohms;
 };
 
@@ -78,38 +78,38 @@ enum mcp4531_type {
 };
 
 static const struct mcp4531_cfg mcp4531_cfg[] = {
-	[MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
-	[MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
-	[MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
-	[MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
-	[MCP454x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
-	[MCP454x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
-	[MCP454x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
-	[MCP454x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
-	[MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
-	[MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
-	[MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
-	[MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
-	[MCP456x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
-	[MCP456x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
-	[MCP456x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
-	[MCP456x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
-	[MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
-	[MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
-	[MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
-	[MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
-	[MCP464x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
-	[MCP464x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
-	[MCP464x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
-	[MCP464x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
-	[MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
-	[MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
-	[MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
-	[MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
-	[MCP466x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
-	[MCP466x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
-	[MCP466x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
-	[MCP466x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
+	[MCP453x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP453x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP453x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP453x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP454x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP454x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP454x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP454x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP455x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP455x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP455x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP455x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
+	[MCP456x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP456x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP456x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP456x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
+	[MCP463x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP463x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP463x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP463x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP464x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP464x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP464x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP464x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP465x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP465x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP465x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP465x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
+	[MCP466x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP466x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP466x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP466x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
 };
 
 #define MCP4531_WRITE (0 << 2)
@@ -124,13 +124,14 @@ struct mcp4531_data {
 	const struct mcp4531_cfg *cfg;
 };
 
-#define MCP4531_CHANNEL(ch) {					\
-	.type = IIO_RESISTANCE,					\
-	.indexed = 1,						\
-	.output = 1,						\
-	.channel = (ch),					\
-	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
-	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+#define MCP4531_CHANNEL(ch) {						\
+	.type = IIO_RESISTANCE,						\
+	.indexed = 1,							\
+	.output = 1,							\
+	.channel = (ch),						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),		\
+	.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW),	\
 }
 
 static const struct iio_chan_spec mcp4531_channels[] = {
@@ -156,13 +157,31 @@ static int mcp4531_read_raw(struct iio_dev *indio_dev,
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
 		*val = 1000 * data->cfg->kohms;
-		*val2 = data->cfg->max_pos;
+		*val2 = data->cfg->avail[2];
 		return IIO_VAL_FRACTIONAL;
 	}
 
 	return -EINVAL;
 }
 
+static int mcp4531_read_avail(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      const int **vals, int *type, int *length,
+			      long mask)
+{
+	struct mcp4531_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		*length = ARRAY_SIZE(data->cfg->avail);
+		*vals = data->cfg->avail;
+		*type = IIO_VAL_INT;
+		return IIO_AVAIL_RANGE;
+	}
+
+	return -EINVAL;
+}
+
 static int mcp4531_write_raw(struct iio_dev *indio_dev,
 			     struct iio_chan_spec const *chan,
 			     int val, int val2, long mask)
@@ -172,7 +191,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		if (val > data->cfg->max_pos || val < 0)
+		if (val > data->cfg->avail[2] || val < 0)
 			return -EINVAL;
 		break;
 	default:
@@ -186,6 +205,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
 
 static const struct iio_info mcp4531_info = {
 	.read_raw = mcp4531_read_raw,
+	.read_avail = mcp4531_read_avail,
 	.write_raw = mcp4531_write_raw,
 	.driver_module = THIS_MODULE,
 };

+ 22 - 0
drivers/iio/potentiostat/Kconfig

@@ -0,0 +1,22 @@
+#
+# Potentiostat drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Digital potentiostats"
+
+config LMP91000
+	tristate "Texas Instruments LMP91000 potentiostat driver"
+	depends on I2C
+	select REGMAP_I2C
+	select IIO_BUFFER
+	select IIO_BUFFER_CB
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for the Texas Instruments
+	  LMP91000 digital potentiostat chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lmp91000
+
+endmenu

+ 6 - 0
drivers/iio/potentiostat/Makefile

@@ -0,0 +1,6 @@
+#
+# Makefile for industrial I/O potentiostat drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_LMP91000) += lmp91000.o

+ 446 - 0
drivers/iio/potentiostat/lmp91000.c

@@ -0,0 +1,446 @@
+/*
+ * lmp91000.c - Support for Texas Instruments digital potentiostats
+ *
+ * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * TODO: bias voltage + polarity control, and multiple chip support
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define LMP91000_REG_LOCK		0x01
+#define LMP91000_REG_TIACN		0x10
+#define LMP91000_REG_TIACN_GAIN_SHIFT	2
+
+#define LMP91000_REG_REFCN		0x11
+#define LMP91000_REG_REFCN_EXT_REF	0x20
+#define LMP91000_REG_REFCN_50_ZERO	0x80
+
+#define LMP91000_REG_MODECN		0x12
+#define LMP91000_REG_MODECN_3LEAD	0x03
+#define LMP91000_REG_MODECN_TEMP	0x07
+
+#define LMP91000_DRV_NAME	"lmp91000"
+
+static const int lmp91000_tia_gain[] = { 0, 2750, 3500, 7000, 14000, 35000,
+					 120000, 350000 };
+
+static const int lmp91000_rload[] = { 10, 33, 50, 100 };
+
+#define LMP91000_TEMP_BASE	-40
+
+static const u16 lmp91000_temp_lut[] = {
+	1875, 1867, 1860, 1852, 1844, 1836, 1828, 1821, 1813, 1805,
+	1797, 1789, 1782, 1774, 1766, 1758, 1750, 1742, 1734, 1727,
+	1719, 1711, 1703, 1695, 1687, 1679, 1671, 1663, 1656, 1648,
+	1640, 1632, 1624, 1616, 1608, 1600, 1592, 1584, 1576, 1568,
+	1560, 1552, 1544, 1536, 1528, 1520, 1512, 1504, 1496, 1488,
+	1480, 1472, 1464, 1456, 1448, 1440, 1432, 1424, 1415, 1407,
+	1399, 1391, 1383, 1375, 1367, 1359, 1351, 1342, 1334, 1326,
+	1318, 1310, 1302, 1293, 1285, 1277, 1269, 1261, 1253, 1244,
+	1236, 1228, 1220, 1212, 1203, 1195, 1187, 1179, 1170, 1162,
+	1154, 1146, 1137, 1129, 1121, 1112, 1104, 1096, 1087, 1079,
+	1071, 1063, 1054, 1046, 1038, 1029, 1021, 1012, 1004,  996,
+	 987,  979,  971,  962,  954,  945,  937,  929,  920,  912,
+	 903,  895,  886,  878,  870,  861 };
+
+static const struct regmap_config lmp91000_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+struct lmp91000_data {
+	struct regmap *regmap;
+	struct device *dev;
+
+	struct iio_trigger *trig;
+	struct iio_cb_buffer *cb_buffer;
+	struct iio_channel *adc_chan;
+
+	struct completion completion;
+	u8 chan_select;
+
+	u32 buffer[4]; /* 64-bit data + 64-bit timestamp */
+};
+
+static const struct iio_chan_spec lmp91000_channels[] = {
+	{ /* chemical channel mV */
+		.type = IIO_VOLTAGE,
+		.channel = 0,
+		.address = LMP91000_REG_MODECN_3LEAD,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_OFFSET) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = 0,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 32,
+			.storagebits = 32,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(1),
+	{ /* temperature channel mV */
+		.type = IIO_TEMP,
+		.channel = 1,
+		.address = LMP91000_REG_MODECN_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.scan_index = -1,
+	},
+};
+
+static int lmp91000_read(struct lmp91000_data *data, int channel, int *val)
+{
+	int state, ret;
+
+	ret = regmap_read(data->regmap, LMP91000_REG_MODECN, &state);
+	if (ret)
+		return -EINVAL;
+
+	ret = regmap_write(data->regmap, LMP91000_REG_MODECN, channel);
+	if (ret)
+		return -EINVAL;
+
+	/* delay till first temperature reading is complete */
+	if ((state != channel) && (channel == LMP91000_REG_MODECN_TEMP))
+		usleep_range(3000, 4000);
+
+	data->chan_select = channel != LMP91000_REG_MODECN_3LEAD;
+
+	iio_trigger_poll_chained(data->trig);
+
+	ret = wait_for_completion_timeout(&data->completion, HZ);
+	reinit_completion(&data->completion);
+
+	if (!ret)
+		return -ETIMEDOUT;
+
+	*val = data->buffer[data->chan_select];
+
+	return 0;
+}
+
+static irqreturn_t lmp91000_buffer_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct lmp91000_data *data = iio_priv(indio_dev);
+	int ret, val;
+
+	memset(data->buffer, 0, sizeof(data->buffer));
+
+	ret = lmp91000_read(data, LMP91000_REG_MODECN_3LEAD, &val);
+	if (!ret) {
+		data->buffer[0] = val;
+		iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+						   iio_get_time_ns(indio_dev));
+	}
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int lmp91000_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val, int *val2, long mask)
+{
+	struct lmp91000_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+	case IIO_CHAN_INFO_PROCESSED: {
+		int ret = iio_channel_start_all_cb(data->cb_buffer);
+
+		if (ret)
+			return ret;
+
+		ret = lmp91000_read(data, chan->address, val);
+
+		iio_channel_stop_all_cb(data->cb_buffer);
+
+		if (ret)
+			return ret;
+
+		if (mask == IIO_CHAN_INFO_PROCESSED) {
+			int tmp, i;
+
+			ret = iio_convert_raw_to_processed(data->adc_chan,
+							   *val, &tmp, 1);
+			if (ret)
+				return ret;
+
+			for (i = 0; i < ARRAY_SIZE(lmp91000_temp_lut); i++)
+				if (lmp91000_temp_lut[i] < tmp)
+					break;
+
+			*val = (LMP91000_TEMP_BASE + i) * 1000;
+		}
+		return IIO_VAL_INT;
+	}
+	case IIO_CHAN_INFO_OFFSET:
+		return iio_read_channel_offset(data->adc_chan, val, val2);
+	case IIO_CHAN_INFO_SCALE:
+		return iio_read_channel_scale(data->adc_chan, val, val2);
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info lmp91000_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = lmp91000_read_raw,
+};
+
+static int lmp91000_read_config(struct lmp91000_data *data)
+{
+	struct device *dev = data->dev;
+	struct device_node *np = dev->of_node;
+	unsigned int reg, val;
+	int i, ret;
+
+	ret = of_property_read_u32(np, "ti,tia-gain-ohm", &val);
+	if (ret) {
+		if (of_property_read_bool(np, "ti,external-tia-resistor"))
+			val = 0;
+		else {
+			dev_err(dev, "no ti,tia-gain-ohm defined");
+			return ret;
+		}
+	}
+
+	ret = -EINVAL;
+	for (i = 0; i < ARRAY_SIZE(lmp91000_tia_gain); i++) {
+		if (lmp91000_tia_gain[i] == val) {
+			reg = i << LMP91000_REG_TIACN_GAIN_SHIFT;
+			ret = 0;
+			break;
+		}
+	}
+
+	if (ret) {
+		dev_err(dev, "invalid ti,tia-gain-ohm %d\n", val);
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "ti,rload-ohm", &val);
+	if (ret) {
+		val = 100;
+		dev_info(dev, "no ti,rload-ohm defined, default to %d\n", val);
+	}
+
+	ret = -EINVAL;
+	for (i = 0; i < ARRAY_SIZE(lmp91000_rload); i++) {
+		if (lmp91000_rload[i] == val) {
+			reg |= i;
+			ret = 0;
+			break;
+		}
+	}
+
+	if (ret) {
+		dev_err(dev, "invalid ti,rload-ohm %d\n", val);
+		return ret;
+	}
+
+	regmap_write(data->regmap, LMP91000_REG_LOCK, 0);
+	regmap_write(data->regmap, LMP91000_REG_TIACN, reg);
+	regmap_write(data->regmap, LMP91000_REG_REFCN, LMP91000_REG_REFCN_EXT_REF
+					| LMP91000_REG_REFCN_50_ZERO);
+	regmap_write(data->regmap, LMP91000_REG_LOCK, 1);
+
+	return 0;
+}
+
+static int lmp91000_buffer_cb(const void *val, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct lmp91000_data *data = iio_priv(indio_dev);
+
+	data->buffer[data->chan_select] = *((int *)val);
+	complete_all(&data->completion);
+
+	return 0;
+}
+
+static const struct iio_trigger_ops lmp91000_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+
+static int lmp91000_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct lmp91000_data *data = iio_priv(indio_dev);
+
+	return iio_channel_start_all_cb(data->cb_buffer);
+}
+
+static int lmp91000_buffer_predisable(struct iio_dev *indio_dev)
+{
+	struct lmp91000_data *data = iio_priv(indio_dev);
+
+	iio_channel_stop_all_cb(data->cb_buffer);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops lmp91000_buffer_setup_ops = {
+	.preenable = lmp91000_buffer_preenable,
+	.postenable = iio_triggered_buffer_postenable,
+	.predisable = lmp91000_buffer_predisable,
+};
+
+static int lmp91000_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct lmp91000_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	indio_dev->info = &lmp91000_info;
+	indio_dev->channels = lmp91000_channels;
+	indio_dev->num_channels = ARRAY_SIZE(lmp91000_channels);
+	indio_dev->name = LMP91000_DRV_NAME;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	i2c_set_clientdata(client, indio_dev);
+
+	data = iio_priv(indio_dev);
+	data->dev = dev;
+	data->regmap = devm_regmap_init_i2c(client, &lmp91000_regmap_config);
+	if (IS_ERR(data->regmap)) {
+		dev_err(dev, "regmap initialization failed.\n");
+		return PTR_ERR(data->regmap);
+	}
+
+	data->trig = devm_iio_trigger_alloc(data->dev, "%s-mux%d",
+					    indio_dev->name, indio_dev->id);
+	if (!data->trig) {
+		dev_err(dev, "cannot allocate iio trigger.\n");
+		return -ENOMEM;
+	}
+
+	data->trig->ops = &lmp91000_trigger_ops;
+	data->trig->dev.parent = dev;
+	init_completion(&data->completion);
+
+	ret = lmp91000_read_config(data);
+	if (ret)
+		return ret;
+
+	ret = iio_trigger_set_immutable(iio_channel_cb_get_iio_dev(data->cb_buffer),
+					data->trig);
+	if (ret) {
+		dev_err(dev, "cannot set immutable trigger.\n");
+		return ret;
+	}
+
+	ret = iio_trigger_register(data->trig);
+	if (ret) {
+		dev_err(dev, "cannot register iio trigger.\n");
+		return ret;
+	}
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 &lmp91000_buffer_handler,
+					 &lmp91000_buffer_setup_ops);
+	if (ret)
+		goto error_unreg_trigger;
+
+	data->cb_buffer = iio_channel_get_all_cb(dev, &lmp91000_buffer_cb,
+						 indio_dev);
+
+	if (IS_ERR(data->cb_buffer)) {
+		if (PTR_ERR(data->cb_buffer) == -ENODEV)
+			ret = -EPROBE_DEFER;
+		else
+			ret = PTR_ERR(data->cb_buffer);
+
+		goto error_unreg_buffer;
+	}
+
+	data->adc_chan = iio_channel_cb_get_channels(data->cb_buffer);
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_unreg_cb_buffer;
+
+	return 0;
+
+error_unreg_cb_buffer:
+	iio_channel_release_all_cb(data->cb_buffer);
+
+error_unreg_buffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+
+error_unreg_trigger:
+	iio_trigger_unregister(data->trig);
+
+	return ret;
+}
+
+static int lmp91000_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct lmp91000_data *data = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	iio_channel_stop_all_cb(data->cb_buffer);
+	iio_channel_release_all_cb(data->cb_buffer);
+
+	iio_triggered_buffer_cleanup(indio_dev);
+	iio_trigger_unregister(data->trig);
+
+	return 0;
+}
+
+static const struct of_device_id lmp91000_of_match[] = {
+	{ .compatible = "ti,lmp91000", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, lmp91000_of_match);
+
+static const struct i2c_device_id lmp91000_id[] = {
+	{ "lmp91000", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, lmp91000_id);
+
+static struct i2c_driver lmp91000_driver = {
+	.driver = {
+		.name = LMP91000_DRV_NAME,
+		.of_match_table = of_match_ptr(lmp91000_of_match),
+	},
+	.probe = lmp91000_probe,
+	.remove = lmp91000_remove,
+	.id_table = lmp91000_id,
+};
+module_i2c_driver(lmp91000_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("LMP91000 digital potentiostat");
+MODULE_LICENSE("GPL");

Some files were not shown because too many files changed in this diff