All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551
@ 2015-01-11 19:10 Irina Tirdea
  2015-01-11 19:10 ` [PATCH v2 01/10] iio: core: Introduce ENERGY channel type Irina Tirdea
                   ` (9 more replies)
  0 siblings, 10 replies; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

This set of patches includes changes for the MMA9551 and MMA9553 drivers:
 - add support for Freescale MMA9553L Intelligent Pedometer Platform
(http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
 - add new iio channels types and modifiers needed by the pedometer
 - add runtime PM support for MMA9551

Changes in v2:
 - removed IIO_CHAN_INFO_CALIBGENDER and used extended attribute
enum interface instead
 - renamed IIO_CALORIES channel to IIO_ENERGY
 - replaced IIO_SPEED with IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z
 - introduced IIO_EV_TYPE_CHANGE to use for step detector instead of
IIO_EV_TYPE_INSTANCE
 - introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and
IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD
 - add kernel-doc comments to functions exported in mma9551_core.c
 - updated ABI documentation to include all exported attributes in mma9553
 - updated mma9553 driver code according to comments from Jonathan

Irina Tirdea (10):
  iio: core: Introduce ENERGY channel type
  iio: core: Introduce DISTANCE channel type
  iio: core: Introduce IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z
  iio: core: Introduce IO_CHAN_INFO_CALIBWEIGHT
  iio: core: Introduce CHANGE event type
  iio: core: Remove IIO_EV_TYPE_INSTANCE
  iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
  iio: accel: mma9551: Add runtime pm support
  iio: accel: mma9551: split driver to expose mma955x api
  iio: add driver for Freescale MMA9553

 Documentation/ABI/testing/sysfs-bus-iio            |  127 +-
 drivers/iio/accel/Kconfig                          |   15 +
 drivers/iio/accel/Makefile                         |    5 +-
 drivers/iio/accel/mma9551.c                        |  489 ++------
 drivers/iio/accel/mma9551_core.c                   |  798 ++++++++++++
 drivers/iio/accel/mma9551_core.h                   |   81 ++
 drivers/iio/accel/mma9553.c                        | 1321 ++++++++++++++++++++
 drivers/iio/industrialio-core.c                    |    7 +
 drivers/iio/industrialio-event.c                   |    2 +-
 .../staging/iio/Documentation/iio_event_monitor.c  |    4 +-
 drivers/staging/iio/iio_simple_dummy.c             |    2 +-
 drivers/staging/iio/iio_simple_dummy_events.c      |    4 +-
 include/linux/iio/iio.h                            |   14 +
 include/linux/iio/types.h                          |    6 +-
 14 files changed, 2455 insertions(+), 420 deletions(-)
 create mode 100644 drivers/iio/accel/mma9551_core.c
 create mode 100644 drivers/iio/accel/mma9551_core.h
 create mode 100644 drivers/iio/accel/mma9553.c

-- 
1.9.1


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

* [PATCH v2 01/10] iio: core: Introduce ENERGY channel type
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-25 22:58   ` Jonathan Cameron
  2015-03-29  0:14   ` Hartmut Knaack
  2015-01-11 19:10 ` [PATCH v2 02/10] iio: core: Introduce DISTANCE " Irina Tirdea
                   ` (8 subsequent siblings)
  9 siblings, 2 replies; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

Human activity sensors report the energy burnt by the user.
One of this devices is Freescale's MMA9553L
(http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
that computes the number of calories based on weight and step rate.

Introduce a new channel type ENERGY to export these values.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
 drivers/iio/industrialio-core.c         |  1 +
 include/linux/iio/types.h               |  1 +
 3 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 831db86..3311886 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -282,6 +282,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_current_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_energy_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
@@ -1049,6 +1050,15 @@ Description:
 		For a list of available output power modes read
 		in_accel_power_mode_available.
 
+What:		/sys/.../iio:deviceX/in_energy_input
+What:		/sys/.../iio:deviceX/in_energy_raw
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This attribute is used to read the energy value reported by the
+		device (e.g.: human activity sensors report energy burnt by the
+		user). Units after application of scale are Joules.
+
 What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
 KernelVersion:	3.4.0
 Contact:	linux-iio@vger.kernel.org
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 69feb91..8d2c9ba 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -72,6 +72,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
 	[IIO_ACTIVITY] = "activity",
 	[IIO_STEPS] = "steps",
+	[IIO_ENERGY] = "energy",
 };
 
 static const char * const iio_modifier_names[] = {
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 904dcbb..26b8a1c 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -32,6 +32,7 @@ enum iio_chan_type {
 	IIO_HUMIDITYRELATIVE,
 	IIO_ACTIVITY,
 	IIO_STEPS,
+	IIO_ENERGY,
 };
 
 enum iio_modifier {
-- 
1.9.1


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

* [PATCH v2 02/10] iio: core: Introduce DISTANCE channel type
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
  2015-01-11 19:10 ` [PATCH v2 01/10] iio: core: Introduce ENERGY channel type Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-25 22:59   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 03/10] iio: core: Introduce IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z Irina Tirdea
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

Some devices export an estimation of the distance the user has covered
since the last reset.

One of this devices is Freescale's MMA9553L
(http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
that computes the distance based on the stride length and step rate.

Introduce a new channel type DISTANCE to export these values.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
 drivers/iio/industrialio-core.c         |  1 +
 include/linux/iio/types.h               |  1 +
 3 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 3311886..c627a9a 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -283,6 +283,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_energy_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_distance_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
@@ -1059,6 +1060,15 @@ Description:
 		device (e.g.: human activity sensors report energy burnt by the
 		user). Units after application of scale are Joules.
 
+What:		/sys/.../iio:deviceX/in_distance_input
+What:		/sys/.../iio:deviceX/in_distance_raw
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This attribute is used to read the distance covered by the user
+		since the last reboot while activated. Units after application
+		of scale are meters.
+
 What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
 KernelVersion:	3.4.0
 Contact:	linux-iio@vger.kernel.org
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 8d2c9ba..655755b 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -73,6 +73,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_ACTIVITY] = "activity",
 	[IIO_STEPS] = "steps",
 	[IIO_ENERGY] = "energy",
+	[IIO_DISTANCE] = "distance",
 };
 
 static const char * const iio_modifier_names[] = {
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 26b8a1c..a7de445 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -33,6 +33,7 @@ enum iio_chan_type {
 	IIO_ACTIVITY,
 	IIO_STEPS,
 	IIO_ENERGY,
+	IIO_DISTANCE,
 };
 
 enum iio_modifier {
-- 
1.9.1


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

* [PATCH v2 03/10] iio: core: Introduce IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
  2015-01-11 19:10 ` [PATCH v2 01/10] iio: core: Introduce ENERGY channel type Irina Tirdea
  2015-01-11 19:10 ` [PATCH v2 02/10] iio: core: Introduce DISTANCE " Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-25 23:00   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 04/10] iio: core: Introduce IO_CHAN_INFO_CALIBWEIGHT Irina Tirdea
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

Some devices export the current speed value of the user.

One of this devices is Freescale's MMA9553L
(http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
that computes the speed of the user based on the number of steps and
stride length.

Introduce a new channel type VELOCITY and a modifier for the magniture or
norm of the velocity vector, IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
 drivers/iio/industrialio-core.c         |  2 ++
 include/linux/iio/types.h               |  2 ++
 3 files changed, 14 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index c627a9a..80b5efb1 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -295,6 +295,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_rot_from_north_true_tilt_comp_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
 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
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -1164,3 +1165,12 @@ Contact:	linux-iio@vger.kernel.org
 Description:
 		This attribute is used to read the number of steps taken by the user
 		since the last reboot while activated.
+
+What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_input
+What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_raw
+KernelVersion:	3.19
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This attribute is used to read the current speed value of the
+		user (which is the norm or magnitude of the velocity vector).
+		Units after application of scale are m/s.
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 655755b..18a8ab9 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -74,6 +74,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_STEPS] = "steps",
 	[IIO_ENERGY] = "energy",
 	[IIO_DISTANCE] = "distance",
+	[IIO_VELOCITY] = "velocity",
 };
 
 static const char * const iio_modifier_names[] = {
@@ -99,6 +100,7 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_JOGGING] = "jogging",
 	[IIO_MOD_WALKING] = "walking",
 	[IIO_MOD_STILL] = "still",
+	[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
 };
 
 /* relies on pairs of these shared then separate */
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index a7de445..c3601c2 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -34,6 +34,7 @@ enum iio_chan_type {
 	IIO_STEPS,
 	IIO_ENERGY,
 	IIO_DISTANCE,
+	IIO_VELOCITY,
 };
 
 enum iio_modifier {
@@ -68,6 +69,7 @@ enum iio_modifier {
 	IIO_MOD_JOGGING,
 	IIO_MOD_WALKING,
 	IIO_MOD_STILL,
+	IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
 };
 
 enum iio_event_type {
-- 
1.9.1


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

* [PATCH v2 04/10] iio: core: Introduce IO_CHAN_INFO_CALIBWEIGHT
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
                   ` (2 preceding siblings ...)
  2015-01-11 19:10 ` [PATCH v2 03/10] iio: core: Introduce IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-25 23:01   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 05/10] iio: core: Introduce CHANGE event type Irina Tirdea
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

Some devices need the weight of the user to compute other
parameters. One of this devices is Freescale's MMA9553L
(http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
that needs the weight of the user to compute the number of calories burnt.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++
 drivers/iio/industrialio-core.c         | 1 +
 include/linux/iio/iio.h                 | 1 +
 3 files changed, 9 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 80b5efb1..71dc8db 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -351,6 +351,13 @@ Description:
 		to compute the stride length, distance, speed and activity
 		type.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibweight
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Weight of the user (in kg). It is needed by some pedometers
+		to compute the calories burnt by the user.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
 What:		/sys/.../iio:deviceX/in_voltageX_scale_available
 What:		/sys/.../iio:deviceX/in_voltage-voltage_scale_available
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 18a8ab9..4ee6fdf 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -125,6 +125,7 @@ static const char * const iio_chan_info_postfix[] = {
 	[IIO_CHAN_INFO_INT_TIME] = "integration_time",
 	[IIO_CHAN_INFO_ENABLE] = "en",
 	[IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
+	[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
 };
 
 /**
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 878d861..752a929 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -40,6 +40,7 @@ enum iio_chan_info_enum {
 	IIO_CHAN_INFO_INT_TIME,
 	IIO_CHAN_INFO_ENABLE,
 	IIO_CHAN_INFO_CALIBHEIGHT,
+	IIO_CHAN_INFO_CALIBWEIGHT,
 };
 
 enum iio_shared_by {
-- 
1.9.1


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

* [PATCH v2 05/10] iio: core: Introduce CHANGE event type
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
                   ` (3 preceding siblings ...)
  2015-01-11 19:10 ` [PATCH v2 04/10] iio: core: Introduce IO_CHAN_INFO_CALIBWEIGHT Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-25 23:03   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 06/10] iio: core: Remove IIO_EV_TYPE_INSTANCE Irina Tirdea
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

A step detector will generate an interrupt each time N step are detected.
A device that has such pedometer functionality is Freescale's MMA9553L:
http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf.

Introduce IIO_EV_TYPE_CHANGE event type for events that are generated
when the channel passes a threshold on the absolute change in value.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 Documentation/ABI/testing/sysfs-bus-iio              | 20 ++++++++++++++++----
 drivers/iio/industrialio-event.c                     |  1 +
 .../staging/iio/Documentation/iio_event_monitor.c    |  2 ++
 include/linux/iio/types.h                            |  1 +
 4 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 71dc8db..c03a140 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -891,12 +891,24 @@ Description:
 		number or direction is not specified, applies to all channels of
 		this type.
 
-What:		/sys/.../events/in_steps_instance_en
-KernelVersion:	3.19
+What:		/sys/.../events/in_steps_change_en
+KernelVersion:	3.20
 Contact:	linux-iio@vger.kernel.org
 Description:
-		Enables or disables step detection. Each time the user takes a step an
-		event of this type will be generated.
+		Event generated when channel passes a threshold on the absolute
+		change in value. E.g. for steps: a step change event is
+		generated each time the user takes N steps, where N is set using
+		in_steps_change_value.
+
+What:		/sys/.../events/in_steps_change_value
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Specifies the value of change threshold that the
+		device is comparing against for the events enabled by
+		<type>[Y][_name]_roc[_rising|falling|]_en. E.g. for steps:
+		if set to 3, a step change event will be generated every 3
+		steps.
 
 What:		/sys/bus/iio/devices/iio:deviceX/trigger/current_trigger
 KernelVersion:	2.6.35
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 3f5cee0..08c4a4c 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -198,6 +198,7 @@ static const char * const iio_ev_type_text[] = {
 	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
 	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
 	[IIO_EV_TYPE_INSTANCE] = "instance",
+	[IIO_EV_TYPE_CHANGE] = "change",
 };
 
 static const char * const iio_ev_dir_text[] = {
diff --git a/drivers/staging/iio/Documentation/iio_event_monitor.c b/drivers/staging/iio/Documentation/iio_event_monitor.c
index def236a..2e78d58 100644
--- a/drivers/staging/iio/Documentation/iio_event_monitor.c
+++ b/drivers/staging/iio/Documentation/iio_event_monitor.c
@@ -60,6 +60,7 @@ static const char * const iio_ev_type_text[] = {
 	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
 	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
 	[IIO_EV_TYPE_INSTANCE] = "instance",
+	[IIO_EV_TYPE_CHANGE] = "change",
 };
 
 static const char * const iio_ev_dir_text[] = {
@@ -179,6 +180,7 @@ static bool event_is_known(struct iio_event_data *event)
 	case IIO_EV_TYPE_THRESH_ADAPTIVE:
 	case IIO_EV_TYPE_MAG_ADAPTIVE:
 	case IIO_EV_TYPE_INSTANCE:
+	case IIO_EV_TYPE_CHANGE:
 		break;
 	default:
 		return false;
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index c3601c2..3ba3d66 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -79,6 +79,7 @@ enum iio_event_type {
 	IIO_EV_TYPE_THRESH_ADAPTIVE,
 	IIO_EV_TYPE_MAG_ADAPTIVE,
 	IIO_EV_TYPE_INSTANCE,
+	IIO_EV_TYPE_CHANGE,
 };
 
 enum iio_event_info {
-- 
1.9.1


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

* [PATCH v2 06/10] iio: core: Remove IIO_EV_TYPE_INSTANCE
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
                   ` (4 preceding siblings ...)
  2015-01-11 19:10 ` [PATCH v2 05/10] iio: core: Introduce CHANGE event type Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-26 19:04   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD Irina Tirdea
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

By introducing IIO_EV_TYPE_CHANGE, IIO_EV_TYPE_INSTANCE becomes redundant.
The effect of IIO_EV_TYPE_INSTANCE can be obtained by using IIO_EV_TYPE_CHANGE
with IIO_EV_INFO_VALUE set to 1.

Remove all instances of IIO_EV_TYPE_INSTANCE and replace them with
IIO_EV_TYPE_CHANGE where needed.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/iio/industrialio-event.c                      | 1 -
 drivers/staging/iio/Documentation/iio_event_monitor.c | 2 --
 drivers/staging/iio/iio_simple_dummy.c                | 2 +-
 drivers/staging/iio/iio_simple_dummy_events.c         | 4 ++--
 include/linux/iio/types.h                             | 1 -
 5 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 08c4a4c..b5663f6 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -197,7 +197,6 @@ static const char * const iio_ev_type_text[] = {
 	[IIO_EV_TYPE_ROC] = "roc",
 	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
 	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
-	[IIO_EV_TYPE_INSTANCE] = "instance",
 	[IIO_EV_TYPE_CHANGE] = "change",
 };
 
diff --git a/drivers/staging/iio/Documentation/iio_event_monitor.c b/drivers/staging/iio/Documentation/iio_event_monitor.c
index 2e78d58..72c96aa 100644
--- a/drivers/staging/iio/Documentation/iio_event_monitor.c
+++ b/drivers/staging/iio/Documentation/iio_event_monitor.c
@@ -59,7 +59,6 @@ static const char * const iio_ev_type_text[] = {
 	[IIO_EV_TYPE_ROC] = "roc",
 	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
 	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
-	[IIO_EV_TYPE_INSTANCE] = "instance",
 	[IIO_EV_TYPE_CHANGE] = "change",
 };
 
@@ -179,7 +178,6 @@ static bool event_is_known(struct iio_event_data *event)
 	case IIO_EV_TYPE_ROC:
 	case IIO_EV_TYPE_THRESH_ADAPTIVE:
 	case IIO_EV_TYPE_MAG_ADAPTIVE:
-	case IIO_EV_TYPE_INSTANCE:
 	case IIO_EV_TYPE_CHANGE:
 		break;
 	default:
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
index 0b8611a..e452021 100644
--- a/drivers/staging/iio/iio_simple_dummy.c
+++ b/drivers/staging/iio/iio_simple_dummy.c
@@ -73,7 +73,7 @@ static const struct iio_event_spec iio_dummy_event = {
  * simple step detect event - triggered when a step is detected
  */
 static const struct iio_event_spec step_detect_event = {
-	.type = IIO_EV_TYPE_INSTANCE,
+	.type = IIO_EV_TYPE_CHANGE,
 	.dir = IIO_EV_DIR_NONE,
 	.mask_separate = BIT(IIO_EV_INFO_ENABLE),
 };
diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c
index ac15a44..a5cd3bb 100644
--- a/drivers/staging/iio/iio_simple_dummy_events.c
+++ b/drivers/staging/iio/iio_simple_dummy_events.c
@@ -86,7 +86,7 @@ int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
 		}
 	case IIO_STEPS:
 		switch (type) {
-		case IIO_EV_TYPE_INSTANCE:
+		case IIO_EV_TYPE_CHANGE:
 			st->event_en = state;
 			break;
 		default:
@@ -201,7 +201,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
 		iio_push_event(indio_dev,
 			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
 					      IIO_EV_DIR_NONE,
-					      IIO_EV_TYPE_INSTANCE, 0, 0, 0),
+					      IIO_EV_TYPE_CHANGE, 0, 0, 0),
 			       iio_get_time_ns());
 		break;
 	default:
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 3ba3d66..580ed5b 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -78,7 +78,6 @@ enum iio_event_type {
 	IIO_EV_TYPE_ROC,
 	IIO_EV_TYPE_THRESH_ADAPTIVE,
 	IIO_EV_TYPE_MAG_ADAPTIVE,
-	IIO_EV_TYPE_INSTANCE,
 	IIO_EV_TYPE_CHANGE,
 };
 
-- 
1.9.1


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

* [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
                   ` (5 preceding siblings ...)
  2015-01-11 19:10 ` [PATCH v2 06/10] iio: core: Remove IIO_EV_TYPE_INSTANCE Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-25 23:07   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support Irina Tirdea
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

The pedometer needs to filter out false steps that might be generated by
tapping the foot, sitting, etc. To do that it computes the number of
steps that occur in a given time and decides the user is moving only
if this value is over a threshold. E.g.: the user starts moving only
if he takes 4 steps in 3 seconds. This filter is applied only when
the user starts moving.

A device that has such pedometer functionality is Freescale's MMA9553L:
http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf.

To export this feature, this patch introduces
IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD.
For the pedometer, in_steps_filter_outlier_thresh will specify the number of
steps that need to occur in in_steps_filter_outlier_period seconds so that
the pedometer decides the user is moving.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 Documentation/ABI/testing/sysfs-bus-iio | 21 +++++++++++++++++++++
 drivers/iio/industrialio-core.c         |  2 ++
 include/linux/iio/iio.h                 |  2 ++
 3 files changed, 25 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index c03a140..e009f49 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -1193,3 +1193,24 @@ Description:
 		This attribute is used to read the current speed value of the
 		user (which is the norm or magnitude of the velocity vector).
 		Units after application of scale are m/s.
+
+What:		/sys/.../iio:deviceX/in_steps_filter_outliers_period
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Specifies the number of seconds in which we compute the
+		values so we can decide if they are outlier values and
+		need to be filter out. These computed values are then
+		compared with in_steps_filter_outliers_thresh. E.g. for steps:
+		specifies number of seconds in which we compute the steps
+		that occur in order to decide if the consumer is making steps.
+
+What:		/sys/.../iio:deviceX/in_steps_filter_outliers_thresh
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Specifies a threshold for filtering outlier values: if value
+		measured in in_steps_filter_outliers_period seconds is below
+		threshold, we filter it out. E.g. for steps: specifies number
+		of steps that must occur within in_steps_filter_outliers_period
+		for the pedometer to decide the consumer is making steps.
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 4ee6fdf..81678b3 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -126,6 +126,8 @@ static const char * const iio_chan_info_postfix[] = {
 	[IIO_CHAN_INFO_ENABLE] = "en",
 	[IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
 	[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
+	[IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH] = "filter_outliers_thresh",
+	[IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD] = "filter_outliers_period",
 };
 
 /**
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 752a929..b6b12ac 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -41,6 +41,8 @@ enum iio_chan_info_enum {
 	IIO_CHAN_INFO_ENABLE,
 	IIO_CHAN_INFO_CALIBHEIGHT,
 	IIO_CHAN_INFO_CALIBWEIGHT,
+	IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH,
+	IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD,
 };
 
 enum iio_shared_by {
-- 
1.9.1


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

* [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
                   ` (6 preceding siblings ...)
  2015-01-11 19:10 ` [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-26 19:08   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 09/10] iio: accel: mma9551: split driver to expose mma955x api Irina Tirdea
  2015-01-11 19:10 ` [PATCH v2 10/10] iio: add driver for Freescale MMA9553 Irina Tirdea
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

Add support for runtime pm to reduce the power consumed by the device
when not used.

If CONFIG_PM is not enabled, the device will be powered on at
init and only powered off on system suspend.

If CONFIG_PM is enabled, runtime pm autosuspend is used:
- for raw reads will keep the device on for a specified time
- for events it will keep the device on as long as we have at least
one event active

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Reviewed-by: Vlad Dogaru <vlad.dogaru@intel.com>
---
 drivers/iio/accel/mma9551.c | 162 +++++++++++++++++++++++++++++++++++++-------
 1 file changed, 139 insertions(+), 23 deletions(-)

diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
index 6563e26..f1a5a06 100644
--- a/drivers/iio/accel/mma9551.c
+++ b/drivers/iio/accel/mma9551.c
@@ -22,6 +22,7 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 #include <linux/iio/events.h>
+#include <linux/pm_runtime.h>
 
 #define MMA9551_DRV_NAME		"mma9551"
 #define MMA9551_IRQ_NAME		"mma9551_event"
@@ -71,6 +72,7 @@ enum mma9551_gpio_pin {
 /* Sleep/Wake application */
 #define MMA9551_SLEEP_CFG		0x06
 #define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
+#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
 #define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
 
 /* AFE application */
@@ -114,6 +116,9 @@ enum mma9551_tilt_axis {
 #define MMA9551_I2C_READ_RETRIES	5
 #define MMA9551_I2C_READ_DELAY	50	/* us */
 
+#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
+#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
+
 struct mma9551_mbox_request {
 	u8 start_mbox;		/* Always 0. */
 	u8 app_id;
@@ -387,16 +392,55 @@ static int mma9551_read_version(struct i2c_client *client)
 }
 
 /*
+ * Power on chip and enable doze mode.
  * Use 'false' as the second parameter to cause the device to enter
  * sleep.
  */
-static int mma9551_set_device_state(struct i2c_client *client,
-				    bool enable)
+static int mma9551_set_device_state(struct i2c_client *client, bool enable)
 {
 	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
 					  MMA9551_SLEEP_CFG,
-					  MMA9551_SLEEP_CFG_SNCEN,
-					  enable ? 0 : MMA9551_SLEEP_CFG_SNCEN);
+					  MMA9551_SLEEP_CFG_SNCEN |
+					  MMA9551_SLEEP_CFG_FLEEN |
+					  MMA9551_SLEEP_CFG_SCHEN,
+					  enable ? MMA9551_SLEEP_CFG_SCHEN |
+					  MMA9551_SLEEP_CFG_FLEEN :
+					  MMA9551_SLEEP_CFG_SNCEN);
+}
+
+static int mma9551_set_power_state(struct i2c_client *client, bool on)
+{
+#ifdef CONFIG_PM
+	int ret;
+
+	if (on)
+		ret = pm_runtime_get_sync(&client->dev);
+	else {
+		pm_runtime_mark_last_busy(&client->dev);
+		ret = pm_runtime_put_autosuspend(&client->dev);
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed to change power state to %d\n", on);
+		if (on)
+			pm_runtime_put_noidle(&client->dev);
+
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static void mma9551_sleep(int freq)
+{
+	int sleep_val = 1000 / freq;
+
+	if (sleep_val < 20)
+		usleep_range(sleep_val * 1000, 20000);
+	else
+		msleep_interruptible(sleep_val);
 }
 
 static int mma9551_read_incli_chan(struct i2c_client *client,
@@ -424,15 +468,19 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
 		return -EINVAL;
 	}
 
+	ret = mma9551_set_power_state(client, true);
+	if (ret < 0)
+		return ret;
+
 	ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
 				       reg_addr, &angle);
 	if (ret < 0)
-		return ret;
+		goto out_poweroff;
 
 	ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
 				       MMA9551_TILT_QUAD_REG, &quadrant);
 	if (ret < 0)
-		return ret;
+		goto out_poweroff;
 
 	angle &= ~MMA9551_TILT_ANGFLG;
 	quadrant = (quadrant >> quad_shift) & 0x03;
@@ -442,7 +490,11 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
 	else
 		*val = angle + 90 * quadrant;
 
-	return IIO_VAL_INT;
+	ret = IIO_VAL_INT;
+
+out_poweroff:
+	mma9551_set_power_state(client, false);
+	return ret;
 }
 
 static int mma9551_read_accel_chan(struct i2c_client *client,
@@ -467,14 +519,22 @@ static int mma9551_read_accel_chan(struct i2c_client *client,
 		return -EINVAL;
 	}
 
+	ret = mma9551_set_power_state(client, true);
+	if (ret < 0)
+		return ret;
+
 	ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
 				       reg_addr, &raw_accel);
 	if (ret < 0)
-		return ret;
+		goto out_poweroff;
 
 	*val = raw_accel;
 
-	return IIO_VAL_INT;
+	ret = IIO_VAL_INT;
+
+out_poweroff:
+	mma9551_set_power_state(client, false);
+	return ret;
 }
 
 static int mma9551_read_raw(struct iio_dev *indio_dev,
@@ -556,6 +616,10 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
 					  MMA9551_APPID_NONE, 0, 0);
 		if (ret < 0)
 			return ret;
+
+		ret = mma9551_set_power_state(data->client, false);
+		if (ret < 0)
+			return ret;
 	} else {
 		int bitnum;
 
@@ -574,11 +638,18 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
 			return -EINVAL;
 		}
 
+
+		ret = mma9551_set_power_state(data->client, true);
+		if (ret < 0)
+			return ret;
+
 		ret = mma9551_gpio_config(data->client,
 					  (enum mma9551_gpio_pin)mma_axis,
 					  MMA9551_APPID_TILT, bitnum, 0);
-		if (ret < 0)
+		if (ret < 0) {
+			mma9551_set_power_state(data->client, false);
 			return ret;
+		}
 	}
 
 	data->event_enabled[mma_axis] = state;
@@ -771,12 +842,7 @@ static int mma9551_init(struct mma9551_data *data)
 	if (ret)
 		return ret;
 
-	/* Power on chip and enable doze mode. */
-	return mma9551_update_config_bits(data->client,
-			 MMA9551_APPID_SLEEP_WAKE,
-			 MMA9551_SLEEP_CFG,
-			 MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN,
-			 MMA9551_SLEEP_CFG_SCHEN);
+	return mma9551_set_device_state(data->client, true);
 }
 
 static int mma9551_gpio_probe(struct iio_dev *indio_dev)
@@ -869,8 +935,19 @@ static int mma9551_probe(struct i2c_client *client,
 		goto out_poweroff;
 	}
 
+	ret = pm_runtime_set_active(&client->dev);
+	if (ret < 0)
+		goto out_iio_unregister;
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev,
+					 MMA9551_AUTO_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(&client->dev);
+
 	return 0;
 
+out_iio_unregister:
+	iio_device_unregister(indio_dev);
 out_poweroff:
 	mma9551_set_device_state(client, false);
 
@@ -882,6 +959,10 @@ static int mma9551_remove(struct i2c_client *client)
 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
 	struct mma9551_data *data = iio_priv(indio_dev);
 
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+
 	iio_device_unregister(indio_dev);
 	mutex_lock(&data->mutex);
 	mma9551_set_device_state(data->client, false);
@@ -890,37 +971,72 @@ static int mma9551_remove(struct i2c_client *client)
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int mma9551_runtime_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mma9551_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = mma9551_set_device_state(data->client, false);
+	mutex_unlock(&data->mutex);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "powering off device failed\n");
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static int mma9551_runtime_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mma9551_data *data = iio_priv(indio_dev);
+	int ret;
+
+	ret = mma9551_set_device_state(data->client, true);
+	if (ret < 0)
+		return ret;
+
+	mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
+
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 static int mma9551_suspend(struct device *dev)
 {
 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
 	struct mma9551_data *data = iio_priv(indio_dev);
+	int ret;
 
 	mutex_lock(&data->mutex);
-	mma9551_set_device_state(data->client, false);
+	ret = mma9551_set_device_state(data->client, false);
 	mutex_unlock(&data->mutex);
 
-	return 0;
+	return ret;
 }
 
 static int mma9551_resume(struct device *dev)
 {
 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
 	struct mma9551_data *data = iio_priv(indio_dev);
+	int ret;
 
 	mutex_lock(&data->mutex);
-	mma9551_set_device_state(data->client, true);
+	ret = mma9551_set_device_state(data->client, true);
 	mutex_unlock(&data->mutex);
 
-	return 0;
+	return ret;
 }
-#else
-#define mma9551_suspend NULL
-#define mma9551_resume NULL
 #endif
 
 static const struct dev_pm_ops mma9551_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
+	SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
+			   mma9551_runtime_resume, NULL)
 };
 
 static const struct acpi_device_id mma9551_acpi_match[] = {
-- 
1.9.1


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

* [PATCH v2 09/10] iio: accel: mma9551: split driver to expose mma955x api
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
                   ` (7 preceding siblings ...)
  2015-01-11 19:10 ` [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-26 19:25   ` Jonathan Cameron
  2015-01-11 19:10 ` [PATCH v2 10/10] iio: add driver for Freescale MMA9553 Irina Tirdea
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

Freescale has the MMA955xL family of devices that use the
same communication protocol (based on i2c messages):
http://www.freescale.com/files/sensors/doc/data_sheet/MMA955xL.pdf.

To support more devices from this family, we need to split the
mma9551 driver so we can export the common functions that will
be used by other mma955x drivers.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Reviewed-by: Vlad Dogaru <vlad.dogaru@intel.com>
---
 drivers/iio/accel/Kconfig        |   5 +
 drivers/iio/accel/Makefile       |   4 +-
 drivers/iio/accel/mma9551.c      | 443 +---------------------------
 drivers/iio/accel/mma9551_core.c | 615 +++++++++++++++++++++++++++++++++++++++
 drivers/iio/accel/mma9551_core.h |  66 +++++
 5 files changed, 693 insertions(+), 440 deletions(-)
 create mode 100644 drivers/iio/accel/mma9551_core.c
 create mode 100644 drivers/iio/accel/mma9551_core.h

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 9f67c10..c53047d 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -111,9 +111,14 @@ config KXCJK1013
 	  To compile this driver as a module, choose M here: the module will
 	  be called kxcjk-1013.
 
+config MMA9551_CORE
+	tristate
+
 config MMA9551
 	tristate "Freescale MMA9551L Intelligent Motion-Sensing Platform Driver"
 	depends on I2C
+	select MMA9551_CORE
+
 	help
 	  Say yes here to build support for the Freescale MMA9551L
 	  Intelligent Motion-Sensing Platform Driver.
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index de5b9cb..8105316 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -9,7 +9,9 @@ obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
 obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
 obj-$(CONFIG_KXSD9)	+= kxsd9.o
 obj-$(CONFIG_MMA8452)	+= mma8452.o
-obj-$(CONFIG_MMA9551)	+= mma9551.o
+
+obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
+obj-$(CONFIG_MMA9551)		+= mma9551.o
 
 obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
 st_accel-y := st_accel_core.o
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
index f1a5a06..46c3835 100644
--- a/drivers/iio/accel/mma9551.c
+++ b/drivers/iio/accel/mma9551.c
@@ -23,63 +23,13 @@
 #include <linux/iio/sysfs.h>
 #include <linux/iio/events.h>
 #include <linux/pm_runtime.h>
+#include "mma9551_core.h"
 
 #define MMA9551_DRV_NAME		"mma9551"
 #define MMA9551_IRQ_NAME		"mma9551_event"
 #define MMA9551_GPIO_NAME		"mma9551_int"
 #define MMA9551_GPIO_COUNT		4
 
-/* Applications IDs */
-#define MMA9551_APPID_VERSION		0x00
-#define MMA9551_APPID_GPIO		0x03
-#define MMA9551_APPID_AFE		0x06
-#define MMA9551_APPID_TILT		0x0B
-#define MMA9551_APPID_SLEEP_WAKE	0x12
-#define MMA9551_APPID_RESET		0x17
-#define MMA9551_APPID_NONE		0xff
-
-/* Command masks for mailbox write command */
-#define MMA9551_CMD_READ_VERSION_INFO	0x00
-#define MMA9551_CMD_READ_CONFIG		0x10
-#define MMA9551_CMD_WRITE_CONFIG	0x20
-#define MMA9551_CMD_READ_STATUS		0x30
-
-enum mma9551_gpio_pin {
-	mma9551_gpio6 = 0,
-	mma9551_gpio7,
-	mma9551_gpio8,
-	mma9551_gpio9,
-	mma9551_gpio_max = mma9551_gpio9,
-};
-
-/* Mailbox read command */
-#define MMA9551_RESPONSE_COCO		BIT(7)
-
-/* Error-Status codes returned in mailbox read command */
-#define MMA9551_MCI_ERROR_NONE			0x00
-#define MMA9551_MCI_ERROR_PARAM			0x04
-#define MMA9551_MCI_INVALID_COUNT		0x19
-#define MMA9551_MCI_ERROR_COMMAND		0x1C
-#define MMA9551_MCI_ERROR_INVALID_LENGTH	0x21
-#define MMA9551_MCI_ERROR_FIFO_BUSY		0x22
-#define MMA9551_MCI_ERROR_FIFO_ALLOCATED	0x23
-#define MMA9551_MCI_ERROR_FIFO_OVERSIZE		0x24
-
-/* GPIO Application */
-#define MMA9551_GPIO_POL_MSB		0x08
-#define MMA9551_GPIO_POL_LSB		0x09
-
-/* Sleep/Wake application */
-#define MMA9551_SLEEP_CFG		0x06
-#define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
-#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
-#define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
-
-/* AFE application */
-#define MMA9551_AFE_X_ACCEL_REG		0x00
-#define MMA9551_AFE_Y_ACCEL_REG		0x02
-#define MMA9551_AFE_Z_ACCEL_REG		0x04
-
 /* Tilt application (inclination in IIO terms). */
 #define MMA9551_TILT_XZ_ANG_REG		0x00
 #define MMA9551_TILT_YZ_ANG_REG		0x01
@@ -92,6 +42,8 @@ enum mma9551_gpio_pin {
 #define MMA9551_TILT_CFG_REG		0x01
 #define MMA9551_TILT_ANG_THRESH_MASK	GENMASK(3, 0)
 
+#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
+
 /* Tilt events are mapped to the first three GPIO pins. */
 enum mma9551_tilt_axis {
 	mma9551_x = 0,
@@ -99,64 +51,6 @@ enum mma9551_tilt_axis {
 	mma9551_z,
 };
 
-/*
- * A response is composed of:
- * - control registers: MB0-3
- * - data registers: MB4-31
- *
- * A request is composed of:
- * - mbox to write to (always 0)
- * - control registers: MB1-4
- * - data registers: MB5-31
- */
-#define MMA9551_MAILBOX_CTRL_REGS	4
-#define MMA9551_MAX_MAILBOX_DATA_REGS	28
-#define MMA9551_MAILBOX_REGS		32
-
-#define MMA9551_I2C_READ_RETRIES	5
-#define MMA9551_I2C_READ_DELAY	50	/* us */
-
-#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
-#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
-
-struct mma9551_mbox_request {
-	u8 start_mbox;		/* Always 0. */
-	u8 app_id;
-	/*
-	 * See Section 5.3.1 of the MMA955xL Software Reference Manual.
-	 *
-	 * Bit 7: reserved, always 0
-	 * Bits 6-4: command
-	 * Bits 3-0: upper bits of register offset
-	 */
-	u8 cmd_off;
-	u8 lower_off;
-	u8 nbytes;
-	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
-} __packed;
-
-struct mma9551_mbox_response {
-	u8 app_id;
-	/*
-	 * See Section 5.3.3 of the MMA955xL Software Reference Manual.
-	 *
-	 * Bit 7: COCO
-	 * Bits 6-0: Error code.
-	 */
-	u8 coco_err;
-	u8 nbytes;
-	u8 req_bytes;
-	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
-} __packed;
-
-struct mma9551_version_info {
-	__be32 device_id;
-	u8 rom_version[2];
-	u8 fw_version[2];
-	u8 hw_version[2];
-	u8 fw_build[2];
-};
-
 struct mma9551_data {
 	struct i2c_client *client;
 	struct mutex mutex;
@@ -164,285 +58,6 @@ struct mma9551_data {
 	int irqs[MMA9551_GPIO_COUNT];
 };
 
-static int mma9551_transfer(struct i2c_client *client,
-			    u8 app_id, u8 command, u16 offset,
-			    u8 *inbytes, int num_inbytes,
-			    u8 *outbytes, int num_outbytes)
-{
-	struct mma9551_mbox_request req;
-	struct mma9551_mbox_response rsp;
-	struct i2c_msg in, out;
-	u8 req_len, err_code;
-	int ret, retries;
-
-	if (offset >= 1 << 12) {
-		dev_err(&client->dev, "register offset too large\n");
-		return -EINVAL;
-	}
-
-	req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
-	req.start_mbox = 0;
-	req.app_id = app_id;
-	req.cmd_off = command | (offset >> 8);
-	req.lower_off = offset;
-
-	if (command == MMA9551_CMD_WRITE_CONFIG)
-		req.nbytes = num_inbytes;
-	else
-		req.nbytes = num_outbytes;
-	if (num_inbytes)
-		memcpy(req.buf, inbytes, num_inbytes);
-
-	out.addr = client->addr;
-	out.flags = 0;
-	out.len = req_len;
-	out.buf = (u8 *)&req;
-
-	ret = i2c_transfer(client->adapter, &out, 1);
-	if (ret < 0) {
-		dev_err(&client->dev, "i2c write failed\n");
-		return ret;
-	}
-
-	retries = MMA9551_I2C_READ_RETRIES;
-	do {
-		udelay(MMA9551_I2C_READ_DELAY);
-
-		in.addr = client->addr;
-		in.flags = I2C_M_RD;
-		in.len = sizeof(rsp);
-		in.buf = (u8 *)&rsp;
-
-		ret = i2c_transfer(client->adapter, &in, 1);
-		if (ret < 0) {
-			dev_err(&client->dev, "i2c read failed\n");
-			return ret;
-		}
-
-		if (rsp.coco_err & MMA9551_RESPONSE_COCO)
-			break;
-	} while (--retries > 0);
-
-	if (retries == 0) {
-		dev_err(&client->dev,
-			"timed out while waiting for command response\n");
-		return -ETIMEDOUT;
-	}
-
-	if (rsp.app_id != app_id) {
-		dev_err(&client->dev,
-			"app_id mismatch in response got %02x expected %02x\n",
-			rsp.app_id, app_id);
-		return -EINVAL;
-	}
-
-	err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
-	if (err_code != MMA9551_MCI_ERROR_NONE) {
-		dev_err(&client->dev, "read returned error %x\n", err_code);
-		return -EINVAL;
-	}
-
-	if (rsp.nbytes != rsp.req_bytes) {
-		dev_err(&client->dev,
-			"output length mismatch got %d expected %d\n",
-			rsp.nbytes, rsp.req_bytes);
-		return -EINVAL;
-	}
-
-	if (num_outbytes)
-		memcpy(outbytes, rsp.buf, num_outbytes);
-
-	return 0;
-}
-
-static int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
-				    u16 reg, u8 *val)
-{
-	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
-				reg, NULL, 0, val, 1);
-}
-
-static int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
-				     u16 reg, u8 val)
-{
-	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
-				&val, 1, NULL, 0);
-}
-
-static int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
-				    u16 reg, u8 *val)
-{
-	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
-				reg, NULL, 0, val, 1);
-}
-
-static int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
-				    u16 reg, u16 *val)
-{
-	int ret;
-	__be16 v;
-
-	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
-			       reg, NULL, 0, (u8 *)&v, 2);
-	*val = be16_to_cpu(v);
-
-	return ret;
-}
-
-static int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
-				      u16 reg, u8 mask, u8 val)
-{
-	int ret;
-	u8 tmp, orig;
-
-	ret = mma9551_read_config_byte(client, app_id, reg, &orig);
-	if (ret < 0)
-		return ret;
-
-	tmp = orig & ~mask;
-	tmp |= val & mask;
-
-	if (tmp == orig)
-		return 0;
-
-	return mma9551_write_config_byte(client, app_id, reg, tmp);
-}
-
-/*
- * The polarity parameter is described in section 6.2.2, page 66, of the
- * Software Reference Manual.  Basically, polarity=0 means the interrupt
- * line has the same value as the selected bit, while polarity=1 means
- * the line is inverted.
- */
-static int mma9551_gpio_config(struct i2c_client *client,
-			       enum mma9551_gpio_pin pin,
-			       u8 app_id, u8 bitnum, int polarity)
-{
-	u8 reg, pol_mask, pol_val;
-	int ret;
-
-	if (pin > mma9551_gpio_max) {
-		dev_err(&client->dev, "bad GPIO pin\n");
-		return -EINVAL;
-	}
-
-	/*
-	 * Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
-	 * 0x03, and so on.
-	 */
-	reg = pin * 2;
-
-	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
-					reg, app_id);
-	if (ret < 0) {
-		dev_err(&client->dev, "error setting GPIO app_id\n");
-		return ret;
-	}
-
-	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
-					reg + 1, bitnum);
-	if (ret < 0) {
-		dev_err(&client->dev, "error setting GPIO bit number\n");
-		return ret;
-	}
-
-	switch (pin) {
-	case mma9551_gpio6:
-		reg = MMA9551_GPIO_POL_LSB;
-		pol_mask = 1 << 6;
-		break;
-	case mma9551_gpio7:
-		reg = MMA9551_GPIO_POL_LSB;
-		pol_mask = 1 << 7;
-		break;
-	case mma9551_gpio8:
-		reg = MMA9551_GPIO_POL_MSB;
-		pol_mask = 1 << 0;
-		break;
-	case mma9551_gpio9:
-		reg = MMA9551_GPIO_POL_MSB;
-		pol_mask = 1 << 1;
-		break;
-	}
-	pol_val = polarity ? pol_mask : 0;
-
-	ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
-					 pol_mask, pol_val);
-	if (ret < 0)
-		dev_err(&client->dev, "error setting GPIO polarity\n");
-
-	return ret;
-}
-
-static int mma9551_read_version(struct i2c_client *client)
-{
-	struct mma9551_version_info info;
-	int ret;
-
-	ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
-			       NULL, 0, (u8 *)&info, sizeof(info));
-	if (ret < 0)
-		return ret;
-
-	dev_info(&client->dev, "Device ID 0x%x, firmware version %02x.%02x\n",
-		 be32_to_cpu(info.device_id), info.fw_version[0],
-		 info.fw_version[1]);
-
-	return 0;
-}
-
-/*
- * Power on chip and enable doze mode.
- * Use 'false' as the second parameter to cause the device to enter
- * sleep.
- */
-static int mma9551_set_device_state(struct i2c_client *client, bool enable)
-{
-	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
-					  MMA9551_SLEEP_CFG,
-					  MMA9551_SLEEP_CFG_SNCEN |
-					  MMA9551_SLEEP_CFG_FLEEN |
-					  MMA9551_SLEEP_CFG_SCHEN,
-					  enable ? MMA9551_SLEEP_CFG_SCHEN |
-					  MMA9551_SLEEP_CFG_FLEEN :
-					  MMA9551_SLEEP_CFG_SNCEN);
-}
-
-static int mma9551_set_power_state(struct i2c_client *client, bool on)
-{
-#ifdef CONFIG_PM
-	int ret;
-
-	if (on)
-		ret = pm_runtime_get_sync(&client->dev);
-	else {
-		pm_runtime_mark_last_busy(&client->dev);
-		ret = pm_runtime_put_autosuspend(&client->dev);
-	}
-
-	if (ret < 0) {
-		dev_err(&client->dev,
-			"failed to change power state to %d\n", on);
-		if (on)
-			pm_runtime_put_noidle(&client->dev);
-
-		return ret;
-	}
-#endif
-
-	return 0;
-}
-
-static void mma9551_sleep(int freq)
-{
-	int sleep_val = 1000 / freq;
-
-	if (sleep_val < 20)
-		usleep_range(sleep_val * 1000, 20000);
-	else
-		msleep_interruptible(sleep_val);
-}
-
 static int mma9551_read_incli_chan(struct i2c_client *client,
 				   const struct iio_chan_spec *chan,
 				   int *val)
@@ -497,46 +112,6 @@ out_poweroff:
 	return ret;
 }
 
-static int mma9551_read_accel_chan(struct i2c_client *client,
-				   const struct iio_chan_spec *chan,
-				   int *val, int *val2)
-{
-	u16 reg_addr;
-	s16 raw_accel;
-	int ret;
-
-	switch (chan->channel2) {
-	case IIO_MOD_X:
-		reg_addr = MMA9551_AFE_X_ACCEL_REG;
-		break;
-	case IIO_MOD_Y:
-		reg_addr = MMA9551_AFE_Y_ACCEL_REG;
-		break;
-	case IIO_MOD_Z:
-		reg_addr = MMA9551_AFE_Z_ACCEL_REG;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	ret = mma9551_set_power_state(client, true);
-	if (ret < 0)
-		return ret;
-
-	ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
-				       reg_addr, &raw_accel);
-	if (ret < 0)
-		goto out_poweroff;
-
-	*val = raw_accel;
-
-	ret = IIO_VAL_INT;
-
-out_poweroff:
-	mma9551_set_power_state(client, false);
-	return ret;
-}
-
 static int mma9551_read_raw(struct iio_dev *indio_dev,
 			    struct iio_chan_spec const *chan,
 			    int *val, int *val2, long mask)
@@ -569,9 +144,7 @@ static int mma9551_read_raw(struct iio_dev *indio_dev,
 	case IIO_CHAN_INFO_SCALE:
 		switch (chan->type) {
 		case IIO_ACCEL:
-			*val = 0;
-			*val2 = 2440;
-			return IIO_VAL_INT_PLUS_MICRO;
+			return mma9551_read_accel_scale(val, val2);
 		default:
 			return -EINVAL;
 		}
@@ -740,14 +313,6 @@ static const struct iio_event_spec mma9551_incli_event = {
 	.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
 };
 
-#define MMA9551_ACCEL_CHANNEL(axis) {				\
-	.type = IIO_ACCEL,					\
-	.modified = 1,						\
-	.channel2 = axis,					\
-	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
-	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
-}
-
 #define MMA9551_INCLI_CHANNEL(axis) {				\
 	.type = IIO_INCLI,					\
 	.modified = 1,						\
diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
new file mode 100644
index 0000000..7f1a73e
--- /dev/null
+++ b/drivers/iio/accel/mma9551_core.c
@@ -0,0 +1,615 @@
+/*
+ * Common code for Freescale MMA955x Intelligent Sensor Platform drivers
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope 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/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/pm_runtime.h>
+#include "mma9551_core.h"
+
+/* Command masks for mailbox write command */
+#define MMA9551_CMD_READ_VERSION_INFO	0x00
+#define MMA9551_CMD_READ_CONFIG		0x10
+#define MMA9551_CMD_WRITE_CONFIG	0x20
+#define MMA9551_CMD_READ_STATUS		0x30
+
+/* Mailbox read command */
+#define MMA9551_RESPONSE_COCO		BIT(7)
+
+/* Error-Status codes returned in mailbox read command */
+#define MMA9551_MCI_ERROR_NONE			0x00
+#define MMA9551_MCI_ERROR_PARAM			0x04
+#define MMA9551_MCI_INVALID_COUNT		0x19
+#define MMA9551_MCI_ERROR_COMMAND		0x1C
+#define MMA9551_MCI_ERROR_INVALID_LENGTH	0x21
+#define MMA9551_MCI_ERROR_FIFO_BUSY		0x22
+#define MMA9551_MCI_ERROR_FIFO_ALLOCATED	0x23
+#define MMA9551_MCI_ERROR_FIFO_OVERSIZE		0x24
+
+/* GPIO Application */
+#define MMA9551_GPIO_POL_MSB		0x08
+#define MMA9551_GPIO_POL_LSB		0x09
+
+/* Sleep/Wake application */
+#define MMA9551_SLEEP_CFG		0x06
+#define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
+#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
+#define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
+
+/* AFE application */
+#define MMA9551_AFE_X_ACCEL_REG		0x00
+#define MMA9551_AFE_Y_ACCEL_REG		0x02
+#define MMA9551_AFE_Z_ACCEL_REG		0x04
+
+/*
+ * A response is composed of:
+ * - control registers: MB0-3
+ * - data registers: MB4-31
+ *
+ * A request is composed of:
+ * - mbox to write to (always 0)
+ * - control registers: MB1-4
+ * - data registers: MB5-31
+ */
+#define MMA9551_MAILBOX_CTRL_REGS	4
+#define MMA9551_MAX_MAILBOX_DATA_REGS	28
+#define MMA9551_MAILBOX_REGS		32
+
+#define MMA9551_I2C_READ_RETRIES	5
+#define MMA9551_I2C_READ_DELAY	50	/* us */
+
+struct mma9551_mbox_request {
+	u8 start_mbox;		/* Always 0. */
+	u8 app_id;
+	/*
+	 * See Section 5.3.1 of the MMA955xL Software Reference Manual.
+	 *
+	 * Bit 7: reserved, always 0
+	 * Bits 6-4: command
+	 * Bits 3-0: upper bits of register offset
+	 */
+	u8 cmd_off;
+	u8 lower_off;
+	u8 nbytes;
+	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
+} __packed;
+
+struct mma9551_mbox_response {
+	u8 app_id;
+	/*
+	 * See Section 5.3.3 of the MMA955xL Software Reference Manual.
+	 *
+	 * Bit 7: COCO
+	 * Bits 6-0: Error code.
+	 */
+	u8 coco_err;
+	u8 nbytes;
+	u8 req_bytes;
+	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+} __packed;
+
+struct mma9551_version_info {
+	__be32 device_id;
+	u8 rom_version[2];
+	u8 fw_version[2];
+	u8 hw_version[2];
+	u8 fw_build[2];
+};
+
+static int mma9551_transfer(struct i2c_client *client,
+			    u8 app_id, u8 command, u16 offset,
+			    u8 *inbytes, int num_inbytes,
+			    u8 *outbytes, int num_outbytes)
+{
+	struct mma9551_mbox_request req;
+	struct mma9551_mbox_response rsp;
+	struct i2c_msg in, out;
+	u8 req_len, err_code;
+	int ret, retries;
+
+	if (offset >= 1 << 12) {
+		dev_err(&client->dev, "register offset too large\n");
+		return -EINVAL;
+	}
+
+	req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
+	req.start_mbox = 0;
+	req.app_id = app_id;
+	req.cmd_off = command | (offset >> 8);
+	req.lower_off = offset;
+
+	if (command == MMA9551_CMD_WRITE_CONFIG)
+		req.nbytes = num_inbytes;
+	else
+		req.nbytes = num_outbytes;
+	if (num_inbytes)
+		memcpy(req.buf, inbytes, num_inbytes);
+
+	out.addr = client->addr;
+	out.flags = 0;
+	out.len = req_len;
+	out.buf = (u8 *)&req;
+
+	ret = i2c_transfer(client->adapter, &out, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "i2c write failed\n");
+		return ret;
+	}
+
+	retries = MMA9551_I2C_READ_RETRIES;
+	do {
+		udelay(MMA9551_I2C_READ_DELAY);
+
+		in.addr = client->addr;
+		in.flags = I2C_M_RD;
+		in.len = sizeof(rsp);
+		in.buf = (u8 *)&rsp;
+
+		ret = i2c_transfer(client->adapter, &in, 1);
+		if (ret < 0) {
+			dev_err(&client->dev, "i2c read failed\n");
+			return ret;
+		}
+
+		if (rsp.coco_err & MMA9551_RESPONSE_COCO)
+			break;
+	} while (--retries > 0);
+
+	if (retries == 0) {
+		dev_err(&client->dev,
+			"timed out while waiting for command response\n");
+		return -ETIMEDOUT;
+	}
+
+	if (rsp.app_id != app_id) {
+		dev_err(&client->dev,
+			"app_id mismatch in response got %02x expected %02x\n",
+			rsp.app_id, app_id);
+		return -EINVAL;
+	}
+
+	err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
+	if (err_code != MMA9551_MCI_ERROR_NONE) {
+		dev_err(&client->dev, "read returned error %x\n", err_code);
+		return -EINVAL;
+	}
+
+	if (rsp.nbytes != rsp.req_bytes) {
+		dev_err(&client->dev,
+			"output length mismatch got %d expected %d\n",
+			rsp.nbytes, rsp.req_bytes);
+		return -EINVAL;
+	}
+
+	if (num_outbytes)
+		memcpy(outbytes, rsp.buf, num_outbytes);
+
+	return 0;
+}
+
+/**
+ * mma9551_read_config_byte() - read 1 configuration byte
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @val:	Pointer to store value read
+ *
+ * Read one configuration byte from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed
+ * by one or more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
+			     u16 reg, u8 *val)
+{
+	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
+				reg, NULL, 0, val, 1);
+}
+EXPORT_SYMBOL(mma9551_read_config_byte);
+
+/**
+ * mma9551_write_config_byte() - write 1 configuration byte
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @val:	Value to write
+ *
+ * Write one configuration byte from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
+			      u16 reg, u8 val)
+{
+	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
+				&val, 1, NULL, 0);
+}
+EXPORT_SYMBOL(mma9551_write_config_byte);
+
+/**
+ * mma9551_read_status_byte() - read 1 status byte
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @val:	Pointer to store value read
+ *
+ * Read one status byte from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
+			     u16 reg, u8 *val)
+{
+	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
+				reg, NULL, 0, val, 1);
+}
+EXPORT_SYMBOL(mma9551_read_status_byte);
+
+/**
+ * mma9551_read_status_word() - read 1 status word
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @val:	Pointer to store value read
+ *
+ * Read one status word from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
+			     u16 reg, u16 *val)
+{
+	int ret;
+	__be16 v;
+
+	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
+			       reg, NULL, 0, (u8 *)&v, 2);
+	*val = be16_to_cpu(v);
+
+	return ret;
+}
+EXPORT_SYMBOL(mma9551_read_status_word);
+
+/**
+ * mma9551_update_config_bits() - update bits in register
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @mask:	Mask for the bits to update
+ * @val:	Value of the bits to update
+ *
+ * Update bits in the given register using a bit mask.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
+			       u16 reg, u8 mask, u8 val)
+{
+	int ret;
+	u8 tmp, orig;
+
+	ret = mma9551_read_config_byte(client, app_id, reg, &orig);
+	if (ret < 0)
+		return ret;
+
+	tmp = orig & ~mask;
+	tmp |= val & mask;
+
+	if (tmp == orig)
+		return 0;
+
+	return mma9551_write_config_byte(client, app_id, reg, tmp);
+}
+EXPORT_SYMBOL(mma9551_update_config_bits);
+
+/**
+ * mma9551_gpio_config() - configure gpio
+ * @client:	I2C client
+ * @pin:	GPIO pin to configure
+ * @app_id:	Application ID
+ * @bitnum:	Bit number of status register being assigned to the GPIO pin.
+ * @polarity:	The polarity parameter is described in section 6.2.2, page 66,
+ *		of the Software Reference Manual.  Basically, polarity=0 means
+ *		the interrupt line has the same value as the selected bit,
+ *		while polarity=1 means the line is inverted.
+ *
+ * Assign a bit from an application’s status register to a specific GPIO pin.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
+			u8 app_id, u8 bitnum, int polarity)
+{
+	u8 reg, pol_mask, pol_val;
+	int ret;
+
+	if (pin > mma9551_gpio_max) {
+		dev_err(&client->dev, "bad GPIO pin\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
+	 * 0x03, and so on.
+	 */
+	reg = pin * 2;
+
+	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
+					reg, app_id);
+	if (ret < 0) {
+		dev_err(&client->dev, "error setting GPIO app_id\n");
+		return ret;
+	}
+
+	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
+					reg + 1, bitnum);
+	if (ret < 0) {
+		dev_err(&client->dev, "error setting GPIO bit number\n");
+		return ret;
+	}
+
+	switch (pin) {
+	case mma9551_gpio6:
+		reg = MMA9551_GPIO_POL_LSB;
+		pol_mask = 1 << 6;
+		break;
+	case mma9551_gpio7:
+		reg = MMA9551_GPIO_POL_LSB;
+		pol_mask = 1 << 7;
+		break;
+	case mma9551_gpio8:
+		reg = MMA9551_GPIO_POL_MSB;
+		pol_mask = 1 << 0;
+		break;
+	case mma9551_gpio9:
+		reg = MMA9551_GPIO_POL_MSB;
+		pol_mask = 1 << 1;
+		break;
+	}
+	pol_val = polarity ? pol_mask : 0;
+
+	ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
+					 pol_mask, pol_val);
+	if (ret < 0)
+		dev_err(&client->dev, "error setting GPIO polarity\n");
+
+	return ret;
+}
+EXPORT_SYMBOL(mma9551_gpio_config);
+
+/**
+ * mma9551_read_version() - read device version information
+ * @client:	I2C client
+ *
+ * Read version information and print device id and firmware version.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_version(struct i2c_client *client)
+{
+	struct mma9551_version_info info;
+	int ret;
+
+	ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
+			       NULL, 0, (u8 *)&info, sizeof(info));
+	if (ret < 0)
+		return ret;
+
+	dev_info(&client->dev, "device ID 0x%x, firmware version %02x.%02x\n",
+		 be32_to_cpu(info.device_id), info.fw_version[0],
+		 info.fw_version[1]);
+
+	return 0;
+}
+EXPORT_SYMBOL(mma9551_read_version);
+
+/**
+ * mma9551_set_device_state() - sets HW power mode
+ * @client:	I2C client
+ * @enable:	Use true to power on device, false to cause the device
+ *		to enter sleep.
+ *
+ * Set power on/off for device using the Sleep/Wake Application.
+ * When enable is true, power on chip and enable doze mode.
+ * When enable is false, enter sleep mode (device remains in the
+ * lowest-power mode).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_set_device_state(struct i2c_client *client, bool enable)
+{
+	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
+					  MMA9551_SLEEP_CFG,
+					  MMA9551_SLEEP_CFG_SNCEN |
+					  MMA9551_SLEEP_CFG_FLEEN |
+					  MMA9551_SLEEP_CFG_SCHEN,
+					  enable ? MMA9551_SLEEP_CFG_SCHEN |
+					  MMA9551_SLEEP_CFG_FLEEN :
+					  MMA9551_SLEEP_CFG_SNCEN);
+}
+EXPORT_SYMBOL(mma9551_set_device_state);
+
+/**
+ * mma9551_set_power_state() - sets runtime PM state
+ * @client:	I2C client
+ * @on:		Use true to power on device, false to power off
+ *
+ * Resume or suspend the device using Runtime PM.
+ * The device will suspend after the autosuspend delay.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_set_power_state(struct i2c_client *client, bool on)
+{
+#ifdef CONFIG_PM
+	int ret;
+
+	if (on)
+		ret = pm_runtime_get_sync(&client->dev);
+	else {
+		pm_runtime_mark_last_busy(&client->dev);
+		ret = pm_runtime_put_autosuspend(&client->dev);
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed to change power state to %d\n", on);
+		if (on)
+			pm_runtime_put_noidle(&client->dev);
+
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+EXPORT_SYMBOL(mma9551_set_power_state);
+
+/**
+ * mma9551_sleep() - sleep
+ * @freq:	Application frequency
+ *
+ * Firmware applications run at a certain frequency on the
+ * device. Sleep for one application cycle to make sure the
+ * application had time to run once and initialize set values.
+ */
+void mma9551_sleep(int freq)
+{
+	int sleep_val = 1000 / freq;
+
+	if (sleep_val < 20)
+		usleep_range(sleep_val * 1000, 20000);
+	else
+		msleep_interruptible(sleep_val);
+}
+EXPORT_SYMBOL(mma9551_sleep);
+
+/**
+ * mma9551_read_accel_chan() - read accelerometer channel
+ * @client:	I2C client
+ * @chan:	IIO channel
+ * @val:	Pointer to the accelerometer value read
+ * @val2:	Unused
+ *
+ * Read accelerometer value for the specified channel.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: IIO_VAL_INT on success, negative value on failure.
+ */
+int mma9551_read_accel_chan(struct i2c_client *client,
+			    const struct iio_chan_spec *chan,
+			    int *val, int *val2)
+{
+	u16 reg_addr;
+	s16 raw_accel;
+	int ret;
+
+	switch (chan->channel2) {
+	case IIO_MOD_X:
+		reg_addr = MMA9551_AFE_X_ACCEL_REG;
+		break;
+	case IIO_MOD_Y:
+		reg_addr = MMA9551_AFE_Y_ACCEL_REG;
+		break;
+	case IIO_MOD_Z:
+		reg_addr = MMA9551_AFE_Z_ACCEL_REG;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = mma9551_set_power_state(client, true);
+	if (ret < 0)
+		return ret;
+
+	ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
+				       reg_addr, &raw_accel);
+	if (ret < 0)
+		goto out_poweroff;
+
+	*val = raw_accel;
+
+	ret = IIO_VAL_INT;
+
+out_poweroff:
+	mma9551_set_power_state(client, false);
+	return ret;
+}
+EXPORT_SYMBOL(mma9551_read_accel_chan);
+
+/**
+ * mma9551_read_accel_scale() - read accelerometer scale
+ * @val:	Pointer to the accelerometer scale (int value)
+ * @val2:	Pointer to the accelerometer scale (micro value)
+ *
+ * Read accelerometer scale.
+ *
+ * Returns: IIO_VAL_INT_PLUS_MICRO.
+ */
+int mma9551_read_accel_scale(int *val, int *val2)
+{
+	*val = 0;
+	*val2 = 2440;
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(mma9551_read_accel_scale);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMA955xL sensors core");
diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
new file mode 100644
index 0000000..e6efd02
--- /dev/null
+++ b/drivers/iio/accel/mma9551_core.h
@@ -0,0 +1,66 @@
+/*
+ * Common code for Freescale MMA955x Intelligent Sensor Platform drivers
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope 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 _MMA9551_CORE_H_
+#define _MMA9551_CORE_H_
+
+/* Applications IDs */
+#define MMA9551_APPID_VERSION		0x00
+#define MMA9551_APPID_GPIO		0x03
+#define MMA9551_APPID_AFE		0x06
+#define MMA9551_APPID_TILT		0x0B
+#define MMA9551_APPID_SLEEP_WAKE	0x12
+#define MMA9551_APPID_RESET		0x17
+#define MMA9551_APPID_NONE		0xff
+
+#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
+
+enum mma9551_gpio_pin {
+	mma9551_gpio6 = 0,
+	mma9551_gpio7,
+	mma9551_gpio8,
+	mma9551_gpio9,
+	mma9551_gpio_max = mma9551_gpio9,
+};
+
+#define MMA9551_ACCEL_CHANNEL(axis) {				\
+	.type = IIO_ACCEL,					\
+	.modified = 1,						\
+	.channel2 = axis,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+}
+
+int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
+			     u16 reg, u8 *val);
+int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
+			      u16 reg, u8 val);
+int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
+			     u16 reg, u8 *val);
+int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
+			     u16 reg, u16 *val);
+int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
+			       u16 reg, u8 mask, u8 val);
+int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
+			u8 app_id, u8 bitnum, int polarity);
+int mma9551_read_version(struct i2c_client *client);
+int mma9551_set_device_state(struct i2c_client *client, bool enable);
+int mma9551_set_power_state(struct i2c_client *client, bool on);
+void mma9551_sleep(int freq);
+int mma9551_read_accel_chan(struct i2c_client *client,
+			    const struct iio_chan_spec *chan,
+			    int *val, int *val2);
+int mma9551_read_accel_scale(int *val, int *val2);
+
+#endif /* _MMA9551_CORE_H_ */
-- 
1.9.1


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

* [PATCH v2 10/10] iio: add driver for Freescale MMA9553
  2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
                   ` (8 preceding siblings ...)
  2015-01-11 19:10 ` [PATCH v2 09/10] iio: accel: mma9551: split driver to expose mma955x api Irina Tirdea
@ 2015-01-11 19:10 ` Irina Tirdea
  2015-01-26 20:44   ` Jonathan Cameron
  9 siblings, 1 reply; 33+ messages in thread
From: Irina Tirdea @ 2015-01-11 19:10 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald, Irina Tirdea

Add support for Freescale MMA9553L Intelligent Pedometer Platform.

The following functionalities are supported:
 - step counter (counts the number of steps using a HW register)
 - step detector (generates an iio event at every step the user takes)
 - activity recognition (rest, walking, jogging, running)
 - speed
 - calories
 - distance

To get accurate pedometer results, the user's height, weight and gender
need to be configured.

The specifications can be downloaded from:
http://www.freescale.com/files/sensors/doc/ref_manual/MMA955xLSWRM.pdf
http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 Documentation/ABI/testing/sysfs-bus-iio |   49 +-
 drivers/iio/accel/Kconfig               |   10 +
 drivers/iio/accel/Makefile              |    1 +
 drivers/iio/accel/mma9551_core.c        |  183 +++++
 drivers/iio/accel/mma9551_core.h        |   17 +-
 drivers/iio/accel/mma9553.c             | 1321 +++++++++++++++++++++++++++++++
 include/linux/iio/iio.h                 |   11 +
 7 files changed, 1587 insertions(+), 5 deletions(-)
 create mode 100644 drivers/iio/accel/mma9553.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index e009f49..732d018 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -343,7 +343,30 @@ Description:
 		production inaccuracies).  If shared across all channels,
 		<type>_calibscale is used.
 
-What:		/sys/bus/iio/devices/iio:deviceX/in_steps_calibheight
+What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
+What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
+What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
+What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Gender of the user (e.g.: male, female) used by some pedometers
+		to compute the stride length, distance, speed and activity
+		type.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
+What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Lists all available gender values (e.g.: male, female).
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
+What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
+What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
+What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
 KernelVersion:	3.19
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -818,6 +841,14 @@ What:		/sys/.../events/in_tempY_roc_falling_period
 What:		/sys/.../events/in_accel_x&y&z_mag_falling_period
 What:		/sys/.../events/in_intensity0_thresh_period
 What:		/sys/.../events/in_proximity0_thresh_period
+What:		/sys/.../events/in_activity_still_thresh_rising_period
+What:		/sys/.../events/in_activity_still_thresh_falling_period
+What:		/sys/.../events/in_activity_walking_thresh_rising_period
+What:		/sys/.../events/in_activity_walking_thresh_falling_period
+What:		/sys/.../events/in_activity_jogging_thresh_rising_period
+What:		/sys/.../events/in_activity_jogging_thresh_falling_period
+What:		/sys/.../events/in_activity_running_thresh_rising_period
+What:		/sys/.../events/in_activity_running_thresh_falling_period
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -1142,6 +1173,12 @@ Description:
 		This attribute is used to get/set the integration time in
 		seconds.
 
+What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
+KernelVersion:	3.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Number of seconds in which to compute speed.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
 KernelVersion:	3.15
 Contact:	linux-iio@vger.kernel.org
@@ -1170,13 +1207,17 @@ Description:
 		present, output should be considered as processed with the
 		unit in milliamps.
 
+What:		/sys/.../iio:deviceX/in_energy_en
+What:		/sys/.../iio:deviceX/in_distance_en
+What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
 What:		/sys/.../iio:deviceX/in_steps_en
 KernelVersion:	3.19
 Contact:	linux-iio@vger.kernel.org
 Description:
-		Activates the step counter. After activation, the number of steps
-		taken by the user will be counted in hardware and exported through
-		in_steps_input.
+		Activates a device feature that runs in firmware/hardware.
+		E.g. for steps: the pedometer saves power while not used;
+		when activated, it will count the steps taken by the user in
+		firmware and export them through in_steps_input.
 
 What:		/sys/.../iio:deviceX/in_steps_input
 KernelVersion:	3.19
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index c53047d..7c9a9a9 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -126,4 +126,14 @@ config MMA9551
 	  To compile this driver as a module, choose M here: the module
 	  will be called mma9551.
 
+config MMA9553
+	tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
+	depends on I2C
+	select MMA9551_CORE
+	help
+	  Say yes here to build support for the Freescale MMA9553L
+	  Intelligent Pedometer Platform Driver.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called mma9553.
 endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 8105316..f815695 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_MMA8452)	+= mma8452.o
 
 obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
 obj-$(CONFIG_MMA9551)		+= mma9551.o
+obj-$(CONFIG_MMA9553)		+= mma9553.o
 
 obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
 st_accel-y := st_accel_core.o
diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
index 7f1a73e..7f55a6d 100644
--- a/drivers/iio/accel/mma9551_core.c
+++ b/drivers/iio/accel/mma9551_core.c
@@ -53,6 +53,11 @@
 #define MMA9551_AFE_Y_ACCEL_REG		0x02
 #define MMA9551_AFE_Z_ACCEL_REG		0x04
 
+/* Reset/Suspend/Clear application */
+#define MMA9551_RSC_RESET		0x00
+#define MMA9551_RSC_OFFSET(mask)	(3 - (ffs(mask) - 1) / 8)
+#define MMA9551_RSC_VAL(mask)		(mask >> (((ffs(mask) - 1) / 8) * 8))
+
 /*
  * A response is composed of:
  * - control registers: MB0-3
@@ -275,6 +280,64 @@ int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
 EXPORT_SYMBOL(mma9551_read_status_byte);
 
 /**
+ * mma9551_read_config_word() - read 1 config word
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @val:	Pointer to store value read
+ *
+ * Read one configuration word from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
+			    u16 reg, u16 *val)
+{
+	int ret;
+	__be16 v;
+
+	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
+			       reg, NULL, 0, (u8 *)&v, 2);
+	*val = be16_to_cpu(v);
+
+	return ret;
+}
+EXPORT_SYMBOL(mma9551_read_config_word);
+
+/**
+ * mma9551_write_config_word() - write 1 config word
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @val:	Value to write
+ *
+ * Write one configuration word from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
+			     u16 reg, u16 val)
+{
+	__be16 v = cpu_to_be16(val);
+
+	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
+				(u8 *) &v, 2, NULL, 0);
+}
+EXPORT_SYMBOL(mma9551_write_config_word);
+
+/**
  * mma9551_read_status_word() - read 1 status word
  * @client:	I2C client
  * @app_id:	Application ID
@@ -306,6 +369,107 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
 EXPORT_SYMBOL(mma9551_read_status_word);
 
 /**
+ * mma9551_read_config_words() - read multiple config words
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @len:	Length of array to read in bytes
+ * @val:	Array of words to read
+ *
+ * Read multiple configuration registers (word-sized registers).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
+			     u16 reg, u8 len, u16 *buf)
+{
+	int ret, i;
+	int len_words = len / sizeof(u16);
+	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+
+	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
+			       reg, NULL, 0, (u8 *) be_buf, len);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < len_words; i++)
+		buf[i] = be16_to_cpu(be_buf[i]);
+
+	return 0;
+}
+EXPORT_SYMBOL(mma9551_read_config_words);
+
+/**
+ * mma9551_read_status_words() - read multiple status words
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @len:	Length of array to read in bytes
+ * @val:	Array of words to read
+ *
+ * Read multiple status registers (word-sized registers).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
+			      u16 reg, u8 len, u16 *buf)
+{
+	int ret, i;
+	int len_words = len / sizeof(u16);
+	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+
+	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
+			       reg, NULL, 0, (u8 *) be_buf, len);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < len_words; i++)
+		buf[i] = be16_to_cpu(be_buf[i]);
+
+	return 0;
+}
+EXPORT_SYMBOL(mma9551_read_status_words);
+
+/**
+ * mma9551_write_config_words() - write multiple config words
+ * @client:	I2C client
+ * @app_id:	Application ID
+ * @reg:	Application register
+ * @len:	Length of array to write in bytes
+ * @val:	Array of words to write
+ *
+ * Write multiple configuration registers (word-sized registers).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
+			       u16 reg, u8 len, u16 *buf)
+{
+	int i;
+	int len_words = len / sizeof(u16);
+	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+
+	for (i = 0; i < len_words; i++)
+		be_buf[i] = cpu_to_be16(buf[i]);
+
+	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
+				reg, (u8 *) be_buf, len, NULL, 0);
+}
+EXPORT_SYMBOL(mma9551_write_config_words);
+
+/**
  * mma9551_update_config_bits() - update bits in register
  * @client:	I2C client
  * @app_id:	Application ID
@@ -609,6 +773,25 @@ int mma9551_read_accel_scale(int *val, int *val2)
 }
 EXPORT_SYMBOL(mma9551_read_accel_scale);
 
+/**
+ * mma9551_app_reset() - reset application
+ * @client:	I2C client
+ * @app_mask:	Application to reset
+ *
+ * Reset the given application (using the Reset/Suspend/Clear
+ * Control Application)
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
+{
+	return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
+					 MMA9551_RSC_RESET +
+					 MMA9551_RSC_OFFSET(app_mask),
+					 MMA9551_RSC_VAL(app_mask));
+}
+EXPORT_SYMBOL(mma9551_app_reset);
+
 MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
 MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
index e6efd02..edaa56b 100644
--- a/drivers/iio/accel/mma9551_core.h
+++ b/drivers/iio/accel/mma9551_core.h
@@ -21,9 +21,13 @@
 #define MMA9551_APPID_AFE		0x06
 #define MMA9551_APPID_TILT		0x0B
 #define MMA9551_APPID_SLEEP_WAKE	0x12
-#define MMA9551_APPID_RESET		0x17
+#define MMA9551_APPID_PEDOMETER	        0x15
+#define MMA9551_APPID_RCS		0x17
 #define MMA9551_APPID_NONE		0xff
 
+/* Reset/Suspend/Clear application app masks */
+#define MMA9551_RSC_PED			BIT(21)
+
 #define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
 
 enum mma9551_gpio_pin {
@@ -48,8 +52,18 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
 			      u16 reg, u8 val);
 int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
 			     u16 reg, u8 *val);
+int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
+			    u16 reg, u16 *val);
+int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
+			     u16 reg, u16 val);
 int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
 			     u16 reg, u16 *val);
+int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
+			     u16 reg, u8 len, u16 *buf);
+int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
+			      u16 reg, u8 len, u16 *buf);
+int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
+			       u16 reg, u8 len, u16 *buf);
 int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
 			       u16 reg, u8 mask, u8 val);
 int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
@@ -62,5 +76,6 @@ int mma9551_read_accel_chan(struct i2c_client *client,
 			    const struct iio_chan_spec *chan,
 			    int *val, int *val2);
 int mma9551_read_accel_scale(int *val, int *val2);
+int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
 
 #endif /* _MMA9551_CORE_H_ */
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
new file mode 100644
index 0000000..ef6a6e3
--- /dev/null
+++ b/drivers/iio/accel/mma9553.c
@@ -0,0 +1,1321 @@
+/*
+ * Freescale MMA9553L Intelligent Pedometer driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope 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/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/pm_runtime.h>
+#include "mma9551_core.h"
+
+#define MMA9553_DRV_NAME			"mma9553"
+#define MMA9553_IRQ_NAME			"mma9553_event"
+#define MMA9553_GPIO_NAME			"mma9553_int"
+
+/* Pedometer configuration registers (R/W) */
+#define MMA9553_CONF_SLEEPMIN		0x00
+#define MMA9553_CONF_SLEEPMAX		0x02
+#define MMA9553_CONF_SLEEPTHD		0x04
+#define MMA9553_CONF_WORD		GENMASK(15, 0)
+
+#define MMA9553_CONF_CONF_STEPLEN	0x06
+#define MMA9553_CONF_CONFIG		BIT(15)
+#define MMA9553_CONF_ACT_DBCNTM		BIT(14)
+#define MMA9553_CONF_SLP_DBCNTM		BIT(13)
+#define MMA9553_CONF_STEPLEN		GENMASK(7, 0)
+
+#define MMA9553_CONF_HEIGHT_WEIGHT	0x08
+#define MMA9553_CONF_HEIGHT		GENMASK(15, 8)
+#define MMA9553_CONF_WEIGHT		GENMASK(7, 0)
+
+#define MMA9553_CONF_FILTER		0x0A
+#define MMA9553_CONF_FILTSTEP		GENMASK(15, 8)
+#define MMA9553_CONF_MALE		BIT(7)
+#define MMA9553_CONF_FILTTIME		GENMASK(6, 0)
+
+#define MMA9553_CONF_SPEED_STEP		0x0C
+#define MMA9553_CONF_SPDPRD		GENMASK(15, 8)
+#define MMA9553_CONF_STEPCOALESCE	GENMASK(7, 0)
+
+#define MMA9553_CONF_ACTTHD		0x0E
+
+/* Pedometer status registers (R-only) */
+#define MMA9553_STATUS			0x00
+#define MMA9553_STATUS_MRGFL		BIT(15)
+#define MMA9553_STATUS_SUSPCHG		BIT(14)
+#define MMA9553_STATUS_STEPCHG		BIT(13)
+#define MMA9553_STATUS_ACTCHG		BIT(12)
+#define MMA9553_STATUS_SUSP		BIT(11)
+#define MMA9553_STATUS_ACTIVITY		(BIT(10) | BIT(9) | BIT(8))
+#define MMA9553_STATUS_VERSION		0x00FF
+
+#define MMA9553_STEPCNT			0x02
+#define MMA9553_DISTANCE		0x04
+#define MMA9553_SPEED			0x06
+#define MMA9553_CALORIES		0x08
+#define MMA9553_SLEEPCNT		0x0A
+
+/* Pedometer events are always mapped to this pin. */
+#define MMA9553_DEFAULT_GPIO_PIN	mma9551_gpio6
+#define MMA9553_DEFAULT_GPIO_POLARITY	0
+
+/* Bitnum used for gpio configuration = bit number in high status byte */
+#define STATUS_TO_BITNUM(bit)		(ffs(bit) - 9)
+
+#define MMA9553_DEFAULT_SAMPLE_RATE	30	/* Hz */
+
+/*
+ * The internal activity level must be stable for ACTTHD samples before
+ * ACTIVITY is updated.The ACTIVITY variable contains the current activity
+ * level and is updated every time a step is detected or once a second
+ * if there are no steps.
+ */
+#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) / MMA9553_DEFAULT_SAMPLE_RATE)
+#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) * MMA9553_DEFAULT_SAMPLE_RATE)
+
+/*
+ * Autonomously suspend pedometer if acceleration vector magnitude
+ * is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
+ */
+#define MMA9553_DEFAULT_SLEEPMIN	3688	/* 0,9 g */
+#define MMA9553_DEFAULT_SLEEPMAX	4508	/* 1,1 g */
+#define MMA9553_DEFAULT_SLEEPTHD	(MMA9553_DEFAULT_SAMPLE_RATE * 30)
+
+#define MMA9553_CONFIG_RETRIES		2
+
+/* Status register - activity field  */
+enum activity_level {
+	ACTIVITY_UNKNOWN,
+	ACTIVITY_REST,
+	ACTIVITY_WALKING,
+	ACTIVITY_JOGGING,
+	ACTIVITY_RUNNING,
+};
+
+static struct mma9553_event_info {
+	enum iio_chan_type type;
+	enum iio_modifier mod;
+	enum iio_event_direction dir;
+} mma9553_events_info[] = {
+	{
+		.type = IIO_STEPS,
+		.mod = IIO_NO_MOD,
+		.dir = IIO_EV_DIR_NONE,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_STILL,
+		.dir = IIO_EV_DIR_RISING,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_STILL,
+		.dir = IIO_EV_DIR_FALLING,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_WALKING,
+		.dir = IIO_EV_DIR_RISING,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_WALKING,
+		.dir = IIO_EV_DIR_FALLING,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_JOGGING,
+		.dir = IIO_EV_DIR_RISING,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_JOGGING,
+		.dir = IIO_EV_DIR_FALLING,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_RUNNING,
+		.dir = IIO_EV_DIR_RISING,
+	},
+	{
+		.type = IIO_ACTIVITY,
+		.mod = IIO_MOD_RUNNING,
+		.dir = IIO_EV_DIR_FALLING,
+	},
+};
+
+#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
+
+struct mma9553_event {
+	struct mma9553_event_info *info;
+	bool enabled;
+};
+
+struct mma9553_conf_regs {
+	u16 sleepmin;
+	u16 sleepmax;
+	u16 sleepthd;
+	u16 config;
+	u16 height_weight;
+	u16 filter;
+	u16 speed_step;
+	u16 actthd;
+} __packed;
+
+struct mma9553_data {
+	struct i2c_client *client;
+	struct mutex mutex;
+	struct mma9553_conf_regs conf;
+	struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
+	int num_events;
+	u8 gpio_bitnum;
+	/*
+	 * This is used for all features that depend on step count:
+	 * step count, distance, speed, calories.
+	 */
+	bool stepcnt_enabled;
+	u16 stepcnt;
+	u8 activity;
+	s64 timestamp;
+};
+
+static u8 mma9553_get_bits(u16 val, u16 mask)
+{
+	return (val & mask) >> (ffs(mask) - 1);
+}
+
+static u16 mma9553_set_bits(u16 current_val, u16 val, u16 mask)
+{
+	return (current_val & ~mask) | (val << (ffs(mask) - 1));
+}
+
+static enum iio_modifier mma9553_activity_to_mod(enum activity_level activity)
+{
+	switch (activity) {
+	case ACTIVITY_RUNNING:
+		return IIO_MOD_RUNNING;
+	case ACTIVITY_JOGGING:
+		return IIO_MOD_JOGGING;
+	case ACTIVITY_WALKING:
+		return IIO_MOD_WALKING;
+	case ACTIVITY_REST:
+		return IIO_MOD_STILL;
+	case ACTIVITY_UNKNOWN:
+	default:
+		return IIO_NO_MOD;
+	}
+}
+
+static void mma9553_init_events(struct mma9553_data *data)
+{
+	int i;
+
+	data->num_events = MMA9553_EVENTS_INFO_SIZE;
+	for (i = 0; i < data->num_events; i++) {
+		data->events[i].info = &mma9553_events_info[i];
+		data->events[i].enabled = false;
+	}
+}
+
+static struct mma9553_event *mma9553_get_event(struct mma9553_data *data,
+					       enum iio_chan_type type,
+					       enum iio_modifier mod,
+					       enum iio_event_direction dir)
+{
+	int i;
+
+	for (i = 0; i < data->num_events; i++)
+		if (data->events[i].info->type == type &&
+		    data->events[i].info->mod == mod &&
+		    data->events[i].info->dir == dir)
+			return &data->events[i];
+
+	return NULL;
+}
+
+static bool mma9553_is_any_event_enabled(struct mma9553_data *data,
+					 bool check_type,
+					 enum iio_chan_type type)
+{
+	int i;
+
+	for (i = 0; i < data->num_events; i++)
+		if ((check_type && data->events[i].info->type == type &&
+		     data->events[i].enabled) ||
+		     (!check_type && data->events[i].enabled))
+			return true;
+
+	return false;
+}
+
+static int mma9553_set_config(struct mma9553_data *data, u16 reg,
+			      u16 *p_reg_val, u16 val, u16 mask)
+{
+	int ret, retries;
+	u16 reg_val, config;
+
+	reg_val = *p_reg_val;
+	if (val == mma9553_get_bits(reg_val, mask))
+		return 0;
+
+	reg_val = mma9553_set_bits(reg_val, val, mask);
+	ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
+					reg, reg_val);
+	if (ret < 0) {
+		dev_err(&data->client->dev,
+			"error writing config register 0x%x\n", reg);
+		return ret;
+	}
+
+	*p_reg_val = reg_val;
+
+	/* Reinitializes the pedometer with current configuration values */
+	config = mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
+
+	ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
+					MMA9553_CONF_CONF_STEPLEN, config);
+	if (ret < 0) {
+		dev_err(&data->client->dev,
+			"error writing config register 0x%x\n",
+			MMA9553_CONF_CONF_STEPLEN);
+		return ret;
+	}
+
+	retries = MMA9553_CONFIG_RETRIES;
+	do {
+		mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
+		ret = mma9551_read_config_word(data->client,
+					       MMA9551_APPID_PEDOMETER,
+					       MMA9553_CONF_CONF_STEPLEN,
+					       &config);
+		if (ret < 0)
+			return ret;
+	} while (mma9553_get_bits(config, MMA9553_CONF_CONFIG) &&
+		 --retries > 0);
+
+	return 0;
+}
+
+static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
+					 u8 *activity, u16 *stepcnt)
+{
+	u32 status_stepcnt;
+	u16 status;
+	int ret;
+
+	ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
+					MMA9553_STATUS, sizeof(u32),
+					(u16 *) &status_stepcnt);
+	if (ret < 0) {
+		dev_err(&data->client->dev,
+			"error reading status and stepcnt\n");
+		return ret;
+	}
+
+	status = status_stepcnt & MMA9553_CONF_WORD;
+	*activity = mma9553_get_bits(status, MMA9553_STATUS_ACTIVITY);
+	*stepcnt = status_stepcnt >> 16;
+
+	return 0;
+}
+
+static int mma9553_conf_gpio(struct mma9553_data *data)
+{
+	u8 bitnum = 0, appid = MMA9551_APPID_PEDOMETER;
+	int ret;
+	struct mma9553_event *ev_step_detect;
+	bool activity_enabled;
+
+	activity_enabled =
+	    mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
+	ev_step_detect =
+	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
+
+	/*
+	 * If both step detector and activity are enabled, use the MRGFL bit.
+	 * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
+	 */
+	if (activity_enabled && ev_step_detect->enabled)
+		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_MRGFL);
+	else if (ev_step_detect->enabled)
+		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_STEPCHG);
+	else if (activity_enabled)
+		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_ACTCHG);
+	else			/* Reset */
+		appid = MMA9551_APPID_NONE;
+
+	if (data->gpio_bitnum == bitnum)
+		return 0;
+
+	/* Save initial values for activity and stepcnt */
+	if (activity_enabled || ev_step_detect->enabled)
+		mma9553_read_activity_stepcnt(data, &data->activity,
+					      &data->stepcnt);
+
+	ret = mma9551_gpio_config(data->client,
+				  MMA9553_DEFAULT_GPIO_PIN,
+				  appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
+	if (ret < 0)
+		return ret;
+	data->gpio_bitnum = bitnum;
+
+	return 0;
+}
+
+static int mma9553_init(struct mma9553_data *data)
+{
+	int ret;
+
+	ret = mma9551_read_version(data->client);
+	if (ret)
+		return ret;
+
+	/*
+	 * Read all the pedometer configuration registers. This is used as
+	 * a device identification command to differentiate the MMA9553L
+	 * from the MMA9550L.
+	 */
+	ret =
+	    mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
+				      MMA9553_CONF_SLEEPMIN,
+				      sizeof(data->conf), (u16 *) &data->conf);
+	if (ret < 0) {
+		dev_err(&data->client->dev,
+			"device is not MMA9553L: failed to read cfg regs\n");
+		return ret;
+	}
+
+
+	/* Reset gpio */
+	data->gpio_bitnum = -1;
+	ret = mma9553_conf_gpio(data);
+	if (ret < 0)
+		return ret;
+
+	ret = mma9551_app_reset(data->client, MMA9551_RSC_PED);
+	if (ret < 0)
+		return ret;
+
+	/* Init config registers */
+	data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
+	data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
+	data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
+	data->conf.config =
+	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
+	/*
+	 * Clear the activity debounce counter when the activity level changes,
+	 * so that the confidence level applies for any activity level.
+	 */
+	data->conf.config =
+	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_ACT_DBCNTM);
+	ret =
+	    mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
+				       MMA9553_CONF_SLEEPMIN,
+				       sizeof(data->conf), (u16 *) &data->conf);
+	if (ret < 0) {
+		dev_err(&data->client->dev,
+			"failed to write configuration registers\n");
+		return ret;
+	}
+
+	return mma9551_set_device_state(data->client, true);
+}
+
+static int mma9553_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct mma9553_data *data = iio_priv(indio_dev);
+	int ret;
+	u16 tmp;
+	u8 activity;
+	bool powered_on;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		switch (chan->type) {
+		case IIO_STEPS:
+			/*
+			 * The HW only counts steps and other dependent
+			 * parameters (speed, distance, calories, activity)
+			 * if power is on (from enabling an event or the
+			 * step counter */
+			powered_on =
+			    mma9553_is_any_event_enabled(data, false, 0) ||
+			    data->stepcnt_enabled;
+			if (!powered_on) {
+				dev_err(&data->client->dev,
+					"No channels enabled\n");
+				return -EINVAL;
+			}
+			mutex_lock(&data->mutex);
+			ret = mma9551_read_status_word(data->client,
+						       MMA9551_APPID_PEDOMETER,
+						       MMA9553_STEPCNT, &tmp);
+			mutex_unlock(&data->mutex);
+			if (ret < 0)
+				return ret;
+			*val = tmp;
+			return IIO_VAL_INT;
+		case IIO_DISTANCE:
+			powered_on =
+			    mma9553_is_any_event_enabled(data, false, 0) ||
+			    data->stepcnt_enabled;
+			if (!powered_on) {
+				dev_err(&data->client->dev,
+					"No channels enabled\n");
+				return -EINVAL;
+			}
+			mutex_lock(&data->mutex);
+			ret = mma9551_read_status_word(data->client,
+						       MMA9551_APPID_PEDOMETER,
+						       MMA9553_DISTANCE, &tmp);
+			mutex_unlock(&data->mutex);
+			if (ret < 0)
+				return ret;
+			*val = tmp;
+			return IIO_VAL_INT;
+		case IIO_ACTIVITY:
+			powered_on =
+			    mma9553_is_any_event_enabled(data, false, 0) ||
+			    data->stepcnt_enabled;
+			if (!powered_on) {
+				dev_err(&data->client->dev,
+					"No channels enabled\n");
+				return -EINVAL;
+			}
+			mutex_lock(&data->mutex);
+			ret = mma9551_read_status_word(data->client,
+						       MMA9551_APPID_PEDOMETER,
+						       MMA9553_STATUS, &tmp);
+			mutex_unlock(&data->mutex);
+			if (ret < 0)
+				return ret;
+
+			activity =
+			    mma9553_get_bits(tmp, MMA9553_STATUS_ACTIVITY);
+
+			/*
+			 * The device does not support confidence value levels,
+			 * so we will always have 100% for current activity and
+			 * 0% for the others.
+			 */
+			if (chan->channel2 == mma9553_activity_to_mod(activity))
+				*val = 100;
+			else
+				*val = 0;
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->type) {
+		case IIO_VELOCITY:	/* m/h */
+			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+				return -EINVAL;
+			powered_on =
+			    mma9553_is_any_event_enabled(data, false, 0) ||
+			    data->stepcnt_enabled;
+			if (!powered_on) {
+				dev_err(&data->client->dev,
+					"No channels enabled\n");
+				return -EINVAL;
+			}
+			mutex_lock(&data->mutex);
+			ret = mma9551_read_status_word(data->client,
+						       MMA9551_APPID_PEDOMETER,
+						       MMA9553_SPEED, &tmp);
+			mutex_unlock(&data->mutex);
+			if (ret < 0)
+				return ret;
+			*val = tmp;
+			return IIO_VAL_INT;
+		case IIO_ENERGY:	/* Cal or kcal */
+			powered_on =
+			    mma9553_is_any_event_enabled(data, false, 0) ||
+			    data->stepcnt_enabled;
+			if (!powered_on) {
+				dev_err(&data->client->dev,
+					"No channels enabled\n");
+				return -EINVAL;
+			}
+			mutex_lock(&data->mutex);
+			ret = mma9551_read_status_word(data->client,
+						       MMA9551_APPID_PEDOMETER,
+						       MMA9553_CALORIES, &tmp);
+			mutex_unlock(&data->mutex);
+			if (ret < 0)
+				return ret;
+			*val = tmp;
+			return IIO_VAL_INT;
+		case IIO_ACCEL:
+			mutex_lock(&data->mutex);
+			ret = mma9551_read_accel_chan(data->client,
+						      chan, val, val2);
+			mutex_unlock(&data->mutex);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_VELOCITY:	/* m/h to m/s */
+			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+				return -EINVAL;
+			*val = 0;
+			*val2 = 277;	/* 0.000277 */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_ENERGY:	/* Cal or kcal to J */
+			*val = 4184;
+			return IIO_VAL_INT;
+		case IIO_ACCEL:
+			return mma9551_read_accel_scale(val, val2);
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_ENABLE:
+		*val = data->stepcnt_enabled;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBHEIGHT:
+		*val = mma9553_get_bits(data->conf.height_weight,
+					MMA9553_CONF_HEIGHT);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBWEIGHT:
+		*val = mma9553_get_bits(data->conf.height_weight,
+					MMA9553_CONF_WEIGHT);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
+		switch (chan->type) {
+		case IIO_STEPS:
+			*val = mma9553_get_bits(data->conf.filter,
+						MMA9553_CONF_FILTSTEP);
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
+		switch (chan->type) {
+		case IIO_STEPS:
+			*val = mma9553_get_bits(data->conf.filter,
+						MMA9553_CONF_FILTTIME);
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_INT_TIME:
+		switch (chan->type) {
+		case IIO_VELOCITY:
+			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+				return -EINVAL;
+			*val = mma9553_get_bits(data->conf.speed_step,
+						MMA9553_CONF_SPDPRD);
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mma9553_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct mma9553_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_ENABLE:
+		if (data->stepcnt_enabled == !!val)
+			return 0;
+		mutex_lock(&data->mutex);
+		ret = mma9551_set_power_state(data->client, val);
+		if (ret < 0) {
+			mutex_unlock(&data->mutex);
+			return ret;
+		}
+		data->stepcnt_enabled = val;
+		mutex_unlock(&data->mutex);
+		return 0;
+	case IIO_CHAN_INFO_CALIBHEIGHT:
+		if (val < 0 || val > 255)
+			return -EINVAL;
+		mutex_lock(&data->mutex);
+		ret = mma9553_set_config(data,
+					 MMA9553_CONF_HEIGHT_WEIGHT,
+					 &data->conf.height_weight,
+					 val, MMA9553_CONF_HEIGHT);
+		mutex_unlock(&data->mutex);
+		return ret;
+	case IIO_CHAN_INFO_CALIBWEIGHT:
+		if (val < 0 || val > 255)
+			return -EINVAL;
+		mutex_lock(&data->mutex);
+		ret = mma9553_set_config(data,
+					 MMA9553_CONF_HEIGHT_WEIGHT,
+					 &data->conf.height_weight,
+					 val, MMA9553_CONF_WEIGHT);
+		mutex_unlock(&data->mutex);
+		return ret;
+	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
+		switch (chan->type) {
+		case IIO_STEPS:
+			/*
+			 * Set to 0 to disable step filtering. If the value
+			 * specified is greater than 6, then 6 will be used.
+			 */
+			if (val < 0)
+				return -EINVAL;
+			if (val > 6)
+				val = 6;
+			mutex_lock(&data->mutex);
+			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
+						 &data->conf.filter, val,
+						 MMA9553_CONF_FILTSTEP);
+			mutex_unlock(&data->mutex);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
+		switch (chan->type) {
+		case IIO_STEPS:
+			if (val < 0 || val > 127)
+				return -EINVAL;
+			mutex_lock(&data->mutex);
+			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
+						 &data->conf.filter, val,
+						 MMA9553_CONF_FILTTIME);
+			mutex_unlock(&data->mutex);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_INT_TIME:
+		switch (chan->type) {
+		case IIO_VELOCITY:
+			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+				return -EINVAL;
+			/*
+			 * If set to a value greater than 5, then 5 will be
+			 * used. Warning: Do not set SPDPRD to 0 or 1 as
+			 * this may cause undesirable behavior.
+			 */
+			if (val < 2)
+				return -EINVAL;
+			if (val > 5)
+				val = 5;
+			mutex_lock(&data->mutex);
+			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
+						 &data->conf.speed_step, val,
+						 MMA9553_CONF_SPDPRD);
+			mutex_unlock(&data->mutex);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
+	struct mma9553_event *event;
+
+	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
+	if (!event)
+		return -EINVAL;
+
+	return event->enabled;
+}
+
+static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
+	struct mma9553_event *event;
+	int ret;
+
+	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
+	if (!event)
+		return -EINVAL;
+
+	if (event->enabled == state)
+		return 0;
+
+	mutex_lock(&data->mutex);
+
+	ret = mma9551_set_power_state(data->client, state);
+	if (ret < 0)
+		goto err_out;
+	event->enabled = state;
+
+	ret = mma9553_conf_gpio(data);
+	if (ret < 0)
+		goto err_conf_gpio;
+
+	mutex_unlock(&data->mutex);
+
+	return ret;
+
+err_conf_gpio:
+	if (state) {
+		event->enabled = false;
+		mma9551_set_power_state(data->client, false);
+	}
+err_out:
+	mutex_unlock(&data->mutex);
+	return ret;
+}
+
+static int mma9553_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)
+{
+	struct mma9553_data *data = iio_priv(indio_dev);
+
+	*val2 = 0;
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		switch (chan->type) {
+		case IIO_STEPS:
+			*val = mma9553_get_bits(data->conf.speed_step,
+						MMA9553_CONF_STEPCOALESCE);
+			return IIO_VAL_INT;
+		case IIO_ACTIVITY:
+			/*
+			 * The device does not support confidence value levels.
+			 * We set an average of 50%.
+			 */
+			*val = 50;
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_EV_INFO_PERIOD:
+		switch (chan->type) {
+		case IIO_ACTIVITY:
+			*val = MMA9553_ACTIVITY_THD_TO_SEC(data->conf.actthd);
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		switch (chan->type) {
+		case IIO_STEPS:
+			if (val < 0 || val > 255)
+				return -EINVAL;
+			mutex_lock(&data->mutex);
+			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
+						 &data->conf.speed_step, val,
+						 MMA9553_CONF_STEPCOALESCE);
+			mutex_unlock(&data->mutex);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	case IIO_EV_INFO_PERIOD:
+		switch (chan->type) {
+		case IIO_ACTIVITY:
+			mutex_lock(&data->mutex);
+			ret = mma9553_set_config(data, MMA9553_CONF_ACTTHD,
+						 &data->conf.actthd,
+						 MMA9553_ACTIVITY_SEC_TO_THD
+						 (val), MMA9553_CONF_WORD);
+			mutex_unlock(&data->mutex);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan)
+{
+	struct mma9553_data *data = iio_priv(indio_dev);
+	u8 gender;
+
+	gender = mma9553_get_bits(data->conf.filter, MMA9553_CONF_MALE);
+	/*
+	 * HW expects 0 for female and 1 for male,
+	 * while iio index is 0 for male and 1 for female
+	 */
+	return !gender;
+}
+
+static int mma9553_set_calibgender_mode(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan,
+					unsigned int mode)
+{
+	struct mma9553_data *data = iio_priv(indio_dev);
+	u8 gender = !mode;
+	int ret;
+
+	if ((mode != 0) && (mode != 1))
+		return -EINVAL;
+	mutex_lock(&data->mutex);
+	ret = mma9553_set_config(data, MMA9553_CONF_FILTER, &data->conf.filter,
+				 gender, MMA9553_CONF_MALE);
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static const struct iio_event_spec mma9553_step_event = {
+	.type = IIO_EV_TYPE_CHANGE,
+	.dir = IIO_EV_DIR_NONE,
+	.mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
+};
+
+static const struct iio_event_spec mma9553_activity_events[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+				 BIT(IIO_EV_INFO_VALUE) |
+				 BIT(IIO_EV_INFO_PERIOD),
+	 },
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+				 BIT(IIO_EV_INFO_VALUE) |
+				 BIT(IIO_EV_INFO_PERIOD),
+	},
+};
+
+static const char * const calibgender_modes[] = { "male", "female" };
+
+static const struct iio_enum mma9553_calibgender_enum = {
+	.items = calibgender_modes,
+	.num_items = ARRAY_SIZE(calibgender_modes),
+	.get = mma9553_get_calibgender_mode,
+	.set = mma9553_set_calibgender_mode,
+};
+
+static const struct iio_chan_spec_ext_info mma9553_ext_info[] = {
+	IIO_CALIBGENDER_EXT_INFO(IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum),
+	{},
+};
+
+#define MMA9553_PEDOMETER_CHANNEL(_type, _mask) {		\
+	.type = _type,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE)      |	\
+			      BIT(IIO_CHAN_INFO_CALIBHEIGHT) |	\
+			      _mask,				\
+	.ext_info = mma9553_ext_info,				\
+}
+
+#define MMA9553_ACTIVITY_CHANNEL(_chan2) {				\
+	.type = IIO_ACTIVITY,						\
+	.modified = 1,							\
+	.channel2 = _chan2,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),		\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),	\
+	.event_spec = mma9553_activity_events,				\
+	.num_event_specs = ARRAY_SIZE(mma9553_activity_events),		\
+	.ext_info = mma9553_ext_info,					\
+}
+
+static const struct iio_chan_spec mma9553_channels[] = {
+	MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
+	MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
+	MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
+
+	{
+		.type = IIO_STEPS,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+				     BIT(IIO_CHAN_INFO_ENABLE) |
+				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH) |
+				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD),
+		.event_spec = &mma9553_step_event,
+		.num_event_specs = 1,
+	},
+
+	MMA9553_PEDOMETER_CHANNEL(IIO_DISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)),
+	{
+		.type = IIO_VELOCITY,
+		.modified = 1,
+		.channel2 = IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE) |
+				      BIT(IIO_CHAN_INFO_INT_TIME) |
+				      BIT(IIO_CHAN_INFO_ENABLE),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),
+		.ext_info = mma9553_ext_info,
+	},
+	MMA9553_PEDOMETER_CHANNEL(IIO_ENERGY, BIT(IIO_CHAN_INFO_RAW) |
+				  BIT(IIO_CHAN_INFO_SCALE) |
+				  BIT(IIO_CHAN_INFO_CALIBWEIGHT)),
+
+	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
+	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_JOGGING),
+	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
+	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_STILL),
+};
+
+static const struct iio_info mma9553_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = mma9553_read_raw,
+	.write_raw = mma9553_write_raw,
+	.read_event_config = mma9553_read_event_config,
+	.write_event_config = mma9553_write_event_config,
+	.read_event_value = mma9553_read_event_value,
+	.write_event_value = mma9553_write_event_value,
+};
+
+static irqreturn_t mma9553_irq_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct mma9553_data *data = iio_priv(indio_dev);
+
+	data->timestamp = iio_get_time_ns();
+	/*
+	 * Since we only configure the interrupt pin when an
+	 * event is enabled, we are sure we have at least
+	 * one event enabled at this point.
+	 */
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t mma9553_event_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct mma9553_data *data = iio_priv(indio_dev);
+	u16 stepcnt;
+	u8 activity;
+	struct mma9553_event *ev_activity, *ev_prev_activity, *ev_step_detect;
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = mma9553_read_activity_stepcnt(data, &activity, &stepcnt);
+	if (ret < 0) {
+		mutex_unlock(&data->mutex);
+		return IRQ_HANDLED;
+	}
+
+	ev_prev_activity =
+	    mma9553_get_event(data, IIO_ACTIVITY,
+			      mma9553_activity_to_mod(data->activity),
+			      IIO_EV_DIR_FALLING);
+	ev_activity =
+	    mma9553_get_event(data, IIO_ACTIVITY,
+			      mma9553_activity_to_mod(activity),
+			      IIO_EV_DIR_RISING);
+	ev_step_detect =
+	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
+
+	if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
+		data->stepcnt = stepcnt;
+		iio_push_event(indio_dev,
+			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
+			       IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
+			       data->timestamp);
+	}
+
+	if (activity != data->activity) {
+		data->activity = activity;
+		/* ev_activity can be NULL if activity == ACTIVITY_UNKNOWN */
+		if (ev_prev_activity && ev_prev_activity->enabled)
+			iio_push_event(indio_dev,
+				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
+				       ev_prev_activity->info->mod,
+				       IIO_EV_DIR_FALLING,
+				       IIO_EV_TYPE_THRESH, 0, 0, 0),
+				       data->timestamp);
+
+		if (ev_activity && ev_activity->enabled)
+			iio_push_event(indio_dev,
+				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
+				       ev_activity->info->mod,
+				       IIO_EV_DIR_RISING,
+				       IIO_EV_TYPE_THRESH, 0, 0, 0),
+				       data->timestamp);
+	}
+	mutex_unlock(&data->mutex);
+
+	return IRQ_HANDLED;
+}
+
+static int mma9553_gpio_probe(struct i2c_client *client)
+{
+	struct device *dev;
+	struct gpio_desc *gpio;
+	int ret;
+
+	if (!client)
+		return -EINVAL;
+
+	dev = &client->dev;
+
+	/* data ready gpio interrupt pin */
+	gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
+	if (IS_ERR(gpio)) {
+		dev_err(dev, "acpi gpio get index failed\n");
+		return PTR_ERR(gpio);
+	}
+
+	ret = gpiod_direction_input(gpio);
+	if (ret)
+		return ret;
+
+	ret = gpiod_to_irq(gpio);
+
+	dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+	return ret;
+}
+
+static const char *mma9553_match_acpi_device(struct device *dev)
+{
+	const struct acpi_device_id *id;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return NULL;
+
+	return dev_name(dev);
+}
+
+static int mma9553_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct mma9553_data *data;
+	struct iio_dev *indio_dev;
+	const char *name = NULL;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	i2c_set_clientdata(client, indio_dev);
+	data->client = client;
+
+	if (id)
+		name = id->name;
+	else if (ACPI_HANDLE(&client->dev))
+		name = mma9553_match_acpi_device(&client->dev);
+	else
+		return -ENOSYS;
+
+	mutex_init(&data->mutex);
+	mma9553_init_events(data);
+
+	ret = mma9553_init(data);
+	if (ret < 0)
+		return ret;
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->channels = mma9553_channels;
+	indio_dev->num_channels = ARRAY_SIZE(mma9553_channels);
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &mma9553_info;
+
+	if (client->irq < 0)
+		client->irq = mma9553_gpio_probe(client);
+
+	if (client->irq >= 0) {
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+						mma9553_irq_handler,
+						mma9553_event_handler,
+						IRQF_TRIGGER_RISING,
+						MMA9553_IRQ_NAME, indio_dev);
+		if (ret < 0) {
+			dev_err(&client->dev, "request irq %d failed\n",
+				client->irq);
+			goto out_poweroff;
+		}
+
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to register iio device\n");
+		goto out_poweroff;
+	}
+
+	ret = pm_runtime_set_active(&client->dev);
+	if (ret < 0)
+		goto out_iio_unregister;
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev,
+					 MMA9551_AUTO_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(&client->dev);
+
+	dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
+
+	return 0;
+
+out_iio_unregister:
+	iio_device_unregister(indio_dev);
+out_poweroff:
+	mma9551_set_device_state(client, false);
+	return ret;
+}
+
+static int mma9553_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct mma9553_data *data = iio_priv(indio_dev);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+
+	iio_device_unregister(indio_dev);
+	mutex_lock(&data->mutex);
+	mma9551_set_device_state(data->client, false);
+	mutex_unlock(&data->mutex);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mma9553_runtime_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mma9553_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = mma9551_set_device_state(data->client, false);
+	mutex_unlock(&data->mutex);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "powering off device failed\n");
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static int mma9553_runtime_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mma9553_data *data = iio_priv(indio_dev);
+	int ret;
+
+	ret = mma9551_set_device_state(data->client, true);
+	if (ret < 0)
+		return ret;
+
+	mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int mma9553_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mma9553_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = mma9551_set_device_state(data->client, false);
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static int mma9553_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mma9553_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = mma9551_set_device_state(data->client, true);
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+#endif
+
+static const struct dev_pm_ops mma9553_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
+	SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
+			   mma9553_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id mma9553_acpi_match[] = {
+	{"MMA9553", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
+
+static const struct i2c_device_id mma9553_id[] = {
+	{"mma9553", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, mma9553_id);
+
+static struct i2c_driver mma9553_driver = {
+	.driver = {
+		   .name = MMA9553_DRV_NAME,
+		   .acpi_match_table = ACPI_PTR(mma9553_acpi_match),
+		   .pm = &mma9553_pm_ops,
+		   },
+	.probe = mma9553_probe,
+	.remove = mma9553_remove,
+	.id_table = mma9553_id,
+};
+
+module_i2c_driver(mma9553_driver);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index b6b12ac..6770a2f 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -634,4 +634,15 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
  */
 #define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
 
+/**
+ * IIO_CALIBGENDER_EXT_INFO() - define the gender of the user
+ *
+ * @_shared:	Whether the attribute is shared between all channels
+ * @_e:		Pointer to an iio_enum struct
+ *
+ */
+#define IIO_CALIBGENDER_EXT_INFO(_shared, _e)			       \
+	IIO_ENUM("calibgender", _shared, _e),			       \
+	IIO_ENUM_AVAILABLE("calibgender", _e)
+
 #endif /* _INDUSTRIAL_IO_H_ */
-- 
1.9.1


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

* Re: [PATCH v2 01/10] iio: core: Introduce ENERGY channel type
  2015-01-11 19:10 ` [PATCH v2 01/10] iio: core: Introduce ENERGY channel type Irina Tirdea
@ 2015-01-25 22:58   ` Jonathan Cameron
  2015-03-29  0:14   ` Hartmut Knaack
  1 sibling, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-25 22:58 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> Human activity sensors report the energy burnt by the user.
> One of this devices is Freescale's MMA9553L
> (http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
> that computes the number of calories based on weight and step rate.
> 
> Introduce a new channel type ENERGY to export these values.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Applied to the togreg branch of iio.git - initially
pushed out as testing for the autobuilders to play with it.

Thanks,

Jonathan
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
>  drivers/iio/industrialio-core.c         |  1 +
>  include/linux/iio/types.h               |  1 +
>  3 files changed, 12 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 831db86..3311886 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -282,6 +282,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_current_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
> @@ -1049,6 +1050,15 @@ Description:
>  		For a list of available output power modes read
>  		in_accel_power_mode_available.
>  
> +What:		/sys/.../iio:deviceX/in_energy_input
> +What:		/sys/.../iio:deviceX/in_energy_raw
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		This attribute is used to read the energy value reported by the
> +		device (e.g.: human activity sensors report energy burnt by the
> +		user). Units after application of scale are Joules.
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
>  KernelVersion:	3.4.0
>  Contact:	linux-iio@vger.kernel.org
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 69feb91..8d2c9ba 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -72,6 +72,7 @@ static const char * const iio_chan_type_name_spec[] = {
>  	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
>  	[IIO_ACTIVITY] = "activity",
>  	[IIO_STEPS] = "steps",
> +	[IIO_ENERGY] = "energy",
>  };
>  
>  static const char * const iio_modifier_names[] = {
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index 904dcbb..26b8a1c 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -32,6 +32,7 @@ enum iio_chan_type {
>  	IIO_HUMIDITYRELATIVE,
>  	IIO_ACTIVITY,
>  	IIO_STEPS,
> +	IIO_ENERGY,
>  };
>  
>  enum iio_modifier {
> 


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

* Re: [PATCH v2 02/10] iio: core: Introduce DISTANCE channel type
  2015-01-11 19:10 ` [PATCH v2 02/10] iio: core: Introduce DISTANCE " Irina Tirdea
@ 2015-01-25 22:59   ` Jonathan Cameron
  0 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-25 22:59 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> Some devices export an estimation of the distance the user has covered
> since the last reset.
> 
> One of this devices is Freescale's MMA9553L
> (http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
> that computes the distance based on the stride length and step rate.
> 
> Introduce a new channel type DISTANCE to export these values.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Applied to the togreg branch of iio.git - pushed out
as testing for now.

> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
>  drivers/iio/industrialio-core.c         |  1 +
>  include/linux/iio/types.h               |  1 +
>  3 files changed, 12 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 3311886..c627a9a 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -283,6 +283,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_energy_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
> @@ -1059,6 +1060,15 @@ Description:
>  		device (e.g.: human activity sensors report energy burnt by the
>  		user). Units after application of scale are Joules.
>  
> +What:		/sys/.../iio:deviceX/in_distance_input
> +What:		/sys/.../iio:deviceX/in_distance_raw
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		This attribute is used to read the distance covered by the user
> +		since the last reboot while activated. Units after application
> +		of scale are meters.
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
>  KernelVersion:	3.4.0
>  Contact:	linux-iio@vger.kernel.org
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 8d2c9ba..655755b 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -73,6 +73,7 @@ static const char * const iio_chan_type_name_spec[] = {
>  	[IIO_ACTIVITY] = "activity",
>  	[IIO_STEPS] = "steps",
>  	[IIO_ENERGY] = "energy",
> +	[IIO_DISTANCE] = "distance",
>  };
>  
>  static const char * const iio_modifier_names[] = {
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index 26b8a1c..a7de445 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -33,6 +33,7 @@ enum iio_chan_type {
>  	IIO_ACTIVITY,
>  	IIO_STEPS,
>  	IIO_ENERGY,
> +	IIO_DISTANCE,
>  };
>  
>  enum iio_modifier {
> 


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

* Re: [PATCH v2 03/10] iio: core: Introduce IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z
  2015-01-11 19:10 ` [PATCH v2 03/10] iio: core: Introduce IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z Irina Tirdea
@ 2015-01-25 23:00   ` Jonathan Cameron
  0 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-25 23:00 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> Some devices export the current speed value of the user.
> 
> One of this devices is Freescale's MMA9553L
> (http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
> that computes the speed of the user based on the number of steps and
> stride length.
> 
> Introduce a new channel type VELOCITY and a modifier for the magniture or
> norm of the velocity vector, IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Appied.
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
>  drivers/iio/industrialio-core.c         |  2 ++
>  include/linux/iio/types.h               |  2 ++
>  3 files changed, 14 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index c627a9a..80b5efb1 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -295,6 +295,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_rot_from_north_true_tilt_comp_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
>  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
>  KernelVersion:	2.6.35
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -1164,3 +1165,12 @@ Contact:	linux-iio@vger.kernel.org
>  Description:
>  		This attribute is used to read the number of steps taken by the user
>  		since the last reboot while activated.
> +
> +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_input
> +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_raw
> +KernelVersion:	3.19
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		This attribute is used to read the current speed value of the
> +		user (which is the norm or magnitude of the velocity vector).
> +		Units after application of scale are m/s.
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 655755b..18a8ab9 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -74,6 +74,7 @@ static const char * const iio_chan_type_name_spec[] = {
>  	[IIO_STEPS] = "steps",
>  	[IIO_ENERGY] = "energy",
>  	[IIO_DISTANCE] = "distance",
> +	[IIO_VELOCITY] = "velocity",
>  };
>  
>  static const char * const iio_modifier_names[] = {
> @@ -99,6 +100,7 @@ static const char * const iio_modifier_names[] = {
>  	[IIO_MOD_JOGGING] = "jogging",
>  	[IIO_MOD_WALKING] = "walking",
>  	[IIO_MOD_STILL] = "still",
> +	[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
>  };
>  
>  /* relies on pairs of these shared then separate */
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index a7de445..c3601c2 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -34,6 +34,7 @@ enum iio_chan_type {
>  	IIO_STEPS,
>  	IIO_ENERGY,
>  	IIO_DISTANCE,
> +	IIO_VELOCITY,
>  };
>  
>  enum iio_modifier {
> @@ -68,6 +69,7 @@ enum iio_modifier {
>  	IIO_MOD_JOGGING,
>  	IIO_MOD_WALKING,
>  	IIO_MOD_STILL,
> +	IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
>  };
>  
>  enum iio_event_type {
> 


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

* Re: [PATCH v2 04/10] iio: core: Introduce IO_CHAN_INFO_CALIBWEIGHT
  2015-01-11 19:10 ` [PATCH v2 04/10] iio: core: Introduce IO_CHAN_INFO_CALIBWEIGHT Irina Tirdea
@ 2015-01-25 23:01   ` Jonathan Cameron
  0 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-25 23:01 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> Some devices need the weight of the user to compute other
> parameters. One of this devices is Freescale's MMA9553L
> (http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
> that needs the weight of the user to compute the number of calories burnt.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Applied with trivial typo fix to patch title...
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++
>  drivers/iio/industrialio-core.c         | 1 +
>  include/linux/iio/iio.h                 | 1 +
>  3 files changed, 9 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 80b5efb1..71dc8db 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -351,6 +351,13 @@ Description:
>  		to compute the stride length, distance, speed and activity
>  		type.
>  
> +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibweight
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Weight of the user (in kg). It is needed by some pedometers
> +		to compute the calories burnt by the user.
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
>  What:		/sys/.../iio:deviceX/in_voltageX_scale_available
>  What:		/sys/.../iio:deviceX/in_voltage-voltage_scale_available
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 18a8ab9..4ee6fdf 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -125,6 +125,7 @@ static const char * const iio_chan_info_postfix[] = {
>  	[IIO_CHAN_INFO_INT_TIME] = "integration_time",
>  	[IIO_CHAN_INFO_ENABLE] = "en",
>  	[IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
> +	[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
>  };
>  
>  /**
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index 878d861..752a929 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -40,6 +40,7 @@ enum iio_chan_info_enum {
>  	IIO_CHAN_INFO_INT_TIME,
>  	IIO_CHAN_INFO_ENABLE,
>  	IIO_CHAN_INFO_CALIBHEIGHT,
> +	IIO_CHAN_INFO_CALIBWEIGHT,
>  };
>  
>  enum iio_shared_by {
> 


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

* Re: [PATCH v2 05/10] iio: core: Introduce CHANGE event type
  2015-01-11 19:10 ` [PATCH v2 05/10] iio: core: Introduce CHANGE event type Irina Tirdea
@ 2015-01-25 23:03   ` Jonathan Cameron
  0 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-25 23:03 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> A step detector will generate an interrupt each time N step are detected.
> A device that has such pedometer functionality is Freescale's MMA9553L:
> http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf.
> 
> Introduce IIO_EV_TYPE_CHANGE event type for events that are generated
> when the channel passes a threshold on the absolute change in value.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Applied.
> ---
>  Documentation/ABI/testing/sysfs-bus-iio              | 20 ++++++++++++++++----
>  drivers/iio/industrialio-event.c                     |  1 +
>  .../staging/iio/Documentation/iio_event_monitor.c    |  2 ++
>  include/linux/iio/types.h                            |  1 +
>  4 files changed, 20 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 71dc8db..c03a140 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -891,12 +891,24 @@ Description:
>  		number or direction is not specified, applies to all channels of
>  		this type.
>  
> -What:		/sys/.../events/in_steps_instance_en
> -KernelVersion:	3.19
> +What:		/sys/.../events/in_steps_change_en
> +KernelVersion:	3.20
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> -		Enables or disables step detection. Each time the user takes a step an
> -		event of this type will be generated.
> +		Event generated when channel passes a threshold on the absolute
> +		change in value. E.g. for steps: a step change event is
> +		generated each time the user takes N steps, where N is set using
> +		in_steps_change_value.
> +
> +What:		/sys/.../events/in_steps_change_value
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Specifies the value of change threshold that the
> +		device is comparing against for the events enabled by
> +		<type>[Y][_name]_roc[_rising|falling|]_en. E.g. for steps:
> +		if set to 3, a step change event will be generated every 3
> +		steps.
>  
>  What:		/sys/bus/iio/devices/iio:deviceX/trigger/current_trigger
>  KernelVersion:	2.6.35
> diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
> index 3f5cee0..08c4a4c 100644
> --- a/drivers/iio/industrialio-event.c
> +++ b/drivers/iio/industrialio-event.c
> @@ -198,6 +198,7 @@ static const char * const iio_ev_type_text[] = {
>  	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
>  	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
>  	[IIO_EV_TYPE_INSTANCE] = "instance",
> +	[IIO_EV_TYPE_CHANGE] = "change",
>  };
>  
>  static const char * const iio_ev_dir_text[] = {
> diff --git a/drivers/staging/iio/Documentation/iio_event_monitor.c b/drivers/staging/iio/Documentation/iio_event_monitor.c
> index def236a..2e78d58 100644
> --- a/drivers/staging/iio/Documentation/iio_event_monitor.c
> +++ b/drivers/staging/iio/Documentation/iio_event_monitor.c
> @@ -60,6 +60,7 @@ static const char * const iio_ev_type_text[] = {
>  	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
>  	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
>  	[IIO_EV_TYPE_INSTANCE] = "instance",
> +	[IIO_EV_TYPE_CHANGE] = "change",
>  };
>  
>  static const char * const iio_ev_dir_text[] = {
> @@ -179,6 +180,7 @@ static bool event_is_known(struct iio_event_data *event)
>  	case IIO_EV_TYPE_THRESH_ADAPTIVE:
>  	case IIO_EV_TYPE_MAG_ADAPTIVE:
>  	case IIO_EV_TYPE_INSTANCE:
> +	case IIO_EV_TYPE_CHANGE:
>  		break;
>  	default:
>  		return false;
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index c3601c2..3ba3d66 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -79,6 +79,7 @@ enum iio_event_type {
>  	IIO_EV_TYPE_THRESH_ADAPTIVE,
>  	IIO_EV_TYPE_MAG_ADAPTIVE,
>  	IIO_EV_TYPE_INSTANCE,
> +	IIO_EV_TYPE_CHANGE,
>  };
>  
>  enum iio_event_info {
> 


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

* Re: [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
  2015-01-11 19:10 ` [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD Irina Tirdea
@ 2015-01-25 23:07   ` Jonathan Cameron
  2015-01-26 14:40     ` Daniel Baluta
  0 siblings, 1 reply; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-25 23:07 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> The pedometer needs to filter out false steps that might be generated by
> tapping the foot, sitting, etc. To do that it computes the number of
> steps that occur in a given time and decides the user is moving only
> if this value is over a threshold. E.g.: the user starts moving only
> if he takes 4 steps in 3 seconds. This filter is applied only when
> the user starts moving.
> 
> A device that has such pedometer functionality is Freescale's MMA9553L:
> http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf.
> 
> To export this feature, this patch introduces
> IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD.
> For the pedometer, in_steps_filter_outlier_thresh will specify the number of
> steps that need to occur in in_steps_filter_outlier_period seconds so that
> the pedometer decides the user is moving.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
I wonder if the naming here is infact too generic.  They aren't what
people would normally think of as outliers.  That would be removing
missidentified steps mid way through grabbing data (to my mind).

These are almost a 'debounce' of the transition from stationary to
walking... Maybe there is another better term.  All suggestions welcome!

> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 21 +++++++++++++++++++++
>  drivers/iio/industrialio-core.c         |  2 ++
>  include/linux/iio/iio.h                 |  2 ++
>  3 files changed, 25 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index c03a140..e009f49 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -1193,3 +1193,24 @@ Description:
>  		This attribute is used to read the current speed value of the
>  		user (which is the norm or magnitude of the velocity vector).
>  		Units after application of scale are m/s.
> +
> +What:		/sys/.../iio:deviceX/in_steps_filter_outliers_period
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Specifies the number of seconds in which we compute the
> +		values so we can decide if they are outlier values and
> +		need to be filter out. These computed values are then
> +		compared with in_steps_filter_outliers_thresh. E.g. for steps:
> +		specifies number of seconds in which we compute the steps
> +		that occur in order to decide if the consumer is making steps.
> +
> +What:		/sys/.../iio:deviceX/in_steps_filter_outliers_thresh
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Specifies a threshold for filtering outlier values: if value
> +		measured in in_steps_filter_outliers_period seconds is below
> +		threshold, we filter it out. E.g. for steps: specifies number
> +		of steps that must occur within in_steps_filter_outliers_period
> +		for the pedometer to decide the consumer is making steps.
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 4ee6fdf..81678b3 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -126,6 +126,8 @@ static const char * const iio_chan_info_postfix[] = {
>  	[IIO_CHAN_INFO_ENABLE] = "en",
>  	[IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
>  	[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
> +	[IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH] = "filter_outliers_thresh",
> +	[IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD] = "filter_outliers_period",
>  };
>  
>  /**
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index 752a929..b6b12ac 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -41,6 +41,8 @@ enum iio_chan_info_enum {
>  	IIO_CHAN_INFO_ENABLE,
>  	IIO_CHAN_INFO_CALIBHEIGHT,
>  	IIO_CHAN_INFO_CALIBWEIGHT,
> +	IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH,
> +	IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD,
>  };
>  
>  enum iio_shared_by {
> 


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

* Re: [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
  2015-01-25 23:07   ` Jonathan Cameron
@ 2015-01-26 14:40     ` Daniel Baluta
  2015-01-26 19:01       ` Jonathan Cameron
  0 siblings, 1 reply; 33+ messages in thread
From: Daniel Baluta @ 2015-01-26 14:40 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Irina Tirdea, linux-iio, Linux Kernel Mailing List, Vlad Dogaru,
	Daniel Baluta, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald

On Mon, Jan 26, 2015 at 1:07 AM, Jonathan Cameron <jic23@kernel.org> wrote:
> On 11/01/15 19:10, Irina Tirdea wrote:
>> The pedometer needs to filter out false steps that might be generated by
>> tapping the foot, sitting, etc. To do that it computes the number of
>> steps that occur in a given time and decides the user is moving only
>> if this value is over a threshold. E.g.: the user starts moving only
>> if he takes 4 steps in 3 seconds. This filter is applied only when
>> the user starts moving.
>>
>> A device that has such pedometer functionality is Freescale's MMA9553L:
>> http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf.
>>
>> To export this feature, this patch introduces
>> IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD.
>> For the pedometer, in_steps_filter_outlier_thresh will specify the number of
>> steps that need to occur in in_steps_filter_outlier_period seconds so that
>> the pedometer decides the user is moving.
>>
>> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> I wonder if the naming here is infact too generic.  They aren't what
> people would normally think of as outliers.  That would be removing
> missidentified steps mid way through grabbing data (to my mind).
>
> These are almost a 'debounce' of the transition from stationary to
> walking... Maybe there is another better term.  All suggestions welcome!

What about:

in_steps_debounce_count instead of in_steps_filter_outlier_thresh
and
in_steps_debounce_delay instead of in_steps_filter_outlier_period.

Also in_steps_debounce_time could work for in_steps_filter_outlier_period.

>
>> ---
>>  Documentation/ABI/testing/sysfs-bus-iio | 21 +++++++++++++++++++++
>>  drivers/iio/industrialio-core.c         |  2 ++
>>  include/linux/iio/iio.h                 |  2 ++
>>  3 files changed, 25 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>> index c03a140..e009f49 100644
>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>> @@ -1193,3 +1193,24 @@ Description:
>>               This attribute is used to read the current speed value of the
>>               user (which is the norm or magnitude of the velocity vector).
>>               Units after application of scale are m/s.
>> +
>> +What:                /sys/.../iio:deviceX/in_steps_filter_outliers_period
>> +KernelVersion:       3.20
>> +Contact:     linux-iio@vger.kernel.org
>> +Description:
>> +             Specifies the number of seconds in which we compute the
>> +             values so we can decide if they are outlier values and
>> +             need to be filter out. These computed values are then
>> +             compared with in_steps_filter_outliers_thresh. E.g. for steps:
>> +             specifies number of seconds in which we compute the steps
>> +             that occur in order to decide if the consumer is making steps.
>> +
>> +What:                /sys/.../iio:deviceX/in_steps_filter_outliers_thresh
>> +KernelVersion:       3.20
>> +Contact:     linux-iio@vger.kernel.org
>> +Description:
>> +             Specifies a threshold for filtering outlier values: if value
>> +             measured in in_steps_filter_outliers_period seconds is below
>> +             threshold, we filter it out. E.g. for steps: specifies number
>> +             of steps that must occur within in_steps_filter_outliers_period
>> +             for the pedometer to decide the consumer is making steps.
>> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
>> index 4ee6fdf..81678b3 100644
>> --- a/drivers/iio/industrialio-core.c
>> +++ b/drivers/iio/industrialio-core.c
>> @@ -126,6 +126,8 @@ static const char * const iio_chan_info_postfix[] = {
>>       [IIO_CHAN_INFO_ENABLE] = "en",
>>       [IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
>>       [IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
>> +     [IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH] = "filter_outliers_thresh",
>> +     [IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD] = "filter_outliers_period",
>>  };
>>
>>  /**
>> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>> index 752a929..b6b12ac 100644
>> --- a/include/linux/iio/iio.h
>> +++ b/include/linux/iio/iio.h
>> @@ -41,6 +41,8 @@ enum iio_chan_info_enum {
>>       IIO_CHAN_INFO_ENABLE,
>>       IIO_CHAN_INFO_CALIBHEIGHT,
>>       IIO_CHAN_INFO_CALIBWEIGHT,
>> +     IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH,
>> +     IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD,
>>  };
>>
>>  enum iio_shared_by {
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
  2015-01-26 14:40     ` Daniel Baluta
@ 2015-01-26 19:01       ` Jonathan Cameron
  2015-01-27 16:20           ` Tirdea, Irina
  0 siblings, 1 reply; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-26 19:01 UTC (permalink / raw)
  To: Daniel Baluta
  Cc: Irina Tirdea, linux-iio, Linux Kernel Mailing List, Vlad Dogaru,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald

On 26/01/15 14:40, Daniel Baluta wrote:
> On Mon, Jan 26, 2015 at 1:07 AM, Jonathan Cameron <jic23@kernel.org> wrote:
>> On 11/01/15 19:10, Irina Tirdea wrote:
>>> The pedometer needs to filter out false steps that might be generated by
>>> tapping the foot, sitting, etc. To do that it computes the number of
>>> steps that occur in a given time and decides the user is moving only
>>> if this value is over a threshold. E.g.: the user starts moving only
>>> if he takes 4 steps in 3 seconds. This filter is applied only when
>>> the user starts moving.
>>>
>>> A device that has such pedometer functionality is Freescale's MMA9553L:
>>> http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf.
>>>
>>> To export this feature, this patch introduces
>>> IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD.
>>> For the pedometer, in_steps_filter_outlier_thresh will specify the number of
>>> steps that need to occur in in_steps_filter_outlier_period seconds so that
>>> the pedometer decides the user is moving.
>>>
>>> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
>> I wonder if the naming here is infact too generic.  They aren't what
>> people would normally think of as outliers.  That would be removing
>> missidentified steps mid way through grabbing data (to my mind).
>>
>> These are almost a 'debounce' of the transition from stationary to
>> walking... Maybe there is another better term.  All suggestions welcome!
> 
> What about:
> 
> in_steps_debounce_count instead of in_steps_filter_outlier_thresh
> and
> in_steps_debounce_delay instead of in_steps_filter_outlier_period.
> 
> Also in_steps_debounce_time could work for in_steps_filter_outlier_period.
Hmm. Slight preference for debounce_time. 

I can't come up with a better option, but we'd best see if anyone else has
a better idea for a day or two!
> 
>>
>>> ---
>>>  Documentation/ABI/testing/sysfs-bus-iio | 21 +++++++++++++++++++++
>>>  drivers/iio/industrialio-core.c         |  2 ++
>>>  include/linux/iio/iio.h                 |  2 ++
>>>  3 files changed, 25 insertions(+)
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>>> index c03a140..e009f49 100644
>>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>>> @@ -1193,3 +1193,24 @@ Description:
>>>               This attribute is used to read the current speed value of the
>>>               user (which is the norm or magnitude of the velocity vector).
>>>               Units after application of scale are m/s.
>>> +
>>> +What:                /sys/.../iio:deviceX/in_steps_filter_outliers_period
>>> +KernelVersion:       3.20
>>> +Contact:     linux-iio@vger.kernel.org
>>> +Description:
>>> +             Specifies the number of seconds in which we compute the
>>> +             values so we can decide if they are outlier values and
>>> +             need to be filter out. These computed values are then
>>> +             compared with in_steps_filter_outliers_thresh. E.g. for steps:
>>> +             specifies number of seconds in which we compute the steps
>>> +             that occur in order to decide if the consumer is making steps.
>>> +
>>> +What:                /sys/.../iio:deviceX/in_steps_filter_outliers_thresh
>>> +KernelVersion:       3.20
>>> +Contact:     linux-iio@vger.kernel.org
>>> +Description:
>>> +             Specifies a threshold for filtering outlier values: if value
>>> +             measured in in_steps_filter_outliers_period seconds is below
>>> +             threshold, we filter it out. E.g. for steps: specifies number
>>> +             of steps that must occur within in_steps_filter_outliers_period
>>> +             for the pedometer to decide the consumer is making steps.
>>> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
>>> index 4ee6fdf..81678b3 100644
>>> --- a/drivers/iio/industrialio-core.c
>>> +++ b/drivers/iio/industrialio-core.c
>>> @@ -126,6 +126,8 @@ static const char * const iio_chan_info_postfix[] = {
>>>       [IIO_CHAN_INFO_ENABLE] = "en",
>>>       [IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
>>>       [IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
>>> +     [IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH] = "filter_outliers_thresh",
>>> +     [IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD] = "filter_outliers_period",
>>>  };
>>>
>>>  /**
>>> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>>> index 752a929..b6b12ac 100644
>>> --- a/include/linux/iio/iio.h
>>> +++ b/include/linux/iio/iio.h
>>> @@ -41,6 +41,8 @@ enum iio_chan_info_enum {
>>>       IIO_CHAN_INFO_ENABLE,
>>>       IIO_CHAN_INFO_CALIBHEIGHT,
>>>       IIO_CHAN_INFO_CALIBWEIGHT,
>>> +     IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH,
>>> +     IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD,
>>>  };
>>>
>>>  enum iio_shared_by {
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* Re: [PATCH v2 06/10] iio: core: Remove IIO_EV_TYPE_INSTANCE
  2015-01-11 19:10 ` [PATCH v2 06/10] iio: core: Remove IIO_EV_TYPE_INSTANCE Irina Tirdea
@ 2015-01-26 19:04   ` Jonathan Cameron
  0 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-26 19:04 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> By introducing IIO_EV_TYPE_CHANGE, IIO_EV_TYPE_INSTANCE becomes redundant.
> The effect of IIO_EV_TYPE_INSTANCE can be obtained by using IIO_EV_TYPE_CHANGE
> with IIO_EV_INFO_VALUE set to 1.
> 
> Remove all instances of IIO_EV_TYPE_INSTANCE and replace them with
> IIO_EV_TYPE_CHANGE where needed.
> 
Definitely a more general interfaces so good.

Applied to the togreg branch of iio.git

Thanks,

Jonathan
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> ---
>  drivers/iio/industrialio-event.c                      | 1 -
>  drivers/staging/iio/Documentation/iio_event_monitor.c | 2 --
>  drivers/staging/iio/iio_simple_dummy.c                | 2 +-
>  drivers/staging/iio/iio_simple_dummy_events.c         | 4 ++--
>  include/linux/iio/types.h                             | 1 -
>  5 files changed, 3 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
> index 08c4a4c..b5663f6 100644
> --- a/drivers/iio/industrialio-event.c
> +++ b/drivers/iio/industrialio-event.c
> @@ -197,7 +197,6 @@ static const char * const iio_ev_type_text[] = {
>  	[IIO_EV_TYPE_ROC] = "roc",
>  	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
>  	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
> -	[IIO_EV_TYPE_INSTANCE] = "instance",
>  	[IIO_EV_TYPE_CHANGE] = "change",
>  };
>  
> diff --git a/drivers/staging/iio/Documentation/iio_event_monitor.c b/drivers/staging/iio/Documentation/iio_event_monitor.c
> index 2e78d58..72c96aa 100644
> --- a/drivers/staging/iio/Documentation/iio_event_monitor.c
> +++ b/drivers/staging/iio/Documentation/iio_event_monitor.c
> @@ -59,7 +59,6 @@ static const char * const iio_ev_type_text[] = {
>  	[IIO_EV_TYPE_ROC] = "roc",
>  	[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
>  	[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
> -	[IIO_EV_TYPE_INSTANCE] = "instance",
>  	[IIO_EV_TYPE_CHANGE] = "change",
>  };
>  
> @@ -179,7 +178,6 @@ static bool event_is_known(struct iio_event_data *event)
>  	case IIO_EV_TYPE_ROC:
>  	case IIO_EV_TYPE_THRESH_ADAPTIVE:
>  	case IIO_EV_TYPE_MAG_ADAPTIVE:
> -	case IIO_EV_TYPE_INSTANCE:
>  	case IIO_EV_TYPE_CHANGE:
>  		break;
>  	default:
> diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
> index 0b8611a..e452021 100644
> --- a/drivers/staging/iio/iio_simple_dummy.c
> +++ b/drivers/staging/iio/iio_simple_dummy.c
> @@ -73,7 +73,7 @@ static const struct iio_event_spec iio_dummy_event = {
>   * simple step detect event - triggered when a step is detected
>   */
>  static const struct iio_event_spec step_detect_event = {
> -	.type = IIO_EV_TYPE_INSTANCE,
> +	.type = IIO_EV_TYPE_CHANGE,
>  	.dir = IIO_EV_DIR_NONE,
>  	.mask_separate = BIT(IIO_EV_INFO_ENABLE),
>  };
> diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c
> index ac15a44..a5cd3bb 100644
> --- a/drivers/staging/iio/iio_simple_dummy_events.c
> +++ b/drivers/staging/iio/iio_simple_dummy_events.c
> @@ -86,7 +86,7 @@ int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
>  		}
>  	case IIO_STEPS:
>  		switch (type) {
> -		case IIO_EV_TYPE_INSTANCE:
> +		case IIO_EV_TYPE_CHANGE:
>  			st->event_en = state;
>  			break;
>  		default:
> @@ -201,7 +201,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
>  		iio_push_event(indio_dev,
>  			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
>  					      IIO_EV_DIR_NONE,
> -					      IIO_EV_TYPE_INSTANCE, 0, 0, 0),
> +					      IIO_EV_TYPE_CHANGE, 0, 0, 0),
>  			       iio_get_time_ns());
>  		break;
>  	default:
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index 3ba3d66..580ed5b 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -78,7 +78,6 @@ enum iio_event_type {
>  	IIO_EV_TYPE_ROC,
>  	IIO_EV_TYPE_THRESH_ADAPTIVE,
>  	IIO_EV_TYPE_MAG_ADAPTIVE,
> -	IIO_EV_TYPE_INSTANCE,
>  	IIO_EV_TYPE_CHANGE,
>  };
>  
> 


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

* Re: [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support
  2015-01-11 19:10 ` [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support Irina Tirdea
@ 2015-01-26 19:08   ` Jonathan Cameron
  2015-01-27 17:18       ` Tirdea, Irina
  0 siblings, 1 reply; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-26 19:08 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> Add support for runtime pm to reduce the power consumed by the device
> when not used.
> 
> If CONFIG_PM is not enabled, the device will be powered on at
> init and only powered off on system suspend.
> 
> If CONFIG_PM is enabled, runtime pm autosuspend is used:
> - for raw reads will keep the device on for a specified time
> - for events it will keep the device on as long as we have at least
> one event active
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> Reviewed-by: Vlad Dogaru <vlad.dogaru@intel.com>
Looks good.

Applied to the togreg branch of iio.git
(at least we are driving down the size of the patch set for the next
revision!)

Jonathan
> ---
>  drivers/iio/accel/mma9551.c | 162 +++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 139 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
> index 6563e26..f1a5a06 100644
> --- a/drivers/iio/accel/mma9551.c
> +++ b/drivers/iio/accel/mma9551.c
> @@ -22,6 +22,7 @@
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
>  #include <linux/iio/events.h>
> +#include <linux/pm_runtime.h>
>  
>  #define MMA9551_DRV_NAME		"mma9551"
>  #define MMA9551_IRQ_NAME		"mma9551_event"
> @@ -71,6 +72,7 @@ enum mma9551_gpio_pin {
>  /* Sleep/Wake application */
>  #define MMA9551_SLEEP_CFG		0x06
>  #define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
> +#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
>  #define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
>  
>  /* AFE application */
> @@ -114,6 +116,9 @@ enum mma9551_tilt_axis {
>  #define MMA9551_I2C_READ_RETRIES	5
>  #define MMA9551_I2C_READ_DELAY	50	/* us */
>  
> +#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
> +#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
> +
>  struct mma9551_mbox_request {
>  	u8 start_mbox;		/* Always 0. */
>  	u8 app_id;
> @@ -387,16 +392,55 @@ static int mma9551_read_version(struct i2c_client *client)
>  }
>  
>  /*
> + * Power on chip and enable doze mode.
>   * Use 'false' as the second parameter to cause the device to enter
>   * sleep.
>   */
> -static int mma9551_set_device_state(struct i2c_client *client,
> -				    bool enable)
> +static int mma9551_set_device_state(struct i2c_client *client, bool enable)
>  {
>  	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
>  					  MMA9551_SLEEP_CFG,
> -					  MMA9551_SLEEP_CFG_SNCEN,
> -					  enable ? 0 : MMA9551_SLEEP_CFG_SNCEN);
> +					  MMA9551_SLEEP_CFG_SNCEN |
> +					  MMA9551_SLEEP_CFG_FLEEN |
> +					  MMA9551_SLEEP_CFG_SCHEN,
> +					  enable ? MMA9551_SLEEP_CFG_SCHEN |
> +					  MMA9551_SLEEP_CFG_FLEEN :
> +					  MMA9551_SLEEP_CFG_SNCEN);
> +}
> +
> +static int mma9551_set_power_state(struct i2c_client *client, bool on)
> +{
> +#ifdef CONFIG_PM
> +	int ret;
> +
> +	if (on)
> +		ret = pm_runtime_get_sync(&client->dev);
> +	else {
> +		pm_runtime_mark_last_busy(&client->dev);
> +		ret = pm_runtime_put_autosuspend(&client->dev);
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev,
> +			"failed to change power state to %d\n", on);
> +		if (on)
> +			pm_runtime_put_noidle(&client->dev);
> +
> +		return ret;
> +	}
> +#endif
> +
> +	return 0;
> +}
> +
> +static void mma9551_sleep(int freq)
> +{
> +	int sleep_val = 1000 / freq;
> +
> +	if (sleep_val < 20)
> +		usleep_range(sleep_val * 1000, 20000);
> +	else
> +		msleep_interruptible(sleep_val);
>  }
>  
>  static int mma9551_read_incli_chan(struct i2c_client *client,
> @@ -424,15 +468,19 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
>  		return -EINVAL;
>  	}
>  
> +	ret = mma9551_set_power_state(client, true);
> +	if (ret < 0)
> +		return ret;
> +
>  	ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
>  				       reg_addr, &angle);
>  	if (ret < 0)
> -		return ret;
> +		goto out_poweroff;
>  
>  	ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
>  				       MMA9551_TILT_QUAD_REG, &quadrant);
>  	if (ret < 0)
> -		return ret;
> +		goto out_poweroff;
>  
>  	angle &= ~MMA9551_TILT_ANGFLG;
>  	quadrant = (quadrant >> quad_shift) & 0x03;
> @@ -442,7 +490,11 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
>  	else
>  		*val = angle + 90 * quadrant;
>  
> -	return IIO_VAL_INT;
> +	ret = IIO_VAL_INT;
> +
> +out_poweroff:
> +	mma9551_set_power_state(client, false);
> +	return ret;
>  }
>  
>  static int mma9551_read_accel_chan(struct i2c_client *client,
> @@ -467,14 +519,22 @@ static int mma9551_read_accel_chan(struct i2c_client *client,
>  		return -EINVAL;
>  	}
>  
> +	ret = mma9551_set_power_state(client, true);
> +	if (ret < 0)
> +		return ret;
> +
>  	ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
>  				       reg_addr, &raw_accel);
>  	if (ret < 0)
> -		return ret;
> +		goto out_poweroff;
>  
>  	*val = raw_accel;
>  
> -	return IIO_VAL_INT;
> +	ret = IIO_VAL_INT;
> +
> +out_poweroff:
> +	mma9551_set_power_state(client, false);
> +	return ret;
>  }
>  
>  static int mma9551_read_raw(struct iio_dev *indio_dev,
> @@ -556,6 +616,10 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
>  					  MMA9551_APPID_NONE, 0, 0);
>  		if (ret < 0)
>  			return ret;
> +
> +		ret = mma9551_set_power_state(data->client, false);
> +		if (ret < 0)
> +			return ret;
>  	} else {
>  		int bitnum;
>  
> @@ -574,11 +638,18 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
>  			return -EINVAL;
>  		}
>  
> +
Trivial but one blank line here is enough...
> +		ret = mma9551_set_power_state(data->client, true);
> +		if (ret < 0)
> +			return ret;
> +
>  		ret = mma9551_gpio_config(data->client,
>  					  (enum mma9551_gpio_pin)mma_axis,
>  					  MMA9551_APPID_TILT, bitnum, 0);
> -		if (ret < 0)
> +		if (ret < 0) {
> +			mma9551_set_power_state(data->client, false);
>  			return ret;
> +		}
>  	}
>  
>  	data->event_enabled[mma_axis] = state;
> @@ -771,12 +842,7 @@ static int mma9551_init(struct mma9551_data *data)
>  	if (ret)
>  		return ret;
>  
> -	/* Power on chip and enable doze mode. */
> -	return mma9551_update_config_bits(data->client,
> -			 MMA9551_APPID_SLEEP_WAKE,
> -			 MMA9551_SLEEP_CFG,
> -			 MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN,
> -			 MMA9551_SLEEP_CFG_SCHEN);
> +	return mma9551_set_device_state(data->client, true);
>  }
>  
>  static int mma9551_gpio_probe(struct iio_dev *indio_dev)
> @@ -869,8 +935,19 @@ static int mma9551_probe(struct i2c_client *client,
>  		goto out_poweroff;
>  	}
>  
> +	ret = pm_runtime_set_active(&client->dev);
> +	if (ret < 0)
> +		goto out_iio_unregister;
> +
> +	pm_runtime_enable(&client->dev);
> +	pm_runtime_set_autosuspend_delay(&client->dev,
> +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
> +	pm_runtime_use_autosuspend(&client->dev);
> +
>  	return 0;
>  
> +out_iio_unregister:
> +	iio_device_unregister(indio_dev);
>  out_poweroff:
>  	mma9551_set_device_state(client, false);
>  
> @@ -882,6 +959,10 @@ static int mma9551_remove(struct i2c_client *client)
>  	struct iio_dev *indio_dev = i2c_get_clientdata(client);
>  	struct mma9551_data *data = iio_priv(indio_dev);
>  
> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_put_noidle(&client->dev);
> +
>  	iio_device_unregister(indio_dev);
>  	mutex_lock(&data->mutex);
>  	mma9551_set_device_state(data->client, false);
> @@ -890,37 +971,72 @@ static int mma9551_remove(struct i2c_client *client)
>  	return 0;
>  }
>  
> +#ifdef CONFIG_PM
> +static int mma9551_runtime_suspend(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct mma9551_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&data->mutex);
> +	ret = mma9551_set_device_state(data->client, false);
> +	mutex_unlock(&data->mutex);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev, "powering off device failed\n");
> +		return -EAGAIN;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mma9551_runtime_resume(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct mma9551_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = mma9551_set_device_state(data->client, true);
> +	if (ret < 0)
> +		return ret;
> +
> +	mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
> +
> +	return 0;
> +}
> +#endif
> +
>  #ifdef CONFIG_PM_SLEEP
>  static int mma9551_suspend(struct device *dev)
>  {
>  	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>  	struct mma9551_data *data = iio_priv(indio_dev);
> +	int ret;
>  
>  	mutex_lock(&data->mutex);
> -	mma9551_set_device_state(data->client, false);
> +	ret = mma9551_set_device_state(data->client, false);
>  	mutex_unlock(&data->mutex);
>  
> -	return 0;
> +	return ret;
>  }
>  
>  static int mma9551_resume(struct device *dev)
>  {
>  	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>  	struct mma9551_data *data = iio_priv(indio_dev);
> +	int ret;
>  
>  	mutex_lock(&data->mutex);
> -	mma9551_set_device_state(data->client, true);
> +	ret = mma9551_set_device_state(data->client, true);
>  	mutex_unlock(&data->mutex);
>  
> -	return 0;
> +	return ret;
>  }
> -#else
> -#define mma9551_suspend NULL
> -#define mma9551_resume NULL
>  #endif
>  
>  static const struct dev_pm_ops mma9551_pm_ops = {
>  	SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
> +	SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
> +			   mma9551_runtime_resume, NULL)
>  };
>  
>  static const struct acpi_device_id mma9551_acpi_match[] = {
> 


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

* Re: [PATCH v2 09/10] iio: accel: mma9551: split driver to expose mma955x api
  2015-01-11 19:10 ` [PATCH v2 09/10] iio: accel: mma9551: split driver to expose mma955x api Irina Tirdea
@ 2015-01-26 19:25   ` Jonathan Cameron
  0 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-26 19:25 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> Freescale has the MMA955xL family of devices that use the
> same communication protocol (based on i2c messages):
> http://www.freescale.com/files/sensors/doc/data_sheet/MMA955xL.pdf.
> 
> To support more devices from this family, we need to split the
> mma9551 driver so we can export the common functions that will
> be used by other mma955x drivers.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> Reviewed-by: Vlad Dogaru <vlad.dogaru@intel.com>
On the basis the next patch is very nearly there and hence
there is a reason to do this, applied to the togreg branch of iio.git.
As ever, initially pushed out as testing for the autobuilders to play
with it.

Jonathan
> ---
>  drivers/iio/accel/Kconfig        |   5 +
>  drivers/iio/accel/Makefile       |   4 +-
>  drivers/iio/accel/mma9551.c      | 443 +---------------------------
>  drivers/iio/accel/mma9551_core.c | 615 +++++++++++++++++++++++++++++++++++++++
>  drivers/iio/accel/mma9551_core.h |  66 +++++
>  5 files changed, 693 insertions(+), 440 deletions(-)
>  create mode 100644 drivers/iio/accel/mma9551_core.c
>  create mode 100644 drivers/iio/accel/mma9551_core.h
> 
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index 9f67c10..c53047d 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -111,9 +111,14 @@ config KXCJK1013
>  	  To compile this driver as a module, choose M here: the module will
>  	  be called kxcjk-1013.
>  
> +config MMA9551_CORE
> +	tristate
> +
>  config MMA9551
>  	tristate "Freescale MMA9551L Intelligent Motion-Sensing Platform Driver"
>  	depends on I2C
> +	select MMA9551_CORE
> +
>  	help
>  	  Say yes here to build support for the Freescale MMA9551L
>  	  Intelligent Motion-Sensing Platform Driver.
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index de5b9cb..8105316 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -9,7 +9,9 @@ obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
>  obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
>  obj-$(CONFIG_KXSD9)	+= kxsd9.o
>  obj-$(CONFIG_MMA8452)	+= mma8452.o
> -obj-$(CONFIG_MMA9551)	+= mma9551.o
> +
> +obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
> +obj-$(CONFIG_MMA9551)		+= mma9551.o
>  
>  obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
>  st_accel-y := st_accel_core.o
> diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
> index f1a5a06..46c3835 100644
> --- a/drivers/iio/accel/mma9551.c
> +++ b/drivers/iio/accel/mma9551.c
> @@ -23,63 +23,13 @@
>  #include <linux/iio/sysfs.h>
>  #include <linux/iio/events.h>
>  #include <linux/pm_runtime.h>
> +#include "mma9551_core.h"
>  
>  #define MMA9551_DRV_NAME		"mma9551"
>  #define MMA9551_IRQ_NAME		"mma9551_event"
>  #define MMA9551_GPIO_NAME		"mma9551_int"
>  #define MMA9551_GPIO_COUNT		4
>  
> -/* Applications IDs */
> -#define MMA9551_APPID_VERSION		0x00
> -#define MMA9551_APPID_GPIO		0x03
> -#define MMA9551_APPID_AFE		0x06
> -#define MMA9551_APPID_TILT		0x0B
> -#define MMA9551_APPID_SLEEP_WAKE	0x12
> -#define MMA9551_APPID_RESET		0x17
> -#define MMA9551_APPID_NONE		0xff
> -
> -/* Command masks for mailbox write command */
> -#define MMA9551_CMD_READ_VERSION_INFO	0x00
> -#define MMA9551_CMD_READ_CONFIG		0x10
> -#define MMA9551_CMD_WRITE_CONFIG	0x20
> -#define MMA9551_CMD_READ_STATUS		0x30
> -
> -enum mma9551_gpio_pin {
> -	mma9551_gpio6 = 0,
> -	mma9551_gpio7,
> -	mma9551_gpio8,
> -	mma9551_gpio9,
> -	mma9551_gpio_max = mma9551_gpio9,
> -};
> -
> -/* Mailbox read command */
> -#define MMA9551_RESPONSE_COCO		BIT(7)
> -
> -/* Error-Status codes returned in mailbox read command */
> -#define MMA9551_MCI_ERROR_NONE			0x00
> -#define MMA9551_MCI_ERROR_PARAM			0x04
> -#define MMA9551_MCI_INVALID_COUNT		0x19
> -#define MMA9551_MCI_ERROR_COMMAND		0x1C
> -#define MMA9551_MCI_ERROR_INVALID_LENGTH	0x21
> -#define MMA9551_MCI_ERROR_FIFO_BUSY		0x22
> -#define MMA9551_MCI_ERROR_FIFO_ALLOCATED	0x23
> -#define MMA9551_MCI_ERROR_FIFO_OVERSIZE		0x24
> -
> -/* GPIO Application */
> -#define MMA9551_GPIO_POL_MSB		0x08
> -#define MMA9551_GPIO_POL_LSB		0x09
> -
> -/* Sleep/Wake application */
> -#define MMA9551_SLEEP_CFG		0x06
> -#define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
> -#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
> -#define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
> -
> -/* AFE application */
> -#define MMA9551_AFE_X_ACCEL_REG		0x00
> -#define MMA9551_AFE_Y_ACCEL_REG		0x02
> -#define MMA9551_AFE_Z_ACCEL_REG		0x04
> -
>  /* Tilt application (inclination in IIO terms). */
>  #define MMA9551_TILT_XZ_ANG_REG		0x00
>  #define MMA9551_TILT_YZ_ANG_REG		0x01
> @@ -92,6 +42,8 @@ enum mma9551_gpio_pin {
>  #define MMA9551_TILT_CFG_REG		0x01
>  #define MMA9551_TILT_ANG_THRESH_MASK	GENMASK(3, 0)
>  
> +#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
> +
>  /* Tilt events are mapped to the first three GPIO pins. */
>  enum mma9551_tilt_axis {
>  	mma9551_x = 0,
> @@ -99,64 +51,6 @@ enum mma9551_tilt_axis {
>  	mma9551_z,
>  };
>  
> -/*
> - * A response is composed of:
> - * - control registers: MB0-3
> - * - data registers: MB4-31
> - *
> - * A request is composed of:
> - * - mbox to write to (always 0)
> - * - control registers: MB1-4
> - * - data registers: MB5-31
> - */
> -#define MMA9551_MAILBOX_CTRL_REGS	4
> -#define MMA9551_MAX_MAILBOX_DATA_REGS	28
> -#define MMA9551_MAILBOX_REGS		32
> -
> -#define MMA9551_I2C_READ_RETRIES	5
> -#define MMA9551_I2C_READ_DELAY	50	/* us */
> -
> -#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
> -#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
> -
> -struct mma9551_mbox_request {
> -	u8 start_mbox;		/* Always 0. */
> -	u8 app_id;
> -	/*
> -	 * See Section 5.3.1 of the MMA955xL Software Reference Manual.
> -	 *
> -	 * Bit 7: reserved, always 0
> -	 * Bits 6-4: command
> -	 * Bits 3-0: upper bits of register offset
> -	 */
> -	u8 cmd_off;
> -	u8 lower_off;
> -	u8 nbytes;
> -	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
> -} __packed;
> -
> -struct mma9551_mbox_response {
> -	u8 app_id;
> -	/*
> -	 * See Section 5.3.3 of the MMA955xL Software Reference Manual.
> -	 *
> -	 * Bit 7: COCO
> -	 * Bits 6-0: Error code.
> -	 */
> -	u8 coco_err;
> -	u8 nbytes;
> -	u8 req_bytes;
> -	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> -} __packed;
> -
> -struct mma9551_version_info {
> -	__be32 device_id;
> -	u8 rom_version[2];
> -	u8 fw_version[2];
> -	u8 hw_version[2];
> -	u8 fw_build[2];
> -};
> -
>  struct mma9551_data {
>  	struct i2c_client *client;
>  	struct mutex mutex;
> @@ -164,285 +58,6 @@ struct mma9551_data {
>  	int irqs[MMA9551_GPIO_COUNT];
>  };
>  
> -static int mma9551_transfer(struct i2c_client *client,
> -			    u8 app_id, u8 command, u16 offset,
> -			    u8 *inbytes, int num_inbytes,
> -			    u8 *outbytes, int num_outbytes)
> -{
> -	struct mma9551_mbox_request req;
> -	struct mma9551_mbox_response rsp;
> -	struct i2c_msg in, out;
> -	u8 req_len, err_code;
> -	int ret, retries;
> -
> -	if (offset >= 1 << 12) {
> -		dev_err(&client->dev, "register offset too large\n");
> -		return -EINVAL;
> -	}
> -
> -	req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
> -	req.start_mbox = 0;
> -	req.app_id = app_id;
> -	req.cmd_off = command | (offset >> 8);
> -	req.lower_off = offset;
> -
> -	if (command == MMA9551_CMD_WRITE_CONFIG)
> -		req.nbytes = num_inbytes;
> -	else
> -		req.nbytes = num_outbytes;
> -	if (num_inbytes)
> -		memcpy(req.buf, inbytes, num_inbytes);
> -
> -	out.addr = client->addr;
> -	out.flags = 0;
> -	out.len = req_len;
> -	out.buf = (u8 *)&req;
> -
> -	ret = i2c_transfer(client->adapter, &out, 1);
> -	if (ret < 0) {
> -		dev_err(&client->dev, "i2c write failed\n");
> -		return ret;
> -	}
> -
> -	retries = MMA9551_I2C_READ_RETRIES;
> -	do {
> -		udelay(MMA9551_I2C_READ_DELAY);
> -
> -		in.addr = client->addr;
> -		in.flags = I2C_M_RD;
> -		in.len = sizeof(rsp);
> -		in.buf = (u8 *)&rsp;
> -
> -		ret = i2c_transfer(client->adapter, &in, 1);
> -		if (ret < 0) {
> -			dev_err(&client->dev, "i2c read failed\n");
> -			return ret;
> -		}
> -
> -		if (rsp.coco_err & MMA9551_RESPONSE_COCO)
> -			break;
> -	} while (--retries > 0);
> -
> -	if (retries == 0) {
> -		dev_err(&client->dev,
> -			"timed out while waiting for command response\n");
> -		return -ETIMEDOUT;
> -	}
> -
> -	if (rsp.app_id != app_id) {
> -		dev_err(&client->dev,
> -			"app_id mismatch in response got %02x expected %02x\n",
> -			rsp.app_id, app_id);
> -		return -EINVAL;
> -	}
> -
> -	err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
> -	if (err_code != MMA9551_MCI_ERROR_NONE) {
> -		dev_err(&client->dev, "read returned error %x\n", err_code);
> -		return -EINVAL;
> -	}
> -
> -	if (rsp.nbytes != rsp.req_bytes) {
> -		dev_err(&client->dev,
> -			"output length mismatch got %d expected %d\n",
> -			rsp.nbytes, rsp.req_bytes);
> -		return -EINVAL;
> -	}
> -
> -	if (num_outbytes)
> -		memcpy(outbytes, rsp.buf, num_outbytes);
> -
> -	return 0;
> -}
> -
> -static int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
> -				    u16 reg, u8 *val)
> -{
> -	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> -				reg, NULL, 0, val, 1);
> -}
> -
> -static int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
> -				     u16 reg, u8 val)
> -{
> -	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
> -				&val, 1, NULL, 0);
> -}
> -
> -static int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
> -				    u16 reg, u8 *val)
> -{
> -	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
> -				reg, NULL, 0, val, 1);
> -}
> -
> -static int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
> -				    u16 reg, u16 *val)
> -{
> -	int ret;
> -	__be16 v;
> -
> -	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
> -			       reg, NULL, 0, (u8 *)&v, 2);
> -	*val = be16_to_cpu(v);
> -
> -	return ret;
> -}
> -
> -static int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
> -				      u16 reg, u8 mask, u8 val)
> -{
> -	int ret;
> -	u8 tmp, orig;
> -
> -	ret = mma9551_read_config_byte(client, app_id, reg, &orig);
> -	if (ret < 0)
> -		return ret;
> -
> -	tmp = orig & ~mask;
> -	tmp |= val & mask;
> -
> -	if (tmp == orig)
> -		return 0;
> -
> -	return mma9551_write_config_byte(client, app_id, reg, tmp);
> -}
> -
> -/*
> - * The polarity parameter is described in section 6.2.2, page 66, of the
> - * Software Reference Manual.  Basically, polarity=0 means the interrupt
> - * line has the same value as the selected bit, while polarity=1 means
> - * the line is inverted.
> - */
> -static int mma9551_gpio_config(struct i2c_client *client,
> -			       enum mma9551_gpio_pin pin,
> -			       u8 app_id, u8 bitnum, int polarity)
> -{
> -	u8 reg, pol_mask, pol_val;
> -	int ret;
> -
> -	if (pin > mma9551_gpio_max) {
> -		dev_err(&client->dev, "bad GPIO pin\n");
> -		return -EINVAL;
> -	}
> -
> -	/*
> -	 * Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
> -	 * 0x03, and so on.
> -	 */
> -	reg = pin * 2;
> -
> -	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
> -					reg, app_id);
> -	if (ret < 0) {
> -		dev_err(&client->dev, "error setting GPIO app_id\n");
> -		return ret;
> -	}
> -
> -	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
> -					reg + 1, bitnum);
> -	if (ret < 0) {
> -		dev_err(&client->dev, "error setting GPIO bit number\n");
> -		return ret;
> -	}
> -
> -	switch (pin) {
> -	case mma9551_gpio6:
> -		reg = MMA9551_GPIO_POL_LSB;
> -		pol_mask = 1 << 6;
> -		break;
> -	case mma9551_gpio7:
> -		reg = MMA9551_GPIO_POL_LSB;
> -		pol_mask = 1 << 7;
> -		break;
> -	case mma9551_gpio8:
> -		reg = MMA9551_GPIO_POL_MSB;
> -		pol_mask = 1 << 0;
> -		break;
> -	case mma9551_gpio9:
> -		reg = MMA9551_GPIO_POL_MSB;
> -		pol_mask = 1 << 1;
> -		break;
> -	}
> -	pol_val = polarity ? pol_mask : 0;
> -
> -	ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
> -					 pol_mask, pol_val);
> -	if (ret < 0)
> -		dev_err(&client->dev, "error setting GPIO polarity\n");
> -
> -	return ret;
> -}
> -
> -static int mma9551_read_version(struct i2c_client *client)
> -{
> -	struct mma9551_version_info info;
> -	int ret;
> -
> -	ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
> -			       NULL, 0, (u8 *)&info, sizeof(info));
> -	if (ret < 0)
> -		return ret;
> -
> -	dev_info(&client->dev, "Device ID 0x%x, firmware version %02x.%02x\n",
> -		 be32_to_cpu(info.device_id), info.fw_version[0],
> -		 info.fw_version[1]);
> -
> -	return 0;
> -}
> -
> -/*
> - * Power on chip and enable doze mode.
> - * Use 'false' as the second parameter to cause the device to enter
> - * sleep.
> - */
> -static int mma9551_set_device_state(struct i2c_client *client, bool enable)
> -{
> -	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
> -					  MMA9551_SLEEP_CFG,
> -					  MMA9551_SLEEP_CFG_SNCEN |
> -					  MMA9551_SLEEP_CFG_FLEEN |
> -					  MMA9551_SLEEP_CFG_SCHEN,
> -					  enable ? MMA9551_SLEEP_CFG_SCHEN |
> -					  MMA9551_SLEEP_CFG_FLEEN :
> -					  MMA9551_SLEEP_CFG_SNCEN);
> -}
> -
> -static int mma9551_set_power_state(struct i2c_client *client, bool on)
> -{
> -#ifdef CONFIG_PM
> -	int ret;
> -
> -	if (on)
> -		ret = pm_runtime_get_sync(&client->dev);
> -	else {
> -		pm_runtime_mark_last_busy(&client->dev);
> -		ret = pm_runtime_put_autosuspend(&client->dev);
> -	}
> -
> -	if (ret < 0) {
> -		dev_err(&client->dev,
> -			"failed to change power state to %d\n", on);
> -		if (on)
> -			pm_runtime_put_noidle(&client->dev);
> -
> -		return ret;
> -	}
> -#endif
> -
> -	return 0;
> -}
> -
> -static void mma9551_sleep(int freq)
> -{
> -	int sleep_val = 1000 / freq;
> -
> -	if (sleep_val < 20)
> -		usleep_range(sleep_val * 1000, 20000);
> -	else
> -		msleep_interruptible(sleep_val);
> -}
> -
>  static int mma9551_read_incli_chan(struct i2c_client *client,
>  				   const struct iio_chan_spec *chan,
>  				   int *val)
> @@ -497,46 +112,6 @@ out_poweroff:
>  	return ret;
>  }
>  
> -static int mma9551_read_accel_chan(struct i2c_client *client,
> -				   const struct iio_chan_spec *chan,
> -				   int *val, int *val2)
> -{
> -	u16 reg_addr;
> -	s16 raw_accel;
> -	int ret;
> -
> -	switch (chan->channel2) {
> -	case IIO_MOD_X:
> -		reg_addr = MMA9551_AFE_X_ACCEL_REG;
> -		break;
> -	case IIO_MOD_Y:
> -		reg_addr = MMA9551_AFE_Y_ACCEL_REG;
> -		break;
> -	case IIO_MOD_Z:
> -		reg_addr = MMA9551_AFE_Z_ACCEL_REG;
> -		break;
> -	default:
> -		return -EINVAL;
> -	}
> -
> -	ret = mma9551_set_power_state(client, true);
> -	if (ret < 0)
> -		return ret;
> -
> -	ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
> -				       reg_addr, &raw_accel);
> -	if (ret < 0)
> -		goto out_poweroff;
> -
> -	*val = raw_accel;
> -
> -	ret = IIO_VAL_INT;
> -
> -out_poweroff:
> -	mma9551_set_power_state(client, false);
> -	return ret;
> -}
> -
>  static int mma9551_read_raw(struct iio_dev *indio_dev,
>  			    struct iio_chan_spec const *chan,
>  			    int *val, int *val2, long mask)
> @@ -569,9 +144,7 @@ static int mma9551_read_raw(struct iio_dev *indio_dev,
>  	case IIO_CHAN_INFO_SCALE:
>  		switch (chan->type) {
>  		case IIO_ACCEL:
> -			*val = 0;
> -			*val2 = 2440;
> -			return IIO_VAL_INT_PLUS_MICRO;
> +			return mma9551_read_accel_scale(val, val2);
>  		default:
>  			return -EINVAL;
>  		}
> @@ -740,14 +313,6 @@ static const struct iio_event_spec mma9551_incli_event = {
>  	.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
>  };
>  
> -#define MMA9551_ACCEL_CHANNEL(axis) {				\
> -	.type = IIO_ACCEL,					\
> -	.modified = 1,						\
> -	.channel2 = axis,					\
> -	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> -	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
> -}
> -
>  #define MMA9551_INCLI_CHANNEL(axis) {				\
>  	.type = IIO_INCLI,					\
>  	.modified = 1,						\
> diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
> new file mode 100644
> index 0000000..7f1a73e
> --- /dev/null
> +++ b/drivers/iio/accel/mma9551_core.c
> @@ -0,0 +1,615 @@
> +/*
> + * Common code for Freescale MMA955x Intelligent Sensor Platform drivers
> + * Copyright (c) 2014, Intel Corporation.
> + *
> + * 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.
> + *
> + * This program is distributed in the hope 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/module.h>
> +#include <linux/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/pm_runtime.h>
> +#include "mma9551_core.h"
> +
> +/* Command masks for mailbox write command */
> +#define MMA9551_CMD_READ_VERSION_INFO	0x00
> +#define MMA9551_CMD_READ_CONFIG		0x10
> +#define MMA9551_CMD_WRITE_CONFIG	0x20
> +#define MMA9551_CMD_READ_STATUS		0x30
> +
> +/* Mailbox read command */
> +#define MMA9551_RESPONSE_COCO		BIT(7)
> +
> +/* Error-Status codes returned in mailbox read command */
> +#define MMA9551_MCI_ERROR_NONE			0x00
> +#define MMA9551_MCI_ERROR_PARAM			0x04
> +#define MMA9551_MCI_INVALID_COUNT		0x19
> +#define MMA9551_MCI_ERROR_COMMAND		0x1C
> +#define MMA9551_MCI_ERROR_INVALID_LENGTH	0x21
> +#define MMA9551_MCI_ERROR_FIFO_BUSY		0x22
> +#define MMA9551_MCI_ERROR_FIFO_ALLOCATED	0x23
> +#define MMA9551_MCI_ERROR_FIFO_OVERSIZE		0x24
> +
> +/* GPIO Application */
> +#define MMA9551_GPIO_POL_MSB		0x08
> +#define MMA9551_GPIO_POL_LSB		0x09
> +
> +/* Sleep/Wake application */
> +#define MMA9551_SLEEP_CFG		0x06
> +#define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
> +#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
> +#define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
> +
> +/* AFE application */
> +#define MMA9551_AFE_X_ACCEL_REG		0x00
> +#define MMA9551_AFE_Y_ACCEL_REG		0x02
> +#define MMA9551_AFE_Z_ACCEL_REG		0x04
> +
> +/*
> + * A response is composed of:
> + * - control registers: MB0-3
> + * - data registers: MB4-31
> + *
> + * A request is composed of:
> + * - mbox to write to (always 0)
> + * - control registers: MB1-4
> + * - data registers: MB5-31
> + */
> +#define MMA9551_MAILBOX_CTRL_REGS	4
> +#define MMA9551_MAX_MAILBOX_DATA_REGS	28
> +#define MMA9551_MAILBOX_REGS		32
> +
> +#define MMA9551_I2C_READ_RETRIES	5
> +#define MMA9551_I2C_READ_DELAY	50	/* us */
> +
> +struct mma9551_mbox_request {
> +	u8 start_mbox;		/* Always 0. */
> +	u8 app_id;
> +	/*
> +	 * See Section 5.3.1 of the MMA955xL Software Reference Manual.
> +	 *
> +	 * Bit 7: reserved, always 0
> +	 * Bits 6-4: command
> +	 * Bits 3-0: upper bits of register offset
> +	 */
> +	u8 cmd_off;
> +	u8 lower_off;
> +	u8 nbytes;
> +	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
> +} __packed;
> +
> +struct mma9551_mbox_response {
> +	u8 app_id;
> +	/*
> +	 * See Section 5.3.3 of the MMA955xL Software Reference Manual.
> +	 *
> +	 * Bit 7: COCO
> +	 * Bits 6-0: Error code.
> +	 */
> +	u8 coco_err;
> +	u8 nbytes;
> +	u8 req_bytes;
> +	u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> +} __packed;
> +
> +struct mma9551_version_info {
> +	__be32 device_id;
> +	u8 rom_version[2];
> +	u8 fw_version[2];
> +	u8 hw_version[2];
> +	u8 fw_build[2];
> +};
> +
> +static int mma9551_transfer(struct i2c_client *client,
> +			    u8 app_id, u8 command, u16 offset,
> +			    u8 *inbytes, int num_inbytes,
> +			    u8 *outbytes, int num_outbytes)
> +{
> +	struct mma9551_mbox_request req;
> +	struct mma9551_mbox_response rsp;
> +	struct i2c_msg in, out;
> +	u8 req_len, err_code;
> +	int ret, retries;
> +
> +	if (offset >= 1 << 12) {
> +		dev_err(&client->dev, "register offset too large\n");
> +		return -EINVAL;
> +	}
> +
> +	req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
> +	req.start_mbox = 0;
> +	req.app_id = app_id;
> +	req.cmd_off = command | (offset >> 8);
> +	req.lower_off = offset;
> +
> +	if (command == MMA9551_CMD_WRITE_CONFIG)
> +		req.nbytes = num_inbytes;
> +	else
> +		req.nbytes = num_outbytes;
> +	if (num_inbytes)
> +		memcpy(req.buf, inbytes, num_inbytes);
> +
> +	out.addr = client->addr;
> +	out.flags = 0;
> +	out.len = req_len;
> +	out.buf = (u8 *)&req;
> +
> +	ret = i2c_transfer(client->adapter, &out, 1);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "i2c write failed\n");
> +		return ret;
> +	}
> +
> +	retries = MMA9551_I2C_READ_RETRIES;
> +	do {
> +		udelay(MMA9551_I2C_READ_DELAY);
> +
> +		in.addr = client->addr;
> +		in.flags = I2C_M_RD;
> +		in.len = sizeof(rsp);
> +		in.buf = (u8 *)&rsp;
> +
> +		ret = i2c_transfer(client->adapter, &in, 1);
> +		if (ret < 0) {
> +			dev_err(&client->dev, "i2c read failed\n");
> +			return ret;
> +		}
> +
> +		if (rsp.coco_err & MMA9551_RESPONSE_COCO)
> +			break;
> +	} while (--retries > 0);
> +
> +	if (retries == 0) {
> +		dev_err(&client->dev,
> +			"timed out while waiting for command response\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (rsp.app_id != app_id) {
> +		dev_err(&client->dev,
> +			"app_id mismatch in response got %02x expected %02x\n",
> +			rsp.app_id, app_id);
> +		return -EINVAL;
> +	}
> +
> +	err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
> +	if (err_code != MMA9551_MCI_ERROR_NONE) {
> +		dev_err(&client->dev, "read returned error %x\n", err_code);
> +		return -EINVAL;
> +	}
> +
> +	if (rsp.nbytes != rsp.req_bytes) {
> +		dev_err(&client->dev,
> +			"output length mismatch got %d expected %d\n",
> +			rsp.nbytes, rsp.req_bytes);
> +		return -EINVAL;
> +	}
> +
> +	if (num_outbytes)
> +		memcpy(outbytes, rsp.buf, num_outbytes);
> +
> +	return 0;
> +}
> +
> +/**
> + * mma9551_read_config_byte() - read 1 configuration byte
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @val:	Pointer to store value read
> + *
> + * Read one configuration byte from the device using MMA955xL command format.
> + * Commands to the MMA955xL platform consist of a write followed
> + * by one or more reads.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u8 *val)
> +{
> +	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> +				reg, NULL, 0, val, 1);
> +}
> +EXPORT_SYMBOL(mma9551_read_config_byte);
> +
> +/**
> + * mma9551_write_config_byte() - write 1 configuration byte
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @val:	Value to write
> + *
> + * Write one configuration byte from the device using MMA955xL command format.
> + * Commands to the MMA955xL platform consist of a write followed by one or
> + * more reads.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
> +			      u16 reg, u8 val)
> +{
> +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
> +				&val, 1, NULL, 0);
> +}
> +EXPORT_SYMBOL(mma9551_write_config_byte);
> +
> +/**
> + * mma9551_read_status_byte() - read 1 status byte
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @val:	Pointer to store value read
> + *
> + * Read one status byte from the device using MMA955xL command format.
> + * Commands to the MMA955xL platform consist of a write followed by one or
> + * more reads.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u8 *val)
> +{
> +	return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
> +				reg, NULL, 0, val, 1);
> +}
> +EXPORT_SYMBOL(mma9551_read_status_byte);
> +
> +/**
> + * mma9551_read_status_word() - read 1 status word
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @val:	Pointer to store value read
> + *
> + * Read one status word from the device using MMA955xL command format.
> + * Commands to the MMA955xL platform consist of a write followed by one or
> + * more reads.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u16 *val)
> +{
> +	int ret;
> +	__be16 v;
> +
> +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
> +			       reg, NULL, 0, (u8 *)&v, 2);
> +	*val = be16_to_cpu(v);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mma9551_read_status_word);
> +
> +/**
> + * mma9551_update_config_bits() - update bits in register
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @mask:	Mask for the bits to update
> + * @val:	Value of the bits to update
> + *
> + * Update bits in the given register using a bit mask.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
> +			       u16 reg, u8 mask, u8 val)
> +{
> +	int ret;
> +	u8 tmp, orig;
> +
> +	ret = mma9551_read_config_byte(client, app_id, reg, &orig);
> +	if (ret < 0)
> +		return ret;
> +
> +	tmp = orig & ~mask;
> +	tmp |= val & mask;
> +
> +	if (tmp == orig)
> +		return 0;
> +
> +	return mma9551_write_config_byte(client, app_id, reg, tmp);
> +}
> +EXPORT_SYMBOL(mma9551_update_config_bits);
> +
> +/**
> + * mma9551_gpio_config() - configure gpio
> + * @client:	I2C client
> + * @pin:	GPIO pin to configure
> + * @app_id:	Application ID
> + * @bitnum:	Bit number of status register being assigned to the GPIO pin.
> + * @polarity:	The polarity parameter is described in section 6.2.2, page 66,
> + *		of the Software Reference Manual.  Basically, polarity=0 means
> + *		the interrupt line has the same value as the selected bit,
> + *		while polarity=1 means the line is inverted.
> + *
> + * Assign a bit from an application’s status register to a specific GPIO pin.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
> +			u8 app_id, u8 bitnum, int polarity)
> +{
> +	u8 reg, pol_mask, pol_val;
> +	int ret;
> +
> +	if (pin > mma9551_gpio_max) {
> +		dev_err(&client->dev, "bad GPIO pin\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
> +	 * 0x03, and so on.
> +	 */
> +	reg = pin * 2;
> +
> +	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
> +					reg, app_id);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "error setting GPIO app_id\n");
> +		return ret;
> +	}
> +
> +	ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
> +					reg + 1, bitnum);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "error setting GPIO bit number\n");
> +		return ret;
> +	}
> +
> +	switch (pin) {
> +	case mma9551_gpio6:
> +		reg = MMA9551_GPIO_POL_LSB;
> +		pol_mask = 1 << 6;
> +		break;
> +	case mma9551_gpio7:
> +		reg = MMA9551_GPIO_POL_LSB;
> +		pol_mask = 1 << 7;
> +		break;
> +	case mma9551_gpio8:
> +		reg = MMA9551_GPIO_POL_MSB;
> +		pol_mask = 1 << 0;
> +		break;
> +	case mma9551_gpio9:
> +		reg = MMA9551_GPIO_POL_MSB;
> +		pol_mask = 1 << 1;
> +		break;
> +	}
> +	pol_val = polarity ? pol_mask : 0;
> +
> +	ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
> +					 pol_mask, pol_val);
> +	if (ret < 0)
> +		dev_err(&client->dev, "error setting GPIO polarity\n");
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mma9551_gpio_config);
> +
> +/**
> + * mma9551_read_version() - read device version information
> + * @client:	I2C client
> + *
> + * Read version information and print device id and firmware version.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_read_version(struct i2c_client *client)
> +{
> +	struct mma9551_version_info info;
> +	int ret;
> +
> +	ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
> +			       NULL, 0, (u8 *)&info, sizeof(info));
> +	if (ret < 0)
> +		return ret;
> +
> +	dev_info(&client->dev, "device ID 0x%x, firmware version %02x.%02x\n",
> +		 be32_to_cpu(info.device_id), info.fw_version[0],
> +		 info.fw_version[1]);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mma9551_read_version);
> +
> +/**
> + * mma9551_set_device_state() - sets HW power mode
> + * @client:	I2C client
> + * @enable:	Use true to power on device, false to cause the device
> + *		to enter sleep.
> + *
> + * Set power on/off for device using the Sleep/Wake Application.
> + * When enable is true, power on chip and enable doze mode.
> + * When enable is false, enter sleep mode (device remains in the
> + * lowest-power mode).
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_set_device_state(struct i2c_client *client, bool enable)
> +{
> +	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
> +					  MMA9551_SLEEP_CFG,
> +					  MMA9551_SLEEP_CFG_SNCEN |
> +					  MMA9551_SLEEP_CFG_FLEEN |
> +					  MMA9551_SLEEP_CFG_SCHEN,
> +					  enable ? MMA9551_SLEEP_CFG_SCHEN |
> +					  MMA9551_SLEEP_CFG_FLEEN :
> +					  MMA9551_SLEEP_CFG_SNCEN);
> +}
> +EXPORT_SYMBOL(mma9551_set_device_state);
> +
> +/**
> + * mma9551_set_power_state() - sets runtime PM state
> + * @client:	I2C client
> + * @on:		Use true to power on device, false to power off
> + *
> + * Resume or suspend the device using Runtime PM.
> + * The device will suspend after the autosuspend delay.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_set_power_state(struct i2c_client *client, bool on)
> +{
> +#ifdef CONFIG_PM
> +	int ret;
> +
> +	if (on)
> +		ret = pm_runtime_get_sync(&client->dev);
> +	else {
> +		pm_runtime_mark_last_busy(&client->dev);
> +		ret = pm_runtime_put_autosuspend(&client->dev);
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev,
> +			"failed to change power state to %d\n", on);
> +		if (on)
> +			pm_runtime_put_noidle(&client->dev);
> +
> +		return ret;
> +	}
> +#endif
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mma9551_set_power_state);
> +
> +/**
> + * mma9551_sleep() - sleep
> + * @freq:	Application frequency
> + *
> + * Firmware applications run at a certain frequency on the
> + * device. Sleep for one application cycle to make sure the
> + * application had time to run once and initialize set values.
> + */
> +void mma9551_sleep(int freq)
> +{
> +	int sleep_val = 1000 / freq;
> +
> +	if (sleep_val < 20)
> +		usleep_range(sleep_val * 1000, 20000);
> +	else
> +		msleep_interruptible(sleep_val);
> +}
> +EXPORT_SYMBOL(mma9551_sleep);
> +
> +/**
> + * mma9551_read_accel_chan() - read accelerometer channel
> + * @client:	I2C client
> + * @chan:	IIO channel
> + * @val:	Pointer to the accelerometer value read
> + * @val2:	Unused
> + *
> + * Read accelerometer value for the specified channel.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: IIO_VAL_INT on success, negative value on failure.
> + */
> +int mma9551_read_accel_chan(struct i2c_client *client,
> +			    const struct iio_chan_spec *chan,
> +			    int *val, int *val2)
> +{
> +	u16 reg_addr;
> +	s16 raw_accel;
> +	int ret;
> +
> +	switch (chan->channel2) {
> +	case IIO_MOD_X:
> +		reg_addr = MMA9551_AFE_X_ACCEL_REG;
> +		break;
> +	case IIO_MOD_Y:
> +		reg_addr = MMA9551_AFE_Y_ACCEL_REG;
> +		break;
> +	case IIO_MOD_Z:
> +		reg_addr = MMA9551_AFE_Z_ACCEL_REG;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = mma9551_set_power_state(client, true);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
> +				       reg_addr, &raw_accel);
> +	if (ret < 0)
> +		goto out_poweroff;
> +
> +	*val = raw_accel;
> +
> +	ret = IIO_VAL_INT;
> +
> +out_poweroff:
> +	mma9551_set_power_state(client, false);
> +	return ret;
> +}
> +EXPORT_SYMBOL(mma9551_read_accel_chan);
> +
> +/**
> + * mma9551_read_accel_scale() - read accelerometer scale
> + * @val:	Pointer to the accelerometer scale (int value)
> + * @val2:	Pointer to the accelerometer scale (micro value)
> + *
> + * Read accelerometer scale.
> + *
> + * Returns: IIO_VAL_INT_PLUS_MICRO.
> + */
> +int mma9551_read_accel_scale(int *val, int *val2)
> +{
> +	*val = 0;
> +	*val2 = 2440;
> +
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}
> +EXPORT_SYMBOL(mma9551_read_accel_scale);
> +
> +MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
> +MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MMA955xL sensors core");
> diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
> new file mode 100644
> index 0000000..e6efd02
> --- /dev/null
> +++ b/drivers/iio/accel/mma9551_core.h
> @@ -0,0 +1,66 @@
> +/*
> + * Common code for Freescale MMA955x Intelligent Sensor Platform drivers
> + * Copyright (c) 2014, Intel Corporation.
> + *
> + * 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.
> + *
> + * This program is distributed in the hope 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 _MMA9551_CORE_H_
> +#define _MMA9551_CORE_H_
> +
> +/* Applications IDs */
> +#define MMA9551_APPID_VERSION		0x00
> +#define MMA9551_APPID_GPIO		0x03
> +#define MMA9551_APPID_AFE		0x06
> +#define MMA9551_APPID_TILT		0x0B
> +#define MMA9551_APPID_SLEEP_WAKE	0x12
> +#define MMA9551_APPID_RESET		0x17
> +#define MMA9551_APPID_NONE		0xff
> +
> +#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
> +
> +enum mma9551_gpio_pin {
> +	mma9551_gpio6 = 0,
> +	mma9551_gpio7,
> +	mma9551_gpio8,
> +	mma9551_gpio9,
> +	mma9551_gpio_max = mma9551_gpio9,
> +};
> +
> +#define MMA9551_ACCEL_CHANNEL(axis) {				\
> +	.type = IIO_ACCEL,					\
> +	.modified = 1,						\
> +	.channel2 = axis,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
> +}
> +
> +int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u8 *val);
> +int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
> +			      u16 reg, u8 val);
> +int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u8 *val);
> +int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u16 *val);
> +int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
> +			       u16 reg, u8 mask, u8 val);
> +int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
> +			u8 app_id, u8 bitnum, int polarity);
> +int mma9551_read_version(struct i2c_client *client);
> +int mma9551_set_device_state(struct i2c_client *client, bool enable);
> +int mma9551_set_power_state(struct i2c_client *client, bool on);
> +void mma9551_sleep(int freq);
> +int mma9551_read_accel_chan(struct i2c_client *client,
> +			    const struct iio_chan_spec *chan,
> +			    int *val, int *val2);
> +int mma9551_read_accel_scale(int *val, int *val2);
> +
> +#endif /* _MMA9551_CORE_H_ */
> 


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

* Re: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
  2015-01-11 19:10 ` [PATCH v2 10/10] iio: add driver for Freescale MMA9553 Irina Tirdea
@ 2015-01-26 20:44   ` Jonathan Cameron
  2015-01-27 17:09       ` Tirdea, Irina
  0 siblings, 1 reply; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-26 20:44 UTC (permalink / raw)
  To: Irina Tirdea, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald

On 11/01/15 19:10, Irina Tirdea wrote:
> Add support for Freescale MMA9553L Intelligent Pedometer Platform.
> 
> The following functionalities are supported:
>  - step counter (counts the number of steps using a HW register)
>  - step detector (generates an iio event at every step the user takes)
>  - activity recognition (rest, walking, jogging, running)
>  - speed
>  - calories
>  - distance
> 
> To get accurate pedometer results, the user's height, weight and gender
> need to be configured.
> 
> The specifications can be downloaded from:
> http://www.freescale.com/files/sensors/doc/ref_manual/MMA955xLSWRM.pdf
> http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
One thing noticed in passing.  Calibheight is in cm whereas distance is
in m. This seems inconsistent.  Would you mind changing to meters for
calibheight?
(lets do it whilst we don't really have any users yet).


A very nice driver, just a few minor queries in line.  Also of course
the interface for the debounce of steps as discussed in the thread
hanging off earlier in this series.

I'm not keen on the addition to iio.h (yet) as it is only used by this
one driver and as a general rule macros that simple are best avoided
(tend to limit things rather than help).

> ---
>  Documentation/ABI/testing/sysfs-bus-iio |   49 +-
>  drivers/iio/accel/Kconfig               |   10 +
>  drivers/iio/accel/Makefile              |    1 +
>  drivers/iio/accel/mma9551_core.c        |  183 +++++
>  drivers/iio/accel/mma9551_core.h        |   17 +-
>  drivers/iio/accel/mma9553.c             | 1321 +++++++++++++++++++++++++++++++
>  include/linux/iio/iio.h                 |   11 +
>  7 files changed, 1587 insertions(+), 5 deletions(-)
>  create mode 100644 drivers/iio/accel/mma9553.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index e009f49..732d018 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -343,7 +343,30 @@ Description:
>  		production inaccuracies).  If shared across all channels,
>  		<type>_calibscale is used.
>  
> -What:		/sys/bus/iio/devices/iio:deviceX/in_steps_calibheight
> +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
> +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
> +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
> +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Gender of the user (e.g.: male, female) used by some pedometers
> +		to compute the stride length, distance, speed and activity
> +		type.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
> +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
> +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
> +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Lists all available gender values (e.g.: male, female).
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
> +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
> +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
> +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
>  KernelVersion:	3.19
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -818,6 +841,14 @@ What:		/sys/.../events/in_tempY_roc_falling_period
>  What:		/sys/.../events/in_accel_x&y&z_mag_falling_period
>  What:		/sys/.../events/in_intensity0_thresh_period
>  What:		/sys/.../events/in_proximity0_thresh_period
> +What:		/sys/.../events/in_activity_still_thresh_rising_period
> +What:		/sys/.../events/in_activity_still_thresh_falling_period
> +What:		/sys/.../events/in_activity_walking_thresh_rising_period
> +What:		/sys/.../events/in_activity_walking_thresh_falling_period
> +What:		/sys/.../events/in_activity_jogging_thresh_rising_period
> +What:		/sys/.../events/in_activity_jogging_thresh_falling_period
> +What:		/sys/.../events/in_activity_running_thresh_rising_period
> +What:		/sys/.../events/in_activity_running_thresh_falling_period
>  KernelVersion:	2.6.37
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -1142,6 +1173,12 @@ Description:
>  		This attribute is used to get/set the integration time in
>  		seconds.
>  
> +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Number of seconds in which to compute speed.
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
>  KernelVersion:	3.15
>  Contact:	linux-iio@vger.kernel.org
> @@ -1170,13 +1207,17 @@ Description:
>  		present, output should be considered as processed with the
>  		unit in milliamps.
>  
> +What:		/sys/.../iio:deviceX/in_energy_en
> +What:		/sys/.../iio:deviceX/in_distance_en
> +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
The speed one is interesting.  It's an instantaneous measure (unlike the
others that are cumulative).  As such I'd normally expect it not to need
an enable (nothing is getting reset).  However as you describe above we
have a speed calculation over a number of seconds so I can see where
this comes from.  Hmm.  Not sure on the right answer for this one.



>  What:		/sys/.../iio:deviceX/in_steps_en
>  KernelVersion:	3.19
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> -		Activates the step counter. After activation, the number of steps
> -		taken by the user will be counted in hardware and exported through
> -		in_steps_input.
> +		Activates a device feature that runs in firmware/hardware.
> +		E.g. for steps: the pedometer saves power while not used;
> +		when activated, it will count the steps taken by the user in
> +		firmware and export them through in_steps_input.
>  
>  What:		/sys/.../iio:deviceX/in_steps_input
>  KernelVersion:	3.19
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index c53047d..7c9a9a9 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -126,4 +126,14 @@ config MMA9551
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called mma9551.
>  
> +config MMA9553
> +	tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
> +	depends on I2C
> +	select MMA9551_CORE
> +	help
> +	  Say yes here to build support for the Freescale MMA9553L
> +	  Intelligent Pedometer Platform Driver.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called mma9553.
>  endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 8105316..f815695 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_MMA8452)	+= mma8452.o
>  
>  obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
>  obj-$(CONFIG_MMA9551)		+= mma9551.o
> +obj-$(CONFIG_MMA9553)		+= mma9553.o
>  
>  obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
>  st_accel-y := st_accel_core.o
> diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
> index 7f1a73e..7f55a6d 100644
> --- a/drivers/iio/accel/mma9551_core.c
> +++ b/drivers/iio/accel/mma9551_core.c
> @@ -53,6 +53,11 @@
>  #define MMA9551_AFE_Y_ACCEL_REG		0x02
>  #define MMA9551_AFE_Z_ACCEL_REG		0x04
>  
> +/* Reset/Suspend/Clear application */
> +#define MMA9551_RSC_RESET		0x00
> +#define MMA9551_RSC_OFFSET(mask)	(3 - (ffs(mask) - 1) / 8)
> +#define MMA9551_RSC_VAL(mask)		(mask >> (((ffs(mask) - 1) / 8) * 8))
> +
>  /*
>   * A response is composed of:
>   * - control registers: MB0-3
> @@ -275,6 +280,64 @@ int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
>  EXPORT_SYMBOL(mma9551_read_status_byte);
>  
>  /**
> + * mma9551_read_config_word() - read 1 config word
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @val:	Pointer to store value read
> + *
> + * Read one configuration word from the device using MMA955xL command format.
> + * Commands to the MMA955xL platform consist of a write followed by one or
> + * more reads.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
> +			    u16 reg, u16 *val)
> +{
> +	int ret;
> +	__be16 v;
> +
> +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> +			       reg, NULL, 0, (u8 *)&v, 2);
> +	*val = be16_to_cpu(v);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mma9551_read_config_word);
> +
> +/**
> + * mma9551_write_config_word() - write 1 config word
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @val:	Value to write
> + *
> + * Write one configuration word from the device using MMA955xL command format.
> + * Commands to the MMA955xL platform consist of a write followed by one or
> + * more reads.
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u16 val)
> +{
> +	__be16 v = cpu_to_be16(val);
> +
> +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
> +				(u8 *) &v, 2, NULL, 0);
> +}
> +EXPORT_SYMBOL(mma9551_write_config_word);
> +
> +/**
>   * mma9551_read_status_word() - read 1 status word
>   * @client:	I2C client
>   * @app_id:	Application ID
> @@ -306,6 +369,107 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
>  EXPORT_SYMBOL(mma9551_read_status_word);
>  
>  /**
> + * mma9551_read_config_words() - read multiple config words
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @len:	Length of array to read in bytes
> + * @val:	Array of words to read
> + *
> + * Read multiple configuration registers (word-sized registers).
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u8 len, u16 *buf)
> +{
> +	int ret, i;
> +	int len_words = len / sizeof(u16);
> +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> +
> +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> +			       reg, NULL, 0, (u8 *) be_buf, len);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < len_words; i++)
> +		buf[i] = be16_to_cpu(be_buf[i]);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mma9551_read_config_words);
> +
> +/**
> + * mma9551_read_status_words() - read multiple status words
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @len:	Length of array to read in bytes
> + * @val:	Array of words to read
> + *
> + * Read multiple status registers (word-sized registers).
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
> +			      u16 reg, u8 len, u16 *buf)
> +{
> +	int ret, i;
> +	int len_words = len / sizeof(u16);
> +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> +
> +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
> +			       reg, NULL, 0, (u8 *) be_buf, len);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < len_words; i++)
> +		buf[i] = be16_to_cpu(be_buf[i]);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mma9551_read_status_words);
> +
> +/**
> + * mma9551_write_config_words() - write multiple config words
> + * @client:	I2C client
> + * @app_id:	Application ID
> + * @reg:	Application register
> + * @len:	Length of array to write in bytes
> + * @val:	Array of words to write
> + *
> + * Write multiple configuration registers (word-sized registers).
> + *
> + * Locking note: This function must be called with the device lock held.
> + * Locking is not handled inside the function. Callers should ensure they
> + * serialize access to the HW.
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
> +			       u16 reg, u8 len, u16 *buf)
> +{
> +	int i;
> +	int len_words = len / sizeof(u16);
> +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> +
> +	for (i = 0; i < len_words; i++)
> +		be_buf[i] = cpu_to_be16(buf[i]);
> +
> +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
> +				reg, (u8 *) be_buf, len, NULL, 0);
> +}
> +EXPORT_SYMBOL(mma9551_write_config_words);
> +
> +/**
>   * mma9551_update_config_bits() - update bits in register
>   * @client:	I2C client
>   * @app_id:	Application ID
> @@ -609,6 +773,25 @@ int mma9551_read_accel_scale(int *val, int *val2)
>  }
>  EXPORT_SYMBOL(mma9551_read_accel_scale);
>  
> +/**
> + * mma9551_app_reset() - reset application
> + * @client:	I2C client
> + * @app_mask:	Application to reset
> + *
> + * Reset the given application (using the Reset/Suspend/Clear
> + * Control Application)
> + *
> + * Returns: 0 on success, negative value on failure.
> + */
> +int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
> +{
> +	return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
> +					 MMA9551_RSC_RESET +
> +					 MMA9551_RSC_OFFSET(app_mask),
> +					 MMA9551_RSC_VAL(app_mask));
> +}
> +EXPORT_SYMBOL(mma9551_app_reset);
> +
>  MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
>  MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
>  MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
> index e6efd02..edaa56b 100644
> --- a/drivers/iio/accel/mma9551_core.h
> +++ b/drivers/iio/accel/mma9551_core.h
> @@ -21,9 +21,13 @@
>  #define MMA9551_APPID_AFE		0x06
>  #define MMA9551_APPID_TILT		0x0B
>  #define MMA9551_APPID_SLEEP_WAKE	0x12
> -#define MMA9551_APPID_RESET		0x17
> +#define MMA9551_APPID_PEDOMETER	        0x15
> +#define MMA9551_APPID_RCS		0x17
>  #define MMA9551_APPID_NONE		0xff
>  
> +/* Reset/Suspend/Clear application app masks */
> +#define MMA9551_RSC_PED			BIT(21)
> +
>  #define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
>  
>  enum mma9551_gpio_pin {
> @@ -48,8 +52,18 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
>  			      u16 reg, u8 val);
>  int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
>  			     u16 reg, u8 *val);
> +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
> +			    u16 reg, u16 *val);
> +int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u16 val);
>  int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
>  			     u16 reg, u16 *val);
> +int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
> +			     u16 reg, u8 len, u16 *buf);
> +int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
> +			      u16 reg, u8 len, u16 *buf);
> +int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
> +			       u16 reg, u8 len, u16 *buf);
>  int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
>  			       u16 reg, u8 mask, u8 val);
>  int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
> @@ -62,5 +76,6 @@ int mma9551_read_accel_chan(struct i2c_client *client,
>  			    const struct iio_chan_spec *chan,
>  			    int *val, int *val2);
>  int mma9551_read_accel_scale(int *val, int *val2);
> +int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
>  
>  #endif /* _MMA9551_CORE_H_ */
> diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
> new file mode 100644
> index 0000000..ef6a6e3
> --- /dev/null
> +++ b/drivers/iio/accel/mma9553.c
> @@ -0,0 +1,1321 @@
> +/*
> + * Freescale MMA9553L Intelligent Pedometer driver
> + * Copyright (c) 2014, Intel Corporation.
> + *
> + * 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.
> + *
> + * This program is distributed in the hope 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/module.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/acpi.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/events.h>
> +#include <linux/pm_runtime.h>
> +#include "mma9551_core.h"
> +
> +#define MMA9553_DRV_NAME			"mma9553"
> +#define MMA9553_IRQ_NAME			"mma9553_event"
> +#define MMA9553_GPIO_NAME			"mma9553_int"
> +
> +/* Pedometer configuration registers (R/W) */
> +#define MMA9553_CONF_SLEEPMIN		0x00
> +#define MMA9553_CONF_SLEEPMAX		0x02
> +#define MMA9553_CONF_SLEEPTHD		0x04
> +#define MMA9553_CONF_WORD		GENMASK(15, 0)
> +
> +#define MMA9553_CONF_CONF_STEPLEN	0x06
> +#define MMA9553_CONF_CONFIG		BIT(15)
> +#define MMA9553_CONF_ACT_DBCNTM		BIT(14)
> +#define MMA9553_CONF_SLP_DBCNTM		BIT(13)
> +#define MMA9553_CONF_STEPLEN		GENMASK(7, 0)
> +
> +#define MMA9553_CONF_HEIGHT_WEIGHT	0x08
Some of this naming is less than clear.  Which ones
are the register addresses and which the internal masks?
(obvious really, but names should reflect this perhaps)

> +#define MMA9553_CONF_HEIGHT		GENMASK(15, 8)
> +#define MMA9553_CONF_WEIGHT		GENMASK(7, 0)
> +
> +#define MMA9553_CONF_FILTER		0x0A
> +#define MMA9553_CONF_FILTSTEP		GENMASK(15, 8)
> +#define MMA9553_CONF_MALE		BIT(7)
> +#define MMA9553_CONF_FILTTIME		GENMASK(6, 0)
> +
> +#define MMA9553_CONF_SPEED_STEP		0x0C
> +#define MMA9553_CONF_SPDPRD		GENMASK(15, 8)
> +#define MMA9553_CONF_STEPCOALESCE	GENMASK(7, 0)
> +
> +#define MMA9553_CONF_ACTTHD		0x0E
> +
> +/* Pedometer status registers (R-only) */
> +#define MMA9553_STATUS			0x00
> +#define MMA9553_STATUS_MRGFL		BIT(15)
> +#define MMA9553_STATUS_SUSPCHG		BIT(14)
> +#define MMA9553_STATUS_STEPCHG		BIT(13)
> +#define MMA9553_STATUS_ACTCHG		BIT(12)
> +#define MMA9553_STATUS_SUSP		BIT(11)
> +#define MMA9553_STATUS_ACTIVITY		(BIT(10) | BIT(9) | BIT(8))
> +#define MMA9553_STATUS_VERSION		0x00FF
> +
> +#define MMA9553_STEPCNT			0x02
> +#define MMA9553_DISTANCE		0x04
> +#define MMA9553_SPEED			0x06
> +#define MMA9553_CALORIES		0x08
> +#define MMA9553_SLEEPCNT		0x0A
> +
> +/* Pedometer events are always mapped to this pin. */
> +#define MMA9553_DEFAULT_GPIO_PIN	mma9551_gpio6
> +#define MMA9553_DEFAULT_GPIO_POLARITY	0
> +
> +/* Bitnum used for gpio configuration = bit number in high status byte */
> +#define STATUS_TO_BITNUM(bit)		(ffs(bit) - 9)
> +
> +#define MMA9553_DEFAULT_SAMPLE_RATE	30	/* Hz */
> +
> +/*
> + * The internal activity level must be stable for ACTTHD samples before
> + * ACTIVITY is updated.The ACTIVITY variable contains the current activity
> + * level and is updated every time a step is detected or once a second
> + * if there are no steps.
> + */
> +#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) / MMA9553_DEFAULT_SAMPLE_RATE)
> +#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) * MMA9553_DEFAULT_SAMPLE_RATE)
> +
> +/*
> + * Autonomously suspend pedometer if acceleration vector magnitude
> + * is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
> + */
> +#define MMA9553_DEFAULT_SLEEPMIN	3688	/* 0,9 g */
> +#define MMA9553_DEFAULT_SLEEPMAX	4508	/* 1,1 g */
> +#define MMA9553_DEFAULT_SLEEPTHD	(MMA9553_DEFAULT_SAMPLE_RATE * 30)
> +
> +#define MMA9553_CONFIG_RETRIES		2
> +
> +/* Status register - activity field  */
> +enum activity_level {
> +	ACTIVITY_UNKNOWN,
> +	ACTIVITY_REST,
> +	ACTIVITY_WALKING,
> +	ACTIVITY_JOGGING,
> +	ACTIVITY_RUNNING,
> +};
> +
> +static struct mma9553_event_info {
> +	enum iio_chan_type type;
> +	enum iio_modifier mod;
> +	enum iio_event_direction dir;
> +} mma9553_events_info[] = {
> +	{
> +		.type = IIO_STEPS,
> +		.mod = IIO_NO_MOD,
> +		.dir = IIO_EV_DIR_NONE,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_STILL,
> +		.dir = IIO_EV_DIR_RISING,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_STILL,
> +		.dir = IIO_EV_DIR_FALLING,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_WALKING,
> +		.dir = IIO_EV_DIR_RISING,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_WALKING,
> +		.dir = IIO_EV_DIR_FALLING,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_JOGGING,
> +		.dir = IIO_EV_DIR_RISING,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_JOGGING,
> +		.dir = IIO_EV_DIR_FALLING,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_RUNNING,
> +		.dir = IIO_EV_DIR_RISING,
> +	},
> +	{
> +		.type = IIO_ACTIVITY,
> +		.mod = IIO_MOD_RUNNING,
> +		.dir = IIO_EV_DIR_FALLING,
> +	},
> +};
> +
> +#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
> +
> +struct mma9553_event {
> +	struct mma9553_event_info *info;
> +	bool enabled;
> +};
> +
> +struct mma9553_conf_regs {
> +	u16 sleepmin;
> +	u16 sleepmax;
> +	u16 sleepthd;
> +	u16 config;
> +	u16 height_weight;
> +	u16 filter;
> +	u16 speed_step;
> +	u16 actthd;
> +} __packed;
> +
> +struct mma9553_data {
> +	struct i2c_client *client;
> +	struct mutex mutex;
> +	struct mma9553_conf_regs conf;
> +	struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
> +	int num_events;
> +	u8 gpio_bitnum;
> +	/*
> +	 * This is used for all features that depend on step count:
> +	 * step count, distance, speed, calories.
> +	 */
> +	bool stepcnt_enabled;
> +	u16 stepcnt;
> +	u8 activity;
> +	s64 timestamp;
> +};
> +
> +static u8 mma9553_get_bits(u16 val, u16 mask)
> +{
> +	return (val & mask) >> (ffs(mask) - 1);
> +}
> +
> +static u16 mma9553_set_bits(u16 current_val, u16 val, u16 mask)
> +{
> +	return (current_val & ~mask) | (val << (ffs(mask) - 1));
> +}
> +
> +static enum iio_modifier mma9553_activity_to_mod(enum activity_level activity)
> +{
> +	switch (activity) {
> +	case ACTIVITY_RUNNING:
> +		return IIO_MOD_RUNNING;
> +	case ACTIVITY_JOGGING:
> +		return IIO_MOD_JOGGING;
> +	case ACTIVITY_WALKING:
> +		return IIO_MOD_WALKING;
> +	case ACTIVITY_REST:
> +		return IIO_MOD_STILL;
> +	case ACTIVITY_UNKNOWN:
> +	default:
> +		return IIO_NO_MOD;
> +	}
> +}
> +
> +static void mma9553_init_events(struct mma9553_data *data)
> +{
> +	int i;
> +
> +	data->num_events = MMA9553_EVENTS_INFO_SIZE;
> +	for (i = 0; i < data->num_events; i++) {
> +		data->events[i].info = &mma9553_events_info[i];
> +		data->events[i].enabled = false;
> +	}
> +}
> +
> +static struct mma9553_event *mma9553_get_event(struct mma9553_data *data,
> +					       enum iio_chan_type type,
> +					       enum iio_modifier mod,
> +					       enum iio_event_direction dir)
> +{
> +	int i;
> +
> +	for (i = 0; i < data->num_events; i++)
> +		if (data->events[i].info->type == type &&
> +		    data->events[i].info->mod == mod &&
> +		    data->events[i].info->dir == dir)
> +			return &data->events[i];
> +
> +	return NULL;
> +}
> +
> +static bool mma9553_is_any_event_enabled(struct mma9553_data *data,
> +					 bool check_type,
> +					 enum iio_chan_type type)
> +{
> +	int i;
> +
> +	for (i = 0; i < data->num_events; i++)
> +		if ((check_type && data->events[i].info->type == type &&
> +		     data->events[i].enabled) ||
> +		     (!check_type && data->events[i].enabled))
> +			return true;
> +
> +	return false;
> +}
> +
> +static int mma9553_set_config(struct mma9553_data *data, u16 reg,
> +			      u16 *p_reg_val, u16 val, u16 mask)
> +{
> +	int ret, retries;
> +	u16 reg_val, config;
> +
> +	reg_val = *p_reg_val;
> +	if (val == mma9553_get_bits(reg_val, mask))
> +		return 0;
> +
> +	reg_val = mma9553_set_bits(reg_val, val, mask);
> +	ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
> +					reg, reg_val);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev,
> +			"error writing config register 0x%x\n", reg);
> +		return ret;
> +	}
> +
> +	*p_reg_val = reg_val;
> +
> +	/* Reinitializes the pedometer with current configuration values */
> +	config = mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
> +
> +	ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
> +					MMA9553_CONF_CONF_STEPLEN, config);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev,
> +			"error writing config register 0x%x\n",
> +			MMA9553_CONF_CONF_STEPLEN);
> +		return ret;
> +	}
> +
> +	retries = MMA9553_CONFIG_RETRIES;
> +	do {
> +		mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
> +		ret = mma9551_read_config_word(data->client,
> +					       MMA9551_APPID_PEDOMETER,
> +					       MMA9553_CONF_CONF_STEPLEN,
> +					       &config);
> +		if (ret < 0)
> +			return ret;
> +	} while (mma9553_get_bits(config, MMA9553_CONF_CONFIG) &&
> +		 --retries > 0);
> +
> +	return 0;
> +}
> +
> +static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
> +					 u8 *activity, u16 *stepcnt)
> +{
> +	u32 status_stepcnt;
> +	u16 status;
> +	int ret;
> +
> +	ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
> +					MMA9553_STATUS, sizeof(u32),
> +					(u16 *) &status_stepcnt);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev,
> +			"error reading status and stepcnt\n");
> +		return ret;
> +	}
> +
> +	status = status_stepcnt & MMA9553_CONF_WORD;
> +	*activity = mma9553_get_bits(status, MMA9553_STATUS_ACTIVITY);
> +	*stepcnt = status_stepcnt >> 16;
> +
> +	return 0;
> +}
> +
> +static int mma9553_conf_gpio(struct mma9553_data *data)
> +{
> +	u8 bitnum = 0, appid = MMA9551_APPID_PEDOMETER;
> +	int ret;
> +	struct mma9553_event *ev_step_detect;
> +	bool activity_enabled;
> +
> +	activity_enabled =
> +	    mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
> +	ev_step_detect =
> +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
> +
> +	/*
> +	 * If both step detector and activity are enabled, use the MRGFL bit.
> +	 * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
> +	 */
> +	if (activity_enabled && ev_step_detect->enabled)
> +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_MRGFL);
> +	else if (ev_step_detect->enabled)
> +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_STEPCHG);
> +	else if (activity_enabled)
> +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_ACTCHG);
> +	else			/* Reset */
> +		appid = MMA9551_APPID_NONE;
> +
> +	if (data->gpio_bitnum == bitnum)
> +		return 0;
> +
> +	/* Save initial values for activity and stepcnt */
> +	if (activity_enabled || ev_step_detect->enabled)
> +		mma9553_read_activity_stepcnt(data, &data->activity,
> +					      &data->stepcnt);
> +
> +	ret = mma9551_gpio_config(data->client,
> +				  MMA9553_DEFAULT_GPIO_PIN,
> +				  appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
> +	if (ret < 0)
> +		return ret;
> +	data->gpio_bitnum = bitnum;
> +
> +	return 0;
> +}
> +
> +static int mma9553_init(struct mma9553_data *data)
> +{
> +	int ret;
> +
> +	ret = mma9551_read_version(data->client);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Read all the pedometer configuration registers. This is used as
> +	 * a device identification command to differentiate the MMA9553L
> +	 * from the MMA9550L.
> +	 */
> +	ret =
> +	    mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
> +				      MMA9553_CONF_SLEEPMIN,
> +				      sizeof(data->conf), (u16 *) &data->conf);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev,
> +			"device is not MMA9553L: failed to read cfg regs\n");
> +		return ret;
> +	}
> +
> +
> +	/* Reset gpio */
> +	data->gpio_bitnum = -1;
> +	ret = mma9553_conf_gpio(data);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mma9551_app_reset(data->client, MMA9551_RSC_PED);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Init config registers */
> +	data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
> +	data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
> +	data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
> +	data->conf.config =
> +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
> +	/*
> +	 * Clear the activity debounce counter when the activity level changes,
> +	 * so that the confidence level applies for any activity level.
> +	 */
> +	data->conf.config =
> +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_ACT_DBCNTM);
> +	ret =
> +	    mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
> +				       MMA9553_CONF_SLEEPMIN,
> +				       sizeof(data->conf), (u16 *) &data->conf);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev,
> +			"failed to write configuration registers\n");
> +		return ret;
> +	}
> +
> +	return mma9551_set_device_state(data->client, true);
> +}
> +
> +static int mma9553_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	int ret;
> +	u16 tmp;
> +	u8 activity;
> +	bool powered_on;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_PROCESSED:
> +		switch (chan->type) {
> +		case IIO_STEPS:
> +			/*
> +			 * The HW only counts steps and other dependent
> +			 * parameters (speed, distance, calories, activity)
> +			 * if power is on (from enabling an event or the
> +			 * step counter */
> +			powered_on =
> +			    mma9553_is_any_event_enabled(data, false, 0) ||
> +			    data->stepcnt_enabled;
> +			if (!powered_on) {
> +				dev_err(&data->client->dev,
> +					"No channels enabled\n");
> +				return -EINVAL;
> +			}
> +			mutex_lock(&data->mutex);
> +			ret = mma9551_read_status_word(data->client,
> +						       MMA9551_APPID_PEDOMETER,
> +						       MMA9553_STEPCNT, &tmp);
> +			mutex_unlock(&data->mutex);
> +			if (ret < 0)
> +				return ret;
> +			*val = tmp;
> +			return IIO_VAL_INT;
> +		case IIO_DISTANCE:
> +			powered_on =
> +			    mma9553_is_any_event_enabled(data, false, 0) ||
> +			    data->stepcnt_enabled;
> +			if (!powered_on) {
> +				dev_err(&data->client->dev,
> +					"No channels enabled\n");
> +				return -EINVAL;
> +			}
> +			mutex_lock(&data->mutex);
> +			ret = mma9551_read_status_word(data->client,
> +						       MMA9551_APPID_PEDOMETER,
> +						       MMA9553_DISTANCE, &tmp);
> +			mutex_unlock(&data->mutex);
> +			if (ret < 0)
> +				return ret;
> +			*val = tmp;
> +			return IIO_VAL_INT;
> +		case IIO_ACTIVITY:
> +			powered_on =
> +			    mma9553_is_any_event_enabled(data, false, 0) ||
> +			    data->stepcnt_enabled;
> +			if (!powered_on) {
> +				dev_err(&data->client->dev,
> +					"No channels enabled\n");
> +				return -EINVAL;
> +			}
> +			mutex_lock(&data->mutex);
> +			ret = mma9551_read_status_word(data->client,
> +						       MMA9551_APPID_PEDOMETER,
> +						       MMA9553_STATUS, &tmp);
> +			mutex_unlock(&data->mutex);
> +			if (ret < 0)
> +				return ret;
> +
> +			activity =
> +			    mma9553_get_bits(tmp, MMA9553_STATUS_ACTIVITY);
> +
> +			/*
> +			 * The device does not support confidence value levels,
> +			 * so we will always have 100% for current activity and
> +			 * 0% for the others.
> +			 */
> +			if (chan->channel2 == mma9553_activity_to_mod(activity))
> +				*val = 100;
> +			else
> +				*val = 0;
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_RAW:
> +		switch (chan->type) {
> +		case IIO_VELOCITY:	/* m/h */
> +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> +				return -EINVAL;
> +			powered_on =
> +			    mma9553_is_any_event_enabled(data, false, 0) ||
> +			    data->stepcnt_enabled;
> +			if (!powered_on) {
> +				dev_err(&data->client->dev,
> +					"No channels enabled\n");
> +				return -EINVAL;
> +			}
> +			mutex_lock(&data->mutex);
> +			ret = mma9551_read_status_word(data->client,
> +						       MMA9551_APPID_PEDOMETER,
> +						       MMA9553_SPEED, &tmp);
> +			mutex_unlock(&data->mutex);
> +			if (ret < 0)
> +				return ret;
> +			*val = tmp;
> +			return IIO_VAL_INT;
> +		case IIO_ENERGY:	/* Cal or kcal */
> +			powered_on =
> +			    mma9553_is_any_event_enabled(data, false, 0) ||
> +			    data->stepcnt_enabled;
> +			if (!powered_on) {
> +				dev_err(&data->client->dev,
> +					"No channels enabled\n");
> +				return -EINVAL;
> +			}
> +			mutex_lock(&data->mutex);
> +			ret = mma9551_read_status_word(data->client,
> +						       MMA9551_APPID_PEDOMETER,
> +						       MMA9553_CALORIES, &tmp);
> +			mutex_unlock(&data->mutex);
> +			if (ret < 0)
> +				return ret;
> +			*val = tmp;
> +			return IIO_VAL_INT;
> +		case IIO_ACCEL:
> +			mutex_lock(&data->mutex);
> +			ret = mma9551_read_accel_chan(data->client,
> +						      chan, val, val2);
> +			mutex_unlock(&data->mutex);
> +			return ret;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_VELOCITY:	/* m/h to m/s */
> +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> +				return -EINVAL;
> +			*val = 0;
> +			*val2 = 277;	/* 0.000277 */
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		case IIO_ENERGY:	/* Cal or kcal to J */
> +			*val = 4184;
> +			return IIO_VAL_INT;
> +		case IIO_ACCEL:
> +			return mma9551_read_accel_scale(val, val2);
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_ENABLE:
> +		*val = data->stepcnt_enabled;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_CALIBHEIGHT:
> +		*val = mma9553_get_bits(data->conf.height_weight,
> +					MMA9553_CONF_HEIGHT);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_CALIBWEIGHT:
> +		*val = mma9553_get_bits(data->conf.height_weight,
> +					MMA9553_CONF_WEIGHT);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
> +		switch (chan->type) {
> +		case IIO_STEPS:
> +			*val = mma9553_get_bits(data->conf.filter,
> +						MMA9553_CONF_FILTSTEP);
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
> +		switch (chan->type) {
> +		case IIO_STEPS:
> +			*val = mma9553_get_bits(data->conf.filter,
> +						MMA9553_CONF_FILTTIME);
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_INT_TIME:
> +		switch (chan->type) {
> +		case IIO_VELOCITY:
> +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> +				return -EINVAL;
> +			*val = mma9553_get_bits(data->conf.speed_step,
> +						MMA9553_CONF_SPDPRD);
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int mma9553_write_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int val, int val2, long mask)
> +{
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_ENABLE:
> +		if (data->stepcnt_enabled == !!val)
> +			return 0;
> +		mutex_lock(&data->mutex);
> +		ret = mma9551_set_power_state(data->client, val);
> +		if (ret < 0) {
> +			mutex_unlock(&data->mutex);
> +			return ret;
> +		}
> +		data->stepcnt_enabled = val;
> +		mutex_unlock(&data->mutex);
> +		return 0;
> +	case IIO_CHAN_INFO_CALIBHEIGHT:
> +		if (val < 0 || val > 255)
> +			return -EINVAL;
We are being rather inconsistent in having calibheight in cm rather than
m (as per distance etc). I wonder if now is the time to change it whilst
we have few users...
> +		mutex_lock(&data->mutex);
> +		ret = mma9553_set_config(data,
> +					 MMA9553_CONF_HEIGHT_WEIGHT,
> +					 &data->conf.height_weight,
> +					 val, MMA9553_CONF_HEIGHT);
> +		mutex_unlock(&data->mutex);
> +		return ret;
> +	case IIO_CHAN_INFO_CALIBWEIGHT:
> +		if (val < 0 || val > 255)
> +			return -EINVAL;
> +		mutex_lock(&data->mutex);
> +		ret = mma9553_set_config(data,
> +					 MMA9553_CONF_HEIGHT_WEIGHT,
> +					 &data->conf.height_weight,
> +					 val, MMA9553_CONF_WEIGHT);
> +		mutex_unlock(&data->mutex);
> +		return ret;
> +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
> +		switch (chan->type) {
> +		case IIO_STEPS:
> +			/*
> +			 * Set to 0 to disable step filtering. If the value
> +			 * specified is greater than 6, then 6 will be used.
> +			 */
> +			if (val < 0)
> +				return -EINVAL;
> +			if (val > 6)
> +				val = 6;
> +			mutex_lock(&data->mutex);
> +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
> +						 &data->conf.filter, val,
> +						 MMA9553_CONF_FILTSTEP);
> +			mutex_unlock(&data->mutex);
> +			return ret;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
> +		switch (chan->type) {
> +		case IIO_STEPS:
> +			if (val < 0 || val > 127)
> +				return -EINVAL;
> +			mutex_lock(&data->mutex);
> +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
> +						 &data->conf.filter, val,
> +						 MMA9553_CONF_FILTTIME);
> +			mutex_unlock(&data->mutex);
> +			return ret;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_INT_TIME:
> +		switch (chan->type) {
> +		case IIO_VELOCITY:
> +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> +				return -EINVAL;
> +			/*
> +			 * If set to a value greater than 5, then 5 will be
> +			 * used. Warning: Do not set SPDPRD to 0 or 1 as
> +			 * this may cause undesirable behavior.
> +			 */
> +			if (val < 2)
> +				return -EINVAL;
> +			if (val > 5)
> +				val = 5;
> +			mutex_lock(&data->mutex);
> +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
> +						 &data->conf.speed_step, val,
> +						 MMA9553_CONF_SPDPRD);
> +			mutex_unlock(&data->mutex);
> +			return ret;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
> +	struct mma9553_event *event;
> +
> +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
> +	if (!event)
> +		return -EINVAL;
> +
> +	return event->enabled;
> +}
> +
> +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
> +	struct mma9553_event *event;
> +	int ret;
> +
> +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
> +	if (!event)
> +		return -EINVAL;
> +
> +	if (event->enabled == state)
> +		return 0;
> +
> +	mutex_lock(&data->mutex);
> +
> +	ret = mma9551_set_power_state(data->client, state);
> +	if (ret < 0)
> +		goto err_out;
> +	event->enabled = state;
> +
> +	ret = mma9553_conf_gpio(data);
> +	if (ret < 0)
> +		goto err_conf_gpio;
> +
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +
> +err_conf_gpio:
> +	if (state) {
> +		event->enabled = false;
> +		mma9551_set_power_state(data->client, false);
> +	}
> +err_out:
> +	mutex_unlock(&data->mutex);
> +	return ret;
> +}
> +
> +static int mma9553_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)
> +{
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +
> +	*val2 = 0;
> +	switch (info) {
> +	case IIO_EV_INFO_VALUE:
> +		switch (chan->type) {
> +		case IIO_STEPS:
> +			*val = mma9553_get_bits(data->conf.speed_step,
> +						MMA9553_CONF_STEPCOALESCE);
> +			return IIO_VAL_INT;
> +		case IIO_ACTIVITY:
> +			/*
> +			 * The device does not support confidence value levels.
> +			 * We set an average of 50%.
> +			 */
> +			*val = 50;
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_EV_INFO_PERIOD:
> +		switch (chan->type) {
> +		case IIO_ACTIVITY:
> +			*val = MMA9553_ACTIVITY_THD_TO_SEC(data->conf.actthd);
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (info) {
> +	case IIO_EV_INFO_VALUE:
> +		switch (chan->type) {
> +		case IIO_STEPS:
> +			if (val < 0 || val > 255)
> +				return -EINVAL;
> +			mutex_lock(&data->mutex);
> +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
> +						 &data->conf.speed_step, val,
> +						 MMA9553_CONF_STEPCOALESCE);
> +			mutex_unlock(&data->mutex);
> +			return ret;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_EV_INFO_PERIOD:
> +		switch (chan->type) {
> +		case IIO_ACTIVITY:
> +			mutex_lock(&data->mutex);
> +			ret = mma9553_set_config(data, MMA9553_CONF_ACTTHD,
> +						 &data->conf.actthd,
> +						 MMA9553_ACTIVITY_SEC_TO_THD
> +						 (val), MMA9553_CONF_WORD);
> +			mutex_unlock(&data->mutex);
> +			return ret;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan)
> +{
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	u8 gender;
> +
> +	gender = mma9553_get_bits(data->conf.filter, MMA9553_CONF_MALE);
> +	/*
> +	 * HW expects 0 for female and 1 for male,
> +	 * while iio index is 0 for male and 1 for female
> +	 */
> +	return !gender;
> +}
> +
> +static int mma9553_set_calibgender_mode(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan,
> +					unsigned int mode)
> +{
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	u8 gender = !mode;
> +	int ret;
> +
> +	if ((mode != 0) && (mode != 1))
> +		return -EINVAL;
> +	mutex_lock(&data->mutex);
> +	ret = mma9553_set_config(data, MMA9553_CONF_FILTER, &data->conf.filter,
> +				 gender, MMA9553_CONF_MALE);
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +}
> +
> +static const struct iio_event_spec mma9553_step_event = {
> +	.type = IIO_EV_TYPE_CHANGE,
> +	.dir = IIO_EV_DIR_NONE,
> +	.mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
> +};
> +
> +static const struct iio_event_spec mma9553_activity_events[] = {
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_RISING,
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
> +				 BIT(IIO_EV_INFO_VALUE) |
> +				 BIT(IIO_EV_INFO_PERIOD),
> +	 },
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_FALLING,
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
> +				 BIT(IIO_EV_INFO_VALUE) |
> +				 BIT(IIO_EV_INFO_PERIOD),
> +	},
> +};
> +
> +static const char * const calibgender_modes[] = { "male", "female" };
> +
> +static const struct iio_enum mma9553_calibgender_enum = {
> +	.items = calibgender_modes,
> +	.num_items = ARRAY_SIZE(calibgender_modes),
> +	.get = mma9553_get_calibgender_mode,
> +	.set = mma9553_set_calibgender_mode,
> +};
> +
> +static const struct iio_chan_spec_ext_info mma9553_ext_info[] = {
> +	IIO_CALIBGENDER_EXT_INFO(IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum),
> +	{},
> +};
> +
> +#define MMA9553_PEDOMETER_CHANNEL(_type, _mask) {		\
> +	.type = _type,						\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE)      |	\
> +			      BIT(IIO_CHAN_INFO_CALIBHEIGHT) |	\
> +			      _mask,				\
> +	.ext_info = mma9553_ext_info,				\
> +}
> +
> +#define MMA9553_ACTIVITY_CHANNEL(_chan2) {				\
> +	.type = IIO_ACTIVITY,						\
> +	.modified = 1,							\
> +	.channel2 = _chan2,						\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),		\
> +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),	\
> +	.event_spec = mma9553_activity_events,				\
> +	.num_event_specs = ARRAY_SIZE(mma9553_activity_events),		\
> +	.ext_info = mma9553_ext_info,					\
> +}
> +
> +static const struct iio_chan_spec mma9553_channels[] = {
> +	MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
> +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
> +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
> +
> +	{
> +		.type = IIO_STEPS,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> +				     BIT(IIO_CHAN_INFO_ENABLE) |
> +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH) |
> +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD),
> +		.event_spec = &mma9553_step_event,
> +		.num_event_specs = 1,
> +	},
> +
> +	MMA9553_PEDOMETER_CHANNEL(IIO_DISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)),
> +	{
> +		.type = IIO_VELOCITY,
> +		.modified = 1,
> +		.channel2 = IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_SCALE) |
> +				      BIT(IIO_CHAN_INFO_INT_TIME) |
> +				      BIT(IIO_CHAN_INFO_ENABLE),
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),
> +		.ext_info = mma9553_ext_info,
> +	},
> +	MMA9553_PEDOMETER_CHANNEL(IIO_ENERGY, BIT(IIO_CHAN_INFO_RAW) |
> +				  BIT(IIO_CHAN_INFO_SCALE) |
> +				  BIT(IIO_CHAN_INFO_CALIBWEIGHT)),
> +
> +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
> +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_JOGGING),
> +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
> +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_STILL),
> +};
> +
> +static const struct iio_info mma9553_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = mma9553_read_raw,
> +	.write_raw = mma9553_write_raw,
> +	.read_event_config = mma9553_read_event_config,
> +	.write_event_config = mma9553_write_event_config,
> +	.read_event_value = mma9553_read_event_value,
> +	.write_event_value = mma9553_write_event_value,
> +};
> +
> +static irqreturn_t mma9553_irq_handler(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +
> +	data->timestamp = iio_get_time_ns();
> +	/*
> +	 * Since we only configure the interrupt pin when an
> +	 * event is enabled, we are sure we have at least
> +	 * one event enabled at this point.
> +	 */
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t mma9553_event_handler(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	u16 stepcnt;
> +	u8 activity;
> +	struct mma9553_event *ev_activity, *ev_prev_activity, *ev_step_detect;
> +	int ret;
> +
> +	mutex_lock(&data->mutex);
> +	ret = mma9553_read_activity_stepcnt(data, &activity, &stepcnt);
> +	if (ret < 0) {
> +		mutex_unlock(&data->mutex);
> +		return IRQ_HANDLED;
> +	}
> +
> +	ev_prev_activity =
> +	    mma9553_get_event(data, IIO_ACTIVITY,
> +			      mma9553_activity_to_mod(data->activity),
> +			      IIO_EV_DIR_FALLING);
> +	ev_activity =
> +	    mma9553_get_event(data, IIO_ACTIVITY,
> +			      mma9553_activity_to_mod(activity),
> +			      IIO_EV_DIR_RISING);
> +	ev_step_detect =
> +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
> +
> +	if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
> +		data->stepcnt = stepcnt;
> +		iio_push_event(indio_dev,
> +			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
> +			       IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
> +			       data->timestamp);
> +	}
> +
> +	if (activity != data->activity) {
> +		data->activity = activity;
> +		/* ev_activity can be NULL if activity == ACTIVITY_UNKNOWN */
> +		if (ev_prev_activity && ev_prev_activity->enabled)
> +			iio_push_event(indio_dev,
> +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
> +				       ev_prev_activity->info->mod,
> +				       IIO_EV_DIR_FALLING,
> +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
> +				       data->timestamp);
> +
> +		if (ev_activity && ev_activity->enabled)
> +			iio_push_event(indio_dev,
> +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
> +				       ev_activity->info->mod,
> +				       IIO_EV_DIR_RISING,
> +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
> +				       data->timestamp);
> +	}
> +	mutex_unlock(&data->mutex);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mma9553_gpio_probe(struct i2c_client *client)
> +{
> +	struct device *dev;
> +	struct gpio_desc *gpio;
> +	int ret;
> +
> +	if (!client)
> +		return -EINVAL;
> +
> +	dev = &client->dev;
> +
> +	/* data ready gpio interrupt pin */
> +	gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
> +	if (IS_ERR(gpio)) {
> +		dev_err(dev, "acpi gpio get index failed\n");
> +		return PTR_ERR(gpio);
> +	}
> +
> +	ret = gpiod_direction_input(gpio);
> +	if (ret)
> +		return ret;
> +
> +	ret = gpiod_to_irq(gpio);
> +
> +	dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
> +
> +	return ret;
> +}
> +
> +static const char *mma9553_match_acpi_device(struct device *dev)
> +{
> +	const struct acpi_device_id *id;
> +
> +	id = acpi_match_device(dev->driver->acpi_match_table, dev);
> +	if (!id)
> +		return NULL;
> +
> +	return dev_name(dev);
> +}
> +
> +static int mma9553_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *id)
> +{
> +	struct mma9553_data *data;
> +	struct iio_dev *indio_dev;
> +	const char *name = NULL;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	data = iio_priv(indio_dev);
> +	i2c_set_clientdata(client, indio_dev);
> +	data->client = client;
> +
> +	if (id)
> +		name = id->name;
> +	else if (ACPI_HANDLE(&client->dev))
> +		name = mma9553_match_acpi_device(&client->dev);
> +	else
> +		return -ENOSYS;
> +
> +	mutex_init(&data->mutex);
> +	mma9553_init_events(data);
> +
> +	ret = mma9553_init(data);
> +	if (ret < 0)
> +		return ret;
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->channels = mma9553_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(mma9553_channels);
> +	indio_dev->name = name;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->info = &mma9553_info;
> +
> +	if (client->irq < 0)
> +		client->irq = mma9553_gpio_probe(client);
> +
> +	if (client->irq >= 0) {
> +		ret = devm_request_threaded_irq(&client->dev, client->irq,
> +						mma9553_irq_handler,
> +						mma9553_event_handler,
> +						IRQF_TRIGGER_RISING,
> +						MMA9553_IRQ_NAME, indio_dev);
> +		if (ret < 0) {
> +			dev_err(&client->dev, "request irq %d failed\n",
> +				client->irq);
> +			goto out_poweroff;
> +		}
> +
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "unable to register iio device\n");
> +		goto out_poweroff;
> +	}
> +
> +	ret = pm_runtime_set_active(&client->dev);
> +	if (ret < 0)
> +		goto out_iio_unregister;
> +
> +	pm_runtime_enable(&client->dev);
> +	pm_runtime_set_autosuspend_delay(&client->dev,
> +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
> +	pm_runtime_use_autosuspend(&client->dev);
> +
> +	dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
> +
> +	return 0;
> +
> +out_iio_unregister:
> +	iio_device_unregister(indio_dev);
> +out_poweroff:
> +	mma9551_set_device_state(client, false);
> +	return ret;
> +}
> +
> +static int mma9553_remove(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +
> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_put_noidle(&client->dev);
> +
> +	iio_device_unregister(indio_dev);
> +	mutex_lock(&data->mutex);
> +	mma9551_set_device_state(data->client, false);
> +	mutex_unlock(&data->mutex);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int mma9553_runtime_suspend(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&data->mutex);
> +	ret = mma9551_set_device_state(data->client, false);
> +	mutex_unlock(&data->mutex);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev, "powering off device failed\n");
> +		return -EAGAIN;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mma9553_runtime_resume(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = mma9551_set_device_state(data->client, true);
> +	if (ret < 0)
> +		return ret;
> +
> +	mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
> +
> +	return 0;
> +}
> +#endif
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mma9553_suspend(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&data->mutex);
> +	ret = mma9551_set_device_state(data->client, false);
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +}
> +
> +static int mma9553_resume(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct mma9553_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&data->mutex);
> +	ret = mma9551_set_device_state(data->client, true);
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +}
> +#endif
> +
> +static const struct dev_pm_ops mma9553_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
> +	SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
> +			   mma9553_runtime_resume, NULL)
> +};
> +
> +static const struct acpi_device_id mma9553_acpi_match[] = {
> +	{"MMA9553", 0},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
> +
> +static const struct i2c_device_id mma9553_id[] = {
> +	{"mma9553", 0},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, mma9553_id);
> +
> +static struct i2c_driver mma9553_driver = {
> +	.driver = {
> +		   .name = MMA9553_DRV_NAME,
> +		   .acpi_match_table = ACPI_PTR(mma9553_acpi_match),
> +		   .pm = &mma9553_pm_ops,
> +		   },
> +	.probe = mma9553_probe,
> +	.remove = mma9553_remove,
> +	.id_table = mma9553_id,
> +};
> +
> +module_i2c_driver(mma9553_driver);
> +
> +MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index b6b12ac..6770a2f 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -634,4 +634,15 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
>   */
>  #define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
>  
> +/**
> + * IIO_CALIBGENDER_EXT_INFO() - define the gender of the user
> + *
> + * @_shared:	Whether the attribute is shared between all channels
> + * @_e:		Pointer to an iio_enum struct
> + *
> + */
> +#define IIO_CALIBGENDER_EXT_INFO(_shared, _e)			       \
> +	IIO_ENUM("calibgender", _shared, _e),			       \
> +	IIO_ENUM_AVAILABLE("calibgender", _e)
> +
For now this is a little obscure to drop into the main header.
If we get lots of instances, then perhaps it'll make sense to
move it here.  Right now, please keep it in the driver.
>  #endif /* _INDUSTRIAL_IO_H_ */
> 


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

* RE: [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
  2015-01-26 19:01       ` Jonathan Cameron
@ 2015-01-27 16:20           ` Tirdea, Irina
  0 siblings, 0 replies; 33+ messages in thread
From: Tirdea, Irina @ 2015-01-27 16:20 UTC (permalink / raw)
  To: Jonathan Cameron, Baluta, Daniel
  Cc: linux-iio, Linux Kernel Mailing List, Dogaru, Vlad,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 6239 bytes --]



> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@kernel.org]
> Sent: 26 January, 2015 21:02
> To: Baluta, Daniel
> Cc: Tirdea, Irina; linux-iio@vger.kernel.org; Linux Kernel Mailing List; Dogaru, Vlad; Hartmut Knaack; Lars-Peter Clausen; Peter
> Meerwald
> Subject: Re: [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
> 
> On 26/01/15 14:40, Daniel Baluta wrote:
> > On Mon, Jan 26, 2015 at 1:07 AM, Jonathan Cameron <jic23@kernel.org> wrote:
> >> On 11/01/15 19:10, Irina Tirdea wrote:
> >>> The pedometer needs to filter out false steps that might be generated by
> >>> tapping the foot, sitting, etc. To do that it computes the number of
> >>> steps that occur in a given time and decides the user is moving only
> >>> if this value is over a threshold. E.g.: the user starts moving only
> >>> if he takes 4 steps in 3 seconds. This filter is applied only when
> >>> the user starts moving.
> >>>
> >>> A device that has such pedometer functionality is Freescale's MMA9553L:
> >>> http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf.
> >>>
> >>> To export this feature, this patch introduces
> >>> IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD.
> >>> For the pedometer, in_steps_filter_outlier_thresh will specify the number of
> >>> steps that need to occur in in_steps_filter_outlier_period seconds so that
> >>> the pedometer decides the user is moving.
> >>>
> >>> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> >> I wonder if the naming here is infact too generic.  They aren't what
> >> people would normally think of as outliers.  That would be removing
> >> missidentified steps mid way through grabbing data (to my mind).
> >>
> >> These are almost a 'debounce' of the transition from stationary to
> >> walking... Maybe there is another better term.  All suggestions welcome!
> >
> > What about:
> >
> > in_steps_debounce_count instead of in_steps_filter_outlier_thresh
> > and
> > in_steps_debounce_delay instead of in_steps_filter_outlier_period.
> >
> > Also in_steps_debounce_time could work for in_steps_filter_outlier_period.
> Hmm. Slight preference for debounce_time.
> 
> I can't come up with a better option, but we'd best see if anyone else has
> a better idea for a day or two!

I also agree with in_steps_debounce_count and in_steps_debounce_time, but I'll wait a couple of days before sending v3 maybe someone comes up with something better.
Thanks Daniel and Jonathan!

> >
> >>
> >>> ---
> >>>  Documentation/ABI/testing/sysfs-bus-iio | 21 +++++++++++++++++++++
> >>>  drivers/iio/industrialio-core.c         |  2 ++
> >>>  include/linux/iio/iio.h                 |  2 ++
> >>>  3 files changed, 25 insertions(+)
> >>>
> >>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> >>> index c03a140..e009f49 100644
> >>> --- a/Documentation/ABI/testing/sysfs-bus-iio
> >>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> >>> @@ -1193,3 +1193,24 @@ Description:
> >>>               This attribute is used to read the current speed value of the
> >>>               user (which is the norm or magnitude of the velocity vector).
> >>>               Units after application of scale are m/s.
> >>> +
> >>> +What:                /sys/.../iio:deviceX/in_steps_filter_outliers_period
> >>> +KernelVersion:       3.20
> >>> +Contact:     linux-iio@vger.kernel.org
> >>> +Description:
> >>> +             Specifies the number of seconds in which we compute the
> >>> +             values so we can decide if they are outlier values and
> >>> +             need to be filter out. These computed values are then
> >>> +             compared with in_steps_filter_outliers_thresh. E.g. for steps:
> >>> +             specifies number of seconds in which we compute the steps
> >>> +             that occur in order to decide if the consumer is making steps.
> >>> +
> >>> +What:                /sys/.../iio:deviceX/in_steps_filter_outliers_thresh
> >>> +KernelVersion:       3.20
> >>> +Contact:     linux-iio@vger.kernel.org
> >>> +Description:
> >>> +             Specifies a threshold for filtering outlier values: if value
> >>> +             measured in in_steps_filter_outliers_period seconds is below
> >>> +             threshold, we filter it out. E.g. for steps: specifies number
> >>> +             of steps that must occur within in_steps_filter_outliers_period
> >>> +             for the pedometer to decide the consumer is making steps.
> >>> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> >>> index 4ee6fdf..81678b3 100644
> >>> --- a/drivers/iio/industrialio-core.c
> >>> +++ b/drivers/iio/industrialio-core.c
> >>> @@ -126,6 +126,8 @@ static const char * const iio_chan_info_postfix[] = {
> >>>       [IIO_CHAN_INFO_ENABLE] = "en",
> >>>       [IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
> >>>       [IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
> >>> +     [IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH] = "filter_outliers_thresh",
> >>> +     [IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD] = "filter_outliers_period",
> >>>  };
> >>>
> >>>  /**
> >>> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> >>> index 752a929..b6b12ac 100644
> >>> --- a/include/linux/iio/iio.h
> >>> +++ b/include/linux/iio/iio.h
> >>> @@ -41,6 +41,8 @@ enum iio_chan_info_enum {
> >>>       IIO_CHAN_INFO_ENABLE,
> >>>       IIO_CHAN_INFO_CALIBHEIGHT,
> >>>       IIO_CHAN_INFO_CALIBWEIGHT,
> >>> +     IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH,
> >>> +     IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD,
> >>>  };
> >>>
> >>>  enum iio_shared_by {
> >>>
> >>
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> >> the body of a message to majordomo@vger.kernel.org
> >> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

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

* RE: [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD
@ 2015-01-27 16:20           ` Tirdea, Irina
  0 siblings, 0 replies; 33+ messages in thread
From: Tirdea, Irina @ 2015-01-27 16:20 UTC (permalink / raw)
  To: Jonathan Cameron, Baluta, Daniel
  Cc: linux-iio, Linux Kernel Mailing List, Dogaru, Vlad,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald

DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogSm9uYXRoYW4gQ2FtZXJv
biBbbWFpbHRvOmppYzIzQGtlcm5lbC5vcmddDQo+IFNlbnQ6IDI2IEphbnVhcnksIDIwMTUgMjE6
MDINCj4gVG86IEJhbHV0YSwgRGFuaWVsDQo+IENjOiBUaXJkZWEsIElyaW5hOyBsaW51eC1paW9A
dmdlci5rZXJuZWwub3JnOyBMaW51eCBLZXJuZWwgTWFpbGluZyBMaXN0OyBEb2dhcnUsIFZsYWQ7
IEhhcnRtdXQgS25hYWNrOyBMYXJzLVBldGVyIENsYXVzZW47IFBldGVyDQo+IE1lZXJ3YWxkDQo+
IFN1YmplY3Q6IFJlOiBbUEFUQ0ggdjIgMDcvMTBdIGlpbzogY29yZTogSW50cm9kdWNlIElJT19D
SEFOX0lORk9fRklMVEVSX09VVExJRVJTX1RIUkVTSCBhbmQgX1BFUklPRA0KPiANCj4gT24gMjYv
MDEvMTUgMTQ6NDAsIERhbmllbCBCYWx1dGEgd3JvdGU6DQo+ID4gT24gTW9uLCBKYW4gMjYsIDIw
MTUgYXQgMTowNyBBTSwgSm9uYXRoYW4gQ2FtZXJvbiA8amljMjNAa2VybmVsLm9yZz4gd3JvdGU6
DQo+ID4+IE9uIDExLzAxLzE1IDE5OjEwLCBJcmluYSBUaXJkZWEgd3JvdGU6DQo+ID4+PiBUaGUg
cGVkb21ldGVyIG5lZWRzIHRvIGZpbHRlciBvdXQgZmFsc2Ugc3RlcHMgdGhhdCBtaWdodCBiZSBn
ZW5lcmF0ZWQgYnkNCj4gPj4+IHRhcHBpbmcgdGhlIGZvb3QsIHNpdHRpbmcsIGV0Yy4gVG8gZG8g
dGhhdCBpdCBjb21wdXRlcyB0aGUgbnVtYmVyIG9mDQo+ID4+PiBzdGVwcyB0aGF0IG9jY3VyIGlu
IGEgZ2l2ZW4gdGltZSBhbmQgZGVjaWRlcyB0aGUgdXNlciBpcyBtb3Zpbmcgb25seQ0KPiA+Pj4g
aWYgdGhpcyB2YWx1ZSBpcyBvdmVyIGEgdGhyZXNob2xkLiBFLmcuOiB0aGUgdXNlciBzdGFydHMg
bW92aW5nIG9ubHkNCj4gPj4+IGlmIGhlIHRha2VzIDQgc3RlcHMgaW4gMyBzZWNvbmRzLiBUaGlz
IGZpbHRlciBpcyBhcHBsaWVkIG9ubHkgd2hlbg0KPiA+Pj4gdGhlIHVzZXIgc3RhcnRzIG1vdmlu
Zy4NCj4gPj4+DQo+ID4+PiBBIGRldmljZSB0aGF0IGhhcyBzdWNoIHBlZG9tZXRlciBmdW5jdGlv
bmFsaXR5IGlzIEZyZWVzY2FsZSdzIE1NQTk1NTNMOg0KPiA+Pj4gaHR0cDovL3d3dy5mcmVlc2Nh
bGUuY29tL2ZpbGVzL3NlbnNvcnMvZG9jL3JlZl9tYW51YWwvTU1BOTU1M0xTV1JNLnBkZi4NCj4g
Pj4+DQo+ID4+PiBUbyBleHBvcnQgdGhpcyBmZWF0dXJlLCB0aGlzIHBhdGNoIGludHJvZHVjZXMN
Cj4gPj4+IElJT19DSEFOX0lORk9fRklMVEVSX09VVExJRVJTX1RIUkVTSCBhbmQgSUlPX0NIQU5f
SU5GT19GSUxURVJfT1VUTElFUlNfUEVSSU9ELg0KPiA+Pj4gRm9yIHRoZSBwZWRvbWV0ZXIsIGlu
X3N0ZXBzX2ZpbHRlcl9vdXRsaWVyX3RocmVzaCB3aWxsIHNwZWNpZnkgdGhlIG51bWJlciBvZg0K
PiA+Pj4gc3RlcHMgdGhhdCBuZWVkIHRvIG9jY3VyIGluIGluX3N0ZXBzX2ZpbHRlcl9vdXRsaWVy
X3BlcmlvZCBzZWNvbmRzIHNvIHRoYXQNCj4gPj4+IHRoZSBwZWRvbWV0ZXIgZGVjaWRlcyB0aGUg
dXNlciBpcyBtb3ZpbmcuDQo+ID4+Pg0KPiA+Pj4gU2lnbmVkLW9mZi1ieTogSXJpbmEgVGlyZGVh
IDxpcmluYS50aXJkZWFAaW50ZWwuY29tPg0KPiA+PiBJIHdvbmRlciBpZiB0aGUgbmFtaW5nIGhl
cmUgaXMgaW5mYWN0IHRvbyBnZW5lcmljLiAgVGhleSBhcmVuJ3Qgd2hhdA0KPiA+PiBwZW9wbGUg
d291bGQgbm9ybWFsbHkgdGhpbmsgb2YgYXMgb3V0bGllcnMuICBUaGF0IHdvdWxkIGJlIHJlbW92
aW5nDQo+ID4+IG1pc3NpZGVudGlmaWVkIHN0ZXBzIG1pZCB3YXkgdGhyb3VnaCBncmFiYmluZyBk
YXRhICh0byBteSBtaW5kKS4NCj4gPj4NCj4gPj4gVGhlc2UgYXJlIGFsbW9zdCBhICdkZWJvdW5j
ZScgb2YgdGhlIHRyYW5zaXRpb24gZnJvbSBzdGF0aW9uYXJ5IHRvDQo+ID4+IHdhbGtpbmcuLi4g
TWF5YmUgdGhlcmUgaXMgYW5vdGhlciBiZXR0ZXIgdGVybS4gIEFsbCBzdWdnZXN0aW9ucyB3ZWxj
b21lIQ0KPiA+DQo+ID4gV2hhdCBhYm91dDoNCj4gPg0KPiA+IGluX3N0ZXBzX2RlYm91bmNlX2Nv
dW50IGluc3RlYWQgb2YgaW5fc3RlcHNfZmlsdGVyX291dGxpZXJfdGhyZXNoDQo+ID4gYW5kDQo+
ID4gaW5fc3RlcHNfZGVib3VuY2VfZGVsYXkgaW5zdGVhZCBvZiBpbl9zdGVwc19maWx0ZXJfb3V0
bGllcl9wZXJpb2QuDQo+ID4NCj4gPiBBbHNvIGluX3N0ZXBzX2RlYm91bmNlX3RpbWUgY291bGQg
d29yayBmb3IgaW5fc3RlcHNfZmlsdGVyX291dGxpZXJfcGVyaW9kLg0KPiBIbW0uIFNsaWdodCBw
cmVmZXJlbmNlIGZvciBkZWJvdW5jZV90aW1lLg0KPiANCj4gSSBjYW4ndCBjb21lIHVwIHdpdGgg
YSBiZXR0ZXIgb3B0aW9uLCBidXQgd2UnZCBiZXN0IHNlZSBpZiBhbnlvbmUgZWxzZSBoYXMNCj4g
YSBiZXR0ZXIgaWRlYSBmb3IgYSBkYXkgb3IgdHdvIQ0KDQpJIGFsc28gYWdyZWUgd2l0aCBpbl9z
dGVwc19kZWJvdW5jZV9jb3VudCBhbmQgaW5fc3RlcHNfZGVib3VuY2VfdGltZSwgYnV0IEknbGwg
d2FpdCBhIGNvdXBsZSBvZiBkYXlzIGJlZm9yZSBzZW5kaW5nIHYzIG1heWJlIHNvbWVvbmUgY29t
ZXMgdXAgd2l0aCBzb21ldGhpbmcgYmV0dGVyLg0KVGhhbmtzIERhbmllbCBhbmQgSm9uYXRoYW4h
DQoNCj4gPg0KPiA+Pg0KPiA+Pj4gLS0tDQo+ID4+PiAgRG9jdW1lbnRhdGlvbi9BQkkvdGVzdGlu
Zy9zeXNmcy1idXMtaWlvIHwgMjEgKysrKysrKysrKysrKysrKysrKysrDQo+ID4+PiAgZHJpdmVy
cy9paW8vaW5kdXN0cmlhbGlvLWNvcmUuYyAgICAgICAgIHwgIDIgKysNCj4gPj4+ICBpbmNsdWRl
L2xpbnV4L2lpby9paW8uaCAgICAgICAgICAgICAgICAgfCAgMiArKw0KPiA+Pj4gIDMgZmlsZXMg
Y2hhbmdlZCwgMjUgaW5zZXJ0aW9ucygrKQ0KPiA+Pj4NCj4gPj4+IGRpZmYgLS1naXQgYS9Eb2N1
bWVudGF0aW9uL0FCSS90ZXN0aW5nL3N5c2ZzLWJ1cy1paW8gYi9Eb2N1bWVudGF0aW9uL0FCSS90
ZXN0aW5nL3N5c2ZzLWJ1cy1paW8NCj4gPj4+IGluZGV4IGMwM2ExNDAuLmUwMDlmNDkgMTAwNjQ0
DQo+ID4+PiAtLS0gYS9Eb2N1bWVudGF0aW9uL0FCSS90ZXN0aW5nL3N5c2ZzLWJ1cy1paW8NCj4g
Pj4+ICsrKyBiL0RvY3VtZW50YXRpb24vQUJJL3Rlc3Rpbmcvc3lzZnMtYnVzLWlpbw0KPiA+Pj4g
QEAgLTExOTMsMyArMTE5MywyNCBAQCBEZXNjcmlwdGlvbjoNCj4gPj4+ICAgICAgICAgICAgICAg
VGhpcyBhdHRyaWJ1dGUgaXMgdXNlZCB0byByZWFkIHRoZSBjdXJyZW50IHNwZWVkIHZhbHVlIG9m
IHRoZQ0KPiA+Pj4gICAgICAgICAgICAgICB1c2VyICh3aGljaCBpcyB0aGUgbm9ybSBvciBtYWdu
aXR1ZGUgb2YgdGhlIHZlbG9jaXR5IHZlY3RvcikuDQo+ID4+PiAgICAgICAgICAgICAgIFVuaXRz
IGFmdGVyIGFwcGxpY2F0aW9uIG9mIHNjYWxlIGFyZSBtL3MuDQo+ID4+PiArDQo+ID4+PiArV2hh
dDogICAgICAgICAgICAgICAgL3N5cy8uLi4vaWlvOmRldmljZVgvaW5fc3RlcHNfZmlsdGVyX291
dGxpZXJzX3BlcmlvZA0KPiA+Pj4gK0tlcm5lbFZlcnNpb246ICAgICAgIDMuMjANCj4gPj4+ICtD
b250YWN0OiAgICAgbGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0KPiA+Pj4gK0Rlc2NyaXB0aW9u
Og0KPiA+Pj4gKyAgICAgICAgICAgICBTcGVjaWZpZXMgdGhlIG51bWJlciBvZiBzZWNvbmRzIGlu
IHdoaWNoIHdlIGNvbXB1dGUgdGhlDQo+ID4+PiArICAgICAgICAgICAgIHZhbHVlcyBzbyB3ZSBj
YW4gZGVjaWRlIGlmIHRoZXkgYXJlIG91dGxpZXIgdmFsdWVzIGFuZA0KPiA+Pj4gKyAgICAgICAg
ICAgICBuZWVkIHRvIGJlIGZpbHRlciBvdXQuIFRoZXNlIGNvbXB1dGVkIHZhbHVlcyBhcmUgdGhl
bg0KPiA+Pj4gKyAgICAgICAgICAgICBjb21wYXJlZCB3aXRoIGluX3N0ZXBzX2ZpbHRlcl9vdXRs
aWVyc190aHJlc2guIEUuZy4gZm9yIHN0ZXBzOg0KPiA+Pj4gKyAgICAgICAgICAgICBzcGVjaWZp
ZXMgbnVtYmVyIG9mIHNlY29uZHMgaW4gd2hpY2ggd2UgY29tcHV0ZSB0aGUgc3RlcHMNCj4gPj4+
ICsgICAgICAgICAgICAgdGhhdCBvY2N1ciBpbiBvcmRlciB0byBkZWNpZGUgaWYgdGhlIGNvbnN1
bWVyIGlzIG1ha2luZyBzdGVwcy4NCj4gPj4+ICsNCj4gPj4+ICtXaGF0OiAgICAgICAgICAgICAg
ICAvc3lzLy4uLi9paW86ZGV2aWNlWC9pbl9zdGVwc19maWx0ZXJfb3V0bGllcnNfdGhyZXNoDQo+
ID4+PiArS2VybmVsVmVyc2lvbjogICAgICAgMy4yMA0KPiA+Pj4gK0NvbnRhY3Q6ICAgICBsaW51
eC1paW9Admdlci5rZXJuZWwub3JnDQo+ID4+PiArRGVzY3JpcHRpb246DQo+ID4+PiArICAgICAg
ICAgICAgIFNwZWNpZmllcyBhIHRocmVzaG9sZCBmb3IgZmlsdGVyaW5nIG91dGxpZXIgdmFsdWVz
OiBpZiB2YWx1ZQ0KPiA+Pj4gKyAgICAgICAgICAgICBtZWFzdXJlZCBpbiBpbl9zdGVwc19maWx0
ZXJfb3V0bGllcnNfcGVyaW9kIHNlY29uZHMgaXMgYmVsb3cNCj4gPj4+ICsgICAgICAgICAgICAg
dGhyZXNob2xkLCB3ZSBmaWx0ZXIgaXQgb3V0LiBFLmcuIGZvciBzdGVwczogc3BlY2lmaWVzIG51
bWJlcg0KPiA+Pj4gKyAgICAgICAgICAgICBvZiBzdGVwcyB0aGF0IG11c3Qgb2NjdXIgd2l0aGlu
IGluX3N0ZXBzX2ZpbHRlcl9vdXRsaWVyc19wZXJpb2QNCj4gPj4+ICsgICAgICAgICAgICAgZm9y
IHRoZSBwZWRvbWV0ZXIgdG8gZGVjaWRlIHRoZSBjb25zdW1lciBpcyBtYWtpbmcgc3RlcHMuDQo+
ID4+PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9paW8vaW5kdXN0cmlhbGlvLWNvcmUuYyBiL2RyaXZl
cnMvaWlvL2luZHVzdHJpYWxpby1jb3JlLmMNCj4gPj4+IGluZGV4IDRlZTZmZGYuLjgxNjc4YjMg
MTAwNjQ0DQo+ID4+PiAtLS0gYS9kcml2ZXJzL2lpby9pbmR1c3RyaWFsaW8tY29yZS5jDQo+ID4+
PiArKysgYi9kcml2ZXJzL2lpby9pbmR1c3RyaWFsaW8tY29yZS5jDQo+ID4+PiBAQCAtMTI2LDYg
KzEyNiw4IEBAIHN0YXRpYyBjb25zdCBjaGFyICogY29uc3QgaWlvX2NoYW5faW5mb19wb3N0Zml4
W10gPSB7DQo+ID4+PiAgICAgICBbSUlPX0NIQU5fSU5GT19FTkFCTEVdID0gImVuIiwNCj4gPj4+
ICAgICAgIFtJSU9fQ0hBTl9JTkZPX0NBTElCSEVJR0hUXSA9ICJjYWxpYmhlaWdodCIsDQo+ID4+
PiAgICAgICBbSUlPX0NIQU5fSU5GT19DQUxJQldFSUdIVF0gPSAiY2FsaWJ3ZWlnaHQiLA0KPiA+
Pj4gKyAgICAgW0lJT19DSEFOX0lORk9fRklMVEVSX09VVExJRVJTX1RIUkVTSF0gPSAiZmlsdGVy
X291dGxpZXJzX3RocmVzaCIsDQo+ID4+PiArICAgICBbSUlPX0NIQU5fSU5GT19GSUxURVJfT1VU
TElFUlNfUEVSSU9EXSA9ICJmaWx0ZXJfb3V0bGllcnNfcGVyaW9kIiwNCj4gPj4+ICB9Ow0KPiA+
Pj4NCj4gPj4+ICAvKioNCj4gPj4+IGRpZmYgLS1naXQgYS9pbmNsdWRlL2xpbnV4L2lpby9paW8u
aCBiL2luY2x1ZGUvbGludXgvaWlvL2lpby5oDQo+ID4+PiBpbmRleCA3NTJhOTI5Li5iNmIxMmFj
IDEwMDY0NA0KPiA+Pj4gLS0tIGEvaW5jbHVkZS9saW51eC9paW8vaWlvLmgNCj4gPj4+ICsrKyBi
L2luY2x1ZGUvbGludXgvaWlvL2lpby5oDQo+ID4+PiBAQCAtNDEsNiArNDEsOCBAQCBlbnVtIGlp
b19jaGFuX2luZm9fZW51bSB7DQo+ID4+PiAgICAgICBJSU9fQ0hBTl9JTkZPX0VOQUJMRSwNCj4g
Pj4+ICAgICAgIElJT19DSEFOX0lORk9fQ0FMSUJIRUlHSFQsDQo+ID4+PiAgICAgICBJSU9fQ0hB
Tl9JTkZPX0NBTElCV0VJR0hULA0KPiA+Pj4gKyAgICAgSUlPX0NIQU5fSU5GT19GSUxURVJfT1VU
TElFUlNfVEhSRVNILA0KPiA+Pj4gKyAgICAgSUlPX0NIQU5fSU5GT19GSUxURVJfT1VUTElFUlNf
UEVSSU9ELA0KPiA+Pj4gIH07DQo+ID4+Pg0KPiA+Pj4gIGVudW0gaWlvX3NoYXJlZF9ieSB7DQo+
ID4+Pg0KPiA+Pg0KPiA+PiAtLQ0KPiA+PiBUbyB1bnN1YnNjcmliZSBmcm9tIHRoaXMgbGlzdDog
c2VuZCB0aGUgbGluZSAidW5zdWJzY3JpYmUgbGludXgtaWlvIiBpbg0KPiA+PiB0aGUgYm9keSBv
ZiBhIG1lc3NhZ2UgdG8gbWFqb3Jkb21vQHZnZXIua2VybmVsLm9yZw0KPiA+PiBNb3JlIG1ham9y
ZG9tbyBpbmZvIGF0ICBodHRwOi8vdmdlci5rZXJuZWwub3JnL21ham9yZG9tby1pbmZvLmh0bWwN
Cj4gPiAtLQ0KPiA+IFRvIHVuc3Vic2NyaWJlIGZyb20gdGhpcyBsaXN0OiBzZW5kIHRoZSBsaW5l
ICJ1bnN1YnNjcmliZSBsaW51eC1paW8iIGluDQo+ID4gdGhlIGJvZHkgb2YgYSBtZXNzYWdlIHRv
IG1ham9yZG9tb0B2Z2VyLmtlcm5lbC5vcmcNCj4gPiBNb3JlIG1ham9yZG9tbyBpbmZvIGF0ICBo
dHRwOi8vdmdlci5rZXJuZWwub3JnL21ham9yZG9tby1pbmZvLmh0bWwNCj4gPg0KDQo=

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

* RE: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
  2015-01-26 20:44   ` Jonathan Cameron
@ 2015-01-27 17:09       ` Tirdea, Irina
  0 siblings, 0 replies; 33+ messages in thread
From: Tirdea, Irina @ 2015-01-27 17:09 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Dogaru, Vlad, Baluta, Daniel, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald



> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@kernel.org]
> Sent: 26 January, 2015 22:44
> To: Tirdea, Irina; linux-iio@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Dogaru, Vlad; Baluta, Daniel; Hartmut Knaack; Lars-Peter Clausen; Peter Meerwald
> Subject: Re: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
> 
> On 11/01/15 19:10, Irina Tirdea wrote:
> > Add support for Freescale MMA9553L Intelligent Pedometer Platform.
> >
> > The following functionalities are supported:
> >  - step counter (counts the number of steps using a HW register)
> >  - step detector (generates an iio event at every step the user takes)
> >  - activity recognition (rest, walking, jogging, running)
> >  - speed
> >  - calories
> >  - distance
> >
> > To get accurate pedometer results, the user's height, weight and gender
> > need to be configured.
> >
> > The specifications can be downloaded from:
> > http://www.freescale.com/files/sensors/doc/ref_manual/MMA955xLSWRM.pdf
> > http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf
> >
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> One thing noticed in passing.  Calibheight is in cm whereas distance is
> in m. This seems inconsistent.  Would you mind changing to meters for
> calibheight?
> (lets do it whilst we don't really have any users yet).
Sure, using meters is better. 
I'll make the change to the iio Documentation and the driver.
> 
> 
> A very nice driver, just a few minor queries in line.  Also of course
> the interface for the debounce of steps as discussed in the thread
> hanging off earlier in this series.
> 
> I'm not keen on the addition to iio.h (yet) as it is only used by this
> one driver and as a general rule macros that simple are best avoided
> (tend to limit things rather than help).
Ok, I'll move the code to the driver.
> 
> > ---
> >  Documentation/ABI/testing/sysfs-bus-iio |   49 +-
> >  drivers/iio/accel/Kconfig               |   10 +
> >  drivers/iio/accel/Makefile              |    1 +
> >  drivers/iio/accel/mma9551_core.c        |  183 +++++
> >  drivers/iio/accel/mma9551_core.h        |   17 +-
> >  drivers/iio/accel/mma9553.c             | 1321 +++++++++++++++++++++++++++++++
> >  include/linux/iio/iio.h                 |   11 +
> >  7 files changed, 1587 insertions(+), 5 deletions(-)
> >  create mode 100644 drivers/iio/accel/mma9553.c
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > index e009f49..732d018 100644
> > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > @@ -343,7 +343,30 @@ Description:
> >  		production inaccuracies).  If shared across all channels,
> >  		<type>_calibscale is used.
> >
> > -What:		/sys/bus/iio/devices/iio:deviceX/in_steps_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
> > +KernelVersion:	3.20
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Gender of the user (e.g.: male, female) used by some pedometers
> > +		to compute the stride length, distance, speed and activity
> > +		type.
> > +
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
> > +KernelVersion:	3.20
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Lists all available gender values (e.g.: male, female).
> > +
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
> >  KernelVersion:	3.19
> >  Contact:	linux-iio@vger.kernel.org
> >  Description:
> > @@ -818,6 +841,14 @@ What:		/sys/.../events/in_tempY_roc_falling_period
> >  What:		/sys/.../events/in_accel_x&y&z_mag_falling_period
> >  What:		/sys/.../events/in_intensity0_thresh_period
> >  What:		/sys/.../events/in_proximity0_thresh_period
> > +What:		/sys/.../events/in_activity_still_thresh_rising_period
> > +What:		/sys/.../events/in_activity_still_thresh_falling_period
> > +What:		/sys/.../events/in_activity_walking_thresh_rising_period
> > +What:		/sys/.../events/in_activity_walking_thresh_falling_period
> > +What:		/sys/.../events/in_activity_jogging_thresh_rising_period
> > +What:		/sys/.../events/in_activity_jogging_thresh_falling_period
> > +What:		/sys/.../events/in_activity_running_thresh_rising_period
> > +What:		/sys/.../events/in_activity_running_thresh_falling_period
> >  KernelVersion:	2.6.37
> >  Contact:	linux-iio@vger.kernel.org
> >  Description:
> > @@ -1142,6 +1173,12 @@ Description:
> >  		This attribute is used to get/set the integration time in
> >  		seconds.
> >
> > +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
> > +KernelVersion:	3.20
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Number of seconds in which to compute speed.
> > +
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
> >  KernelVersion:	3.15
> >  Contact:	linux-iio@vger.kernel.org
> > @@ -1170,13 +1207,17 @@ Description:
> >  		present, output should be considered as processed with the
> >  		unit in milliamps.
> >
> > +What:		/sys/.../iio:deviceX/in_energy_en
> > +What:		/sys/.../iio:deviceX/in_distance_en
> > +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
> The speed one is interesting.  It's an instantaneous measure (unlike the
> others that are cumulative).  As such I'd normally expect it not to need
> an enable (nothing is getting reset).  However as you describe above we
> have a speed calculation over a number of seconds so I can see where
> this comes from.  Hmm.  Not sure on the right answer for this one.
The problem is that all the attributes this driver exports  (speed, energy, distance) are based on the step counter.

Although the step counter is always enabled, we do not want to keep the power on if nobody is using it. The *_en attributes are used to start the step counter by powering on the device.
We cannot use one _en attribute for all exported values because we are using different iio types, but internally all _en attributes are treated as one flag to power on/off the device.
I'm thinking of this scenario: I want to go run, so I start my running application that can show me speed and steps, but I do not want to keep the pedometer on once I'm done.

The _en attribute is more related to power management in this case (although other pedometers have a special register to enable/disable the step counter).

> 
> 
> 
> >  What:		/sys/.../iio:deviceX/in_steps_en
> >  KernelVersion:	3.19
> >  Contact:	linux-iio@vger.kernel.org
> >  Description:
> > -		Activates the step counter. After activation, the number of steps
> > -		taken by the user will be counted in hardware and exported through
> > -		in_steps_input.
> > +		Activates a device feature that runs in firmware/hardware.
> > +		E.g. for steps: the pedometer saves power while not used;
> > +		when activated, it will count the steps taken by the user in
> > +		firmware and export them through in_steps_input.
> >
> >  What:		/sys/.../iio:deviceX/in_steps_input
> >  KernelVersion:	3.19
> > diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> > index c53047d..7c9a9a9 100644
> > --- a/drivers/iio/accel/Kconfig
> > +++ b/drivers/iio/accel/Kconfig
> > @@ -126,4 +126,14 @@ config MMA9551
> >  	  To compile this driver as a module, choose M here: the module
> >  	  will be called mma9551.
> >
> > +config MMA9553
> > +	tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
> > +	depends on I2C
> > +	select MMA9551_CORE
> > +	help
> > +	  Say yes here to build support for the Freescale MMA9553L
> > +	  Intelligent Pedometer Platform Driver.
> > +
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called mma9553.
> >  endmenu
> > diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> > index 8105316..f815695 100644
> > --- a/drivers/iio/accel/Makefile
> > +++ b/drivers/iio/accel/Makefile
> > @@ -12,6 +12,7 @@ obj-$(CONFIG_MMA8452)	+= mma8452.o
> >
> >  obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
> >  obj-$(CONFIG_MMA9551)		+= mma9551.o
> > +obj-$(CONFIG_MMA9553)		+= mma9553.o
> >
> >  obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
> >  st_accel-y := st_accel_core.o
> > diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
> > index 7f1a73e..7f55a6d 100644
> > --- a/drivers/iio/accel/mma9551_core.c
> > +++ b/drivers/iio/accel/mma9551_core.c
> > @@ -53,6 +53,11 @@
> >  #define MMA9551_AFE_Y_ACCEL_REG		0x02
> >  #define MMA9551_AFE_Z_ACCEL_REG		0x04
> >
> > +/* Reset/Suspend/Clear application */
> > +#define MMA9551_RSC_RESET		0x00
> > +#define MMA9551_RSC_OFFSET(mask)	(3 - (ffs(mask) - 1) / 8)
> > +#define MMA9551_RSC_VAL(mask)		(mask >> (((ffs(mask) - 1) / 8) * 8))
> > +
> >  /*
> >   * A response is composed of:
> >   * - control registers: MB0-3
> > @@ -275,6 +280,64 @@ int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
> >  EXPORT_SYMBOL(mma9551_read_status_byte);
> >
> >  /**
> > + * mma9551_read_config_word() - read 1 config word
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @val:	Pointer to store value read
> > + *
> > + * Read one configuration word from the device using MMA955xL command format.
> > + * Commands to the MMA955xL platform consist of a write followed by one or
> > + * more reads.
> > + *
> > + * Locking note: This function must be called with the device lock held.
> > + * Locking is not handled inside the function. Callers should ensure they
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
> > +			    u16 reg, u16 *val)
> > +{
> > +	int ret;
> > +	__be16 v;
> > +
> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> > +			       reg, NULL, 0, (u8 *)&v, 2);
> > +	*val = be16_to_cpu(v);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(mma9551_read_config_word);
> > +
> > +/**
> > + * mma9551_write_config_word() - write 1 config word
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @val:	Value to write
> > + *
> > + * Write one configuration word from the device using MMA955xL command format.
> > + * Commands to the MMA955xL platform consist of a write followed by one or
> > + * more reads.
> > + *
> > + * Locking note: This function must be called with the device lock held.
> > + * Locking is not handled inside the function. Callers should ensure they
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u16 val)
> > +{
> > +	__be16 v = cpu_to_be16(val);
> > +
> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
> > +				(u8 *) &v, 2, NULL, 0);
> > +}
> > +EXPORT_SYMBOL(mma9551_write_config_word);
> > +
> > +/**
> >   * mma9551_read_status_word() - read 1 status word
> >   * @client:	I2C client
> >   * @app_id:	Application ID
> > @@ -306,6 +369,107 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
> >  EXPORT_SYMBOL(mma9551_read_status_word);
> >
> >  /**
> > + * mma9551_read_config_words() - read multiple config words
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @len:	Length of array to read in bytes
> > + * @val:	Array of words to read
> > + *
> > + * Read multiple configuration registers (word-sized registers).
> > + *
> > + * Locking note: This function must be called with the device lock held.
> > + * Locking is not handled inside the function. Callers should ensure they
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u8 len, u16 *buf)
> > +{
> > +	int ret, i;
> > +	int len_words = len / sizeof(u16);
> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> > +
> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> > +			       reg, NULL, 0, (u8 *) be_buf, len);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	for (i = 0; i < len_words; i++)
> > +		buf[i] = be16_to_cpu(be_buf[i]);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(mma9551_read_config_words);
> > +
> > +/**
> > + * mma9551_read_status_words() - read multiple status words
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @len:	Length of array to read in bytes
> > + * @val:	Array of words to read
> > + *
> > + * Read multiple status registers (word-sized registers).
> > + *
> > + * Locking note: This function must be called with the device lock held.
> > + * Locking is not handled inside the function. Callers should ensure they
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
> > +			      u16 reg, u8 len, u16 *buf)
> > +{
> > +	int ret, i;
> > +	int len_words = len / sizeof(u16);
> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> > +
> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
> > +			       reg, NULL, 0, (u8 *) be_buf, len);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	for (i = 0; i < len_words; i++)
> > +		buf[i] = be16_to_cpu(be_buf[i]);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(mma9551_read_status_words);
> > +
> > +/**
> > + * mma9551_write_config_words() - write multiple config words
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @len:	Length of array to write in bytes
> > + * @val:	Array of words to write
> > + *
> > + * Write multiple configuration registers (word-sized registers).
> > + *
> > + * Locking note: This function must be called with the device lock held.
> > + * Locking is not handled inside the function. Callers should ensure they
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
> > +			       u16 reg, u8 len, u16 *buf)
> > +{
> > +	int i;
> > +	int len_words = len / sizeof(u16);
> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> > +
> > +	for (i = 0; i < len_words; i++)
> > +		be_buf[i] = cpu_to_be16(buf[i]);
> > +
> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
> > +				reg, (u8 *) be_buf, len, NULL, 0);
> > +}
> > +EXPORT_SYMBOL(mma9551_write_config_words);
> > +
> > +/**
> >   * mma9551_update_config_bits() - update bits in register
> >   * @client:	I2C client
> >   * @app_id:	Application ID
> > @@ -609,6 +773,25 @@ int mma9551_read_accel_scale(int *val, int *val2)
> >  }
> >  EXPORT_SYMBOL(mma9551_read_accel_scale);
> >
> > +/**
> > + * mma9551_app_reset() - reset application
> > + * @client:	I2C client
> > + * @app_mask:	Application to reset
> > + *
> > + * Reset the given application (using the Reset/Suspend/Clear
> > + * Control Application)
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
> > +{
> > +	return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
> > +					 MMA9551_RSC_RESET +
> > +					 MMA9551_RSC_OFFSET(app_mask),
> > +					 MMA9551_RSC_VAL(app_mask));
> > +}
> > +EXPORT_SYMBOL(mma9551_app_reset);
> > +
> >  MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
> >  MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
> >  MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
> > index e6efd02..edaa56b 100644
> > --- a/drivers/iio/accel/mma9551_core.h
> > +++ b/drivers/iio/accel/mma9551_core.h
> > @@ -21,9 +21,13 @@
> >  #define MMA9551_APPID_AFE		0x06
> >  #define MMA9551_APPID_TILT		0x0B
> >  #define MMA9551_APPID_SLEEP_WAKE	0x12
> > -#define MMA9551_APPID_RESET		0x17
> > +#define MMA9551_APPID_PEDOMETER	        0x15
> > +#define MMA9551_APPID_RCS		0x17
> >  #define MMA9551_APPID_NONE		0xff
> >
> > +/* Reset/Suspend/Clear application app masks */
> > +#define MMA9551_RSC_PED			BIT(21)
> > +
> >  #define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
> >
> >  enum mma9551_gpio_pin {
> > @@ -48,8 +52,18 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
> >  			      u16 reg, u8 val);
> >  int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
> >  			     u16 reg, u8 *val);
> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
> > +			    u16 reg, u16 *val);
> > +int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u16 val);
> >  int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
> >  			     u16 reg, u16 *val);
> > +int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u8 len, u16 *buf);
> > +int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
> > +			      u16 reg, u8 len, u16 *buf);
> > +int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
> > +			       u16 reg, u8 len, u16 *buf);
> >  int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
> >  			       u16 reg, u8 mask, u8 val);
> >  int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
> > @@ -62,5 +76,6 @@ int mma9551_read_accel_chan(struct i2c_client *client,
> >  			    const struct iio_chan_spec *chan,
> >  			    int *val, int *val2);
> >  int mma9551_read_accel_scale(int *val, int *val2);
> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
> >
> >  #endif /* _MMA9551_CORE_H_ */
> > diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
> > new file mode 100644
> > index 0000000..ef6a6e3
> > --- /dev/null
> > +++ b/drivers/iio/accel/mma9553.c
> > @@ -0,0 +1,1321 @@
> > +/*
> > + * Freescale MMA9553L Intelligent Pedometer driver
> > + * Copyright (c) 2014, Intel Corporation.
> > + *
> > + * 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.
> > + *
> > + * This program is distributed in the hope 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/module.h>
> > +#include <linux/i2c.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/slab.h>
> > +#include <linux/acpi.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/iio/events.h>
> > +#include <linux/pm_runtime.h>
> > +#include "mma9551_core.h"
> > +
> > +#define MMA9553_DRV_NAME			"mma9553"
> > +#define MMA9553_IRQ_NAME			"mma9553_event"
> > +#define MMA9553_GPIO_NAME			"mma9553_int"
> > +
> > +/* Pedometer configuration registers (R/W) */
> > +#define MMA9553_CONF_SLEEPMIN		0x00
> > +#define MMA9553_CONF_SLEEPMAX		0x02
> > +#define MMA9553_CONF_SLEEPTHD		0x04
> > +#define MMA9553_CONF_WORD		GENMASK(15, 0)
> > +
> > +#define MMA9553_CONF_CONF_STEPLEN	0x06
> > +#define MMA9553_CONF_CONFIG		BIT(15)
> > +#define MMA9553_CONF_ACT_DBCNTM		BIT(14)
> > +#define MMA9553_CONF_SLP_DBCNTM		BIT(13)
> > +#define MMA9553_CONF_STEPLEN		GENMASK(7, 0)
> > +
> > +#define MMA9553_CONF_HEIGHT_WEIGHT	0x08
> Some of this naming is less than clear.  Which ones
> are the register addresses and which the internal masks?
> (obvious really, but names should reflect this perhaps)
> 
That's true. I'll rename them to make it more clear.

> > +#define MMA9553_CONF_HEIGHT		GENMASK(15, 8)
> > +#define MMA9553_CONF_WEIGHT		GENMASK(7, 0)
> > +
> > +#define MMA9553_CONF_FILTER		0x0A
> > +#define MMA9553_CONF_FILTSTEP		GENMASK(15, 8)
> > +#define MMA9553_CONF_MALE		BIT(7)
> > +#define MMA9553_CONF_FILTTIME		GENMASK(6, 0)
> > +
> > +#define MMA9553_CONF_SPEED_STEP		0x0C
> > +#define MMA9553_CONF_SPDPRD		GENMASK(15, 8)
> > +#define MMA9553_CONF_STEPCOALESCE	GENMASK(7, 0)
> > +
> > +#define MMA9553_CONF_ACTTHD		0x0E
> > +
> > +/* Pedometer status registers (R-only) */
> > +#define MMA9553_STATUS			0x00
> > +#define MMA9553_STATUS_MRGFL		BIT(15)
> > +#define MMA9553_STATUS_SUSPCHG		BIT(14)
> > +#define MMA9553_STATUS_STEPCHG		BIT(13)
> > +#define MMA9553_STATUS_ACTCHG		BIT(12)
> > +#define MMA9553_STATUS_SUSP		BIT(11)
> > +#define MMA9553_STATUS_ACTIVITY		(BIT(10) | BIT(9) | BIT(8))
> > +#define MMA9553_STATUS_VERSION		0x00FF
> > +
> > +#define MMA9553_STEPCNT			0x02
> > +#define MMA9553_DISTANCE		0x04
> > +#define MMA9553_SPEED			0x06
> > +#define MMA9553_CALORIES		0x08
> > +#define MMA9553_SLEEPCNT		0x0A
> > +
> > +/* Pedometer events are always mapped to this pin. */
> > +#define MMA9553_DEFAULT_GPIO_PIN	mma9551_gpio6
> > +#define MMA9553_DEFAULT_GPIO_POLARITY	0
> > +
> > +/* Bitnum used for gpio configuration = bit number in high status byte */
> > +#define STATUS_TO_BITNUM(bit)		(ffs(bit) - 9)
> > +
> > +#define MMA9553_DEFAULT_SAMPLE_RATE	30	/* Hz */
> > +
> > +/*
> > + * The internal activity level must be stable for ACTTHD samples before
> > + * ACTIVITY is updated.The ACTIVITY variable contains the current activity
> > + * level and is updated every time a step is detected or once a second
> > + * if there are no steps.
> > + */
> > +#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) / MMA9553_DEFAULT_SAMPLE_RATE)
> > +#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) * MMA9553_DEFAULT_SAMPLE_RATE)
> > +
> > +/*
> > + * Autonomously suspend pedometer if acceleration vector magnitude
> > + * is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
> > + */
> > +#define MMA9553_DEFAULT_SLEEPMIN	3688	/* 0,9 g */
> > +#define MMA9553_DEFAULT_SLEEPMAX	4508	/* 1,1 g */
> > +#define MMA9553_DEFAULT_SLEEPTHD	(MMA9553_DEFAULT_SAMPLE_RATE * 30)
> > +
> > +#define MMA9553_CONFIG_RETRIES		2
> > +
> > +/* Status register - activity field  */
> > +enum activity_level {
> > +	ACTIVITY_UNKNOWN,
> > +	ACTIVITY_REST,
> > +	ACTIVITY_WALKING,
> > +	ACTIVITY_JOGGING,
> > +	ACTIVITY_RUNNING,
> > +};
> > +
> > +static struct mma9553_event_info {
> > +	enum iio_chan_type type;
> > +	enum iio_modifier mod;
> > +	enum iio_event_direction dir;
> > +} mma9553_events_info[] = {
> > +	{
> > +		.type = IIO_STEPS,
> > +		.mod = IIO_NO_MOD,
> > +		.dir = IIO_EV_DIR_NONE,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_STILL,
> > +		.dir = IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_STILL,
> > +		.dir = IIO_EV_DIR_FALLING,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_WALKING,
> > +		.dir = IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_WALKING,
> > +		.dir = IIO_EV_DIR_FALLING,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_JOGGING,
> > +		.dir = IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_JOGGING,
> > +		.dir = IIO_EV_DIR_FALLING,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_RUNNING,
> > +		.dir = IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type = IIO_ACTIVITY,
> > +		.mod = IIO_MOD_RUNNING,
> > +		.dir = IIO_EV_DIR_FALLING,
> > +	},
> > +};
> > +
> > +#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
> > +
> > +struct mma9553_event {
> > +	struct mma9553_event_info *info;
> > +	bool enabled;
> > +};
> > +
> > +struct mma9553_conf_regs {
> > +	u16 sleepmin;
> > +	u16 sleepmax;
> > +	u16 sleepthd;
> > +	u16 config;
> > +	u16 height_weight;
> > +	u16 filter;
> > +	u16 speed_step;
> > +	u16 actthd;
> > +} __packed;
> > +
> > +struct mma9553_data {
> > +	struct i2c_client *client;
> > +	struct mutex mutex;
> > +	struct mma9553_conf_regs conf;
> > +	struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
> > +	int num_events;
> > +	u8 gpio_bitnum;
> > +	/*
> > +	 * This is used for all features that depend on step count:
> > +	 * step count, distance, speed, calories.
> > +	 */
> > +	bool stepcnt_enabled;
> > +	u16 stepcnt;
> > +	u8 activity;
> > +	s64 timestamp;
> > +};
> > +
> > +static u8 mma9553_get_bits(u16 val, u16 mask)
> > +{
> > +	return (val & mask) >> (ffs(mask) - 1);
> > +}
> > +
> > +static u16 mma9553_set_bits(u16 current_val, u16 val, u16 mask)
> > +{
> > +	return (current_val & ~mask) | (val << (ffs(mask) - 1));
> > +}
> > +
> > +static enum iio_modifier mma9553_activity_to_mod(enum activity_level activity)
> > +{
> > +	switch (activity) {
> > +	case ACTIVITY_RUNNING:
> > +		return IIO_MOD_RUNNING;
> > +	case ACTIVITY_JOGGING:
> > +		return IIO_MOD_JOGGING;
> > +	case ACTIVITY_WALKING:
> > +		return IIO_MOD_WALKING;
> > +	case ACTIVITY_REST:
> > +		return IIO_MOD_STILL;
> > +	case ACTIVITY_UNKNOWN:
> > +	default:
> > +		return IIO_NO_MOD;
> > +	}
> > +}
> > +
> > +static void mma9553_init_events(struct mma9553_data *data)
> > +{
> > +	int i;
> > +
> > +	data->num_events = MMA9553_EVENTS_INFO_SIZE;
> > +	for (i = 0; i < data->num_events; i++) {
> > +		data->events[i].info = &mma9553_events_info[i];
> > +		data->events[i].enabled = false;
> > +	}
> > +}
> > +
> > +static struct mma9553_event *mma9553_get_event(struct mma9553_data *data,
> > +					       enum iio_chan_type type,
> > +					       enum iio_modifier mod,
> > +					       enum iio_event_direction dir)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < data->num_events; i++)
> > +		if (data->events[i].info->type == type &&
> > +		    data->events[i].info->mod == mod &&
> > +		    data->events[i].info->dir == dir)
> > +			return &data->events[i];
> > +
> > +	return NULL;
> > +}
> > +
> > +static bool mma9553_is_any_event_enabled(struct mma9553_data *data,
> > +					 bool check_type,
> > +					 enum iio_chan_type type)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < data->num_events; i++)
> > +		if ((check_type && data->events[i].info->type == type &&
> > +		     data->events[i].enabled) ||
> > +		     (!check_type && data->events[i].enabled))
> > +			return true;
> > +
> > +	return false;
> > +}
> > +
> > +static int mma9553_set_config(struct mma9553_data *data, u16 reg,
> > +			      u16 *p_reg_val, u16 val, u16 mask)
> > +{
> > +	int ret, retries;
> > +	u16 reg_val, config;
> > +
> > +	reg_val = *p_reg_val;
> > +	if (val == mma9553_get_bits(reg_val, mask))
> > +		return 0;
> > +
> > +	reg_val = mma9553_set_bits(reg_val, val, mask);
> > +	ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
> > +					reg, reg_val);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"error writing config register 0x%x\n", reg);
> > +		return ret;
> > +	}
> > +
> > +	*p_reg_val = reg_val;
> > +
> > +	/* Reinitializes the pedometer with current configuration values */
> > +	config = mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
> > +
> > +	ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
> > +					MMA9553_CONF_CONF_STEPLEN, config);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"error writing config register 0x%x\n",
> > +			MMA9553_CONF_CONF_STEPLEN);
> > +		return ret;
> > +	}
> > +
> > +	retries = MMA9553_CONFIG_RETRIES;
> > +	do {
> > +		mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
> > +		ret = mma9551_read_config_word(data->client,
> > +					       MMA9551_APPID_PEDOMETER,
> > +					       MMA9553_CONF_CONF_STEPLEN,
> > +					       &config);
> > +		if (ret < 0)
> > +			return ret;
> > +	} while (mma9553_get_bits(config, MMA9553_CONF_CONFIG) &&
> > +		 --retries > 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
> > +					 u8 *activity, u16 *stepcnt)
> > +{
> > +	u32 status_stepcnt;
> > +	u16 status;
> > +	int ret;
> > +
> > +	ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
> > +					MMA9553_STATUS, sizeof(u32),
> > +					(u16 *) &status_stepcnt);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"error reading status and stepcnt\n");
> > +		return ret;
> > +	}
> > +
> > +	status = status_stepcnt & MMA9553_CONF_WORD;
> > +	*activity = mma9553_get_bits(status, MMA9553_STATUS_ACTIVITY);
> > +	*stepcnt = status_stepcnt >> 16;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_conf_gpio(struct mma9553_data *data)
> > +{
> > +	u8 bitnum = 0, appid = MMA9551_APPID_PEDOMETER;
> > +	int ret;
> > +	struct mma9553_event *ev_step_detect;
> > +	bool activity_enabled;
> > +
> > +	activity_enabled =
> > +	    mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
> > +	ev_step_detect =
> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
> > +
> > +	/*
> > +	 * If both step detector and activity are enabled, use the MRGFL bit.
> > +	 * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
> > +	 */
> > +	if (activity_enabled && ev_step_detect->enabled)
> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_MRGFL);
> > +	else if (ev_step_detect->enabled)
> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_STEPCHG);
> > +	else if (activity_enabled)
> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_ACTCHG);
> > +	else			/* Reset */
> > +		appid = MMA9551_APPID_NONE;
> > +
> > +	if (data->gpio_bitnum == bitnum)
> > +		return 0;
> > +
> > +	/* Save initial values for activity and stepcnt */
> > +	if (activity_enabled || ev_step_detect->enabled)
> > +		mma9553_read_activity_stepcnt(data, &data->activity,
> > +					      &data->stepcnt);
> > +
> > +	ret = mma9551_gpio_config(data->client,
> > +				  MMA9553_DEFAULT_GPIO_PIN,
> > +				  appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
> > +	if (ret < 0)
> > +		return ret;
> > +	data->gpio_bitnum = bitnum;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_init(struct mma9553_data *data)
> > +{
> > +	int ret;
> > +
> > +	ret = mma9551_read_version(data->client);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 * Read all the pedometer configuration registers. This is used as
> > +	 * a device identification command to differentiate the MMA9553L
> > +	 * from the MMA9550L.
> > +	 */
> > +	ret =
> > +	    mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
> > +				      MMA9553_CONF_SLEEPMIN,
> > +				      sizeof(data->conf), (u16 *) &data->conf);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"device is not MMA9553L: failed to read cfg regs\n");
> > +		return ret;
> > +	}
> > +
> > +
> > +	/* Reset gpio */
> > +	data->gpio_bitnum = -1;
> > +	ret = mma9553_conf_gpio(data);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret = mma9551_app_reset(data->client, MMA9551_RSC_PED);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* Init config registers */
> > +	data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
> > +	data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
> > +	data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
> > +	data->conf.config =
> > +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
> > +	/*
> > +	 * Clear the activity debounce counter when the activity level changes,
> > +	 * so that the confidence level applies for any activity level.
> > +	 */
> > +	data->conf.config =
> > +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_ACT_DBCNTM);
> > +	ret =
> > +	    mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
> > +				       MMA9553_CONF_SLEEPMIN,
> > +				       sizeof(data->conf), (u16 *) &data->conf);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"failed to write configuration registers\n");
> > +		return ret;
> > +	}
> > +
> > +	return mma9551_set_device_state(data->client, true);
> > +}
> > +
> > +static int mma9553_read_raw(struct iio_dev *indio_dev,
> > +			    struct iio_chan_spec const *chan,
> > +			    int *val, int *val2, long mask)
> > +{
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +	u16 tmp;
> > +	u8 activity;
> > +	bool powered_on;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_PROCESSED:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			/*
> > +			 * The HW only counts steps and other dependent
> > +			 * parameters (speed, distance, calories, activity)
> > +			 * if power is on (from enabling an event or the
> > +			 * step counter */
> > +			powered_on =
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_STEPCNT, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val = tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_DISTANCE:
> > +			powered_on =
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_DISTANCE, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val = tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_ACTIVITY:
> > +			powered_on =
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_STATUS, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +
> > +			activity =
> > +			    mma9553_get_bits(tmp, MMA9553_STATUS_ACTIVITY);
> > +
> > +			/*
> > +			 * The device does not support confidence value levels,
> > +			 * so we will always have 100% for current activity and
> > +			 * 0% for the others.
> > +			 */
> > +			if (chan->channel2 == mma9553_activity_to_mod(activity))
> > +				*val = 100;
> > +			else
> > +				*val = 0;
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_RAW:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:	/* m/h */
> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			powered_on =
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_SPEED, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val = tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_ENERGY:	/* Cal or kcal */
> > +			powered_on =
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_CALORIES, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val = tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_ACCEL:
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9551_read_accel_chan(data->client,
> > +						      chan, val, val2);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_SCALE:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:	/* m/h to m/s */
> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			*val = 0;
> > +			*val2 = 277;	/* 0.000277 */
> > +			return IIO_VAL_INT_PLUS_MICRO;
> > +		case IIO_ENERGY:	/* Cal or kcal to J */
> > +			*val = 4184;
> > +			return IIO_VAL_INT;
> > +		case IIO_ACCEL:
> > +			return mma9551_read_accel_scale(val, val2);
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_ENABLE:
> > +		*val = data->stepcnt_enabled;
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
> > +		*val = mma9553_get_bits(data->conf.height_weight,
> > +					MMA9553_CONF_HEIGHT);
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
> > +		*val = mma9553_get_bits(data->conf.height_weight,
> > +					MMA9553_CONF_WEIGHT);
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			*val = mma9553_get_bits(data->conf.filter,
> > +						MMA9553_CONF_FILTSTEP);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			*val = mma9553_get_bits(data->conf.filter,
> > +						MMA9553_CONF_FILTTIME);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_INT_TIME:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:
> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			*val = mma9553_get_bits(data->conf.speed_step,
> > +						MMA9553_CONF_SPDPRD);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_write_raw(struct iio_dev *indio_dev,
> > +			     struct iio_chan_spec const *chan,
> > +			     int val, int val2, long mask)
> > +{
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_ENABLE:
> > +		if (data->stepcnt_enabled == !!val)
> > +			return 0;
> > +		mutex_lock(&data->mutex);
> > +		ret = mma9551_set_power_state(data->client, val);
> > +		if (ret < 0) {
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		}
> > +		data->stepcnt_enabled = val;
> > +		mutex_unlock(&data->mutex);
> > +		return 0;
> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
> > +		if (val < 0 || val > 255)
> > +			return -EINVAL;
> We are being rather inconsistent in having calibheight in cm rather than
> m (as per distance etc). I wonder if now is the time to change it whilst
> we have few users...

Sure, I'll change it.
> > +		mutex_lock(&data->mutex);
> > +		ret = mma9553_set_config(data,
> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
> > +					 &data->conf.height_weight,
> > +					 val, MMA9553_CONF_HEIGHT);
> > +		mutex_unlock(&data->mutex);
> > +		return ret;
> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
> > +		if (val < 0 || val > 255)
> > +			return -EINVAL;
> > +		mutex_lock(&data->mutex);
> > +		ret = mma9553_set_config(data,
> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
> > +					 &data->conf.height_weight,
> > +					 val, MMA9553_CONF_WEIGHT);
> > +		mutex_unlock(&data->mutex);
> > +		return ret;
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			/*
> > +			 * Set to 0 to disable step filtering. If the value
> > +			 * specified is greater than 6, then 6 will be used.
> > +			 */
> > +			if (val < 0)
> > +				return -EINVAL;
> > +			if (val > 6)
> > +				val = 6;
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
> > +						 &data->conf.filter, val,
> > +						 MMA9553_CONF_FILTSTEP);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			if (val < 0 || val > 127)
> > +				return -EINVAL;
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
> > +						 &data->conf.filter, val,
> > +						 MMA9553_CONF_FILTTIME);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_INT_TIME:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:
> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			/*
> > +			 * If set to a value greater than 5, then 5 will be
> > +			 * used. Warning: Do not set SPDPRD to 0 or 1 as
> > +			 * this may cause undesirable behavior.
> > +			 */
> > +			if (val < 2)
> > +				return -EINVAL;
> > +			if (val > 5)
> > +				val = 5;
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
> > +						 &data->conf.speed_step, val,
> > +						 MMA9553_CONF_SPDPRD);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
> > +	struct mma9553_event *event;
> > +
> > +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
> > +	if (!event)
> > +		return -EINVAL;
> > +
> > +	return event->enabled;
> > +}
> > +
> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
> > +	struct mma9553_event *event;
> > +	int ret;
> > +
> > +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
> > +	if (!event)
> > +		return -EINVAL;
> > +
> > +	if (event->enabled == state)
> > +		return 0;
> > +
> > +	mutex_lock(&data->mutex);
> > +
> > +	ret = mma9551_set_power_state(data->client, state);
> > +	if (ret < 0)
> > +		goto err_out;
> > +	event->enabled = state;
> > +
> > +	ret = mma9553_conf_gpio(data);
> > +	if (ret < 0)
> > +		goto err_conf_gpio;
> > +
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +
> > +err_conf_gpio:
> > +	if (state) {
> > +		event->enabled = false;
> > +		mma9551_set_power_state(data->client, false);
> > +	}
> > +err_out:
> > +	mutex_unlock(&data->mutex);
> > +	return ret;
> > +}
> > +
> > +static int mma9553_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)
> > +{
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +
> > +	*val2 = 0;
> > +	switch (info) {
> > +	case IIO_EV_INFO_VALUE:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			*val = mma9553_get_bits(data->conf.speed_step,
> > +						MMA9553_CONF_STEPCOALESCE);
> > +			return IIO_VAL_INT;
> > +		case IIO_ACTIVITY:
> > +			/*
> > +			 * The device does not support confidence value levels.
> > +			 * We set an average of 50%.
> > +			 */
> > +			*val = 50;
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_EV_INFO_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_ACTIVITY:
> > +			*val = MMA9553_ACTIVITY_THD_TO_SEC(data->conf.actthd);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (info) {
> > +	case IIO_EV_INFO_VALUE:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			if (val < 0 || val > 255)
> > +				return -EINVAL;
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
> > +						 &data->conf.speed_step, val,
> > +						 MMA9553_CONF_STEPCOALESCE);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_EV_INFO_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_ACTIVITY:
> > +			mutex_lock(&data->mutex);
> > +			ret = mma9553_set_config(data, MMA9553_CONF_ACTTHD,
> > +						 &data->conf.actthd,
> > +						 MMA9553_ACTIVITY_SEC_TO_THD
> > +						 (val), MMA9553_CONF_WORD);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
> > +					const struct iio_chan_spec *chan)
> > +{
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	u8 gender;
> > +
> > +	gender = mma9553_get_bits(data->conf.filter, MMA9553_CONF_MALE);
> > +	/*
> > +	 * HW expects 0 for female and 1 for male,
> > +	 * while iio index is 0 for male and 1 for female
> > +	 */
> > +	return !gender;
> > +}
> > +
> > +static int mma9553_set_calibgender_mode(struct iio_dev *indio_dev,
> > +					const struct iio_chan_spec *chan,
> > +					unsigned int mode)
> > +{
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	u8 gender = !mode;
> > +	int ret;
> > +
> > +	if ((mode != 0) && (mode != 1))
> > +		return -EINVAL;
> > +	mutex_lock(&data->mutex);
> > +	ret = mma9553_set_config(data, MMA9553_CONF_FILTER, &data->conf.filter,
> > +				 gender, MMA9553_CONF_MALE);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct iio_event_spec mma9553_step_event = {
> > +	.type = IIO_EV_TYPE_CHANGE,
> > +	.dir = IIO_EV_DIR_NONE,
> > +	.mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
> > +};
> > +
> > +static const struct iio_event_spec mma9553_activity_events[] = {
> > +	{
> > +		.type = IIO_EV_TYPE_THRESH,
> > +		.dir = IIO_EV_DIR_RISING,
> > +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
> > +				 BIT(IIO_EV_INFO_VALUE) |
> > +				 BIT(IIO_EV_INFO_PERIOD),
> > +	 },
> > +	{
> > +		.type = IIO_EV_TYPE_THRESH,
> > +		.dir = IIO_EV_DIR_FALLING,
> > +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
> > +				 BIT(IIO_EV_INFO_VALUE) |
> > +				 BIT(IIO_EV_INFO_PERIOD),
> > +	},
> > +};
> > +
> > +static const char * const calibgender_modes[] = { "male", "female" };
> > +
> > +static const struct iio_enum mma9553_calibgender_enum = {
> > +	.items = calibgender_modes,
> > +	.num_items = ARRAY_SIZE(calibgender_modes),
> > +	.get = mma9553_get_calibgender_mode,
> > +	.set = mma9553_set_calibgender_mode,
> > +};
> > +
> > +static const struct iio_chan_spec_ext_info mma9553_ext_info[] = {
> > +	IIO_CALIBGENDER_EXT_INFO(IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum),
> > +	{},
> > +};
> > +
> > +#define MMA9553_PEDOMETER_CHANNEL(_type, _mask) {		\
> > +	.type = _type,						\
> > +	.info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE)      |	\
> > +			      BIT(IIO_CHAN_INFO_CALIBHEIGHT) |	\
> > +			      _mask,				\
> > +	.ext_info = mma9553_ext_info,				\
> > +}
> > +
> > +#define MMA9553_ACTIVITY_CHANNEL(_chan2) {				\
> > +	.type = IIO_ACTIVITY,						\
> > +	.modified = 1,							\
> > +	.channel2 = _chan2,						\
> > +	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),		\
> > +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),	\
> > +	.event_spec = mma9553_activity_events,				\
> > +	.num_event_specs = ARRAY_SIZE(mma9553_activity_events),		\
> > +	.ext_info = mma9553_ext_info,					\
> > +}
> > +
> > +static const struct iio_chan_spec mma9553_channels[] = {
> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
> > +
> > +	{
> > +		.type = IIO_STEPS,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> > +				     BIT(IIO_CHAN_INFO_ENABLE) |
> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH) |
> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD),
> > +		.event_spec = &mma9553_step_event,
> > +		.num_event_specs = 1,
> > +	},
> > +
> > +	MMA9553_PEDOMETER_CHANNEL(IIO_DISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)),
> > +	{
> > +		.type = IIO_VELOCITY,
> > +		.modified = 1,
> > +		.channel2 = IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE) |
> > +				      BIT(IIO_CHAN_INFO_INT_TIME) |
> > +				      BIT(IIO_CHAN_INFO_ENABLE),
> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),
> > +		.ext_info = mma9553_ext_info,
> > +	},
> > +	MMA9553_PEDOMETER_CHANNEL(IIO_ENERGY, BIT(IIO_CHAN_INFO_RAW) |
> > +				  BIT(IIO_CHAN_INFO_SCALE) |
> > +				  BIT(IIO_CHAN_INFO_CALIBWEIGHT)),
> > +
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_JOGGING),
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_STILL),
> > +};
> > +
> > +static const struct iio_info mma9553_info = {
> > +	.driver_module = THIS_MODULE,
> > +	.read_raw = mma9553_read_raw,
> > +	.write_raw = mma9553_write_raw,
> > +	.read_event_config = mma9553_read_event_config,
> > +	.write_event_config = mma9553_write_event_config,
> > +	.read_event_value = mma9553_read_event_value,
> > +	.write_event_value = mma9553_write_event_value,
> > +};
> > +
> > +static irqreturn_t mma9553_irq_handler(int irq, void *private)
> > +{
> > +	struct iio_dev *indio_dev = private;
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +
> > +	data->timestamp = iio_get_time_ns();
> > +	/*
> > +	 * Since we only configure the interrupt pin when an
> > +	 * event is enabled, we are sure we have at least
> > +	 * one event enabled at this point.
> > +	 */
> > +	return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t mma9553_event_handler(int irq, void *private)
> > +{
> > +	struct iio_dev *indio_dev = private;
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	u16 stepcnt;
> > +	u8 activity;
> > +	struct mma9553_event *ev_activity, *ev_prev_activity, *ev_step_detect;
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret = mma9553_read_activity_stepcnt(data, &activity, &stepcnt);
> > +	if (ret < 0) {
> > +		mutex_unlock(&data->mutex);
> > +		return IRQ_HANDLED;
> > +	}
> > +
> > +	ev_prev_activity =
> > +	    mma9553_get_event(data, IIO_ACTIVITY,
> > +			      mma9553_activity_to_mod(data->activity),
> > +			      IIO_EV_DIR_FALLING);
> > +	ev_activity =
> > +	    mma9553_get_event(data, IIO_ACTIVITY,
> > +			      mma9553_activity_to_mod(activity),
> > +			      IIO_EV_DIR_RISING);
> > +	ev_step_detect =
> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
> > +
> > +	if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
> > +		data->stepcnt = stepcnt;
> > +		iio_push_event(indio_dev,
> > +			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
> > +			       IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
> > +			       data->timestamp);
> > +	}
> > +
> > +	if (activity != data->activity) {
> > +		data->activity = activity;
> > +		/* ev_activity can be NULL if activity == ACTIVITY_UNKNOWN */
> > +		if (ev_prev_activity && ev_prev_activity->enabled)
> > +			iio_push_event(indio_dev,
> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
> > +				       ev_prev_activity->info->mod,
> > +				       IIO_EV_DIR_FALLING,
> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
> > +				       data->timestamp);
> > +
> > +		if (ev_activity && ev_activity->enabled)
> > +			iio_push_event(indio_dev,
> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
> > +				       ev_activity->info->mod,
> > +				       IIO_EV_DIR_RISING,
> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
> > +				       data->timestamp);
> > +	}
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int mma9553_gpio_probe(struct i2c_client *client)
> > +{
> > +	struct device *dev;
> > +	struct gpio_desc *gpio;
> > +	int ret;
> > +
> > +	if (!client)
> > +		return -EINVAL;
> > +
> > +	dev = &client->dev;
> > +
> > +	/* data ready gpio interrupt pin */
> > +	gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
> > +	if (IS_ERR(gpio)) {
> > +		dev_err(dev, "acpi gpio get index failed\n");
> > +		return PTR_ERR(gpio);
> > +	}
> > +
> > +	ret = gpiod_direction_input(gpio);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = gpiod_to_irq(gpio);
> > +
> > +	dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
> > +
> > +	return ret;
> > +}
> > +
> > +static const char *mma9553_match_acpi_device(struct device *dev)
> > +{
> > +	const struct acpi_device_id *id;
> > +
> > +	id = acpi_match_device(dev->driver->acpi_match_table, dev);
> > +	if (!id)
> > +		return NULL;
> > +
> > +	return dev_name(dev);
> > +}
> > +
> > +static int mma9553_probe(struct i2c_client *client,
> > +			 const struct i2c_device_id *id)
> > +{
> > +	struct mma9553_data *data;
> > +	struct iio_dev *indio_dev;
> > +	const char *name = NULL;
> > +	int ret;
> > +
> > +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	data = iio_priv(indio_dev);
> > +	i2c_set_clientdata(client, indio_dev);
> > +	data->client = client;
> > +
> > +	if (id)
> > +		name = id->name;
> > +	else if (ACPI_HANDLE(&client->dev))
> > +		name = mma9553_match_acpi_device(&client->dev);
> > +	else
> > +		return -ENOSYS;
> > +
> > +	mutex_init(&data->mutex);
> > +	mma9553_init_events(data);
> > +
> > +	ret = mma9553_init(data);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	indio_dev->dev.parent = &client->dev;
> > +	indio_dev->channels = mma9553_channels;
> > +	indio_dev->num_channels = ARRAY_SIZE(mma9553_channels);
> > +	indio_dev->name = name;
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->info = &mma9553_info;
> > +
> > +	if (client->irq < 0)
> > +		client->irq = mma9553_gpio_probe(client);
> > +
> > +	if (client->irq >= 0) {
> > +		ret = devm_request_threaded_irq(&client->dev, client->irq,
> > +						mma9553_irq_handler,
> > +						mma9553_event_handler,
> > +						IRQF_TRIGGER_RISING,
> > +						MMA9553_IRQ_NAME, indio_dev);
> > +		if (ret < 0) {
> > +			dev_err(&client->dev, "request irq %d failed\n",
> > +				client->irq);
> > +			goto out_poweroff;
> > +		}
> > +
> > +	}
> > +
> > +	ret = iio_device_register(indio_dev);
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "unable to register iio device\n");
> > +		goto out_poweroff;
> > +	}
> > +
> > +	ret = pm_runtime_set_active(&client->dev);
> > +	if (ret < 0)
> > +		goto out_iio_unregister;
> > +
> > +	pm_runtime_enable(&client->dev);
> > +	pm_runtime_set_autosuspend_delay(&client->dev,
> > +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
> > +	pm_runtime_use_autosuspend(&client->dev);
> > +
> > +	dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
> > +
> > +	return 0;
> > +
> > +out_iio_unregister:
> > +	iio_device_unregister(indio_dev);
> > +out_poweroff:
> > +	mma9551_set_device_state(client, false);
> > +	return ret;
> > +}
> > +
> > +static int mma9553_remove(struct i2c_client *client)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +
> > +	pm_runtime_disable(&client->dev);
> > +	pm_runtime_set_suspended(&client->dev);
> > +	pm_runtime_put_noidle(&client->dev);
> > +
> > +	iio_device_unregister(indio_dev);
> > +	mutex_lock(&data->mutex);
> > +	mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int mma9553_runtime_suspend(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret = mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev, "powering off device failed\n");
> > +		return -EAGAIN;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_runtime_resume(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	ret = mma9551_set_device_state(data->client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
> > +
> > +	return 0;
> > +}
> > +#endif
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mma9553_suspend(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret = mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +static int mma9553_resume(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret = mma9551_set_device_state(data->client, true);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +}
> > +#endif
> > +
> > +static const struct dev_pm_ops mma9553_pm_ops = {
> > +	SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
> > +	SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
> > +			   mma9553_runtime_resume, NULL)
> > +};
> > +
> > +static const struct acpi_device_id mma9553_acpi_match[] = {
> > +	{"MMA9553", 0},
> > +	{},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
> > +
> > +static const struct i2c_device_id mma9553_id[] = {
> > +	{"mma9553", 0},
> > +	{},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(i2c, mma9553_id);
> > +
> > +static struct i2c_driver mma9553_driver = {
> > +	.driver = {
> > +		   .name = MMA9553_DRV_NAME,
> > +		   .acpi_match_table = ACPI_PTR(mma9553_acpi_match),
> > +		   .pm = &mma9553_pm_ops,
> > +		   },
> > +	.probe = mma9553_probe,
> > +	.remove = mma9553_remove,
> > +	.id_table = mma9553_id,
> > +};
> > +
> > +module_i2c_driver(mma9553_driver);
> > +
> > +MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > index b6b12ac..6770a2f 100644
> > --- a/include/linux/iio/iio.h
> > +++ b/include/linux/iio/iio.h
> > @@ -634,4 +634,15 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
> >   */
> >  #define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
> >
> > +/**
> > + * IIO_CALIBGENDER_EXT_INFO() - define the gender of the user
> > + *
> > + * @_shared:	Whether the attribute is shared between all channels
> > + * @_e:		Pointer to an iio_enum struct
> > + *
> > + */
> > +#define IIO_CALIBGENDER_EXT_INFO(_shared, _e)			       \
> > +	IIO_ENUM("calibgender", _shared, _e),			       \
> > +	IIO_ENUM_AVAILABLE("calibgender", _e)
> > +
> For now this is a little obscure to drop into the main header.
> If we get lots of instances, then perhaps it'll make sense to
> move it here.  Right now, please keep it in the driver.


Ok, will move this to the driver.
> >  #endif /* _INDUSTRIAL_IO_H_ */
> >


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

* RE: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
@ 2015-01-27 17:09       ` Tirdea, Irina
  0 siblings, 0 replies; 33+ messages in thread
From: Tirdea, Irina @ 2015-01-27 17:09 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Dogaru, Vlad, Baluta, Daniel, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald



> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@kernel.org]
> Sent: 26 January, 2015 22:44
> To: Tirdea, Irina; linux-iio@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Dogaru, Vlad; Baluta, Daniel; Hartmut K=
naack; Lars-Peter Clausen; Peter Meerwald
> Subject: Re: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
>=20
> On 11/01/15 19:10, Irina Tirdea wrote:
> > Add support for Freescale MMA9553L Intelligent Pedometer Platform.
> >
> > The following functionalities are supported:
> >  - step counter (counts the number of steps using a HW register)
> >  - step detector (generates an iio event at every step the user takes)
> >  - activity recognition (rest, walking, jogging, running)
> >  - speed
> >  - calories
> >  - distance
> >
> > To get accurate pedometer results, the user's height, weight and gender
> > need to be configured.
> >
> > The specifications can be downloaded from:
> > http://www.freescale.com/files/sensors/doc/ref_manual/MMA955xLSWRM.pdf
> > http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf
> >
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> One thing noticed in passing.  Calibheight is in cm whereas distance is
> in m. This seems inconsistent.  Would you mind changing to meters for
> calibheight?
> (lets do it whilst we don't really have any users yet).
Sure, using meters is better.=20
I'll make the change to the iio Documentation and the driver.
>=20
>=20
> A very nice driver, just a few minor queries in line.  Also of course
> the interface for the debounce of steps as discussed in the thread
> hanging off earlier in this series.
>=20
> I'm not keen on the addition to iio.h (yet) as it is only used by this
> one driver and as a general rule macros that simple are best avoided
> (tend to limit things rather than help).
Ok, I'll move the code to the driver.
>=20
> > ---
> >  Documentation/ABI/testing/sysfs-bus-iio |   49 +-
> >  drivers/iio/accel/Kconfig               |   10 +
> >  drivers/iio/accel/Makefile              |    1 +
> >  drivers/iio/accel/mma9551_core.c        |  183 +++++
> >  drivers/iio/accel/mma9551_core.h        |   17 +-
> >  drivers/iio/accel/mma9553.c             | 1321 +++++++++++++++++++++++=
++++++++
> >  include/linux/iio/iio.h                 |   11 +
> >  7 files changed, 1587 insertions(+), 5 deletions(-)
> >  create mode 100644 drivers/iio/accel/mma9553.c
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/AB=
I/testing/sysfs-bus-iio
> > index e009f49..732d018 100644
> > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > @@ -343,7 +343,30 @@ Description:
> >  		production inaccuracies).  If shared across all channels,
> >  		<type>_calibscale is used.
> >
> > -What:		/sys/bus/iio/devices/iio:deviceX/in_steps_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
> > +KernelVersion:	3.20
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Gender of the user (e.g.: male, female) used by some pedometers
> > +		to compute the stride length, distance, speed and activity
> > +		type.
> > +
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_availa=
ble
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_availabl=
e
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_availa=
ble
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_availa=
ble
> > +KernelVersion:	3.20
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Lists all available gender values (e.g.: male, female).
> > +
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
> >  KernelVersion:	3.19
> >  Contact:	linux-iio@vger.kernel.org
> >  Description:
> > @@ -818,6 +841,14 @@ What:		/sys/.../events/in_tempY_roc_falling_period
> >  What:		/sys/.../events/in_accel_x&y&z_mag_falling_period
> >  What:		/sys/.../events/in_intensity0_thresh_period
> >  What:		/sys/.../events/in_proximity0_thresh_period
> > +What:		/sys/.../events/in_activity_still_thresh_rising_period
> > +What:		/sys/.../events/in_activity_still_thresh_falling_period
> > +What:		/sys/.../events/in_activity_walking_thresh_rising_period
> > +What:		/sys/.../events/in_activity_walking_thresh_falling_period
> > +What:		/sys/.../events/in_activity_jogging_thresh_rising_period
> > +What:		/sys/.../events/in_activity_jogging_thresh_falling_period
> > +What:		/sys/.../events/in_activity_running_thresh_rising_period
> > +What:		/sys/.../events/in_activity_running_thresh_falling_period
> >  KernelVersion:	2.6.37
> >  Contact:	linux-iio@vger.kernel.org
> >  Description:
> > @@ -1142,6 +1173,12 @@ Description:
> >  		This attribute is used to get/set the integration time in
> >  		seconds.
> >
> > +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_=
time
> > +KernelVersion:	3.20
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Number of seconds in which to compute speed.
> > +
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
> >  KernelVersion:	3.15
> >  Contact:	linux-iio@vger.kernel.org
> > @@ -1170,13 +1207,17 @@ Description:
> >  		present, output should be considered as processed with the
> >  		unit in milliamps.
> >
> > +What:		/sys/.../iio:deviceX/in_energy_en
> > +What:		/sys/.../iio:deviceX/in_distance_en
> > +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
> The speed one is interesting.  It's an instantaneous measure (unlike the
> others that are cumulative).  As such I'd normally expect it not to need
> an enable (nothing is getting reset).  However as you describe above we
> have a speed calculation over a number of seconds so I can see where
> this comes from.  Hmm.  Not sure on the right answer for this one.
The problem is that all the attributes this driver exports  (speed, energy,=
 distance) are based on the step counter.

Although the step counter is always enabled, we do not want to keep the pow=
er on if nobody is using it. The *_en attributes are used to start the step=
 counter by powering on the device.
We cannot use one _en attribute for all exported values because we are usin=
g different iio types, but internally all _en attributes are treated as one=
 flag to power on/off the device.
I'm thinking of this scenario: I want to go run, so I start my running appl=
ication that can show me speed and steps, but I do not want to keep the ped=
ometer on once I'm done.

The _en attribute is more related to power management in this case (althoug=
h other pedometers have a special register to enable/disable the step count=
er).

>=20
>=20
>=20
> >  What:		/sys/.../iio:deviceX/in_steps_en
> >  KernelVersion:	3.19
> >  Contact:	linux-iio@vger.kernel.org
> >  Description:
> > -		Activates the step counter. After activation, the number of steps
> > -		taken by the user will be counted in hardware and exported through
> > -		in_steps_input.
> > +		Activates a device feature that runs in firmware/hardware.
> > +		E.g. for steps: the pedometer saves power while not used;
> > +		when activated, it will count the steps taken by the user in
> > +		firmware and export them through in_steps_input.
> >
> >  What:		/sys/.../iio:deviceX/in_steps_input
> >  KernelVersion:	3.19
> > diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> > index c53047d..7c9a9a9 100644
> > --- a/drivers/iio/accel/Kconfig
> > +++ b/drivers/iio/accel/Kconfig
> > @@ -126,4 +126,14 @@ config MMA9551
> >  	  To compile this driver as a module, choose M here: the module
> >  	  will be called mma9551.
> >
> > +config MMA9553
> > +	tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
> > +	depends on I2C
> > +	select MMA9551_CORE
> > +	help
> > +	  Say yes here to build support for the Freescale MMA9553L
> > +	  Intelligent Pedometer Platform Driver.
> > +
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called mma9553.
> >  endmenu
> > diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> > index 8105316..f815695 100644
> > --- a/drivers/iio/accel/Makefile
> > +++ b/drivers/iio/accel/Makefile
> > @@ -12,6 +12,7 @@ obj-$(CONFIG_MMA8452)	+=3D mma8452.o
> >
> >  obj-$(CONFIG_MMA9551_CORE)	+=3D mma9551_core.o
> >  obj-$(CONFIG_MMA9551)		+=3D mma9551.o
> > +obj-$(CONFIG_MMA9553)		+=3D mma9553.o
> >
> >  obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) +=3D st_accel.o
> >  st_accel-y :=3D st_accel_core.o
> > diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma95=
51_core.c
> > index 7f1a73e..7f55a6d 100644
> > --- a/drivers/iio/accel/mma9551_core.c
> > +++ b/drivers/iio/accel/mma9551_core.c
> > @@ -53,6 +53,11 @@
> >  #define MMA9551_AFE_Y_ACCEL_REG		0x02
> >  #define MMA9551_AFE_Z_ACCEL_REG		0x04
> >
> > +/* Reset/Suspend/Clear application */
> > +#define MMA9551_RSC_RESET		0x00
> > +#define MMA9551_RSC_OFFSET(mask)	(3 - (ffs(mask) - 1) / 8)
> > +#define MMA9551_RSC_VAL(mask)		(mask >> (((ffs(mask) - 1) / 8) * 8))
> > +
> >  /*
> >   * A response is composed of:
> >   * - control registers: MB0-3
> > @@ -275,6 +280,64 @@ int mma9551_read_status_byte(struct i2c_client *cl=
ient, u8 app_id,
> >  EXPORT_SYMBOL(mma9551_read_status_byte);
> >
> >  /**
> > + * mma9551_read_config_word() - read 1 config word
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @val:	Pointer to store value read
> > + *
> > + * Read one configuration word from the device using MMA955xL command =
format.
> > + * Commands to the MMA955xL platform consist of a write followed by on=
e or
> > + * more reads.
> > + *
> > + * Locking note: This function must be called with the device lock hel=
d.
> > + * Locking is not handled inside the function. Callers should ensure t=
hey
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
> > +			    u16 reg, u16 *val)
> > +{
> > +	int ret;
> > +	__be16 v;
> > +
> > +	ret =3D mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> > +			       reg, NULL, 0, (u8 *)&v, 2);
> > +	*val =3D be16_to_cpu(v);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(mma9551_read_config_word);
> > +
> > +/**
> > + * mma9551_write_config_word() - write 1 config word
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @val:	Value to write
> > + *
> > + * Write one configuration word from the device using MMA955xL command=
 format.
> > + * Commands to the MMA955xL platform consist of a write followed by on=
e or
> > + * more reads.
> > + *
> > + * Locking note: This function must be called with the device lock hel=
d.
> > + * Locking is not handled inside the function. Callers should ensure t=
hey
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u16 val)
> > +{
> > +	__be16 v =3D cpu_to_be16(val);
> > +
> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg=
,
> > +				(u8 *) &v, 2, NULL, 0);
> > +}
> > +EXPORT_SYMBOL(mma9551_write_config_word);
> > +
> > +/**
> >   * mma9551_read_status_word() - read 1 status word
> >   * @client:	I2C client
> >   * @app_id:	Application ID
> > @@ -306,6 +369,107 @@ int mma9551_read_status_word(struct i2c_client *c=
lient, u8 app_id,
> >  EXPORT_SYMBOL(mma9551_read_status_word);
> >
> >  /**
> > + * mma9551_read_config_words() - read multiple config words
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @len:	Length of array to read in bytes
> > + * @val:	Array of words to read
> > + *
> > + * Read multiple configuration registers (word-sized registers).
> > + *
> > + * Locking note: This function must be called with the device lock hel=
d.
> > + * Locking is not handled inside the function. Callers should ensure t=
hey
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u8 len, u16 *buf)
> > +{
> > +	int ret, i;
> > +	int len_words =3D len / sizeof(u16);
> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> > +
> > +	ret =3D mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
> > +			       reg, NULL, 0, (u8 *) be_buf, len);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	for (i =3D 0; i < len_words; i++)
> > +		buf[i] =3D be16_to_cpu(be_buf[i]);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(mma9551_read_config_words);
> > +
> > +/**
> > + * mma9551_read_status_words() - read multiple status words
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @len:	Length of array to read in bytes
> > + * @val:	Array of words to read
> > + *
> > + * Read multiple status registers (word-sized registers).
> > + *
> > + * Locking note: This function must be called with the device lock hel=
d.
> > + * Locking is not handled inside the function. Callers should ensure t=
hey
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
> > +			      u16 reg, u8 len, u16 *buf)
> > +{
> > +	int ret, i;
> > +	int len_words =3D len / sizeof(u16);
> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> > +
> > +	ret =3D mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
> > +			       reg, NULL, 0, (u8 *) be_buf, len);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	for (i =3D 0; i < len_words; i++)
> > +		buf[i] =3D be16_to_cpu(be_buf[i]);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(mma9551_read_status_words);
> > +
> > +/**
> > + * mma9551_write_config_words() - write multiple config words
> > + * @client:	I2C client
> > + * @app_id:	Application ID
> > + * @reg:	Application register
> > + * @len:	Length of array to write in bytes
> > + * @val:	Array of words to write
> > + *
> > + * Write multiple configuration registers (word-sized registers).
> > + *
> > + * Locking note: This function must be called with the device lock hel=
d.
> > + * Locking is not handled inside the function. Callers should ensure t=
hey
> > + * serialize access to the HW.
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
> > +			       u16 reg, u8 len, u16 *buf)
> > +{
> > +	int i;
> > +	int len_words =3D len / sizeof(u16);
> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
> > +
> > +	for (i =3D 0; i < len_words; i++)
> > +		be_buf[i] =3D cpu_to_be16(buf[i]);
> > +
> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
> > +				reg, (u8 *) be_buf, len, NULL, 0);
> > +}
> > +EXPORT_SYMBOL(mma9551_write_config_words);
> > +
> > +/**
> >   * mma9551_update_config_bits() - update bits in register
> >   * @client:	I2C client
> >   * @app_id:	Application ID
> > @@ -609,6 +773,25 @@ int mma9551_read_accel_scale(int *val, int *val2)
> >  }
> >  EXPORT_SYMBOL(mma9551_read_accel_scale);
> >
> > +/**
> > + * mma9551_app_reset() - reset application
> > + * @client:	I2C client
> > + * @app_mask:	Application to reset
> > + *
> > + * Reset the given application (using the Reset/Suspend/Clear
> > + * Control Application)
> > + *
> > + * Returns: 0 on success, negative value on failure.
> > + */
> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
> > +{
> > +	return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
> > +					 MMA9551_RSC_RESET +
> > +					 MMA9551_RSC_OFFSET(app_mask),
> > +					 MMA9551_RSC_VAL(app_mask));
> > +}
> > +EXPORT_SYMBOL(mma9551_app_reset);
> > +
> >  MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
> >  MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
> >  MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma95=
51_core.h
> > index e6efd02..edaa56b 100644
> > --- a/drivers/iio/accel/mma9551_core.h
> > +++ b/drivers/iio/accel/mma9551_core.h
> > @@ -21,9 +21,13 @@
> >  #define MMA9551_APPID_AFE		0x06
> >  #define MMA9551_APPID_TILT		0x0B
> >  #define MMA9551_APPID_SLEEP_WAKE	0x12
> > -#define MMA9551_APPID_RESET		0x17
> > +#define MMA9551_APPID_PEDOMETER	        0x15
> > +#define MMA9551_APPID_RCS		0x17
> >  #define MMA9551_APPID_NONE		0xff
> >
> > +/* Reset/Suspend/Clear application app masks */
> > +#define MMA9551_RSC_PED			BIT(21)
> > +
> >  #define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
> >
> >  enum mma9551_gpio_pin {
> > @@ -48,8 +52,18 @@ int mma9551_write_config_byte(struct i2c_client *cli=
ent, u8 app_id,
> >  			      u16 reg, u8 val);
> >  int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
> >  			     u16 reg, u8 *val);
> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
> > +			    u16 reg, u16 *val);
> > +int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u16 val);
> >  int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
> >  			     u16 reg, u16 *val);
> > +int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
> > +			     u16 reg, u8 len, u16 *buf);
> > +int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
> > +			      u16 reg, u8 len, u16 *buf);
> > +int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
> > +			       u16 reg, u8 len, u16 *buf);
> >  int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
> >  			       u16 reg, u8 mask, u8 val);
> >  int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_p=
in pin,
> > @@ -62,5 +76,6 @@ int mma9551_read_accel_chan(struct i2c_client *client=
,
> >  			    const struct iio_chan_spec *chan,
> >  			    int *val, int *val2);
> >  int mma9551_read_accel_scale(int *val, int *val2);
> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
> >
> >  #endif /* _MMA9551_CORE_H_ */
> > diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
> > new file mode 100644
> > index 0000000..ef6a6e3
> > --- /dev/null
> > +++ b/drivers/iio/accel/mma9553.c
> > @@ -0,0 +1,1321 @@
> > +/*
> > + * Freescale MMA9553L Intelligent Pedometer driver
> > + * Copyright (c) 2014, Intel Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or modif=
y it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITH=
OUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY =
or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public Licen=
se for
> > + * more details.
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/i2c.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/slab.h>
> > +#include <linux/acpi.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/iio/events.h>
> > +#include <linux/pm_runtime.h>
> > +#include "mma9551_core.h"
> > +
> > +#define MMA9553_DRV_NAME			"mma9553"
> > +#define MMA9553_IRQ_NAME			"mma9553_event"
> > +#define MMA9553_GPIO_NAME			"mma9553_int"
> > +
> > +/* Pedometer configuration registers (R/W) */
> > +#define MMA9553_CONF_SLEEPMIN		0x00
> > +#define MMA9553_CONF_SLEEPMAX		0x02
> > +#define MMA9553_CONF_SLEEPTHD		0x04
> > +#define MMA9553_CONF_WORD		GENMASK(15, 0)
> > +
> > +#define MMA9553_CONF_CONF_STEPLEN	0x06
> > +#define MMA9553_CONF_CONFIG		BIT(15)
> > +#define MMA9553_CONF_ACT_DBCNTM		BIT(14)
> > +#define MMA9553_CONF_SLP_DBCNTM		BIT(13)
> > +#define MMA9553_CONF_STEPLEN		GENMASK(7, 0)
> > +
> > +#define MMA9553_CONF_HEIGHT_WEIGHT	0x08
> Some of this naming is less than clear.  Which ones
> are the register addresses and which the internal masks?
> (obvious really, but names should reflect this perhaps)
>=20
That's true. I'll rename them to make it more clear.

> > +#define MMA9553_CONF_HEIGHT		GENMASK(15, 8)
> > +#define MMA9553_CONF_WEIGHT		GENMASK(7, 0)
> > +
> > +#define MMA9553_CONF_FILTER		0x0A
> > +#define MMA9553_CONF_FILTSTEP		GENMASK(15, 8)
> > +#define MMA9553_CONF_MALE		BIT(7)
> > +#define MMA9553_CONF_FILTTIME		GENMASK(6, 0)
> > +
> > +#define MMA9553_CONF_SPEED_STEP		0x0C
> > +#define MMA9553_CONF_SPDPRD		GENMASK(15, 8)
> > +#define MMA9553_CONF_STEPCOALESCE	GENMASK(7, 0)
> > +
> > +#define MMA9553_CONF_ACTTHD		0x0E
> > +
> > +/* Pedometer status registers (R-only) */
> > +#define MMA9553_STATUS			0x00
> > +#define MMA9553_STATUS_MRGFL		BIT(15)
> > +#define MMA9553_STATUS_SUSPCHG		BIT(14)
> > +#define MMA9553_STATUS_STEPCHG		BIT(13)
> > +#define MMA9553_STATUS_ACTCHG		BIT(12)
> > +#define MMA9553_STATUS_SUSP		BIT(11)
> > +#define MMA9553_STATUS_ACTIVITY		(BIT(10) | BIT(9) | BIT(8))
> > +#define MMA9553_STATUS_VERSION		0x00FF
> > +
> > +#define MMA9553_STEPCNT			0x02
> > +#define MMA9553_DISTANCE		0x04
> > +#define MMA9553_SPEED			0x06
> > +#define MMA9553_CALORIES		0x08
> > +#define MMA9553_SLEEPCNT		0x0A
> > +
> > +/* Pedometer events are always mapped to this pin. */
> > +#define MMA9553_DEFAULT_GPIO_PIN	mma9551_gpio6
> > +#define MMA9553_DEFAULT_GPIO_POLARITY	0
> > +
> > +/* Bitnum used for gpio configuration =3D bit number in high status by=
te */
> > +#define STATUS_TO_BITNUM(bit)		(ffs(bit) - 9)
> > +
> > +#define MMA9553_DEFAULT_SAMPLE_RATE	30	/* Hz */
> > +
> > +/*
> > + * The internal activity level must be stable for ACTTHD samples befor=
e
> > + * ACTIVITY is updated.The ACTIVITY variable contains the current acti=
vity
> > + * level and is updated every time a step is detected or once a second
> > + * if there are no steps.
> > + */
> > +#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) / MMA9553_DEFAULT_SAMP=
LE_RATE)
> > +#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) * MMA9553_DEFAULT_SAMP=
LE_RATE)
> > +
> > +/*
> > + * Autonomously suspend pedometer if acceleration vector magnitude
> > + * is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
> > + */
> > +#define MMA9553_DEFAULT_SLEEPMIN	3688	/* 0,9 g */
> > +#define MMA9553_DEFAULT_SLEEPMAX	4508	/* 1,1 g */
> > +#define MMA9553_DEFAULT_SLEEPTHD	(MMA9553_DEFAULT_SAMPLE_RATE * 30)
> > +
> > +#define MMA9553_CONFIG_RETRIES		2
> > +
> > +/* Status register - activity field  */
> > +enum activity_level {
> > +	ACTIVITY_UNKNOWN,
> > +	ACTIVITY_REST,
> > +	ACTIVITY_WALKING,
> > +	ACTIVITY_JOGGING,
> > +	ACTIVITY_RUNNING,
> > +};
> > +
> > +static struct mma9553_event_info {
> > +	enum iio_chan_type type;
> > +	enum iio_modifier mod;
> > +	enum iio_event_direction dir;
> > +} mma9553_events_info[] =3D {
> > +	{
> > +		.type =3D IIO_STEPS,
> > +		.mod =3D IIO_NO_MOD,
> > +		.dir =3D IIO_EV_DIR_NONE,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_STILL,
> > +		.dir =3D IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_STILL,
> > +		.dir =3D IIO_EV_DIR_FALLING,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_WALKING,
> > +		.dir =3D IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_WALKING,
> > +		.dir =3D IIO_EV_DIR_FALLING,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_JOGGING,
> > +		.dir =3D IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_JOGGING,
> > +		.dir =3D IIO_EV_DIR_FALLING,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_RUNNING,
> > +		.dir =3D IIO_EV_DIR_RISING,
> > +	},
> > +	{
> > +		.type =3D IIO_ACTIVITY,
> > +		.mod =3D IIO_MOD_RUNNING,
> > +		.dir =3D IIO_EV_DIR_FALLING,
> > +	},
> > +};
> > +
> > +#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
> > +
> > +struct mma9553_event {
> > +	struct mma9553_event_info *info;
> > +	bool enabled;
> > +};
> > +
> > +struct mma9553_conf_regs {
> > +	u16 sleepmin;
> > +	u16 sleepmax;
> > +	u16 sleepthd;
> > +	u16 config;
> > +	u16 height_weight;
> > +	u16 filter;
> > +	u16 speed_step;
> > +	u16 actthd;
> > +} __packed;
> > +
> > +struct mma9553_data {
> > +	struct i2c_client *client;
> > +	struct mutex mutex;
> > +	struct mma9553_conf_regs conf;
> > +	struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
> > +	int num_events;
> > +	u8 gpio_bitnum;
> > +	/*
> > +	 * This is used for all features that depend on step count:
> > +	 * step count, distance, speed, calories.
> > +	 */
> > +	bool stepcnt_enabled;
> > +	u16 stepcnt;
> > +	u8 activity;
> > +	s64 timestamp;
> > +};
> > +
> > +static u8 mma9553_get_bits(u16 val, u16 mask)
> > +{
> > +	return (val & mask) >> (ffs(mask) - 1);
> > +}
> > +
> > +static u16 mma9553_set_bits(u16 current_val, u16 val, u16 mask)
> > +{
> > +	return (current_val & ~mask) | (val << (ffs(mask) - 1));
> > +}
> > +
> > +static enum iio_modifier mma9553_activity_to_mod(enum activity_level a=
ctivity)
> > +{
> > +	switch (activity) {
> > +	case ACTIVITY_RUNNING:
> > +		return IIO_MOD_RUNNING;
> > +	case ACTIVITY_JOGGING:
> > +		return IIO_MOD_JOGGING;
> > +	case ACTIVITY_WALKING:
> > +		return IIO_MOD_WALKING;
> > +	case ACTIVITY_REST:
> > +		return IIO_MOD_STILL;
> > +	case ACTIVITY_UNKNOWN:
> > +	default:
> > +		return IIO_NO_MOD;
> > +	}
> > +}
> > +
> > +static void mma9553_init_events(struct mma9553_data *data)
> > +{
> > +	int i;
> > +
> > +	data->num_events =3D MMA9553_EVENTS_INFO_SIZE;
> > +	for (i =3D 0; i < data->num_events; i++) {
> > +		data->events[i].info =3D &mma9553_events_info[i];
> > +		data->events[i].enabled =3D false;
> > +	}
> > +}
> > +
> > +static struct mma9553_event *mma9553_get_event(struct mma9553_data *da=
ta,
> > +					       enum iio_chan_type type,
> > +					       enum iio_modifier mod,
> > +					       enum iio_event_direction dir)
> > +{
> > +	int i;
> > +
> > +	for (i =3D 0; i < data->num_events; i++)
> > +		if (data->events[i].info->type =3D=3D type &&
> > +		    data->events[i].info->mod =3D=3D mod &&
> > +		    data->events[i].info->dir =3D=3D dir)
> > +			return &data->events[i];
> > +
> > +	return NULL;
> > +}
> > +
> > +static bool mma9553_is_any_event_enabled(struct mma9553_data *data,
> > +					 bool check_type,
> > +					 enum iio_chan_type type)
> > +{
> > +	int i;
> > +
> > +	for (i =3D 0; i < data->num_events; i++)
> > +		if ((check_type && data->events[i].info->type =3D=3D type &&
> > +		     data->events[i].enabled) ||
> > +		     (!check_type && data->events[i].enabled))
> > +			return true;
> > +
> > +	return false;
> > +}
> > +
> > +static int mma9553_set_config(struct mma9553_data *data, u16 reg,
> > +			      u16 *p_reg_val, u16 val, u16 mask)
> > +{
> > +	int ret, retries;
> > +	u16 reg_val, config;
> > +
> > +	reg_val =3D *p_reg_val;
> > +	if (val =3D=3D mma9553_get_bits(reg_val, mask))
> > +		return 0;
> > +
> > +	reg_val =3D mma9553_set_bits(reg_val, val, mask);
> > +	ret =3D mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMET=
ER,
> > +					reg, reg_val);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"error writing config register 0x%x\n", reg);
> > +		return ret;
> > +	}
> > +
> > +	*p_reg_val =3D reg_val;
> > +
> > +	/* Reinitializes the pedometer with current configuration values */
> > +	config =3D mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG=
);
> > +
> > +	ret =3D mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMET=
ER,
> > +					MMA9553_CONF_CONF_STEPLEN, config);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"error writing config register 0x%x\n",
> > +			MMA9553_CONF_CONF_STEPLEN);
> > +		return ret;
> > +	}
> > +
> > +	retries =3D MMA9553_CONFIG_RETRIES;
> > +	do {
> > +		mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
> > +		ret =3D mma9551_read_config_word(data->client,
> > +					       MMA9551_APPID_PEDOMETER,
> > +					       MMA9553_CONF_CONF_STEPLEN,
> > +					       &config);
> > +		if (ret < 0)
> > +			return ret;
> > +	} while (mma9553_get_bits(config, MMA9553_CONF_CONFIG) &&
> > +		 --retries > 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
> > +					 u8 *activity, u16 *stepcnt)
> > +{
> > +	u32 status_stepcnt;
> > +	u16 status;
> > +	int ret;
> > +
> > +	ret =3D mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMET=
ER,
> > +					MMA9553_STATUS, sizeof(u32),
> > +					(u16 *) &status_stepcnt);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"error reading status and stepcnt\n");
> > +		return ret;
> > +	}
> > +
> > +	status =3D status_stepcnt & MMA9553_CONF_WORD;
> > +	*activity =3D mma9553_get_bits(status, MMA9553_STATUS_ACTIVITY);
> > +	*stepcnt =3D status_stepcnt >> 16;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_conf_gpio(struct mma9553_data *data)
> > +{
> > +	u8 bitnum =3D 0, appid =3D MMA9551_APPID_PEDOMETER;
> > +	int ret;
> > +	struct mma9553_event *ev_step_detect;
> > +	bool activity_enabled;
> > +
> > +	activity_enabled =3D
> > +	    mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
> > +	ev_step_detect =3D
> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
> > +
> > +	/*
> > +	 * If both step detector and activity are enabled, use the MRGFL bit.
> > +	 * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG fla=
gs.
> > +	 */
> > +	if (activity_enabled && ev_step_detect->enabled)
> > +		bitnum =3D STATUS_TO_BITNUM(MMA9553_STATUS_MRGFL);
> > +	else if (ev_step_detect->enabled)
> > +		bitnum =3D STATUS_TO_BITNUM(MMA9553_STATUS_STEPCHG);
> > +	else if (activity_enabled)
> > +		bitnum =3D STATUS_TO_BITNUM(MMA9553_STATUS_ACTCHG);
> > +	else			/* Reset */
> > +		appid =3D MMA9551_APPID_NONE;
> > +
> > +	if (data->gpio_bitnum =3D=3D bitnum)
> > +		return 0;
> > +
> > +	/* Save initial values for activity and stepcnt */
> > +	if (activity_enabled || ev_step_detect->enabled)
> > +		mma9553_read_activity_stepcnt(data, &data->activity,
> > +					      &data->stepcnt);
> > +
> > +	ret =3D mma9551_gpio_config(data->client,
> > +				  MMA9553_DEFAULT_GPIO_PIN,
> > +				  appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
> > +	if (ret < 0)
> > +		return ret;
> > +	data->gpio_bitnum =3D bitnum;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_init(struct mma9553_data *data)
> > +{
> > +	int ret;
> > +
> > +	ret =3D mma9551_read_version(data->client);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 * Read all the pedometer configuration registers. This is used as
> > +	 * a device identification command to differentiate the MMA9553L
> > +	 * from the MMA9550L.
> > +	 */
> > +	ret =3D
> > +	    mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
> > +				      MMA9553_CONF_SLEEPMIN,
> > +				      sizeof(data->conf), (u16 *) &data->conf);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"device is not MMA9553L: failed to read cfg regs\n");
> > +		return ret;
> > +	}
> > +
> > +
> > +	/* Reset gpio */
> > +	data->gpio_bitnum =3D -1;
> > +	ret =3D mma9553_conf_gpio(data);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret =3D mma9551_app_reset(data->client, MMA9551_RSC_PED);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* Init config registers */
> > +	data->conf.sleepmin =3D MMA9553_DEFAULT_SLEEPMIN;
> > +	data->conf.sleepmax =3D MMA9553_DEFAULT_SLEEPMAX;
> > +	data->conf.sleepthd =3D MMA9553_DEFAULT_SLEEPTHD;
> > +	data->conf.config =3D
> > +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
> > +	/*
> > +	 * Clear the activity debounce counter when the activity level change=
s,
> > +	 * so that the confidence level applies for any activity level.
> > +	 */
> > +	data->conf.config =3D
> > +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_ACT_DBCNTM);
> > +	ret =3D
> > +	    mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
> > +				       MMA9553_CONF_SLEEPMIN,
> > +				       sizeof(data->conf), (u16 *) &data->conf);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev,
> > +			"failed to write configuration registers\n");
> > +		return ret;
> > +	}
> > +
> > +	return mma9551_set_device_state(data->client, true);
> > +}
> > +
> > +static int mma9553_read_raw(struct iio_dev *indio_dev,
> > +			    struct iio_chan_spec const *chan,
> > +			    int *val, int *val2, long mask)
> > +{
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +	u16 tmp;
> > +	u8 activity;
> > +	bool powered_on;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_PROCESSED:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			/*
> > +			 * The HW only counts steps and other dependent
> > +			 * parameters (speed, distance, calories, activity)
> > +			 * if power is on (from enabling an event or the
> > +			 * step counter */
> > +			powered_on =3D
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_STEPCNT, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val =3D tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_DISTANCE:
> > +			powered_on =3D
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_DISTANCE, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val =3D tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_ACTIVITY:
> > +			powered_on =3D
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_STATUS, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +
> > +			activity =3D
> > +			    mma9553_get_bits(tmp, MMA9553_STATUS_ACTIVITY);
> > +
> > +			/*
> > +			 * The device does not support confidence value levels,
> > +			 * so we will always have 100% for current activity and
> > +			 * 0% for the others.
> > +			 */
> > +			if (chan->channel2 =3D=3D mma9553_activity_to_mod(activity))
> > +				*val =3D 100;
> > +			else
> > +				*val =3D 0;
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_RAW:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:	/* m/h */
> > +			if (chan->channel2 !=3D IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			powered_on =3D
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_SPEED, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val =3D tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_ENERGY:	/* Cal or kcal */
> > +			powered_on =3D
> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
> > +			    data->stepcnt_enabled;
> > +			if (!powered_on) {
> > +				dev_err(&data->client->dev,
> > +					"No channels enabled\n");
> > +				return -EINVAL;
> > +			}
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9551_read_status_word(data->client,
> > +						       MMA9551_APPID_PEDOMETER,
> > +						       MMA9553_CALORIES, &tmp);
> > +			mutex_unlock(&data->mutex);
> > +			if (ret < 0)
> > +				return ret;
> > +			*val =3D tmp;
> > +			return IIO_VAL_INT;
> > +		case IIO_ACCEL:
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9551_read_accel_chan(data->client,
> > +						      chan, val, val2);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_SCALE:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:	/* m/h to m/s */
> > +			if (chan->channel2 !=3D IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			*val =3D 0;
> > +			*val2 =3D 277;	/* 0.000277 */
> > +			return IIO_VAL_INT_PLUS_MICRO;
> > +		case IIO_ENERGY:	/* Cal or kcal to J */
> > +			*val =3D 4184;
> > +			return IIO_VAL_INT;
> > +		case IIO_ACCEL:
> > +			return mma9551_read_accel_scale(val, val2);
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_ENABLE:
> > +		*val =3D data->stepcnt_enabled;
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
> > +		*val =3D mma9553_get_bits(data->conf.height_weight,
> > +					MMA9553_CONF_HEIGHT);
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
> > +		*val =3D mma9553_get_bits(data->conf.height_weight,
> > +					MMA9553_CONF_WEIGHT);
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			*val =3D mma9553_get_bits(data->conf.filter,
> > +						MMA9553_CONF_FILTSTEP);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			*val =3D mma9553_get_bits(data->conf.filter,
> > +						MMA9553_CONF_FILTTIME);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_INT_TIME:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:
> > +			if (chan->channel2 !=3D IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			*val =3D mma9553_get_bits(data->conf.speed_step,
> > +						MMA9553_CONF_SPDPRD);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_write_raw(struct iio_dev *indio_dev,
> > +			     struct iio_chan_spec const *chan,
> > +			     int val, int val2, long mask)
> > +{
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_ENABLE:
> > +		if (data->stepcnt_enabled =3D=3D !!val)
> > +			return 0;
> > +		mutex_lock(&data->mutex);
> > +		ret =3D mma9551_set_power_state(data->client, val);
> > +		if (ret < 0) {
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		}
> > +		data->stepcnt_enabled =3D val;
> > +		mutex_unlock(&data->mutex);
> > +		return 0;
> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
> > +		if (val < 0 || val > 255)
> > +			return -EINVAL;
> We are being rather inconsistent in having calibheight in cm rather than
> m (as per distance etc). I wonder if now is the time to change it whilst
> we have few users...

Sure, I'll change it.
> > +		mutex_lock(&data->mutex);
> > +		ret =3D mma9553_set_config(data,
> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
> > +					 &data->conf.height_weight,
> > +					 val, MMA9553_CONF_HEIGHT);
> > +		mutex_unlock(&data->mutex);
> > +		return ret;
> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
> > +		if (val < 0 || val > 255)
> > +			return -EINVAL;
> > +		mutex_lock(&data->mutex);
> > +		ret =3D mma9553_set_config(data,
> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
> > +					 &data->conf.height_weight,
> > +					 val, MMA9553_CONF_WEIGHT);
> > +		mutex_unlock(&data->mutex);
> > +		return ret;
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			/*
> > +			 * Set to 0 to disable step filtering. If the value
> > +			 * specified is greater than 6, then 6 will be used.
> > +			 */
> > +			if (val < 0)
> > +				return -EINVAL;
> > +			if (val > 6)
> > +				val =3D 6;
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9553_set_config(data, MMA9553_CONF_FILTER,
> > +						 &data->conf.filter, val,
> > +						 MMA9553_CONF_FILTSTEP);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			if (val < 0 || val > 127)
> > +				return -EINVAL;
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9553_set_config(data, MMA9553_CONF_FILTER,
> > +						 &data->conf.filter, val,
> > +						 MMA9553_CONF_FILTTIME);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_INT_TIME:
> > +		switch (chan->type) {
> > +		case IIO_VELOCITY:
> > +			if (chan->channel2 !=3D IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
> > +				return -EINVAL;
> > +			/*
> > +			 * If set to a value greater than 5, then 5 will be
> > +			 * used. Warning: Do not set SPDPRD to 0 or 1 as
> > +			 * this may cause undesirable behavior.
> > +			 */
> > +			if (val < 2)
> > +				return -EINVAL;
> > +			if (val > 5)
> > +				val =3D 5;
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
> > +						 &data->conf.speed_step, val,
> > +						 MMA9553_CONF_SPDPRD);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_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 mma9553_data *data =3D iio_priv(indio_dev);
> > +	struct mma9553_event *event;
> > +
> > +	event =3D mma9553_get_event(data, chan->type, chan->channel2, dir);
> > +	if (!event)
> > +		return -EINVAL;
> > +
> > +	return event->enabled;
> > +}
> > +
> > +static int mma9553_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 mma9553_data *data =3D iio_priv(indio_dev);
> > +	struct mma9553_event *event;
> > +	int ret;
> > +
> > +	event =3D mma9553_get_event(data, chan->type, chan->channel2, dir);
> > +	if (!event)
> > +		return -EINVAL;
> > +
> > +	if (event->enabled =3D=3D state)
> > +		return 0;
> > +
> > +	mutex_lock(&data->mutex);
> > +
> > +	ret =3D mma9551_set_power_state(data->client, state);
> > +	if (ret < 0)
> > +		goto err_out;
> > +	event->enabled =3D state;
> > +
> > +	ret =3D mma9553_conf_gpio(data);
> > +	if (ret < 0)
> > +		goto err_conf_gpio;
> > +
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +
> > +err_conf_gpio:
> > +	if (state) {
> > +		event->enabled =3D false;
> > +		mma9551_set_power_state(data->client, false);
> > +	}
> > +err_out:
> > +	mutex_unlock(&data->mutex);
> > +	return ret;
> > +}
> > +
> > +static int mma9553_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)
> > +{
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +
> > +	*val2 =3D 0;
> > +	switch (info) {
> > +	case IIO_EV_INFO_VALUE:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			*val =3D mma9553_get_bits(data->conf.speed_step,
> > +						MMA9553_CONF_STEPCOALESCE);
> > +			return IIO_VAL_INT;
> > +		case IIO_ACTIVITY:
> > +			/*
> > +			 * The device does not support confidence value levels.
> > +			 * We set an average of 50%.
> > +			 */
> > +			*val =3D 50;
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_EV_INFO_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_ACTIVITY:
> > +			*val =3D MMA9553_ACTIVITY_THD_TO_SEC(data->conf.actthd);
> > +			return IIO_VAL_INT;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_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 mma9553_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (info) {
> > +	case IIO_EV_INFO_VALUE:
> > +		switch (chan->type) {
> > +		case IIO_STEPS:
> > +			if (val < 0 || val > 255)
> > +				return -EINVAL;
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
> > +						 &data->conf.speed_step, val,
> > +						 MMA9553_CONF_STEPCOALESCE);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_EV_INFO_PERIOD:
> > +		switch (chan->type) {
> > +		case IIO_ACTIVITY:
> > +			mutex_lock(&data->mutex);
> > +			ret =3D mma9553_set_config(data, MMA9553_CONF_ACTTHD,
> > +						 &data->conf.actthd,
> > +						 MMA9553_ACTIVITY_SEC_TO_THD
> > +						 (val), MMA9553_CONF_WORD);
> > +			mutex_unlock(&data->mutex);
> > +			return ret;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
> > +					const struct iio_chan_spec *chan)
> > +{
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	u8 gender;
> > +
> > +	gender =3D mma9553_get_bits(data->conf.filter, MMA9553_CONF_MALE);
> > +	/*
> > +	 * HW expects 0 for female and 1 for male,
> > +	 * while iio index is 0 for male and 1 for female
> > +	 */
> > +	return !gender;
> > +}
> > +
> > +static int mma9553_set_calibgender_mode(struct iio_dev *indio_dev,
> > +					const struct iio_chan_spec *chan,
> > +					unsigned int mode)
> > +{
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	u8 gender =3D !mode;
> > +	int ret;
> > +
> > +	if ((mode !=3D 0) && (mode !=3D 1))
> > +		return -EINVAL;
> > +	mutex_lock(&data->mutex);
> > +	ret =3D mma9553_set_config(data, MMA9553_CONF_FILTER, &data->conf.fil=
ter,
> > +				 gender, MMA9553_CONF_MALE);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct iio_event_spec mma9553_step_event =3D {
> > +	.type =3D IIO_EV_TYPE_CHANGE,
> > +	.dir =3D IIO_EV_DIR_NONE,
> > +	.mask_separate =3D BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
> > +};
> > +
> > +static const struct iio_event_spec mma9553_activity_events[] =3D {
> > +	{
> > +		.type =3D IIO_EV_TYPE_THRESH,
> > +		.dir =3D IIO_EV_DIR_RISING,
> > +		.mask_separate =3D BIT(IIO_EV_INFO_ENABLE) |
> > +				 BIT(IIO_EV_INFO_VALUE) |
> > +				 BIT(IIO_EV_INFO_PERIOD),
> > +	 },
> > +	{
> > +		.type =3D IIO_EV_TYPE_THRESH,
> > +		.dir =3D IIO_EV_DIR_FALLING,
> > +		.mask_separate =3D BIT(IIO_EV_INFO_ENABLE) |
> > +				 BIT(IIO_EV_INFO_VALUE) |
> > +				 BIT(IIO_EV_INFO_PERIOD),
> > +	},
> > +};
> > +
> > +static const char * const calibgender_modes[] =3D { "male", "female" }=
;
> > +
> > +static const struct iio_enum mma9553_calibgender_enum =3D {
> > +	.items =3D calibgender_modes,
> > +	.num_items =3D ARRAY_SIZE(calibgender_modes),
> > +	.get =3D mma9553_get_calibgender_mode,
> > +	.set =3D mma9553_set_calibgender_mode,
> > +};
> > +
> > +static const struct iio_chan_spec_ext_info mma9553_ext_info[] =3D {
> > +	IIO_CALIBGENDER_EXT_INFO(IIO_SHARED_BY_TYPE, &mma9553_calibgender_enu=
m),
> > +	{},
> > +};
> > +
> > +#define MMA9553_PEDOMETER_CHANNEL(_type, _mask) {		\
> > +	.type =3D _type,						\
> > +	.info_mask_separate =3D BIT(IIO_CHAN_INFO_ENABLE)      |	\
> > +			      BIT(IIO_CHAN_INFO_CALIBHEIGHT) |	\
> > +			      _mask,				\
> > +	.ext_info =3D mma9553_ext_info,				\
> > +}
> > +
> > +#define MMA9553_ACTIVITY_CHANNEL(_chan2) {				\
> > +	.type =3D IIO_ACTIVITY,						\
> > +	.modified =3D 1,							\
> > +	.channel2 =3D _chan2,						\
> > +	.info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),		\
> > +	.info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_CALIBHEIGHT),	\
> > +	.event_spec =3D mma9553_activity_events,				\
> > +	.num_event_specs =3D ARRAY_SIZE(mma9553_activity_events),		\
> > +	.ext_info =3D mma9553_ext_info,					\
> > +}
> > +
> > +static const struct iio_chan_spec mma9553_channels[] =3D {
> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
> > +
> > +	{
> > +		.type =3D IIO_STEPS,
> > +		.info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED) |
> > +				     BIT(IIO_CHAN_INFO_ENABLE) |
> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH) |
> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD),
> > +		.event_spec =3D &mma9553_step_event,
> > +		.num_event_specs =3D 1,
> > +	},
> > +
> > +	MMA9553_PEDOMETER_CHANNEL(IIO_DISTANCE, BIT(IIO_CHAN_INFO_PROCESSED))=
,
> > +	{
> > +		.type =3D IIO_VELOCITY,
> > +		.modified =3D 1,
> > +		.channel2 =3D IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
> > +		.info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE) |
> > +				      BIT(IIO_CHAN_INFO_INT_TIME) |
> > +				      BIT(IIO_CHAN_INFO_ENABLE),
> > +		.info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_CALIBHEIGHT),
> > +		.ext_info =3D mma9553_ext_info,
> > +	},
> > +	MMA9553_PEDOMETER_CHANNEL(IIO_ENERGY, BIT(IIO_CHAN_INFO_RAW) |
> > +				  BIT(IIO_CHAN_INFO_SCALE) |
> > +				  BIT(IIO_CHAN_INFO_CALIBWEIGHT)),
> > +
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_JOGGING),
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_STILL),
> > +};
> > +
> > +static const struct iio_info mma9553_info =3D {
> > +	.driver_module =3D THIS_MODULE,
> > +	.read_raw =3D mma9553_read_raw,
> > +	.write_raw =3D mma9553_write_raw,
> > +	.read_event_config =3D mma9553_read_event_config,
> > +	.write_event_config =3D mma9553_write_event_config,
> > +	.read_event_value =3D mma9553_read_event_value,
> > +	.write_event_value =3D mma9553_write_event_value,
> > +};
> > +
> > +static irqreturn_t mma9553_irq_handler(int irq, void *private)
> > +{
> > +	struct iio_dev *indio_dev =3D private;
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +
> > +	data->timestamp =3D iio_get_time_ns();
> > +	/*
> > +	 * Since we only configure the interrupt pin when an
> > +	 * event is enabled, we are sure we have at least
> > +	 * one event enabled at this point.
> > +	 */
> > +	return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t mma9553_event_handler(int irq, void *private)
> > +{
> > +	struct iio_dev *indio_dev =3D private;
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	u16 stepcnt;
> > +	u8 activity;
> > +	struct mma9553_event *ev_activity, *ev_prev_activity, *ev_step_detect=
;
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret =3D mma9553_read_activity_stepcnt(data, &activity, &stepcnt);
> > +	if (ret < 0) {
> > +		mutex_unlock(&data->mutex);
> > +		return IRQ_HANDLED;
> > +	}
> > +
> > +	ev_prev_activity =3D
> > +	    mma9553_get_event(data, IIO_ACTIVITY,
> > +			      mma9553_activity_to_mod(data->activity),
> > +			      IIO_EV_DIR_FALLING);
> > +	ev_activity =3D
> > +	    mma9553_get_event(data, IIO_ACTIVITY,
> > +			      mma9553_activity_to_mod(activity),
> > +			      IIO_EV_DIR_RISING);
> > +	ev_step_detect =3D
> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
> > +
> > +	if (ev_step_detect->enabled && (stepcnt !=3D data->stepcnt)) {
> > +		data->stepcnt =3D stepcnt;
> > +		iio_push_event(indio_dev,
> > +			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
> > +			       IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
> > +			       data->timestamp);
> > +	}
> > +
> > +	if (activity !=3D data->activity) {
> > +		data->activity =3D activity;
> > +		/* ev_activity can be NULL if activity =3D=3D ACTIVITY_UNKNOWN */
> > +		if (ev_prev_activity && ev_prev_activity->enabled)
> > +			iio_push_event(indio_dev,
> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
> > +				       ev_prev_activity->info->mod,
> > +				       IIO_EV_DIR_FALLING,
> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
> > +				       data->timestamp);
> > +
> > +		if (ev_activity && ev_activity->enabled)
> > +			iio_push_event(indio_dev,
> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
> > +				       ev_activity->info->mod,
> > +				       IIO_EV_DIR_RISING,
> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
> > +				       data->timestamp);
> > +	}
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int mma9553_gpio_probe(struct i2c_client *client)
> > +{
> > +	struct device *dev;
> > +	struct gpio_desc *gpio;
> > +	int ret;
> > +
> > +	if (!client)
> > +		return -EINVAL;
> > +
> > +	dev =3D &client->dev;
> > +
> > +	/* data ready gpio interrupt pin */
> > +	gpio =3D devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
> > +	if (IS_ERR(gpio)) {
> > +		dev_err(dev, "acpi gpio get index failed\n");
> > +		return PTR_ERR(gpio);
> > +	}
> > +
> > +	ret =3D gpiod_direction_input(gpio);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret =3D gpiod_to_irq(gpio);
> > +
> > +	dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret=
);
> > +
> > +	return ret;
> > +}
> > +
> > +static const char *mma9553_match_acpi_device(struct device *dev)
> > +{
> > +	const struct acpi_device_id *id;
> > +
> > +	id =3D acpi_match_device(dev->driver->acpi_match_table, dev);
> > +	if (!id)
> > +		return NULL;
> > +
> > +	return dev_name(dev);
> > +}
> > +
> > +static int mma9553_probe(struct i2c_client *client,
> > +			 const struct i2c_device_id *id)
> > +{
> > +	struct mma9553_data *data;
> > +	struct iio_dev *indio_dev;
> > +	const char *name =3D NULL;
> > +	int ret;
> > +
> > +	indio_dev =3D devm_iio_device_alloc(&client->dev, sizeof(*data));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	data =3D iio_priv(indio_dev);
> > +	i2c_set_clientdata(client, indio_dev);
> > +	data->client =3D client;
> > +
> > +	if (id)
> > +		name =3D id->name;
> > +	else if (ACPI_HANDLE(&client->dev))
> > +		name =3D mma9553_match_acpi_device(&client->dev);
> > +	else
> > +		return -ENOSYS;
> > +
> > +	mutex_init(&data->mutex);
> > +	mma9553_init_events(data);
> > +
> > +	ret =3D mma9553_init(data);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	indio_dev->dev.parent =3D &client->dev;
> > +	indio_dev->channels =3D mma9553_channels;
> > +	indio_dev->num_channels =3D ARRAY_SIZE(mma9553_channels);
> > +	indio_dev->name =3D name;
> > +	indio_dev->modes =3D INDIO_DIRECT_MODE;
> > +	indio_dev->info =3D &mma9553_info;
> > +
> > +	if (client->irq < 0)
> > +		client->irq =3D mma9553_gpio_probe(client);
> > +
> > +	if (client->irq >=3D 0) {
> > +		ret =3D devm_request_threaded_irq(&client->dev, client->irq,
> > +						mma9553_irq_handler,
> > +						mma9553_event_handler,
> > +						IRQF_TRIGGER_RISING,
> > +						MMA9553_IRQ_NAME, indio_dev);
> > +		if (ret < 0) {
> > +			dev_err(&client->dev, "request irq %d failed\n",
> > +				client->irq);
> > +			goto out_poweroff;
> > +		}
> > +
> > +	}
> > +
> > +	ret =3D iio_device_register(indio_dev);
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "unable to register iio device\n");
> > +		goto out_poweroff;
> > +	}
> > +
> > +	ret =3D pm_runtime_set_active(&client->dev);
> > +	if (ret < 0)
> > +		goto out_iio_unregister;
> > +
> > +	pm_runtime_enable(&client->dev);
> > +	pm_runtime_set_autosuspend_delay(&client->dev,
> > +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
> > +	pm_runtime_use_autosuspend(&client->dev);
> > +
> > +	dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
> > +
> > +	return 0;
> > +
> > +out_iio_unregister:
> > +	iio_device_unregister(indio_dev);
> > +out_poweroff:
> > +	mma9551_set_device_state(client, false);
> > +	return ret;
> > +}
> > +
> > +static int mma9553_remove(struct i2c_client *client)
> > +{
> > +	struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +
> > +	pm_runtime_disable(&client->dev);
> > +	pm_runtime_set_suspended(&client->dev);
> > +	pm_runtime_put_noidle(&client->dev);
> > +
> > +	iio_device_unregister(indio_dev);
> > +	mutex_lock(&data->mutex);
> > +	mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int mma9553_runtime_suspend(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret =3D mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev, "powering off device failed\n");
> > +		return -EAGAIN;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9553_runtime_resume(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	ret =3D mma9551_set_device_state(data->client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
> > +
> > +	return 0;
> > +}
> > +#endif
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mma9553_suspend(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret =3D mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +static int mma9553_resume(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9553_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret =3D mma9551_set_device_state(data->client, true);
> > +	mutex_unlock(&data->mutex);
> > +
> > +	return ret;
> > +}
> > +#endif
> > +
> > +static const struct dev_pm_ops mma9553_pm_ops =3D {
> > +	SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
> > +	SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
> > +			   mma9553_runtime_resume, NULL)
> > +};
> > +
> > +static const struct acpi_device_id mma9553_acpi_match[] =3D {
> > +	{"MMA9553", 0},
> > +	{},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
> > +
> > +static const struct i2c_device_id mma9553_id[] =3D {
> > +	{"mma9553", 0},
> > +	{},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(i2c, mma9553_id);
> > +
> > +static struct i2c_driver mma9553_driver =3D {
> > +	.driver =3D {
> > +		   .name =3D MMA9553_DRV_NAME,
> > +		   .acpi_match_table =3D ACPI_PTR(mma9553_acpi_match),
> > +		   .pm =3D &mma9553_pm_ops,
> > +		   },
> > +	.probe =3D mma9553_probe,
> > +	.remove =3D mma9553_remove,
> > +	.id_table =3D mma9553_id,
> > +};
> > +
> > +module_i2c_driver(mma9553_driver);
> > +
> > +MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > index b6b12ac..6770a2f 100644
> > --- a/include/linux/iio/iio.h
> > +++ b/include/linux/iio/iio.h
> > @@ -634,4 +634,15 @@ int iio_str_to_fixpoint(const char *str, int fract=
_mult, int *integer,
> >   */
> >  #define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
> >
> > +/**
> > + * IIO_CALIBGENDER_EXT_INFO() - define the gender of the user
> > + *
> > + * @_shared:	Whether the attribute is shared between all channels
> > + * @_e:		Pointer to an iio_enum struct
> > + *
> > + */
> > +#define IIO_CALIBGENDER_EXT_INFO(_shared, _e)			       \
> > +	IIO_ENUM("calibgender", _shared, _e),			       \
> > +	IIO_ENUM_AVAILABLE("calibgender", _e)
> > +
> For now this is a little obscure to drop into the main header.
> If we get lots of instances, then perhaps it'll make sense to
> move it here.  Right now, please keep it in the driver.


Ok, will move this to the driver.
> >  #endif /* _INDUSTRIAL_IO_H_ */
> >

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

* RE: [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support
  2015-01-26 19:08   ` Jonathan Cameron
@ 2015-01-27 17:18       ` Tirdea, Irina
  0 siblings, 0 replies; 33+ messages in thread
From: Tirdea, Irina @ 2015-01-27 17:18 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Dogaru, Vlad, Baluta, Daniel, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald



> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@kernel.org]
> Sent: 26 January, 2015 21:08
> To: Tirdea, Irina; linux-iio@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Dogaru, Vlad; Baluta, Daniel; Hartmut Knaack; Lars-Peter Clausen; Peter Meerwald
> Subject: Re: [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support
> 
> On 11/01/15 19:10, Irina Tirdea wrote:
> > Add support for runtime pm to reduce the power consumed by the device
> > when not used.
> >
> > If CONFIG_PM is not enabled, the device will be powered on at
> > init and only powered off on system suspend.
> >
> > If CONFIG_PM is enabled, runtime pm autosuspend is used:
> > - for raw reads will keep the device on for a specified time
> > - for events it will keep the device on as long as we have at least
> > one event active
> >
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > Reviewed-by: Vlad Dogaru <vlad.dogaru@intel.com>
> Looks good.
> 
> Applied to the togreg branch of iio.git
> (at least we are driving down the size of the patch set for the next
> revision!)
Yes, that helps. Thanks Jonathan!

Irina
> 
> Jonathan
> > ---
> >  drivers/iio/accel/mma9551.c | 162 +++++++++++++++++++++++++++++++++++++-------
> >  1 file changed, 139 insertions(+), 23 deletions(-)
> >
> > diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
> > index 6563e26..f1a5a06 100644
> > --- a/drivers/iio/accel/mma9551.c
> > +++ b/drivers/iio/accel/mma9551.c
> > @@ -22,6 +22,7 @@
> >  #include <linux/iio/iio.h>
> >  #include <linux/iio/sysfs.h>
> >  #include <linux/iio/events.h>
> > +#include <linux/pm_runtime.h>
> >
> >  #define MMA9551_DRV_NAME		"mma9551"
> >  #define MMA9551_IRQ_NAME		"mma9551_event"
> > @@ -71,6 +72,7 @@ enum mma9551_gpio_pin {
> >  /* Sleep/Wake application */
> >  #define MMA9551_SLEEP_CFG		0x06
> >  #define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
> > +#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
> >  #define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
> >
> >  /* AFE application */
> > @@ -114,6 +116,9 @@ enum mma9551_tilt_axis {
> >  #define MMA9551_I2C_READ_RETRIES	5
> >  #define MMA9551_I2C_READ_DELAY	50	/* us */
> >
> > +#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
> > +#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
> > +
> >  struct mma9551_mbox_request {
> >  	u8 start_mbox;		/* Always 0. */
> >  	u8 app_id;
> > @@ -387,16 +392,55 @@ static int mma9551_read_version(struct i2c_client *client)
> >  }
> >
> >  /*
> > + * Power on chip and enable doze mode.
> >   * Use 'false' as the second parameter to cause the device to enter
> >   * sleep.
> >   */
> > -static int mma9551_set_device_state(struct i2c_client *client,
> > -				    bool enable)
> > +static int mma9551_set_device_state(struct i2c_client *client, bool enable)
> >  {
> >  	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
> >  					  MMA9551_SLEEP_CFG,
> > -					  MMA9551_SLEEP_CFG_SNCEN,
> > -					  enable ? 0 : MMA9551_SLEEP_CFG_SNCEN);
> > +					  MMA9551_SLEEP_CFG_SNCEN |
> > +					  MMA9551_SLEEP_CFG_FLEEN |
> > +					  MMA9551_SLEEP_CFG_SCHEN,
> > +					  enable ? MMA9551_SLEEP_CFG_SCHEN |
> > +					  MMA9551_SLEEP_CFG_FLEEN :
> > +					  MMA9551_SLEEP_CFG_SNCEN);
> > +}
> > +
> > +static int mma9551_set_power_state(struct i2c_client *client, bool on)
> > +{
> > +#ifdef CONFIG_PM
> > +	int ret;
> > +
> > +	if (on)
> > +		ret = pm_runtime_get_sync(&client->dev);
> > +	else {
> > +		pm_runtime_mark_last_busy(&client->dev);
> > +		ret = pm_runtime_put_autosuspend(&client->dev);
> > +	}
> > +
> > +	if (ret < 0) {
> > +		dev_err(&client->dev,
> > +			"failed to change power state to %d\n", on);
> > +		if (on)
> > +			pm_runtime_put_noidle(&client->dev);
> > +
> > +		return ret;
> > +	}
> > +#endif
> > +
> > +	return 0;
> > +}
> > +
> > +static void mma9551_sleep(int freq)
> > +{
> > +	int sleep_val = 1000 / freq;
> > +
> > +	if (sleep_val < 20)
> > +		usleep_range(sleep_val * 1000, 20000);
> > +	else
> > +		msleep_interruptible(sleep_val);
> >  }
> >
> >  static int mma9551_read_incli_chan(struct i2c_client *client,
> > @@ -424,15 +468,19 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
> >  		return -EINVAL;
> >  	}
> >
> > +	ret = mma9551_set_power_state(client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> >  	ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
> >  				       reg_addr, &angle);
> >  	if (ret < 0)
> > -		return ret;
> > +		goto out_poweroff;
> >
> >  	ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
> >  				       MMA9551_TILT_QUAD_REG, &quadrant);
> >  	if (ret < 0)
> > -		return ret;
> > +		goto out_poweroff;
> >
> >  	angle &= ~MMA9551_TILT_ANGFLG;
> >  	quadrant = (quadrant >> quad_shift) & 0x03;
> > @@ -442,7 +490,11 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
> >  	else
> >  		*val = angle + 90 * quadrant;
> >
> > -	return IIO_VAL_INT;
> > +	ret = IIO_VAL_INT;
> > +
> > +out_poweroff:
> > +	mma9551_set_power_state(client, false);
> > +	return ret;
> >  }
> >
> >  static int mma9551_read_accel_chan(struct i2c_client *client,
> > @@ -467,14 +519,22 @@ static int mma9551_read_accel_chan(struct i2c_client *client,
> >  		return -EINVAL;
> >  	}
> >
> > +	ret = mma9551_set_power_state(client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> >  	ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
> >  				       reg_addr, &raw_accel);
> >  	if (ret < 0)
> > -		return ret;
> > +		goto out_poweroff;
> >
> >  	*val = raw_accel;
> >
> > -	return IIO_VAL_INT;
> > +	ret = IIO_VAL_INT;
> > +
> > +out_poweroff:
> > +	mma9551_set_power_state(client, false);
> > +	return ret;
> >  }
> >
> >  static int mma9551_read_raw(struct iio_dev *indio_dev,
> > @@ -556,6 +616,10 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
> >  					  MMA9551_APPID_NONE, 0, 0);
> >  		if (ret < 0)
> >  			return ret;
> > +
> > +		ret = mma9551_set_power_state(data->client, false);
> > +		if (ret < 0)
> > +			return ret;
> >  	} else {
> >  		int bitnum;
> >
> > @@ -574,11 +638,18 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
> >  			return -EINVAL;
> >  		}
> >
> > +
> Trivial but one blank line here is enough...
> > +		ret = mma9551_set_power_state(data->client, true);
> > +		if (ret < 0)
> > +			return ret;
> > +
> >  		ret = mma9551_gpio_config(data->client,
> >  					  (enum mma9551_gpio_pin)mma_axis,
> >  					  MMA9551_APPID_TILT, bitnum, 0);
> > -		if (ret < 0)
> > +		if (ret < 0) {
> > +			mma9551_set_power_state(data->client, false);
> >  			return ret;
> > +		}
> >  	}
> >
> >  	data->event_enabled[mma_axis] = state;
> > @@ -771,12 +842,7 @@ static int mma9551_init(struct mma9551_data *data)
> >  	if (ret)
> >  		return ret;
> >
> > -	/* Power on chip and enable doze mode. */
> > -	return mma9551_update_config_bits(data->client,
> > -			 MMA9551_APPID_SLEEP_WAKE,
> > -			 MMA9551_SLEEP_CFG,
> > -			 MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN,
> > -			 MMA9551_SLEEP_CFG_SCHEN);
> > +	return mma9551_set_device_state(data->client, true);
> >  }
> >
> >  static int mma9551_gpio_probe(struct iio_dev *indio_dev)
> > @@ -869,8 +935,19 @@ static int mma9551_probe(struct i2c_client *client,
> >  		goto out_poweroff;
> >  	}
> >
> > +	ret = pm_runtime_set_active(&client->dev);
> > +	if (ret < 0)
> > +		goto out_iio_unregister;
> > +
> > +	pm_runtime_enable(&client->dev);
> > +	pm_runtime_set_autosuspend_delay(&client->dev,
> > +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
> > +	pm_runtime_use_autosuspend(&client->dev);
> > +
> >  	return 0;
> >
> > +out_iio_unregister:
> > +	iio_device_unregister(indio_dev);
> >  out_poweroff:
> >  	mma9551_set_device_state(client, false);
> >
> > @@ -882,6 +959,10 @@ static int mma9551_remove(struct i2c_client *client)
> >  	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> >  	struct mma9551_data *data = iio_priv(indio_dev);
> >
> > +	pm_runtime_disable(&client->dev);
> > +	pm_runtime_set_suspended(&client->dev);
> > +	pm_runtime_put_noidle(&client->dev);
> > +
> >  	iio_device_unregister(indio_dev);
> >  	mutex_lock(&data->mutex);
> >  	mma9551_set_device_state(data->client, false);
> > @@ -890,37 +971,72 @@ static int mma9551_remove(struct i2c_client *client)
> >  	return 0;
> >  }
> >
> > +#ifdef CONFIG_PM
> > +static int mma9551_runtime_suspend(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9551_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret = mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev, "powering off device failed\n");
> > +		return -EAGAIN;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9551_runtime_resume(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9551_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	ret = mma9551_set_device_state(data->client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
> > +
> > +	return 0;
> > +}
> > +#endif
> > +
> >  #ifdef CONFIG_PM_SLEEP
> >  static int mma9551_suspend(struct device *dev)
> >  {
> >  	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> >  	struct mma9551_data *data = iio_priv(indio_dev);
> > +	int ret;
> >
> >  	mutex_lock(&data->mutex);
> > -	mma9551_set_device_state(data->client, false);
> > +	ret = mma9551_set_device_state(data->client, false);
> >  	mutex_unlock(&data->mutex);
> >
> > -	return 0;
> > +	return ret;
> >  }
> >
> >  static int mma9551_resume(struct device *dev)
> >  {
> >  	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> >  	struct mma9551_data *data = iio_priv(indio_dev);
> > +	int ret;
> >
> >  	mutex_lock(&data->mutex);
> > -	mma9551_set_device_state(data->client, true);
> > +	ret = mma9551_set_device_state(data->client, true);
> >  	mutex_unlock(&data->mutex);
> >
> > -	return 0;
> > +	return ret;
> >  }
> > -#else
> > -#define mma9551_suspend NULL
> > -#define mma9551_resume NULL
> >  #endif
> >
> >  static const struct dev_pm_ops mma9551_pm_ops = {
> >  	SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
> > +	SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
> > +			   mma9551_runtime_resume, NULL)
> >  };
> >
> >  static const struct acpi_device_id mma9551_acpi_match[] = {
> >


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

* RE: [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support
@ 2015-01-27 17:18       ` Tirdea, Irina
  0 siblings, 0 replies; 33+ messages in thread
From: Tirdea, Irina @ 2015-01-27 17:18 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio
  Cc: linux-kernel, Dogaru, Vlad, Baluta, Daniel, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald



> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@kernel.org]
> Sent: 26 January, 2015 21:08
> To: Tirdea, Irina; linux-iio@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Dogaru, Vlad; Baluta, Daniel; Hartmut K=
naack; Lars-Peter Clausen; Peter Meerwald
> Subject: Re: [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support
>=20
> On 11/01/15 19:10, Irina Tirdea wrote:
> > Add support for runtime pm to reduce the power consumed by the device
> > when not used.
> >
> > If CONFIG_PM is not enabled, the device will be powered on at
> > init and only powered off on system suspend.
> >
> > If CONFIG_PM is enabled, runtime pm autosuspend is used:
> > - for raw reads will keep the device on for a specified time
> > - for events it will keep the device on as long as we have at least
> > one event active
> >
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > Reviewed-by: Vlad Dogaru <vlad.dogaru@intel.com>
> Looks good.
>=20
> Applied to the togreg branch of iio.git
> (at least we are driving down the size of the patch set for the next
> revision!)
Yes, that helps. Thanks Jonathan!

Irina
>=20
> Jonathan
> > ---
> >  drivers/iio/accel/mma9551.c | 162 ++++++++++++++++++++++++++++++++++++=
+-------
> >  1 file changed, 139 insertions(+), 23 deletions(-)
> >
> > diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
> > index 6563e26..f1a5a06 100644
> > --- a/drivers/iio/accel/mma9551.c
> > +++ b/drivers/iio/accel/mma9551.c
> > @@ -22,6 +22,7 @@
> >  #include <linux/iio/iio.h>
> >  #include <linux/iio/sysfs.h>
> >  #include <linux/iio/events.h>
> > +#include <linux/pm_runtime.h>
> >
> >  #define MMA9551_DRV_NAME		"mma9551"
> >  #define MMA9551_IRQ_NAME		"mma9551_event"
> > @@ -71,6 +72,7 @@ enum mma9551_gpio_pin {
> >  /* Sleep/Wake application */
> >  #define MMA9551_SLEEP_CFG		0x06
> >  #define MMA9551_SLEEP_CFG_SNCEN		BIT(0)
> > +#define MMA9551_SLEEP_CFG_FLEEN		BIT(1)
> >  #define MMA9551_SLEEP_CFG_SCHEN		BIT(2)
> >
> >  /* AFE application */
> > @@ -114,6 +116,9 @@ enum mma9551_tilt_axis {
> >  #define MMA9551_I2C_READ_RETRIES	5
> >  #define MMA9551_I2C_READ_DELAY	50	/* us */
> >
> > +#define MMA9551_DEFAULT_SAMPLE_RATE	122	/* Hz */
> > +#define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
> > +
> >  struct mma9551_mbox_request {
> >  	u8 start_mbox;		/* Always 0. */
> >  	u8 app_id;
> > @@ -387,16 +392,55 @@ static int mma9551_read_version(struct i2c_client=
 *client)
> >  }
> >
> >  /*
> > + * Power on chip and enable doze mode.
> >   * Use 'false' as the second parameter to cause the device to enter
> >   * sleep.
> >   */
> > -static int mma9551_set_device_state(struct i2c_client *client,
> > -				    bool enable)
> > +static int mma9551_set_device_state(struct i2c_client *client, bool en=
able)
> >  {
> >  	return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
> >  					  MMA9551_SLEEP_CFG,
> > -					  MMA9551_SLEEP_CFG_SNCEN,
> > -					  enable ? 0 : MMA9551_SLEEP_CFG_SNCEN);
> > +					  MMA9551_SLEEP_CFG_SNCEN |
> > +					  MMA9551_SLEEP_CFG_FLEEN |
> > +					  MMA9551_SLEEP_CFG_SCHEN,
> > +					  enable ? MMA9551_SLEEP_CFG_SCHEN |
> > +					  MMA9551_SLEEP_CFG_FLEEN :
> > +					  MMA9551_SLEEP_CFG_SNCEN);
> > +}
> > +
> > +static int mma9551_set_power_state(struct i2c_client *client, bool on)
> > +{
> > +#ifdef CONFIG_PM
> > +	int ret;
> > +
> > +	if (on)
> > +		ret =3D pm_runtime_get_sync(&client->dev);
> > +	else {
> > +		pm_runtime_mark_last_busy(&client->dev);
> > +		ret =3D pm_runtime_put_autosuspend(&client->dev);
> > +	}
> > +
> > +	if (ret < 0) {
> > +		dev_err(&client->dev,
> > +			"failed to change power state to %d\n", on);
> > +		if (on)
> > +			pm_runtime_put_noidle(&client->dev);
> > +
> > +		return ret;
> > +	}
> > +#endif
> > +
> > +	return 0;
> > +}
> > +
> > +static void mma9551_sleep(int freq)
> > +{
> > +	int sleep_val =3D 1000 / freq;
> > +
> > +	if (sleep_val < 20)
> > +		usleep_range(sleep_val * 1000, 20000);
> > +	else
> > +		msleep_interruptible(sleep_val);
> >  }
> >
> >  static int mma9551_read_incli_chan(struct i2c_client *client,
> > @@ -424,15 +468,19 @@ static int mma9551_read_incli_chan(struct i2c_cli=
ent *client,
> >  		return -EINVAL;
> >  	}
> >
> > +	ret =3D mma9551_set_power_state(client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> >  	ret =3D mma9551_read_status_byte(client, MMA9551_APPID_TILT,
> >  				       reg_addr, &angle);
> >  	if (ret < 0)
> > -		return ret;
> > +		goto out_poweroff;
> >
> >  	ret =3D mma9551_read_status_byte(client, MMA9551_APPID_TILT,
> >  				       MMA9551_TILT_QUAD_REG, &quadrant);
> >  	if (ret < 0)
> > -		return ret;
> > +		goto out_poweroff;
> >
> >  	angle &=3D ~MMA9551_TILT_ANGFLG;
> >  	quadrant =3D (quadrant >> quad_shift) & 0x03;
> > @@ -442,7 +490,11 @@ static int mma9551_read_incli_chan(struct i2c_clie=
nt *client,
> >  	else
> >  		*val =3D angle + 90 * quadrant;
> >
> > -	return IIO_VAL_INT;
> > +	ret =3D IIO_VAL_INT;
> > +
> > +out_poweroff:
> > +	mma9551_set_power_state(client, false);
> > +	return ret;
> >  }
> >
> >  static int mma9551_read_accel_chan(struct i2c_client *client,
> > @@ -467,14 +519,22 @@ static int mma9551_read_accel_chan(struct i2c_cli=
ent *client,
> >  		return -EINVAL;
> >  	}
> >
> > +	ret =3D mma9551_set_power_state(client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> >  	ret =3D mma9551_read_status_word(client, MMA9551_APPID_AFE,
> >  				       reg_addr, &raw_accel);
> >  	if (ret < 0)
> > -		return ret;
> > +		goto out_poweroff;
> >
> >  	*val =3D raw_accel;
> >
> > -	return IIO_VAL_INT;
> > +	ret =3D IIO_VAL_INT;
> > +
> > +out_poweroff:
> > +	mma9551_set_power_state(client, false);
> > +	return ret;
> >  }
> >
> >  static int mma9551_read_raw(struct iio_dev *indio_dev,
> > @@ -556,6 +616,10 @@ static int mma9551_config_incli_event(struct iio_d=
ev *indio_dev,
> >  					  MMA9551_APPID_NONE, 0, 0);
> >  		if (ret < 0)
> >  			return ret;
> > +
> > +		ret =3D mma9551_set_power_state(data->client, false);
> > +		if (ret < 0)
> > +			return ret;
> >  	} else {
> >  		int bitnum;
> >
> > @@ -574,11 +638,18 @@ static int mma9551_config_incli_event(struct iio_=
dev *indio_dev,
> >  			return -EINVAL;
> >  		}
> >
> > +
> Trivial but one blank line here is enough...
> > +		ret =3D mma9551_set_power_state(data->client, true);
> > +		if (ret < 0)
> > +			return ret;
> > +
> >  		ret =3D mma9551_gpio_config(data->client,
> >  					  (enum mma9551_gpio_pin)mma_axis,
> >  					  MMA9551_APPID_TILT, bitnum, 0);
> > -		if (ret < 0)
> > +		if (ret < 0) {
> > +			mma9551_set_power_state(data->client, false);
> >  			return ret;
> > +		}
> >  	}
> >
> >  	data->event_enabled[mma_axis] =3D state;
> > @@ -771,12 +842,7 @@ static int mma9551_init(struct mma9551_data *data)
> >  	if (ret)
> >  		return ret;
> >
> > -	/* Power on chip and enable doze mode. */
> > -	return mma9551_update_config_bits(data->client,
> > -			 MMA9551_APPID_SLEEP_WAKE,
> > -			 MMA9551_SLEEP_CFG,
> > -			 MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN,
> > -			 MMA9551_SLEEP_CFG_SCHEN);
> > +	return mma9551_set_device_state(data->client, true);
> >  }
> >
> >  static int mma9551_gpio_probe(struct iio_dev *indio_dev)
> > @@ -869,8 +935,19 @@ static int mma9551_probe(struct i2c_client *client=
,
> >  		goto out_poweroff;
> >  	}
> >
> > +	ret =3D pm_runtime_set_active(&client->dev);
> > +	if (ret < 0)
> > +		goto out_iio_unregister;
> > +
> > +	pm_runtime_enable(&client->dev);
> > +	pm_runtime_set_autosuspend_delay(&client->dev,
> > +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
> > +	pm_runtime_use_autosuspend(&client->dev);
> > +
> >  	return 0;
> >
> > +out_iio_unregister:
> > +	iio_device_unregister(indio_dev);
> >  out_poweroff:
> >  	mma9551_set_device_state(client, false);
> >
> > @@ -882,6 +959,10 @@ static int mma9551_remove(struct i2c_client *clien=
t)
> >  	struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
> >  	struct mma9551_data *data =3D iio_priv(indio_dev);
> >
> > +	pm_runtime_disable(&client->dev);
> > +	pm_runtime_set_suspended(&client->dev);
> > +	pm_runtime_put_noidle(&client->dev);
> > +
> >  	iio_device_unregister(indio_dev);
> >  	mutex_lock(&data->mutex);
> >  	mma9551_set_device_state(data->client, false);
> > @@ -890,37 +971,72 @@ static int mma9551_remove(struct i2c_client *clie=
nt)
> >  	return 0;
> >  }
> >
> > +#ifdef CONFIG_PM
> > +static int mma9551_runtime_suspend(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9551_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	mutex_lock(&data->mutex);
> > +	ret =3D mma9551_set_device_state(data->client, false);
> > +	mutex_unlock(&data->mutex);
> > +	if (ret < 0) {
> > +		dev_err(&data->client->dev, "powering off device failed\n");
> > +		return -EAGAIN;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int mma9551_runtime_resume(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> > +	struct mma9551_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	ret =3D mma9551_set_device_state(data->client, true);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
> > +
> > +	return 0;
> > +}
> > +#endif
> > +
> >  #ifdef CONFIG_PM_SLEEP
> >  static int mma9551_suspend(struct device *dev)
> >  {
> >  	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> >  	struct mma9551_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> >
> >  	mutex_lock(&data->mutex);
> > -	mma9551_set_device_state(data->client, false);
> > +	ret =3D mma9551_set_device_state(data->client, false);
> >  	mutex_unlock(&data->mutex);
> >
> > -	return 0;
> > +	return ret;
> >  }
> >
> >  static int mma9551_resume(struct device *dev)
> >  {
> >  	struct iio_dev *indio_dev =3D i2c_get_clientdata(to_i2c_client(dev));
> >  	struct mma9551_data *data =3D iio_priv(indio_dev);
> > +	int ret;
> >
> >  	mutex_lock(&data->mutex);
> > -	mma9551_set_device_state(data->client, true);
> > +	ret =3D mma9551_set_device_state(data->client, true);
> >  	mutex_unlock(&data->mutex);
> >
> > -	return 0;
> > +	return ret;
> >  }
> > -#else
> > -#define mma9551_suspend NULL
> > -#define mma9551_resume NULL
> >  #endif
> >
> >  static const struct dev_pm_ops mma9551_pm_ops =3D {
> >  	SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
> > +	SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
> > +			   mma9551_runtime_resume, NULL)
> >  };
> >
> >  static const struct acpi_device_id mma9551_acpi_match[] =3D {
> >

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

* RE: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
  2015-01-27 17:09       ` Tirdea, Irina
@ 2015-01-27 17:31         ` Jonathan Cameron
  -1 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-27 17:31 UTC (permalink / raw)
  To: Tirdea, Irina, Jonathan Cameron, linux-iio
  Cc: linux-kernel, Dogaru, Vlad, Baluta, Daniel, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald




On 27 January 2015 17:09:20 GMT+00:00, "Tirdea, Irina" <irina.tirdea@intel.com> wrote:
>
>
>> -----Original Message-----
>> From: Jonathan Cameron [mailto:jic23@kernel.org]
>> Sent: 26 January, 2015 22:44
>> To: Tirdea, Irina; linux-iio@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org; Dogaru, Vlad; Baluta, Daniel;
>Hartmut Knaack; Lars-Peter Clausen; Peter Meerwald
>> Subject: Re: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
>> 
>> On 11/01/15 19:10, Irina Tirdea wrote:
>> > Add support for Freescale MMA9553L Intelligent Pedometer Platform.
>> >
>> > The following functionalities are supported:
>> >  - step counter (counts the number of steps using a HW register)
>> >  - step detector (generates an iio event at every step the user
>takes)
>> >  - activity recognition (rest, walking, jogging, running)
>> >  - speed
>> >  - calories
>> >  - distance
>> >
>> > To get accurate pedometer results, the user's height, weight and
>gender
>> > need to be configured.
>> >
>> > The specifications can be downloaded from:
>> >
>http://www.freescale.com/files/sensors/doc/ref_manual/MMA955xLSWRM.pdf
>> >
>http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf
>> >
>> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
>> One thing noticed in passing.  Calibheight is in cm whereas distance
>is
>> in m. This seems inconsistent.  Would you mind changing to meters for
>> calibheight?
>> (lets do it whilst we don't really have any users yet).
>Sure, using meters is better. 
>I'll make the change to the iio Documentation and the driver.
>> 
>> 
>> A very nice driver, just a few minor queries in line.  Also of course
>> the interface for the debounce of steps as discussed in the thread
>> hanging off earlier in this series.
>> 
>> I'm not keen on the addition to iio.h (yet) as it is only used by
>this
>> one driver and as a general rule macros that simple are best avoided
>> (tend to limit things rather than help).
>Ok, I'll move the code to the driver.
>> 
>> > ---
>> >  Documentation/ABI/testing/sysfs-bus-iio |   49 +-
>> >  drivers/iio/accel/Kconfig               |   10 +
>> >  drivers/iio/accel/Makefile              |    1 +
>> >  drivers/iio/accel/mma9551_core.c        |  183 +++++
>> >  drivers/iio/accel/mma9551_core.h        |   17 +-
>> >  drivers/iio/accel/mma9553.c             | 1321
>+++++++++++++++++++++++++++++++
>> >  include/linux/iio/iio.h                 |   11 +
>> >  7 files changed, 1587 insertions(+), 5 deletions(-)
>> >  create mode 100644 drivers/iio/accel/mma9553.c
>> >
>> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio
>b/Documentation/ABI/testing/sysfs-bus-iio
>> > index e009f49..732d018 100644
>> > --- a/Documentation/ABI/testing/sysfs-bus-iio
>> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
>> > @@ -343,7 +343,30 @@ Description:
>> >  		production inaccuracies).  If shared across all channels,
>> >  		<type>_calibscale is used.
>> >
>> > -What:		/sys/bus/iio/devices/iio:deviceX/in_steps_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
>> > +KernelVersion:	3.20
>> > +Contact:	linux-iio@vger.kernel.org
>> > +Description:
>> > +		Gender of the user (e.g.: male, female) used by some pedometers
>> > +		to compute the stride length, distance, speed and activity
>> > +		type.
>> > +
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
>> > +KernelVersion:	3.20
>> > +Contact:	linux-iio@vger.kernel.org
>> > +Description:
>> > +		Lists all available gender values (e.g.: male, female).
>> > +
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
>> >  KernelVersion:	3.19
>> >  Contact:	linux-iio@vger.kernel.org
>> >  Description:
>> > @@ -818,6 +841,14 @@
>What:		/sys/.../events/in_tempY_roc_falling_period
>> >  What:		/sys/.../events/in_accel_x&y&z_mag_falling_period
>> >  What:		/sys/.../events/in_intensity0_thresh_period
>> >  What:		/sys/.../events/in_proximity0_thresh_period
>> > +What:		/sys/.../events/in_activity_still_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_still_thresh_falling_period
>> > +What:		/sys/.../events/in_activity_walking_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_walking_thresh_falling_period
>> > +What:		/sys/.../events/in_activity_jogging_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_jogging_thresh_falling_period
>> > +What:		/sys/.../events/in_activity_running_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_running_thresh_falling_period
>> >  KernelVersion:	2.6.37
>> >  Contact:	linux-iio@vger.kernel.org
>> >  Description:
>> > @@ -1142,6 +1173,12 @@ Description:
>> >  		This attribute is used to get/set the integration time in
>> >  		seconds.
>> >
>> >
>+What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
>> > +KernelVersion:	3.20
>> > +Contact:	linux-iio@vger.kernel.org
>> > +Description:
>> > +		Number of seconds in which to compute speed.
>> > +
>> >  What:		/sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
>> >  KernelVersion:	3.15
>> >  Contact:	linux-iio@vger.kernel.org
>> > @@ -1170,13 +1207,17 @@ Description:
>> >  		present, output should be considered as processed with the
>> >  		unit in milliamps.
>> >
>> > +What:		/sys/.../iio:deviceX/in_energy_en
>> > +What:		/sys/.../iio:deviceX/in_distance_en
>> > +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
>> The speed one is interesting.  It's an instantaneous measure (unlike
>the
>> others that are cumulative).  As such I'd normally expect it not to
>need
>> an enable (nothing is getting reset).  However as you describe above
>we
>> have a speed calculation over a number of seconds so I can see where
>> this comes from.  Hmm.  Not sure on the right answer for this one.
>The problem is that all the attributes this driver exports  (speed,
>energy, distance) are based on the step counter.
>
>Although the step counter is always enabled, we do not want to keep the
>power on if nobody is using it. The *_en attributes are used to start
>the step counter by powering on the device.
>We cannot use one _en attribute for all exported values because we are
>using different iio types, but internally all _en attributes are
>treated as one flag to power on/off the device.
>I'm thinking of this scenario: I want to go run, so I start my running
>application that can show me speed and steps, but I do not want to keep
>the pedometer on once I'm done.
>
>The _en attribute is more related to power management in this case
>(although other pedometers have a special register to enable/disable
>the step counter).
Fair enough. I don't want to encourage this interface where power control can be automatic but here in can't!
>
>> 
>> 
>> 
>> >  What:		/sys/.../iio:deviceX/in_steps_en
>> >  KernelVersion:	3.19
>> >  Contact:	linux-iio@vger.kernel.org
>> >  Description:
>> > -		Activates the step counter. After activation, the number of
>steps
>> > -		taken by the user will be counted in hardware and exported
>through
>> > -		in_steps_input.
>> > +		Activates a device feature that runs in firmware/hardware.
>> > +		E.g. for steps: the pedometer saves power while not used;
>> > +		when activated, it will count the steps taken by the user in
>> > +		firmware and export them through in_steps_input.
>> >
>> >  What:		/sys/.../iio:deviceX/in_steps_input
>> >  KernelVersion:	3.19
>> > diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
>> > index c53047d..7c9a9a9 100644
>> > --- a/drivers/iio/accel/Kconfig
>> > +++ b/drivers/iio/accel/Kconfig
>> > @@ -126,4 +126,14 @@ config MMA9551
>> >  	  To compile this driver as a module, choose M here: the module
>> >  	  will be called mma9551.
>> >
>> > +config MMA9553
>> > +	tristate "Freescale MMA9553L Intelligent Pedometer Platform
>Driver"
>> > +	depends on I2C
>> > +	select MMA9551_CORE
>> > +	help
>> > +	  Say yes here to build support for the Freescale MMA9553L
>> > +	  Intelligent Pedometer Platform Driver.
>> > +
>> > +	  To compile this driver as a module, choose M here: the module
>> > +	  will be called mma9553.
>> >  endmenu
>> > diff --git a/drivers/iio/accel/Makefile
>b/drivers/iio/accel/Makefile
>> > index 8105316..f815695 100644
>> > --- a/drivers/iio/accel/Makefile
>> > +++ b/drivers/iio/accel/Makefile
>> > @@ -12,6 +12,7 @@ obj-$(CONFIG_MMA8452)	+= mma8452.o
>> >
>> >  obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
>> >  obj-$(CONFIG_MMA9551)		+= mma9551.o
>> > +obj-$(CONFIG_MMA9553)		+= mma9553.o
>> >
>> >  obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
>> >  st_accel-y := st_accel_core.o
>> > diff --git a/drivers/iio/accel/mma9551_core.c
>b/drivers/iio/accel/mma9551_core.c
>> > index 7f1a73e..7f55a6d 100644
>> > --- a/drivers/iio/accel/mma9551_core.c
>> > +++ b/drivers/iio/accel/mma9551_core.c
>> > @@ -53,6 +53,11 @@
>> >  #define MMA9551_AFE_Y_ACCEL_REG		0x02
>> >  #define MMA9551_AFE_Z_ACCEL_REG		0x04
>> >
>> > +/* Reset/Suspend/Clear application */
>> > +#define MMA9551_RSC_RESET		0x00
>> > +#define MMA9551_RSC_OFFSET(mask)	(3 - (ffs(mask) - 1) / 8)
>> > +#define MMA9551_RSC_VAL(mask)		(mask >> (((ffs(mask) - 1) / 8) *
>8))
>> > +
>> >  /*
>> >   * A response is composed of:
>> >   * - control registers: MB0-3
>> > @@ -275,6 +280,64 @@ int mma9551_read_status_byte(struct i2c_client
>*client, u8 app_id,
>> >  EXPORT_SYMBOL(mma9551_read_status_byte);
>> >
>> >  /**
>> > + * mma9551_read_config_word() - read 1 config word
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @val:	Pointer to store value read
>> > + *
>> > + * Read one configuration word from the device using MMA955xL
>command format.
>> > + * Commands to the MMA955xL platform consist of a write followed
>by one or
>> > + * more reads.
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
>> > +			    u16 reg, u16 *val)
>> > +{
>> > +	int ret;
>> > +	__be16 v;
>> > +
>> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
>> > +			       reg, NULL, 0, (u8 *)&v, 2);
>> > +	*val = be16_to_cpu(v);
>> > +
>> > +	return ret;
>> > +}
>> > +EXPORT_SYMBOL(mma9551_read_config_word);
>> > +
>> > +/**
>> > + * mma9551_write_config_word() - write 1 config word
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @val:	Value to write
>> > + *
>> > + * Write one configuration word from the device using MMA955xL
>command format.
>> > + * Commands to the MMA955xL platform consist of a write followed
>by one or
>> > + * more reads.
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_write_config_word(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u16 val)
>> > +{
>> > +	__be16 v = cpu_to_be16(val);
>> > +
>> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
>reg,
>> > +				(u8 *) &v, 2, NULL, 0);
>> > +}
>> > +EXPORT_SYMBOL(mma9551_write_config_word);
>> > +
>> > +/**
>> >   * mma9551_read_status_word() - read 1 status word
>> >   * @client:	I2C client
>> >   * @app_id:	Application ID
>> > @@ -306,6 +369,107 @@ int mma9551_read_status_word(struct
>i2c_client *client, u8 app_id,
>> >  EXPORT_SYMBOL(mma9551_read_status_word);
>> >
>> >  /**
>> > + * mma9551_read_config_words() - read multiple config words
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @len:	Length of array to read in bytes
>> > + * @val:	Array of words to read
>> > + *
>> > + * Read multiple configuration registers (word-sized registers).
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_read_config_words(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u8 len, u16 *buf)
>> > +{
>> > +	int ret, i;
>> > +	int len_words = len / sizeof(u16);
>> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
>> > +
>> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
>> > +			       reg, NULL, 0, (u8 *) be_buf, len);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	for (i = 0; i < len_words; i++)
>> > +		buf[i] = be16_to_cpu(be_buf[i]);
>> > +
>> > +	return 0;
>> > +}
>> > +EXPORT_SYMBOL(mma9551_read_config_words);
>> > +
>> > +/**
>> > + * mma9551_read_status_words() - read multiple status words
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @len:	Length of array to read in bytes
>> > + * @val:	Array of words to read
>> > + *
>> > + * Read multiple status registers (word-sized registers).
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_read_status_words(struct i2c_client *client, u8
>app_id,
>> > +			      u16 reg, u8 len, u16 *buf)
>> > +{
>> > +	int ret, i;
>> > +	int len_words = len / sizeof(u16);
>> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
>> > +
>> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
>> > +			       reg, NULL, 0, (u8 *) be_buf, len);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	for (i = 0; i < len_words; i++)
>> > +		buf[i] = be16_to_cpu(be_buf[i]);
>> > +
>> > +	return 0;
>> > +}
>> > +EXPORT_SYMBOL(mma9551_read_status_words);
>> > +
>> > +/**
>> > + * mma9551_write_config_words() - write multiple config words
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @len:	Length of array to write in bytes
>> > + * @val:	Array of words to write
>> > + *
>> > + * Write multiple configuration registers (word-sized registers).
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_write_config_words(struct i2c_client *client, u8
>app_id,
>> > +			       u16 reg, u8 len, u16 *buf)
>> > +{
>> > +	int i;
>> > +	int len_words = len / sizeof(u16);
>> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
>> > +
>> > +	for (i = 0; i < len_words; i++)
>> > +		be_buf[i] = cpu_to_be16(buf[i]);
>> > +
>> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
>> > +				reg, (u8 *) be_buf, len, NULL, 0);
>> > +}
>> > +EXPORT_SYMBOL(mma9551_write_config_words);
>> > +
>> > +/**
>> >   * mma9551_update_config_bits() - update bits in register
>> >   * @client:	I2C client
>> >   * @app_id:	Application ID
>> > @@ -609,6 +773,25 @@ int mma9551_read_accel_scale(int *val, int
>*val2)
>> >  }
>> >  EXPORT_SYMBOL(mma9551_read_accel_scale);
>> >
>> > +/**
>> > + * mma9551_app_reset() - reset application
>> > + * @client:	I2C client
>> > + * @app_mask:	Application to reset
>> > + *
>> > + * Reset the given application (using the Reset/Suspend/Clear
>> > + * Control Application)
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
>> > +{
>> > +	return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
>> > +					 MMA9551_RSC_RESET +
>> > +					 MMA9551_RSC_OFFSET(app_mask),
>> > +					 MMA9551_RSC_VAL(app_mask));
>> > +}
>> > +EXPORT_SYMBOL(mma9551_app_reset);
>> > +
>> >  MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
>> >  MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
>> >  MODULE_LICENSE("GPL v2");
>> > diff --git a/drivers/iio/accel/mma9551_core.h
>b/drivers/iio/accel/mma9551_core.h
>> > index e6efd02..edaa56b 100644
>> > --- a/drivers/iio/accel/mma9551_core.h
>> > +++ b/drivers/iio/accel/mma9551_core.h
>> > @@ -21,9 +21,13 @@
>> >  #define MMA9551_APPID_AFE		0x06
>> >  #define MMA9551_APPID_TILT		0x0B
>> >  #define MMA9551_APPID_SLEEP_WAKE	0x12
>> > -#define MMA9551_APPID_RESET		0x17
>> > +#define MMA9551_APPID_PEDOMETER	        0x15
>> > +#define MMA9551_APPID_RCS		0x17
>> >  #define MMA9551_APPID_NONE		0xff
>> >
>> > +/* Reset/Suspend/Clear application app masks */
>> > +#define MMA9551_RSC_PED			BIT(21)
>> > +
>> >  #define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
>> >
>> >  enum mma9551_gpio_pin {
>> > @@ -48,8 +52,18 @@ int mma9551_write_config_byte(struct i2c_client
>*client, u8 app_id,
>> >  			      u16 reg, u8 val);
>> >  int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
>> >  			     u16 reg, u8 *val);
>> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
>> > +			    u16 reg, u16 *val);
>> > +int mma9551_write_config_word(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u16 val);
>> >  int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
>> >  			     u16 reg, u16 *val);
>> > +int mma9551_read_config_words(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u8 len, u16 *buf);
>> > +int mma9551_read_status_words(struct i2c_client *client, u8
>app_id,
>> > +			      u16 reg, u8 len, u16 *buf);
>> > +int mma9551_write_config_words(struct i2c_client *client, u8
>app_id,
>> > +			       u16 reg, u8 len, u16 *buf);
>> >  int mma9551_update_config_bits(struct i2c_client *client, u8
>app_id,
>> >  			       u16 reg, u8 mask, u8 val);
>> >  int mma9551_gpio_config(struct i2c_client *client, enum
>mma9551_gpio_pin pin,
>> > @@ -62,5 +76,6 @@ int mma9551_read_accel_chan(struct i2c_client
>*client,
>> >  			    const struct iio_chan_spec *chan,
>> >  			    int *val, int *val2);
>> >  int mma9551_read_accel_scale(int *val, int *val2);
>> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
>> >
>> >  #endif /* _MMA9551_CORE_H_ */
>> > diff --git a/drivers/iio/accel/mma9553.c
>b/drivers/iio/accel/mma9553.c
>> > new file mode 100644
>> > index 0000000..ef6a6e3
>> > --- /dev/null
>> > +++ b/drivers/iio/accel/mma9553.c
>> > @@ -0,0 +1,1321 @@
>> > +/*
>> > + * Freescale MMA9553L Intelligent Pedometer driver
>> > + * Copyright (c) 2014, Intel Corporation.
>> > + *
>> > + * 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.
>> > + *
>> > + * This program is distributed in the hope 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/module.h>
>> > +#include <linux/i2c.h>
>> > +#include <linux/interrupt.h>
>> > +#include <linux/slab.h>
>> > +#include <linux/acpi.h>
>> > +#include <linux/gpio/consumer.h>
>> > +#include <linux/iio/iio.h>
>> > +#include <linux/iio/sysfs.h>
>> > +#include <linux/iio/events.h>
>> > +#include <linux/pm_runtime.h>
>> > +#include "mma9551_core.h"
>> > +
>> > +#define MMA9553_DRV_NAME			"mma9553"
>> > +#define MMA9553_IRQ_NAME			"mma9553_event"
>> > +#define MMA9553_GPIO_NAME			"mma9553_int"
>> > +
>> > +/* Pedometer configuration registers (R/W) */
>> > +#define MMA9553_CONF_SLEEPMIN		0x00
>> > +#define MMA9553_CONF_SLEEPMAX		0x02
>> > +#define MMA9553_CONF_SLEEPTHD		0x04
>> > +#define MMA9553_CONF_WORD		GENMASK(15, 0)
>> > +
>> > +#define MMA9553_CONF_CONF_STEPLEN	0x06
>> > +#define MMA9553_CONF_CONFIG		BIT(15)
>> > +#define MMA9553_CONF_ACT_DBCNTM		BIT(14)
>> > +#define MMA9553_CONF_SLP_DBCNTM		BIT(13)
>> > +#define MMA9553_CONF_STEPLEN		GENMASK(7, 0)
>> > +
>> > +#define MMA9553_CONF_HEIGHT_WEIGHT	0x08
>> Some of this naming is less than clear.  Which ones
>> are the register addresses and which the internal masks?
>> (obvious really, but names should reflect this perhaps)
>> 
>That's true. I'll rename them to make it more clear.
>
>> > +#define MMA9553_CONF_HEIGHT		GENMASK(15, 8)
>> > +#define MMA9553_CONF_WEIGHT		GENMASK(7, 0)
>> > +
>> > +#define MMA9553_CONF_FILTER		0x0A
>> > +#define MMA9553_CONF_FILTSTEP		GENMASK(15, 8)
>> > +#define MMA9553_CONF_MALE		BIT(7)
>> > +#define MMA9553_CONF_FILTTIME		GENMASK(6, 0)
>> > +
>> > +#define MMA9553_CONF_SPEED_STEP		0x0C
>> > +#define MMA9553_CONF_SPDPRD		GENMASK(15, 8)
>> > +#define MMA9553_CONF_STEPCOALESCE	GENMASK(7, 0)
>> > +
>> > +#define MMA9553_CONF_ACTTHD		0x0E
>> > +
>> > +/* Pedometer status registers (R-only) */
>> > +#define MMA9553_STATUS			0x00
>> > +#define MMA9553_STATUS_MRGFL		BIT(15)
>> > +#define MMA9553_STATUS_SUSPCHG		BIT(14)
>> > +#define MMA9553_STATUS_STEPCHG		BIT(13)
>> > +#define MMA9553_STATUS_ACTCHG		BIT(12)
>> > +#define MMA9553_STATUS_SUSP		BIT(11)
>> > +#define MMA9553_STATUS_ACTIVITY		(BIT(10) | BIT(9) | BIT(8))
>> > +#define MMA9553_STATUS_VERSION		0x00FF
>> > +
>> > +#define MMA9553_STEPCNT			0x02
>> > +#define MMA9553_DISTANCE		0x04
>> > +#define MMA9553_SPEED			0x06
>> > +#define MMA9553_CALORIES		0x08
>> > +#define MMA9553_SLEEPCNT		0x0A
>> > +
>> > +/* Pedometer events are always mapped to this pin. */
>> > +#define MMA9553_DEFAULT_GPIO_PIN	mma9551_gpio6
>> > +#define MMA9553_DEFAULT_GPIO_POLARITY	0
>> > +
>> > +/* Bitnum used for gpio configuration = bit number in high status
>byte */
>> > +#define STATUS_TO_BITNUM(bit)		(ffs(bit) - 9)
>> > +
>> > +#define MMA9553_DEFAULT_SAMPLE_RATE	30	/* Hz */
>> > +
>> > +/*
>> > + * The internal activity level must be stable for ACTTHD samples
>before
>> > + * ACTIVITY is updated.The ACTIVITY variable contains the current
>activity
>> > + * level and is updated every time a step is detected or once a
>second
>> > + * if there are no steps.
>> > + */
>> > +#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) /
>MMA9553_DEFAULT_SAMPLE_RATE)
>> > +#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) *
>MMA9553_DEFAULT_SAMPLE_RATE)
>> > +
>> > +/*
>> > + * Autonomously suspend pedometer if acceleration vector magnitude
>> > + * is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
>> > + */
>> > +#define MMA9553_DEFAULT_SLEEPMIN	3688	/* 0,9 g */
>> > +#define MMA9553_DEFAULT_SLEEPMAX	4508	/* 1,1 g */
>> > +#define MMA9553_DEFAULT_SLEEPTHD	(MMA9553_DEFAULT_SAMPLE_RATE *
>30)
>> > +
>> > +#define MMA9553_CONFIG_RETRIES		2
>> > +
>> > +/* Status register - activity field  */
>> > +enum activity_level {
>> > +	ACTIVITY_UNKNOWN,
>> > +	ACTIVITY_REST,
>> > +	ACTIVITY_WALKING,
>> > +	ACTIVITY_JOGGING,
>> > +	ACTIVITY_RUNNING,
>> > +};
>> > +
>> > +static struct mma9553_event_info {
>> > +	enum iio_chan_type type;
>> > +	enum iio_modifier mod;
>> > +	enum iio_event_direction dir;
>> > +} mma9553_events_info[] = {
>> > +	{
>> > +		.type = IIO_STEPS,
>> > +		.mod = IIO_NO_MOD,
>> > +		.dir = IIO_EV_DIR_NONE,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_STILL,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_STILL,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_WALKING,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_WALKING,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_JOGGING,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_JOGGING,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_RUNNING,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_RUNNING,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +};
>> > +
>> > +#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
>> > +
>> > +struct mma9553_event {
>> > +	struct mma9553_event_info *info;
>> > +	bool enabled;
>> > +};
>> > +
>> > +struct mma9553_conf_regs {
>> > +	u16 sleepmin;
>> > +	u16 sleepmax;
>> > +	u16 sleepthd;
>> > +	u16 config;
>> > +	u16 height_weight;
>> > +	u16 filter;
>> > +	u16 speed_step;
>> > +	u16 actthd;
>> > +} __packed;
>> > +
>> > +struct mma9553_data {
>> > +	struct i2c_client *client;
>> > +	struct mutex mutex;
>> > +	struct mma9553_conf_regs conf;
>> > +	struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
>> > +	int num_events;
>> > +	u8 gpio_bitnum;
>> > +	/*
>> > +	 * This is used for all features that depend on step count:
>> > +	 * step count, distance, speed, calories.
>> > +	 */
>> > +	bool stepcnt_enabled;
>> > +	u16 stepcnt;
>> > +	u8 activity;
>> > +	s64 timestamp;
>> > +};
>> > +
>> > +static u8 mma9553_get_bits(u16 val, u16 mask)
>> > +{
>> > +	return (val & mask) >> (ffs(mask) - 1);
>> > +}
>> > +
>> > +static u16 mma9553_set_bits(u16 current_val, u16 val, u16 mask)
>> > +{
>> > +	return (current_val & ~mask) | (val << (ffs(mask) - 1));
>> > +}
>> > +
>> > +static enum iio_modifier mma9553_activity_to_mod(enum
>activity_level activity)
>> > +{
>> > +	switch (activity) {
>> > +	case ACTIVITY_RUNNING:
>> > +		return IIO_MOD_RUNNING;
>> > +	case ACTIVITY_JOGGING:
>> > +		return IIO_MOD_JOGGING;
>> > +	case ACTIVITY_WALKING:
>> > +		return IIO_MOD_WALKING;
>> > +	case ACTIVITY_REST:
>> > +		return IIO_MOD_STILL;
>> > +	case ACTIVITY_UNKNOWN:
>> > +	default:
>> > +		return IIO_NO_MOD;
>> > +	}
>> > +}
>> > +
>> > +static void mma9553_init_events(struct mma9553_data *data)
>> > +{
>> > +	int i;
>> > +
>> > +	data->num_events = MMA9553_EVENTS_INFO_SIZE;
>> > +	for (i = 0; i < data->num_events; i++) {
>> > +		data->events[i].info = &mma9553_events_info[i];
>> > +		data->events[i].enabled = false;
>> > +	}
>> > +}
>> > +
>> > +static struct mma9553_event *mma9553_get_event(struct mma9553_data
>*data,
>> > +					       enum iio_chan_type type,
>> > +					       enum iio_modifier mod,
>> > +					       enum iio_event_direction dir)
>> > +{
>> > +	int i;
>> > +
>> > +	for (i = 0; i < data->num_events; i++)
>> > +		if (data->events[i].info->type == type &&
>> > +		    data->events[i].info->mod == mod &&
>> > +		    data->events[i].info->dir == dir)
>> > +			return &data->events[i];
>> > +
>> > +	return NULL;
>> > +}
>> > +
>> > +static bool mma9553_is_any_event_enabled(struct mma9553_data
>*data,
>> > +					 bool check_type,
>> > +					 enum iio_chan_type type)
>> > +{
>> > +	int i;
>> > +
>> > +	for (i = 0; i < data->num_events; i++)
>> > +		if ((check_type && data->events[i].info->type == type &&
>> > +		     data->events[i].enabled) ||
>> > +		     (!check_type && data->events[i].enabled))
>> > +			return true;
>> > +
>> > +	return false;
>> > +}
>> > +
>> > +static int mma9553_set_config(struct mma9553_data *data, u16 reg,
>> > +			      u16 *p_reg_val, u16 val, u16 mask)
>> > +{
>> > +	int ret, retries;
>> > +	u16 reg_val, config;
>> > +
>> > +	reg_val = *p_reg_val;
>> > +	if (val == mma9553_get_bits(reg_val, mask))
>> > +		return 0;
>> > +
>> > +	reg_val = mma9553_set_bits(reg_val, val, mask);
>> > +	ret = mma9551_write_config_word(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +					reg, reg_val);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"error writing config register 0x%x\n", reg);
>> > +		return ret;
>> > +	}
>> > +
>> > +	*p_reg_val = reg_val;
>> > +
>> > +	/* Reinitializes the pedometer with current configuration values
>*/
>> > +	config = mma9553_set_bits(data->conf.config, 1,
>MMA9553_CONF_CONFIG);
>> > +
>> > +	ret = mma9551_write_config_word(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +					MMA9553_CONF_CONF_STEPLEN, config);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"error writing config register 0x%x\n",
>> > +			MMA9553_CONF_CONF_STEPLEN);
>> > +		return ret;
>> > +	}
>> > +
>> > +	retries = MMA9553_CONFIG_RETRIES;
>> > +	do {
>> > +		mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
>> > +		ret = mma9551_read_config_word(data->client,
>> > +					       MMA9551_APPID_PEDOMETER,
>> > +					       MMA9553_CONF_CONF_STEPLEN,
>> > +					       &config);
>> > +		if (ret < 0)
>> > +			return ret;
>> > +	} while (mma9553_get_bits(config, MMA9553_CONF_CONFIG) &&
>> > +		 --retries > 0);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_read_activity_stepcnt(struct mma9553_data
>*data,
>> > +					 u8 *activity, u16 *stepcnt)
>> > +{
>> > +	u32 status_stepcnt;
>> > +	u16 status;
>> > +	int ret;
>> > +
>> > +	ret = mma9551_read_status_words(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +					MMA9553_STATUS, sizeof(u32),
>> > +					(u16 *) &status_stepcnt);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"error reading status and stepcnt\n");
>> > +		return ret;
>> > +	}
>> > +
>> > +	status = status_stepcnt & MMA9553_CONF_WORD;
>> > +	*activity = mma9553_get_bits(status, MMA9553_STATUS_ACTIVITY);
>> > +	*stepcnt = status_stepcnt >> 16;
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_conf_gpio(struct mma9553_data *data)
>> > +{
>> > +	u8 bitnum = 0, appid = MMA9551_APPID_PEDOMETER;
>> > +	int ret;
>> > +	struct mma9553_event *ev_step_detect;
>> > +	bool activity_enabled;
>> > +
>> > +	activity_enabled =
>> > +	    mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
>> > +	ev_step_detect =
>> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
>IIO_EV_DIR_NONE);
>> > +
>> > +	/*
>> > +	 * If both step detector and activity are enabled, use the MRGFL
>bit.
>> > +	 * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG
>flags.
>> > +	 */
>> > +	if (activity_enabled && ev_step_detect->enabled)
>> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_MRGFL);
>> > +	else if (ev_step_detect->enabled)
>> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_STEPCHG);
>> > +	else if (activity_enabled)
>> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_ACTCHG);
>> > +	else			/* Reset */
>> > +		appid = MMA9551_APPID_NONE;
>> > +
>> > +	if (data->gpio_bitnum == bitnum)
>> > +		return 0;
>> > +
>> > +	/* Save initial values for activity and stepcnt */
>> > +	if (activity_enabled || ev_step_detect->enabled)
>> > +		mma9553_read_activity_stepcnt(data, &data->activity,
>> > +					      &data->stepcnt);
>> > +
>> > +	ret = mma9551_gpio_config(data->client,
>> > +				  MMA9553_DEFAULT_GPIO_PIN,
>> > +				  appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +	data->gpio_bitnum = bitnum;
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_init(struct mma9553_data *data)
>> > +{
>> > +	int ret;
>> > +
>> > +	ret = mma9551_read_version(data->client);
>> > +	if (ret)
>> > +		return ret;
>> > +
>> > +	/*
>> > +	 * Read all the pedometer configuration registers. This is used
>as
>> > +	 * a device identification command to differentiate the MMA9553L
>> > +	 * from the MMA9550L.
>> > +	 */
>> > +	ret =
>> > +	    mma9551_read_config_words(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +				      MMA9553_CONF_SLEEPMIN,
>> > +				      sizeof(data->conf), (u16 *) &data->conf);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"device is not MMA9553L: failed to read cfg regs\n");
>> > +		return ret;
>> > +	}
>> > +
>> > +
>> > +	/* Reset gpio */
>> > +	data->gpio_bitnum = -1;
>> > +	ret = mma9553_conf_gpio(data);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	ret = mma9551_app_reset(data->client, MMA9551_RSC_PED);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	/* Init config registers */
>> > +	data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
>> > +	data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
>> > +	data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
>> > +	data->conf.config =
>> > +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
>> > +	/*
>> > +	 * Clear the activity debounce counter when the activity level
>changes,
>> > +	 * so that the confidence level applies for any activity level.
>> > +	 */
>> > +	data->conf.config =
>> > +	    mma9553_set_bits(data->conf.config, 1,
>MMA9553_CONF_ACT_DBCNTM);
>> > +	ret =
>> > +	    mma9551_write_config_words(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +				       MMA9553_CONF_SLEEPMIN,
>> > +				       sizeof(data->conf), (u16 *) &data->conf);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"failed to write configuration registers\n");
>> > +		return ret;
>> > +	}
>> > +
>> > +	return mma9551_set_device_state(data->client, true);
>> > +}
>> > +
>> > +static int mma9553_read_raw(struct iio_dev *indio_dev,
>> > +			    struct iio_chan_spec const *chan,
>> > +			    int *val, int *val2, long mask)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +	u16 tmp;
>> > +	u8 activity;
>> > +	bool powered_on;
>> > +
>> > +	switch (mask) {
>> > +	case IIO_CHAN_INFO_PROCESSED:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			/*
>> > +			 * The HW only counts steps and other dependent
>> > +			 * parameters (speed, distance, calories, activity)
>> > +			 * if power is on (from enabling an event or the
>> > +			 * step counter */
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_STEPCNT, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_DISTANCE:
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_DISTANCE, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACTIVITY:
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_STATUS, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +
>> > +			activity =
>> > +			    mma9553_get_bits(tmp, MMA9553_STATUS_ACTIVITY);
>> > +
>> > +			/*
>> > +			 * The device does not support confidence value levels,
>> > +			 * so we will always have 100% for current activity and
>> > +			 * 0% for the others.
>> > +			 */
>> > +			if (chan->channel2 == mma9553_activity_to_mod(activity))
>> > +				*val = 100;
>> > +			else
>> > +				*val = 0;
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_RAW:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:	/* m/h */
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_SPEED, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ENERGY:	/* Cal or kcal */
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_CALORIES, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACCEL:
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_accel_chan(data->client,
>> > +						      chan, val, val2);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_SCALE:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:	/* m/h to m/s */
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			*val = 0;
>> > +			*val2 = 277;	/* 0.000277 */
>> > +			return IIO_VAL_INT_PLUS_MICRO;
>> > +		case IIO_ENERGY:	/* Cal or kcal to J */
>> > +			*val = 4184;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACCEL:
>> > +			return mma9551_read_accel_scale(val, val2);
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_ENABLE:
>> > +		*val = data->stepcnt_enabled;
>> > +		return IIO_VAL_INT;
>> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
>> > +		*val = mma9553_get_bits(data->conf.height_weight,
>> > +					MMA9553_CONF_HEIGHT);
>> > +		return IIO_VAL_INT;
>> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
>> > +		*val = mma9553_get_bits(data->conf.height_weight,
>> > +					MMA9553_CONF_WEIGHT);
>> > +		return IIO_VAL_INT;
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			*val = mma9553_get_bits(data->conf.filter,
>> > +						MMA9553_CONF_FILTSTEP);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			*val = mma9553_get_bits(data->conf.filter,
>> > +						MMA9553_CONF_FILTTIME);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_INT_TIME:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			*val = mma9553_get_bits(data->conf.speed_step,
>> > +						MMA9553_CONF_SPDPRD);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_write_raw(struct iio_dev *indio_dev,
>> > +			     struct iio_chan_spec const *chan,
>> > +			     int val, int val2, long mask)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	switch (mask) {
>> > +	case IIO_CHAN_INFO_ENABLE:
>> > +		if (data->stepcnt_enabled == !!val)
>> > +			return 0;
>> > +		mutex_lock(&data->mutex);
>> > +		ret = mma9551_set_power_state(data->client, val);
>> > +		if (ret < 0) {
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		}
>> > +		data->stepcnt_enabled = val;
>> > +		mutex_unlock(&data->mutex);
>> > +		return 0;
>> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
>> > +		if (val < 0 || val > 255)
>> > +			return -EINVAL;
>> We are being rather inconsistent in having calibheight in cm rather
>than
>> m (as per distance etc). I wonder if now is the time to change it
>whilst
>> we have few users...
>
>Sure, I'll change it.
>> > +		mutex_lock(&data->mutex);
>> > +		ret = mma9553_set_config(data,
>> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
>> > +					 &data->conf.height_weight,
>> > +					 val, MMA9553_CONF_HEIGHT);
>> > +		mutex_unlock(&data->mutex);
>> > +		return ret;
>> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
>> > +		if (val < 0 || val > 255)
>> > +			return -EINVAL;
>> > +		mutex_lock(&data->mutex);
>> > +		ret = mma9553_set_config(data,
>> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
>> > +					 &data->conf.height_weight,
>> > +					 val, MMA9553_CONF_WEIGHT);
>> > +		mutex_unlock(&data->mutex);
>> > +		return ret;
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			/*
>> > +			 * Set to 0 to disable step filtering. If the value
>> > +			 * specified is greater than 6, then 6 will be used.
>> > +			 */
>> > +			if (val < 0)
>> > +				return -EINVAL;
>> > +			if (val > 6)
>> > +				val = 6;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
>> > +						 &data->conf.filter, val,
>> > +						 MMA9553_CONF_FILTSTEP);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			if (val < 0 || val > 127)
>> > +				return -EINVAL;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
>> > +						 &data->conf.filter, val,
>> > +						 MMA9553_CONF_FILTTIME);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_INT_TIME:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			/*
>> > +			 * If set to a value greater than 5, then 5 will be
>> > +			 * used. Warning: Do not set SPDPRD to 0 or 1 as
>> > +			 * this may cause undesirable behavior.
>> > +			 */
>> > +			if (val < 2)
>> > +				return -EINVAL;
>> > +			if (val > 5)
>> > +				val = 5;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
>> > +						 &data->conf.speed_step, val,
>> > +						 MMA9553_CONF_SPDPRD);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
>> > +	struct mma9553_event *event;
>> > +
>> > +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
>> > +	if (!event)
>> > +		return -EINVAL;
>> > +
>> > +	return event->enabled;
>> > +}
>> > +
>> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
>> > +	struct mma9553_event *event;
>> > +	int ret;
>> > +
>> > +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
>> > +	if (!event)
>> > +		return -EINVAL;
>> > +
>> > +	if (event->enabled == state)
>> > +		return 0;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +
>> > +	ret = mma9551_set_power_state(data->client, state);
>> > +	if (ret < 0)
>> > +		goto err_out;
>> > +	event->enabled = state;
>> > +
>> > +	ret = mma9553_conf_gpio(data);
>> > +	if (ret < 0)
>> > +		goto err_conf_gpio;
>> > +
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +
>> > +err_conf_gpio:
>> > +	if (state) {
>> > +		event->enabled = false;
>> > +		mma9551_set_power_state(data->client, false);
>> > +	}
>> > +err_out:
>> > +	mutex_unlock(&data->mutex);
>> > +	return ret;
>> > +}
>> > +
>> > +static int mma9553_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)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +
>> > +	*val2 = 0;
>> > +	switch (info) {
>> > +	case IIO_EV_INFO_VALUE:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			*val = mma9553_get_bits(data->conf.speed_step,
>> > +						MMA9553_CONF_STEPCOALESCE);
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACTIVITY:
>> > +			/*
>> > +			 * The device does not support confidence value levels.
>> > +			 * We set an average of 50%.
>> > +			 */
>> > +			*val = 50;
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_EV_INFO_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_ACTIVITY:
>> > +			*val = MMA9553_ACTIVITY_THD_TO_SEC(data->conf.actthd);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	switch (info) {
>> > +	case IIO_EV_INFO_VALUE:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			if (val < 0 || val > 255)
>> > +				return -EINVAL;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
>> > +						 &data->conf.speed_step, val,
>> > +						 MMA9553_CONF_STEPCOALESCE);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_EV_INFO_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_ACTIVITY:
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_ACTTHD,
>> > +						 &data->conf.actthd,
>> > +						 MMA9553_ACTIVITY_SEC_TO_THD
>> > +						 (val), MMA9553_CONF_WORD);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
>> > +					const struct iio_chan_spec *chan)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	u8 gender;
>> > +
>> > +	gender = mma9553_get_bits(data->conf.filter, MMA9553_CONF_MALE);
>> > +	/*
>> > +	 * HW expects 0 for female and 1 for male,
>> > +	 * while iio index is 0 for male and 1 for female
>> > +	 */
>> > +	return !gender;
>> > +}
>> > +
>> > +static int mma9553_set_calibgender_mode(struct iio_dev *indio_dev,
>> > +					const struct iio_chan_spec *chan,
>> > +					unsigned int mode)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	u8 gender = !mode;
>> > +	int ret;
>> > +
>> > +	if ((mode != 0) && (mode != 1))
>> > +		return -EINVAL;
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
>&data->conf.filter,
>> > +				 gender, MMA9553_CONF_MALE);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +}
>> > +
>> > +static const struct iio_event_spec mma9553_step_event = {
>> > +	.type = IIO_EV_TYPE_CHANGE,
>> > +	.dir = IIO_EV_DIR_NONE,
>> > +	.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
>BIT(IIO_EV_INFO_VALUE),
>> > +};
>> > +
>> > +static const struct iio_event_spec mma9553_activity_events[] = {
>> > +	{
>> > +		.type = IIO_EV_TYPE_THRESH,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
>> > +				 BIT(IIO_EV_INFO_VALUE) |
>> > +				 BIT(IIO_EV_INFO_PERIOD),
>> > +	 },
>> > +	{
>> > +		.type = IIO_EV_TYPE_THRESH,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
>> > +				 BIT(IIO_EV_INFO_VALUE) |
>> > +				 BIT(IIO_EV_INFO_PERIOD),
>> > +	},
>> > +};
>> > +
>> > +static const char * const calibgender_modes[] = { "male", "female"
>};
>> > +
>> > +static const struct iio_enum mma9553_calibgender_enum = {
>> > +	.items = calibgender_modes,
>> > +	.num_items = ARRAY_SIZE(calibgender_modes),
>> > +	.get = mma9553_get_calibgender_mode,
>> > +	.set = mma9553_set_calibgender_mode,
>> > +};
>> > +
>> > +static const struct iio_chan_spec_ext_info mma9553_ext_info[] = {
>> > +	IIO_CALIBGENDER_EXT_INFO(IIO_SHARED_BY_TYPE,
>&mma9553_calibgender_enum),
>> > +	{},
>> > +};
>> > +
>> > +#define MMA9553_PEDOMETER_CHANNEL(_type, _mask) {		\
>> > +	.type = _type,						\
>> > +	.info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE)      |	\
>> > +			      BIT(IIO_CHAN_INFO_CALIBHEIGHT) |	\
>> > +			      _mask,				\
>> > +	.ext_info = mma9553_ext_info,				\
>> > +}
>> > +
>> > +#define MMA9553_ACTIVITY_CHANNEL(_chan2) {				\
>> > +	.type = IIO_ACTIVITY,						\
>> > +	.modified = 1,							\
>> > +	.channel2 = _chan2,						\
>> > +	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),		\
>> > +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),	\
>> > +	.event_spec = mma9553_activity_events,				\
>> > +	.num_event_specs = ARRAY_SIZE(mma9553_activity_events),		\
>> > +	.ext_info = mma9553_ext_info,					\
>> > +}
>> > +
>> > +static const struct iio_chan_spec mma9553_channels[] = {
>> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
>> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
>> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
>> > +
>> > +	{
>> > +		.type = IIO_STEPS,
>> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
>> > +				     BIT(IIO_CHAN_INFO_ENABLE) |
>> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH) |
>> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD),
>> > +		.event_spec = &mma9553_step_event,
>> > +		.num_event_specs = 1,
>> > +	},
>> > +
>> > +	MMA9553_PEDOMETER_CHANNEL(IIO_DISTANCE,
>BIT(IIO_CHAN_INFO_PROCESSED)),
>> > +	{
>> > +		.type = IIO_VELOCITY,
>> > +		.modified = 1,
>> > +		.channel2 = IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
>> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> > +				      BIT(IIO_CHAN_INFO_SCALE) |
>> > +				      BIT(IIO_CHAN_INFO_INT_TIME) |
>> > +				      BIT(IIO_CHAN_INFO_ENABLE),
>> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),
>> > +		.ext_info = mma9553_ext_info,
>> > +	},
>> > +	MMA9553_PEDOMETER_CHANNEL(IIO_ENERGY, BIT(IIO_CHAN_INFO_RAW) |
>> > +				  BIT(IIO_CHAN_INFO_SCALE) |
>> > +				  BIT(IIO_CHAN_INFO_CALIBWEIGHT)),
>> > +
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_JOGGING),
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_STILL),
>> > +};
>> > +
>> > +static const struct iio_info mma9553_info = {
>> > +	.driver_module = THIS_MODULE,
>> > +	.read_raw = mma9553_read_raw,
>> > +	.write_raw = mma9553_write_raw,
>> > +	.read_event_config = mma9553_read_event_config,
>> > +	.write_event_config = mma9553_write_event_config,
>> > +	.read_event_value = mma9553_read_event_value,
>> > +	.write_event_value = mma9553_write_event_value,
>> > +};
>> > +
>> > +static irqreturn_t mma9553_irq_handler(int irq, void *private)
>> > +{
>> > +	struct iio_dev *indio_dev = private;
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +
>> > +	data->timestamp = iio_get_time_ns();
>> > +	/*
>> > +	 * Since we only configure the interrupt pin when an
>> > +	 * event is enabled, we are sure we have at least
>> > +	 * one event enabled at this point.
>> > +	 */
>> > +	return IRQ_WAKE_THREAD;
>> > +}
>> > +
>> > +static irqreturn_t mma9553_event_handler(int irq, void *private)
>> > +{
>> > +	struct iio_dev *indio_dev = private;
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	u16 stepcnt;
>> > +	u8 activity;
>> > +	struct mma9553_event *ev_activity, *ev_prev_activity,
>*ev_step_detect;
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9553_read_activity_stepcnt(data, &activity, &stepcnt);
>> > +	if (ret < 0) {
>> > +		mutex_unlock(&data->mutex);
>> > +		return IRQ_HANDLED;
>> > +	}
>> > +
>> > +	ev_prev_activity =
>> > +	    mma9553_get_event(data, IIO_ACTIVITY,
>> > +			      mma9553_activity_to_mod(data->activity),
>> > +			      IIO_EV_DIR_FALLING);
>> > +	ev_activity =
>> > +	    mma9553_get_event(data, IIO_ACTIVITY,
>> > +			      mma9553_activity_to_mod(activity),
>> > +			      IIO_EV_DIR_RISING);
>> > +	ev_step_detect =
>> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
>IIO_EV_DIR_NONE);
>> > +
>> > +	if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
>> > +		data->stepcnt = stepcnt;
>> > +		iio_push_event(indio_dev,
>> > +			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
>> > +			       IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
>> > +			       data->timestamp);
>> > +	}
>> > +
>> > +	if (activity != data->activity) {
>> > +		data->activity = activity;
>> > +		/* ev_activity can be NULL if activity == ACTIVITY_UNKNOWN */
>> > +		if (ev_prev_activity && ev_prev_activity->enabled)
>> > +			iio_push_event(indio_dev,
>> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
>> > +				       ev_prev_activity->info->mod,
>> > +				       IIO_EV_DIR_FALLING,
>> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
>> > +				       data->timestamp);
>> > +
>> > +		if (ev_activity && ev_activity->enabled)
>> > +			iio_push_event(indio_dev,
>> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
>> > +				       ev_activity->info->mod,
>> > +				       IIO_EV_DIR_RISING,
>> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
>> > +				       data->timestamp);
>> > +	}
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return IRQ_HANDLED;
>> > +}
>> > +
>> > +static int mma9553_gpio_probe(struct i2c_client *client)
>> > +{
>> > +	struct device *dev;
>> > +	struct gpio_desc *gpio;
>> > +	int ret;
>> > +
>> > +	if (!client)
>> > +		return -EINVAL;
>> > +
>> > +	dev = &client->dev;
>> > +
>> > +	/* data ready gpio interrupt pin */
>> > +	gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
>> > +	if (IS_ERR(gpio)) {
>> > +		dev_err(dev, "acpi gpio get index failed\n");
>> > +		return PTR_ERR(gpio);
>> > +	}
>> > +
>> > +	ret = gpiod_direction_input(gpio);
>> > +	if (ret)
>> > +		return ret;
>> > +
>> > +	ret = gpiod_to_irq(gpio);
>> > +
>> > +	dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio),
>ret);
>> > +
>> > +	return ret;
>> > +}
>> > +
>> > +static const char *mma9553_match_acpi_device(struct device *dev)
>> > +{
>> > +	const struct acpi_device_id *id;
>> > +
>> > +	id = acpi_match_device(dev->driver->acpi_match_table, dev);
>> > +	if (!id)
>> > +		return NULL;
>> > +
>> > +	return dev_name(dev);
>> > +}
>> > +
>> > +static int mma9553_probe(struct i2c_client *client,
>> > +			 const struct i2c_device_id *id)
>> > +{
>> > +	struct mma9553_data *data;
>> > +	struct iio_dev *indio_dev;
>> > +	const char *name = NULL;
>> > +	int ret;
>> > +
>> > +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
>> > +	if (!indio_dev)
>> > +		return -ENOMEM;
>> > +
>> > +	data = iio_priv(indio_dev);
>> > +	i2c_set_clientdata(client, indio_dev);
>> > +	data->client = client;
>> > +
>> > +	if (id)
>> > +		name = id->name;
>> > +	else if (ACPI_HANDLE(&client->dev))
>> > +		name = mma9553_match_acpi_device(&client->dev);
>> > +	else
>> > +		return -ENOSYS;
>> > +
>> > +	mutex_init(&data->mutex);
>> > +	mma9553_init_events(data);
>> > +
>> > +	ret = mma9553_init(data);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	indio_dev->dev.parent = &client->dev;
>> > +	indio_dev->channels = mma9553_channels;
>> > +	indio_dev->num_channels = ARRAY_SIZE(mma9553_channels);
>> > +	indio_dev->name = name;
>> > +	indio_dev->modes = INDIO_DIRECT_MODE;
>> > +	indio_dev->info = &mma9553_info;
>> > +
>> > +	if (client->irq < 0)
>> > +		client->irq = mma9553_gpio_probe(client);
>> > +
>> > +	if (client->irq >= 0) {
>> > +		ret = devm_request_threaded_irq(&client->dev, client->irq,
>> > +						mma9553_irq_handler,
>> > +						mma9553_event_handler,
>> > +						IRQF_TRIGGER_RISING,
>> > +						MMA9553_IRQ_NAME, indio_dev);
>> > +		if (ret < 0) {
>> > +			dev_err(&client->dev, "request irq %d failed\n",
>> > +				client->irq);
>> > +			goto out_poweroff;
>> > +		}
>> > +
>> > +	}
>> > +
>> > +	ret = iio_device_register(indio_dev);
>> > +	if (ret < 0) {
>> > +		dev_err(&client->dev, "unable to register iio device\n");
>> > +		goto out_poweroff;
>> > +	}
>> > +
>> > +	ret = pm_runtime_set_active(&client->dev);
>> > +	if (ret < 0)
>> > +		goto out_iio_unregister;
>> > +
>> > +	pm_runtime_enable(&client->dev);
>> > +	pm_runtime_set_autosuspend_delay(&client->dev,
>> > +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
>> > +	pm_runtime_use_autosuspend(&client->dev);
>> > +
>> > +	dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
>> > +
>> > +	return 0;
>> > +
>> > +out_iio_unregister:
>> > +	iio_device_unregister(indio_dev);
>> > +out_poweroff:
>> > +	mma9551_set_device_state(client, false);
>> > +	return ret;
>> > +}
>> > +
>> > +static int mma9553_remove(struct i2c_client *client)
>> > +{
>> > +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +
>> > +	pm_runtime_disable(&client->dev);
>> > +	pm_runtime_set_suspended(&client->dev);
>> > +	pm_runtime_put_noidle(&client->dev);
>> > +
>> > +	iio_device_unregister(indio_dev);
>> > +	mutex_lock(&data->mutex);
>> > +	mma9551_set_device_state(data->client, false);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +#ifdef CONFIG_PM
>> > +static int mma9553_runtime_suspend(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9551_set_device_state(data->client, false);
>> > +	mutex_unlock(&data->mutex);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev, "powering off device failed\n");
>> > +		return -EAGAIN;
>> > +	}
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_runtime_resume(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	ret = mma9551_set_device_state(data->client, true);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
>> > +
>> > +	return 0;
>> > +}
>> > +#endif
>> > +
>> > +#ifdef CONFIG_PM_SLEEP
>> > +static int mma9553_suspend(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9551_set_device_state(data->client, false);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +}
>> > +
>> > +static int mma9553_resume(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9551_set_device_state(data->client, true);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +}
>> > +#endif
>> > +
>> > +static const struct dev_pm_ops mma9553_pm_ops = {
>> > +	SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
>> > +	SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
>> > +			   mma9553_runtime_resume, NULL)
>> > +};
>> > +
>> > +static const struct acpi_device_id mma9553_acpi_match[] = {
>> > +	{"MMA9553", 0},
>> > +	{},
>> > +};
>> > +
>> > +MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
>> > +
>> > +static const struct i2c_device_id mma9553_id[] = {
>> > +	{"mma9553", 0},
>> > +	{},
>> > +};
>> > +
>> > +MODULE_DEVICE_TABLE(i2c, mma9553_id);
>> > +
>> > +static struct i2c_driver mma9553_driver = {
>> > +	.driver = {
>> > +		   .name = MMA9553_DRV_NAME,
>> > +		   .acpi_match_table = ACPI_PTR(mma9553_acpi_match),
>> > +		   .pm = &mma9553_pm_ops,
>> > +		   },
>> > +	.probe = mma9553_probe,
>> > +	.remove = mma9553_remove,
>> > +	.id_table = mma9553_id,
>> > +};
>> > +
>> > +module_i2c_driver(mma9553_driver);
>> > +
>> > +MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
>> > +MODULE_LICENSE("GPL v2");
>> > +MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
>> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>> > index b6b12ac..6770a2f 100644
>> > --- a/include/linux/iio/iio.h
>> > +++ b/include/linux/iio/iio.h
>> > @@ -634,4 +634,15 @@ int iio_str_to_fixpoint(const char *str, int
>fract_mult, int *integer,
>> >   */
>> >  #define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
>> >
>> > +/**
>> > + * IIO_CALIBGENDER_EXT_INFO() - define the gender of the user
>> > + *
>> > + * @_shared:	Whether the attribute is shared between all channels
>> > + * @_e:		Pointer to an iio_enum struct
>> > + *
>> > + */
>> > +#define IIO_CALIBGENDER_EXT_INFO(_shared, _e)			       \
>> > +	IIO_ENUM("calibgender", _shared, _e),			       \
>> > +	IIO_ENUM_AVAILABLE("calibgender", _e)
>> > +
>> For now this is a little obscure to drop into the main header.
>> If we get lots of instances, then perhaps it'll make sense to
>> move it here.  Right now, please keep it in the driver.
>
>
>Ok, will move this to the driver.
>> >  #endif /* _INDUSTRIAL_IO_H_ */
>> >

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* RE: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
@ 2015-01-27 17:31         ` Jonathan Cameron
  0 siblings, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2015-01-27 17:31 UTC (permalink / raw)
  To: Tirdea, Irina, Jonathan Cameron, linux-iio
  Cc: linux-kernel, Dogaru, Vlad, Baluta, Daniel, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald




On 27 January 2015 17:09:20 GMT+00:00, "Tirdea, Irina" <irina.tirdea@intel.com> wrote:
>
>
>> -----Original Message-----
>> From: Jonathan Cameron [mailto:jic23@kernel.org]
>> Sent: 26 January, 2015 22:44
>> To: Tirdea, Irina; linux-iio@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org; Dogaru, Vlad; Baluta, Daniel;
>Hartmut Knaack; Lars-Peter Clausen; Peter Meerwald
>> Subject: Re: [PATCH v2 10/10] iio: add driver for Freescale MMA9553
>> 
>> On 11/01/15 19:10, Irina Tirdea wrote:
>> > Add support for Freescale MMA9553L Intelligent Pedometer Platform.
>> >
>> > The following functionalities are supported:
>> >  - step counter (counts the number of steps using a HW register)
>> >  - step detector (generates an iio event at every step the user
>takes)
>> >  - activity recognition (rest, walking, jogging, running)
>> >  - speed
>> >  - calories
>> >  - distance
>> >
>> > To get accurate pedometer results, the user's height, weight and
>gender
>> > need to be configured.
>> >
>> > The specifications can be downloaded from:
>> >
>http://www.freescale.com/files/sensors/doc/ref_manual/MMA955xLSWRM.pdf
>> >
>http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf
>> >
>> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
>> One thing noticed in passing.  Calibheight is in cm whereas distance
>is
>> in m. This seems inconsistent.  Would you mind changing to meters for
>> calibheight?
>> (lets do it whilst we don't really have any users yet).
>Sure, using meters is better. 
>I'll make the change to the iio Documentation and the driver.
>> 
>> 
>> A very nice driver, just a few minor queries in line.  Also of course
>> the interface for the debounce of steps as discussed in the thread
>> hanging off earlier in this series.
>> 
>> I'm not keen on the addition to iio.h (yet) as it is only used by
>this
>> one driver and as a general rule macros that simple are best avoided
>> (tend to limit things rather than help).
>Ok, I'll move the code to the driver.
>> 
>> > ---
>> >  Documentation/ABI/testing/sysfs-bus-iio |   49 +-
>> >  drivers/iio/accel/Kconfig               |   10 +
>> >  drivers/iio/accel/Makefile              |    1 +
>> >  drivers/iio/accel/mma9551_core.c        |  183 +++++
>> >  drivers/iio/accel/mma9551_core.h        |   17 +-
>> >  drivers/iio/accel/mma9553.c             | 1321
>+++++++++++++++++++++++++++++++
>> >  include/linux/iio/iio.h                 |   11 +
>> >  7 files changed, 1587 insertions(+), 5 deletions(-)
>> >  create mode 100644 drivers/iio/accel/mma9553.c
>> >
>> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio
>b/Documentation/ABI/testing/sysfs-bus-iio
>> > index e009f49..732d018 100644
>> > --- a/Documentation/ABI/testing/sysfs-bus-iio
>> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
>> > @@ -343,7 +343,30 @@ Description:
>> >  		production inaccuracies).  If shared across all channels,
>> >  		<type>_calibscale is used.
>> >
>> > -What:		/sys/bus/iio/devices/iio:deviceX/in_steps_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
>> > +KernelVersion:	3.20
>> > +Contact:	linux-iio@vger.kernel.org
>> > +Description:
>> > +		Gender of the user (e.g.: male, female) used by some pedometers
>> > +		to compute the stride length, distance, speed and activity
>> > +		type.
>> > +
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
>> >
>+What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
>> > +KernelVersion:	3.20
>> > +Contact:	linux-iio@vger.kernel.org
>> > +Description:
>> > +		Lists all available gender values (e.g.: male, female).
>> > +
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
>> > +What:		/sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
>> >  KernelVersion:	3.19
>> >  Contact:	linux-iio@vger.kernel.org
>> >  Description:
>> > @@ -818,6 +841,14 @@
>What:		/sys/.../events/in_tempY_roc_falling_period
>> >  What:		/sys/.../events/in_accel_x&y&z_mag_falling_period
>> >  What:		/sys/.../events/in_intensity0_thresh_period
>> >  What:		/sys/.../events/in_proximity0_thresh_period
>> > +What:		/sys/.../events/in_activity_still_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_still_thresh_falling_period
>> > +What:		/sys/.../events/in_activity_walking_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_walking_thresh_falling_period
>> > +What:		/sys/.../events/in_activity_jogging_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_jogging_thresh_falling_period
>> > +What:		/sys/.../events/in_activity_running_thresh_rising_period
>> > +What:		/sys/.../events/in_activity_running_thresh_falling_period
>> >  KernelVersion:	2.6.37
>> >  Contact:	linux-iio@vger.kernel.org
>> >  Description:
>> > @@ -1142,6 +1173,12 @@ Description:
>> >  		This attribute is used to get/set the integration time in
>> >  		seconds.
>> >
>> >
>+What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
>> > +KernelVersion:	3.20
>> > +Contact:	linux-iio@vger.kernel.org
>> > +Description:
>> > +		Number of seconds in which to compute speed.
>> > +
>> >  What:		/sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
>> >  KernelVersion:	3.15
>> >  Contact:	linux-iio@vger.kernel.org
>> > @@ -1170,13 +1207,17 @@ Description:
>> >  		present, output should be considered as processed with the
>> >  		unit in milliamps.
>> >
>> > +What:		/sys/.../iio:deviceX/in_energy_en
>> > +What:		/sys/.../iio:deviceX/in_distance_en
>> > +What:		/sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
>> The speed one is interesting.  It's an instantaneous measure (unlike
>the
>> others that are cumulative).  As such I'd normally expect it not to
>need
>> an enable (nothing is getting reset).  However as you describe above
>we
>> have a speed calculation over a number of seconds so I can see where
>> this comes from.  Hmm.  Not sure on the right answer for this one.
>The problem is that all the attributes this driver exports  (speed,
>energy, distance) are based on the step counter.
>
>Although the step counter is always enabled, we do not want to keep the
>power on if nobody is using it. The *_en attributes are used to start
>the step counter by powering on the device.
>We cannot use one _en attribute for all exported values because we are
>using different iio types, but internally all _en attributes are
>treated as one flag to power on/off the device.
>I'm thinking of this scenario: I want to go run, so I start my running
>application that can show me speed and steps, but I do not want to keep
>the pedometer on once I'm done.
>
>The _en attribute is more related to power management in this case
>(although other pedometers have a special register to enable/disable
>the step counter).
Fair enough. I don't want to encourage this interface where power control can be automatic but here in can't!
>
>> 
>> 
>> 
>> >  What:		/sys/.../iio:deviceX/in_steps_en
>> >  KernelVersion:	3.19
>> >  Contact:	linux-iio@vger.kernel.org
>> >  Description:
>> > -		Activates the step counter. After activation, the number of
>steps
>> > -		taken by the user will be counted in hardware and exported
>through
>> > -		in_steps_input.
>> > +		Activates a device feature that runs in firmware/hardware.
>> > +		E.g. for steps: the pedometer saves power while not used;
>> > +		when activated, it will count the steps taken by the user in
>> > +		firmware and export them through in_steps_input.
>> >
>> >  What:		/sys/.../iio:deviceX/in_steps_input
>> >  KernelVersion:	3.19
>> > diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
>> > index c53047d..7c9a9a9 100644
>> > --- a/drivers/iio/accel/Kconfig
>> > +++ b/drivers/iio/accel/Kconfig
>> > @@ -126,4 +126,14 @@ config MMA9551
>> >  	  To compile this driver as a module, choose M here: the module
>> >  	  will be called mma9551.
>> >
>> > +config MMA9553
>> > +	tristate "Freescale MMA9553L Intelligent Pedometer Platform
>Driver"
>> > +	depends on I2C
>> > +	select MMA9551_CORE
>> > +	help
>> > +	  Say yes here to build support for the Freescale MMA9553L
>> > +	  Intelligent Pedometer Platform Driver.
>> > +
>> > +	  To compile this driver as a module, choose M here: the module
>> > +	  will be called mma9553.
>> >  endmenu
>> > diff --git a/drivers/iio/accel/Makefile
>b/drivers/iio/accel/Makefile
>> > index 8105316..f815695 100644
>> > --- a/drivers/iio/accel/Makefile
>> > +++ b/drivers/iio/accel/Makefile
>> > @@ -12,6 +12,7 @@ obj-$(CONFIG_MMA8452)	+= mma8452.o
>> >
>> >  obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
>> >  obj-$(CONFIG_MMA9551)		+= mma9551.o
>> > +obj-$(CONFIG_MMA9553)		+= mma9553.o
>> >
>> >  obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
>> >  st_accel-y := st_accel_core.o
>> > diff --git a/drivers/iio/accel/mma9551_core.c
>b/drivers/iio/accel/mma9551_core.c
>> > index 7f1a73e..7f55a6d 100644
>> > --- a/drivers/iio/accel/mma9551_core.c
>> > +++ b/drivers/iio/accel/mma9551_core.c
>> > @@ -53,6 +53,11 @@
>> >  #define MMA9551_AFE_Y_ACCEL_REG		0x02
>> >  #define MMA9551_AFE_Z_ACCEL_REG		0x04
>> >
>> > +/* Reset/Suspend/Clear application */
>> > +#define MMA9551_RSC_RESET		0x00
>> > +#define MMA9551_RSC_OFFSET(mask)	(3 - (ffs(mask) - 1) / 8)
>> > +#define MMA9551_RSC_VAL(mask)		(mask >> (((ffs(mask) - 1) / 8) *
>8))
>> > +
>> >  /*
>> >   * A response is composed of:
>> >   * - control registers: MB0-3
>> > @@ -275,6 +280,64 @@ int mma9551_read_status_byte(struct i2c_client
>*client, u8 app_id,
>> >  EXPORT_SYMBOL(mma9551_read_status_byte);
>> >
>> >  /**
>> > + * mma9551_read_config_word() - read 1 config word
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @val:	Pointer to store value read
>> > + *
>> > + * Read one configuration word from the device using MMA955xL
>command format.
>> > + * Commands to the MMA955xL platform consist of a write followed
>by one or
>> > + * more reads.
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
>> > +			    u16 reg, u16 *val)
>> > +{
>> > +	int ret;
>> > +	__be16 v;
>> > +
>> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
>> > +			       reg, NULL, 0, (u8 *)&v, 2);
>> > +	*val = be16_to_cpu(v);
>> > +
>> > +	return ret;
>> > +}
>> > +EXPORT_SYMBOL(mma9551_read_config_word);
>> > +
>> > +/**
>> > + * mma9551_write_config_word() - write 1 config word
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @val:	Value to write
>> > + *
>> > + * Write one configuration word from the device using MMA955xL
>command format.
>> > + * Commands to the MMA955xL platform consist of a write followed
>by one or
>> > + * more reads.
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_write_config_word(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u16 val)
>> > +{
>> > +	__be16 v = cpu_to_be16(val);
>> > +
>> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
>reg,
>> > +				(u8 *) &v, 2, NULL, 0);
>> > +}
>> > +EXPORT_SYMBOL(mma9551_write_config_word);
>> > +
>> > +/**
>> >   * mma9551_read_status_word() - read 1 status word
>> >   * @client:	I2C client
>> >   * @app_id:	Application ID
>> > @@ -306,6 +369,107 @@ int mma9551_read_status_word(struct
>i2c_client *client, u8 app_id,
>> >  EXPORT_SYMBOL(mma9551_read_status_word);
>> >
>> >  /**
>> > + * mma9551_read_config_words() - read multiple config words
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @len:	Length of array to read in bytes
>> > + * @val:	Array of words to read
>> > + *
>> > + * Read multiple configuration registers (word-sized registers).
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_read_config_words(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u8 len, u16 *buf)
>> > +{
>> > +	int ret, i;
>> > +	int len_words = len / sizeof(u16);
>> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
>> > +
>> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
>> > +			       reg, NULL, 0, (u8 *) be_buf, len);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	for (i = 0; i < len_words; i++)
>> > +		buf[i] = be16_to_cpu(be_buf[i]);
>> > +
>> > +	return 0;
>> > +}
>> > +EXPORT_SYMBOL(mma9551_read_config_words);
>> > +
>> > +/**
>> > + * mma9551_read_status_words() - read multiple status words
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @len:	Length of array to read in bytes
>> > + * @val:	Array of words to read
>> > + *
>> > + * Read multiple status registers (word-sized registers).
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_read_status_words(struct i2c_client *client, u8
>app_id,
>> > +			      u16 reg, u8 len, u16 *buf)
>> > +{
>> > +	int ret, i;
>> > +	int len_words = len / sizeof(u16);
>> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
>> > +
>> > +	ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
>> > +			       reg, NULL, 0, (u8 *) be_buf, len);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	for (i = 0; i < len_words; i++)
>> > +		buf[i] = be16_to_cpu(be_buf[i]);
>> > +
>> > +	return 0;
>> > +}
>> > +EXPORT_SYMBOL(mma9551_read_status_words);
>> > +
>> > +/**
>> > + * mma9551_write_config_words() - write multiple config words
>> > + * @client:	I2C client
>> > + * @app_id:	Application ID
>> > + * @reg:	Application register
>> > + * @len:	Length of array to write in bytes
>> > + * @val:	Array of words to write
>> > + *
>> > + * Write multiple configuration registers (word-sized registers).
>> > + *
>> > + * Locking note: This function must be called with the device lock
>held.
>> > + * Locking is not handled inside the function. Callers should
>ensure they
>> > + * serialize access to the HW.
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_write_config_words(struct i2c_client *client, u8
>app_id,
>> > +			       u16 reg, u8 len, u16 *buf)
>> > +{
>> > +	int i;
>> > +	int len_words = len / sizeof(u16);
>> > +	__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
>> > +
>> > +	for (i = 0; i < len_words; i++)
>> > +		be_buf[i] = cpu_to_be16(buf[i]);
>> > +
>> > +	return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
>> > +				reg, (u8 *) be_buf, len, NULL, 0);
>> > +}
>> > +EXPORT_SYMBOL(mma9551_write_config_words);
>> > +
>> > +/**
>> >   * mma9551_update_config_bits() - update bits in register
>> >   * @client:	I2C client
>> >   * @app_id:	Application ID
>> > @@ -609,6 +773,25 @@ int mma9551_read_accel_scale(int *val, int
>*val2)
>> >  }
>> >  EXPORT_SYMBOL(mma9551_read_accel_scale);
>> >
>> > +/**
>> > + * mma9551_app_reset() - reset application
>> > + * @client:	I2C client
>> > + * @app_mask:	Application to reset
>> > + *
>> > + * Reset the given application (using the Reset/Suspend/Clear
>> > + * Control Application)
>> > + *
>> > + * Returns: 0 on success, negative value on failure.
>> > + */
>> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
>> > +{
>> > +	return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
>> > +					 MMA9551_RSC_RESET +
>> > +					 MMA9551_RSC_OFFSET(app_mask),
>> > +					 MMA9551_RSC_VAL(app_mask));
>> > +}
>> > +EXPORT_SYMBOL(mma9551_app_reset);
>> > +
>> >  MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
>> >  MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
>> >  MODULE_LICENSE("GPL v2");
>> > diff --git a/drivers/iio/accel/mma9551_core.h
>b/drivers/iio/accel/mma9551_core.h
>> > index e6efd02..edaa56b 100644
>> > --- a/drivers/iio/accel/mma9551_core.h
>> > +++ b/drivers/iio/accel/mma9551_core.h
>> > @@ -21,9 +21,13 @@
>> >  #define MMA9551_APPID_AFE		0x06
>> >  #define MMA9551_APPID_TILT		0x0B
>> >  #define MMA9551_APPID_SLEEP_WAKE	0x12
>> > -#define MMA9551_APPID_RESET		0x17
>> > +#define MMA9551_APPID_PEDOMETER	        0x15
>> > +#define MMA9551_APPID_RCS		0x17
>> >  #define MMA9551_APPID_NONE		0xff
>> >
>> > +/* Reset/Suspend/Clear application app masks */
>> > +#define MMA9551_RSC_PED			BIT(21)
>> > +
>> >  #define MMA9551_AUTO_SUSPEND_DELAY_MS	2000
>> >
>> >  enum mma9551_gpio_pin {
>> > @@ -48,8 +52,18 @@ int mma9551_write_config_byte(struct i2c_client
>*client, u8 app_id,
>> >  			      u16 reg, u8 val);
>> >  int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
>> >  			     u16 reg, u8 *val);
>> > +int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
>> > +			    u16 reg, u16 *val);
>> > +int mma9551_write_config_word(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u16 val);
>> >  int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
>> >  			     u16 reg, u16 *val);
>> > +int mma9551_read_config_words(struct i2c_client *client, u8
>app_id,
>> > +			     u16 reg, u8 len, u16 *buf);
>> > +int mma9551_read_status_words(struct i2c_client *client, u8
>app_id,
>> > +			      u16 reg, u8 len, u16 *buf);
>> > +int mma9551_write_config_words(struct i2c_client *client, u8
>app_id,
>> > +			       u16 reg, u8 len, u16 *buf);
>> >  int mma9551_update_config_bits(struct i2c_client *client, u8
>app_id,
>> >  			       u16 reg, u8 mask, u8 val);
>> >  int mma9551_gpio_config(struct i2c_client *client, enum
>mma9551_gpio_pin pin,
>> > @@ -62,5 +76,6 @@ int mma9551_read_accel_chan(struct i2c_client
>*client,
>> >  			    const struct iio_chan_spec *chan,
>> >  			    int *val, int *val2);
>> >  int mma9551_read_accel_scale(int *val, int *val2);
>> > +int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
>> >
>> >  #endif /* _MMA9551_CORE_H_ */
>> > diff --git a/drivers/iio/accel/mma9553.c
>b/drivers/iio/accel/mma9553.c
>> > new file mode 100644
>> > index 0000000..ef6a6e3
>> > --- /dev/null
>> > +++ b/drivers/iio/accel/mma9553.c
>> > @@ -0,0 +1,1321 @@
>> > +/*
>> > + * Freescale MMA9553L Intelligent Pedometer driver
>> > + * Copyright (c) 2014, Intel Corporation.
>> > + *
>> > + * 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.
>> > + *
>> > + * This program is distributed in the hope 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/module.h>
>> > +#include <linux/i2c.h>
>> > +#include <linux/interrupt.h>
>> > +#include <linux/slab.h>
>> > +#include <linux/acpi.h>
>> > +#include <linux/gpio/consumer.h>
>> > +#include <linux/iio/iio.h>
>> > +#include <linux/iio/sysfs.h>
>> > +#include <linux/iio/events.h>
>> > +#include <linux/pm_runtime.h>
>> > +#include "mma9551_core.h"
>> > +
>> > +#define MMA9553_DRV_NAME			"mma9553"
>> > +#define MMA9553_IRQ_NAME			"mma9553_event"
>> > +#define MMA9553_GPIO_NAME			"mma9553_int"
>> > +
>> > +/* Pedometer configuration registers (R/W) */
>> > +#define MMA9553_CONF_SLEEPMIN		0x00
>> > +#define MMA9553_CONF_SLEEPMAX		0x02
>> > +#define MMA9553_CONF_SLEEPTHD		0x04
>> > +#define MMA9553_CONF_WORD		GENMASK(15, 0)
>> > +
>> > +#define MMA9553_CONF_CONF_STEPLEN	0x06
>> > +#define MMA9553_CONF_CONFIG		BIT(15)
>> > +#define MMA9553_CONF_ACT_DBCNTM		BIT(14)
>> > +#define MMA9553_CONF_SLP_DBCNTM		BIT(13)
>> > +#define MMA9553_CONF_STEPLEN		GENMASK(7, 0)
>> > +
>> > +#define MMA9553_CONF_HEIGHT_WEIGHT	0x08
>> Some of this naming is less than clear.  Which ones
>> are the register addresses and which the internal masks?
>> (obvious really, but names should reflect this perhaps)
>> 
>That's true. I'll rename them to make it more clear.
>
>> > +#define MMA9553_CONF_HEIGHT		GENMASK(15, 8)
>> > +#define MMA9553_CONF_WEIGHT		GENMASK(7, 0)
>> > +
>> > +#define MMA9553_CONF_FILTER		0x0A
>> > +#define MMA9553_CONF_FILTSTEP		GENMASK(15, 8)
>> > +#define MMA9553_CONF_MALE		BIT(7)
>> > +#define MMA9553_CONF_FILTTIME		GENMASK(6, 0)
>> > +
>> > +#define MMA9553_CONF_SPEED_STEP		0x0C
>> > +#define MMA9553_CONF_SPDPRD		GENMASK(15, 8)
>> > +#define MMA9553_CONF_STEPCOALESCE	GENMASK(7, 0)
>> > +
>> > +#define MMA9553_CONF_ACTTHD		0x0E
>> > +
>> > +/* Pedometer status registers (R-only) */
>> > +#define MMA9553_STATUS			0x00
>> > +#define MMA9553_STATUS_MRGFL		BIT(15)
>> > +#define MMA9553_STATUS_SUSPCHG		BIT(14)
>> > +#define MMA9553_STATUS_STEPCHG		BIT(13)
>> > +#define MMA9553_STATUS_ACTCHG		BIT(12)
>> > +#define MMA9553_STATUS_SUSP		BIT(11)
>> > +#define MMA9553_STATUS_ACTIVITY		(BIT(10) | BIT(9) | BIT(8))
>> > +#define MMA9553_STATUS_VERSION		0x00FF
>> > +
>> > +#define MMA9553_STEPCNT			0x02
>> > +#define MMA9553_DISTANCE		0x04
>> > +#define MMA9553_SPEED			0x06
>> > +#define MMA9553_CALORIES		0x08
>> > +#define MMA9553_SLEEPCNT		0x0A
>> > +
>> > +/* Pedometer events are always mapped to this pin. */
>> > +#define MMA9553_DEFAULT_GPIO_PIN	mma9551_gpio6
>> > +#define MMA9553_DEFAULT_GPIO_POLARITY	0
>> > +
>> > +/* Bitnum used for gpio configuration = bit number in high status
>byte */
>> > +#define STATUS_TO_BITNUM(bit)		(ffs(bit) - 9)
>> > +
>> > +#define MMA9553_DEFAULT_SAMPLE_RATE	30	/* Hz */
>> > +
>> > +/*
>> > + * The internal activity level must be stable for ACTTHD samples
>before
>> > + * ACTIVITY is updated.The ACTIVITY variable contains the current
>activity
>> > + * level and is updated every time a step is detected or once a
>second
>> > + * if there are no steps.
>> > + */
>> > +#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) /
>MMA9553_DEFAULT_SAMPLE_RATE)
>> > +#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) *
>MMA9553_DEFAULT_SAMPLE_RATE)
>> > +
>> > +/*
>> > + * Autonomously suspend pedometer if acceleration vector magnitude
>> > + * is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
>> > + */
>> > +#define MMA9553_DEFAULT_SLEEPMIN	3688	/* 0,9 g */
>> > +#define MMA9553_DEFAULT_SLEEPMAX	4508	/* 1,1 g */
>> > +#define MMA9553_DEFAULT_SLEEPTHD	(MMA9553_DEFAULT_SAMPLE_RATE *
>30)
>> > +
>> > +#define MMA9553_CONFIG_RETRIES		2
>> > +
>> > +/* Status register - activity field  */
>> > +enum activity_level {
>> > +	ACTIVITY_UNKNOWN,
>> > +	ACTIVITY_REST,
>> > +	ACTIVITY_WALKING,
>> > +	ACTIVITY_JOGGING,
>> > +	ACTIVITY_RUNNING,
>> > +};
>> > +
>> > +static struct mma9553_event_info {
>> > +	enum iio_chan_type type;
>> > +	enum iio_modifier mod;
>> > +	enum iio_event_direction dir;
>> > +} mma9553_events_info[] = {
>> > +	{
>> > +		.type = IIO_STEPS,
>> > +		.mod = IIO_NO_MOD,
>> > +		.dir = IIO_EV_DIR_NONE,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_STILL,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_STILL,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_WALKING,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_WALKING,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_JOGGING,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_JOGGING,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_RUNNING,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +	},
>> > +	{
>> > +		.type = IIO_ACTIVITY,
>> > +		.mod = IIO_MOD_RUNNING,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +	},
>> > +};
>> > +
>> > +#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
>> > +
>> > +struct mma9553_event {
>> > +	struct mma9553_event_info *info;
>> > +	bool enabled;
>> > +};
>> > +
>> > +struct mma9553_conf_regs {
>> > +	u16 sleepmin;
>> > +	u16 sleepmax;
>> > +	u16 sleepthd;
>> > +	u16 config;
>> > +	u16 height_weight;
>> > +	u16 filter;
>> > +	u16 speed_step;
>> > +	u16 actthd;
>> > +} __packed;
>> > +
>> > +struct mma9553_data {
>> > +	struct i2c_client *client;
>> > +	struct mutex mutex;
>> > +	struct mma9553_conf_regs conf;
>> > +	struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
>> > +	int num_events;
>> > +	u8 gpio_bitnum;
>> > +	/*
>> > +	 * This is used for all features that depend on step count:
>> > +	 * step count, distance, speed, calories.
>> > +	 */
>> > +	bool stepcnt_enabled;
>> > +	u16 stepcnt;
>> > +	u8 activity;
>> > +	s64 timestamp;
>> > +};
>> > +
>> > +static u8 mma9553_get_bits(u16 val, u16 mask)
>> > +{
>> > +	return (val & mask) >> (ffs(mask) - 1);
>> > +}
>> > +
>> > +static u16 mma9553_set_bits(u16 current_val, u16 val, u16 mask)
>> > +{
>> > +	return (current_val & ~mask) | (val << (ffs(mask) - 1));
>> > +}
>> > +
>> > +static enum iio_modifier mma9553_activity_to_mod(enum
>activity_level activity)
>> > +{
>> > +	switch (activity) {
>> > +	case ACTIVITY_RUNNING:
>> > +		return IIO_MOD_RUNNING;
>> > +	case ACTIVITY_JOGGING:
>> > +		return IIO_MOD_JOGGING;
>> > +	case ACTIVITY_WALKING:
>> > +		return IIO_MOD_WALKING;
>> > +	case ACTIVITY_REST:
>> > +		return IIO_MOD_STILL;
>> > +	case ACTIVITY_UNKNOWN:
>> > +	default:
>> > +		return IIO_NO_MOD;
>> > +	}
>> > +}
>> > +
>> > +static void mma9553_init_events(struct mma9553_data *data)
>> > +{
>> > +	int i;
>> > +
>> > +	data->num_events = MMA9553_EVENTS_INFO_SIZE;
>> > +	for (i = 0; i < data->num_events; i++) {
>> > +		data->events[i].info = &mma9553_events_info[i];
>> > +		data->events[i].enabled = false;
>> > +	}
>> > +}
>> > +
>> > +static struct mma9553_event *mma9553_get_event(struct mma9553_data
>*data,
>> > +					       enum iio_chan_type type,
>> > +					       enum iio_modifier mod,
>> > +					       enum iio_event_direction dir)
>> > +{
>> > +	int i;
>> > +
>> > +	for (i = 0; i < data->num_events; i++)
>> > +		if (data->events[i].info->type == type &&
>> > +		    data->events[i].info->mod == mod &&
>> > +		    data->events[i].info->dir == dir)
>> > +			return &data->events[i];
>> > +
>> > +	return NULL;
>> > +}
>> > +
>> > +static bool mma9553_is_any_event_enabled(struct mma9553_data
>*data,
>> > +					 bool check_type,
>> > +					 enum iio_chan_type type)
>> > +{
>> > +	int i;
>> > +
>> > +	for (i = 0; i < data->num_events; i++)
>> > +		if ((check_type && data->events[i].info->type == type &&
>> > +		     data->events[i].enabled) ||
>> > +		     (!check_type && data->events[i].enabled))
>> > +			return true;
>> > +
>> > +	return false;
>> > +}
>> > +
>> > +static int mma9553_set_config(struct mma9553_data *data, u16 reg,
>> > +			      u16 *p_reg_val, u16 val, u16 mask)
>> > +{
>> > +	int ret, retries;
>> > +	u16 reg_val, config;
>> > +
>> > +	reg_val = *p_reg_val;
>> > +	if (val == mma9553_get_bits(reg_val, mask))
>> > +		return 0;
>> > +
>> > +	reg_val = mma9553_set_bits(reg_val, val, mask);
>> > +	ret = mma9551_write_config_word(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +					reg, reg_val);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"error writing config register 0x%x\n", reg);
>> > +		return ret;
>> > +	}
>> > +
>> > +	*p_reg_val = reg_val;
>> > +
>> > +	/* Reinitializes the pedometer with current configuration values
>*/
>> > +	config = mma9553_set_bits(data->conf.config, 1,
>MMA9553_CONF_CONFIG);
>> > +
>> > +	ret = mma9551_write_config_word(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +					MMA9553_CONF_CONF_STEPLEN, config);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"error writing config register 0x%x\n",
>> > +			MMA9553_CONF_CONF_STEPLEN);
>> > +		return ret;
>> > +	}
>> > +
>> > +	retries = MMA9553_CONFIG_RETRIES;
>> > +	do {
>> > +		mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
>> > +		ret = mma9551_read_config_word(data->client,
>> > +					       MMA9551_APPID_PEDOMETER,
>> > +					       MMA9553_CONF_CONF_STEPLEN,
>> > +					       &config);
>> > +		if (ret < 0)
>> > +			return ret;
>> > +	} while (mma9553_get_bits(config, MMA9553_CONF_CONFIG) &&
>> > +		 --retries > 0);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_read_activity_stepcnt(struct mma9553_data
>*data,
>> > +					 u8 *activity, u16 *stepcnt)
>> > +{
>> > +	u32 status_stepcnt;
>> > +	u16 status;
>> > +	int ret;
>> > +
>> > +	ret = mma9551_read_status_words(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +					MMA9553_STATUS, sizeof(u32),
>> > +					(u16 *) &status_stepcnt);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"error reading status and stepcnt\n");
>> > +		return ret;
>> > +	}
>> > +
>> > +	status = status_stepcnt & MMA9553_CONF_WORD;
>> > +	*activity = mma9553_get_bits(status, MMA9553_STATUS_ACTIVITY);
>> > +	*stepcnt = status_stepcnt >> 16;
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_conf_gpio(struct mma9553_data *data)
>> > +{
>> > +	u8 bitnum = 0, appid = MMA9551_APPID_PEDOMETER;
>> > +	int ret;
>> > +	struct mma9553_event *ev_step_detect;
>> > +	bool activity_enabled;
>> > +
>> > +	activity_enabled =
>> > +	    mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
>> > +	ev_step_detect =
>> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
>IIO_EV_DIR_NONE);
>> > +
>> > +	/*
>> > +	 * If both step detector and activity are enabled, use the MRGFL
>bit.
>> > +	 * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG
>flags.
>> > +	 */
>> > +	if (activity_enabled && ev_step_detect->enabled)
>> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_MRGFL);
>> > +	else if (ev_step_detect->enabled)
>> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_STEPCHG);
>> > +	else if (activity_enabled)
>> > +		bitnum = STATUS_TO_BITNUM(MMA9553_STATUS_ACTCHG);
>> > +	else			/* Reset */
>> > +		appid = MMA9551_APPID_NONE;
>> > +
>> > +	if (data->gpio_bitnum == bitnum)
>> > +		return 0;
>> > +
>> > +	/* Save initial values for activity and stepcnt */
>> > +	if (activity_enabled || ev_step_detect->enabled)
>> > +		mma9553_read_activity_stepcnt(data, &data->activity,
>> > +					      &data->stepcnt);
>> > +
>> > +	ret = mma9551_gpio_config(data->client,
>> > +				  MMA9553_DEFAULT_GPIO_PIN,
>> > +				  appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +	data->gpio_bitnum = bitnum;
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_init(struct mma9553_data *data)
>> > +{
>> > +	int ret;
>> > +
>> > +	ret = mma9551_read_version(data->client);
>> > +	if (ret)
>> > +		return ret;
>> > +
>> > +	/*
>> > +	 * Read all the pedometer configuration registers. This is used
>as
>> > +	 * a device identification command to differentiate the MMA9553L
>> > +	 * from the MMA9550L.
>> > +	 */
>> > +	ret =
>> > +	    mma9551_read_config_words(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +				      MMA9553_CONF_SLEEPMIN,
>> > +				      sizeof(data->conf), (u16 *) &data->conf);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"device is not MMA9553L: failed to read cfg regs\n");
>> > +		return ret;
>> > +	}
>> > +
>> > +
>> > +	/* Reset gpio */
>> > +	data->gpio_bitnum = -1;
>> > +	ret = mma9553_conf_gpio(data);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	ret = mma9551_app_reset(data->client, MMA9551_RSC_PED);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	/* Init config registers */
>> > +	data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
>> > +	data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
>> > +	data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
>> > +	data->conf.config =
>> > +	    mma9553_set_bits(data->conf.config, 1, MMA9553_CONF_CONFIG);
>> > +	/*
>> > +	 * Clear the activity debounce counter when the activity level
>changes,
>> > +	 * so that the confidence level applies for any activity level.
>> > +	 */
>> > +	data->conf.config =
>> > +	    mma9553_set_bits(data->conf.config, 1,
>MMA9553_CONF_ACT_DBCNTM);
>> > +	ret =
>> > +	    mma9551_write_config_words(data->client,
>MMA9551_APPID_PEDOMETER,
>> > +				       MMA9553_CONF_SLEEPMIN,
>> > +				       sizeof(data->conf), (u16 *) &data->conf);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev,
>> > +			"failed to write configuration registers\n");
>> > +		return ret;
>> > +	}
>> > +
>> > +	return mma9551_set_device_state(data->client, true);
>> > +}
>> > +
>> > +static int mma9553_read_raw(struct iio_dev *indio_dev,
>> > +			    struct iio_chan_spec const *chan,
>> > +			    int *val, int *val2, long mask)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +	u16 tmp;
>> > +	u8 activity;
>> > +	bool powered_on;
>> > +
>> > +	switch (mask) {
>> > +	case IIO_CHAN_INFO_PROCESSED:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			/*
>> > +			 * The HW only counts steps and other dependent
>> > +			 * parameters (speed, distance, calories, activity)
>> > +			 * if power is on (from enabling an event or the
>> > +			 * step counter */
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_STEPCNT, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_DISTANCE:
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_DISTANCE, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACTIVITY:
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_STATUS, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +
>> > +			activity =
>> > +			    mma9553_get_bits(tmp, MMA9553_STATUS_ACTIVITY);
>> > +
>> > +			/*
>> > +			 * The device does not support confidence value levels,
>> > +			 * so we will always have 100% for current activity and
>> > +			 * 0% for the others.
>> > +			 */
>> > +			if (chan->channel2 == mma9553_activity_to_mod(activity))
>> > +				*val = 100;
>> > +			else
>> > +				*val = 0;
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_RAW:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:	/* m/h */
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_SPEED, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ENERGY:	/* Cal or kcal */
>> > +			powered_on =
>> > +			    mma9553_is_any_event_enabled(data, false, 0) ||
>> > +			    data->stepcnt_enabled;
>> > +			if (!powered_on) {
>> > +				dev_err(&data->client->dev,
>> > +					"No channels enabled\n");
>> > +				return -EINVAL;
>> > +			}
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_status_word(data->client,
>> > +						       MMA9551_APPID_PEDOMETER,
>> > +						       MMA9553_CALORIES, &tmp);
>> > +			mutex_unlock(&data->mutex);
>> > +			if (ret < 0)
>> > +				return ret;
>> > +			*val = tmp;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACCEL:
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9551_read_accel_chan(data->client,
>> > +						      chan, val, val2);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_SCALE:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:	/* m/h to m/s */
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			*val = 0;
>> > +			*val2 = 277;	/* 0.000277 */
>> > +			return IIO_VAL_INT_PLUS_MICRO;
>> > +		case IIO_ENERGY:	/* Cal or kcal to J */
>> > +			*val = 4184;
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACCEL:
>> > +			return mma9551_read_accel_scale(val, val2);
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_ENABLE:
>> > +		*val = data->stepcnt_enabled;
>> > +		return IIO_VAL_INT;
>> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
>> > +		*val = mma9553_get_bits(data->conf.height_weight,
>> > +					MMA9553_CONF_HEIGHT);
>> > +		return IIO_VAL_INT;
>> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
>> > +		*val = mma9553_get_bits(data->conf.height_weight,
>> > +					MMA9553_CONF_WEIGHT);
>> > +		return IIO_VAL_INT;
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			*val = mma9553_get_bits(data->conf.filter,
>> > +						MMA9553_CONF_FILTSTEP);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			*val = mma9553_get_bits(data->conf.filter,
>> > +						MMA9553_CONF_FILTTIME);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_INT_TIME:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			*val = mma9553_get_bits(data->conf.speed_step,
>> > +						MMA9553_CONF_SPDPRD);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_write_raw(struct iio_dev *indio_dev,
>> > +			     struct iio_chan_spec const *chan,
>> > +			     int val, int val2, long mask)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	switch (mask) {
>> > +	case IIO_CHAN_INFO_ENABLE:
>> > +		if (data->stepcnt_enabled == !!val)
>> > +			return 0;
>> > +		mutex_lock(&data->mutex);
>> > +		ret = mma9551_set_power_state(data->client, val);
>> > +		if (ret < 0) {
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		}
>> > +		data->stepcnt_enabled = val;
>> > +		mutex_unlock(&data->mutex);
>> > +		return 0;
>> > +	case IIO_CHAN_INFO_CALIBHEIGHT:
>> > +		if (val < 0 || val > 255)
>> > +			return -EINVAL;
>> We are being rather inconsistent in having calibheight in cm rather
>than
>> m (as per distance etc). I wonder if now is the time to change it
>whilst
>> we have few users...
>
>Sure, I'll change it.
>> > +		mutex_lock(&data->mutex);
>> > +		ret = mma9553_set_config(data,
>> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
>> > +					 &data->conf.height_weight,
>> > +					 val, MMA9553_CONF_HEIGHT);
>> > +		mutex_unlock(&data->mutex);
>> > +		return ret;
>> > +	case IIO_CHAN_INFO_CALIBWEIGHT:
>> > +		if (val < 0 || val > 255)
>> > +			return -EINVAL;
>> > +		mutex_lock(&data->mutex);
>> > +		ret = mma9553_set_config(data,
>> > +					 MMA9553_CONF_HEIGHT_WEIGHT,
>> > +					 &data->conf.height_weight,
>> > +					 val, MMA9553_CONF_WEIGHT);
>> > +		mutex_unlock(&data->mutex);
>> > +		return ret;
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			/*
>> > +			 * Set to 0 to disable step filtering. If the value
>> > +			 * specified is greater than 6, then 6 will be used.
>> > +			 */
>> > +			if (val < 0)
>> > +				return -EINVAL;
>> > +			if (val > 6)
>> > +				val = 6;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
>> > +						 &data->conf.filter, val,
>> > +						 MMA9553_CONF_FILTSTEP);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			if (val < 0 || val > 127)
>> > +				return -EINVAL;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
>> > +						 &data->conf.filter, val,
>> > +						 MMA9553_CONF_FILTTIME);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_CHAN_INFO_INT_TIME:
>> > +		switch (chan->type) {
>> > +		case IIO_VELOCITY:
>> > +			if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
>> > +				return -EINVAL;
>> > +			/*
>> > +			 * If set to a value greater than 5, then 5 will be
>> > +			 * used. Warning: Do not set SPDPRD to 0 or 1 as
>> > +			 * this may cause undesirable behavior.
>> > +			 */
>> > +			if (val < 2)
>> > +				return -EINVAL;
>> > +			if (val > 5)
>> > +				val = 5;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
>> > +						 &data->conf.speed_step, val,
>> > +						 MMA9553_CONF_SPDPRD);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
>> > +	struct mma9553_event *event;
>> > +
>> > +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
>> > +	if (!event)
>> > +		return -EINVAL;
>> > +
>> > +	return event->enabled;
>> > +}
>> > +
>> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
>> > +	struct mma9553_event *event;
>> > +	int ret;
>> > +
>> > +	event = mma9553_get_event(data, chan->type, chan->channel2, dir);
>> > +	if (!event)
>> > +		return -EINVAL;
>> > +
>> > +	if (event->enabled == state)
>> > +		return 0;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +
>> > +	ret = mma9551_set_power_state(data->client, state);
>> > +	if (ret < 0)
>> > +		goto err_out;
>> > +	event->enabled = state;
>> > +
>> > +	ret = mma9553_conf_gpio(data);
>> > +	if (ret < 0)
>> > +		goto err_conf_gpio;
>> > +
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +
>> > +err_conf_gpio:
>> > +	if (state) {
>> > +		event->enabled = false;
>> > +		mma9551_set_power_state(data->client, false);
>> > +	}
>> > +err_out:
>> > +	mutex_unlock(&data->mutex);
>> > +	return ret;
>> > +}
>> > +
>> > +static int mma9553_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)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +
>> > +	*val2 = 0;
>> > +	switch (info) {
>> > +	case IIO_EV_INFO_VALUE:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			*val = mma9553_get_bits(data->conf.speed_step,
>> > +						MMA9553_CONF_STEPCOALESCE);
>> > +			return IIO_VAL_INT;
>> > +		case IIO_ACTIVITY:
>> > +			/*
>> > +			 * The device does not support confidence value levels.
>> > +			 * We set an average of 50%.
>> > +			 */
>> > +			*val = 50;
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_EV_INFO_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_ACTIVITY:
>> > +			*val = MMA9553_ACTIVITY_THD_TO_SEC(data->conf.actthd);
>> > +			return IIO_VAL_INT;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	switch (info) {
>> > +	case IIO_EV_INFO_VALUE:
>> > +		switch (chan->type) {
>> > +		case IIO_STEPS:
>> > +			if (val < 0 || val > 255)
>> > +				return -EINVAL;
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_SPEED_STEP,
>> > +						 &data->conf.speed_step, val,
>> > +						 MMA9553_CONF_STEPCOALESCE);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	case IIO_EV_INFO_PERIOD:
>> > +		switch (chan->type) {
>> > +		case IIO_ACTIVITY:
>> > +			mutex_lock(&data->mutex);
>> > +			ret = mma9553_set_config(data, MMA9553_CONF_ACTTHD,
>> > +						 &data->conf.actthd,
>> > +						 MMA9553_ACTIVITY_SEC_TO_THD
>> > +						 (val), MMA9553_CONF_WORD);
>> > +			mutex_unlock(&data->mutex);
>> > +			return ret;
>> > +		default:
>> > +			return -EINVAL;
>> > +		}
>> > +	default:
>> > +		return -EINVAL;
>> > +	}
>> > +}
>> > +
>> > +static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
>> > +					const struct iio_chan_spec *chan)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	u8 gender;
>> > +
>> > +	gender = mma9553_get_bits(data->conf.filter, MMA9553_CONF_MALE);
>> > +	/*
>> > +	 * HW expects 0 for female and 1 for male,
>> > +	 * while iio index is 0 for male and 1 for female
>> > +	 */
>> > +	return !gender;
>> > +}
>> > +
>> > +static int mma9553_set_calibgender_mode(struct iio_dev *indio_dev,
>> > +					const struct iio_chan_spec *chan,
>> > +					unsigned int mode)
>> > +{
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	u8 gender = !mode;
>> > +	int ret;
>> > +
>> > +	if ((mode != 0) && (mode != 1))
>> > +		return -EINVAL;
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9553_set_config(data, MMA9553_CONF_FILTER,
>&data->conf.filter,
>> > +				 gender, MMA9553_CONF_MALE);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +}
>> > +
>> > +static const struct iio_event_spec mma9553_step_event = {
>> > +	.type = IIO_EV_TYPE_CHANGE,
>> > +	.dir = IIO_EV_DIR_NONE,
>> > +	.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
>BIT(IIO_EV_INFO_VALUE),
>> > +};
>> > +
>> > +static const struct iio_event_spec mma9553_activity_events[] = {
>> > +	{
>> > +		.type = IIO_EV_TYPE_THRESH,
>> > +		.dir = IIO_EV_DIR_RISING,
>> > +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
>> > +				 BIT(IIO_EV_INFO_VALUE) |
>> > +				 BIT(IIO_EV_INFO_PERIOD),
>> > +	 },
>> > +	{
>> > +		.type = IIO_EV_TYPE_THRESH,
>> > +		.dir = IIO_EV_DIR_FALLING,
>> > +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
>> > +				 BIT(IIO_EV_INFO_VALUE) |
>> > +				 BIT(IIO_EV_INFO_PERIOD),
>> > +	},
>> > +};
>> > +
>> > +static const char * const calibgender_modes[] = { "male", "female"
>};
>> > +
>> > +static const struct iio_enum mma9553_calibgender_enum = {
>> > +	.items = calibgender_modes,
>> > +	.num_items = ARRAY_SIZE(calibgender_modes),
>> > +	.get = mma9553_get_calibgender_mode,
>> > +	.set = mma9553_set_calibgender_mode,
>> > +};
>> > +
>> > +static const struct iio_chan_spec_ext_info mma9553_ext_info[] = {
>> > +	IIO_CALIBGENDER_EXT_INFO(IIO_SHARED_BY_TYPE,
>&mma9553_calibgender_enum),
>> > +	{},
>> > +};
>> > +
>> > +#define MMA9553_PEDOMETER_CHANNEL(_type, _mask) {		\
>> > +	.type = _type,						\
>> > +	.info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE)      |	\
>> > +			      BIT(IIO_CHAN_INFO_CALIBHEIGHT) |	\
>> > +			      _mask,				\
>> > +	.ext_info = mma9553_ext_info,				\
>> > +}
>> > +
>> > +#define MMA9553_ACTIVITY_CHANNEL(_chan2) {				\
>> > +	.type = IIO_ACTIVITY,						\
>> > +	.modified = 1,							\
>> > +	.channel2 = _chan2,						\
>> > +	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),		\
>> > +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),	\
>> > +	.event_spec = mma9553_activity_events,				\
>> > +	.num_event_specs = ARRAY_SIZE(mma9553_activity_events),		\
>> > +	.ext_info = mma9553_ext_info,					\
>> > +}
>> > +
>> > +static const struct iio_chan_spec mma9553_channels[] = {
>> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
>> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
>> > +	MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
>> > +
>> > +	{
>> > +		.type = IIO_STEPS,
>> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
>> > +				     BIT(IIO_CHAN_INFO_ENABLE) |
>> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH) |
>> > +				     BIT(IIO_CHAN_INFO_FILTER_OUTLIERS_PERIOD),
>> > +		.event_spec = &mma9553_step_event,
>> > +		.num_event_specs = 1,
>> > +	},
>> > +
>> > +	MMA9553_PEDOMETER_CHANNEL(IIO_DISTANCE,
>BIT(IIO_CHAN_INFO_PROCESSED)),
>> > +	{
>> > +		.type = IIO_VELOCITY,
>> > +		.modified = 1,
>> > +		.channel2 = IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
>> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> > +				      BIT(IIO_CHAN_INFO_SCALE) |
>> > +				      BIT(IIO_CHAN_INFO_INT_TIME) |
>> > +				      BIT(IIO_CHAN_INFO_ENABLE),
>> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),
>> > +		.ext_info = mma9553_ext_info,
>> > +	},
>> > +	MMA9553_PEDOMETER_CHANNEL(IIO_ENERGY, BIT(IIO_CHAN_INFO_RAW) |
>> > +				  BIT(IIO_CHAN_INFO_SCALE) |
>> > +				  BIT(IIO_CHAN_INFO_CALIBWEIGHT)),
>> > +
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_JOGGING),
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
>> > +	MMA9553_ACTIVITY_CHANNEL(IIO_MOD_STILL),
>> > +};
>> > +
>> > +static const struct iio_info mma9553_info = {
>> > +	.driver_module = THIS_MODULE,
>> > +	.read_raw = mma9553_read_raw,
>> > +	.write_raw = mma9553_write_raw,
>> > +	.read_event_config = mma9553_read_event_config,
>> > +	.write_event_config = mma9553_write_event_config,
>> > +	.read_event_value = mma9553_read_event_value,
>> > +	.write_event_value = mma9553_write_event_value,
>> > +};
>> > +
>> > +static irqreturn_t mma9553_irq_handler(int irq, void *private)
>> > +{
>> > +	struct iio_dev *indio_dev = private;
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +
>> > +	data->timestamp = iio_get_time_ns();
>> > +	/*
>> > +	 * Since we only configure the interrupt pin when an
>> > +	 * event is enabled, we are sure we have at least
>> > +	 * one event enabled at this point.
>> > +	 */
>> > +	return IRQ_WAKE_THREAD;
>> > +}
>> > +
>> > +static irqreturn_t mma9553_event_handler(int irq, void *private)
>> > +{
>> > +	struct iio_dev *indio_dev = private;
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	u16 stepcnt;
>> > +	u8 activity;
>> > +	struct mma9553_event *ev_activity, *ev_prev_activity,
>*ev_step_detect;
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9553_read_activity_stepcnt(data, &activity, &stepcnt);
>> > +	if (ret < 0) {
>> > +		mutex_unlock(&data->mutex);
>> > +		return IRQ_HANDLED;
>> > +	}
>> > +
>> > +	ev_prev_activity =
>> > +	    mma9553_get_event(data, IIO_ACTIVITY,
>> > +			      mma9553_activity_to_mod(data->activity),
>> > +			      IIO_EV_DIR_FALLING);
>> > +	ev_activity =
>> > +	    mma9553_get_event(data, IIO_ACTIVITY,
>> > +			      mma9553_activity_to_mod(activity),
>> > +			      IIO_EV_DIR_RISING);
>> > +	ev_step_detect =
>> > +	    mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
>IIO_EV_DIR_NONE);
>> > +
>> > +	if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
>> > +		data->stepcnt = stepcnt;
>> > +		iio_push_event(indio_dev,
>> > +			       IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
>> > +			       IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
>> > +			       data->timestamp);
>> > +	}
>> > +
>> > +	if (activity != data->activity) {
>> > +		data->activity = activity;
>> > +		/* ev_activity can be NULL if activity == ACTIVITY_UNKNOWN */
>> > +		if (ev_prev_activity && ev_prev_activity->enabled)
>> > +			iio_push_event(indio_dev,
>> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
>> > +				       ev_prev_activity->info->mod,
>> > +				       IIO_EV_DIR_FALLING,
>> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
>> > +				       data->timestamp);
>> > +
>> > +		if (ev_activity && ev_activity->enabled)
>> > +			iio_push_event(indio_dev,
>> > +				       IIO_EVENT_CODE(IIO_ACTIVITY, 0,
>> > +				       ev_activity->info->mod,
>> > +				       IIO_EV_DIR_RISING,
>> > +				       IIO_EV_TYPE_THRESH, 0, 0, 0),
>> > +				       data->timestamp);
>> > +	}
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return IRQ_HANDLED;
>> > +}
>> > +
>> > +static int mma9553_gpio_probe(struct i2c_client *client)
>> > +{
>> > +	struct device *dev;
>> > +	struct gpio_desc *gpio;
>> > +	int ret;
>> > +
>> > +	if (!client)
>> > +		return -EINVAL;
>> > +
>> > +	dev = &client->dev;
>> > +
>> > +	/* data ready gpio interrupt pin */
>> > +	gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
>> > +	if (IS_ERR(gpio)) {
>> > +		dev_err(dev, "acpi gpio get index failed\n");
>> > +		return PTR_ERR(gpio);
>> > +	}
>> > +
>> > +	ret = gpiod_direction_input(gpio);
>> > +	if (ret)
>> > +		return ret;
>> > +
>> > +	ret = gpiod_to_irq(gpio);
>> > +
>> > +	dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio),
>ret);
>> > +
>> > +	return ret;
>> > +}
>> > +
>> > +static const char *mma9553_match_acpi_device(struct device *dev)
>> > +{
>> > +	const struct acpi_device_id *id;
>> > +
>> > +	id = acpi_match_device(dev->driver->acpi_match_table, dev);
>> > +	if (!id)
>> > +		return NULL;
>> > +
>> > +	return dev_name(dev);
>> > +}
>> > +
>> > +static int mma9553_probe(struct i2c_client *client,
>> > +			 const struct i2c_device_id *id)
>> > +{
>> > +	struct mma9553_data *data;
>> > +	struct iio_dev *indio_dev;
>> > +	const char *name = NULL;
>> > +	int ret;
>> > +
>> > +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
>> > +	if (!indio_dev)
>> > +		return -ENOMEM;
>> > +
>> > +	data = iio_priv(indio_dev);
>> > +	i2c_set_clientdata(client, indio_dev);
>> > +	data->client = client;
>> > +
>> > +	if (id)
>> > +		name = id->name;
>> > +	else if (ACPI_HANDLE(&client->dev))
>> > +		name = mma9553_match_acpi_device(&client->dev);
>> > +	else
>> > +		return -ENOSYS;
>> > +
>> > +	mutex_init(&data->mutex);
>> > +	mma9553_init_events(data);
>> > +
>> > +	ret = mma9553_init(data);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	indio_dev->dev.parent = &client->dev;
>> > +	indio_dev->channels = mma9553_channels;
>> > +	indio_dev->num_channels = ARRAY_SIZE(mma9553_channels);
>> > +	indio_dev->name = name;
>> > +	indio_dev->modes = INDIO_DIRECT_MODE;
>> > +	indio_dev->info = &mma9553_info;
>> > +
>> > +	if (client->irq < 0)
>> > +		client->irq = mma9553_gpio_probe(client);
>> > +
>> > +	if (client->irq >= 0) {
>> > +		ret = devm_request_threaded_irq(&client->dev, client->irq,
>> > +						mma9553_irq_handler,
>> > +						mma9553_event_handler,
>> > +						IRQF_TRIGGER_RISING,
>> > +						MMA9553_IRQ_NAME, indio_dev);
>> > +		if (ret < 0) {
>> > +			dev_err(&client->dev, "request irq %d failed\n",
>> > +				client->irq);
>> > +			goto out_poweroff;
>> > +		}
>> > +
>> > +	}
>> > +
>> > +	ret = iio_device_register(indio_dev);
>> > +	if (ret < 0) {
>> > +		dev_err(&client->dev, "unable to register iio device\n");
>> > +		goto out_poweroff;
>> > +	}
>> > +
>> > +	ret = pm_runtime_set_active(&client->dev);
>> > +	if (ret < 0)
>> > +		goto out_iio_unregister;
>> > +
>> > +	pm_runtime_enable(&client->dev);
>> > +	pm_runtime_set_autosuspend_delay(&client->dev,
>> > +					 MMA9551_AUTO_SUSPEND_DELAY_MS);
>> > +	pm_runtime_use_autosuspend(&client->dev);
>> > +
>> > +	dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
>> > +
>> > +	return 0;
>> > +
>> > +out_iio_unregister:
>> > +	iio_device_unregister(indio_dev);
>> > +out_poweroff:
>> > +	mma9551_set_device_state(client, false);
>> > +	return ret;
>> > +}
>> > +
>> > +static int mma9553_remove(struct i2c_client *client)
>> > +{
>> > +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +
>> > +	pm_runtime_disable(&client->dev);
>> > +	pm_runtime_set_suspended(&client->dev);
>> > +	pm_runtime_put_noidle(&client->dev);
>> > +
>> > +	iio_device_unregister(indio_dev);
>> > +	mutex_lock(&data->mutex);
>> > +	mma9551_set_device_state(data->client, false);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +#ifdef CONFIG_PM
>> > +static int mma9553_runtime_suspend(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9551_set_device_state(data->client, false);
>> > +	mutex_unlock(&data->mutex);
>> > +	if (ret < 0) {
>> > +		dev_err(&data->client->dev, "powering off device failed\n");
>> > +		return -EAGAIN;
>> > +	}
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int mma9553_runtime_resume(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	ret = mma9551_set_device_state(data->client, true);
>> > +	if (ret < 0)
>> > +		return ret;
>> > +
>> > +	mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
>> > +
>> > +	return 0;
>> > +}
>> > +#endif
>> > +
>> > +#ifdef CONFIG_PM_SLEEP
>> > +static int mma9553_suspend(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9551_set_device_state(data->client, false);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +}
>> > +
>> > +static int mma9553_resume(struct device *dev)
>> > +{
>> > +	struct iio_dev *indio_dev =
>i2c_get_clientdata(to_i2c_client(dev));
>> > +	struct mma9553_data *data = iio_priv(indio_dev);
>> > +	int ret;
>> > +
>> > +	mutex_lock(&data->mutex);
>> > +	ret = mma9551_set_device_state(data->client, true);
>> > +	mutex_unlock(&data->mutex);
>> > +
>> > +	return ret;
>> > +}
>> > +#endif
>> > +
>> > +static const struct dev_pm_ops mma9553_pm_ops = {
>> > +	SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
>> > +	SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
>> > +			   mma9553_runtime_resume, NULL)
>> > +};
>> > +
>> > +static const struct acpi_device_id mma9553_acpi_match[] = {
>> > +	{"MMA9553", 0},
>> > +	{},
>> > +};
>> > +
>> > +MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
>> > +
>> > +static const struct i2c_device_id mma9553_id[] = {
>> > +	{"mma9553", 0},
>> > +	{},
>> > +};
>> > +
>> > +MODULE_DEVICE_TABLE(i2c, mma9553_id);
>> > +
>> > +static struct i2c_driver mma9553_driver = {
>> > +	.driver = {
>> > +		   .name = MMA9553_DRV_NAME,
>> > +		   .acpi_match_table = ACPI_PTR(mma9553_acpi_match),
>> > +		   .pm = &mma9553_pm_ops,
>> > +		   },
>> > +	.probe = mma9553_probe,
>> > +	.remove = mma9553_remove,
>> > +	.id_table = mma9553_id,
>> > +};
>> > +
>> > +module_i2c_driver(mma9553_driver);
>> > +
>> > +MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
>> > +MODULE_LICENSE("GPL v2");
>> > +MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
>> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>> > index b6b12ac..6770a2f 100644
>> > --- a/include/linux/iio/iio.h
>> > +++ b/include/linux/iio/iio.h
>> > @@ -634,4 +634,15 @@ int iio_str_to_fixpoint(const char *str, int
>fract_mult, int *integer,
>> >   */
>> >  #define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
>> >
>> > +/**
>> > + * IIO_CALIBGENDER_EXT_INFO() - define the gender of the user
>> > + *
>> > + * @_shared:	Whether the attribute is shared between all channels
>> > + * @_e:		Pointer to an iio_enum struct
>> > + *
>> > + */
>> > +#define IIO_CALIBGENDER_EXT_INFO(_shared, _e)			       \
>> > +	IIO_ENUM("calibgender", _shared, _e),			       \
>> > +	IIO_ENUM_AVAILABLE("calibgender", _e)
>> > +
>> For now this is a little obscure to drop into the main header.
>> If we get lots of instances, then perhaps it'll make sense to
>> move it here.  Right now, please keep it in the driver.
>
>
>Ok, will move this to the driver.
>> >  #endif /* _INDUSTRIAL_IO_H_ */
>> >

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* Re: [PATCH v2 01/10] iio: core: Introduce ENERGY channel type
  2015-01-11 19:10 ` [PATCH v2 01/10] iio: core: Introduce ENERGY channel type Irina Tirdea
  2015-01-25 22:58   ` Jonathan Cameron
@ 2015-03-29  0:14   ` Hartmut Knaack
  2015-03-30 11:18     ` Tirdea, Irina
  1 sibling, 1 reply; 33+ messages in thread
From: Hartmut Knaack @ 2015-03-29  0:14 UTC (permalink / raw)
  To: Irina Tirdea, Jonathan Cameron, linux-iio
  Cc: linux-kernel, Vlad Dogaru, Daniel Baluta, Lars-Peter Clausen,
	Peter Meerwald

Irina Tirdea schrieb am 11.01.2015 um 20:10:
> Human activity sensors report the energy burnt by the user.
> One of this devices is Freescale's MMA9553L
> (http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
> that computes the number of calories based on weight and step rate.
> 
> Introduce a new channel type ENERGY to export these values.
> 

Hi Irina,
annoyingly, some people decided to increase the major version number of the
kernel. Could you take care of updating your introduced instances from 3.20
to 4.0?
Thanks,

Hartmut

> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
>  drivers/iio/industrialio-core.c         |  1 +
>  include/linux/iio/types.h               |  1 +
>  3 files changed, 12 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 831db86..3311886 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -282,6 +282,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_current_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
> @@ -1049,6 +1050,15 @@ Description:
>  		For a list of available output power modes read
>  		in_accel_power_mode_available.
>  
> +What:		/sys/.../iio:deviceX/in_energy_input
> +What:		/sys/.../iio:deviceX/in_energy_raw
> +KernelVersion:	3.20
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		This attribute is used to read the energy value reported by the
> +		device (e.g.: human activity sensors report energy burnt by the
> +		user). Units after application of scale are Joules.
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
>  KernelVersion:	3.4.0
>  Contact:	linux-iio@vger.kernel.org
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 69feb91..8d2c9ba 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -72,6 +72,7 @@ static const char * const iio_chan_type_name_spec[] = {
>  	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
>  	[IIO_ACTIVITY] = "activity",
>  	[IIO_STEPS] = "steps",
> +	[IIO_ENERGY] = "energy",
>  };
>  
>  static const char * const iio_modifier_names[] = {
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index 904dcbb..26b8a1c 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -32,6 +32,7 @@ enum iio_chan_type {
>  	IIO_HUMIDITYRELATIVE,
>  	IIO_ACTIVITY,
>  	IIO_STEPS,
> +	IIO_ENERGY,
>  };
>  
>  enum iio_modifier {
> 


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

* RE: [PATCH v2 01/10] iio: core: Introduce ENERGY channel type
  2015-03-29  0:14   ` Hartmut Knaack
@ 2015-03-30 11:18     ` Tirdea, Irina
  0 siblings, 0 replies; 33+ messages in thread
From: Tirdea, Irina @ 2015-03-30 11:18 UTC (permalink / raw)
  To: Hartmut Knaack, Jonathan Cameron, linux-iio
  Cc: linux-kernel, Dogaru, Vlad, Baluta, Daniel, Lars-Peter Clausen,
	Peter Meerwald



> -----Original Message-----
> From: Hartmut Knaack [mailto:knaack.h@gmx.de]
> Sent: 29 March, 2015 2:14
> To: Tirdea, Irina; Jonathan Cameron; linux-iio@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Dogaru, Vlad; Baluta, Daniel; Lars-Peter Clausen; Peter Meerwald
> Subject: Re: [PATCH v2 01/10] iio: core: Introduce ENERGY channel type
> 
> Irina Tirdea schrieb am 11.01.2015 um 20:10:
> > Human activity sensors report the energy burnt by the user.
> > One of this devices is Freescale's MMA9553L
> > (http://www.freescale.com/files/sensors/doc/ref_manual/MMA9553LSWRM.pdf)
> > that computes the number of calories based on weight and step rate.
> >
> > Introduce a new channel type ENERGY to export these values.
> >
> 
> Hi Irina,
> annoyingly, some people decided to increase the major version number of the
> kernel. Could you take care of updating your introduced instances from 3.20
> to 4.0?

Hi Hartmut,

Sure, I'll send a patch to fix all instances .
Thanks for pointing it out to me, I completely forgot about the version change since the patches have been merged before that.

Thanks,
Irina

> Thanks,
> 
> Hartmut
> 
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > ---
> >  Documentation/ABI/testing/sysfs-bus-iio | 10 ++++++++++
> >  drivers/iio/industrialio-core.c         |  1 +
> >  include/linux/iio/types.h               |  1 +
> >  3 files changed, 12 insertions(+)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > index 831db86..3311886 100644
> > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > @@ -282,6 +282,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_current_scale
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
> > +What:		/sys/bus/iio/devices/iio:deviceX/in_energy_scale
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
> >  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
> > @@ -1049,6 +1050,15 @@ Description:
> >  		For a list of available output power modes read
> >  		in_accel_power_mode_available.
> >
> > +What:		/sys/.../iio:deviceX/in_energy_input
> > +What:		/sys/.../iio:deviceX/in_energy_raw
> > +KernelVersion:	3.20
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		This attribute is used to read the energy value reported by the
> > +		device (e.g.: human activity sensors report energy burnt by the
> > +		user). Units after application of scale are Joules.
> > +
> >  What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
> >  KernelVersion:	3.4.0
> >  Contact:	linux-iio@vger.kernel.org
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index 69feb91..8d2c9ba 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -72,6 +72,7 @@ static const char * const iio_chan_type_name_spec[] = {
> >  	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
> >  	[IIO_ACTIVITY] = "activity",
> >  	[IIO_STEPS] = "steps",
> > +	[IIO_ENERGY] = "energy",
> >  };
> >
> >  static const char * const iio_modifier_names[] = {
> > diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> > index 904dcbb..26b8a1c 100644
> > --- a/include/linux/iio/types.h
> > +++ b/include/linux/iio/types.h
> > @@ -32,6 +32,7 @@ enum iio_chan_type {
> >  	IIO_HUMIDITYRELATIVE,
> >  	IIO_ACTIVITY,
> >  	IIO_STEPS,
> > +	IIO_ENERGY,
> >  };
> >
> >  enum iio_modifier {
> >


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

end of thread, other threads:[~2015-03-30 11:18 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-11 19:10 [PATCH v2 00/10] Add MMA9553 driver & PM support for MMA9551 Irina Tirdea
2015-01-11 19:10 ` [PATCH v2 01/10] iio: core: Introduce ENERGY channel type Irina Tirdea
2015-01-25 22:58   ` Jonathan Cameron
2015-03-29  0:14   ` Hartmut Knaack
2015-03-30 11:18     ` Tirdea, Irina
2015-01-11 19:10 ` [PATCH v2 02/10] iio: core: Introduce DISTANCE " Irina Tirdea
2015-01-25 22:59   ` Jonathan Cameron
2015-01-11 19:10 ` [PATCH v2 03/10] iio: core: Introduce IIO_VELOCITY and IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z Irina Tirdea
2015-01-25 23:00   ` Jonathan Cameron
2015-01-11 19:10 ` [PATCH v2 04/10] iio: core: Introduce IO_CHAN_INFO_CALIBWEIGHT Irina Tirdea
2015-01-25 23:01   ` Jonathan Cameron
2015-01-11 19:10 ` [PATCH v2 05/10] iio: core: Introduce CHANGE event type Irina Tirdea
2015-01-25 23:03   ` Jonathan Cameron
2015-01-11 19:10 ` [PATCH v2 06/10] iio: core: Remove IIO_EV_TYPE_INSTANCE Irina Tirdea
2015-01-26 19:04   ` Jonathan Cameron
2015-01-11 19:10 ` [PATCH v2 07/10] iio: core: Introduce IIO_CHAN_INFO_FILTER_OUTLIERS_THRESH and _PERIOD Irina Tirdea
2015-01-25 23:07   ` Jonathan Cameron
2015-01-26 14:40     ` Daniel Baluta
2015-01-26 19:01       ` Jonathan Cameron
2015-01-27 16:20         ` Tirdea, Irina
2015-01-27 16:20           ` Tirdea, Irina
2015-01-11 19:10 ` [PATCH v2 08/10] iio: accel: mma9551: Add runtime pm support Irina Tirdea
2015-01-26 19:08   ` Jonathan Cameron
2015-01-27 17:18     ` Tirdea, Irina
2015-01-27 17:18       ` Tirdea, Irina
2015-01-11 19:10 ` [PATCH v2 09/10] iio: accel: mma9551: split driver to expose mma955x api Irina Tirdea
2015-01-26 19:25   ` Jonathan Cameron
2015-01-11 19:10 ` [PATCH v2 10/10] iio: add driver for Freescale MMA9553 Irina Tirdea
2015-01-26 20:44   ` Jonathan Cameron
2015-01-27 17:09     ` Tirdea, Irina
2015-01-27 17:09       ` Tirdea, Irina
2015-01-27 17:31       ` Jonathan Cameron
2015-01-27 17:31         ` Jonathan Cameron

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