linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] Add support for cros-ec-sensors
@ 2016-07-18  7:02 Enric Balletbo i Serra
  2016-07-18  7:02 ` [PATCH 01/10] mfd: cros_ec: add ChromeOS EC sensor platform information Enric Balletbo i Serra
                   ` (9 more replies)
  0 siblings, 10 replies; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Hi,

This is the first patchset to add support for the sensors behind the ChromeOS
Embedded Controller. The first two patches needs to reviewed  by Lee Jones
as are MFD related, then there are six IIO patches for Jonathan Cameron and
finally the last two patches are platform/chrome specific so are for Olof.

The code was checked out from the ChromeOS v3.18 tree and forward ported to
mainline solving all the conflicts and build issues. I splitted the code in
single patches that have sense by themselves in order to avoid the chances
to fail.

Note that the patches depends on this [1] to apply and work.

[1] https://lkml.org/lkml/2016/7/1/188

Waiting for your reviews and feedback.

Best regards,

Enric Balletbo i Serra (8):
  mfd: cros_ec: add ChromeOS EC sensor platform information.
  mfd: cros_ec: update MOTIONSENSE definitions and commands.
  iio: cros_ec: Add common functions for cros_ec sensors.
  iio: cros_ec_sensors: add ChromeOS EC Contiguous Sensors driver
  iio: cros_ec_light_prox: add ChromeOS EC Light and Proximity Sensors
  iio: cros_ec_activity: add ChromeOS EC Activity Sensors
  iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring
  platform/chrome: cros_ec_dev - Register cros-ec sensors

Gwendal Grignou (1):
  iio: core: Add double tap as possible gesture

Vincent Palatin (1):
  platform/chrome: Introduce a new function to check EC features.

 drivers/iio/common/Kconfig                         |   1 +
 drivers/iio/common/Makefile                        |   1 +
 drivers/iio/common/cros_ec_sensors/Kconfig         |  50 ++
 drivers/iio/common/cros_ec_sensors/Makefile        |   9 +
 .../iio/common/cros_ec_sensors/cros_ec_activity.c  | 294 +++++++++++
 .../common/cros_ec_sensors/cros_ec_light_prox.c    | 288 +++++++++++
 .../iio/common/cros_ec_sensors/cros_ec_sensors.c   | 322 ++++++++++++
 .../common/cros_ec_sensors/cros_ec_sensors_core.c  | 564 +++++++++++++++++++++
 .../common/cros_ec_sensors/cros_ec_sensors_core.h  | 155 ++++++
 .../common/cros_ec_sensors/cros_ec_sensors_ring.c  | 541 ++++++++++++++++++++
 drivers/iio/industrialio-core.c                    |   1 +
 drivers/platform/chrome/cros_ec_dev.c              | 159 ++++++
 include/linux/mfd/cros_ec.h                        |  11 +
 include/linux/mfd/cros_ec_commands.h               | 344 +++++++++++--
 include/uapi/linux/iio/types.h                     |   1 +
 15 files changed, 2712 insertions(+), 29 deletions(-)
 create mode 100644 drivers/iio/common/cros_ec_sensors/Kconfig
 create mode 100644 drivers/iio/common/cros_ec_sensors/Makefile
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_activity.c
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c

-- 
2.1.0

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

* [PATCH 01/10] mfd: cros_ec: add ChromeOS EC sensor platform information.
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18 13:43   ` Jonathan Cameron
  2016-07-18  7:02 ` [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands Enric Balletbo i Serra
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

The platform information will be used for the new cros-ec sensors driver
that presents sensors attached to the ChromeOS Embedded Controller.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 include/linux/mfd/cros_ec.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index d6539c1..cd4833a 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -151,6 +151,16 @@ struct cros_ec_device {
 	int event_size;
 };
 
+/* struct cros_ec_sensor_platform - ChromeOS EC sensor platform information
+ *
+ * On top of cros_ec_devicem information cros_ec_sensors needs.
+ *
+ * @sensor_num: Id of the sensor, as reported by the EC.
+ */
+struct cros_ec_sensor_platform {
+	u8 sensor_num;
+};
+
 /* struct cros_ec_platform - ChromeOS EC platform information
  *
  * @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...)
-- 
2.1.0

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

* [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands.
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
  2016-07-18  7:02 ` [PATCH 01/10] mfd: cros_ec: add ChromeOS EC sensor platform information Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18 13:49   ` Jonathan Cameron
  2016-07-18  7:02 ` [PATCH 03/10] iio: core: Add double tap as possible gesture Enric Balletbo i Serra
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Let's update the command header to include the definitions related to
the sensors attached behind the ChromeOS Embedded Controller. The new
commands and definitions allow us to get information from these sensors.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 include/linux/mfd/cros_ec_commands.h | 260 +++++++++++++++++++++++++++++++----
 1 file changed, 231 insertions(+), 29 deletions(-)

diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
index 76728ff..f26a806 100644
--- a/include/linux/mfd/cros_ec_commands.h
+++ b/include/linux/mfd/cros_ec_commands.h
@@ -1290,7 +1290,13 @@ enum motionsense_command {
 
 	/*
 	 * EC Rate command is a setter/getter command for the EC sampling rate
-	 * of all motion sensors in milliseconds.
+	 * in milliseconds.
+	 * It is per sensor, the EC run sample task  at the minimum of all
+	 * sensors EC_RATE.
+	 * For sensors without hardware FIFO, EC_RATE should be equals to 1/ODR
+	 * to collect all the sensor samples.
+	 * For sensor with hardware FIFO, EC_RATE is used as the maximal delay
+	 * to process of all motion sensors in milliseconds.
 	 */
 	MOTIONSENSE_CMD_EC_RATE = 2,
 
@@ -1315,37 +1321,138 @@ enum motionsense_command {
 	 */
 	MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5,
 
-	/* Number of motionsense sub-commands. */
-	MOTIONSENSE_NUM_CMDS
-};
+	/*
+	 * Returns a single sensor data.
+	 */
+	MOTIONSENSE_CMD_DATA = 6,
+
+	/*
+	 * Return sensor fifo info.
+	 */
+	MOTIONSENSE_CMD_FIFO_INFO = 7,
+
+	/*
+	 * Insert a flush element in the fifo and return sensor fifo info.
+	 * The host can use that element to synchronize its operation.
+	 */
+	MOTIONSENSE_CMD_FIFO_FLUSH = 8,
 
-enum motionsensor_id {
-	EC_MOTION_SENSOR_ACCEL_BASE = 0,
-	EC_MOTION_SENSOR_ACCEL_LID = 1,
-	EC_MOTION_SENSOR_GYRO = 2,
+	/*
+	 * Return a portion of the fifo.
+	 */
+	MOTIONSENSE_CMD_FIFO_READ = 9,
+
+	/*
+	 * Perform low level calibration.
+	 * On sensors that support it, ask to do offset calibration.
+	 */
+	MOTIONSENSE_CMD_PERFORM_CALIB = 10,
+
+	/*
+	 * Sensor Offset command is a setter/getter command for the offset
+	 * used for calibration.
+	 * The offsets can be calculated by the host, or via
+	 * PERFORM_CALIB command.
+	 */
+	MOTIONSENSE_CMD_SENSOR_OFFSET = 11,
+
+	/*
+	 * List available activities for a MOTION sensor.
+	 * Indicates if they are enabled or disabled.
+	 */
+	MOTIONSENSE_CMD_LIST_ACTIVITIES = 12,
 
 	/*
-	 * Note, if more sensors are added and this count changes, the padding
-	 * in ec_response_motion_sense dump command must be modified.
+	 * Activity management
+	 * Enable/Disable activity recognition.
 	 */
-	EC_MOTION_SENSOR_COUNT = 3
+	MOTIONSENSE_CMD_SET_ACTIVITY = 13,
+
+	/* Number of motionsense sub-commands. */
+	MOTIONSENSE_NUM_CMDS
 };
 
 /* List of motion sensor types. */
 enum motionsensor_type {
 	MOTIONSENSE_TYPE_ACCEL = 0,
 	MOTIONSENSE_TYPE_GYRO = 1,
+	MOTIONSENSE_TYPE_MAG = 2,
+	MOTIONSENSE_TYPE_PROX = 3,
+	MOTIONSENSE_TYPE_LIGHT = 4,
+	MOTIONSENSE_TYPE_ACTIVITY = 5,
+	MOTIONSENSE_TYPE_MAX,
 };
 
 /* List of motion sensor locations. */
 enum motionsensor_location {
 	MOTIONSENSE_LOC_BASE = 0,
 	MOTIONSENSE_LOC_LID = 1,
+	MOTIONSENSE_LOC_MAX,
 };
 
 /* List of motion sensor chips. */
 enum motionsensor_chip {
 	MOTIONSENSE_CHIP_KXCJ9 = 0,
+	MOTIONSENSE_CHIP_LSM6DS0 = 1,
+	MOTIONSENSE_CHIP_BMI160 = 2,
+	MOTIONSENSE_CHIP_SI1141 = 3,
+	MOTIONSENSE_CHIP_SI1142 = 4,
+	MOTIONSENSE_CHIP_SI1143 = 5,
+	MOTIONSENSE_CHIP_KX022 = 6,
+	MOTIONSENSE_CHIP_L3GD20H = 7,
+};
+
+struct ec_response_motion_sensor_data {
+	/* Flags for each sensor. */
+	uint8_t flags;
+	/* sensor number the data comes from */
+	uint8_t sensor_num;
+	/* Each sensor is up to 3-axis. */
+	union {
+		int16_t             data[3];
+		struct {
+			uint16_t    rsvd;
+			uint32_t    timestamp;
+		} __packed;
+		struct {
+			uint8_t     activity; /* motionsensor_activity */
+			uint8_t     state;
+			int16_t     add_info[2];
+		};
+	};
+} __packed;
+
+struct ec_response_motion_sense_fifo_info {
+	/* Size of the fifo */
+	uint16_t size;
+	/* Amount of space used in the fifo */
+	uint16_t count;
+	/* TImestamp recorded in us */
+	uint32_t timestamp;
+	/* Total amount of vector lost */
+	uint16_t total_lost;
+	/* Lost events since the last fifo_info, per sensors */
+	uint16_t lost[0];
+} __packed;
+
+struct ec_response_motion_sense_fifo_data {
+	uint32_t number_data;
+	struct ec_response_motion_sensor_data data[0];
+} __packed;
+
+/* List supported activity recognition */
+enum motionsensor_activity {
+	MOTIONSENSE_ACTIVITY_RESERVED = 0,
+	MOTIONSENSE_ACTIVITY_SIG_MOTION = 1,
+	MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2,
+};
+
+struct ec_motion_sense_activity {
+	uint8_t sensor_num;
+	uint8_t activity; /* one of enum motionsensor_activity */
+	uint8_t enable;   /* 1: enable, 0: disable */
+	uint8_t reserved;
+	uint16_t parameters[3]; /* activity dependent parameters */
 };
 
 /* Module flag masks used for the dump sub-command. */
@@ -1355,41 +1462,61 @@ enum motionsensor_chip {
 #define MOTIONSENSE_SENSOR_FLAG_PRESENT (1<<0)
 
 /*
+ * Flush entry for synchronisation.
+ * data contains time stamp
+ */
+#define MOTIONSENSE_SENSOR_FLAG_FLUSH (1<<0)
+#define MOTIONSENSE_SENSOR_FLAG_TIMESTAMP (1<<1)
+#define MOTIONSENSE_SENSOR_FLAG_WAKEUP (1<<2)
+
+/*
  * Send this value for the data element to only perform a read. If you
  * send any other value, the EC will interpret it as data to set and will
  * return the actual value set.
  */
 #define EC_MOTION_SENSE_NO_VALUE -1
 
+#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000
+
+/* MOTIONSENSE_CMD_SENSOR_OFFSET subcommand flag */
+/* Set Calibration information */
+#define MOTION_SENSE_SET_OFFSET 1
+
 struct ec_params_motion_sense {
 	uint8_t cmd;
 	union {
-		/* Used for MOTIONSENSE_CMD_DUMP. */
+		/* Used for MOTIONSENSE_CMD_DUMP */
 		struct {
-			/* no args */
+			/*
+			 * Maximal number of sensor the host is expecting.
+			 * 0 means the host is only interested in the number
+			 * of sensors controlled by the EC.
+			 */
+			uint8_t max_sensor_count;
 		} dump;
 
 		/*
-		 * Used for MOTIONSENSE_CMD_EC_RATE and
-		 * MOTIONSENSE_CMD_KB_WAKE_ANGLE.
+		 * Used for MOTIONSENSE_CMD_KB_WAKE_ANGLE.
 		 */
 		struct {
-			/* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
+			/* Data to set or EC_MOTION_SENSE_NO_VALUE to read.
+			 * kb_wake_angle: angle to wakup AP.
+			 */
 			int16_t data;
-		} ec_rate, kb_wake_angle;
+		} kb_wake_angle;
 
-		/* Used for MOTIONSENSE_CMD_INFO. */
+		/* Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA
+		 * and MOTIONSENSE_CMD_PERFORM_CALIB.
+		 */
 		struct {
-			/* Should be element of enum motionsensor_id. */
 			uint8_t sensor_num;
-		} info;
+		} info, data, fifo_flush, perform_calib, list_activities;
 
 		/*
-		 * Used for MOTIONSENSE_CMD_SENSOR_ODR and
-		 * MOTIONSENSE_CMD_SENSOR_RANGE.
+		 * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR
+		 * and MOTIONSENSE_CMD_SENSOR_RANGE.
 		 */
 		struct {
-			/* Should be element of enum motionsensor_id. */
 			uint8_t sensor_num;
 
 			/* Rounding flag, true for round-up, false for down. */
@@ -1399,22 +1526,69 @@ struct ec_params_motion_sense {
 
 			/* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
 			int32_t data;
-		} sensor_odr, sensor_range;
+		} ec_rate, sensor_odr, sensor_range;
+
+		/* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
+		struct {
+			uint8_t sensor_num;
+
+			/*
+			 * bit 0: If set (MOTION_SENSE_SET_OFFSET), set
+			 * the calibration information in the EC.
+			 * If unset, just retrieve calibration information.
+			 */
+			uint16_t flags;
+
+			/*
+			 * Temperature at calibration, in units of 0.01 C
+			 * 0x8000: invalid / unknown.
+			 * 0x0: 0C
+			 * 0x7fff: +327.67C
+			 */
+			int16_t temp;
+
+			/*
+			 * Offset for calibration.
+			 * Unit:
+			 * Accelerometer: 1/1024 g
+			 * Gyro:          1/1024 deg/s
+			 * Compass:       1/16 uT
+			 */
+			int16_t offset[3];
+		} __packed sensor_offset;
+
+		/* Used for MOTIONSENSE_CMD_FIFO_INFO */
+		struct {
+		} fifo_info;
+
+		/* Used for MOTIONSENSE_CMD_FIFO_READ */
+		struct {
+			/*
+			 * Number of expected vector to return.
+			 * EC may return less or 0 if none available.
+			 */
+			uint32_t max_data_vector;
+		} fifo_read;
+
+		struct ec_motion_sense_activity set_activity;
 	};
 } __packed;
 
 struct ec_response_motion_sense {
 	union {
-		/* Used for MOTIONSENSE_CMD_DUMP. */
+		/* Used for MOTIONSENSE_CMD_DUMP */
 		struct {
 			/* Flags representing the motion sensor module. */
 			uint8_t module_flags;
 
-			/* Flags for each sensor in enum motionsensor_id. */
-			uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT];
+			/* Number of sensors managed directly by the EC */
+			uint8_t sensor_count;
 
-			/* Array of all sensor data. Each sensor is 3-axis. */
-			int16_t data[3*EC_MOTION_SENSOR_COUNT];
+			/*
+			 * sensor data is truncated if response_max is too small
+			 * for holding all the data.
+			 */
+			struct ec_response_motion_sensor_data sensor[0];
 		} dump;
 
 		/* Used for MOTIONSENSE_CMD_INFO. */
@@ -1429,6 +1603,9 @@ struct ec_response_motion_sense {
 			uint8_t chip;
 		} info;
 
+		/* Used for MOTIONSENSE_CMD_DATA */
+		struct ec_response_motion_sensor_data data;
+
 		/*
 		 * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR,
 		 * MOTIONSENSE_CMD_SENSOR_RANGE, and
@@ -1438,6 +1615,25 @@ struct ec_response_motion_sense {
 			/* Current value of the parameter queried. */
 			int32_t ret;
 		} ec_rate, sensor_odr, sensor_range, kb_wake_angle;
+
+		/* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
+		struct {
+			int16_t temp;
+			int16_t offset[3];
+		} sensor_offset, perform_calib;
+
+		struct ec_response_motion_sense_fifo_info fifo_info, fifo_flush;
+
+		struct ec_response_motion_sense_fifo_data fifo_read;
+
+		struct {
+			uint16_t reserved;
+			uint32_t enabled;
+			uint32_t disabled;
+		} __packed list_activities;
+
+		struct {
+		} set_activity;
 	};
 } __packed;
 
@@ -1819,6 +2015,12 @@ union ec_response_get_next_data {
 
 	/* Unaligned */
 	uint32_t  host_event;
+
+	struct {
+		/* For aligning the fifo_info */
+		uint8_t rsvd[3];
+		struct ec_response_motion_sense_fifo_info info;
+	}        sensor_fifo;
 } __packed;
 
 struct ec_response_get_next_event {
-- 
2.1.0

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

* [PATCH 03/10] iio: core: Add double tap as possible gesture
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
  2016-07-18  7:02 ` [PATCH 01/10] mfd: cros_ec: add ChromeOS EC sensor platform information Enric Balletbo i Serra
  2016-07-18  7:02 ` [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18 13:54   ` Jonathan Cameron
  2016-07-18  7:02 ` [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors Enric Balletbo i Serra
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

From: Gwendal Grignou <gwendal@chromium.org>

This is an interface change: however, the sysfs entry is based on string,
so if other gestures are added on the trunk in the meantime, we will
still be able to merge this change.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: Guenter Roeck <groeck@chromium.org>
[enric: Rebased and resolved conflicts]
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/iio/industrialio-core.c | 1 +
 include/uapi/linux/iio/types.h  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index e6319a9..f700e67 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -119,6 +119,7 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_Q] = "q",
 	[IIO_MOD_CO2] = "co2",
 	[IIO_MOD_VOC] = "voc",
+	[IIO_MOD_DOUBLE_TAP] = "double_tap",
 };
 
 /* relies on pairs of these shared then separate */
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index b0916fc..c290167 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -79,6 +79,7 @@ enum iio_modifier {
 	IIO_MOD_CO2,
 	IIO_MOD_VOC,
 	IIO_MOD_LIGHT_UV,
+	IIO_MOD_DOUBLE_TAP,
 };
 
 enum iio_event_type {
-- 
2.1.0

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

* [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors.
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
                   ` (2 preceding siblings ...)
  2016-07-18  7:02 ` [PATCH 03/10] iio: core: Add double tap as possible gesture Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18  7:29   ` Peter Meerwald-Stadler
  2016-07-18  7:02 ` [PATCH 05/10] iio: cros_ec_sensors: add ChromeOS EC Contiguous Sensors driver Enric Balletbo i Serra
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Add the core functions to be able to support the sensors attached behind
the ChromeOS Embedded Controller and used by other IIO cros-ec sensor
drivers.

The cros_ec_sensor_core driver matches with current driver in ChromeOS
4.4 tree, so it includes all the fixes at the moment. The support for
this driver was made by Gwendal Grignou. The original patch and all the
fixes has been squashed and rebased on top of mainline.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: Guenter Roeck <groeck@chromium.org>
[eballetbo: split, squash and rebase on top of mainline the patches
found in ChromeOS tree]
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/iio/common/Kconfig                         |   1 +
 drivers/iio/common/Makefile                        |   1 +
 drivers/iio/common/cros_ec_sensors/Kconfig         |  14 +
 drivers/iio/common/cros_ec_sensors/Makefile        |   5 +
 .../common/cros_ec_sensors/cros_ec_sensors_core.c  | 564 +++++++++++++++++++++
 .../common/cros_ec_sensors/cros_ec_sensors_core.h  | 155 ++++++
 6 files changed, 740 insertions(+)
 create mode 100644 drivers/iio/common/cros_ec_sensors/Kconfig
 create mode 100644 drivers/iio/common/cros_ec_sensors/Makefile
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index 26a6026..e108996 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -2,6 +2,7 @@
 # IIO common modules
 #
 
+source "drivers/iio/common/cros_ec_sensors/Kconfig"
 source "drivers/iio/common/hid-sensors/Kconfig"
 source "drivers/iio/common/ms_sensors/Kconfig"
 source "drivers/iio/common/ssp_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 585da6a..6fa760e 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,6 +7,7 @@
 #
 
 # When adding new entries keep the list in alphabetical order
+obj-y += cros_ec_sensors/
 obj-y += hid-sensors/
 obj-y += ms_sensors/
 obj-y += ssp_sensors/
diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
new file mode 100644
index 0000000..a30f41e
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/Kconfig
@@ -0,0 +1,14 @@
+#
+# Chrome OS Embedded Controller managed sensors library
+#
+config IIO_CROS_EC_SENSORS_CORE
+	tristate "ChromeOS EC Sensors Core"
+	depends on SYSFS && MFD_CROS_EC
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Base module for the ChromeOS EC Sensors module.
+	  Contains core functions used by other IIO CrosEC sensor
+	  driver.
+	  Define common attributes and sysfs interrupt handler.
+
diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
new file mode 100644
index 0000000..95b6901
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for sensors seen through the ChromeOS EC sensor hub.
+#
+
+obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
new file mode 100644
index 0000000..cb3de8f
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -0,0 +1,564 @@
+/*
+ * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
+ *
+ * Copyright (C) 2015 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the cros-ec interface to communicate with the Chrome OS
+ * EC about accelerometer data. Accelerometer access is presented through
+ * iio sysfs.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+
+#include "cros_ec_sensors_core.h"
+
+static char *cros_ec_loc[] = {
+	[MOTIONSENSE_LOC_BASE] = "base",
+	[MOTIONSENSE_LOC_LID] = "lid",
+	[MOTIONSENSE_LOC_MAX] = "unknown",
+};
+
+/*
+ * cros_ec_sensors_core_init
+ *
+ * Initialize core sensor strucure, fill the response are
+ * with the return of Sensor info.
+ *
+ * @pdev plarform device created for the sensors
+ * @indio_dev iio device structure of the device
+ * @physical_device True if the device refers to a physical device.
+ */
+int cros_ec_sensors_core_init(struct platform_device *pdev,
+			      struct iio_dev *indio_dev,
+			      bool physical_device)
+{
+	struct device *dev = &pdev->dev;
+	struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
+	struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
+	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	state->ec = ec->ec_dev;
+	state->msg = devm_kzalloc(&pdev->dev,
+				max((u16)sizeof(struct ec_params_motion_sense),
+				state->ec->max_response), GFP_KERNEL);
+	if (!state->msg)
+		return -ENOMEM;
+
+	state->resp = (struct ec_response_motion_sense *)state->msg->data;
+
+	mutex_init(&state->cmd_lock);
+	/* Set up the host command structure. */
+	state->msg->version = 2;
+	state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+	state->msg->outsize = sizeof(struct ec_params_motion_sense);
+
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->name = pdev->name;
+
+	if (physical_device) {
+		indio_dev->modes = INDIO_DIRECT_MODE;
+
+		state->param.cmd = MOTIONSENSE_CMD_INFO;
+		state->param.info.sensor_num = sensor_platform->sensor_num;
+		if (cros_ec_motion_send_host_cmd(state, 0)) {
+			dev_warn(dev, "Can not access sensor info\n");
+			return -EIO;
+		}
+		state->type = state->resp->info.type;
+		state->loc = state->resp->info.location;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
+
+/*
+ * cros_ec_motion_send_host_cmd - send motion sense host command
+ *
+ * @st Pointer to state information for device.
+ * @return 0 if ok, -ve on error.
+ *
+ * Note, when called, the sub-command is assumed to be set in param->cmd.
+ */
+int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
+				 u16 opt_length)
+{
+	int ret;
+
+	if (opt_length)
+		state->msg->insize = min(opt_length, state->ec->max_response);
+	else
+		state->msg->insize = state->ec->max_response;
+
+	memcpy(state->msg->data, &state->param, sizeof(state->param));
+	/* Send host command. */
+	ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
+	if (ret < 0)
+		return -EIO;
+
+	if (ret &&
+	    state->resp != (struct ec_response_motion_sense *)state->msg->data)
+		memcpy(state->resp, state->msg->data, ret);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
+
+static ssize_t __maybe_unused cros_ec_sensors_flush(struct iio_dev *indio_dev,
+		uintptr_t private, const struct iio_chan_spec *chan,
+		const char *buf, size_t len)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int ret = 0;
+	bool flush;
+
+	ret = strtobool(buf, &flush);
+	if (ret < 0)
+		return ret;
+	if (!flush)
+		return -EINVAL;
+
+	mutex_lock(&st->cmd_lock);
+	st->param.cmd = MOTIONSENSE_CMD_FIFO_FLUSH;
+	ret = cros_ec_motion_send_host_cmd(st, 0);
+	if (ret != 0)
+		dev_warn(&indio_dev->dev, "Unable to flush sensor\n");
+	mutex_unlock(&st->cmd_lock);
+	return ret ? ret : len;
+}
+
+static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
+		uintptr_t private, const struct iio_chan_spec *chan,
+		const char *buf, size_t len)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int ret, i;
+	bool calibrate;
+
+	ret = strtobool(buf, &calibrate);
+	if (ret < 0)
+		return ret;
+	if (!calibrate)
+		return -EINVAL;
+
+	mutex_lock(&st->cmd_lock);
+	st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
+	ret = cros_ec_motion_send_host_cmd(st, 0);
+	if (ret != 0) {
+		dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
+	} else {
+		/* Save values */
+		for (i = X; i < MAX_AXIS; i++)
+			st->calib[i].offset = st->resp->perform_calib.offset[i];
+	}
+	mutex_unlock(&st->cmd_lock);
+	return ret ? ret : len;
+}
+
+static ssize_t cros_ec_sensors_id(struct iio_dev *indio_dev,
+		uintptr_t private, const struct iio_chan_spec *chan,
+		char *buf)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n", st->param.info.sensor_num);
+}
+
+static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
+		uintptr_t private, const struct iio_chan_spec *chan,
+		char *buf)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+	return sprintf(buf, "%s\n", cros_ec_loc[st->loc]);
+}
+
+const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
+#ifdef CONFIG_IIO_CROS_EC_SENSORS_RING
+	{
+		.name = "flush",
+		.shared = IIO_SHARED_BY_ALL,
+		.write = cros_ec_sensors_flush
+	},
+#endif
+	{
+		.name = "calibrate",
+		.shared = IIO_SHARED_BY_ALL,
+		.write = cros_ec_sensors_calibrate
+	},
+	{
+		.name = "id",
+		.shared = IIO_SHARED_BY_ALL,
+		.read = cros_ec_sensors_id
+	},
+	{
+		.name = "location",
+		.shared = IIO_SHARED_BY_ALL,
+		.read = cros_ec_sensors_loc
+	},
+	{ },
+};
+EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
+
+const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[] = {
+	{
+		.name = "id",
+		.shared = IIO_SHARED_BY_ALL,
+		.read = cros_ec_sensors_id
+	},
+	{
+		.name = "location",
+		.shared = IIO_SHARED_BY_ALL,
+		.read = cros_ec_sensors_loc
+	},
+	{ },
+};
+EXPORT_SYMBOL_GPL(cros_ec_sensors_limited_info);
+/*
+ * idx_to_reg - convert sensor index into offset in shared memory region.
+ *
+ * @st: private data
+ * @idx: sensor index (should be element of enum sensor_index)
+ * @return address to read at.
+ */
+static unsigned int idx_to_reg(struct cros_ec_sensors_core_state *st,
+			       unsigned int idx)
+{
+	/*
+	 * When using LPC interface, only space for 2 Accel and one Gyro.
+	 * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
+	 */
+	if (st->type == MOTIONSENSE_TYPE_ACCEL)
+		return EC_MEMMAP_ACC_DATA + sizeof(u16) *
+			(1 + idx + st->param.info.sensor_num *
+			 MAX_AXIS);
+	else
+		return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
+}
+
+static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
+			  u8 *dest)
+{
+	return ec->cmd_readmem(ec, offset, 1, dest);
+}
+
+static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
+			   u16 *dest)
+{
+	u16 tmp;
+	int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
+
+	*dest = le16_to_cpu(tmp);
+
+	return ret;
+}
+
+/*
+ * read_ec_until_not_busy - read from EC status byte until it reads not busy.
+ *
+ * @st Pointer to state information for device.
+ * @return 8-bit status if ok, -ve on error
+ */
+static int read_ec_until_not_busy(struct cros_ec_sensors_core_state *st)
+{
+	struct cros_ec_device *ec = st->ec;
+	u8 status;
+	int attempts = 0;
+
+	ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
+	while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
+		/* Give up after enough attempts, return error. */
+		if (attempts++ >= 50)
+			return -EIO;
+
+		/* Small delay every so often. */
+		if (attempts % 5 == 0)
+			msleep(25);
+
+		ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
+	}
+
+	return status;
+}
+
+/*
+ * read_ec_sensors_data_unsafe - read acceleration data from EC shared memory.
+ *
+ * @st Pointer to state information for device.
+ * @scan_mask Bitmap of the sensor indices to scan.
+ * @data Location to store data.
+ *
+ * Note this is the unsafe function for reading the EC data. It does not
+ * guarantee that the EC will not modify the data as it is being read in.
+ */
+static void read_ec_sensors_data_unsafe(struct iio_dev *indio_dev,
+			 unsigned long scan_mask, s16 *data)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	struct cros_ec_device *ec = st->ec;
+	unsigned int i = 0;
+
+	/*
+	 * Read all sensors enabled in scan_mask. Each value is 2
+	 * bytes.
+	 */
+	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+		ec_cmd_read_u16(ec, idx_to_reg(st, i), data);
+		data++;
+	}
+}
+
+/*
+ * cros_ec_sensors_read_lpc - read acceleration data from EC shared memory.
+ *
+ * @st Pointer to state information for device.
+ * @scan_mask Bitmap of the sensor indices to scan.
+ * @data Location to store data.
+ * @return 0 if ok, -ve on error
+ *
+ * Note: this is the safe function for reading the EC data. It guarantees
+ * that the data sampled was not modified by the EC while being read.
+ */
+int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
+			     unsigned long scan_mask, s16 *data)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	struct cros_ec_device *ec = st->ec;
+	u8 samp_id = 0xff, status = 0;
+	int attempts = 0;
+
+	/*
+	 * Continually read all data from EC until the status byte after
+	 * all reads reflects that the EC is not busy and the sample id
+	 * matches the sample id from before all reads. This guarantees
+	 * that data read in was not modified by the EC while reading.
+	 */
+	while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
+			  EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
+		/* If we have tried to read too many times, return error. */
+		if (attempts++ >= 5)
+			return -EIO;
+
+		/* Read status byte until EC is not busy. */
+		status = read_ec_until_not_busy(st);
+		if (status < 0)
+			return status;
+
+		/*
+		 * Store the current sample id so that we can compare to the
+		 * sample id after reading the data.
+		 */
+		samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
+
+		/* Read all EC data, format it, and store it into data. */
+		read_ec_sensors_data_unsafe(indio_dev, scan_mask, data);
+
+		/* Read status byte. */
+		ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
+
+int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
+			     unsigned long scan_mask, s16 *data)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int ret;
+	unsigned int i = 0;
+
+	/*
+	 * read all sensor data through a command.
+	 */
+	st->param.cmd = MOTIONSENSE_CMD_DATA;
+	ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
+	if (ret != 0) {
+		dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
+		return ret;
+	}
+
+	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+		*data = st->resp->data.data[i];
+		data++;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
+
+irqreturn_t cros_ec_sensors_capture(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+	mutex_lock(&st->cmd_lock);
+	/* Clear capture data. */
+	memset(st->samples, 0, indio_dev->scan_bytes);
+
+	/* Read data based on which channels are enabled in scan mask. */
+	st->read_ec_sensors_data(indio_dev, *(indio_dev->active_scan_mask),
+			   (s16 *)st->samples);
+
+	/* Store the timestamp last 8 bytes of data. */
+	if (indio_dev->scan_timestamp)
+		*(s64 *)&st->samples[round_down(indio_dev->scan_bytes -
+						sizeof(s64),
+				     sizeof(s64))] = iio_get_time_ns();
+
+	iio_push_to_buffers(indio_dev, st->samples);
+
+	/*
+	 * Tell the core we are done with this trigger and ready for the
+	 * next one.
+	 */
+	iio_trigger_notify_done(indio_dev->trig);
+	mutex_unlock(&st->cmd_lock);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
+
+int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
+			  struct iio_chan_spec const *chan,
+			  int *val, int *val2, long mask)
+{
+	int ret = IIO_VAL_INT;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+		st->param.ec_rate.data =
+			EC_MOTION_SENSE_NO_VALUE;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		else
+			*val = st->resp->ec_rate.ret;
+		break;
+	case IIO_CHAN_INFO_FREQUENCY:
+		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
+		st->param.sensor_odr.data =
+			EC_MOTION_SENSE_NO_VALUE;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		else
+			*val = st->resp->sensor_odr.ret;
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
+
+int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask)
+{
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_FREQUENCY:
+		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
+		st->param.sensor_odr.data = val;
+
+		/* Always roundup, so caller gets at least what it asks for. */
+		st->param.sensor_odr.roundup = 1;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+		st->param.ec_rate.data = val;
+
+		if (cros_ec_motion_send_host_cmd(st, 0))
+			ret = -EIO;
+		else
+			st->curr_sampl_freq = val;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
+
+static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+	if (st->curr_sampl_freq == 0)
+		return 0;
+
+	/*
+	 * If the sensors are sampled at high frequency, we will not be able to
+	 * sleep. Set to sampling to a long period if necessary.
+	 */
+	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
+		mutex_lock(&st->cmd_lock);
+		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+		st->param.ec_rate.data = CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY;
+		cros_ec_motion_send_host_cmd(st, 0);
+		mutex_unlock(&st->cmd_lock);
+	}
+	return 0;
+}
+
+static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+	if (st->curr_sampl_freq == 0)
+		return;
+
+	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
+		mutex_lock(&st->cmd_lock);
+		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+		st->param.ec_rate.data = st->curr_sampl_freq;
+		cros_ec_motion_send_host_cmd(st, 0);
+		mutex_unlock(&st->cmd_lock);
+	}
+}
+
+#ifdef CONFIG_PM_SLEEP
+const struct dev_pm_ops cros_ec_sensors_pm_ops = {
+	.prepare = cros_ec_sensors_prepare,
+	.complete = cros_ec_sensors_complete
+};
+#else
+const struct dev_pm_ops cros_ec_sensors_pm_ops = { };
+#endif
+EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
+
+MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
new file mode 100644
index 0000000..53e09e1
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
@@ -0,0 +1,155 @@
+/*
+ * ChromeOS EC sensor hub
+ *
+ * Copyright (C) 2015 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CROS_EC_SENSORS_CORE_H
+#define __CROS_EC_SENSORS_CORE_H
+
+#include <linux/irqreturn.h>
+
+enum {
+	X,
+	Y,
+	Z,
+	MAX_AXIS,
+};
+
+/*
+ * EC returns sensor values using signed 16 bit registers
+ */
+#define CROS_EC_SENSOR_BITS 16
+
+/*
+ * 4 16 bit channels are allowed.
+ * Good enough for current sensors, thye use up to 3 16 bit vectors.
+ */
+#define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
+
+/*
+ * minimum sampling period to use when device is suspending.
+ */
+#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
+
+/*
+ * Function to read the sensor data.
+ *
+ * Data can be retrieve using the cros ec command protocol.
+ * Some machines also allow accessing some sensor data via
+ * IO space.
+ */
+typedef int (cros_ec_sensors_read_t)(struct iio_dev *indio_dev,
+		unsigned long scan_mask, s16 *data);
+
+cros_ec_sensors_read_t cros_ec_sensors_read_lpc;
+cros_ec_sensors_read_t cros_ec_sensors_read_cmd;
+
+/* State data for ec_sensors iio driver. */
+struct cros_ec_sensors_core_state {
+	struct cros_ec_device *ec;
+	/*
+	 *  Location to store command and response to the EC.
+	 */
+	struct mutex cmd_lock;
+
+	/*
+	 * Statically allocated command structure that holds parameters
+	 * and response.
+	 */
+	struct cros_ec_command *msg;
+	struct ec_params_motion_sense param;
+	struct ec_response_motion_sense *resp;
+
+	/* Type of sensor */
+	enum motionsensor_type type;
+	enum motionsensor_location loc;
+
+	/*
+	 * Calibration parameters. Note that trigger captured data will always
+	 * provide the calibrated values.
+	 */
+	struct calib_data {
+		s16 offset;
+	} calib[MAX_AXIS];
+
+	/*
+	 * Static array to hold data from a single capture. For each
+	 * channel we need 2 bytes, except for the timestamp. The timestamp
+	 * is always last and is always 8-byte aligned.
+	 */
+	u8 samples[CROS_EC_SAMPLE_SIZE];
+
+	/* Pointer to function used for accessing sensors values. */
+	cros_ec_sensors_read_t *read_ec_sensors_data;
+
+	/* Current sampling period */
+	int curr_sampl_freq;
+};
+
+/* Basic initialization of the core structure. */
+int cros_ec_sensors_core_init(struct platform_device *pdev,
+			      struct iio_dev *indio_dev,
+			      bool physical_device);
+
+/*
+ * cros_ec_sensors_capture - the trigger handler function
+ *
+ * @irq: the interrupt number
+ * @p: private data - always a pointer to the poll func.
+ *
+ * On a trigger event occurring, if the pollfunc is attached then this
+ * handler is called as a threaded interrupt (and hence may sleep). It
+ * is responsible for grabbing data from the device and pushing it into
+ * the associated buffer.
+ */
+irqreturn_t cros_ec_sensors_capture(int irq, void *p);
+
+
+/*
+ * cros_ec_motion_send_host_cmd - send motion sense host command
+ *
+ * @st Pointer to state information for device.
+ * @opt_length: optional length: to reduce the response size,
+ *    useful on the data path.
+ *    Otherwise, the maximal allowed response size is used.
+ * @return 0 if ok, -ve on error.
+ *
+ * Note, when called, the sub-command is assumed to be set in param->cmd.
+ */
+int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
+				 u16 opt_length);
+
+/*
+ * cros_ec_sensors_core_read/write: handler for core attributes.
+ *
+ * Handler for attributes identical among sensors:
+ * - frequency,
+ * - sampling_frequency.
+ *
+ * cmd_lock lock must be held.
+ */
+int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
+			  struct iio_chan_spec const *chan,
+			  int *val, int *val2, long mask);
+
+int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask);
+
+extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
+
+/* List of extended channel specification for all sensors */
+extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
+extern const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[];
+
+#endif  /* __CROS_EC_SENSORS_CORE_H */
-- 
2.1.0

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

* [PATCH 05/10] iio: cros_ec_sensors: add ChromeOS EC Contiguous Sensors driver
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
                   ` (3 preceding siblings ...)
  2016-07-18  7:02 ` [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18 16:09   ` Jonathan Cameron
  2016-07-18  7:02 ` [PATCH 06/10] iio: cros_ec_light_prox: add ChromeOS EC Light and Proximity Sensors Enric Balletbo i Serra
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Handle 3d contiguous sensors like Accelerometers, Gyroscope and
Magnetometer that are presented by the ChromeOS EC Sensor hub.

Signed-off-by: Guenter Roeck <groeck@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/iio/common/cros_ec_sensors/Kconfig         |   8 +
 drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
 .../iio/common/cros_ec_sensors/cros_ec_sensors.c   | 322 +++++++++++++++++++++
 3 files changed, 331 insertions(+)
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c

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

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

* [PATCH 06/10] iio: cros_ec_light_prox: add ChromeOS EC Light and Proximity Sensors
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
                   ` (4 preceding siblings ...)
  2016-07-18  7:02 ` [PATCH 05/10] iio: cros_ec_sensors: add ChromeOS EC Contiguous Sensors driver Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18 16:11   ` Jonathan Cameron
  2016-07-18  7:02 ` [PATCH 07/10] iio: cros_ec_activity: add ChromeOS EC Activity Sensors Enric Balletbo i Serra
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Handle Light and Proximity sensors presented by the ChromeOS EC Sensor hub.
Creates an IIO device for each functions.

Signed-off-by: Guenter Roeck <groeck@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/iio/common/cros_ec_sensors/Kconfig         |   9 +
 drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
 .../common/cros_ec_sensors/cros_ec_light_prox.c    | 288 +++++++++++++++++++++
 3 files changed, 298 insertions(+)
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c

diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
index 18002d6..02559d2 100644
--- a/drivers/iio/common/cros_ec_sensors/Kconfig
+++ b/drivers/iio/common/cros_ec_sensors/Kconfig
@@ -20,3 +20,12 @@ config IIO_CROS_EC_SENSORS
 	  Accelerometers, Gyroscope and Magnetometer that are
 	  presented by the ChromeOS EC Sensor hub.
 	  Creates an IIO device for each functions.
+
+config IIO_CROS_EC_LIGHT_PROX
+	tristate "ChromeOS EC Light and Proximity Sensors"
+	select IIO_CROS_EC_SENSORS_CORE
+	help
+	  Module to handle Light and Proximity sensors
+	  presented by the ChromeOS EC Sensor hub.
+	  Creates an IIO device for each functions.
+	  Only one source is exposed by the EC.
diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
index ec716ff..7aaf2a2 100644
--- a/drivers/iio/common/cros_ec_sensors/Makefile
+++ b/drivers/iio/common/cros_ec_sensors/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
 obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
+obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c b/drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c
new file mode 100644
index 0000000..333e927
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c
@@ -0,0 +1,288 @@
+/*
+ * cros_ec_light_proxmity - Driver for light and prox sensors behing CrOS EC.
+ *
+ * Copyright (C) 2015 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the cros-ec interface to communicate with the Chrome OS
+ * EC about accelerometer data. Accelerometer access is presented through
+ * iio sysfs.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+
+#include "cros_ec_sensors_core.h"
+
+/*
+ * We only represent one entry for light or proximity.
+ * EC is merging different light sensors to return the
+ * what the eye would see.
+ * For proximity, we currently support only one light source.
+ */
+#define MAX_CHANNELS (1 + 1)
+
+/* State data for ec_sensors iio driver. */
+struct cros_ec_sensors_state {
+	/* Shared by all sensors */
+	struct cros_ec_sensors_core_state core;
+
+	struct iio_chan_spec channels[MAX_CHANNELS];
+};
+
+static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val, int *val2, long mask)
+{
+	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
+	u16 data = 0;
+	s64 val64;
+	int ret = IIO_VAL_INT;
+	int idx = chan->scan_index;
+
+	mutex_lock(&st->core.cmd_lock);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
+					(s16 *)&data) < 0)
+			ret = -EIO;
+		*val = data;
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
+		st->core.param.sensor_offset.flags = 0;
+
+		if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
+			ret = -EIO;
+			break;
+		}
+
+		/* Save values */
+		st->core.calib[0].offset =
+			st->core.resp->sensor_offset.offset[0];
+
+		*val = st->core.calib[idx].offset;
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		/*
+		 * RANGE is used for calibration
+		 * scalse is a number x.y, where x is coded on 16bits,
+		 * y coded on 16 bits, between 0 and 9999.
+		 */
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
+		st->core.param.sensor_range.data =
+			EC_MOTION_SENSE_NO_VALUE;
+
+		if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
+			ret = -EIO;
+			break;
+		}
+		val64 = st->core.resp->sensor_range.ret;
+		*val = val64 >> 16;
+		*val2 = (val64 & 0xffff) * 100;
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		/* Light: Result in Lux, using calibration multiplier */
+		/* Prox: Result in cm. */
+		*val = 1;
+		ret = IIO_VAL_INT;
+		break;
+	default:
+		ret = cros_ec_sensors_core_read(
+				&st->core, chan, val, val2, mask);
+		break;
+	}
+	mutex_unlock(&st->core.cmd_lock);
+	return ret;
+}
+
+static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask)
+{
+	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
+	int ret = 0;
+	int idx = chan->scan_index;
+
+	mutex_lock(&st->core.cmd_lock);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		st->core.calib[idx].offset = val;
+		/* Send to EC for each axis, even if not complete */
+
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
+		st->core.param.sensor_offset.flags =
+			MOTION_SENSE_SET_OFFSET;
+		st->core.param.sensor_offset.offset[0] =
+			st->core.calib[0].offset;
+		st->core.param.sensor_offset.temp =
+			EC_MOTION_SENSE_INVALID_CALIB_TEMP;
+
+		if (cros_ec_motion_send_host_cmd(&st->core, 0))
+			ret = -EIO;
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
+		st->core.param.sensor_range.data = (val << 16) | (val2 / 100);
+		if (cros_ec_motion_send_host_cmd(&st->core, 0))
+			ret = -EIO;
+		break;
+	default:
+		ret = cros_ec_sensors_core_write(
+				&st->core, chan, val, val2, mask);
+		break;
+	}
+
+	mutex_unlock(&st->core.cmd_lock);
+	return ret;
+}
+
+static const struct iio_info ec_sensors_info = {
+	.read_raw = &cros_ec_light_prox_read,
+	.write_raw = &cros_ec_light_prox_write,
+	.driver_module = THIS_MODULE,
+};
+
+static int cros_ec_sensors_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+	struct cros_ec_device *ec_device;
+	struct iio_dev *indio_dev;
+	struct cros_ec_sensors_state *state;
+	struct iio_chan_spec *channel;
+	int ret;
+
+	if (!ec_dev || !ec_dev->ec_dev) {
+		dev_warn(&pdev->dev, "No CROS EC device found.\n");
+		return -EINVAL;
+	}
+	ec_device = ec_dev->ec_dev;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
+	if (!indio_dev)
+		return -ENOMEM;
+
+
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+	if (ret)
+		return ret;
+
+	indio_dev->info = &ec_sensors_info;
+	state = iio_priv(indio_dev);
+	state->core.type = state->core.resp->info.type;
+	state->core.loc = state->core.resp->info.location;
+	channel = state->channels;
+	/* common part */
+	channel->info_mask_separate =
+		BIT(IIO_CHAN_INFO_RAW) |
+		BIT(IIO_CHAN_INFO_CALIBBIAS) |
+		BIT(IIO_CHAN_INFO_CALIBSCALE);
+	channel->info_mask_shared_by_all =
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_FREQUENCY);
+	channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
+	channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
+	channel->scan_type.shift = 0;
+	channel->scan_index = 0;
+	channel->ext_info = cros_ec_sensors_ext_info;
+	channel->scan_type.sign = 'u';
+
+	state->core.calib[0].offset = 0;
+
+	/* sensor specific */
+	switch (state->core.type) {
+	case MOTIONSENSE_TYPE_LIGHT:
+		channel->type = IIO_LIGHT;
+		break;
+	case MOTIONSENSE_TYPE_PROX:
+		channel->type = IIO_PROXIMITY;
+		break;
+	default:
+		dev_warn(&pdev->dev, "unknown\n");
+		return -EINVAL;
+	}
+
+	/* Timestamp */
+	channel++;
+	channel->type = IIO_TIMESTAMP;
+	channel->channel = -1;
+	channel->scan_index = 1;
+	channel->scan_type.sign = 's';
+	channel->scan_type.realbits = 64;
+	channel->scan_type.storagebits = 64;
+
+	indio_dev->channels = state->channels;
+	indio_dev->num_channels = MAX_CHANNELS;
+
+	state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+			cros_ec_sensors_capture, NULL);
+	if (ret)
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		iio_triggered_buffer_cleanup(indio_dev);
+	return ret;
+}
+
+static int cros_ec_sensors_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	return 0;
+}
+
+static const struct platform_device_id cros_ec_sensors_ids[] = {
+	{
+		.name = "cros-ec-prox",
+	},
+	{
+		.name = "cros-ec-light",
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
+
+static struct platform_driver cros_ec_sensors_platform_driver = {
+	.driver = {
+		.name	= "cros-ec-light-prox",
+		.pm	= &cros_ec_sensors_pm_ops,
+	},
+	.probe		= cros_ec_sensors_probe,
+	.remove		= cros_ec_sensors_remove,
+	.id_table	= cros_ec_sensors_ids,
+};
+module_platform_driver(cros_ec_sensors_platform_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.0

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

* [PATCH 07/10] iio: cros_ec_activity: add ChromeOS EC Activity Sensors
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
                   ` (5 preceding siblings ...)
  2016-07-18  7:02 ` [PATCH 06/10] iio: cros_ec_light_prox: add ChromeOS EC Light and Proximity Sensors Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18 16:16   ` Jonathan Cameron
  2016-07-18  7:02 ` [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring Enric Balletbo i Serra
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Handle activity events detections presented by the ChromeOS
EC Sensor hub. Activities can be simple (low/no motion) or more complex
(riding train). They are being reported by physical devices or the EC
itself.

Signed-off-by: Guenter Roeck <groeck@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/iio/common/cros_ec_sensors/Kconfig         |  10 +
 drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
 .../iio/common/cros_ec_sensors/cros_ec_activity.c  | 294 +++++++++++++++++++++
 3 files changed, 305 insertions(+)
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_activity.c

diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
index 02559d2..22b4211 100644
--- a/drivers/iio/common/cros_ec_sensors/Kconfig
+++ b/drivers/iio/common/cros_ec_sensors/Kconfig
@@ -29,3 +29,13 @@ config IIO_CROS_EC_LIGHT_PROX
 	  presented by the ChromeOS EC Sensor hub.
 	  Creates an IIO device for each functions.
 	  Only one source is exposed by the EC.
+
+config IIO_CROS_EC_ACTIVITY
+	tristate "ChromeOS EC Activity Sensors"
+	select IIO_CROS_EC_SENSORS_CORE
+	help
+	  Module to handle activity events detections presented by the ChromeOS
+	  EC Sensor hub.
+	  Activities can be simple (low/no motion) or more complex (riding train).
+	  They are being reported by physical devices or the EC itself.
+	  Creates an IIO device to manage all activities.
diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
index 7aaf2a2..8f54f1e 100644
--- a/drivers/iio/common/cros_ec_sensors/Makefile
+++ b/drivers/iio/common/cros_ec_sensors/Makefile
@@ -5,3 +5,4 @@
 obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
 obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
 obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
+obj-$(CONFIG_IIO_CROS_EC_ACTIVITY) += cros_ec_activity.o
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c
new file mode 100644
index 0000000..b2e39d6
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c
@@ -0,0 +1,294 @@
+/*
+ * cros_ec_sensors_activity - Driver for activities/gesture recognition.
+ *
+ * Copyright (C) 2015 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the cros-ec interface to communicate with the Chrome OS
+ * EC about accelerometer data. Accelerometer access is presented through
+ * iio sysfs.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+
+#include "cros_ec_sensors_core.h"
+
+/* st data for ec_sensors iio driver. */
+struct cros_ec_sensors_state {
+	/* Shared by all sensors */
+	struct cros_ec_sensors_core_state core;
+
+	struct iio_chan_spec *channels;
+	unsigned int nb_activities;
+};
+
+static const struct iio_event_spec cros_ec_activity_single_shot[] = {
+	{
+		.type = IIO_EV_TYPE_CHANGE,
+		/* significant motion trigger when we get out of still. */
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+	 },
+};
+
+static int ec_sensors_read(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val, int *val2, long mask)
+{
+	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
+		 chan->channel2);
+	return -EPERM;
+}
+
+static int ec_sensors_write(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask)
+{
+	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
+		 chan->channel2);
+	return -EPERM;
+}
+
+static int cros_ec_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 cros_ec_sensors_state *st = iio_priv(indio_dev);
+	int ret;
+
+	if (chan->type != IIO_ACTIVITY)
+		return -EINVAL;
+
+	mutex_lock(&st->core.cmd_lock);
+	st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES;
+	if (cros_ec_motion_send_host_cmd(&st->core, 0) == EC_RES_SUCCESS) {
+		switch (chan->channel2) {
+		case IIO_MOD_STILL:
+			ret = !!(st->core.resp->list_activities.enabled &
+				 (1 << MOTIONSENSE_ACTIVITY_SIG_MOTION));
+			break;
+		case IIO_MOD_DOUBLE_TAP:
+			ret = !!(st->core.resp->list_activities.enabled &
+				 (1 << MOTIONSENSE_ACTIVITY_DOUBLE_TAP));
+			break;
+		default:
+			dev_warn(&indio_dev->dev, "Unknown activity: %d\n",
+				 chan->channel2);
+			ret = -EINVAL;
+		}
+	} else {
+		ret = -EIO;
+	}
+	mutex_unlock(&st->core.cmd_lock);
+	return ret;
+}
+
+static int cros_ec_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 cros_ec_sensors_state *st = iio_priv(indio_dev);
+	int ret;
+
+	if (chan->type != IIO_ACTIVITY)
+		return -EINVAL;
+
+	mutex_lock(&st->core.cmd_lock);
+	st->core.param.cmd = MOTIONSENSE_CMD_SET_ACTIVITY;
+	switch (chan->channel2) {
+	case IIO_MOD_STILL:
+		st->core.param.set_activity.activity =
+			MOTIONSENSE_ACTIVITY_SIG_MOTION;
+		break;
+	case IIO_MOD_DOUBLE_TAP:
+		st->core.param.set_activity.activity =
+			MOTIONSENSE_ACTIVITY_DOUBLE_TAP;
+		break;
+	default:
+		dev_warn(&indio_dev->dev, "Unknown activity: %d\n",
+			 chan->channel2);
+	}
+	st->core.param.set_activity.enable = state;
+
+	ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+
+	mutex_unlock(&st->core.cmd_lock);
+	return ret;
+}
+
+/* Not implemented */
+static int cros_ec_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)
+{
+	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
+		 chan->channel2);
+	return -EPERM;
+}
+
+static int cros_ec_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)
+{
+	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
+		 chan->channel2);
+	return -EPERM;
+}
+
+static const struct iio_info ec_sensors_info = {
+	.read_raw = &ec_sensors_read,
+	.write_raw = &ec_sensors_write,
+	.read_event_config = cros_ec_read_event_config,
+	.write_event_config = cros_ec_write_event_config,
+	.read_event_value = cros_ec_read_event_value,
+	.write_event_value = cros_ec_write_event_value,
+};
+
+static int cros_ec_sensors_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+	struct cros_ec_device *ec_device;
+	struct iio_dev *indio_dev;
+	struct cros_ec_sensors_state *st;
+	struct iio_chan_spec *channel;
+	unsigned long activities;
+	int i, index, ret, nb_activities;
+
+	if (!ec_dev || !ec_dev->ec_dev) {
+		dev_warn(&pdev->dev, "No CROS EC device found.\n");
+		return -EINVAL;
+	}
+	ec_device = ec_dev->ec_dev;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+	if (ret)
+		return ret;
+
+	indio_dev->info = &ec_sensors_info;
+	st = iio_priv(indio_dev);
+	st->core.type = st->core.resp->info.type;
+	st->core.loc = st->core.resp->info.location;
+
+	/*
+	 * List all available activities
+	 */
+	st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES;
+	ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+	if (ret)
+		return ret;
+	activities = st->core.resp->list_activities.enabled |
+		     st->core.resp->list_activities.disabled;
+	nb_activities = hweight_long(activities) + 1;
+
+	if (!activities)
+		return -ENODEV;
+
+	/* Allocate a channel per activity and one for timestamp */
+	st->channels = devm_kcalloc(&pdev->dev, nb_activities,
+				    sizeof(*st->channels), GFP_KERNEL);
+	if (!st->channels)
+		return -ENOMEM;
+
+	channel = &st->channels[0];
+	index = 0;
+	for_each_set_bit(i, &activities, BITS_PER_LONG) {
+		channel->scan_index = index;
+
+		/* List all available activities */
+		channel->type = IIO_ACTIVITY;
+		channel->modified = 1;
+		channel->event_spec = cros_ec_activity_single_shot;
+		channel->num_event_specs =
+				ARRAY_SIZE(cros_ec_activity_single_shot);
+		switch (i) {
+		case MOTIONSENSE_ACTIVITY_SIG_MOTION:
+			channel->channel2 = IIO_MOD_STILL;
+			break;
+		case MOTIONSENSE_ACTIVITY_DOUBLE_TAP:
+			channel->channel2 = IIO_MOD_DOUBLE_TAP;
+			break;
+		default:
+			dev_warn(&pdev->dev, "Unknown activity: %d\n", i);
+			continue;
+		}
+		channel->ext_info = cros_ec_sensors_limited_info;
+		channel++;
+		index++;
+	}
+
+	/* Timestamp */
+	channel->scan_index = index;
+	channel->type = IIO_TIMESTAMP;
+	channel->channel = -1;
+	channel->scan_type.sign = 's';
+	channel->scan_type.realbits = 64;
+	channel->scan_type.storagebits = 64;
+
+	indio_dev->channels = st->channels;
+	indio_dev->num_channels = index + 1;
+
+	st->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
+
+	/* Driver is incomplete: by itself, no way to get event directly */
+	ret = iio_device_register(indio_dev);
+	return ret;
+}
+
+static int cros_ec_sensors_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	iio_device_unregister(indio_dev);
+	return 0;
+}
+
+static const struct platform_device_id cros_ec_sensors_ids[] = {
+	{
+		.name = "cros-ec-activity",
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
+
+static struct platform_driver cros_ec_sensors_platform_driver = {
+	.driver = {
+		.name	= "cros-ec-activity",
+	},
+	.probe		= cros_ec_sensors_probe,
+	.remove		= cros_ec_sensors_remove,
+	.id_table	= cros_ec_sensors_ids,
+};
+module_platform_driver(cros_ec_sensors_platform_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC activity sensors driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.0

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

* [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
                   ` (6 preceding siblings ...)
  2016-07-18  7:02 ` [PATCH 07/10] iio: cros_ec_activity: add ChromeOS EC Activity Sensors Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18 16:27   ` Jonathan Cameron
  2016-07-27  0:17   ` Guenter Roeck
  2016-07-18  7:02 ` [PATCH 09/10] platform/chrome: Introduce a new function to check EC features Enric Balletbo i Serra
  2016-07-18  7:02 ` [PATCH 10/10] platform/chrome: cros_ec_dev - Register cros-ec sensors Enric Balletbo i Serra
  9 siblings, 2 replies; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Add support for handling sensor events FIFO produced by the sensor
hub. A single device with a buffer will collect all samples produced
by the sensors managed by the CrosEC sensor hub.

Signed-off-by: Guenter Roeck <groeck@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/iio/common/cros_ec_sensors/Kconfig         |   9 +
 drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
 .../common/cros_ec_sensors/cros_ec_sensors_ring.c  | 541 +++++++++++++++++++++
 3 files changed, 551 insertions(+)
 create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c

diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
index 22b4211..778c3bf 100644
--- a/drivers/iio/common/cros_ec_sensors/Kconfig
+++ b/drivers/iio/common/cros_ec_sensors/Kconfig
@@ -39,3 +39,12 @@ config IIO_CROS_EC_ACTIVITY
 	  Activities can be simple (low/no motion) or more complex (riding train).
 	  They are being reported by physical devices or the EC itself.
 	  Creates an IIO device to manage all activities.
+
+config IIO_CROS_EC_SENSORS_RING
+	tristate "ChromeOS EC Sensors Ring"
+	depends on IIO_CROS_EC_SENSORS || IIO_CROS_EC_LIGHT_PROX
+	help
+	  Add support for handling sensor events FIFO produced by
+	  the sensor hub.
+	  A single device with a buffer will collect all samples produced
+	  by the sensors managed by the CrosEC sensor hub
diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
index 8f54f1e..0eb3fc5 100644
--- a/drivers/iio/common/cros_ec_sensors/Makefile
+++ b/drivers/iio/common/cros_ec_sensors/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
 obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
 obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
 obj-$(CONFIG_IIO_CROS_EC_ACTIVITY) += cros_ec_activity.o
+obj-$(CONFIG_IIO_CROS_EC_SENSORS_RING) += cros_ec_sensors_ring.o
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
new file mode 100644
index 0000000..1c74df9
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
@@ -0,0 +1,541 @@
+/*
+ * cros_ec_sensors_ring - Driver for Chrome OS EC Sensor hub FIFO.
+ *
+ * Copyright (C) 2015 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the cros-ec interface to communicate with the Chrome OS
+ * EC about accelerometer data. Accelerometer access is presented through
+ * iio sysfs.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+
+#include "cros_ec_sensors_core.h"
+
+/* The ring is a FIFO that return sensor information from
+ * the single EC FIFO.
+ * There are always 5 channels returned:
+* | ID | FLAG | X | Y | Z | Timestamp |
+ * ID is the EC sensor id
+ * FLAG is for meta data, only flush bit is defined.
+ */
+#define CROS_EC_FLUSH_BIT 1
+
+enum {
+	CHANNEL_SENSOR_ID,
+	CHANNEL_SENSOR_FLAG,
+	CHANNEL_X,
+	CHANNEL_Y,
+	CHANNEL_Z,
+	CHANNEL_TIMESTAMP,
+	MAX_CHANNEL,
+};
+
+enum {
+	LAST_TS,
+	NEW_TS,
+	ALL_TS
+};
+
+#define CROS_EC_SENSOR_MAX 16
+
+struct cros_ec_fifo_info {
+	struct ec_response_motion_sense_fifo_info info;
+	uint16_t lost[CROS_EC_SENSOR_MAX];
+};
+
+
+struct cros_ec_sensors_ring_sample {
+	uint8_t sensor_id;
+	uint8_t flag;
+	int16_t  vector[MAX_AXIS];
+	s64      timestamp;
+} __packed;
+
+/* State data for ec_sensors iio driver. */
+struct cros_ec_sensors_ring_state {
+	/* Shared by all sensors */
+	struct cros_ec_sensors_core_state core;
+
+	/* Notifier to kick to the interrupt */
+	struct notifier_block notifier;
+
+	/* Preprocessed ring to send to kfifo */
+	struct cros_ec_sensors_ring_sample *ring;
+
+	struct iio_trigger *trig;
+	s64    fifo_timestamp[ALL_TS];
+	struct ec_response_motion_sense_fifo_info fifo_info;
+};
+
+static const struct iio_info ec_sensors_info = {
+	.driver_module = THIS_MODULE,
+};
+
+static s64 cros_ec_get_time_ns(void)
+{
+	struct timespec ts;
+
+	get_monotonic_boottime(&ts);
+	return timespec_to_ns(&ts);
+}
+
+/*
+ * cros_ec_ring_process_event: process one EC FIFO event
+ *
+ * Process one EC event, add it in the ring if necessary.
+ *
+ * Return true if out event has been populated.
+ *
+ * fifo_info: fifo information from the EC.
+ * fifo_timestamp: timestamp at time of fifo_info collection.
+ * current_timestamp: estimated current timestamp.
+ * in: incoming FIFO event from EC
+ * out: outgoing event to user space.
+ */
+bool cros_ec_ring_process_event(const struct cros_ec_fifo_info *fifo_info,
+				const s64 fifo_timestamp,
+				s64 *current_timestamp,
+				struct ec_response_motion_sensor_data *in,
+				struct cros_ec_sensors_ring_sample *out)
+{
+	int axis;
+	s64 new_timestamp;
+
+	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) {
+		new_timestamp = fifo_timestamp -
+			((s64)fifo_info->info.timestamp * 1000) +
+			((s64)in->timestamp * 1000);
+		/*
+		 * The timestamp can be stale if we had to use the fifo
+		 * info timestamp.
+		 */
+		if (new_timestamp - *current_timestamp > 0)
+			*current_timestamp = new_timestamp;
+	}
+
+	if (in->flags & MOTIONSENSE_SENSOR_FLAG_FLUSH) {
+		out->sensor_id = in->sensor_num;
+		out->timestamp = *current_timestamp;
+		out->flag = CROS_EC_FLUSH_BIT;
+		return true;
+	}
+	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
+		/* If we just have a timestamp, skip this entry. */
+		return false;
+
+	/* Regular sample */
+	out->sensor_id = in->sensor_num;
+	out->timestamp = *current_timestamp;
+	out->flag = 0;
+	for (axis = X; axis < MAX_AXIS; axis++)
+		out->vector[axis] = in->data[axis];
+	return true;
+}
+/*
+ * cros_ec_ring_handler - the trigger handler function
+ *
+ * @irq: the interrupt number
+ * @p: private data - always a pointer to the poll func.
+ *
+ * On a trigger event occurring, if the pollfunc is attached then this
+ * handler is called as a threaded interrupt (and hence may sleep). It
+ * is responsible for grabbing data from the device and pushing it into
+ * the associated buffer.
+ */
+static irqreturn_t cros_ec_ring_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct cros_ec_sensors_ring_state *state = iio_priv(indio_dev);
+	struct cros_ec_fifo_info fifo_info;
+	s64    fifo_timestamp, current_timestamp;
+	int    i, j, number_data, ret;
+	unsigned long sensor_mask = 0;
+	struct ec_response_motion_sensor_data *in;
+	struct cros_ec_sensors_ring_sample *out, *last_out;
+
+
+	/* Get FIFO information */
+	mutex_lock(&state->core.cmd_lock);
+	memcpy(&fifo_info, &state->fifo_info, sizeof(state->fifo_info));
+	fifo_timestamp = state->fifo_timestamp[NEW_TS];
+	mutex_unlock(&state->core.cmd_lock);
+
+	/* Copy elements in the main fifo */
+	if (fifo_info.info.total_lost) {
+		/* Need to retrieve the number of lost vectors per sensor */
+		state->core.param.cmd = MOTIONSENSE_CMD_FIFO_INFO;
+		if (cros_ec_motion_send_host_cmd(&state->core, 0))
+			goto ring_handler_end;
+		memcpy(&fifo_info, &state->core.resp->fifo_info,
+		       sizeof(fifo_info));
+		fifo_timestamp = cros_ec_get_time_ns();
+	}
+
+	current_timestamp = state->fifo_timestamp[LAST_TS];
+	out = state->ring;
+	for (i = 0; i < fifo_info.info.count; i += number_data) {
+		state->core.param.cmd = MOTIONSENSE_CMD_FIFO_READ;
+		state->core.param.fifo_read.max_data_vector =
+			fifo_info.info.count - i;
+		ret = cros_ec_motion_send_host_cmd(&state->core,
+			       sizeof(state->core.resp->fifo_read) +
+			       state->core.param.fifo_read.max_data_vector *
+			       sizeof(struct ec_response_motion_sensor_data));
+		if (ret != EC_RES_SUCCESS) {
+			dev_warn(&indio_dev->dev, "Fifo error: %d\n", ret);
+			break;
+		}
+		number_data =
+			state->core.resp->fifo_read.number_data;
+		if (number_data == 0) {
+			dev_dbg(&indio_dev->dev, "Unexpected empty FIFO\n");
+			break;
+		}
+
+		for (in = state->core.resp->fifo_read.data, j = 0;
+		     j < number_data; j++, in++) {
+			BUG_ON(out >= state->ring + fifo_info.info.size);
+			if (cros_ec_ring_process_event(
+					&fifo_info, fifo_timestamp,
+					&current_timestamp, in, out)) {
+				sensor_mask |= (1 << in->sensor_num);
+				out++;
+			}
+		}
+	}
+	last_out = out;
+
+	if (out == state->ring)
+		/* Unexpected empty FIFO. */
+		goto ring_handler_end;
+
+	/*
+	 * Check if current_timestamp is ahead of the last sample.
+	 * Normally, the EC appends a timestamp after the last sample, but if
+	 * the AP is slow to respond to the IRQ, the EC may have added new
+	 * samples. Use the FIFO info timestamp as last timestamp then.
+	 */
+	if ((last_out-1)->timestamp == current_timestamp)
+		current_timestamp = fifo_timestamp;
+
+	/* check if buffer is set properly */
+	if (!indio_dev->active_scan_mask ||
+	    (bitmap_empty(indio_dev->active_scan_mask,
+			  indio_dev->masklength)))
+		goto ring_handler_end;
+
+	/*
+	 * calculate proper timestamps
+	 *
+	 * If there is a sample with a proper timestamp
+	 *                        timestamp | count
+	 * older_unprocess_out --> TS1      | 1
+	 *                         TS1      | 2
+	 * out -->                 TS1      | 3
+	 * next_out -->            TS2      |
+	 * We spread time for the samples [older_unprocess_out .. out]
+	 * between TS1 and TS2: [TS1+1/4, TS1+2/4, TS1+3/4, TS2].
+	 *
+	 * If we reach the end of the samples, we compare with the
+	 * current timestamp:
+	 *
+	 * older_unprocess_out --> TS1      | 1
+	 *                         TS1      | 2
+	 * out -->                 TS1      | 3
+	 * We know have [TS1+1/3, TS1+2/3, current timestamp]
+	 */
+	for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
+		s64 older_timestamp;
+		s64 timestamp;
+		struct cros_ec_sensors_ring_sample *older_unprocess_out =
+			state->ring;
+		struct cros_ec_sensors_ring_sample *next_out;
+		int count = 1;
+
+		if (fifo_info.info.total_lost) {
+			int lost = fifo_info.lost[i];
+
+			if (lost)
+				dev_warn(&indio_dev->dev,
+					"Sensor %d: lost: %d out of %d\n", i,
+					lost, fifo_info.info.total_lost);
+		}
+
+		for (out = state->ring; out < last_out; out = next_out) {
+			s64 time_period;
+
+			next_out = out + 1;
+			if (out->sensor_id != i)
+				continue;
+
+			/* Timestamp to start with */
+			older_timestamp = out->timestamp;
+
+			/* find next sample */
+			while (next_out < last_out && next_out->sensor_id != i)
+				next_out++;
+
+			if (next_out >= last_out) {
+				timestamp = current_timestamp;
+			} else {
+				timestamp = next_out->timestamp;
+				if (timestamp == older_timestamp) {
+					count++;
+					continue;
+				}
+			}
+
+			/* The next sample has a new timestamp,
+			 * spread the unprocessed samples
+			 */
+			if (next_out < last_out)
+				count++;
+			time_period = div_s64(timestamp - older_timestamp,
+					      count);
+
+			for (; older_unprocess_out <= out;
+					older_unprocess_out++) {
+				if (older_unprocess_out->sensor_id != i)
+					continue;
+				older_timestamp += time_period;
+				older_unprocess_out->timestamp =
+					older_timestamp;
+			}
+			count = 1;
+			/* The next_out sample has a valid timestamp, skip. */
+			next_out++;
+			older_unprocess_out = next_out;
+		}
+	}
+
+	/* push the event into the kfifo */
+	for (out = state->ring; out < last_out; out++)
+		iio_push_to_buffers(indio_dev, (u8 *)out);
+
+ring_handler_end:
+	state->fifo_timestamp[LAST_TS] = current_timestamp;
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int cros_ec_ring_event(struct notifier_block *nb,
+	unsigned long queued_during_suspend, void *_notify)
+{
+	struct cros_ec_sensors_ring_state *state;
+	struct cros_ec_device *ec;
+
+	state = container_of(nb, struct cros_ec_sensors_ring_state, notifier);
+	ec = state->core.ec;
+
+	if (ec->event_data.event_type != EC_MKBP_EVENT_SENSOR_FIFO)
+		return NOTIFY_DONE;
+
+	if (ec->event_size != sizeof(ec->event_data.data.sensor_fifo)) {
+		dev_warn(ec->dev, "Invalid fifo info size\n");
+		return NOTIFY_DONE;
+	}
+
+	if (queued_during_suspend)
+		return NOTIFY_OK;
+
+	mutex_lock(&state->core.cmd_lock);
+	memcpy(&state->fifo_info, &ec->event_data.data.sensor_fifo.info,
+			ec->event_size);
+	state->fifo_timestamp[NEW_TS] = cros_ec_get_time_ns();
+	mutex_unlock(&state->core.cmd_lock);
+
+	/*
+	 * We are not in a low level interrupt,
+	 * we can not call iio_trigger_poll().
+	 */
+	iio_trigger_poll_chained(state->trig);
+	return NOTIFY_OK;
+}
+
+#define CROS_EC_RING_ID(_id, _name)		\
+{						\
+	.type = IIO_ACCEL,			\
+	.modified = 1,				\
+	.channel2 = IIO_NO_MOD,			\
+	.scan_index = _id,			\
+	.scan_type = {				\
+		.sign = 'u',			\
+		.realbits = 8,			\
+		.storagebits = 8,		\
+	},					\
+	.extend_name = _name,			\
+}
+
+#define CROS_EC_RING_AXIS(_axis)		\
+{						\
+	.type = IIO_ACCEL,			\
+	.modified = 1,				\
+	.channel2 = IIO_MOD_##_axis,		\
+	.scan_index = CHANNEL_##_axis,		\
+	.scan_type = {				\
+		.sign = 's',			\
+		.realbits = 16,			\
+		.storagebits = 16,		\
+	},					\
+	.extend_name = "ring",			\
+}
+
+static const struct iio_chan_spec cros_ec_ring_channels[] = {
+	CROS_EC_RING_ID(CHANNEL_SENSOR_ID, "id"),
+	CROS_EC_RING_ID(CHANNEL_SENSOR_FLAG, "flag"),
+	CROS_EC_RING_AXIS(X),
+	CROS_EC_RING_AXIS(Y),
+	CROS_EC_RING_AXIS(Z),
+	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_TIMESTAMP)
+};
+
+static int cros_ec_ring_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+	struct cros_ec_device *ec_device;
+	struct iio_dev *indio_dev;
+	struct cros_ec_sensors_ring_state *state;
+	int ret;
+
+	if (!ec_dev || !ec_dev->ec_dev) {
+		dev_warn(&pdev->dev, "No CROS EC device found.\n");
+		return -EINVAL;
+	}
+	ec_device = ec_dev->ec_dev;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, false);
+	if (ret)
+		return ret;
+
+	state = iio_priv(indio_dev);
+	/* Retrieve FIFO information */
+	state->core.param.cmd = MOTIONSENSE_CMD_FIFO_INFO;
+	/* If it fails, just assume the FIFO is not supported.
+	 * For other errors, the other sensor drivers would have noticed
+	 * already.
+	 */
+	if (cros_ec_motion_send_host_cmd(&state->core, 0))
+		return -ENODEV;
+
+	/* Allocate the full fifo.
+	 * We need to copy the whole FIFO to set timestamps properly *
+	 */
+	state->ring = devm_kcalloc(&pdev->dev,
+			state->core.resp->fifo_info.size,
+			sizeof(*state->ring), GFP_KERNEL);
+	if (!state->ring)
+		return -ENOMEM;
+
+	state->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
+
+	indio_dev->channels = cros_ec_ring_channels;
+	indio_dev->num_channels = ARRAY_SIZE(cros_ec_ring_channels);
+
+	indio_dev->info = &ec_sensors_info;
+
+	state->trig = devm_iio_trigger_alloc(&pdev->dev,
+			"%s-trigger%d", indio_dev->name, indio_dev->id);
+	if (!state->trig)
+		return -ENOMEM;
+	state->trig->dev.parent = &pdev->dev;
+	iio_trigger_set_drvdata(state->trig, indio_dev);
+
+	ret = iio_trigger_register(state->trig);
+	if (ret)
+		return ret;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 cros_ec_ring_handler, NULL);
+	if (ret < 0)
+		goto err_trigger_unregister;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto err_buffer_cleanup;
+
+	/* register the notifier that will act as a top half interrupt. */
+	state->notifier.notifier_call = cros_ec_ring_event;
+	ret = blocking_notifier_chain_register(&ec_device->event_notifier,
+					       &state->notifier);
+	if (ret < 0) {
+		dev_warn(&indio_dev->dev, "failed to register notifier\n");
+		goto err_device_unregister;
+	}
+	return ret;
+
+err_device_unregister:
+	iio_device_unregister(indio_dev);
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_trigger_unregister:
+	iio_trigger_unregister(state->trig);
+	return ret;
+}
+
+static int cros_ec_ring_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct cros_ec_sensors_ring_state *state = iio_priv(indio_dev);
+	struct cros_ec_device *ec = state->core.ec;
+
+	blocking_notifier_chain_unregister(&ec->event_notifier,
+					   &state->notifier);
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	iio_trigger_unregister(state->trig);
+	return 0;
+}
+
+static const struct platform_device_id cros_ec_ring_ids[] = {
+	{
+		.name = "cros-ec-ring",
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_ring_ids);
+
+static struct platform_driver cros_ec_ring_platform_driver = {
+	.driver = {
+		.name	= "cros-ec-ring",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= cros_ec_ring_probe,
+	.remove		= cros_ec_ring_remove,
+	.id_table	= cros_ec_ring_ids,
+};
+module_platform_driver(cros_ec_ring_platform_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC sensor hub ring driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.0

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

* [PATCH 09/10] platform/chrome: Introduce a new function to check EC features.
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
                   ` (7 preceding siblings ...)
  2016-07-18  7:02 ` [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  2016-07-18  7:02 ` [PATCH 10/10] platform/chrome: cros_ec_dev - Register cros-ec sensors Enric Balletbo i Serra
  9 siblings, 0 replies; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou, Vincent Palatin, Tomeu Vizoso

From: Vincent Palatin <vpalatin@chromium.org>

Use the EC_CMD_GET_FEATURES message to check the supported features for
each MCU.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
[tomeu: adapted to changes in mainline]
Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
[enric: remove references to USB PD feature and do it more generic]
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/platform/chrome/cros_ec_dev.c | 37 +++++++++++++++
 include/linux/mfd/cros_ec.h           |  1 +
 include/linux/mfd/cros_ec_commands.h  | 84 +++++++++++++++++++++++++++++++++++
 3 files changed, 122 insertions(+)

diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index 8abd80d..fa5f060 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -87,6 +87,41 @@ exit:
 	return ret;
 }
 
+static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
+{
+	struct cros_ec_command *msg;
+	int ret;
+
+	if (ec->features[0] == -1U && ec->features[1] == -1U) {
+		/* features bitmap not read yet */
+
+		msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
+		if (!msg)
+			return -ENOMEM;
+
+		msg->version = 0;
+		msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
+		msg->insize = sizeof(ec->features);
+		msg->outsize = 0;
+
+		ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+		if ((ret < 0) || msg->result != EC_RES_SUCCESS) {
+			dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
+				 ret, msg->result);
+			memset(ec->features, 0, sizeof(ec->features));
+		}
+
+		memcpy(ec->features, msg->data, sizeof(ec->features));
+
+		dev_dbg(ec->dev, "EC features %08x %08x\n",
+			ec->features[0], ec->features[1]);
+
+		kfree(msg);
+	}
+
+	return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
+}
+
 /* Device file ops */
 static int ec_device_open(struct inode *inode, struct file *filp)
 {
@@ -245,6 +280,8 @@ static int ec_device_probe(struct platform_device *pdev)
 	ec->ec_dev = dev_get_drvdata(dev->parent);
 	ec->dev = dev;
 	ec->cmd_offset = ec_platform->cmd_offset;
+	ec->features[0] = -1U; /* Not cached yet */
+	ec->features[1] = -1U; /* Not cached yet */
 	device_initialize(&ec->class_dev);
 	cdev_init(&ec->cdev, &fops);
 
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index cd4833a..402b5b3 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -188,6 +188,7 @@ struct cros_ec_dev {
 	struct cros_ec_device *ec_dev;
 	struct device *dev;
 	u16 cmd_offset;
+	u32 features[2];
 };
 
 /**
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
index f26a806..2fbc595 100644
--- a/include/linux/mfd/cros_ec_commands.h
+++ b/include/linux/mfd/cros_ec_commands.h
@@ -713,6 +713,90 @@ struct ec_response_get_set_value {
 /* More than one command can use these structs to get/set paramters. */
 #define EC_CMD_GSV_PAUSE_IN_S5	0x0c
 
+/*****************************************************************************/
+/* List the features supported by the firmware */
+#define EC_CMD_GET_FEATURES  0x0d
+
+/* Supported features */
+enum ec_feature_code {
+	/*
+	 * This image contains a limited set of features. Another image
+	 * in RW partition may support more features.
+	 */
+	EC_FEATURE_LIMITED = 0,
+	/*
+	 * Commands for probing/reading/writing/erasing the flash in the
+	 * EC are present.
+	 */
+	EC_FEATURE_FLASH = 1,
+	/*
+	 * Can control the fan speed directly.
+	 */
+	EC_FEATURE_PWM_FAN = 2,
+	/*
+	 * Can control the intensity of the keyboard backlight.
+	 */
+	EC_FEATURE_PWM_KEYB = 3,
+	/*
+	 * Support Google lightbar, introduced on Pixel.
+	 */
+	EC_FEATURE_LIGHTBAR = 4,
+	/* Control of LEDs  */
+	EC_FEATURE_LED = 5,
+	/* Exposes an interface to control gyro and sensors.
+	 * The host goes through the EC to access these sensors.
+	 * In addition, the EC may provide composite sensors, like lid angle.
+	 */
+	EC_FEATURE_MOTION_SENSE = 6,
+	/* The keyboard is controlled by the EC */
+	EC_FEATURE_KEYB = 7,
+	/* The AP can use part of the EC flash as persistent storage. */
+	EC_FEATURE_PSTORE = 8,
+	/* The EC monitors BIOS port 80h, and can return POST codes. */
+	EC_FEATURE_PORT80 = 9,
+	/*
+	 * Thermal management: include TMP specific commands.
+	 * Higher level than direct fan control.
+	 */
+	EC_FEATURE_THERMAL = 10,
+	/* Can switch the screen backlight on/off */
+	EC_FEATURE_BKLIGHT_SWITCH = 11,
+	/* Can switch the wifi module on/off */
+	EC_FEATURE_WIFI_SWITCH = 12,
+	/* Monitor host events, through for example SMI or SCI */
+	EC_FEATURE_HOST_EVENTS = 13,
+	/* The EC exposes GPIO commands to control/monitor connected devices. */
+	EC_FEATURE_GPIO = 14,
+	/* The EC can send i2c messages to downstream devices. */
+	EC_FEATURE_I2C = 15,
+	/* Command to control charger are included */
+	EC_FEATURE_CHARGER = 16,
+	/* Simple battery support. */
+	EC_FEATURE_BATTERY = 17,
+	/*
+	 * Support Smart battery protocol
+	 * (Common Smart Battery System Interface Specification)
+	 */
+	EC_FEATURE_SMART_BATTERY = 18,
+	/* EC can dectect when the host hangs. */
+	EC_FEATURE_HANG_DETECT = 19,
+	/* Report power information, for pit only */
+	EC_FEATURE_PMU = 20,
+	/* Another Cros EC device is present downstream of this one */
+	EC_FEATURE_SUB_MCU = 21,
+	/* Support USB Power delivery (PD) commands */
+	EC_FEATURE_USB_PD = 22,
+	/* Control USB multiplexer, for audio through USB port for instance. */
+	EC_FEATURE_USB_MUX = 23,
+	/* Motion Sensor code has an internal software FIFO */
+	EC_FEATURE_MOTION_SENSE_FIFO = 24,
+};
+
+#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
+#define EC_FEATURE_MASK_1(event_code) (1UL << (event_code - 32))
+struct ec_response_get_features {
+	uint32_t flags[2];
+} __packed;
 
 /*****************************************************************************/
 /* Flash commands */
-- 
2.1.0

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

* [PATCH 10/10] platform/chrome: cros_ec_dev - Register cros-ec sensors
  2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
                   ` (8 preceding siblings ...)
  2016-07-18  7:02 ` [PATCH 09/10] platform/chrome: Introduce a new function to check EC features Enric Balletbo i Serra
@ 2016-07-18  7:02 ` Enric Balletbo i Serra
  9 siblings, 0 replies; 25+ messages in thread
From: Enric Balletbo i Serra @ 2016-07-18  7:02 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Guenter Roeck,
	Gwendal Grignou

Check whether the ChromeOS Embedded Controller is a sensor hub and in
such case issue a command to get the number of sensors and register them
all.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/platform/chrome/cros_ec_dev.c | 122 ++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)

diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index fa5f060..0bec60e 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -18,6 +18,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/mfd/core.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -265,6 +266,123 @@ static void __remove(struct device *dev)
 	kfree(ec);
 }
 
+static void cros_ec_sensors_register(struct cros_ec_dev *ec)
+{
+	/*
+	 * Issue a command to get the number of sensor reported.
+	 * Build an array of sensors driver and register them all.
+	 */
+	int ret, i, id, sensor_num;
+	struct mfd_cell *sensor_cells;
+	struct cros_ec_sensor_platform *sensor_platforms;
+	int sensor_type[MOTIONSENSE_TYPE_MAX];
+	struct ec_params_motion_sense *params;
+	struct ec_response_motion_sense *resp;
+	struct cros_ec_command *msg;
+
+	msg = kzalloc(sizeof(struct cros_ec_command) +
+		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
+	if (msg == NULL)
+		return;
+
+	msg->version = 2;
+	msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+	msg->outsize = sizeof(*params);
+	msg->insize = sizeof(*resp);
+
+	params = (struct ec_params_motion_sense *)msg->data;
+	params->cmd = MOTIONSENSE_CMD_DUMP;
+
+	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+	if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+		dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
+			 ret, msg->result);
+		goto error;
+	}
+
+	resp = (struct ec_response_motion_sense *)msg->data;
+	sensor_num = resp->dump.sensor_count;
+	/* Allocate 2 extra sensors in case lid angle or FIFO are needed */
+	sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
+			       GFP_KERNEL);
+	if (sensor_cells == NULL)
+		goto error;
+
+	sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
+		  (sensor_num + 1), GFP_KERNEL);
+	if (sensor_platforms == NULL)
+		goto error_platforms;
+
+	memset(sensor_type, 0, sizeof(sensor_type));
+	id = 0;
+	for (i = 0; i < sensor_num; i++) {
+		params->cmd = MOTIONSENSE_CMD_INFO;
+		params->info.sensor_num = i;
+		ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+		if ((ret < 0) || msg->result != EC_RES_SUCCESS) {
+			dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
+				 i, ret, msg->result);
+			continue;
+		}
+		switch (resp->info.type) {
+		case MOTIONSENSE_TYPE_ACCEL:
+			sensor_cells[id].name = "cros-ec-accel";
+			break;
+		case MOTIONSENSE_TYPE_GYRO:
+			sensor_cells[id].name = "cros-ec-gyro";
+			break;
+		case MOTIONSENSE_TYPE_MAG:
+			sensor_cells[id].name = "cros-ec-mag";
+			break;
+		case MOTIONSENSE_TYPE_PROX:
+			sensor_cells[id].name = "cros-ec-prox";
+			break;
+		case MOTIONSENSE_TYPE_LIGHT:
+			sensor_cells[id].name = "cros-ec-light";
+			break;
+		case MOTIONSENSE_TYPE_ACTIVITY:
+			sensor_cells[id].name = "cros-ec-activity";
+			break;
+		default:
+			dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
+			continue;
+		}
+		sensor_platforms[id].sensor_num = i;
+		sensor_cells[id].id = sensor_type[resp->info.type];
+		sensor_cells[id].platform_data = &sensor_platforms[id];
+		sensor_cells[id].pdata_size =
+			sizeof(struct cros_ec_sensor_platform);
+
+		sensor_type[resp->info.type]++;
+		id++;
+	}
+	if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
+		sensor_platforms[id].sensor_num = sensor_num;
+
+		sensor_cells[id].name = "cros-ec-angle";
+		sensor_cells[id].id = 0;
+		sensor_cells[id].platform_data = &sensor_platforms[id];
+		sensor_cells[id].pdata_size =
+			sizeof(struct cros_ec_sensor_platform);
+		id++;
+	}
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+		sensor_cells[id].name = "cros-ec-ring";
+		id++;
+	}
+
+	ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
+			      NULL, 0, NULL);
+	if (ret)
+		dev_err(ec->dev, "failed to add EC sensors\n");
+
+	kfree(sensor_platforms);
+error_platforms:
+	kfree(sensor_cells);
+error:
+	kfree(msg);
+}
+
 static int ec_device_probe(struct platform_device *pdev)
 {
 	int retval = -ENOMEM;
@@ -319,6 +437,10 @@ static int ec_device_probe(struct platform_device *pdev)
 		goto dev_reg_failed;
 	}
 
+	/* check whether this EC is a sensor hub. */
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
+		cros_ec_sensors_register(ec);
+
 	return 0;
 
 dev_reg_failed:
-- 
2.1.0

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

* Re: [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors.
  2016-07-18  7:02 ` [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors Enric Balletbo i Serra
@ 2016-07-18  7:29   ` Peter Meerwald-Stadler
  2016-07-18 15:51     ` Jonathan Cameron
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Meerwald-Stadler @ 2016-07-18  7:29 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: linux-kernel, linux-iio, Olof Johansson, Lee Jones,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Guenter Roeck, Gwendal Grignou


> Add the core functions to be able to support the sensors attached behind
> the ChromeOS Embedded Controller and used by other IIO cros-ec sensor
> drivers.

comments below from a quick read
there is plenty on undocumented private API
 
> The cros_ec_sensor_core driver matches with current driver in ChromeOS
> 4.4 tree, so it includes all the fixes at the moment. The support for
> this driver was made by Gwendal Grignou. The original patch and all the
> fixes has been squashed and rebased on top of mainline.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> Signed-off-by: Guenter Roeck <groeck@chromium.org>
> [eballetbo: split, squash and rebase on top of mainline the patches
> found in ChromeOS tree]
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> ---
>  drivers/iio/common/Kconfig                         |   1 +
>  drivers/iio/common/Makefile                        |   1 +
>  drivers/iio/common/cros_ec_sensors/Kconfig         |  14 +
>  drivers/iio/common/cros_ec_sensors/Makefile        |   5 +
>  .../common/cros_ec_sensors/cros_ec_sensors_core.c  | 564 +++++++++++++++++++++
>  .../common/cros_ec_sensors/cros_ec_sensors_core.h  | 155 ++++++
>  6 files changed, 740 insertions(+)
>  create mode 100644 drivers/iio/common/cros_ec_sensors/Kconfig
>  create mode 100644 drivers/iio/common/cros_ec_sensors/Makefile
>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
> 
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index 26a6026..e108996 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -2,6 +2,7 @@
>  # IIO common modules
>  #
>  
> +source "drivers/iio/common/cros_ec_sensors/Kconfig"
>  source "drivers/iio/common/hid-sensors/Kconfig"
>  source "drivers/iio/common/ms_sensors/Kconfig"
>  source "drivers/iio/common/ssp_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 585da6a..6fa760e 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -7,6 +7,7 @@
>  #
>  
>  # When adding new entries keep the list in alphabetical order
> +obj-y += cros_ec_sensors/
>  obj-y += hid-sensors/
>  obj-y += ms_sensors/
>  obj-y += ssp_sensors/
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> new file mode 100644
> index 0000000..a30f41e
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -0,0 +1,14 @@
> +#
> +# Chrome OS Embedded Controller managed sensors library
> +#
> +config IIO_CROS_EC_SENSORS_CORE
> +	tristate "ChromeOS EC Sensors Core"
> +	depends on SYSFS && MFD_CROS_EC
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Base module for the ChromeOS EC Sensors module.
> +	  Contains core functions used by other IIO CrosEC sensor
> +	  driver.

'drivers' probably

> +	  Define common attributes and sysfs interrupt handler.
> +
> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
> new file mode 100644
> index 0000000..95b6901
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for sensors seen through the ChromeOS EC sensor hub.
> +#
> +
> +obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> new file mode 100644
> index 0000000..cb3de8f
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -0,0 +1,564 @@
> +/*
> + * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
> + *
> + * Copyright (C) 2015 Google, Inc
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * This driver uses the cros-ec interface to communicate with the Chrome OS
> + * EC about accelerometer data. Accelerometer access is presented through
> + * iio sysfs.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/mfd/cros_ec_commands.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/platform_device.h>
> +
> +#include "cros_ec_sensors_core.h"
> +
> +static char *cros_ec_loc[] = {
> +	[MOTIONSENSE_LOC_BASE] = "base",
> +	[MOTIONSENSE_LOC_LID] = "lid",
> +	[MOTIONSENSE_LOC_MAX] = "unknown",
> +};
> +
> +/*
> + * cros_ec_sensors_core_init
> + *
> + * Initialize core sensor strucure, fill the response are
> + * with the return of Sensor info.

rephrase please

> + *
> + * @pdev plarform device created for the sensors

platform

> + * @indio_dev iio device structure of the device
> + * @physical_device True if the device refers to a physical device.
> + */
> +int cros_ec_sensors_core_init(struct platform_device *pdev,
> +			      struct iio_dev *indio_dev,
> +			      bool physical_device)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
> +	struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
> +	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	state->ec = ec->ec_dev;
> +	state->msg = devm_kzalloc(&pdev->dev,
> +				max((u16)sizeof(struct ec_params_motion_sense),
> +				state->ec->max_response), GFP_KERNEL);
> +	if (!state->msg)
> +		return -ENOMEM;
> +
> +	state->resp = (struct ec_response_motion_sense *)state->msg->data;
> +
> +	mutex_init(&state->cmd_lock);
> +	/* Set up the host command structure. */
> +	state->msg->version = 2;
> +	state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
> +	state->msg->outsize = sizeof(struct ec_params_motion_sense);
> +
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->name = pdev->name;
> +
> +	if (physical_device) {
> +		indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +		state->param.cmd = MOTIONSENSE_CMD_INFO;
> +		state->param.info.sensor_num = sensor_platform->sensor_num;
> +		if (cros_ec_motion_send_host_cmd(state, 0)) {
> +			dev_warn(dev, "Can not access sensor info\n");
> +			return -EIO;
> +		}
> +		state->type = state->resp->info.type;
> +		state->loc = state->resp->info.location;
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
> +
> +/*
> + * cros_ec_motion_send_host_cmd - send motion sense host command
> + *
> + * @st Pointer to state information for device.

@state
opt_length is not documented

> + * @return 0 if ok, -ve on error.

what is -ve?

> + *
> + * Note, when called, the sub-command is assumed to be set in param->cmd.
> + */
> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
> +				 u16 opt_length)
> +{
> +	int ret;
> +
> +	if (opt_length)
> +		state->msg->insize = min(opt_length, state->ec->max_response);
> +	else
> +		state->msg->insize = state->ec->max_response;
> +
> +	memcpy(state->msg->data, &state->param, sizeof(state->param));
> +	/* Send host command. */

comment needed?

> +	ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
> +	if (ret < 0)
> +		return -EIO;
> +
> +	if (ret &&
> +	    state->resp != (struct ec_response_motion_sense *)state->msg->data)
> +		memcpy(state->resp, state->msg->data, ret);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
> +
> +static ssize_t __maybe_unused cros_ec_sensors_flush(struct iio_dev *indio_dev,
> +		uintptr_t private, const struct iio_chan_spec *chan,
> +		const char *buf, size_t len)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	int ret = 0;

initialization not needed

> +	bool flush;
> +
> +	ret = strtobool(buf, &flush);
> +	if (ret < 0)
> +		return ret;
> +	if (!flush)
> +		return -EINVAL;
> +
> +	mutex_lock(&st->cmd_lock);
> +	st->param.cmd = MOTIONSENSE_CMD_FIFO_FLUSH;
> +	ret = cros_ec_motion_send_host_cmd(st, 0);
> +	if (ret != 0)
> +		dev_warn(&indio_dev->dev, "Unable to flush sensor\n");
> +	mutex_unlock(&st->cmd_lock);
> +	return ret ? ret : len;
> +}
> +
> +static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
> +		uintptr_t private, const struct iio_chan_spec *chan,
> +		const char *buf, size_t len)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	int ret, i;
> +	bool calibrate;
> +
> +	ret = strtobool(buf, &calibrate);
> +	if (ret < 0)
> +		return ret;
> +	if (!calibrate)
> +		return -EINVAL;
> +
> +	mutex_lock(&st->cmd_lock);
> +	st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
> +	ret = cros_ec_motion_send_host_cmd(st, 0);
> +	if (ret != 0) {
> +		dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
> +	} else {
> +		/* Save values */
> +		for (i = X; i < MAX_AXIS; i++)

what is X here?

> +			st->calib[i].offset = st->resp->perform_calib.offset[i];
> +	}
> +	mutex_unlock(&st->cmd_lock);
> +	return ret ? ret : len;
> +}
> +
> +static ssize_t cros_ec_sensors_id(struct iio_dev *indio_dev,
> +		uintptr_t private, const struct iio_chan_spec *chan,
> +		char *buf)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +
> +	return sprintf(buf, "%d\n", st->param.info.sensor_num);

can't we be a bit more careful with buffer sizes here (and below)?

> +}
> +
> +static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
> +		uintptr_t private, const struct iio_chan_spec *chan,
> +		char *buf)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +
> +	return sprintf(buf, "%s\n", cros_ec_loc[st->loc]);
> +}
> +
> +const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
> +#ifdef CONFIG_IIO_CROS_EC_SENSORS_RING
> +	{
> +		.name = "flush",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.write = cros_ec_sensors_flush
> +	},
> +#endif
> +	{
> +		.name = "calibrate",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.write = cros_ec_sensors_calibrate
> +	},
> +	{
> +		.name = "id",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.read = cros_ec_sensors_id
> +	},
> +	{
> +		.name = "location",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.read = cros_ec_sensors_loc
> +	},
> +	{ },
> +};
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
> +
> +const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[] = {
> +	{
> +		.name = "id",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.read = cros_ec_sensors_id
> +	},
> +	{
> +		.name = "location",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.read = cros_ec_sensors_loc
> +	},
> +	{ },
> +};
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_limited_info);
> +/*
> + * idx_to_reg - convert sensor index into offset in shared memory region.
> + *
> + * @st: private data
> + * @idx: sensor index (should be element of enum sensor_index)
> + * @return address to read at.
> + */
> +static unsigned int idx_to_reg(struct cros_ec_sensors_core_state *st,

cros_ec_ prefix please (here and below)

> +			       unsigned int idx)
> +{
> +	/*
> +	 * When using LPC interface, only space for 2 Accel and one Gyro.
> +	 * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
> +	 */
> +	if (st->type == MOTIONSENSE_TYPE_ACCEL)
> +		return EC_MEMMAP_ACC_DATA + sizeof(u16) *
> +			(1 + idx + st->param.info.sensor_num *
> +			 MAX_AXIS);
> +	else
> +		return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
> +}
> +
> +static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
> +			  u8 *dest)
> +{
> +	return ec->cmd_readmem(ec, offset, 1, dest);
> +}
> +
> +static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
> +			   u16 *dest)
> +{
> +	u16 tmp;
> +	int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
> +
> +	*dest = le16_to_cpu(tmp);
> +
> +	return ret;
> +}
> +
> +/*
> + * read_ec_until_not_busy - read from EC status byte until it reads not busy.
> + *
> + * @st Pointer to state information for device.
> + * @return 8-bit status if ok, -ve on error
> + */
> +static int read_ec_until_not_busy(struct cros_ec_sensors_core_state *st)
> +{
> +	struct cros_ec_device *ec = st->ec;
> +	u8 status;
> +	int attempts = 0;
> +
> +	ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);

check return value (here and below)?

> +	while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
> +		/* Give up after enough attempts, return error. */
> +		if (attempts++ >= 50)
> +			return -EIO;
> +
> +		/* Small delay every so often. */
> +		if (attempts % 5 == 0)
> +			msleep(25);
> +
> +		ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
> +	}
> +
> +	return status;
> +}
> +
> +/*
> + * read_ec_sensors_data_unsafe - read acceleration data from EC shared memory.
> + *
> + * @st Pointer to state information for device.

@indio_dev? here and below

> + * @scan_mask Bitmap of the sensor indices to scan.
> + * @data Location to store data.
> + *
> + * Note this is the unsafe function for reading the EC data. It does not
> + * guarantee that the EC will not modify the data as it is being read in.
> + */
> +static void read_ec_sensors_data_unsafe(struct iio_dev *indio_dev,
> +			 unsigned long scan_mask, s16 *data)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	struct cros_ec_device *ec = st->ec;
> +	unsigned int i = 0;

init not needed

> +
> +	/*
> +	 * Read all sensors enabled in scan_mask. Each value is 2
> +	 * bytes.
> +	 */
> +	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
> +		ec_cmd_read_u16(ec, idx_to_reg(st, i), data);

no return value?

> +		data++;
> +	}
> +}
> +
> +/*
> + * cros_ec_sensors_read_lpc - read acceleration data from EC shared memory.
> + *
> + * @st Pointer to state information for device.
> + * @scan_mask Bitmap of the sensor indices to scan.
> + * @data Location to store data.
> + * @return 0 if ok, -ve on error
> + *
> + * Note: this is the safe function for reading the EC data. It guarantees
> + * that the data sampled was not modified by the EC while being read.
> + */
> +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
> +			     unsigned long scan_mask, s16 *data)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	struct cros_ec_device *ec = st->ec;
> +	u8 samp_id = 0xff, status = 0;
> +	int attempts = 0;
> +
> +	/*
> +	 * Continually read all data from EC until the status byte after
> +	 * all reads reflects that the EC is not busy and the sample id
> +	 * matches the sample id from before all reads. This guarantees
> +	 * that data read in was not modified by the EC while reading.
> +	 */
> +	while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
> +			  EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
> +		/* If we have tried to read too many times, return error. */
> +		if (attempts++ >= 5)
> +			return -EIO;
> +
> +		/* Read status byte until EC is not busy. */
> +		status = read_ec_until_not_busy(st);
> +		if (status < 0)
> +			return status;
> +
> +		/*
> +		 * Store the current sample id so that we can compare to the
> +		 * sample id after reading the data.
> +		 */
> +		samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
> +
> +		/* Read all EC data, format it, and store it into data. */
> +		read_ec_sensors_data_unsafe(indio_dev, scan_mask, data);
> +
> +		/* Read status byte. */
> +		ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
> +
> +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
> +			     unsigned long scan_mask, s16 *data)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	int ret;
> +	unsigned int i = 0;
> +
> +	/*
> +	 * read all sensor data through a command.

start with uppercase as everywhere else

> +	 */
> +	st->param.cmd = MOTIONSENSE_CMD_DATA;
> +	ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
> +	if (ret != 0) {
> +		dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
> +		return ret;
> +	}
> +
> +	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
> +		*data = st->resp->data.data[i];
> +		data++;
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
> +
> +irqreturn_t cros_ec_sensors_capture(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +
> +	mutex_lock(&st->cmd_lock);
> +	/* Clear capture data. */
> +	memset(st->samples, 0, indio_dev->scan_bytes);
> +
> +	/* Read data based on which channels are enabled in scan mask. */
> +	st->read_ec_sensors_data(indio_dev, *(indio_dev->active_scan_mask),
> +			   (s16 *)st->samples);

no return value?

> +
> +	/* Store the timestamp last 8 bytes of data. */
> +	if (indio_dev->scan_timestamp)
> +		*(s64 *)&st->samples[round_down(indio_dev->scan_bytes -
> +						sizeof(s64),
> +				     sizeof(s64))] = iio_get_time_ns();
> +
> +	iio_push_to_buffers(indio_dev, st->samples);

use iio_push_to_buffers_with_timestamp()

> +
> +	/*
> +	 * Tell the core we are done with this trigger and ready for the
> +	 * next one.
> +	 */
> +	iio_trigger_notify_done(indio_dev->trig);
> +	mutex_unlock(&st->cmd_lock);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
> +
> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
> +			  struct iio_chan_spec const *chan,
> +			  int *val, int *val2, long mask)
> +{
> +	int ret = IIO_VAL_INT;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> +		st->param.ec_rate.data =
> +			EC_MOTION_SENSE_NO_VALUE;
> +
> +		if (cros_ec_motion_send_host_cmd(st, 0))
> +			ret = -EIO;
> +		else
> +			*val = st->resp->ec_rate.ret;
> +		break;
> +	case IIO_CHAN_INFO_FREQUENCY:
> +		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
> +		st->param.sensor_odr.data =
> +			EC_MOTION_SENSE_NO_VALUE;
> +
> +		if (cros_ec_motion_send_host_cmd(st, 0))
> +			ret = -EIO;
> +		else
> +			*val = st->resp->sensor_odr.ret;
> +		break;
> +	default:
> +		break;
> +	}
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
> +
> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
> +			       struct iio_chan_spec const *chan,
> +			       int val, int val2, long mask)
> +{
> +	int ret = 0;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_FREQUENCY:
> +		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
> +		st->param.sensor_odr.data = val;
> +
> +		/* Always roundup, so caller gets at least what it asks for. */
> +		st->param.sensor_odr.roundup = 1;
> +
> +		if (cros_ec_motion_send_host_cmd(st, 0))
> +			ret = -EIO;
> +		break;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> +		st->param.ec_rate.data = val;
> +
> +		if (cros_ec_motion_send_host_cmd(st, 0))
> +			ret = -EIO;
> +		else
> +			st->curr_sampl_freq = val;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
> +
> +static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +
> +	if (st->curr_sampl_freq == 0)
> +		return 0;
> +
> +	/*
> +	 * If the sensors are sampled at high frequency, we will not be able to
> +	 * sleep. Set to sampling to a long period if necessary.
> +	 */
> +	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
> +		mutex_lock(&st->cmd_lock);
> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> +		st->param.ec_rate.data = CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY;
> +		cros_ec_motion_send_host_cmd(st, 0);
> +		mutex_unlock(&st->cmd_lock);
> +	}
> +	return 0;
> +}
> +
> +static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +
> +	if (st->curr_sampl_freq == 0)
> +		return;
> +
> +	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
> +		mutex_lock(&st->cmd_lock);
> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> +		st->param.ec_rate.data = st->curr_sampl_freq;
> +		cros_ec_motion_send_host_cmd(st, 0);
> +		mutex_unlock(&st->cmd_lock);
> +	}
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +const struct dev_pm_ops cros_ec_sensors_pm_ops = {
> +	.prepare = cros_ec_sensors_prepare,
> +	.complete = cros_ec_sensors_complete
> +};
> +#else
> +const struct dev_pm_ops cros_ec_sensors_pm_ops = { };
> +#endif
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
> +
> +MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
> new file mode 100644
> index 0000000..53e09e1
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
> @@ -0,0 +1,155 @@
> +/*
> + * ChromeOS EC sensor hub
> + *
> + * Copyright (C) 2015 Google, Inc
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __CROS_EC_SENSORS_CORE_H
> +#define __CROS_EC_SENSORS_CORE_H
> +
> +#include <linux/irqreturn.h>
> +
> +enum {
> +	X,

such short #defines scare me :)

> +	Y,
> +	Z,
> +	MAX_AXIS,
> +};
> +
> +/*
> + * EC returns sensor values using signed 16 bit registers
> + */
> +#define CROS_EC_SENSOR_BITS 16
> +
> +/*
> + * 4 16 bit channels are allowed.
> + * Good enough for current sensors, thye use up to 3 16 bit vectors.

they

> + */
> +#define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
> +
> +/*
> + * minimum sampling period to use when device is suspending.

Minimum (uppercase)

> + */
> +#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
> +
> +/*
> + * Function to read the sensor data.
> + *
> + * Data can be retrieve using the cros ec command protocol.
> + * Some machines also allow accessing some sensor data via
> + * IO space.
> + */
> +typedef int (cros_ec_sensors_read_t)(struct iio_dev *indio_dev,
> +		unsigned long scan_mask, s16 *data);
> +
> +cros_ec_sensors_read_t cros_ec_sensors_read_lpc;
> +cros_ec_sensors_read_t cros_ec_sensors_read_cmd;
> +
> +/* State data for ec_sensors iio driver. */
> +struct cros_ec_sensors_core_state {
> +	struct cros_ec_device *ec;
> +	/*
> +	 *  Location to store command and response to the EC.
> +	 */
> +	struct mutex cmd_lock;
> +
> +	/*
> +	 * Statically allocated command structure that holds parameters
> +	 * and response.
> +	 */
> +	struct cros_ec_command *msg;
> +	struct ec_params_motion_sense param;
> +	struct ec_response_motion_sense *resp;
> +
> +	/* Type of sensor */
> +	enum motionsensor_type type;
> +	enum motionsensor_location loc;
> +
> +	/*
> +	 * Calibration parameters. Note that trigger captured data will always
> +	 * provide the calibrated values.
> +	 */
> +	struct calib_data {
> +		s16 offset;
> +	} calib[MAX_AXIS];
> +
> +	/*
> +	 * Static array to hold data from a single capture. For each
> +	 * channel we need 2 bytes, except for the timestamp. The timestamp
> +	 * is always last and is always 8-byte aligned.
> +	 */
> +	u8 samples[CROS_EC_SAMPLE_SIZE];
> +
> +	/* Pointer to function used for accessing sensors values. */
> +	cros_ec_sensors_read_t *read_ec_sensors_data;
> +
> +	/* Current sampling period */
> +	int curr_sampl_freq;
> +};
> +
> +/* Basic initialization of the core structure. */
> +int cros_ec_sensors_core_init(struct platform_device *pdev,
> +			      struct iio_dev *indio_dev,
> +			      bool physical_device);
> +
> +/*
> + * cros_ec_sensors_capture - the trigger handler function
> + *
> + * @irq: the interrupt number
> + * @p: private data - always a pointer to the poll func.
> + *
> + * On a trigger event occurring, if the pollfunc is attached then this
> + * handler is called as a threaded interrupt (and hence may sleep). It
> + * is responsible for grabbing data from the device and pushing it into
> + * the associated buffer.
> + */
> +irqreturn_t cros_ec_sensors_capture(int irq, void *p);
> +
> +
> +/*
> + * cros_ec_motion_send_host_cmd - send motion sense host command
> + *
> + * @st Pointer to state information for device.
> + * @opt_length: optional length: to reduce the response size,
> + *    useful on the data path.
> + *    Otherwise, the maximal allowed response size is used.
> + * @return 0 if ok, -ve on error.
> + *
> + * Note, when called, the sub-command is assumed to be set in param->cmd.
> + */
> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
> +				 u16 opt_length);
> +
> +/*
> + * cros_ec_sensors_core_read/write: handler for core attributes.
> + *
> + * Handler for attributes identical among sensors:
> + * - frequency,
> + * - sampling_frequency.
> + *
> + * cmd_lock lock must be held.
> + */
> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
> +			  struct iio_chan_spec const *chan,
> +			  int *val, int *val2, long mask);
> +
> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
> +			       struct iio_chan_spec const *chan,
> +			       int val, int val2, long mask);
> +
> +extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
> +
> +/* List of extended channel specification for all sensors */
> +extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
> +extern const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[];
> +
> +#endif  /* __CROS_EC_SENSORS_CORE_H */
> 

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)

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

* Re: [PATCH 01/10] mfd: cros_ec: add ChromeOS EC sensor platform information.
  2016-07-18  7:02 ` [PATCH 01/10] mfd: cros_ec: add ChromeOS EC sensor platform information Enric Balletbo i Serra
@ 2016-07-18 13:43   ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 13:43 UTC (permalink / raw)
  To: Enric Balletbo i Serra, linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 18/07/16 08:02, Enric Balletbo i Serra wrote:
> The platform information will be used for the new cros-ec sensors driver
> that presents sensors attached to the ChromeOS Embedded Controller.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Bring this in as an when you need it in the rest of the series.
It certainly doesn't need it's own patch.
> ---
>  include/linux/mfd/cros_ec.h | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
> index d6539c1..cd4833a 100644
> --- a/include/linux/mfd/cros_ec.h
> +++ b/include/linux/mfd/cros_ec.h
> @@ -151,6 +151,16 @@ struct cros_ec_device {
>  	int event_size;
>  };
>  
Check your kernel doc formatting.
> +/* struct cros_ec_sensor_platform - ChromeOS EC sensor platform information
> + *
> + * On top of cros_ec_devicem information cros_ec_sensors needs.
> + *
> + * @sensor_num: Id of the sensor, as reported by the EC.
> + */
> +struct cros_ec_sensor_platform {
> +	u8 sensor_num;
> +};
> +
>  /* struct cros_ec_platform - ChromeOS EC platform information
>   *
>   * @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...)
> 

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

* Re: [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands.
  2016-07-18  7:02 ` [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands Enric Balletbo i Serra
@ 2016-07-18 13:49   ` Jonathan Cameron
  2016-07-19 18:00     ` Enric Balletbo Serra
  0 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 13:49 UTC (permalink / raw)
  To: Enric Balletbo i Serra, linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 18/07/16 08:02, Enric Balletbo i Serra wrote:
> Let's update the command header to include the definitions related to
> the sensors attached behind the ChromeOS Embedded Controller. The new
> commands and definitions allow us to get information from these sensors.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Again, I'd be happier seeing this stuff introduced as and when it
is needed rather than in a magic patch. It's hard to review stuff
if it's broken up across multiple patches like this.

A few other bits and pieces inline.

Jonathan
> ---
>  include/linux/mfd/cros_ec_commands.h | 260 +++++++++++++++++++++++++++++++----
>  1 file changed, 231 insertions(+), 29 deletions(-)
> 
> diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
> index 76728ff..f26a806 100644
> --- a/include/linux/mfd/cros_ec_commands.h
> +++ b/include/linux/mfd/cros_ec_commands.h
> @@ -1290,7 +1290,13 @@ enum motionsense_command {
>  
>  	/*
>  	 * EC Rate command is a setter/getter command for the EC sampling rate
> -	 * of all motion sensors in milliseconds.
> +	 * in milliseconds.
> +	 * It is per sensor, the EC run sample task  at the minimum of all
> +	 * sensors EC_RATE.
> +	 * For sensors without hardware FIFO, EC_RATE should be equals to 1/ODR
> +	 * to collect all the sensor samples.
> +	 * For sensor with hardware FIFO, EC_RATE is used as the maximal delay
> +	 * to process of all motion sensors in milliseconds.
>  	 */
>  	MOTIONSENSE_CMD_EC_RATE = 2,
>  
> @@ -1315,37 +1321,138 @@ enum motionsense_command {
>  	 */
>  	MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5,
>  
> -	/* Number of motionsense sub-commands. */
> -	MOTIONSENSE_NUM_CMDS
> -};
> +	/*
> +	 * Returns a single sensor data.
> +	 */
Please use standard kernel documentation formats throughout.
If not you may face a Linus rant ;)
> +	MOTIONSENSE_CMD_DATA = 6,
> +
> +	/*
> +	 * Return sensor fifo info.
> +	 */
> +	MOTIONSENSE_CMD_FIFO_INFO = 7,
> +
> +	/*
> +	 * Insert a flush element in the fifo and return sensor fifo info.
> +	 * The host can use that element to synchronize its operation.
> +	 */
> +	MOTIONSENSE_CMD_FIFO_FLUSH = 8,
>  
> -enum motionsensor_id {
> -	EC_MOTION_SENSOR_ACCEL_BASE = 0,
> -	EC_MOTION_SENSOR_ACCEL_LID = 1,
> -	EC_MOTION_SENSOR_GYRO = 2,
> +	/*
> +	 * Return a portion of the fifo.
> +	 */
> +	MOTIONSENSE_CMD_FIFO_READ = 9,
> +
> +	/*
> +	 * Perform low level calibration.
> +	 * On sensors that support it, ask to do offset calibration.
> +	 */
> +	MOTIONSENSE_CMD_PERFORM_CALIB = 10,
> +
> +	/*
> +	 * Sensor Offset command is a setter/getter command for the offset
> +	 * used for calibration.
> +	 * The offsets can be calculated by the host, or via
> +	 * PERFORM_CALIB command.
> +	 */
> +	MOTIONSENSE_CMD_SENSOR_OFFSET = 11,
> +
> +	/*
> +	 * List available activities for a MOTION sensor.
> +	 * Indicates if they are enabled or disabled.
> +	 */
> +	MOTIONSENSE_CMD_LIST_ACTIVITIES = 12,
>  
>  	/*
> -	 * Note, if more sensors are added and this count changes, the padding
> -	 * in ec_response_motion_sense dump command must be modified.
> +	 * Activity management
> +	 * Enable/Disable activity recognition.
>  	 */
> -	EC_MOTION_SENSOR_COUNT = 3
> +	MOTIONSENSE_CMD_SET_ACTIVITY = 13,
> +
> +	/* Number of motionsense sub-commands. */
> +	MOTIONSENSE_NUM_CMDS
>  };
>  
>  /* List of motion sensor types. */
>  enum motionsensor_type {
>  	MOTIONSENSE_TYPE_ACCEL = 0,
>  	MOTIONSENSE_TYPE_GYRO = 1,
> +	MOTIONSENSE_TYPE_MAG = 2,
> +	MOTIONSENSE_TYPE_PROX = 3,
> +	MOTIONSENSE_TYPE_LIGHT = 4,
> +	MOTIONSENSE_TYPE_ACTIVITY = 5,
> +	MOTIONSENSE_TYPE_MAX,
>  };
>  
>  /* List of motion sensor locations. */
>  enum motionsensor_location {
>  	MOTIONSENSE_LOC_BASE = 0,
>  	MOTIONSENSE_LOC_LID = 1,
> +	MOTIONSENSE_LOC_MAX,
>  };
>  
>  /* List of motion sensor chips. */
>  enum motionsensor_chip {
>  	MOTIONSENSE_CHIP_KXCJ9 = 0,
> +	MOTIONSENSE_CHIP_LSM6DS0 = 1,
> +	MOTIONSENSE_CHIP_BMI160 = 2,
> +	MOTIONSENSE_CHIP_SI1141 = 3,
> +	MOTIONSENSE_CHIP_SI1142 = 4,
> +	MOTIONSENSE_CHIP_SI1143 = 5,
> +	MOTIONSENSE_CHIP_KX022 = 6,
> +	MOTIONSENSE_CHIP_L3GD20H = 7,
Interesting.  So the driver needs some knowledge of what
is behind it.  I'll read on with interest ;)
> +};
> +
> +struct ec_response_motion_sensor_data {
> +	/* Flags for each sensor. */
> +	uint8_t flags;
> +	/* sensor number the data comes from */
> +	uint8_t sensor_num;
> +	/* Each sensor is up to 3-axis. */
> +	union {
> +		int16_t             data[3];
> +		struct {
> +			uint16_t    rsvd;
> +			uint32_t    timestamp;
> +		} __packed;
> +		struct {
> +			uint8_t     activity; /* motionsensor_activity */
> +			uint8_t     state;
> +			int16_t     add_info[2];
> +		};
> +	};
> +} __packed;
> +
> +struct ec_response_motion_sense_fifo_info {
> +	/* Size of the fifo */
> +	uint16_t size;
> +	/* Amount of space used in the fifo */
> +	uint16_t count;
> +	/* TImestamp recorded in us */
Timestamp
> +	uint32_t timestamp;
> +	/* Total amount of vector lost */
> +	uint16_t total_lost;
> +	/* Lost events since the last fifo_info, per sensors */
> +	uint16_t lost[0];
> +} __packed;
> +
> +struct ec_response_motion_sense_fifo_data {
> +	uint32_t number_data;
> +	struct ec_response_motion_sensor_data data[0];
> +} __packed;
> +
> +/* List supported activity recognition */
> +enum motionsensor_activity {
> +	MOTIONSENSE_ACTIVITY_RESERVED = 0,
> +	MOTIONSENSE_ACTIVITY_SIG_MOTION = 1,
> +	MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2,
> +};
> +
> +struct ec_motion_sense_activity {
> +	uint8_t sensor_num;
> +	uint8_t activity; /* one of enum motionsensor_activity */
> +	uint8_t enable;   /* 1: enable, 0: disable */
> +	uint8_t reserved;
> +	uint16_t parameters[3]; /* activity dependent parameters */
>  };
>  
>  /* Module flag masks used for the dump sub-command. */
> @@ -1355,41 +1462,61 @@ enum motionsensor_chip {
>  #define MOTIONSENSE_SENSOR_FLAG_PRESENT (1<<0)
>  
>  /*
> + * Flush entry for synchronisation.
> + * data contains time stamp
> + */
> +#define MOTIONSENSE_SENSOR_FLAG_FLUSH (1<<0)
> +#define MOTIONSENSE_SENSOR_FLAG_TIMESTAMP (1<<1)
> +#define MOTIONSENSE_SENSOR_FLAG_WAKEUP (1<<2)
> +
> +/*
>   * Send this value for the data element to only perform a read. If you
>   * send any other value, the EC will interpret it as data to set and will
>   * return the actual value set.
>   */
>  #define EC_MOTION_SENSE_NO_VALUE -1
>  
> +#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000
> +
> +/* MOTIONSENSE_CMD_SENSOR_OFFSET subcommand flag */
> +/* Set Calibration information */
> +#define MOTION_SENSE_SET_OFFSET 1
> +
>  struct ec_params_motion_sense {
>  	uint8_t cmd;
>  	union {
> -		/* Used for MOTIONSENSE_CMD_DUMP. */
> +		/* Used for MOTIONSENSE_CMD_DUMP */
>  		struct {
> -			/* no args */
> +			/*
> +			 * Maximal number of sensor the host is expecting.
> +			 * 0 means the host is only interested in the number
> +			 * of sensors controlled by the EC.
> +			 */
> +			uint8_t max_sensor_count;
>  		} dump;
>  
>  		/*
> -		 * Used for MOTIONSENSE_CMD_EC_RATE and
> -		 * MOTIONSENSE_CMD_KB_WAKE_ANGLE.
> +		 * Used for MOTIONSENSE_CMD_KB_WAKE_ANGLE.
>  		 */
>  		struct {
> -			/* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
> +			/* Data to set or EC_MOTION_SENSE_NO_VALUE to read.
> +			 * kb_wake_angle: angle to wakup AP.
> +			 */
>  			int16_t data;
> -		} ec_rate, kb_wake_angle;
> +		} kb_wake_angle;
>  
> -		/* Used for MOTIONSENSE_CMD_INFO. */
> +		/* Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA
> +		 * and MOTIONSENSE_CMD_PERFORM_CALIB.
> +		 */
>  		struct {
> -			/* Should be element of enum motionsensor_id. */
>  			uint8_t sensor_num;
> -		} info;
> +		} info, data, fifo_flush, perform_calib, list_activities;
>  
>  		/*
> -		 * Used for MOTIONSENSE_CMD_SENSOR_ODR and
> -		 * MOTIONSENSE_CMD_SENSOR_RANGE.
> +		 * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR
> +		 * and MOTIONSENSE_CMD_SENSOR_RANGE.
>  		 */
>  		struct {
> -			/* Should be element of enum motionsensor_id. */
>  			uint8_t sensor_num;
>  
>  			/* Rounding flag, true for round-up, false for down. */
> @@ -1399,22 +1526,69 @@ struct ec_params_motion_sense {
>  
>  			/* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
>  			int32_t data;
> -		} sensor_odr, sensor_range;
> +		} ec_rate, sensor_odr, sensor_range;
> +
> +		/* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
> +		struct {
> +			uint8_t sensor_num;
> +
> +			/*
> +			 * bit 0: If set (MOTION_SENSE_SET_OFFSET), set
> +			 * the calibration information in the EC.
> +			 * If unset, just retrieve calibration information.
> +			 */
> +			uint16_t flags;
> +
> +			/*
> +			 * Temperature at calibration, in units of 0.01 C
> +			 * 0x8000: invalid / unknown.
> +			 * 0x0: 0C
> +			 * 0x7fff: +327.67C
> +			 */
> +			int16_t temp;
> +
> +			/*
> +			 * Offset for calibration.
> +			 * Unit:
> +			 * Accelerometer: 1/1024 g
> +			 * Gyro:          1/1024 deg/s
> +			 * Compass:       1/16 uT
> +			 */
> +			int16_t offset[3];
> +		} __packed sensor_offset;
> +
> +		/* Used for MOTIONSENSE_CMD_FIFO_INFO */
> +		struct {
> +		} fifo_info;
> +
> +		/* Used for MOTIONSENSE_CMD_FIFO_READ */
> +		struct {
> +			/*
> +			 * Number of expected vector to return.
> +			 * EC may return less or 0 if none available.
> +			 */
> +			uint32_t max_data_vector;
> +		} fifo_read;
> +
> +		struct ec_motion_sense_activity set_activity;
>  	};
>  } __packed;
>  
>  struct ec_response_motion_sense {
>  	union {
> -		/* Used for MOTIONSENSE_CMD_DUMP. */
> +		/* Used for MOTIONSENSE_CMD_DUMP */
>  		struct {
>  			/* Flags representing the motion sensor module. */
>  			uint8_t module_flags;
>  
> -			/* Flags for each sensor in enum motionsensor_id. */
> -			uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT];
> +			/* Number of sensors managed directly by the EC */
> +			uint8_t sensor_count;
>  
> -			/* Array of all sensor data. Each sensor is 3-axis. */
> -			int16_t data[3*EC_MOTION_SENSOR_COUNT];
> +			/*
> +			 * sensor data is truncated if response_max is too small
> +			 * for holding all the data.
> +			 */
> +			struct ec_response_motion_sensor_data sensor[0];
>  		} dump;
>  
>  		/* Used for MOTIONSENSE_CMD_INFO. */
> @@ -1429,6 +1603,9 @@ struct ec_response_motion_sense {
>  			uint8_t chip;
>  		} info;
>  
> +		/* Used for MOTIONSENSE_CMD_DATA */
> +		struct ec_response_motion_sensor_data data;
> +
>  		/*
>  		 * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR,
>  		 * MOTIONSENSE_CMD_SENSOR_RANGE, and
> @@ -1438,6 +1615,25 @@ struct ec_response_motion_sense {
>  			/* Current value of the parameter queried. */
>  			int32_t ret;
>  		} ec_rate, sensor_odr, sensor_range, kb_wake_angle;
> +
> +		/* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
> +		struct {
> +			int16_t temp;
> +			int16_t offset[3];
> +		} sensor_offset, perform_calib;
> +
> +		struct ec_response_motion_sense_fifo_info fifo_info, fifo_flush;
> +
> +		struct ec_response_motion_sense_fifo_data fifo_read;
> +
> +		struct {
> +			uint16_t reserved;
> +			uint32_t enabled;
> +			uint32_t disabled;
> +		} __packed list_activities;
> +
> +		struct {
> +		} set_activity;
>  	};
>  } __packed;
>  
> @@ -1819,6 +2015,12 @@ union ec_response_get_next_data {
>  
>  	/* Unaligned */
>  	uint32_t  host_event;
> +
> +	struct {
> +		/* For aligning the fifo_info */
> +		uint8_t rsvd[3];
> +		struct ec_response_motion_sense_fifo_info info;
> +	}        sensor_fifo;
>  } __packed;
>  
>  struct ec_response_get_next_event {
> 

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

* Re: [PATCH 03/10] iio: core: Add double tap as possible gesture
  2016-07-18  7:02 ` [PATCH 03/10] iio: core: Add double tap as possible gesture Enric Balletbo i Serra
@ 2016-07-18 13:54   ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 13:54 UTC (permalink / raw)
  To: Enric Balletbo i Serra, linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 18/07/16 08:02, Enric Balletbo i Serra wrote:
> From: Gwendal Grignou <gwendal@chromium.org>
> 
> This is an interface change: however, the sysfs entry is based on string,
> so if other gestures are added on the trunk in the meantime, we will
> still be able to merge this change.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> Signed-off-by: Guenter Roeck <groeck@chromium.org>
> [enric: Rebased and resolved conflicts]
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
So you are creating an entire channel type for double tap?

Not keen on this I'm afraid.

The activity types in there are the moment are not events, but rather
attempts to estimate the 'likelihood' that a given activity is currently
being undertaken.

Hmm. Double tap is more of an event type.  It's one I've been wondering
how to describe for a while... At the end of the day it's just a
reasonably sophisticated filter - kind of a 'magic' version of Rate
of Change.  Unfortunately there isn't a clean mathematical definition
as it can be implemented in lots of ways.  

I guess the best may be to just have it as an event type on it's own...

What do others think?

Jonathan
> ---
>  drivers/iio/industrialio-core.c | 1 +
>  include/uapi/linux/iio/types.h  | 1 +
>  2 files changed, 2 insertions(+)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index e6319a9..f700e67 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -119,6 +119,7 @@ static const char * const iio_modifier_names[] = {
>  	[IIO_MOD_Q] = "q",
>  	[IIO_MOD_CO2] = "co2",
>  	[IIO_MOD_VOC] = "voc",
> +	[IIO_MOD_DOUBLE_TAP] = "double_tap",
>  };
>  
>  /* relies on pairs of these shared then separate */
> diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> index b0916fc..c290167 100644
> --- a/include/uapi/linux/iio/types.h
> +++ b/include/uapi/linux/iio/types.h
> @@ -79,6 +79,7 @@ enum iio_modifier {
>  	IIO_MOD_CO2,
>  	IIO_MOD_VOC,
>  	IIO_MOD_LIGHT_UV,
> +	IIO_MOD_DOUBLE_TAP,
>  };
>  
>  enum iio_event_type {
> 

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

* Re: [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors.
  2016-07-18  7:29   ` Peter Meerwald-Stadler
@ 2016-07-18 15:51     ` Jonathan Cameron
  2016-07-20  7:57       ` Enric Balletbo Serra
  0 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 15:51 UTC (permalink / raw)
  To: Peter Meerwald-Stadler, Enric Balletbo i Serra
  Cc: linux-kernel, linux-iio, Olof Johansson, Lee Jones,
	Hartmut Knaack, Lars-Peter Clausen, Guenter Roeck,
	Gwendal Grignou

On 18/07/16 08:29, Peter Meerwald-Stadler wrote:
> 
>> Add the core functions to be able to support the sensors attached behind
>> the ChromeOS Embedded Controller and used by other IIO cros-ec sensor
>> drivers.
> 
> comments below from a quick read
> there is plenty on undocumented private API
Few more comments from me. Peter is of course quite correct, its the
new, undocumented ABI, which is the biggest issue.

Interesting looking device.  Any docs out there?

Jonathan
>  
>> The cros_ec_sensor_core driver matches with current driver in ChromeOS
>> 4.4 tree, so it includes all the fixes at the moment. The support for
>> this driver was made by Gwendal Grignou. The original patch and all the
>> fixes has been squashed and rebased on top of mainline.
>>
>> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
>> Signed-off-by: Guenter Roeck <groeck@chromium.org>
>> [eballetbo: split, squash and rebase on top of mainline the patches
>> found in ChromeOS tree]
>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>> ---
>>  drivers/iio/common/Kconfig                         |   1 +
>>  drivers/iio/common/Makefile                        |   1 +
>>  drivers/iio/common/cros_ec_sensors/Kconfig         |  14 +
>>  drivers/iio/common/cros_ec_sensors/Makefile        |   5 +
>>  .../common/cros_ec_sensors/cros_ec_sensors_core.c  | 564 +++++++++++++++++++++
>>  .../common/cros_ec_sensors/cros_ec_sensors_core.h  | 155 ++++++
>>  6 files changed, 740 insertions(+)
>>  create mode 100644 drivers/iio/common/cros_ec_sensors/Kconfig
>>  create mode 100644 drivers/iio/common/cros_ec_sensors/Makefile
>>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
>>
>> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
>> index 26a6026..e108996 100644
>> --- a/drivers/iio/common/Kconfig
>> +++ b/drivers/iio/common/Kconfig
>> @@ -2,6 +2,7 @@
>>  # IIO common modules
>>  #
>>  
>> +source "drivers/iio/common/cros_ec_sensors/Kconfig"
>>  source "drivers/iio/common/hid-sensors/Kconfig"
>>  source "drivers/iio/common/ms_sensors/Kconfig"
>>  source "drivers/iio/common/ssp_sensors/Kconfig"
>> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
>> index 585da6a..6fa760e 100644
>> --- a/drivers/iio/common/Makefile
>> +++ b/drivers/iio/common/Makefile
>> @@ -7,6 +7,7 @@
>>  #
>>  
>>  # When adding new entries keep the list in alphabetical order
>> +obj-y += cros_ec_sensors/
>>  obj-y += hid-sensors/
>>  obj-y += ms_sensors/
>>  obj-y += ssp_sensors/
>> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
>> new file mode 100644
>> index 0000000..a30f41e
>> --- /dev/null
>> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
>> @@ -0,0 +1,14 @@
>> +#
>> +# Chrome OS Embedded Controller managed sensors library
>> +#
>> +config IIO_CROS_EC_SENSORS_CORE
>> +	tristate "ChromeOS EC Sensors Core"
>> +	depends on SYSFS && MFD_CROS_EC
>> +	select IIO_BUFFER
>> +	select IIO_TRIGGERED_BUFFER
>> +	help
>> +	  Base module for the ChromeOS EC Sensors module.
>> +	  Contains core functions used by other IIO CrosEC sensor
>> +	  driver.
> 
> 'drivers' probably
> 
>> +	  Define common attributes and sysfs interrupt handler.
>> +
>> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
>> new file mode 100644
>> index 0000000..95b6901
>> --- /dev/null
>> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
>> @@ -0,0 +1,5 @@
>> +#
>> +# Makefile for sensors seen through the ChromeOS EC sensor hub.
>> +#
>> +
>> +obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>> new file mode 100644
>> index 0000000..cb3de8f
>> --- /dev/null
>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>> @@ -0,0 +1,564 @@
>> +/*
>> + * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
>> + *
>> + * Copyright (C) 2015 Google, Inc
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * This driver uses the cros-ec interface to communicate with the Chrome OS
>> + * EC about accelerometer data. Accelerometer access is presented through
>> + * iio sysfs.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/kfifo_buf.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mfd/cros_ec.h>
>> +#include <linux/mfd/cros_ec_commands.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include "cros_ec_sensors_core.h"
>> +
>> +static char *cros_ec_loc[] = {
>> +	[MOTIONSENSE_LOC_BASE] = "base",
>> +	[MOTIONSENSE_LOC_LID] = "lid",
>> +	[MOTIONSENSE_LOC_MAX] = "unknown",
>> +};
>> +
>> +/*
>> + * cros_ec_sensors_core_init
>> + *
>> + * Initialize core sensor strucure, fill the response are
>> + * with the return of Sensor info.
> 
> rephrase please
> 
>> + *
>> + * @pdev plarform device created for the sensors
> 
> platform
> 
>> + * @indio_dev iio device structure of the device
>> + * @physical_device True if the device refers to a physical device.
>> + */
>> +int cros_ec_sensors_core_init(struct platform_device *pdev,
>> +			      struct iio_dev *indio_dev,
>> +			      bool physical_device)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
>> +	struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
>> +	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
>> +
>> +	platform_set_drvdata(pdev, indio_dev);
>> +
>> +	state->ec = ec->ec_dev;
>> +	state->msg = devm_kzalloc(&pdev->dev,
>> +				max((u16)sizeof(struct ec_params_motion_sense),
>> +				state->ec->max_response), GFP_KERNEL);
>> +	if (!state->msg)
>> +		return -ENOMEM;
>> +
>> +	state->resp = (struct ec_response_motion_sense *)state->msg->data;
>> +
>> +	mutex_init(&state->cmd_lock);
>> +	/* Set up the host command structure. */
>> +	state->msg->version = 2;
>> +	state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
>> +	state->msg->outsize = sizeof(struct ec_params_motion_sense);
>> +
>> +	indio_dev->dev.parent = &pdev->dev;
>> +	indio_dev->name = pdev->name;
>> +
>> +	if (physical_device) {
>> +		indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +		state->param.cmd = MOTIONSENSE_CMD_INFO;
>> +		state->param.info.sensor_num = sensor_platform->sensor_num;
>> +		if (cros_ec_motion_send_host_cmd(state, 0)) {
>> +			dev_warn(dev, "Can not access sensor info\n");
>> +			return -EIO;
>> +		}
>> +		state->type = state->resp->info.type;
>> +		state->loc = state->resp->info.location;
>> +	}
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
>> +
>> +/*
>> + * cros_ec_motion_send_host_cmd - send motion sense host command
>> + *
>> + * @st Pointer to state information for device.
> 
> @state
> opt_length is not documented
> 
>> + * @return 0 if ok, -ve on error.
> 
> what is -ve?
> 
>> + *
>> + * Note, when called, the sub-command is assumed to be set in param->cmd.
>> + */
>> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
>> +				 u16 opt_length)
>> +{
>> +	int ret;
>> +
>> +	if (opt_length)
>> +		state->msg->insize = min(opt_length, state->ec->max_response);
>> +	else
>> +		state->msg->insize = state->ec->max_response;
>> +
>> +	memcpy(state->msg->data, &state->param, sizeof(state->param));
>> +	/* Send host command. */
> 
> comment needed?
> 
>> +	ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
>> +	if (ret < 0)
>> +		return -EIO;
>> +
>> +	if (ret &&
>> +	    state->resp != (struct ec_response_motion_sense *)state->msg->data)
>> +		memcpy(state->resp, state->msg->data, ret);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
>> +
>> +static ssize_t __maybe_unused cros_ec_sensors_flush(struct iio_dev *indio_dev,
>> +		uintptr_t private, const struct iio_chan_spec *chan,
>> +		const char *buf, size_t len)
>> +{
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +	int ret = 0;
> 
> initialization not needed
> 
>> +	bool flush;
>> +
>> +	ret = strtobool(buf, &flush);
>> +	if (ret < 0)
>> +		return ret;
>> +	if (!flush)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&st->cmd_lock);
>> +	st->param.cmd = MOTIONSENSE_CMD_FIFO_FLUSH;
>> +	ret = cros_ec_motion_send_host_cmd(st, 0);
>> +	if (ret != 0)
>> +		dev_warn(&indio_dev->dev, "Unable to flush sensor\n");
>> +	mutex_unlock(&st->cmd_lock);
blank line here (nitpick of the day)
>> +	return ret ? ret : len;
>> +}
>> +
>> +static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
>> +		uintptr_t private, const struct iio_chan_spec *chan,
>> +		const char *buf, size_t len)
>> +{
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +	int ret, i;
>> +	bool calibrate;
>> +
>> +	ret = strtobool(buf, &calibrate);
>> +	if (ret < 0)
>> +		return ret;
>> +	if (!calibrate)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&st->cmd_lock);
>> +	st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
>> +	ret = cros_ec_motion_send_host_cmd(st, 0);
>> +	if (ret != 0) {
>> +		dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
>> +	} else {
>> +		/* Save values */
>> +		for (i = X; i < MAX_AXIS; i++)
> 
> what is X here?
> 
>> +			st->calib[i].offset = st->resp->perform_calib.offset[i];
>> +	}
>> +	mutex_unlock(&st->cmd_lock);
>> +	return ret ? ret : len;
>> +}
>> +
>> +static ssize_t cros_ec_sensors_id(struct iio_dev *indio_dev,
>> +		uintptr_t private, const struct iio_chan_spec *chan,
>> +		char *buf)
>> +{
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +
>> +	return sprintf(buf, "%d\n", st->param.info.sensor_num);
> 
> can't we be a bit more careful with buffer sizes here (and below)?
> 
>> +}
>> +
>> +static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
>> +		uintptr_t private, const struct iio_chan_spec *chan,
>> +		char *buf)
>> +{
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +
>> +	return sprintf(buf, "%s\n", cros_ec_loc[st->loc]);
>> +}
>> +
>> +const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
>> +#ifdef CONFIG_IIO_CROS_EC_SENSORS_RING
>> +	{
>> +		.name = "flush",
>> +		.shared = IIO_SHARED_BY_ALL,
>> +		.write = cros_ec_sensors_flush
>> +	},
>> +#endif
>> +	{
>> +		.name = "calibrate",
>> +		.shared = IIO_SHARED_BY_ALL,
>> +		.write = cros_ec_sensors_calibrate
>> +	},
>> +	{
>> +		.name = "id",
>> +		.shared = IIO_SHARED_BY_ALL,
>> +		.read = cros_ec_sensors_id
>> +	},
>> +	{
>> +		.name = "location",
>> +		.shared = IIO_SHARED_BY_ALL,
>> +		.read = cros_ec_sensors_loc
>> +	},
>> +	{ },
>> +};
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
>> +
>> +const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[] = {
>> +	{
>> +		.name = "id",
These all need documentation..  Any ABI needs an entry in
Documentation/ABI/testing/sysfs-bus-iio-* before any real discussion can
start.

>> +		.shared = IIO_SHARED_BY_ALL,
>> +		.read = cros_ec_sensors_id
>> +	},
>> +	{
>> +		.name = "location",
>> +		.shared = IIO_SHARED_BY_ALL,
>> +		.read = cros_ec_sensors_loc
>> +	},
>> +	{ },
>> +};
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_limited_info);
>> +/*
>> + * idx_to_reg - convert sensor index into offset in shared memory region.
>> + *
>> + * @st: private data
>> + * @idx: sensor index (should be element of enum sensor_index)
>> + * @return address to read at.
>> + */
>> +static unsigned int idx_to_reg(struct cros_ec_sensors_core_state *st,
> 
> cros_ec_ prefix please (here and below)
> 
>> +			       unsigned int idx)
>> +{
>> +	/*
>> +	 * When using LPC interface, only space for 2 Accel and one Gyro.
>> +	 * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
>> +	 */
>> +	if (st->type == MOTIONSENSE_TYPE_ACCEL)
>> +		return EC_MEMMAP_ACC_DATA + sizeof(u16) *
>> +			(1 + idx + st->param.info.sensor_num *
>> +			 MAX_AXIS);
>> +	else
>> +		return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
>> +}
>> +
>> +static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
>> +			  u8 *dest)
>> +{
>> +	return ec->cmd_readmem(ec, offset, 1, dest);
>> +}
>> +
>> +static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
>> +			   u16 *dest)
>> +{
>> +	u16 tmp;
__le16 tmp;
>> +	int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
>> +
>> +	*dest = le16_to_cpu(tmp);
>> +
>> +	return ret;
>> +}
>> +
>> +/*
>> + * read_ec_until_not_busy - read from EC status byte until it reads not busy.
>> + *
>> + * @st Pointer to state information for device.
>> + * @return 8-bit status if ok, -ve on error
>> + */
>> +static int read_ec_until_not_busy(struct cros_ec_sensors_core_state *st)
>> +{
>> +	struct cros_ec_device *ec = st->ec;
>> +	u8 status;
>> +	int attempts = 0;
>> +
>> +	ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
> 
> check return value (here and below)?
> 
>> +	while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
>> +		/* Give up after enough attempts, return error. */
>> +		if (attempts++ >= 50)
>> +			return -EIO;
>> +
>> +		/* Small delay every so often. */
>> +		if (attempts % 5 == 0)
>> +			msleep(25);
>> +
>> +		ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
>> +	}
>> +
>> +	return status;
>> +}
>> +
>> +/*
>> + * read_ec_sensors_data_unsafe - read acceleration data from EC shared memory.
>> + *
>> + * @st Pointer to state information for device.
> 
> @indio_dev? here and below
> 
>> + * @scan_mask Bitmap of the sensor indices to scan.
>> + * @data Location to store data.
>> + *
>> + * Note this is the unsafe function for reading the EC data. It does not
>> + * guarantee that the EC will not modify the data as it is being read in.
>> + */
>> +static void read_ec_sensors_data_unsafe(struct iio_dev *indio_dev,
>> +			 unsigned long scan_mask, s16 *data)
>> +{
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +	struct cros_ec_device *ec = st->ec;
>> +	unsigned int i = 0;
> 
> init not needed
> 
>> +
>> +	/*
>> +	 * Read all sensors enabled in scan_mask. Each value is 2
>> +	 * bytes.
>> +	 */
>> +	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
>> +		ec_cmd_read_u16(ec, idx_to_reg(st, i), data);
> 
> no return value?
> 
>> +		data++;
>> +	}
Hmm. So this one has random access unlike below. Interesting...
>> +}
>> +
>> +/*
>> + * cros_ec_sensors_read_lpc - read acceleration data from EC shared memory.
>> + *
>> + * @st Pointer to state information for device.
>> + * @scan_mask Bitmap of the sensor indices to scan.
>> + * @data Location to store data.
>> + * @return 0 if ok, -ve on error
>> + *
>> + * Note: this is the safe function for reading the EC data. It guarantees
>> + * that the data sampled was not modified by the EC while being read.
>> + */
>> +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
>> +			     unsigned long scan_mask, s16 *data)
>> +{
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +	struct cros_ec_device *ec = st->ec;
>> +	u8 samp_id = 0xff, status = 0;
>> +	int attempts = 0;
>> +
>> +	/*
>> +	 * Continually read all data from EC until the status byte after
>> +	 * all reads reflects that the EC is not busy and the sample id
>> +	 * matches the sample id from before all reads. This guarantees
>> +	 * that data read in was not modified by the EC while reading.
>> +	 */
kind of papers over any overwrites that have occured though...
Unfortunately there is no particularly good solution to this. Oh for a hardware
fifo!

>> +	while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
>> +			  EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
>> +		/* If we have tried to read too many times, return error. */
>> +		if (attempts++ >= 5)
>> +			return -EIO;
>> +
>> +		/* Read status byte until EC is not busy. */
>> +		status = read_ec_until_not_busy(st);
>> +		if (status < 0)
>> +			return status;
Any chance this can spin for ever? (I don't think so but worth checking).
>> +
>> +		/*
>> +		 * Store the current sample id so that we can compare to the
>> +		 * sample id after reading the data.
>> +		 */
>> +		samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
>> +
>> +		/* Read all EC data, format it, and store it into data. */
>> +		read_ec_sensors_data_unsafe(indio_dev, scan_mask, data);
>> +
>> +		/* Read status byte. */
>> +		ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
>> +
>> +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
>> +			     unsigned long scan_mask, s16 *data)
>> +{
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +	int ret;
>> +	unsigned int i = 0;
>> +
>> +	/*
>> +	 * read all sensor data through a command.
> 
> start with uppercase as everywhere else
> 
>> +	 */
>> +	st->param.cmd = MOTIONSENSE_CMD_DATA;
>> +	ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
>> +	if (ret != 0) {
>> +		dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
>> +		return ret;
>> +	}
>> +
>> +	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
>> +		*data = st->resp->data.data[i];
>> +		data++;
>> +	}
So the device spits out a whole record every time?  If so let the demux
in the IIO core handle this step.   Chances are you'll end up with
clients of this datastream down the line and demuxing everything twice
+ that implementation has a few little tricks up it's sleeve ;)

Provide available_scan_masks and then just throw the whole scan at it every
time.  

>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
>> +
>> +irqreturn_t cros_ec_sensors_capture(int irq, void *p)
>> +{
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *indio_dev = pf->indio_dev;
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +
>> +	mutex_lock(&st->cmd_lock);
>> +	/* Clear capture data. */
>> +	memset(st->samples, 0, indio_dev->scan_bytes);
>> +
>> +	/* Read data based on which channels are enabled in scan mask. */
>> +	st->read_ec_sensors_data(indio_dev, *(indio_dev->active_scan_mask),
>> +			   (s16 *)st->samples);
> 
> no return value?
> 
>> +
>> +	/* Store the timestamp last 8 bytes of data. */
>> +	if (indio_dev->scan_timestamp)
>> +		*(s64 *)&st->samples[round_down(indio_dev->scan_bytes -
>> +						sizeof(s64),
>> +				     sizeof(s64))] = iio_get_time_ns();
>> +
>> +	iio_push_to_buffers(indio_dev, st->samples);
> 
> use iio_push_to_buffers_with_timestamp()
but make sure you obey the slightly odd space requirements for the buffer
passed into this function.
> 
>> +
>> +	/*
>> +	 * Tell the core we are done with this trigger and ready for the
>> +	 * next one.
>> +	 */
>> +	iio_trigger_notify_done(indio_dev->trig);
>> +	mutex_unlock(&st->cmd_lock);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
>> +
>> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>> +			  struct iio_chan_spec const *chan,
>> +			  int *val, int *val2, long mask)
>> +{
>> +	int ret = IIO_VAL_INT;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>> +		st->param.ec_rate.data =
>> +			EC_MOTION_SENSE_NO_VALUE;
>> +
>> +		if (cros_ec_motion_send_host_cmd(st, 0))
>> +			ret = -EIO;
>> +		else
>> +			*val = st->resp->ec_rate.ret;
>> +		break;
>> +	case IIO_CHAN_INFO_FREQUENCY:
>> +		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
>> +		st->param.sensor_odr.data =
>> +			EC_MOTION_SENSE_NO_VALUE;
>> +
>> +		if (cros_ec_motion_send_host_cmd(st, 0))
>> +			ret = -EIO;
>> +		else
>> +			*val = st->resp->sensor_odr.ret;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
>> +
>> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>> +			       struct iio_chan_spec const *chan,
>> +			       int val, int val2, long mask)
>> +{
>> +	int ret = 0;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_FREQUENCY:
This needs explanation... The frequency chan info element has so far only
applied to output sensors where we are controlling a signal generator of
some type.
>> +		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
>> +		st->param.sensor_odr.data = val;
>> +
>> +		/* Always roundup, so caller gets at least what it asks for. */
>> +		st->param.sensor_odr.roundup = 1;
>> +
>> +		if (cros_ec_motion_send_host_cmd(st, 0))
>> +			ret = -EIO;
>> +		break;
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>> +		st->param.ec_rate.data = val;
>> +
>> +		if (cros_ec_motion_send_host_cmd(st, 0))
>> +			ret = -EIO;
>> +		else
>> +			st->curr_sampl_freq = val;
>> +		break;
>> +	default:
>> +		ret = -EINVAL;
>> +		break;
>> +	}
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
>> +
>> +static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
>> +{
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +
>> +	if (st->curr_sampl_freq == 0)
>> +		return 0;
>> +
>> +	/*
>> +	 * If the sensors are sampled at high frequency, we will not be able to
>> +	 * sleep. Set to sampling to a long period if necessary.
>> +	 */
>> +	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
>> +		mutex_lock(&st->cmd_lock);
>> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>> +		st->param.ec_rate.data = CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY;
>> +		cros_ec_motion_send_host_cmd(st, 0);
>> +		mutex_unlock(&st->cmd_lock);
>> +	}
>> +	return 0;
>> +}
>> +
>> +static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
>> +{
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> +
>> +	if (st->curr_sampl_freq == 0)
>> +		return;
>> +
>> +	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
>> +		mutex_lock(&st->cmd_lock);
>> +		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>> +		st->param.ec_rate.data = st->curr_sampl_freq;
>> +		cros_ec_motion_send_host_cmd(st, 0);
>> +		mutex_unlock(&st->cmd_lock);
>> +	}
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +const struct dev_pm_ops cros_ec_sensors_pm_ops = {
>> +	.prepare = cros_ec_sensors_prepare,
>> +	.complete = cros_ec_sensors_complete
>> +};
Hmm. You meet something new everyday.  I don't feel nearly as comfortable
reviewing these as the callbacks we tend to get as first time I've encountered
them...

>> +#else
>> +const struct dev_pm_ops cros_ec_sensors_pm_ops = { };
>> +#endif
>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
>> +
>> +MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
>> new file mode 100644
>> index 0000000..53e09e1
>> --- /dev/null
>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
>> @@ -0,0 +1,155 @@
>> +/*
>> + * ChromeOS EC sensor hub
>> + *
>> + * Copyright (C) 2015 Google, Inc
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __CROS_EC_SENSORS_CORE_H
>> +#define __CROS_EC_SENSORS_CORE_H
>> +
>> +#include <linux/irqreturn.h>
>> +
>> +enum {
>> +	X,
> 
> such short #defines scare me :)
Absolutely - prefix these to avoid likely future crashes..
> 
>> +	Y,
>> +	Z,
>> +	MAX_AXIS,
>> +};
>> +
>> +/*
>> + * EC returns sensor values using signed 16 bit registers
>> + */
>> +#define CROS_EC_SENSOR_BITS 16
>> +
>> +/*
>> + * 4 16 bit channels are allowed.
>> + * Good enough for current sensors, thye use up to 3 16 bit vectors.
> 
> they
> 
>> + */
>> +#define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
>> +
>> +/*
>> + * minimum sampling period to use when device is suspending.
> 
> Minimum (uppercase)
Comment syntax as well :)
> 
>> + */
>> +#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
>> +
>> +/*
>> + * Function to read the sensor data.
>> + *
>> + * Data can be retrieve using the cros ec command protocol.
>> + * Some machines also allow accessing some sensor data via
>> + * IO space.
>> + */
>> +typedef int (cros_ec_sensors_read_t)(struct iio_dev *indio_dev,
>> +		unsigned long scan_mask, s16 *data);
>> +
>> +cros_ec_sensors_read_t cros_ec_sensors_read_lpc;
>> +cros_ec_sensors_read_t cros_ec_sensors_read_cmd;
>> +
>> +/* State data for ec_sensors iio driver. */
>> +struct cros_ec_sensors_core_state {
>> +	struct cros_ec_device *ec;
>> +	/*
>> +	 *  Location to store command and response to the EC.
>> +	 */
Please fix comment style throughout.  If not I'll get a dozen fixes within
the first week and it'll be merge pain for everyone.  Yeah, we are fussy
about this, but it does make life less painful in the long run.
>> +	struct mutex cmd_lock;
>> +
>> +	/*
>> +	 * Statically allocated command structure that holds parameters
>> +	 * and response.
>> +	 */
>> +	struct cros_ec_command *msg;
>> +	struct ec_params_motion_sense param;
>> +	struct ec_response_motion_sense *resp;
>> +
>> +	/* Type of sensor */
>> +	enum motionsensor_type type;
>> +	enum motionsensor_location loc;
>> +
>> +	/*
>> +	 * Calibration parameters. Note that trigger captured data will always
>> +	 * provide the calibrated values.
>> +	 */
>> +	struct calib_data {
>> +		s16 offset;
>> +	} calib[MAX_AXIS];
>> +
>> +	/*
>> +	 * Static array to hold data from a single capture. For each
>> +	 * channel we need 2 bytes, except for the timestamp. The timestamp
>> +	 * is always last and is always 8-byte aligned.
>> +	 */
>> +	u8 samples[CROS_EC_SAMPLE_SIZE];
>> +
>> +	/* Pointer to function used for accessing sensors values. */
>> +	cros_ec_sensors_read_t *read_ec_sensors_data;
>> +
>> +	/* Current sampling period */
>> +	int curr_sampl_freq;
>> +};
>> +
>> +/* Basic initialization of the core structure. */
>> +int cros_ec_sensors_core_init(struct platform_device *pdev,
>> +			      struct iio_dev *indio_dev,
>> +			      bool physical_device);
>> +
>> +/*
>> + * cros_ec_sensors_capture - the trigger handler function
>> + *
>> + * @irq: the interrupt number
>> + * @p: private data - always a pointer to the poll func.
>> + *
>> + * On a trigger event occurring, if the pollfunc is attached then this
>> + * handler is called as a threaded interrupt (and hence may sleep). It
>> + * is responsible for grabbing data from the device and pushing it into
>> + * the associated buffer.
>> + */
>> +irqreturn_t cros_ec_sensors_capture(int irq, void *p);
>> +
>> +
>> +/*
>> + * cros_ec_motion_send_host_cmd - send motion sense host command
>> + *
>> + * @st Pointer to state information for device.
>> + * @opt_length: optional length: to reduce the response size,
>> + *    useful on the data path.
>> + *    Otherwise, the maximal allowed response size is used.
>> + * @return 0 if ok, -ve on error.
>> + *
>> + * Note, when called, the sub-command is assumed to be set in param->cmd.
>> + */
>> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
>> +				 u16 opt_length);
>> +
>> +/*
>> + * cros_ec_sensors_core_read/write: handler for core attributes.
>> + *
>> + * Handler for attributes identical among sensors:
>> + * - frequency,
>> + * - sampling_frequency.
>> + *
>> + * cmd_lock lock must be held.
Full kerneldoc preferred for descriptions of functions etc. Lets us
do pretty printed manuals nice and easily ;)
>> + */
>> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>> +			  struct iio_chan_spec const *chan,
>> +			  int *val, int *val2, long mask);
>> +
>> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>> +			       struct iio_chan_spec const *chan,
>> +			       int val, int val2, long mask);
>> +
>> +extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
>> +
>> +/* List of extended channel specification for all sensors */
>> +extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
>> +extern const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[];
>> +
>> +#endif  /* __CROS_EC_SENSORS_CORE_H */
>>
> 

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

* Re: [PATCH 05/10] iio: cros_ec_sensors: add ChromeOS EC Contiguous Sensors driver
  2016-07-18  7:02 ` [PATCH 05/10] iio: cros_ec_sensors: add ChromeOS EC Contiguous Sensors driver Enric Balletbo i Serra
@ 2016-07-18 16:09   ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 16:09 UTC (permalink / raw)
  To: Enric Balletbo i Serra, linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 18/07/16 08:02, Enric Balletbo i Serra wrote:
> Handle 3d contiguous sensors like Accelerometers, Gyroscope and
> Magnetometer that are presented by the ChromeOS EC Sensor hub.
> 
> Signed-off-by: Guenter Roeck <groeck@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Various nitpicks inline.

Jonathan
> ---
>  drivers/iio/common/cros_ec_sensors/Kconfig         |   8 +
>  drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
>  .../iio/common/cros_ec_sensors/cros_ec_sensors.c   | 322 +++++++++++++++++++++
>  3 files changed, 331 insertions(+)
>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> 
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> index a30f41e..18002d6 100644
> --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -12,3 +12,11 @@ config IIO_CROS_EC_SENSORS_CORE
>  	  driver.
>  	  Define common attributes and sysfs interrupt handler.
>  
> +config IIO_CROS_EC_SENSORS
> +	tristate "ChromeOS EC Contiguous Sensors"
> +	select IIO_CROS_EC_SENSORS_CORE
> +	help
> +	  Module to handle 3d contiguous sensors like
> +	  Accelerometers, Gyroscope and Magnetometer that are
> +	  presented by the ChromeOS EC Sensor hub.
> +	  Creates an IIO device for each functions.
> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
> index 95b6901..ec716ff 100644
> --- a/drivers/iio/common/cros_ec_sensors/Makefile
> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
> @@ -3,3 +3,4 @@
>  #
>  
>  obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
> +obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> new file mode 100644
> index 0000000..4741118
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -0,0 +1,322 @@
> +/*
> + * cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
> + *
> + * Copyright (C) 2015 Google, Inc
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * This driver uses the cros-ec interface to communicate with the Chrome OS
> + * EC about accelerometer data. Accelerometer access is presented through
> + * iio sysfs.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/mfd/cros_ec_commands.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/platform_device.h>
> +
> +#include "cros_ec_sensors_core.h"
> +
> +#define MAX_CHANNELS (MAX_AXIS + 1)
Really needs a prefix - chances of another define for MAX_CHANNELS in an
included header is way too high!
Same for MAX_AXIS etc...  Basically prefix everything and you'll have it
about right...
> +
> +/* State data for ec_sensors iio driver. */
> +struct cros_ec_sensors_state {
> +	/* Shared by all sensors */
> +	struct cros_ec_sensors_core_state core;
> +
> +	struct iio_chan_spec channels[MAX_CHANNELS];
> +};
> +
> +static int cros_ec_sensors_read(struct iio_dev *indio_dev,
> +			  struct iio_chan_spec const *chan,
> +			  int *val, int *val2, long mask)
> +{
> +	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
> +	s16 data = 0;
> +	s64 val64;
> +	int i;
> +	int ret = IIO_VAL_INT;
> +	int idx = chan->scan_index;
> +
> +	mutex_lock(&st->core.cmd_lock);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (st->core.read_ec_sensors_data(indio_dev,
> +						  1 << idx, &data) < 0)
> +			ret = -EIO;
Never get any useful errors in from the function?  Seems like you are eating
it if there is.
> +		*val = data;
> +		break;
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
> +		st->core.param.sensor_offset.flags = 0;
> +
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
< 0 for consistency?
> +			ret = -EIO;
> +			break;
> +		}
> +
> +		/* Save values */
> +		for (i = X; i < MAX_AXIS; i++)
> +			st->core.calib[i].offset =
> +				st->core.resp->sensor_offset.offset[i];
> +
> +		*val = st->core.calib[idx].offset;
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
> +		st->core.param.sensor_range.data =
> +			EC_MOTION_SENSE_NO_VALUE;
> +
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
> +			ret = -EIO;
> +			break;
> +		}
> +		val64 = st->core.resp->sensor_range.ret;
> +		switch (st->core.type) {
> +		case MOTIONSENSE_TYPE_ACCEL:
> +			/*
> +			 * EC returns data in g, iio exepects m/s^2.
> +			 * Do not use IIO_G_TO_M_S_2 to avoid precision loss.
> +			 */
> +			*val = div_s64(val64 * 980665, 10);
> +			*val2 = 10000 << (CROS_EC_SENSOR_BITS - 1);
> +			ret = IIO_VAL_FRACTIONAL;
> +			break;
> +		case MOTIONSENSE_TYPE_GYRO:
> +			/* EC returns date in dps, iio expects rad/s.
Fixup any multiline comment syntax issues please.
> +			 * Do not use IIO_DEGREE_TO_RAD to avoid precision
> +			 * loss. Round to the nearest integer.
> +			 */
> +			*val = div_s64(val64 * 314159 + 9000000ULL, 1000);
> +			*val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
> +			ret = IIO_VAL_FRACTIONAL;
> +			break;
> +		case MOTIONSENSE_TYPE_MAG:
> +			/*
> +			 * EC returns date in 16LSB / uT,
> +			 * iio expects Gauss
> +			 */
> +			*val = val64;
> +			*val2 = 100 << (CROS_EC_SENSOR_BITS - 1);
> +			ret = IIO_VAL_FRACTIONAL;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +		}
> +		break;
> +	default:
> +		ret = cros_ec_sensors_core_read(
> +				&st->core, chan, val, val2, mask);
> +		break;
> +	}
> +	mutex_unlock(&st->core.cmd_lock);
> +	return ret;
> +}
> +
> +static int cros_ec_sensors_write(struct iio_dev *indio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       int val, int val2, long mask)
> +{
> +	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
> +	int i;
> +	int ret = 0;
> +	int idx = chan->scan_index;
> +
> +	mutex_lock(&st->core.cmd_lock);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		st->core.calib[idx].offset = val;
Spacing wise I'd expect a blank line here as I think this refers to the
stuff below.

> +		/* Send to EC for each axis, even if not complete */
> +
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
> +		st->core.param.sensor_offset.flags =
> +			MOTION_SENSE_SET_OFFSET;
> +		for (i = X; i < MAX_AXIS; i++)
> +			st->core.param.sensor_offset.offset[i] =
> +				st->core.calib[i].offset;
> +		st->core.param.sensor_offset.temp =
> +			EC_MOTION_SENSE_INVALID_CALIB_TEMP;
> +
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0))
> +			ret = -EIO;
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		if (st->core.type == MOTIONSENSE_TYPE_MAG) {
> +			ret = -EINVAL;
> +			break;
> +		}
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
> +		st->core.param.sensor_range.data = val;
> +
> +		/* Always roundup, so caller gets at least what it asks for. */
> +		st->core.param.sensor_range.roundup = 1;
> +
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0))
> +			ret = -EIO;
> +		break;
> +	default:
> +		ret = cros_ec_sensors_core_write(
> +				&st->core, chan, val, val2, mask);
> +		break;
> +	}
> +
> +	mutex_unlock(&st->core.cmd_lock);
> +	return ret;
> +}
> +
> +static const struct iio_info ec_sensors_info = {
> +	.read_raw = &cros_ec_sensors_read,
> +	.write_raw = &cros_ec_sensors_write,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int cros_ec_sensors_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> +	struct cros_ec_device *ec_device;
> +	struct iio_dev *indio_dev;
> +	struct cros_ec_sensors_state *state;
> +	struct iio_chan_spec *channel;
> +	int ret, i;
> +
> +	if (!ec_dev || !ec_dev->ec_dev) {
> +		dev_warn(&pdev->dev, "No CROS EC device found.\n");
> +		return -EINVAL;
> +	}
> +	ec_device = ec_dev->ec_dev;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->info = &ec_sensors_info;
> +	state = iio_priv(indio_dev);
> +	for (channel = state->channels, i = X; i < MAX_AXIS; i++, channel++) {
> +		/* common part */
> +		channel->info_mask_separate =
> +			BIT(IIO_CHAN_INFO_RAW) |
> +			BIT(IIO_CHAN_INFO_CALIBBIAS);
> +		channel->info_mask_shared_by_all =
> +			BIT(IIO_CHAN_INFO_SCALE) |
> +			BIT(IIO_CHAN_INFO_FREQUENCY) |
> +			BIT(IIO_CHAN_INFO_SAMP_FREQ);
> +		channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
> +		channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
> +		channel->scan_type.shift = 0;
> +		channel->scan_index = i;
> +		channel->ext_info = cros_ec_sensors_ext_info;
> +		channel->modified = 1;
> +		channel->channel2 = IIO_MOD_X + i;
> +		channel->scan_type.sign = 's';
> +
> +		state->core.calib[i].offset = 0;
> +
> +		/* sensor specific */
> +		switch (state->core.type) {
> +		case MOTIONSENSE_TYPE_ACCEL:
> +			channel->type = IIO_ACCEL;
> +			break;
> +		case MOTIONSENSE_TYPE_GYRO:
> +			channel->type = IIO_ANGL_VEL;
> +			break;
> +		case MOTIONSENSE_TYPE_MAG:
> +			channel->type = IIO_MAGN;
> +			break;
> +		default:
> +			dev_warn(&pdev->dev, "unknown\n");
> +		}
> +	}
> +
> +	/* Timestamp */
> +	channel->type = IIO_TIMESTAMP;
> +	channel->channel = -1;
> +	channel->scan_index = MAX_AXIS;
> +	channel->scan_type.sign = 's';
> +	channel->scan_type.realbits = 64;
> +	channel->scan_type.storagebits = 64;
> +
> +	indio_dev->channels = state->channels;
> +	indio_dev->num_channels = MAX_CHANNELS;
This needs to be precisely the number of channels, so a generic value called
MAX_CHANNELS seems a little wrong.  If it's always 4, then just put a 4 here
(guessing!)
> +
> +	/* There is only enough room for accel and gyro in the io space */
*laughs* - genious ;)
> +	if ((state->core.ec->cmd_readmem != NULL) &&
> +	    (state->core.type != MOTIONSENSE_TYPE_MAG))
> +		state->core.read_ec_sensors_data = cros_ec_sensors_read_lpc;
> +	else
> +		state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> +					 cros_ec_sensors_capture, NULL);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret)
> +		goto error_uninit_buffer;
> +	return 0;
> +
> +error_uninit_buffer:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +	return ret;
> +}
> +
> +static int cros_ec_sensors_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(indio_dev);
> +	iio_triggered_buffer_cleanup(indio_dev);
blank line here preferred (and in similar locations).

> +	return 0;
> +}
> +
> +static const struct platform_device_id cros_ec_sensors_ids[] = {
> +	{
> +		.name = "cros-ec-accel",
> +	},
> +	{
> +		.name = "cros-ec-gyro",
> +	},
> +	{
> +		.name = "cros-ec-mag",
> +	},
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
> +
> +static struct platform_driver cros_ec_sensors_platform_driver = {
> +	.driver = {
> +		.name	= "cros-ec-sensors",
> +		.pm	= &cros_ec_sensors_pm_ops,
> +	},
> +	.probe		= cros_ec_sensors_probe,
> +	.remove		= cros_ec_sensors_remove,
> +	.id_table	= cros_ec_sensors_ids,
> +};
> +module_platform_driver(cros_ec_sensors_platform_driver);
> +
> +MODULE_DESCRIPTION("ChromeOS EC 3-axis sensors driver");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH 06/10] iio: cros_ec_light_prox: add ChromeOS EC Light and Proximity Sensors
  2016-07-18  7:02 ` [PATCH 06/10] iio: cros_ec_light_prox: add ChromeOS EC Light and Proximity Sensors Enric Balletbo i Serra
@ 2016-07-18 16:11   ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 16:11 UTC (permalink / raw)
  To: Enric Balletbo i Serra, linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 18/07/16 08:02, Enric Balletbo i Serra wrote:
> Handle Light and Proximity sensors presented by the ChromeOS EC Sensor hub.
> Creates an IIO device for each functions.
> 
> Signed-off-by: Guenter Roeck <groeck@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Few more nitpicks, but basically the same as the previous..

Jonathan
> ---
>  drivers/iio/common/cros_ec_sensors/Kconfig         |   9 +
>  drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
>  .../common/cros_ec_sensors/cros_ec_light_prox.c    | 288 +++++++++++++++++++++
>  3 files changed, 298 insertions(+)
>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c
> 
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> index 18002d6..02559d2 100644
> --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -20,3 +20,12 @@ config IIO_CROS_EC_SENSORS
>  	  Accelerometers, Gyroscope and Magnetometer that are
>  	  presented by the ChromeOS EC Sensor hub.
>  	  Creates an IIO device for each functions.
> +
> +config IIO_CROS_EC_LIGHT_PROX
> +	tristate "ChromeOS EC Light and Proximity Sensors"
> +	select IIO_CROS_EC_SENSORS_CORE
> +	help
> +	  Module to handle Light and Proximity sensors
> +	  presented by the ChromeOS EC Sensor hub.
> +	  Creates an IIO device for each functions.
> +	  Only one source is exposed by the EC.
> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
> index ec716ff..7aaf2a2 100644
> --- a/drivers/iio/common/cros_ec_sensors/Makefile
> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
> @@ -4,3 +4,4 @@
>  
>  obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
>  obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
> +obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c b/drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c
> new file mode 100644
> index 0000000..333e927
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_light_prox.c
> @@ -0,0 +1,288 @@
> +/*
> + * cros_ec_light_proxmity - Driver for light and prox sensors behing CrOS EC.
> + *
> + * Copyright (C) 2015 Google, Inc
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * This driver uses the cros-ec interface to communicate with the Chrome OS
> + * EC about accelerometer data. Accelerometer access is presented through
> + * iio sysfs.
Fix this comment as I doubt this driver does that ;)
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/mfd/cros_ec_commands.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/platform_device.h>
> +
> +#include "cros_ec_sensors_core.h"
> +
> +/*
> + * We only represent one entry for light or proximity.
> + * EC is merging different light sensors to return the
> + * what the eye would see.
> + * For proximity, we currently support only one light source.
> + */
> +#define MAX_CHANNELS (1 + 1)
> +
> +/* State data for ec_sensors iio driver. */
> +struct cros_ec_sensors_state {
> +	/* Shared by all sensors */
> +	struct cros_ec_sensors_core_state core;
> +
> +	struct iio_chan_spec channels[MAX_CHANNELS];
> +};
> +
> +static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
> +			  struct iio_chan_spec const *chan,
> +			  int *val, int *val2, long mask)
> +{
> +	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
> +	u16 data = 0;
> +	s64 val64;
> +	int ret = IIO_VAL_INT;
> +	int idx = chan->scan_index;
> +
> +	mutex_lock(&st->core.cmd_lock);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
> +					(s16 *)&data) < 0)
> +			ret = -EIO;
> +		*val = data;
> +		break;
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
> +		st->core.param.sensor_offset.flags = 0;
> +
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
> +			ret = -EIO;
> +			break;
> +		}
> +
> +		/* Save values */
> +		st->core.calib[0].offset =
> +			st->core.resp->sensor_offset.offset[0];
> +
> +		*val = st->core.calib[idx].offset;
> +		break;
> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		/*
> +		 * RANGE is used for calibration
> +		 * scalse is a number x.y, where x is coded on 16bits,
> +		 * y coded on 16 bits, between 0 and 9999.
> +		 */
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
> +		st->core.param.sensor_range.data =
> +			EC_MOTION_SENSE_NO_VALUE;
> +
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
> +			ret = -EIO;
> +			break;
> +		}
> +		val64 = st->core.resp->sensor_range.ret;
> +		*val = val64 >> 16;
> +		*val2 = (val64 & 0xffff) * 100;
> +		ret = IIO_VAL_INT_PLUS_MICRO;
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		/* Light: Result in Lux, using calibration multiplier */
> +		/* Prox: Result in cm. */
> +		*val = 1;
> +		ret = IIO_VAL_INT;
> +		break;
> +	default:
> +		ret = cros_ec_sensors_core_read(
> +				&st->core, chan, val, val2, mask);
> +		break;
> +	}
> +	mutex_unlock(&st->core.cmd_lock);
> +	return ret;
> +}
> +
> +static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       int val, int val2, long mask)
> +{
> +	struct cros_ec_sensors_state *st = iio_priv(indio_dev);
> +	int ret = 0;
> +	int idx = chan->scan_index;
> +
> +	mutex_lock(&st->core.cmd_lock);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		st->core.calib[idx].offset = val;
> +		/* Send to EC for each axis, even if not complete */
> +
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
> +		st->core.param.sensor_offset.flags =
> +			MOTION_SENSE_SET_OFFSET;
> +		st->core.param.sensor_offset.offset[0] =
> +			st->core.calib[0].offset;
> +		st->core.param.sensor_offset.temp =
> +			EC_MOTION_SENSE_INVALID_CALIB_TEMP;
> +
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0))
> +			ret = -EIO;
> +		break;
> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
> +		st->core.param.sensor_range.data = (val << 16) | (val2 / 100);
> +		if (cros_ec_motion_send_host_cmd(&st->core, 0))
> +			ret = -EIO;
> +		break;
> +	default:
> +		ret = cros_ec_sensors_core_write(
> +				&st->core, chan, val, val2, mask);
> +		break;
> +	}
> +
> +	mutex_unlock(&st->core.cmd_lock);
> +	return ret;
> +}
> +
> +static const struct iio_info ec_sensors_info = {
> +	.read_raw = &cros_ec_light_prox_read,
> +	.write_raw = &cros_ec_light_prox_write,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int cros_ec_sensors_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> +	struct cros_ec_device *ec_device;
> +	struct iio_dev *indio_dev;
> +	struct cros_ec_sensors_state *state;
> +	struct iio_chan_spec *channel;
> +	int ret;
> +
> +	if (!ec_dev || !ec_dev->ec_dev) {
> +		dev_warn(&pdev->dev, "No CROS EC device found.\n");
> +		return -EINVAL;
> +	}
> +	ec_device = ec_dev->ec_dev;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->info = &ec_sensors_info;
> +	state = iio_priv(indio_dev);
> +	state->core.type = state->core.resp->info.type;
> +	state->core.loc = state->core.resp->info.location;
> +	channel = state->channels;
perhaps a blank line here for readability?
> +	/* common part */
> +	channel->info_mask_separate =
> +		BIT(IIO_CHAN_INFO_RAW) |
> +		BIT(IIO_CHAN_INFO_CALIBBIAS) |
> +		BIT(IIO_CHAN_INFO_CALIBSCALE);
> +	channel->info_mask_shared_by_all =
> +		BIT(IIO_CHAN_INFO_SCALE) |
> +		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> +		BIT(IIO_CHAN_INFO_FREQUENCY);
> +	channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
> +	channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
> +	channel->scan_type.shift = 0;
> +	channel->scan_index = 0;
> +	channel->ext_info = cros_ec_sensors_ext_info;
> +	channel->scan_type.sign = 'u';
> +
> +	state->core.calib[0].offset = 0;
> +
> +	/* sensor specific */
> +	switch (state->core.type) {
> +	case MOTIONSENSE_TYPE_LIGHT:
> +		channel->type = IIO_LIGHT;
> +		break;
> +	case MOTIONSENSE_TYPE_PROX:
> +		channel->type = IIO_PROXIMITY;
> +		break;
> +	default:
> +		dev_warn(&pdev->dev, "unknown\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Timestamp */
> +	channel++;
> +	channel->type = IIO_TIMESTAMP;
> +	channel->channel = -1;
> +	channel->scan_index = 1;
> +	channel->scan_type.sign = 's';
> +	channel->scan_type.realbits = 64;
> +	channel->scan_type.storagebits = 64;
> +
> +	indio_dev->channels = state->channels;
> +	indio_dev->num_channels = MAX_CHANNELS;
> +
> +	state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> +			cros_ec_sensors_capture, NULL);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret)
> +		iio_triggered_buffer_cleanup(indio_dev);
> +	return ret;
> +}
> +
> +static int cros_ec_sensors_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(indio_dev);
> +	iio_triggered_buffer_cleanup(indio_dev);
blank line here please + similar locations.
> +	return 0;
> +}
> +
> +static const struct platform_device_id cros_ec_sensors_ids[] = {
> +	{
> +		.name = "cros-ec-prox",
> +	},
> +	{
> +		.name = "cros-ec-light",
> +	},
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
> +
> +static struct platform_driver cros_ec_sensors_platform_driver = {
> +	.driver = {
> +		.name	= "cros-ec-light-prox",
> +		.pm	= &cros_ec_sensors_pm_ops,
> +	},
> +	.probe		= cros_ec_sensors_probe,
> +	.remove		= cros_ec_sensors_remove,
> +	.id_table	= cros_ec_sensors_ids,
> +};
> +module_platform_driver(cros_ec_sensors_platform_driver);
> +
> +MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH 07/10] iio: cros_ec_activity: add ChromeOS EC Activity Sensors
  2016-07-18  7:02 ` [PATCH 07/10] iio: cros_ec_activity: add ChromeOS EC Activity Sensors Enric Balletbo i Serra
@ 2016-07-18 16:16   ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 16:16 UTC (permalink / raw)
  To: Enric Balletbo i Serra, linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 18/07/16 08:02, Enric Balletbo i Serra wrote:
> Handle activity events detections presented by the ChromeOS
> EC Sensor hub. Activities can be simple (low/no motion) or more complex
> (riding train). They are being reported by physical devices or the EC
> itself.
> 
> Signed-off-by: Guenter Roeck <groeck@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Interesting.  What docs exist?

For now this is probably more of an RFC than a finished driver, but
certainly interesting to know this stuff exists.

Jonathan
> ---
>  drivers/iio/common/cros_ec_sensors/Kconfig         |  10 +
>  drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
>  .../iio/common/cros_ec_sensors/cros_ec_activity.c  | 294 +++++++++++++++++++++
>  3 files changed, 305 insertions(+)
>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_activity.c
> 
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> index 02559d2..22b4211 100644
> --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -29,3 +29,13 @@ config IIO_CROS_EC_LIGHT_PROX
>  	  presented by the ChromeOS EC Sensor hub.
>  	  Creates an IIO device for each functions.
>  	  Only one source is exposed by the EC.
> +
> +config IIO_CROS_EC_ACTIVITY
> +	tristate "ChromeOS EC Activity Sensors"
> +	select IIO_CROS_EC_SENSORS_CORE
> +	help
> +	  Module to handle activity events detections presented by the ChromeOS
> +	  EC Sensor hub.
> +	  Activities can be simple (low/no motion) or more complex (riding train).
> +	  They are being reported by physical devices or the EC itself.
> +	  Creates an IIO device to manage all activities.
> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
> index 7aaf2a2..8f54f1e 100644
> --- a/drivers/iio/common/cros_ec_sensors/Makefile
> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
> @@ -5,3 +5,4 @@
>  obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
>  obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
>  obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
> +obj-$(CONFIG_IIO_CROS_EC_ACTIVITY) += cros_ec_activity.o
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c
> new file mode 100644
> index 0000000..b2e39d6
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c
> @@ -0,0 +1,294 @@
> +/*
> + * cros_ec_sensors_activity - Driver for activities/gesture recognition.
> + *
> + * Copyright (C) 2015 Google, Inc
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * This driver uses the cros-ec interface to communicate with the Chrome OS
> + * EC about accelerometer data. Accelerometer access is presented through
> + * iio sysfs.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/iio/iio.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/mfd/cros_ec_commands.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/platform_device.h>
> +
> +#include "cros_ec_sensors_core.h"
> +
> +/* st data for ec_sensors iio driver. */
> +struct cros_ec_sensors_state {
> +	/* Shared by all sensors */
> +	struct cros_ec_sensors_core_state core;
> +
> +	struct iio_chan_spec *channels;
> +	unsigned int nb_activities;
> +};
> +
> +static const struct iio_event_spec cros_ec_activity_single_shot[] = {
> +	{
> +		.type = IIO_EV_TYPE_CHANGE,
> +		/* significant motion trigger when we get out of still. */
> +		.dir = IIO_EV_DIR_FALLING,
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
> +	 },
> +};
> +
> +static int ec_sensors_read(struct iio_dev *indio_dev,
> +			  struct iio_chan_spec const *chan,
> +			  int *val, int *val2, long mask)
> +{
> +	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
> +		 chan->channel2);
> +	return -EPERM;
> +}
> +
> +static int ec_sensors_write(struct iio_dev *indio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       int val, int val2, long mask)
> +{
> +	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
> +		 chan->channel2);
> +	return -EPERM;
> +}
> +
> +static int cros_ec_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 cros_ec_sensors_state *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (chan->type != IIO_ACTIVITY)
> +		return -EINVAL;
> +
> +	mutex_lock(&st->core.cmd_lock);
> +	st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES;
> +	if (cros_ec_motion_send_host_cmd(&st->core, 0) == EC_RES_SUCCESS) {
> +		switch (chan->channel2) {
> +		case IIO_MOD_STILL:
> +			ret = !!(st->core.resp->list_activities.enabled &
> +				 (1 << MOTIONSENSE_ACTIVITY_SIG_MOTION));
> +			break;
> +		case IIO_MOD_DOUBLE_TAP:
> +			ret = !!(st->core.resp->list_activities.enabled &
> +				 (1 << MOTIONSENSE_ACTIVITY_DOUBLE_TAP));
> +			break;
> +		default:
> +			dev_warn(&indio_dev->dev, "Unknown activity: %d\n",
> +				 chan->channel2);
> +			ret = -EINVAL;
> +		}
> +	} else {
> +		ret = -EIO;
> +	}
> +	mutex_unlock(&st->core.cmd_lock);
> +	return ret;
> +}
> +
> +static int cros_ec_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 cros_ec_sensors_state *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (chan->type != IIO_ACTIVITY)
> +		return -EINVAL;
> +
> +	mutex_lock(&st->core.cmd_lock);
> +	st->core.param.cmd = MOTIONSENSE_CMD_SET_ACTIVITY;
> +	switch (chan->channel2) {
> +	case IIO_MOD_STILL:
> +		st->core.param.set_activity.activity =
> +			MOTIONSENSE_ACTIVITY_SIG_MOTION;
> +		break;
> +	case IIO_MOD_DOUBLE_TAP:
> +		st->core.param.set_activity.activity =
> +			MOTIONSENSE_ACTIVITY_DOUBLE_TAP;
> +		break;
> +	default:
> +		dev_warn(&indio_dev->dev, "Unknown activity: %d\n",
> +			 chan->channel2);
> +	}
> +	st->core.param.set_activity.enable = state;
> +
> +	ret = cros_ec_motion_send_host_cmd(&st->core, 0);
> +
> +	mutex_unlock(&st->core.cmd_lock);
> +	return ret;
> +}
> +
> +/* Not implemented */
> +static int cros_ec_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)
> +{
> +	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
> +		 chan->channel2);
> +	return -EPERM;
> +}
> +
> +static int cros_ec_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)
> +{
> +	dev_warn(&indio_dev->dev, "%s: Not Expected: %d\n", __func__,
> +		 chan->channel2);
> +	return -EPERM;
> +}
> +
> +static const struct iio_info ec_sensors_info = {
> +	.read_raw = &ec_sensors_read,
> +	.write_raw = &ec_sensors_write,
> +	.read_event_config = cros_ec_read_event_config,
> +	.write_event_config = cros_ec_write_event_config,
> +	.read_event_value = cros_ec_read_event_value,
> +	.write_event_value = cros_ec_write_event_value,
> +};
> +
> +static int cros_ec_sensors_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> +	struct cros_ec_device *ec_device;
> +	struct iio_dev *indio_dev;
> +	struct cros_ec_sensors_state *st;
> +	struct iio_chan_spec *channel;
> +	unsigned long activities;
> +	int i, index, ret, nb_activities;
> +
> +	if (!ec_dev || !ec_dev->ec_dev) {
> +		dev_warn(&pdev->dev, "No CROS EC device found.\n");
> +		return -EINVAL;
> +	}
> +	ec_device = ec_dev->ec_dev;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->info = &ec_sensors_info;
> +	st = iio_priv(indio_dev);
> +	st->core.type = st->core.resp->info.type;
> +	st->core.loc = st->core.resp->info.location;
> +
> +	/*
> +	 * List all available activities
> +	 */
Comment syntax please.
> +	st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES;
> +	ret = cros_ec_motion_send_host_cmd(&st->core, 0);
> +	if (ret)
> +		return ret;
> +	activities = st->core.resp->list_activities.enabled |
> +		     st->core.resp->list_activities.disabled;
> +	nb_activities = hweight_long(activities) + 1;
> +
> +	if (!activities)
> +		return -ENODEV;
> +
> +	/* Allocate a channel per activity and one for timestamp */
> +	st->channels = devm_kcalloc(&pdev->dev, nb_activities,
> +				    sizeof(*st->channels), GFP_KERNEL);
> +	if (!st->channels)
> +		return -ENOMEM;
> +
> +	channel = &st->channels[0];
> +	index = 0;
> +	for_each_set_bit(i, &activities, BITS_PER_LONG) {
> +		channel->scan_index = index;
> +
> +		/* List all available activities */
> +		channel->type = IIO_ACTIVITY;
> +		channel->modified = 1;
> +		channel->event_spec = cros_ec_activity_single_shot;
> +		channel->num_event_specs =
> +				ARRAY_SIZE(cros_ec_activity_single_shot);
> +		switch (i) {
> +		case MOTIONSENSE_ACTIVITY_SIG_MOTION:
> +			channel->channel2 = IIO_MOD_STILL;
:) The only match we currently have?  Ah well guess that set is about
to get bigger.
> +			break;
> +		case MOTIONSENSE_ACTIVITY_DOUBLE_TAP:
> +			channel->channel2 = IIO_MOD_DOUBLE_TAP;
This particular one is an event that happens once, report it as such.

> +			break;
> +		default:
> +			dev_warn(&pdev->dev, "Unknown activity: %d\n", i);
> +			continue;
> +		}
> +		channel->ext_info = cros_ec_sensors_limited_info;
> +		channel++;
> +		index++;
> +	}
> +
> +	/* Timestamp */
> +	channel->scan_index = index;
> +	channel->type = IIO_TIMESTAMP;
> +	channel->channel = -1;
> +	channel->scan_type.sign = 's';
> +	channel->scan_type.realbits = 64;
> +	channel->scan_type.storagebits = 64;
> +
> +	indio_dev->channels = st->channels;
> +	indio_dev->num_channels = index + 1;
> +
> +	st->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
> +
> +	/* Driver is incomplete: by itself, no way to get event directly */
Probably going to be a case of expanding the range of activities we already
report.  Trick has always been to assign these a percentage (likelihood).
So far all devices have given us 100 or 0, but who knows when they will start
being realistic about their ability to work this stuff out ;)  Just like
old weather forecasts used to be...

> +	ret = iio_device_register(indio_dev);
> +	return ret;
return iio_device_register(..)
> +}
> +
> +static int cros_ec_sensors_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(indio_dev);
blank line.
> +	return 0;
> +}
> +
> +static const struct platform_device_id cros_ec_sensors_ids[] = {
> +	{
> +		.name = "cros-ec-activity",
> +	},
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
> +
> +static struct platform_driver cros_ec_sensors_platform_driver = {
> +	.driver = {
> +		.name	= "cros-ec-activity",
> +	},
> +	.probe		= cros_ec_sensors_probe,
> +	.remove		= cros_ec_sensors_remove,
> +	.id_table	= cros_ec_sensors_ids,
> +};
> +module_platform_driver(cros_ec_sensors_platform_driver);
> +
> +MODULE_DESCRIPTION("ChromeOS EC activity sensors driver");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring
  2016-07-18  7:02 ` [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring Enric Balletbo i Serra
@ 2016-07-18 16:27   ` Jonathan Cameron
  2016-07-26 23:29     ` Gwendal Grignou
  2016-07-27  0:17   ` Guenter Roeck
  1 sibling, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-18 16:27 UTC (permalink / raw)
  To: Enric Balletbo i Serra, linux-kernel, linux-iio
  Cc: Olof Johansson, Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 18/07/16 08:02, Enric Balletbo i Serra wrote:
> Add support for handling sensor events FIFO produced by the sensor
> hub. A single device with a buffer will collect all samples produced
> by the sensors managed by the CrosEC sensor hub.
So you are defining a different device to support the buffer?
That's 'unusual'...
> 
> Signed-off-by: Guenter Roeck <groeck@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> ---
>  drivers/iio/common/cros_ec_sensors/Kconfig         |   9 +
>  drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
>  .../common/cros_ec_sensors/cros_ec_sensors_ring.c  | 541 +++++++++++++++++++++
>  3 files changed, 551 insertions(+)
>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
> 
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> index 22b4211..778c3bf 100644
> --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -39,3 +39,12 @@ config IIO_CROS_EC_ACTIVITY
>  	  Activities can be simple (low/no motion) or more complex (riding train).
>  	  They are being reported by physical devices or the EC itself.
>  	  Creates an IIO device to manage all activities.
> +
> +config IIO_CROS_EC_SENSORS_RING
> +	tristate "ChromeOS EC Sensors Ring"
> +	depends on IIO_CROS_EC_SENSORS || IIO_CROS_EC_LIGHT_PROX
> +	help
> +	  Add support for handling sensor events FIFO produced by
> +	  the sensor hub.
> +	  A single device with a buffer will collect all samples produced
> +	  by the sensors managed by the CrosEC sensor hub
> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
> index 8f54f1e..0eb3fc5 100644
> --- a/drivers/iio/common/cros_ec_sensors/Makefile
> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
>  obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
>  obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
>  obj-$(CONFIG_IIO_CROS_EC_ACTIVITY) += cros_ec_activity.o
> +obj-$(CONFIG_IIO_CROS_EC_SENSORS_RING) += cros_ec_sensors_ring.o
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
> new file mode 100644
> index 0000000..1c74df9
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
> @@ -0,0 +1,541 @@
> +/*
> + * cros_ec_sensors_ring - Driver for Chrome OS EC Sensor hub FIFO.
> + *
> + * Copyright (C) 2015 Google, Inc
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * This driver uses the cros-ec interface to communicate with the Chrome OS
> + * EC about accelerometer data. Accelerometer access is presented through
> + * iio sysfs.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/mfd/cros_ec_commands.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/platform_device.h>
> +
> +#include "cros_ec_sensors_core.h"
> +
> +/* The ring is a FIFO that return sensor information from
> + * the single EC FIFO.
> + * There are always 5 channels returned:
> +* | ID | FLAG | X | Y | Z | Timestamp |
> + * ID is the EC sensor id
> + * FLAG is for meta data, only flush bit is defined.
> + */
> +#define CROS_EC_FLUSH_BIT 1
> +
> +enum {
> +	CHANNEL_SENSOR_ID,
> +	CHANNEL_SENSOR_FLAG,
Ah, I'm beginning to understand.

I think you really need to demux these into the appropriate devices individual
buffers... If these are coming in fairly randomly (guessing so?) then you'll
want a top level mfd pushing data to each of the child devices (representing
the different possible sensors).

Simply pushing it into a buffer with metadata doesn't fit with the IIO ABI
at all.

Note that there is no obligation to have any triggers involved at all. Here I'd
suggest you don't as it'll just make life difficult for little gain.

> +	CHANNEL_X,
> +	CHANNEL_Y,
> +	CHANNEL_Z,
> +	CHANNEL_TIMESTAMP,
> +	MAX_CHANNEL,
> +};
> +
> +enum {
> +	LAST_TS,
> +	NEW_TS,
> +	ALL_TS
> +};
> +
> +#define CROS_EC_SENSOR_MAX 16
> +
> +struct cros_ec_fifo_info {
> +	struct ec_response_motion_sense_fifo_info info;
> +	uint16_t lost[CROS_EC_SENSOR_MAX];
> +};
> +
single blank line is plenty.
> +
> +struct cros_ec_sensors_ring_sample {
> +	uint8_t sensor_id;
> +	uint8_t flag;
> +	int16_t  vector[MAX_AXIS];
> +	s64      timestamp;
> +} __packed;
> +
> +/* State data for ec_sensors iio driver. */
> +struct cros_ec_sensors_ring_state {
> +	/* Shared by all sensors */
> +	struct cros_ec_sensors_core_state core;
> +
> +	/* Notifier to kick to the interrupt */
> +	struct notifier_block notifier;
> +
> +	/* Preprocessed ring to send to kfifo */
> +	struct cros_ec_sensors_ring_sample *ring;
> +
> +	struct iio_trigger *trig;
> +	s64    fifo_timestamp[ALL_TS];
> +	struct ec_response_motion_sense_fifo_info fifo_info;
> +};
> +
> +static const struct iio_info ec_sensors_info = {
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static s64 cros_ec_get_time_ns(void)
> +{
> +	struct timespec ts;
> +
> +	get_monotonic_boottime(&ts);
> +	return timespec_to_ns(&ts);
Use the core IIO timestamp helper? (now supports all the different clocks...)
> +}
> +
> +/*
> + * cros_ec_ring_process_event: process one EC FIFO event
> + *
> + * Process one EC event, add it in the ring if necessary.
> + *
> + * Return true if out event has been populated.
> + *
> + * fifo_info: fifo information from the EC.
> + * fifo_timestamp: timestamp at time of fifo_info collection.
> + * current_timestamp: estimated current timestamp.
> + * in: incoming FIFO event from EC
> + * out: outgoing event to user space.
> + */
> +bool cros_ec_ring_process_event(const struct cros_ec_fifo_info *fifo_info,
> +				const s64 fifo_timestamp,
> +				s64 *current_timestamp,
> +				struct ec_response_motion_sensor_data *in,
> +				struct cros_ec_sensors_ring_sample *out)
> +{
> +	int axis;
> +	s64 new_timestamp;
> +
> +	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) {
> +		new_timestamp = fifo_timestamp -
> +			((s64)fifo_info->info.timestamp * 1000) +
> +			((s64)in->timestamp * 1000);
> +		/*
> +		 * The timestamp can be stale if we had to use the fifo
> +		 * info timestamp.
> +		 */
> +		if (new_timestamp - *current_timestamp > 0)
> +			*current_timestamp = new_timestamp;
> +	}
> +
> +	if (in->flags & MOTIONSENSE_SENSOR_FLAG_FLUSH) {
> +		out->sensor_id = in->sensor_num;
> +		out->timestamp = *current_timestamp;
> +		out->flag = CROS_EC_FLUSH_BIT;
> +		return true;
> +	}
> +	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
> +		/* If we just have a timestamp, skip this entry. */
> +		return false;
> +
> +	/* Regular sample */
> +	out->sensor_id = in->sensor_num;
> +	out->timestamp = *current_timestamp;
> +	out->flag = 0;
> +	for (axis = X; axis < MAX_AXIS; axis++)
> +		out->vector[axis] = in->data[axis];
> +	return true;
> +}
> +/*
> + * cros_ec_ring_handler - the trigger handler function
> + *
> + * @irq: the interrupt number
> + * @p: private data - always a pointer to the poll func.
> + *
> + * On a trigger event occurring, if the pollfunc is attached then this
> + * handler is called as a threaded interrupt (and hence may sleep). It
> + * is responsible for grabbing data from the device and pushing it into
> + * the associated buffer.
> + */
> +static irqreturn_t cros_ec_ring_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct cros_ec_sensors_ring_state *state = iio_priv(indio_dev);
> +	struct cros_ec_fifo_info fifo_info;
> +	s64    fifo_timestamp, current_timestamp;
> +	int    i, j, number_data, ret;
> +	unsigned long sensor_mask = 0;
> +	struct ec_response_motion_sensor_data *in;
> +	struct cros_ec_sensors_ring_sample *out, *last_out;
> +
> +
> +	/* Get FIFO information */
> +	mutex_lock(&state->core.cmd_lock);
> +	memcpy(&fifo_info, &state->fifo_info, sizeof(state->fifo_info));
> +	fifo_timestamp = state->fifo_timestamp[NEW_TS];
> +	mutex_unlock(&state->core.cmd_lock);
> +
> +	/* Copy elements in the main fifo */
> +	if (fifo_info.info.total_lost) {
> +		/* Need to retrieve the number of lost vectors per sensor */
> +		state->core.param.cmd = MOTIONSENSE_CMD_FIFO_INFO;
> +		if (cros_ec_motion_send_host_cmd(&state->core, 0))
> +			goto ring_handler_end;
> +		memcpy(&fifo_info, &state->core.resp->fifo_info,
> +		       sizeof(fifo_info));
> +		fifo_timestamp = cros_ec_get_time_ns();
> +	}
> +
> +	current_timestamp = state->fifo_timestamp[LAST_TS];
> +	out = state->ring;
> +	for (i = 0; i < fifo_info.info.count; i += number_data) {
> +		state->core.param.cmd = MOTIONSENSE_CMD_FIFO_READ;
> +		state->core.param.fifo_read.max_data_vector =
> +			fifo_info.info.count - i;
> +		ret = cros_ec_motion_send_host_cmd(&state->core,
> +			       sizeof(state->core.resp->fifo_read) +
> +			       state->core.param.fifo_read.max_data_vector *
> +			       sizeof(struct ec_response_motion_sensor_data));
> +		if (ret != EC_RES_SUCCESS) {
> +			dev_warn(&indio_dev->dev, "Fifo error: %d\n", ret);
> +			break;
> +		}
> +		number_data =
> +			state->core.resp->fifo_read.number_data;
> +		if (number_data == 0) {
> +			dev_dbg(&indio_dev->dev, "Unexpected empty FIFO\n");
> +			break;
> +		}
> +
> +		for (in = state->core.resp->fifo_read.data, j = 0;
> +		     j < number_data; j++, in++) {
> +			BUG_ON(out >= state->ring + fifo_info.info.size);
> +			if (cros_ec_ring_process_event(
> +					&fifo_info, fifo_timestamp,
> +					&current_timestamp, in, out)) {
> +				sensor_mask |= (1 << in->sensor_num);
> +				out++;
> +			}
> +		}
> +	}
> +	last_out = out;
> +
> +	if (out == state->ring)
> +		/* Unexpected empty FIFO. */
> +		goto ring_handler_end;
> +
> +	/*
> +	 * Check if current_timestamp is ahead of the last sample.
> +	 * Normally, the EC appends a timestamp after the last sample, but if
> +	 * the AP is slow to respond to the IRQ, the EC may have added new
> +	 * samples. Use the FIFO info timestamp as last timestamp then.
> +	 */
> +	if ((last_out-1)->timestamp == current_timestamp)
> +		current_timestamp = fifo_timestamp;
> +
> +	/* check if buffer is set properly */
> +	if (!indio_dev->active_scan_mask ||
> +	    (bitmap_empty(indio_dev->active_scan_mask,
> +			  indio_dev->masklength)))
> +		goto ring_handler_end;
> +
> +	/*
> +	 * calculate proper timestamps
> +	 *
> +	 * If there is a sample with a proper timestamp
> +	 *                        timestamp | count
> +	 * older_unprocess_out --> TS1      | 1
> +	 *                         TS1      | 2
> +	 * out -->                 TS1      | 3
> +	 * next_out -->            TS2      |
> +	 * We spread time for the samples [older_unprocess_out .. out]
> +	 * between TS1 and TS2: [TS1+1/4, TS1+2/4, TS1+3/4, TS2].
> +	 *
> +	 * If we reach the end of the samples, we compare with the
> +	 * current timestamp:
> +	 *
> +	 * older_unprocess_out --> TS1      | 1
> +	 *                         TS1      | 2
> +	 * out -->                 TS1      | 3
> +	 * We know have [TS1+1/3, TS1+2/3, current timestamp]
> +	 */
> +	for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
> +		s64 older_timestamp;
> +		s64 timestamp;
> +		struct cros_ec_sensors_ring_sample *older_unprocess_out =
> +			state->ring;
> +		struct cros_ec_sensors_ring_sample *next_out;
> +		int count = 1;
> +
> +		if (fifo_info.info.total_lost) {
> +			int lost = fifo_info.lost[i];
> +
> +			if (lost)
> +				dev_warn(&indio_dev->dev,
> +					"Sensor %d: lost: %d out of %d\n", i,
> +					lost, fifo_info.info.total_lost);
> +		}
> +
> +		for (out = state->ring; out < last_out; out = next_out) {
> +			s64 time_period;
> +
> +			next_out = out + 1;
> +			if (out->sensor_id != i)
> +				continue;
> +
> +			/* Timestamp to start with */
> +			older_timestamp = out->timestamp;
> +
> +			/* find next sample */
> +			while (next_out < last_out && next_out->sensor_id != i)
> +				next_out++;
> +
> +			if (next_out >= last_out) {
> +				timestamp = current_timestamp;
> +			} else {
> +				timestamp = next_out->timestamp;
> +				if (timestamp == older_timestamp) {
> +					count++;
> +					continue;
> +				}
> +			}
> +
> +			/* The next sample has a new timestamp,
> +			 * spread the unprocessed samples
> +			 */
> +			if (next_out < last_out)
> +				count++;
> +			time_period = div_s64(timestamp - older_timestamp,
> +					      count);
> +
> +			for (; older_unprocess_out <= out;
> +					older_unprocess_out++) {
> +				if (older_unprocess_out->sensor_id != i)
> +					continue;
> +				older_timestamp += time_period;
> +				older_unprocess_out->timestamp =
> +					older_timestamp;
> +			}
> +			count = 1;
> +			/* The next_out sample has a valid timestamp, skip. */
> +			next_out++;
> +			older_unprocess_out = next_out;
> +		}
> +	}
> +
> +	/* push the event into the kfifo */
> +	for (out = state->ring; out < last_out; out++)
> +		iio_push_to_buffers(indio_dev, (u8 *)out);
> +
> +ring_handler_end:
> +	state->fifo_timestamp[LAST_TS] = current_timestamp;
> +	iio_trigger_notify_done(indio_dev->trig);
> +	return IRQ_HANDLED;
> +}
> +
> +static int cros_ec_ring_event(struct notifier_block *nb,
> +	unsigned long queued_during_suspend, void *_notify)
> +{
> +	struct cros_ec_sensors_ring_state *state;
> +	struct cros_ec_device *ec;
> +
> +	state = container_of(nb, struct cros_ec_sensors_ring_state, notifier);
> +	ec = state->core.ec;
> +
> +	if (ec->event_data.event_type != EC_MKBP_EVENT_SENSOR_FIFO)
> +		return NOTIFY_DONE;
> +
> +	if (ec->event_size != sizeof(ec->event_data.data.sensor_fifo)) {
> +		dev_warn(ec->dev, "Invalid fifo info size\n");
> +		return NOTIFY_DONE;
> +	}
> +
> +	if (queued_during_suspend)
> +		return NOTIFY_OK;
> +
> +	mutex_lock(&state->core.cmd_lock);
> +	memcpy(&state->fifo_info, &ec->event_data.data.sensor_fifo.info,
> +			ec->event_size);
> +	state->fifo_timestamp[NEW_TS] = cros_ec_get_time_ns();
> +	mutex_unlock(&state->core.cmd_lock);
> +
> +	/*
> +	 * We are not in a low level interrupt,
> +	 * we can not call iio_trigger_poll().
> +	 */
> +	iio_trigger_poll_chained(state->trig);
> +	return NOTIFY_OK;
> +}
> +
> +#define CROS_EC_RING_ID(_id, _name)		\
> +{						\
> +	.type = IIO_ACCEL,			\
> +	.modified = 1,				\
> +	.channel2 = IIO_NO_MOD,			\
> +	.scan_index = _id,			\
> +	.scan_type = {				\
> +		.sign = 'u',			\
> +		.realbits = 8,			\
> +		.storagebits = 8,		\
> +	},					\
> +	.extend_name = _name,			\
> +}
See comments earlier.  There is no need to have metadata here that userspace
will have to unwind, just demux the data appropriately and push to the
correct iio devices (one per 'scan' - set of samples acquired at approximately
the same time - every time)
> +
> +#define CROS_EC_RING_AXIS(_axis)		\
> +{						\
> +	.type = IIO_ACCEL,			\
> +	.modified = 1,				\
> +	.channel2 = IIO_MOD_##_axis,		\
> +	.scan_index = CHANNEL_##_axis,		\
> +	.scan_type = {				\
> +		.sign = 's',			\
> +		.realbits = 16,			\
> +		.storagebits = 16,		\
> +	},					\
> +	.extend_name = "ring",			\
For obvious reasons given earlier comments I don't like this!
> +}
> +
> +static const struct iio_chan_spec cros_ec_ring_channels[] = {
> +	CROS_EC_RING_ID(CHANNEL_SENSOR_ID, "id"),
> +	CROS_EC_RING_ID(CHANNEL_SENSOR_FLAG, "flag"),
> +	CROS_EC_RING_AXIS(X),
> +	CROS_EC_RING_AXIS(Y),
> +	CROS_EC_RING_AXIS(Z),
> +	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_TIMESTAMP)
> +};
> +
> +static int cros_ec_ring_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> +	struct cros_ec_device *ec_device;
> +	struct iio_dev *indio_dev;
> +	struct cros_ec_sensors_ring_state *state;
> +	int ret;
> +
> +	if (!ec_dev || !ec_dev->ec_dev) {
> +		dev_warn(&pdev->dev, "No CROS EC device found.\n");
> +		return -EINVAL;
> +	}
> +	ec_device = ec_dev->ec_dev;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, false);
> +	if (ret)
> +		return ret;
> +
> +	state = iio_priv(indio_dev);
> +	/* Retrieve FIFO information */
> +	state->core.param.cmd = MOTIONSENSE_CMD_FIFO_INFO;
> +	/* If it fails, just assume the FIFO is not supported.
> +	 * For other errors, the other sensor drivers would have noticed
> +	 * already.
> +	 */
> +	if (cros_ec_motion_send_host_cmd(&state->core, 0))
> +		return -ENODEV;
> +
> +	/* Allocate the full fifo.
> +	 * We need to copy the whole FIFO to set timestamps properly *
> +	 */
> +	state->ring = devm_kcalloc(&pdev->dev,
> +			state->core.resp->fifo_info.size,
> +			sizeof(*state->ring), GFP_KERNEL);
> +	if (!state->ring)
> +		return -ENOMEM;
> +
> +	state->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
> +
> +	indio_dev->channels = cros_ec_ring_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(cros_ec_ring_channels);
> +
> +	indio_dev->info = &ec_sensors_info;
> +
> +	state->trig = devm_iio_trigger_alloc(&pdev->dev,
> +			"%s-trigger%d", indio_dev->name, indio_dev->id);
> +	if (!state->trig)
> +		return -ENOMEM;
> +	state->trig->dev.parent = &pdev->dev;
> +	iio_trigger_set_drvdata(state->trig, indio_dev);
> +
> +	ret = iio_trigger_register(state->trig);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> +					 cros_ec_ring_handler, NULL);
> +	if (ret < 0)
> +		goto err_trigger_unregister;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0)
> +		goto err_buffer_cleanup;
> +
> +	/* register the notifier that will act as a top half interrupt. */
> +	state->notifier.notifier_call = cros_ec_ring_event;
> +	ret = blocking_notifier_chain_register(&ec_device->event_notifier,
> +					       &state->notifier);
> +	if (ret < 0) {
> +		dev_warn(&indio_dev->dev, "failed to register notifier\n");
> +		goto err_device_unregister;
> +	}
> +	return ret;
> +
> +err_device_unregister:
> +	iio_device_unregister(indio_dev);
> +err_buffer_cleanup:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +err_trigger_unregister:
> +	iio_trigger_unregister(state->trig);
> +	return ret;
> +}
> +
> +static int cros_ec_ring_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct cros_ec_sensors_ring_state *state = iio_priv(indio_dev);
> +	struct cros_ec_device *ec = state->core.ec;
> +
> +	blocking_notifier_chain_unregister(&ec->event_notifier,
> +					   &state->notifier);
> +	iio_device_unregister(indio_dev);
> +	iio_triggered_buffer_cleanup(indio_dev);
> +	iio_trigger_unregister(state->trig);
> +	return 0;
> +}
> +
> +static const struct platform_device_id cros_ec_ring_ids[] = {
> +	{
> +		.name = "cros-ec-ring",
> +	},
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(platform, cros_ec_ring_ids);
> +
> +static struct platform_driver cros_ec_ring_platform_driver = {
> +	.driver = {
> +		.name	= "cros-ec-ring",
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= cros_ec_ring_probe,
> +	.remove		= cros_ec_ring_remove,
> +	.id_table	= cros_ec_ring_ids,
> +};
> +module_platform_driver(cros_ec_ring_platform_driver);
> +
> +MODULE_DESCRIPTION("ChromeOS EC sensor hub ring driver");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands.
  2016-07-18 13:49   ` Jonathan Cameron
@ 2016-07-19 18:00     ` Enric Balletbo Serra
  2016-07-21  6:18       ` Jonathan Cameron
  0 siblings, 1 reply; 25+ messages in thread
From: Enric Balletbo Serra @ 2016-07-19 18:00 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Enric Balletbo i Serra, linux-kernel, linux-iio, Olof Johansson,
	Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

Hi Jonathan,

Many thanks for your comments.

2016-07-18 15:49 GMT+02:00 Jonathan Cameron <jic23@kernel.org>:
> On 18/07/16 08:02, Enric Balletbo i Serra wrote:
>> Let's update the command header to include the definitions related to
>> the sensors attached behind the ChromeOS Embedded Controller. The new
>> commands and definitions allow us to get information from these sensors.
>>
>> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Again, I'd be happier seeing this stuff introduced as and when it
> is needed rather than in a magic patch. It's hard to review stuff
> if it's broken up across multiple patches like this.
>
> A few other bits and pieces inline.
>
> Jonathan
>> ---
>>  include/linux/mfd/cros_ec_commands.h | 260 +++++++++++++++++++++++++++++++----
>>  1 file changed, 231 insertions(+), 29 deletions(-)
>>
>> diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
>> index 76728ff..f26a806 100644
>> --- a/include/linux/mfd/cros_ec_commands.h
>> +++ b/include/linux/mfd/cros_ec_commands.h
>> @@ -1290,7 +1290,13 @@ enum motionsense_command {
>>
>>       /*
>>        * EC Rate command is a setter/getter command for the EC sampling rate
>> -      * of all motion sensors in milliseconds.
>> +      * in milliseconds.
>> +      * It is per sensor, the EC run sample task  at the minimum of all
>> +      * sensors EC_RATE.
>> +      * For sensors without hardware FIFO, EC_RATE should be equals to 1/ODR
>> +      * to collect all the sensor samples.
>> +      * For sensor with hardware FIFO, EC_RATE is used as the maximal delay
>> +      * to process of all motion sensors in milliseconds.
>>        */
>>       MOTIONSENSE_CMD_EC_RATE = 2,
>>
>> @@ -1315,37 +1321,138 @@ enum motionsense_command {
>>        */
>>       MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5,
>>
>> -     /* Number of motionsense sub-commands. */
>> -     MOTIONSENSE_NUM_CMDS
>> -};
>> +     /*
>> +      * Returns a single sensor data.
>> +      */
> Please use standard kernel documentation formats throughout.
> If not you may face a Linus rant ;)

Seems that this specific file does not follow the kernel-doc format
strictly [1], should I add the new entries in kernel-doc format or
follow the file style?

Or maybe I should first do a patch to change all the file to the
kernel-doc format?

Or you meant only replace  the

/*
 * one line
 */

for

/* one line */

[1] https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt

>> +     MOTIONSENSE_CMD_DATA = 6,
>> +
>> +     /*
>> +      * Return sensor fifo info.
>> +      */
>> +     MOTIONSENSE_CMD_FIFO_INFO = 7,
>> +
>> +     /*
>> +      * Insert a flush element in the fifo and return sensor fifo info.
>> +      * The host can use that element to synchronize its operation.
>> +      */
>> +     MOTIONSENSE_CMD_FIFO_FLUSH = 8,
>>
>> -enum motionsensor_id {
>> -     EC_MOTION_SENSOR_ACCEL_BASE = 0,
>> -     EC_MOTION_SENSOR_ACCEL_LID = 1,
>> -     EC_MOTION_SENSOR_GYRO = 2,
>> +     /*
>> +      * Return a portion of the fifo.
>> +      */
>> +     MOTIONSENSE_CMD_FIFO_READ = 9,
>> +
>> +     /*
>> +      * Perform low level calibration.
>> +      * On sensors that support it, ask to do offset calibration.
>> +      */
>> +     MOTIONSENSE_CMD_PERFORM_CALIB = 10,
>> +
>> +     /*
>> +      * Sensor Offset command is a setter/getter command for the offset
>> +      * used for calibration.
>> +      * The offsets can be calculated by the host, or via
>> +      * PERFORM_CALIB command.
>> +      */
>> +     MOTIONSENSE_CMD_SENSOR_OFFSET = 11,
>> +
>> +     /*
>> +      * List available activities for a MOTION sensor.
>> +      * Indicates if they are enabled or disabled.
>> +      */
>> +     MOTIONSENSE_CMD_LIST_ACTIVITIES = 12,
>>
>>       /*
>> -      * Note, if more sensors are added and this count changes, the padding
>> -      * in ec_response_motion_sense dump command must be modified.
>> +      * Activity management
>> +      * Enable/Disable activity recognition.
>>        */
>> -     EC_MOTION_SENSOR_COUNT = 3
>> +     MOTIONSENSE_CMD_SET_ACTIVITY = 13,
>> +
>> +     /* Number of motionsense sub-commands. */
>> +     MOTIONSENSE_NUM_CMDS
>>  };
>>
>>  /* List of motion sensor types. */
>>  enum motionsensor_type {
>>       MOTIONSENSE_TYPE_ACCEL = 0,
>>       MOTIONSENSE_TYPE_GYRO = 1,
>> +     MOTIONSENSE_TYPE_MAG = 2,
>> +     MOTIONSENSE_TYPE_PROX = 3,
>> +     MOTIONSENSE_TYPE_LIGHT = 4,
>> +     MOTIONSENSE_TYPE_ACTIVITY = 5,
>> +     MOTIONSENSE_TYPE_MAX,
>>  };
>>
>>  /* List of motion sensor locations. */
>>  enum motionsensor_location {
>>       MOTIONSENSE_LOC_BASE = 0,
>>       MOTIONSENSE_LOC_LID = 1,
>> +     MOTIONSENSE_LOC_MAX,
>>  };
>>
>>  /* List of motion sensor chips. */
>>  enum motionsensor_chip {
>>       MOTIONSENSE_CHIP_KXCJ9 = 0,
>> +     MOTIONSENSE_CHIP_LSM6DS0 = 1,
>> +     MOTIONSENSE_CHIP_BMI160 = 2,
>> +     MOTIONSENSE_CHIP_SI1141 = 3,
>> +     MOTIONSENSE_CHIP_SI1142 = 4,
>> +     MOTIONSENSE_CHIP_SI1143 = 5,
>> +     MOTIONSENSE_CHIP_KX022 = 6,
>> +     MOTIONSENSE_CHIP_L3GD20H = 7,
> Interesting.  So the driver needs some knowledge of what
> is behind it.  I'll read on with interest ;)
>> +};
>> +
>> +struct ec_response_motion_sensor_data {
>> +     /* Flags for each sensor. */
>> +     uint8_t flags;
>> +     /* sensor number the data comes from */
>> +     uint8_t sensor_num;
>> +     /* Each sensor is up to 3-axis. */
>> +     union {
>> +             int16_t             data[3];
>> +             struct {
>> +                     uint16_t    rsvd;
>> +                     uint32_t    timestamp;
>> +             } __packed;
>> +             struct {
>> +                     uint8_t     activity; /* motionsensor_activity */
>> +                     uint8_t     state;
>> +                     int16_t     add_info[2];
>> +             };
>> +     };
>> +} __packed;
>> +
>> +struct ec_response_motion_sense_fifo_info {
>> +     /* Size of the fifo */
>> +     uint16_t size;
>> +     /* Amount of space used in the fifo */
>> +     uint16_t count;
>> +     /* TImestamp recorded in us */
> Timestamp
>> +     uint32_t timestamp;
>> +     /* Total amount of vector lost */
>> +     uint16_t total_lost;
>> +     /* Lost events since the last fifo_info, per sensors */
>> +     uint16_t lost[0];
>> +} __packed;
>> +
>> +struct ec_response_motion_sense_fifo_data {
>> +     uint32_t number_data;
>> +     struct ec_response_motion_sensor_data data[0];
>> +} __packed;
>> +
>> +/* List supported activity recognition */
>> +enum motionsensor_activity {
>> +     MOTIONSENSE_ACTIVITY_RESERVED = 0,
>> +     MOTIONSENSE_ACTIVITY_SIG_MOTION = 1,
>> +     MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2,
>> +};
>> +
>> +struct ec_motion_sense_activity {
>> +     uint8_t sensor_num;
>> +     uint8_t activity; /* one of enum motionsensor_activity */
>> +     uint8_t enable;   /* 1: enable, 0: disable */
>> +     uint8_t reserved;
>> +     uint16_t parameters[3]; /* activity dependent parameters */
>>  };
>>
>>  /* Module flag masks used for the dump sub-command. */
>> @@ -1355,41 +1462,61 @@ enum motionsensor_chip {
>>  #define MOTIONSENSE_SENSOR_FLAG_PRESENT (1<<0)
>>
>>  /*
>> + * Flush entry for synchronisation.
>> + * data contains time stamp
>> + */
>> +#define MOTIONSENSE_SENSOR_FLAG_FLUSH (1<<0)
>> +#define MOTIONSENSE_SENSOR_FLAG_TIMESTAMP (1<<1)
>> +#define MOTIONSENSE_SENSOR_FLAG_WAKEUP (1<<2)
>> +
>> +/*
>>   * Send this value for the data element to only perform a read. If you
>>   * send any other value, the EC will interpret it as data to set and will
>>   * return the actual value set.
>>   */
>>  #define EC_MOTION_SENSE_NO_VALUE -1
>>
>> +#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000
>> +
>> +/* MOTIONSENSE_CMD_SENSOR_OFFSET subcommand flag */
>> +/* Set Calibration information */
>> +#define MOTION_SENSE_SET_OFFSET 1
>> +
>>  struct ec_params_motion_sense {
>>       uint8_t cmd;
>>       union {
>> -             /* Used for MOTIONSENSE_CMD_DUMP. */
>> +             /* Used for MOTIONSENSE_CMD_DUMP */
>>               struct {
>> -                     /* no args */
>> +                     /*
>> +                      * Maximal number of sensor the host is expecting.
>> +                      * 0 means the host is only interested in the number
>> +                      * of sensors controlled by the EC.
>> +                      */
>> +                     uint8_t max_sensor_count;
>>               } dump;
>>
>>               /*
>> -              * Used for MOTIONSENSE_CMD_EC_RATE and
>> -              * MOTIONSENSE_CMD_KB_WAKE_ANGLE.
>> +              * Used for MOTIONSENSE_CMD_KB_WAKE_ANGLE.
>>                */
>>               struct {
>> -                     /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
>> +                     /* Data to set or EC_MOTION_SENSE_NO_VALUE to read.
>> +                      * kb_wake_angle: angle to wakup AP.
>> +                      */
>>                       int16_t data;
>> -             } ec_rate, kb_wake_angle;
>> +             } kb_wake_angle;
>>
>> -             /* Used for MOTIONSENSE_CMD_INFO. */
>> +             /* Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA
>> +              * and MOTIONSENSE_CMD_PERFORM_CALIB.
>> +              */
>>               struct {
>> -                     /* Should be element of enum motionsensor_id. */
>>                       uint8_t sensor_num;
>> -             } info;
>> +             } info, data, fifo_flush, perform_calib, list_activities;
>>
>>               /*
>> -              * Used for MOTIONSENSE_CMD_SENSOR_ODR and
>> -              * MOTIONSENSE_CMD_SENSOR_RANGE.
>> +              * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR
>> +              * and MOTIONSENSE_CMD_SENSOR_RANGE.
>>                */
>>               struct {
>> -                     /* Should be element of enum motionsensor_id. */
>>                       uint8_t sensor_num;
>>
>>                       /* Rounding flag, true for round-up, false for down. */
>> @@ -1399,22 +1526,69 @@ struct ec_params_motion_sense {
>>
>>                       /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
>>                       int32_t data;
>> -             } sensor_odr, sensor_range;
>> +             } ec_rate, sensor_odr, sensor_range;
>> +
>> +             /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
>> +             struct {
>> +                     uint8_t sensor_num;
>> +
>> +                     /*
>> +                      * bit 0: If set (MOTION_SENSE_SET_OFFSET), set
>> +                      * the calibration information in the EC.
>> +                      * If unset, just retrieve calibration information.
>> +                      */
>> +                     uint16_t flags;
>> +
>> +                     /*
>> +                      * Temperature at calibration, in units of 0.01 C
>> +                      * 0x8000: invalid / unknown.
>> +                      * 0x0: 0C
>> +                      * 0x7fff: +327.67C
>> +                      */
>> +                     int16_t temp;
>> +
>> +                     /*
>> +                      * Offset for calibration.
>> +                      * Unit:
>> +                      * Accelerometer: 1/1024 g
>> +                      * Gyro:          1/1024 deg/s
>> +                      * Compass:       1/16 uT
>> +                      */
>> +                     int16_t offset[3];
>> +             } __packed sensor_offset;
>> +
>> +             /* Used for MOTIONSENSE_CMD_FIFO_INFO */
>> +             struct {
>> +             } fifo_info;
>> +
>> +             /* Used for MOTIONSENSE_CMD_FIFO_READ */
>> +             struct {
>> +                     /*
>> +                      * Number of expected vector to return.
>> +                      * EC may return less or 0 if none available.
>> +                      */
>> +                     uint32_t max_data_vector;
>> +             } fifo_read;
>> +
>> +             struct ec_motion_sense_activity set_activity;
>>       };
>>  } __packed;
>>
>>  struct ec_response_motion_sense {
>>       union {
>> -             /* Used for MOTIONSENSE_CMD_DUMP. */
>> +             /* Used for MOTIONSENSE_CMD_DUMP */
>>               struct {
>>                       /* Flags representing the motion sensor module. */
>>                       uint8_t module_flags;
>>
>> -                     /* Flags for each sensor in enum motionsensor_id. */
>> -                     uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT];
>> +                     /* Number of sensors managed directly by the EC */
>> +                     uint8_t sensor_count;
>>
>> -                     /* Array of all sensor data. Each sensor is 3-axis. */
>> -                     int16_t data[3*EC_MOTION_SENSOR_COUNT];
>> +                     /*
>> +                      * sensor data is truncated if response_max is too small
>> +                      * for holding all the data.
>> +                      */
>> +                     struct ec_response_motion_sensor_data sensor[0];
>>               } dump;
>>
>>               /* Used for MOTIONSENSE_CMD_INFO. */
>> @@ -1429,6 +1603,9 @@ struct ec_response_motion_sense {
>>                       uint8_t chip;
>>               } info;
>>
>> +             /* Used for MOTIONSENSE_CMD_DATA */
>> +             struct ec_response_motion_sensor_data data;
>> +
>>               /*
>>                * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR,
>>                * MOTIONSENSE_CMD_SENSOR_RANGE, and
>> @@ -1438,6 +1615,25 @@ struct ec_response_motion_sense {
>>                       /* Current value of the parameter queried. */
>>                       int32_t ret;
>>               } ec_rate, sensor_odr, sensor_range, kb_wake_angle;
>> +
>> +             /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
>> +             struct {
>> +                     int16_t temp;
>> +                     int16_t offset[3];
>> +             } sensor_offset, perform_calib;
>> +
>> +             struct ec_response_motion_sense_fifo_info fifo_info, fifo_flush;
>> +
>> +             struct ec_response_motion_sense_fifo_data fifo_read;
>> +
>> +             struct {
>> +                     uint16_t reserved;
>> +                     uint32_t enabled;
>> +                     uint32_t disabled;
>> +             } __packed list_activities;
>> +
>> +             struct {
>> +             } set_activity;
>>       };
>>  } __packed;
>>
>> @@ -1819,6 +2015,12 @@ union ec_response_get_next_data {
>>
>>       /* Unaligned */
>>       uint32_t  host_event;
>> +
>> +     struct {
>> +             /* For aligning the fifo_info */
>> +             uint8_t rsvd[3];
>> +             struct ec_response_motion_sense_fifo_info info;
>> +     }        sensor_fifo;
>>  } __packed;
>>
>>  struct ec_response_get_next_event {
>>
>

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

* Re: [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors.
  2016-07-18 15:51     ` Jonathan Cameron
@ 2016-07-20  7:57       ` Enric Balletbo Serra
  0 siblings, 0 replies; 25+ messages in thread
From: Enric Balletbo Serra @ 2016-07-20  7:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Peter Meerwald-Stadler, Enric Balletbo i Serra, linux-kernel,
	linux-iio, Olof Johansson, Lee Jones, Hartmut Knaack,
	Lars-Peter Clausen, Guenter Roeck, Gwendal Grignou

Hi Peter and Jonathan,

Thanks for the review, I'll add the changes in v2, some answers below ...

2016-07-18 17:51 GMT+02:00 Jonathan Cameron <jic23@kernel.org>:
> On 18/07/16 08:29, Peter Meerwald-Stadler wrote:
>>
>>> Add the core functions to be able to support the sensors attached behind
>>> the ChromeOS Embedded Controller and used by other IIO cros-ec sensor
>>> drivers.
>>
>> comments below from a quick read
>> there is plenty on undocumented private API
> Few more comments from me. Peter is of course quite correct, its the
> new, undocumented ABI, which is the biggest issue.
>
> Interesting looking device.  Any docs out there?
>

No afaik, maybe the chromium guys can add something here?

> Jonathan
>>
>>> The cros_ec_sensor_core driver matches with current driver in ChromeOS
>>> 4.4 tree, so it includes all the fixes at the moment. The support for
>>> this driver was made by Gwendal Grignou. The original patch and all the
>>> fixes has been squashed and rebased on top of mainline.
>>>
>>> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
>>> Signed-off-by: Guenter Roeck <groeck@chromium.org>
>>> [eballetbo: split, squash and rebase on top of mainline the patches
>>> found in ChromeOS tree]
>>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>>> ---
>>>  drivers/iio/common/Kconfig                         |   1 +
>>>  drivers/iio/common/Makefile                        |   1 +
>>>  drivers/iio/common/cros_ec_sensors/Kconfig         |  14 +
>>>  drivers/iio/common/cros_ec_sensors/Makefile        |   5 +
>>>  .../common/cros_ec_sensors/cros_ec_sensors_core.c  | 564 +++++++++++++++++++++
>>>  .../common/cros_ec_sensors/cros_ec_sensors_core.h  | 155 ++++++
>>>  6 files changed, 740 insertions(+)
>>>  create mode 100644 drivers/iio/common/cros_ec_sensors/Kconfig
>>>  create mode 100644 drivers/iio/common/cros_ec_sensors/Makefile
>>>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>>>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
>>>
>>> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
>>> index 26a6026..e108996 100644
>>> --- a/drivers/iio/common/Kconfig
>>> +++ b/drivers/iio/common/Kconfig
>>> @@ -2,6 +2,7 @@
>>>  # IIO common modules
>>>  #
>>>
>>> +source "drivers/iio/common/cros_ec_sensors/Kconfig"
>>>  source "drivers/iio/common/hid-sensors/Kconfig"
>>>  source "drivers/iio/common/ms_sensors/Kconfig"
>>>  source "drivers/iio/common/ssp_sensors/Kconfig"
>>> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
>>> index 585da6a..6fa760e 100644
>>> --- a/drivers/iio/common/Makefile
>>> +++ b/drivers/iio/common/Makefile
>>> @@ -7,6 +7,7 @@
>>>  #
>>>
>>>  # When adding new entries keep the list in alphabetical order
>>> +obj-y += cros_ec_sensors/
>>>  obj-y += hid-sensors/
>>>  obj-y += ms_sensors/
>>>  obj-y += ssp_sensors/
>>> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
>>> new file mode 100644
>>> index 0000000..a30f41e
>>> --- /dev/null
>>> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
>>> @@ -0,0 +1,14 @@
>>> +#
>>> +# Chrome OS Embedded Controller managed sensors library
>>> +#
>>> +config IIO_CROS_EC_SENSORS_CORE
>>> +    tristate "ChromeOS EC Sensors Core"
>>> +    depends on SYSFS && MFD_CROS_EC
>>> +    select IIO_BUFFER
>>> +    select IIO_TRIGGERED_BUFFER
>>> +    help
>>> +      Base module for the ChromeOS EC Sensors module.
>>> +      Contains core functions used by other IIO CrosEC sensor
>>> +      driver.
>>
>> 'drivers' probably
>>
>>> +      Define common attributes and sysfs interrupt handler.
>>> +
>>> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
>>> new file mode 100644
>>> index 0000000..95b6901
>>> --- /dev/null
>>> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
>>> @@ -0,0 +1,5 @@
>>> +#
>>> +# Makefile for sensors seen through the ChromeOS EC sensor hub.
>>> +#
>>> +
>>> +obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
>>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>>> new file mode 100644
>>> index 0000000..cb3de8f
>>> --- /dev/null
>>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
>>> @@ -0,0 +1,564 @@
>>> +/*
>>> + * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
>>> + *
>>> + * Copyright (C) 2015 Google, Inc
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * This driver uses the cros-ec interface to communicate with the Chrome OS
>>> + * EC about accelerometer data. Accelerometer access is presented through
>>> + * iio sysfs.
>>> + */
>>> +
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/iio/buffer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/kfifo_buf.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/mfd/cros_ec.h>
>>> +#include <linux/mfd/cros_ec_commands.h>
>>> +#include <linux/module.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/sysfs.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +#include "cros_ec_sensors_core.h"
>>> +
>>> +static char *cros_ec_loc[] = {
>>> +    [MOTIONSENSE_LOC_BASE] = "base",
>>> +    [MOTIONSENSE_LOC_LID] = "lid",
>>> +    [MOTIONSENSE_LOC_MAX] = "unknown",
>>> +};
>>> +
>>> +/*
>>> + * cros_ec_sensors_core_init
>>> + *
>>> + * Initialize core sensor strucure, fill the response are
>>> + * with the return of Sensor info.
>>
>> rephrase please
>>
>>> + *
>>> + * @pdev plarform device created for the sensors
>>
>> platform
>>
>>> + * @indio_dev iio device structure of the device
>>> + * @physical_device True if the device refers to a physical device.
>>> + */
>>> +int cros_ec_sensors_core_init(struct platform_device *pdev,
>>> +                          struct iio_dev *indio_dev,
>>> +                          bool physical_device)
>>> +{
>>> +    struct device *dev = &pdev->dev;
>>> +    struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
>>> +    struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
>>> +    struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
>>> +
>>> +    platform_set_drvdata(pdev, indio_dev);
>>> +
>>> +    state->ec = ec->ec_dev;
>>> +    state->msg = devm_kzalloc(&pdev->dev,
>>> +                            max((u16)sizeof(struct ec_params_motion_sense),
>>> +                            state->ec->max_response), GFP_KERNEL);
>>> +    if (!state->msg)
>>> +            return -ENOMEM;
>>> +
>>> +    state->resp = (struct ec_response_motion_sense *)state->msg->data;
>>> +
>>> +    mutex_init(&state->cmd_lock);
>>> +    /* Set up the host command structure. */
>>> +    state->msg->version = 2;
>>> +    state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
>>> +    state->msg->outsize = sizeof(struct ec_params_motion_sense);
>>> +
>>> +    indio_dev->dev.parent = &pdev->dev;
>>> +    indio_dev->name = pdev->name;
>>> +
>>> +    if (physical_device) {
>>> +            indio_dev->modes = INDIO_DIRECT_MODE;
>>> +
>>> +            state->param.cmd = MOTIONSENSE_CMD_INFO;
>>> +            state->param.info.sensor_num = sensor_platform->sensor_num;
>>> +            if (cros_ec_motion_send_host_cmd(state, 0)) {
>>> +                    dev_warn(dev, "Can not access sensor info\n");
>>> +                    return -EIO;
>>> +            }
>>> +            state->type = state->resp->info.type;
>>> +            state->loc = state->resp->info.location;
>>> +    }
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
>>> +
>>> +/*
>>> + * cros_ec_motion_send_host_cmd - send motion sense host command
>>> + *
>>> + * @st Pointer to state information for device.
>>
>> @state
>> opt_length is not documented
>>
>>> + * @return 0 if ok, -ve on error.
>>
>> what is -ve?
>>

I would mean negative number, I'll replace the -ve for a more
appropiate explanation :)


>>> + *
>>> + * Note, when called, the sub-command is assumed to be set in param->cmd.
>>> + */
>>> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
>>> +                             u16 opt_length)
>>> +{
>>> +    int ret;
>>> +
>>> +    if (opt_length)
>>> +            state->msg->insize = min(opt_length, state->ec->max_response);
>>> +    else
>>> +            state->msg->insize = state->ec->max_response;
>>> +
>>> +    memcpy(state->msg->data, &state->param, sizeof(state->param));
>>> +    /* Send host command. */
>>
>> comment needed?
>>

Probably not, I'll remove it

>>> +    ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
>>> +    if (ret < 0)
>>> +            return -EIO;
>>> +
>>> +    if (ret &&
>>> +        state->resp != (struct ec_response_motion_sense *)state->msg->data)
>>> +            memcpy(state->resp, state->msg->data, ret);
>>> +
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
>>> +
>>> +static ssize_t __maybe_unused cros_ec_sensors_flush(struct iio_dev *indio_dev,
>>> +            uintptr_t private, const struct iio_chan_spec *chan,
>>> +            const char *buf, size_t len)
>>> +{
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +    int ret = 0;
>>
>> initialization not needed
>>
>>> +    bool flush;
>>> +
>>> +    ret = strtobool(buf, &flush);
>>> +    if (ret < 0)
>>> +            return ret;
>>> +    if (!flush)
>>> +            return -EINVAL;
>>> +
>>> +    mutex_lock(&st->cmd_lock);
>>> +    st->param.cmd = MOTIONSENSE_CMD_FIFO_FLUSH;
>>> +    ret = cros_ec_motion_send_host_cmd(st, 0);
>>> +    if (ret != 0)
>>> +            dev_warn(&indio_dev->dev, "Unable to flush sensor\n");
>>> +    mutex_unlock(&st->cmd_lock);
> blank line here (nitpick of the day)
>>> +    return ret ? ret : len;
>>> +}
>>> +
>>> +static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
>>> +            uintptr_t private, const struct iio_chan_spec *chan,
>>> +            const char *buf, size_t len)
>>> +{
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +    int ret, i;
>>> +    bool calibrate;
>>> +
>>> +    ret = strtobool(buf, &calibrate);
>>> +    if (ret < 0)
>>> +            return ret;
>>> +    if (!calibrate)
>>> +            return -EINVAL;
>>> +
>>> +    mutex_lock(&st->cmd_lock);
>>> +    st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
>>> +    ret = cros_ec_motion_send_host_cmd(st, 0);
>>> +    if (ret != 0) {
>>> +            dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
>>> +    } else {
>>> +            /* Save values */
>>> +            for (i = X; i < MAX_AXIS; i++)
>>
>> what is X here?
>>

I'll change for the proper define ...


>>> +                    st->calib[i].offset = st->resp->perform_calib.offset[i];
>>> +    }
>>> +    mutex_unlock(&st->cmd_lock);
>>> +    return ret ? ret : len;
>>> +}
>>> +
>>> +static ssize_t cros_ec_sensors_id(struct iio_dev *indio_dev,
>>> +            uintptr_t private, const struct iio_chan_spec *chan,
>>> +            char *buf)
>>> +{
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +
>>> +    return sprintf(buf, "%d\n", st->param.info.sensor_num);
>>
>> can't we be a bit more careful with buffer sizes here (and below)?
>>

I'm not sure I get what you mean here.

>>> +}
>>> +
>>> +static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
>>> +            uintptr_t private, const struct iio_chan_spec *chan,
>>> +            char *buf)
>>> +{
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +
>>> +    return sprintf(buf, "%s\n", cros_ec_loc[st->loc]);
>>> +}
>>> +
>>> +const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
>>> +#ifdef CONFIG_IIO_CROS_EC_SENSORS_RING
>>> +    {
>>> +            .name = "flush",
>>> +            .shared = IIO_SHARED_BY_ALL,
>>> +            .write = cros_ec_sensors_flush
>>> +    },
>>> +#endif
>>> +    {
>>> +            .name = "calibrate",
>>> +            .shared = IIO_SHARED_BY_ALL,
>>> +            .write = cros_ec_sensors_calibrate
>>> +    },
>>> +    {
>>> +            .name = "id",
>>> +            .shared = IIO_SHARED_BY_ALL,
>>> +            .read = cros_ec_sensors_id
>>> +    },
>>> +    {
>>> +            .name = "location",
>>> +            .shared = IIO_SHARED_BY_ALL,
>>> +            .read = cros_ec_sensors_loc
>>> +    },
>>> +    { },
>>> +};
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
>>> +
>>> +const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[] = {
>>> +    {
>>> +            .name = "id",
> These all need documentation..  Any ABI needs an entry in
> Documentation/ABI/testing/sysfs-bus-iio-* before any real discussion can
> start.
>

Ok, I'll include the documentation in order to start the discussion in v2.

>>> +            .shared = IIO_SHARED_BY_ALL,
>>> +            .read = cros_ec_sensors_id
>>> +    },
>>> +    {
>>> +            .name = "location",
>>> +            .shared = IIO_SHARED_BY_ALL,
>>> +            .read = cros_ec_sensors_loc
>>> +    },
>>> +    { },
>>> +};
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_limited_info);
>>> +/*
>>> + * idx_to_reg - convert sensor index into offset in shared memory region.
>>> + *
>>> + * @st: private data
>>> + * @idx: sensor index (should be element of enum sensor_index)
>>> + * @return address to read at.
>>> + */
>>> +static unsigned int idx_to_reg(struct cros_ec_sensors_core_state *st,
>>
>> cros_ec_ prefix please (here and below)
>>
>>> +                           unsigned int idx)
>>> +{
>>> +    /*
>>> +     * When using LPC interface, only space for 2 Accel and one Gyro.
>>> +     * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
>>> +     */
>>> +    if (st->type == MOTIONSENSE_TYPE_ACCEL)
>>> +            return EC_MEMMAP_ACC_DATA + sizeof(u16) *
>>> +                    (1 + idx + st->param.info.sensor_num *
>>> +                     MAX_AXIS);
>>> +    else
>>> +            return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
>>> +}
>>> +
>>> +static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
>>> +                      u8 *dest)
>>> +{
>>> +    return ec->cmd_readmem(ec, offset, 1, dest);
>>> +}
>>> +
>>> +static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
>>> +                       u16 *dest)
>>> +{
>>> +    u16 tmp;
> __le16 tmp;
>>> +    int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
>>> +
>>> +    *dest = le16_to_cpu(tmp);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/*
>>> + * read_ec_until_not_busy - read from EC status byte until it reads not busy.
>>> + *
>>> + * @st Pointer to state information for device.
>>> + * @return 8-bit status if ok, -ve on error
>>> + */
>>> +static int read_ec_until_not_busy(struct cros_ec_sensors_core_state *st)
>>> +{
>>> +    struct cros_ec_device *ec = st->ec;
>>> +    u8 status;
>>> +    int attempts = 0;
>>> +
>>> +    ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
>>
>> check return value (here and below)?
>>

Ok, I will do.

>>> +    while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
>>> +            /* Give up after enough attempts, return error. */
>>> +            if (attempts++ >= 50)
>>> +                    return -EIO;
>>> +
>>> +            /* Small delay every so often. */
>>> +            if (attempts % 5 == 0)
>>> +                    msleep(25);
>>> +
>>> +            ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
>>> +    }
>>> +
>>> +    return status;
>>> +}
>>> +
>>> +/*
>>> + * read_ec_sensors_data_unsafe - read acceleration data from EC shared memory.
>>> + *
>>> + * @st Pointer to state information for device.
>>
>> @indio_dev? here and below
>>

Yep, I tried to fix all the documentation style in v2.

>>> + * @scan_mask Bitmap of the sensor indices to scan.
>>> + * @data Location to store data.
>>> + *
>>> + * Note this is the unsafe function for reading the EC data. It does not
>>> + * guarantee that the EC will not modify the data as it is being read in.
>>> + */
>>> +static void read_ec_sensors_data_unsafe(struct iio_dev *indio_dev,
>>> +                     unsigned long scan_mask, s16 *data)
>>> +{
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +    struct cros_ec_device *ec = st->ec;
>>> +    unsigned int i = 0;
>>
>> init not needed
>>
>>> +
>>> +    /*
>>> +     * Read all sensors enabled in scan_mask. Each value is 2
>>> +     * bytes.
>>> +     */
>>> +    for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
>>> +            ec_cmd_read_u16(ec, idx_to_reg(st, i), data);
>>
>> no return value?
>>

Changed

>>> +            data++;
>>> +    }
> Hmm. So this one has random access unlike below. Interesting...
>>> +}
>>> +
>>> +/*
>>> + * cros_ec_sensors_read_lpc - read acceleration data from EC shared memory.
>>> + *
>>> + * @st Pointer to state information for device.
>>> + * @scan_mask Bitmap of the sensor indices to scan.
>>> + * @data Location to store data.
>>> + * @return 0 if ok, -ve on error
>>> + *
>>> + * Note: this is the safe function for reading the EC data. It guarantees
>>> + * that the data sampled was not modified by the EC while being read.
>>> + */
>>> +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
>>> +                         unsigned long scan_mask, s16 *data)
>>> +{
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +    struct cros_ec_device *ec = st->ec;
>>> +    u8 samp_id = 0xff, status = 0;
>>> +    int attempts = 0;
>>> +
>>> +    /*
>>> +     * Continually read all data from EC until the status byte after
>>> +     * all reads reflects that the EC is not busy and the sample id
>>> +     * matches the sample id from before all reads. This guarantees
>>> +     * that data read in was not modified by the EC while reading.
>>> +     */
> kind of papers over any overwrites that have occured though...
> Unfortunately there is no particularly good solution to this. Oh for a hardware
> fifo!
>
>>> +    while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
>>> +                      EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
>>> +            /* If we have tried to read too many times, return error. */
>>> +            if (attempts++ >= 5)
>>> +                    return -EIO;
>>> +
>>> +            /* Read status byte until EC is not busy. */
>>> +            status = read_ec_until_not_busy(st);
>>> +            if (status < 0)
>>> +                    return status;
> Any chance this can spin for ever? (I don't think so but worth checking).

No

>>> +
>>> +            /*
>>> +             * Store the current sample id so that we can compare to the
>>> +             * sample id after reading the data.
>>> +             */
>>> +            samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
>>> +
>>> +            /* Read all EC data, format it, and store it into data. */
>>> +            read_ec_sensors_data_unsafe(indio_dev, scan_mask, data);
>>> +
>>> +            /* Read status byte. */
>>> +            ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
>>> +
>>> +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
>>> +                         unsigned long scan_mask, s16 *data)
>>> +{
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +    int ret;
>>> +    unsigned int i = 0;
>>> +
>>> +    /*
>>> +     * read all sensor data through a command.
>>
>> start with uppercase as everywhere else
>>
>>> +     */
>>> +    st->param.cmd = MOTIONSENSE_CMD_DATA;
>>> +    ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
>>> +    if (ret != 0) {
>>> +            dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
>>> +            return ret;
>>> +    }
>>> +
>>> +    for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
>>> +            *data = st->resp->data.data[i];
>>> +            data++;
>>> +    }
> So the device spits out a whole record every time?  If so let the demux
> in the IIO core handle this step.   Chances are you'll end up with
> clients of this datastream down the line and demuxing everything twice
> + that implementation has a few little tricks up it's sleeve ;)
>
> Provide available_scan_masks and then just throw the whole scan at it every
> time.
>
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
>>> +
>>> +irqreturn_t cros_ec_sensors_capture(int irq, void *p)
>>> +{
>>> +    struct iio_poll_func *pf = p;
>>> +    struct iio_dev *indio_dev = pf->indio_dev;
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +
>>> +    mutex_lock(&st->cmd_lock);
>>> +    /* Clear capture data. */
>>> +    memset(st->samples, 0, indio_dev->scan_bytes);
>>> +
>>> +    /* Read data based on which channels are enabled in scan mask. */
>>> +    st->read_ec_sensors_data(indio_dev, *(indio_dev->active_scan_mask),
>>> +                       (s16 *)st->samples);
>>
>> no return value?
>>

Checking return value in v2.

>>> +
>>> +    /* Store the timestamp last 8 bytes of data. */
>>> +    if (indio_dev->scan_timestamp)
>>> +            *(s64 *)&st->samples[round_down(indio_dev->scan_bytes -
>>> +                                            sizeof(s64),
>>> +                                 sizeof(s64))] = iio_get_time_ns();
>>> +
>>> +    iio_push_to_buffers(indio_dev, st->samples);
>>
>> use iio_push_to_buffers_with_timestamp()
> but make sure you obey the slightly odd space requirements for the buffer
> passed into this function.
>>
>>> +
>>> +    /*
>>> +     * Tell the core we are done with this trigger and ready for the
>>> +     * next one.
>>> +     */
>>> +    iio_trigger_notify_done(indio_dev->trig);
>>> +    mutex_unlock(&st->cmd_lock);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
>>> +
>>> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>>> +                      struct iio_chan_spec const *chan,
>>> +                      int *val, int *val2, long mask)
>>> +{
>>> +    int ret = IIO_VAL_INT;
>>> +
>>> +    switch (mask) {
>>> +    case IIO_CHAN_INFO_SAMP_FREQ:
>>> +            st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>>> +            st->param.ec_rate.data =
>>> +                    EC_MOTION_SENSE_NO_VALUE;
>>> +
>>> +            if (cros_ec_motion_send_host_cmd(st, 0))
>>> +                    ret = -EIO;
>>> +            else
>>> +                    *val = st->resp->ec_rate.ret;
>>> +            break;
>>> +    case IIO_CHAN_INFO_FREQUENCY:
>>> +            st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
>>> +            st->param.sensor_odr.data =
>>> +                    EC_MOTION_SENSE_NO_VALUE;
>>> +
>>> +            if (cros_ec_motion_send_host_cmd(st, 0))
>>> +                    ret = -EIO;
>>> +            else
>>> +                    *val = st->resp->sensor_odr.ret;
>>> +            break;
>>> +    default:
>>> +            break;
>>> +    }
>>> +    return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
>>> +
>>> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>>> +                           struct iio_chan_spec const *chan,
>>> +                           int val, int val2, long mask)
>>> +{
>>> +    int ret = 0;
>>> +
>>> +    switch (mask) {
>>> +    case IIO_CHAN_INFO_FREQUENCY:
> This needs explanation... The frequency chan info element has so far only
> applied to output sensors where we are controlling a signal generator of
> some type.
>>> +            st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
>>> +            st->param.sensor_odr.data = val;
>>> +
>>> +            /* Always roundup, so caller gets at least what it asks for. */
>>> +            st->param.sensor_odr.roundup = 1;
>>> +
>>> +            if (cros_ec_motion_send_host_cmd(st, 0))
>>> +                    ret = -EIO;
>>> +            break;
>>> +    case IIO_CHAN_INFO_SAMP_FREQ:
>>> +            st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>>> +            st->param.ec_rate.data = val;
>>> +
>>> +            if (cros_ec_motion_send_host_cmd(st, 0))
>>> +                    ret = -EIO;
>>> +            else
>>> +                    st->curr_sampl_freq = val;
>>> +            break;
>>> +    default:
>>> +            ret = -EINVAL;
>>> +            break;
>>> +    }
>>> +    return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
>>> +
>>> +static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
>>> +{
>>> +    struct platform_device *pdev = to_platform_device(dev);
>>> +    struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +
>>> +    if (st->curr_sampl_freq == 0)
>>> +            return 0;
>>> +
>>> +    /*
>>> +     * If the sensors are sampled at high frequency, we will not be able to
>>> +     * sleep. Set to sampling to a long period if necessary.
>>> +     */
>>> +    if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
>>> +            mutex_lock(&st->cmd_lock);
>>> +            st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>>> +            st->param.ec_rate.data = CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY;
>>> +            cros_ec_motion_send_host_cmd(st, 0);
>>> +            mutex_unlock(&st->cmd_lock);
>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
>>> +{
>>> +    struct platform_device *pdev = to_platform_device(dev);
>>> +    struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>>> +    struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>>> +
>>> +    if (st->curr_sampl_freq == 0)
>>> +            return;
>>> +
>>> +    if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
>>> +            mutex_lock(&st->cmd_lock);
>>> +            st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>>> +            st->param.ec_rate.data = st->curr_sampl_freq;
>>> +            cros_ec_motion_send_host_cmd(st, 0);
>>> +            mutex_unlock(&st->cmd_lock);
>>> +    }
>>> +}
>>> +
>>> +#ifdef CONFIG_PM_SLEEP
>>> +const struct dev_pm_ops cros_ec_sensors_pm_ops = {
>>> +    .prepare = cros_ec_sensors_prepare,
>>> +    .complete = cros_ec_sensors_complete
>>> +};
> Hmm. You meet something new everyday.  I don't feel nearly as comfortable
> reviewing these as the callbacks we tend to get as first time I've encountered
> them...
>
>>> +#else
>>> +const struct dev_pm_ops cros_ec_sensors_pm_ops = { };
>>> +#endif
>>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
>>> +
>>> +MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
>>> new file mode 100644
>>> index 0000000..53e09e1
>>> --- /dev/null
>>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
>>> @@ -0,0 +1,155 @@
>>> +/*
>>> + * ChromeOS EC sensor hub
>>> + *
>>> + * Copyright (C) 2015 Google, Inc
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#ifndef __CROS_EC_SENSORS_CORE_H
>>> +#define __CROS_EC_SENSORS_CORE_H
>>> +
>>> +#include <linux/irqreturn.h>
>>> +
>>> +enum {
>>> +    X,
>>
>> such short #defines scare me :)
> Absolutely - prefix these to avoid likely future crashes..
>>
>>> +    Y,
>>> +    Z,
>>> +    MAX_AXIS,
>>> +};
>>> +
>>> +/*
>>> + * EC returns sensor values using signed 16 bit registers
>>> + */
>>> +#define CROS_EC_SENSOR_BITS 16
>>> +
>>> +/*
>>> + * 4 16 bit channels are allowed.
>>> + * Good enough for current sensors, thye use up to 3 16 bit vectors.
>>
>> they
>>
>>> + */
>>> +#define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
>>> +
>>> +/*
>>> + * minimum sampling period to use when device is suspending.
>>
>> Minimum (uppercase)
> Comment syntax as well :)
>>
>>> + */
>>> +#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
>>> +
>>> +/*
>>> + * Function to read the sensor data.
>>> + *
>>> + * Data can be retrieve using the cros ec command protocol.
>>> + * Some machines also allow accessing some sensor data via
>>> + * IO space.
>>> + */
>>> +typedef int (cros_ec_sensors_read_t)(struct iio_dev *indio_dev,
>>> +            unsigned long scan_mask, s16 *data);
>>> +
>>> +cros_ec_sensors_read_t cros_ec_sensors_read_lpc;
>>> +cros_ec_sensors_read_t cros_ec_sensors_read_cmd;
>>> +
>>> +/* State data for ec_sensors iio driver. */
>>> +struct cros_ec_sensors_core_state {
>>> +    struct cros_ec_device *ec;
>>> +    /*
>>> +     *  Location to store command and response to the EC.
>>> +     */
> Please fix comment style throughout.  If not I'll get a dozen fixes within
> the first week and it'll be merge pain for everyone.  Yeah, we are fussy
> about this, but it does make life less painful in the long run.
>>> +    struct mutex cmd_lock;
>>> +
>>> +    /*
>>> +     * Statically allocated command structure that holds parameters
>>> +     * and response.
>>> +     */
>>> +    struct cros_ec_command *msg;
>>> +    struct ec_params_motion_sense param;
>>> +    struct ec_response_motion_sense *resp;
>>> +
>>> +    /* Type of sensor */
>>> +    enum motionsensor_type type;
>>> +    enum motionsensor_location loc;
>>> +
>>> +    /*
>>> +     * Calibration parameters. Note that trigger captured data will always
>>> +     * provide the calibrated values.
>>> +     */
>>> +    struct calib_data {
>>> +            s16 offset;
>>> +    } calib[MAX_AXIS];
>>> +
>>> +    /*
>>> +     * Static array to hold data from a single capture. For each
>>> +     * channel we need 2 bytes, except for the timestamp. The timestamp
>>> +     * is always last and is always 8-byte aligned.
>>> +     */
>>> +    u8 samples[CROS_EC_SAMPLE_SIZE];
>>> +
>>> +    /* Pointer to function used for accessing sensors values. */
>>> +    cros_ec_sensors_read_t *read_ec_sensors_data;
>>> +
>>> +    /* Current sampling period */
>>> +    int curr_sampl_freq;
>>> +};
>>> +
>>> +/* Basic initialization of the core structure. */
>>> +int cros_ec_sensors_core_init(struct platform_device *pdev,
>>> +                          struct iio_dev *indio_dev,
>>> +                          bool physical_device);
>>> +
>>> +/*
>>> + * cros_ec_sensors_capture - the trigger handler function
>>> + *
>>> + * @irq: the interrupt number
>>> + * @p: private data - always a pointer to the poll func.
>>> + *
>>> + * On a trigger event occurring, if the pollfunc is attached then this
>>> + * handler is called as a threaded interrupt (and hence may sleep). It
>>> + * is responsible for grabbing data from the device and pushing it into
>>> + * the associated buffer.
>>> + */
>>> +irqreturn_t cros_ec_sensors_capture(int irq, void *p);
>>> +
>>> +
>>> +/*
>>> + * cros_ec_motion_send_host_cmd - send motion sense host command
>>> + *
>>> + * @st Pointer to state information for device.
>>> + * @opt_length: optional length: to reduce the response size,
>>> + *    useful on the data path.
>>> + *    Otherwise, the maximal allowed response size is used.
>>> + * @return 0 if ok, -ve on error.
>>> + *
>>> + * Note, when called, the sub-command is assumed to be set in param->cmd.
>>> + */
>>> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
>>> +                             u16 opt_length);
>>> +
>>> +/*
>>> + * cros_ec_sensors_core_read/write: handler for core attributes.
>>> + *
>>> + * Handler for attributes identical among sensors:
>>> + * - frequency,
>>> + * - sampling_frequency.
>>> + *
>>> + * cmd_lock lock must be held.
> Full kerneldoc preferred for descriptions of functions etc. Lets us
> do pretty printed manuals nice and easily ;)
>>> + */
>>> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>>> +                      struct iio_chan_spec const *chan,
>>> +                      int *val, int *val2, long mask);
>>> +
>>> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>>> +                           struct iio_chan_spec const *chan,
>>> +                           int val, int val2, long mask);
>>> +
>>> +extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
>>> +
>>> +/* List of extended channel specification for all sensors */
>>> +extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
>>> +extern const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[];
>>> +
>>> +#endif  /* __CROS_EC_SENSORS_CORE_H */
>>>
>>
>

Thanks, I'll do some rework on this series and send v2 asap.

Enric

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

* Re: [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands.
  2016-07-19 18:00     ` Enric Balletbo Serra
@ 2016-07-21  6:18       ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-21  6:18 UTC (permalink / raw)
  To: Enric Balletbo Serra
  Cc: Enric Balletbo i Serra, linux-kernel, linux-iio, Olof Johansson,
	Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On 19/07/16 19:00, Enric Balletbo Serra wrote:
> Hi Jonathan,
> 
> Many thanks for your comments.
> 
> 2016-07-18 15:49 GMT+02:00 Jonathan Cameron <jic23@kernel.org>:
>> On 18/07/16 08:02, Enric Balletbo i Serra wrote:
>>> Let's update the command header to include the definitions related to
>>> the sensors attached behind the ChromeOS Embedded Controller. The new
>>> commands and definitions allow us to get information from these sensors.
>>>
>>> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
>>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>> Again, I'd be happier seeing this stuff introduced as and when it
>> is needed rather than in a magic patch. It's hard to review stuff
>> if it's broken up across multiple patches like this.
>>
>> A few other bits and pieces inline.
>>
>> Jonathan
>>> ---
>>>  include/linux/mfd/cros_ec_commands.h | 260 +++++++++++++++++++++++++++++++----
>>>  1 file changed, 231 insertions(+), 29 deletions(-)
>>>
>>> diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
>>> index 76728ff..f26a806 100644
>>> --- a/include/linux/mfd/cros_ec_commands.h
>>> +++ b/include/linux/mfd/cros_ec_commands.h
>>> @@ -1290,7 +1290,13 @@ enum motionsense_command {
>>>
>>>       /*
>>>        * EC Rate command is a setter/getter command for the EC sampling rate
>>> -      * of all motion sensors in milliseconds.
>>> +      * in milliseconds.
>>> +      * It is per sensor, the EC run sample task  at the minimum of all
>>> +      * sensors EC_RATE.
>>> +      * For sensors without hardware FIFO, EC_RATE should be equals to 1/ODR
>>> +      * to collect all the sensor samples.
>>> +      * For sensor with hardware FIFO, EC_RATE is used as the maximal delay
>>> +      * to process of all motion sensors in milliseconds.
>>>        */
>>>       MOTIONSENSE_CMD_EC_RATE = 2,
>>>
>>> @@ -1315,37 +1321,138 @@ enum motionsense_command {
>>>        */
>>>       MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5,
>>>
>>> -     /* Number of motionsense sub-commands. */
>>> -     MOTIONSENSE_NUM_CMDS
>>> -};
>>> +     /*
>>> +      * Returns a single sensor data.
>>> +      */
>> Please use standard kernel documentation formats throughout.
>> If not you may face a Linus rant ;)
> 
> Seems that this specific file does not follow the kernel-doc format
> strictly [1], should I add the new entries in kernel-doc format or
> follow the file style?
> 
> Or maybe I should first do a patch to change all the file to the
> kernel-doc format?
It would be a nice tidy up to fix the file.  Good to get any
new comments in the right style though - fixing old ones can
happen any time.
> 
> Or you meant only replace  the
> 
> /*
>  * one line
>  */
> 
> for
> 
> /* one line */
Definitely do that one.
> 
> [1] https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt
> 
>>> +     MOTIONSENSE_CMD_DATA = 6,
>>> +
>>> +     /*
>>> +      * Return sensor fifo info.
>>> +      */
>>> +     MOTIONSENSE_CMD_FIFO_INFO = 7,
>>> +
>>> +     /*
>>> +      * Insert a flush element in the fifo and return sensor fifo info.
>>> +      * The host can use that element to synchronize its operation.
>>> +      */
>>> +     MOTIONSENSE_CMD_FIFO_FLUSH = 8,
>>>
>>> -enum motionsensor_id {
>>> -     EC_MOTION_SENSOR_ACCEL_BASE = 0,
>>> -     EC_MOTION_SENSOR_ACCEL_LID = 1,
>>> -     EC_MOTION_SENSOR_GYRO = 2,
>>> +     /*
>>> +      * Return a portion of the fifo.
>>> +      */
>>> +     MOTIONSENSE_CMD_FIFO_READ = 9,
>>> +
>>> +     /*
>>> +      * Perform low level calibration.
>>> +      * On sensors that support it, ask to do offset calibration.
>>> +      */
>>> +     MOTIONSENSE_CMD_PERFORM_CALIB = 10,
>>> +
>>> +     /*
>>> +      * Sensor Offset command is a setter/getter command for the offset
>>> +      * used for calibration.
>>> +      * The offsets can be calculated by the host, or via
>>> +      * PERFORM_CALIB command.
>>> +      */
>>> +     MOTIONSENSE_CMD_SENSOR_OFFSET = 11,
>>> +
>>> +     /*
>>> +      * List available activities for a MOTION sensor.
>>> +      * Indicates if they are enabled or disabled.
>>> +      */
>>> +     MOTIONSENSE_CMD_LIST_ACTIVITIES = 12,
>>>
>>>       /*
>>> -      * Note, if more sensors are added and this count changes, the padding
>>> -      * in ec_response_motion_sense dump command must be modified.
>>> +      * Activity management
>>> +      * Enable/Disable activity recognition.
>>>        */
>>> -     EC_MOTION_SENSOR_COUNT = 3
>>> +     MOTIONSENSE_CMD_SET_ACTIVITY = 13,
>>> +
>>> +     /* Number of motionsense sub-commands. */
>>> +     MOTIONSENSE_NUM_CMDS
>>>  };
>>>
>>>  /* List of motion sensor types. */
>>>  enum motionsensor_type {
>>>       MOTIONSENSE_TYPE_ACCEL = 0,
>>>       MOTIONSENSE_TYPE_GYRO = 1,
>>> +     MOTIONSENSE_TYPE_MAG = 2,
>>> +     MOTIONSENSE_TYPE_PROX = 3,
>>> +     MOTIONSENSE_TYPE_LIGHT = 4,
>>> +     MOTIONSENSE_TYPE_ACTIVITY = 5,
>>> +     MOTIONSENSE_TYPE_MAX,
>>>  };
>>>
>>>  /* List of motion sensor locations. */
>>>  enum motionsensor_location {
>>>       MOTIONSENSE_LOC_BASE = 0,
>>>       MOTIONSENSE_LOC_LID = 1,
>>> +     MOTIONSENSE_LOC_MAX,
>>>  };
>>>
>>>  /* List of motion sensor chips. */
>>>  enum motionsensor_chip {
>>>       MOTIONSENSE_CHIP_KXCJ9 = 0,
>>> +     MOTIONSENSE_CHIP_LSM6DS0 = 1,
>>> +     MOTIONSENSE_CHIP_BMI160 = 2,
>>> +     MOTIONSENSE_CHIP_SI1141 = 3,
>>> +     MOTIONSENSE_CHIP_SI1142 = 4,
>>> +     MOTIONSENSE_CHIP_SI1143 = 5,
>>> +     MOTIONSENSE_CHIP_KX022 = 6,
>>> +     MOTIONSENSE_CHIP_L3GD20H = 7,
>> Interesting.  So the driver needs some knowledge of what
>> is behind it.  I'll read on with interest ;)
>>> +};
>>> +
>>> +struct ec_response_motion_sensor_data {
>>> +     /* Flags for each sensor. */
>>> +     uint8_t flags;
>>> +     /* sensor number the data comes from */
>>> +     uint8_t sensor_num;
>>> +     /* Each sensor is up to 3-axis. */
>>> +     union {
>>> +             int16_t             data[3];
>>> +             struct {
>>> +                     uint16_t    rsvd;
>>> +                     uint32_t    timestamp;
>>> +             } __packed;
>>> +             struct {
>>> +                     uint8_t     activity; /* motionsensor_activity */
>>> +                     uint8_t     state;
>>> +                     int16_t     add_info[2];
>>> +             };
>>> +     };
>>> +} __packed;
>>> +
>>> +struct ec_response_motion_sense_fifo_info {
>>> +     /* Size of the fifo */
>>> +     uint16_t size;
>>> +     /* Amount of space used in the fifo */
>>> +     uint16_t count;
>>> +     /* TImestamp recorded in us */
>> Timestamp
>>> +     uint32_t timestamp;
>>> +     /* Total amount of vector lost */
>>> +     uint16_t total_lost;
>>> +     /* Lost events since the last fifo_info, per sensors */
>>> +     uint16_t lost[0];
>>> +} __packed;
>>> +
>>> +struct ec_response_motion_sense_fifo_data {
>>> +     uint32_t number_data;
>>> +     struct ec_response_motion_sensor_data data[0];
>>> +} __packed;
>>> +
>>> +/* List supported activity recognition */
>>> +enum motionsensor_activity {
>>> +     MOTIONSENSE_ACTIVITY_RESERVED = 0,
>>> +     MOTIONSENSE_ACTIVITY_SIG_MOTION = 1,
>>> +     MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2,
>>> +};
>>> +
>>> +struct ec_motion_sense_activity {
>>> +     uint8_t sensor_num;
>>> +     uint8_t activity; /* one of enum motionsensor_activity */
>>> +     uint8_t enable;   /* 1: enable, 0: disable */
>>> +     uint8_t reserved;
>>> +     uint16_t parameters[3]; /* activity dependent parameters */
>>>  };
>>>
>>>  /* Module flag masks used for the dump sub-command. */
>>> @@ -1355,41 +1462,61 @@ enum motionsensor_chip {
>>>  #define MOTIONSENSE_SENSOR_FLAG_PRESENT (1<<0)
>>>
>>>  /*
>>> + * Flush entry for synchronisation.
>>> + * data contains time stamp
>>> + */
>>> +#define MOTIONSENSE_SENSOR_FLAG_FLUSH (1<<0)
>>> +#define MOTIONSENSE_SENSOR_FLAG_TIMESTAMP (1<<1)
>>> +#define MOTIONSENSE_SENSOR_FLAG_WAKEUP (1<<2)
>>> +
>>> +/*
>>>   * Send this value for the data element to only perform a read. If you
>>>   * send any other value, the EC will interpret it as data to set and will
>>>   * return the actual value set.
>>>   */
>>>  #define EC_MOTION_SENSE_NO_VALUE -1
>>>
>>> +#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000
>>> +
>>> +/* MOTIONSENSE_CMD_SENSOR_OFFSET subcommand flag */
>>> +/* Set Calibration information */
>>> +#define MOTION_SENSE_SET_OFFSET 1
>>> +
>>>  struct ec_params_motion_sense {
>>>       uint8_t cmd;
>>>       union {
>>> -             /* Used for MOTIONSENSE_CMD_DUMP. */
>>> +             /* Used for MOTIONSENSE_CMD_DUMP */
>>>               struct {
>>> -                     /* no args */
>>> +                     /*
>>> +                      * Maximal number of sensor the host is expecting.
>>> +                      * 0 means the host is only interested in the number
>>> +                      * of sensors controlled by the EC.
>>> +                      */
>>> +                     uint8_t max_sensor_count;
>>>               } dump;
>>>
>>>               /*
>>> -              * Used for MOTIONSENSE_CMD_EC_RATE and
>>> -              * MOTIONSENSE_CMD_KB_WAKE_ANGLE.
>>> +              * Used for MOTIONSENSE_CMD_KB_WAKE_ANGLE.
>>>                */
>>>               struct {
>>> -                     /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
>>> +                     /* Data to set or EC_MOTION_SENSE_NO_VALUE to read.
>>> +                      * kb_wake_angle: angle to wakup AP.
>>> +                      */
>>>                       int16_t data;
>>> -             } ec_rate, kb_wake_angle;
>>> +             } kb_wake_angle;
>>>
>>> -             /* Used for MOTIONSENSE_CMD_INFO. */
>>> +             /* Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA
>>> +              * and MOTIONSENSE_CMD_PERFORM_CALIB.
>>> +              */
>>>               struct {
>>> -                     /* Should be element of enum motionsensor_id. */
>>>                       uint8_t sensor_num;
>>> -             } info;
>>> +             } info, data, fifo_flush, perform_calib, list_activities;
>>>
>>>               /*
>>> -              * Used for MOTIONSENSE_CMD_SENSOR_ODR and
>>> -              * MOTIONSENSE_CMD_SENSOR_RANGE.
>>> +              * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR
>>> +              * and MOTIONSENSE_CMD_SENSOR_RANGE.
>>>                */
>>>               struct {
>>> -                     /* Should be element of enum motionsensor_id. */
>>>                       uint8_t sensor_num;
>>>
>>>                       /* Rounding flag, true for round-up, false for down. */
>>> @@ -1399,22 +1526,69 @@ struct ec_params_motion_sense {
>>>
>>>                       /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */
>>>                       int32_t data;
>>> -             } sensor_odr, sensor_range;
>>> +             } ec_rate, sensor_odr, sensor_range;
>>> +
>>> +             /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
>>> +             struct {
>>> +                     uint8_t sensor_num;
>>> +
>>> +                     /*
>>> +                      * bit 0: If set (MOTION_SENSE_SET_OFFSET), set
>>> +                      * the calibration information in the EC.
>>> +                      * If unset, just retrieve calibration information.
>>> +                      */
>>> +                     uint16_t flags;
>>> +
>>> +                     /*
>>> +                      * Temperature at calibration, in units of 0.01 C
>>> +                      * 0x8000: invalid / unknown.
>>> +                      * 0x0: 0C
>>> +                      * 0x7fff: +327.67C
>>> +                      */
>>> +                     int16_t temp;
>>> +
>>> +                     /*
>>> +                      * Offset for calibration.
>>> +                      * Unit:
>>> +                      * Accelerometer: 1/1024 g
>>> +                      * Gyro:          1/1024 deg/s
>>> +                      * Compass:       1/16 uT
>>> +                      */
>>> +                     int16_t offset[3];
>>> +             } __packed sensor_offset;
>>> +
>>> +             /* Used for MOTIONSENSE_CMD_FIFO_INFO */
>>> +             struct {
>>> +             } fifo_info;
>>> +
>>> +             /* Used for MOTIONSENSE_CMD_FIFO_READ */
>>> +             struct {
>>> +                     /*
>>> +                      * Number of expected vector to return.
>>> +                      * EC may return less or 0 if none available.
>>> +                      */
>>> +                     uint32_t max_data_vector;
>>> +             } fifo_read;
>>> +
>>> +             struct ec_motion_sense_activity set_activity;
>>>       };
>>>  } __packed;
>>>
>>>  struct ec_response_motion_sense {
>>>       union {
>>> -             /* Used for MOTIONSENSE_CMD_DUMP. */
>>> +             /* Used for MOTIONSENSE_CMD_DUMP */
>>>               struct {
>>>                       /* Flags representing the motion sensor module. */
>>>                       uint8_t module_flags;
>>>
>>> -                     /* Flags for each sensor in enum motionsensor_id. */
>>> -                     uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT];
>>> +                     /* Number of sensors managed directly by the EC */
>>> +                     uint8_t sensor_count;
>>>
>>> -                     /* Array of all sensor data. Each sensor is 3-axis. */
>>> -                     int16_t data[3*EC_MOTION_SENSOR_COUNT];
>>> +                     /*
>>> +                      * sensor data is truncated if response_max is too small
>>> +                      * for holding all the data.
>>> +                      */
>>> +                     struct ec_response_motion_sensor_data sensor[0];
>>>               } dump;
>>>
>>>               /* Used for MOTIONSENSE_CMD_INFO. */
>>> @@ -1429,6 +1603,9 @@ struct ec_response_motion_sense {
>>>                       uint8_t chip;
>>>               } info;
>>>
>>> +             /* Used for MOTIONSENSE_CMD_DATA */
>>> +             struct ec_response_motion_sensor_data data;
>>> +
>>>               /*
>>>                * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR,
>>>                * MOTIONSENSE_CMD_SENSOR_RANGE, and
>>> @@ -1438,6 +1615,25 @@ struct ec_response_motion_sense {
>>>                       /* Current value of the parameter queried. */
>>>                       int32_t ret;
>>>               } ec_rate, sensor_odr, sensor_range, kb_wake_angle;
>>> +
>>> +             /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
>>> +             struct {
>>> +                     int16_t temp;
>>> +                     int16_t offset[3];
>>> +             } sensor_offset, perform_calib;
>>> +
>>> +             struct ec_response_motion_sense_fifo_info fifo_info, fifo_flush;
>>> +
>>> +             struct ec_response_motion_sense_fifo_data fifo_read;
>>> +
>>> +             struct {
>>> +                     uint16_t reserved;
>>> +                     uint32_t enabled;
>>> +                     uint32_t disabled;
>>> +             } __packed list_activities;
>>> +
>>> +             struct {
>>> +             } set_activity;
>>>       };
>>>  } __packed;
>>>
>>> @@ -1819,6 +2015,12 @@ union ec_response_get_next_data {
>>>
>>>       /* Unaligned */
>>>       uint32_t  host_event;
>>> +
>>> +     struct {
>>> +             /* For aligning the fifo_info */
>>> +             uint8_t rsvd[3];
>>> +             struct ec_response_motion_sense_fifo_info info;
>>> +     }        sensor_fifo;
>>>  } __packed;
>>>
>>>  struct ec_response_get_next_event {
>>>
>>
> --
> 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] 25+ messages in thread

* Re: [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring
  2016-07-18 16:27   ` Jonathan Cameron
@ 2016-07-26 23:29     ` Gwendal Grignou
  0 siblings, 0 replies; 25+ messages in thread
From: Gwendal Grignou @ 2016-07-26 23:29 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Enric Balletbo i Serra, Linux Kernel, linux-iio, Olof Johansson,
	Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On Mon, Jul 18, 2016 at 9:27 AM, Jonathan Cameron <jic23@kernel.org> wrote:
> On 18/07/16 08:02, Enric Balletbo i Serra wrote:
>> Add support for handling sensor events FIFO produced by the sensor
>> hub. A single device with a buffer will collect all samples produced
>> by the sensors managed by the CrosEC sensor hub.
> So you are defining a different device to support the buffer?
> That's 'unusual'...


>>
>> Signed-off-by: Guenter Roeck <groeck@chromium.org>
>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>> ---
>> +     CHANNEL_SENSOR_FLAG,
> Ah, I'm beginning to understand.
>
> I think you really need to demux these into the appropriate devices individual
> buffers... If these are coming in fairly randomly (guessing so?) then you'll
> want a top level mfd pushing data to each of the child devices (representing
> the different possible sensors).

Yes, indeed. I did not find a good solution to demux the hardware FIFO
into each sensor. As you said, I should have written a MFD driver
between the sensor device driver and the cros ec device driver.
But at the same time, that MFD driver, while not being an IIO driver,
would have to know the iio_dev of each sensor and call them with
iio_push_to_buffers(). Is it acceptable to call iio function from
outside the IIO subsystem?

>
> Simply pushing it into a buffer with metadata doesn't fit with the IIO ABI
> at all.
>
> Note that there is no obligation to have any triggers involved at all. Here I'd
> suggest you don't as it'll just make life difficult for little gain.
>

The way chrome OS works today is one process (chrome) is gathering
accelerometer sensor data on a time basis (using sysfs trigger), while
another "process" (android) is listening to all sensors events as they
come. I see that IIO has support for serveral buffer per iio_dev, but
I did not see this feature used. Do you suggest to creating virtual
sensors - not tied to physical hardware - to be able to set different
trigger, or calll iio_update_buffers() directly with new buffers?

>> +
>> +static s64 cros_ec_get_time_ns(void)
>> +{
>> +     struct timespec ts;
>> +
>> +     get_monotonic_boottime(&ts);
>> +     return timespec_to_ns(&ts);
> Use the core IIO timestamp helper? (now supports all the different clocks...)

>> +}
> See comments earlier.  There is no need to have metadata here that userspace
> will have to unwind, just demux the data appropriately and push to the
> correct iio devices (one per 'scan' - set of samples acquired at approximately
> the same time - every time)
>> +
>> +#define CROS_EC_RING_AXIS(_axis)             \
>> +{                                            \
>> +     .type = IIO_ACCEL,                      \
>> +     .modified = 1,                          \
>> +     .channel2 = IIO_MOD_##_axis,            \
>> +     .scan_index = CHANNEL_##_axis,          \
>> +     .scan_type = {                          \
>> +             .sign = 's',                    \
>> +             .realbits = 16,                 \
>> +             .storagebits = 16,              \
>> +     },                                      \
>> +     .extend_name = "ring",                  \
> For obvious reasons given earlier comments I don't like this!
I got your point. We will defer upstreaming this driver until we have
a better solution for sensors behind a muxed FIFO.

Gwendal.

>> +}

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

* Re: [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring
  2016-07-18  7:02 ` [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring Enric Balletbo i Serra
  2016-07-18 16:27   ` Jonathan Cameron
@ 2016-07-27  0:17   ` Guenter Roeck
  1 sibling, 0 replies; 25+ messages in thread
From: Guenter Roeck @ 2016-07-27  0:17 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: linux-kernel, linux-iio, Olof Johansson, Lee Jones,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Guenter Roeck, Gwendal Grignou

On Mon, Jul 18, 2016 at 12:02 AM, Enric Balletbo i Serra
<enric.balletbo@collabora.com> wrote:
> Add support for handling sensor events FIFO produced by the sensor
> hub. A single device with a buffer will collect all samples produced
> by the sensors managed by the CrosEC sensor hub.
>
> Signed-off-by: Guenter Roeck <groeck@chromium.org>

It would probably be better to have Gwendal's Signed-off here.
All I did was to port the code to chromeos-4.4

Guenter

> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> ---
>  drivers/iio/common/cros_ec_sensors/Kconfig         |   9 +
>  drivers/iio/common/cros_ec_sensors/Makefile        |   1 +
>  .../common/cros_ec_sensors/cros_ec_sensors_ring.c  | 541 +++++++++++++++++++++
>  3 files changed, 551 insertions(+)
>  create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
>
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> index 22b4211..778c3bf 100644
> --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -39,3 +39,12 @@ config IIO_CROS_EC_ACTIVITY
>           Activities can be simple (low/no motion) or more complex (riding train).
>           They are being reported by physical devices or the EC itself.
>           Creates an IIO device to manage all activities.
> +
> +config IIO_CROS_EC_SENSORS_RING
> +       tristate "ChromeOS EC Sensors Ring"
> +       depends on IIO_CROS_EC_SENSORS || IIO_CROS_EC_LIGHT_PROX
> +       help
> +         Add support for handling sensor events FIFO produced by
> +         the sensor hub.
> +         A single device with a buffer will collect all samples produced
> +         by the sensors managed by the CrosEC sensor hub
> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
> index 8f54f1e..0eb3fc5 100644
> --- a/drivers/iio/common/cros_ec_sensors/Makefile
> +++ b/drivers/iio/common/cros_ec_sensors/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
>  obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
>  obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
>  obj-$(CONFIG_IIO_CROS_EC_ACTIVITY) += cros_ec_activity.o
> +obj-$(CONFIG_IIO_CROS_EC_SENSORS_RING) += cros_ec_sensors_ring.o
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
> new file mode 100644
> index 0000000..1c74df9
> --- /dev/null
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_ring.c
> @@ -0,0 +1,541 @@
> +/*
> + * cros_ec_sensors_ring - Driver for Chrome OS EC Sensor hub FIFO.
> + *
> + * Copyright (C) 2015 Google, Inc
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * This driver uses the cros-ec interface to communicate with the Chrome OS
> + * EC about accelerometer data. Accelerometer access is presented through
> + * iio sysfs.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/mfd/cros_ec_commands.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/platform_device.h>
> +
> +#include "cros_ec_sensors_core.h"
> +
> +/* The ring is a FIFO that return sensor information from
> + * the single EC FIFO.
> + * There are always 5 channels returned:
> +* | ID | FLAG | X | Y | Z | Timestamp |
> + * ID is the EC sensor id
> + * FLAG is for meta data, only flush bit is defined.
> + */
> +#define CROS_EC_FLUSH_BIT 1
> +
> +enum {
> +       CHANNEL_SENSOR_ID,
> +       CHANNEL_SENSOR_FLAG,
> +       CHANNEL_X,
> +       CHANNEL_Y,
> +       CHANNEL_Z,
> +       CHANNEL_TIMESTAMP,
> +       MAX_CHANNEL,
> +};
> +
> +enum {
> +       LAST_TS,
> +       NEW_TS,
> +       ALL_TS
> +};
> +
> +#define CROS_EC_SENSOR_MAX 16
> +
> +struct cros_ec_fifo_info {
> +       struct ec_response_motion_sense_fifo_info info;
> +       uint16_t lost[CROS_EC_SENSOR_MAX];
> +};
> +
> +
> +struct cros_ec_sensors_ring_sample {
> +       uint8_t sensor_id;
> +       uint8_t flag;
> +       int16_t  vector[MAX_AXIS];
> +       s64      timestamp;
> +} __packed;
> +
> +/* State data for ec_sensors iio driver. */
> +struct cros_ec_sensors_ring_state {
> +       /* Shared by all sensors */
> +       struct cros_ec_sensors_core_state core;
> +
> +       /* Notifier to kick to the interrupt */
> +       struct notifier_block notifier;
> +
> +       /* Preprocessed ring to send to kfifo */
> +       struct cros_ec_sensors_ring_sample *ring;
> +
> +       struct iio_trigger *trig;
> +       s64    fifo_timestamp[ALL_TS];
> +       struct ec_response_motion_sense_fifo_info fifo_info;
> +};
> +
> +static const struct iio_info ec_sensors_info = {
> +       .driver_module = THIS_MODULE,
> +};
> +
> +static s64 cros_ec_get_time_ns(void)
> +{
> +       struct timespec ts;
> +
> +       get_monotonic_boottime(&ts);
> +       return timespec_to_ns(&ts);
> +}
> +
> +/*
> + * cros_ec_ring_process_event: process one EC FIFO event
> + *
> + * Process one EC event, add it in the ring if necessary.
> + *
> + * Return true if out event has been populated.
> + *
> + * fifo_info: fifo information from the EC.
> + * fifo_timestamp: timestamp at time of fifo_info collection.
> + * current_timestamp: estimated current timestamp.
> + * in: incoming FIFO event from EC
> + * out: outgoing event to user space.
> + */
> +bool cros_ec_ring_process_event(const struct cros_ec_fifo_info *fifo_info,
> +                               const s64 fifo_timestamp,
> +                               s64 *current_timestamp,
> +                               struct ec_response_motion_sensor_data *in,
> +                               struct cros_ec_sensors_ring_sample *out)
> +{
> +       int axis;
> +       s64 new_timestamp;
> +
> +       if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) {
> +               new_timestamp = fifo_timestamp -
> +                       ((s64)fifo_info->info.timestamp * 1000) +
> +                       ((s64)in->timestamp * 1000);
> +               /*
> +                * The timestamp can be stale if we had to use the fifo
> +                * info timestamp.
> +                */
> +               if (new_timestamp - *current_timestamp > 0)
> +                       *current_timestamp = new_timestamp;
> +       }
> +
> +       if (in->flags & MOTIONSENSE_SENSOR_FLAG_FLUSH) {
> +               out->sensor_id = in->sensor_num;
> +               out->timestamp = *current_timestamp;
> +               out->flag = CROS_EC_FLUSH_BIT;
> +               return true;
> +       }
> +       if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
> +               /* If we just have a timestamp, skip this entry. */
> +               return false;
> +
> +       /* Regular sample */
> +       out->sensor_id = in->sensor_num;
> +       out->timestamp = *current_timestamp;
> +       out->flag = 0;
> +       for (axis = X; axis < MAX_AXIS; axis++)
> +               out->vector[axis] = in->data[axis];
> +       return true;
> +}
> +/*
> + * cros_ec_ring_handler - the trigger handler function
> + *
> + * @irq: the interrupt number
> + * @p: private data - always a pointer to the poll func.
> + *
> + * On a trigger event occurring, if the pollfunc is attached then this
> + * handler is called as a threaded interrupt (and hence may sleep). It
> + * is responsible for grabbing data from the device and pushing it into
> + * the associated buffer.
> + */
> +static irqreturn_t cros_ec_ring_handler(int irq, void *p)
> +{
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       struct cros_ec_sensors_ring_state *state = iio_priv(indio_dev);
> +       struct cros_ec_fifo_info fifo_info;
> +       s64    fifo_timestamp, current_timestamp;
> +       int    i, j, number_data, ret;
> +       unsigned long sensor_mask = 0;
> +       struct ec_response_motion_sensor_data *in;
> +       struct cros_ec_sensors_ring_sample *out, *last_out;
> +
> +
> +       /* Get FIFO information */
> +       mutex_lock(&state->core.cmd_lock);
> +       memcpy(&fifo_info, &state->fifo_info, sizeof(state->fifo_info));
> +       fifo_timestamp = state->fifo_timestamp[NEW_TS];
> +       mutex_unlock(&state->core.cmd_lock);
> +
> +       /* Copy elements in the main fifo */
> +       if (fifo_info.info.total_lost) {
> +               /* Need to retrieve the number of lost vectors per sensor */
> +               state->core.param.cmd = MOTIONSENSE_CMD_FIFO_INFO;
> +               if (cros_ec_motion_send_host_cmd(&state->core, 0))
> +                       goto ring_handler_end;
> +               memcpy(&fifo_info, &state->core.resp->fifo_info,
> +                      sizeof(fifo_info));
> +               fifo_timestamp = cros_ec_get_time_ns();
> +       }
> +
> +       current_timestamp = state->fifo_timestamp[LAST_TS];
> +       out = state->ring;
> +       for (i = 0; i < fifo_info.info.count; i += number_data) {
> +               state->core.param.cmd = MOTIONSENSE_CMD_FIFO_READ;
> +               state->core.param.fifo_read.max_data_vector =
> +                       fifo_info.info.count - i;
> +               ret = cros_ec_motion_send_host_cmd(&state->core,
> +                              sizeof(state->core.resp->fifo_read) +
> +                              state->core.param.fifo_read.max_data_vector *
> +                              sizeof(struct ec_response_motion_sensor_data));
> +               if (ret != EC_RES_SUCCESS) {
> +                       dev_warn(&indio_dev->dev, "Fifo error: %d\n", ret);
> +                       break;
> +               }
> +               number_data =
> +                       state->core.resp->fifo_read.number_data;
> +               if (number_data == 0) {
> +                       dev_dbg(&indio_dev->dev, "Unexpected empty FIFO\n");
> +                       break;
> +               }
> +
> +               for (in = state->core.resp->fifo_read.data, j = 0;
> +                    j < number_data; j++, in++) {
> +                       BUG_ON(out >= state->ring + fifo_info.info.size);
> +                       if (cros_ec_ring_process_event(
> +                                       &fifo_info, fifo_timestamp,
> +                                       &current_timestamp, in, out)) {
> +                               sensor_mask |= (1 << in->sensor_num);
> +                               out++;
> +                       }
> +               }
> +       }
> +       last_out = out;
> +
> +       if (out == state->ring)
> +               /* Unexpected empty FIFO. */
> +               goto ring_handler_end;
> +
> +       /*
> +        * Check if current_timestamp is ahead of the last sample.
> +        * Normally, the EC appends a timestamp after the last sample, but if
> +        * the AP is slow to respond to the IRQ, the EC may have added new
> +        * samples. Use the FIFO info timestamp as last timestamp then.
> +        */
> +       if ((last_out-1)->timestamp == current_timestamp)
> +               current_timestamp = fifo_timestamp;
> +
> +       /* check if buffer is set properly */
> +       if (!indio_dev->active_scan_mask ||
> +           (bitmap_empty(indio_dev->active_scan_mask,
> +                         indio_dev->masklength)))
> +               goto ring_handler_end;
> +
> +       /*
> +        * calculate proper timestamps
> +        *
> +        * If there is a sample with a proper timestamp
> +        *                        timestamp | count
> +        * older_unprocess_out --> TS1      | 1
> +        *                         TS1      | 2
> +        * out -->                 TS1      | 3
> +        * next_out -->            TS2      |
> +        * We spread time for the samples [older_unprocess_out .. out]
> +        * between TS1 and TS2: [TS1+1/4, TS1+2/4, TS1+3/4, TS2].
> +        *
> +        * If we reach the end of the samples, we compare with the
> +        * current timestamp:
> +        *
> +        * older_unprocess_out --> TS1      | 1
> +        *                         TS1      | 2
> +        * out -->                 TS1      | 3
> +        * We know have [TS1+1/3, TS1+2/3, current timestamp]
> +        */
> +       for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
> +               s64 older_timestamp;
> +               s64 timestamp;
> +               struct cros_ec_sensors_ring_sample *older_unprocess_out =
> +                       state->ring;
> +               struct cros_ec_sensors_ring_sample *next_out;
> +               int count = 1;
> +
> +               if (fifo_info.info.total_lost) {
> +                       int lost = fifo_info.lost[i];
> +
> +                       if (lost)
> +                               dev_warn(&indio_dev->dev,
> +                                       "Sensor %d: lost: %d out of %d\n", i,
> +                                       lost, fifo_info.info.total_lost);
> +               }
> +
> +               for (out = state->ring; out < last_out; out = next_out) {
> +                       s64 time_period;
> +
> +                       next_out = out + 1;
> +                       if (out->sensor_id != i)
> +                               continue;
> +
> +                       /* Timestamp to start with */
> +                       older_timestamp = out->timestamp;
> +
> +                       /* find next sample */
> +                       while (next_out < last_out && next_out->sensor_id != i)
> +                               next_out++;
> +
> +                       if (next_out >= last_out) {
> +                               timestamp = current_timestamp;
> +                       } else {
> +                               timestamp = next_out->timestamp;
> +                               if (timestamp == older_timestamp) {
> +                                       count++;
> +                                       continue;
> +                               }
> +                       }
> +
> +                       /* The next sample has a new timestamp,
> +                        * spread the unprocessed samples
> +                        */
> +                       if (next_out < last_out)
> +                               count++;
> +                       time_period = div_s64(timestamp - older_timestamp,
> +                                             count);
> +
> +                       for (; older_unprocess_out <= out;
> +                                       older_unprocess_out++) {
> +                               if (older_unprocess_out->sensor_id != i)
> +                                       continue;
> +                               older_timestamp += time_period;
> +                               older_unprocess_out->timestamp =
> +                                       older_timestamp;
> +                       }
> +                       count = 1;
> +                       /* The next_out sample has a valid timestamp, skip. */
> +                       next_out++;
> +                       older_unprocess_out = next_out;
> +               }
> +       }
> +
> +       /* push the event into the kfifo */
> +       for (out = state->ring; out < last_out; out++)
> +               iio_push_to_buffers(indio_dev, (u8 *)out);
> +
> +ring_handler_end:
> +       state->fifo_timestamp[LAST_TS] = current_timestamp;
> +       iio_trigger_notify_done(indio_dev->trig);
> +       return IRQ_HANDLED;
> +}
> +
> +static int cros_ec_ring_event(struct notifier_block *nb,
> +       unsigned long queued_during_suspend, void *_notify)
> +{
> +       struct cros_ec_sensors_ring_state *state;
> +       struct cros_ec_device *ec;
> +
> +       state = container_of(nb, struct cros_ec_sensors_ring_state, notifier);
> +       ec = state->core.ec;
> +
> +       if (ec->event_data.event_type != EC_MKBP_EVENT_SENSOR_FIFO)
> +               return NOTIFY_DONE;
> +
> +       if (ec->event_size != sizeof(ec->event_data.data.sensor_fifo)) {
> +               dev_warn(ec->dev, "Invalid fifo info size\n");
> +               return NOTIFY_DONE;
> +       }
> +
> +       if (queued_during_suspend)
> +               return NOTIFY_OK;
> +
> +       mutex_lock(&state->core.cmd_lock);
> +       memcpy(&state->fifo_info, &ec->event_data.data.sensor_fifo.info,
> +                       ec->event_size);
> +       state->fifo_timestamp[NEW_TS] = cros_ec_get_time_ns();
> +       mutex_unlock(&state->core.cmd_lock);
> +
> +       /*
> +        * We are not in a low level interrupt,
> +        * we can not call iio_trigger_poll().
> +        */
> +       iio_trigger_poll_chained(state->trig);
> +       return NOTIFY_OK;
> +}
> +
> +#define CROS_EC_RING_ID(_id, _name)            \
> +{                                              \
> +       .type = IIO_ACCEL,                      \
> +       .modified = 1,                          \
> +       .channel2 = IIO_NO_MOD,                 \
> +       .scan_index = _id,                      \
> +       .scan_type = {                          \
> +               .sign = 'u',                    \
> +               .realbits = 8,                  \
> +               .storagebits = 8,               \
> +       },                                      \
> +       .extend_name = _name,                   \
> +}
> +
> +#define CROS_EC_RING_AXIS(_axis)               \
> +{                                              \
> +       .type = IIO_ACCEL,                      \
> +       .modified = 1,                          \
> +       .channel2 = IIO_MOD_##_axis,            \
> +       .scan_index = CHANNEL_##_axis,          \
> +       .scan_type = {                          \
> +               .sign = 's',                    \
> +               .realbits = 16,                 \
> +               .storagebits = 16,              \
> +       },                                      \
> +       .extend_name = "ring",                  \
> +}
> +
> +static const struct iio_chan_spec cros_ec_ring_channels[] = {
> +       CROS_EC_RING_ID(CHANNEL_SENSOR_ID, "id"),
> +       CROS_EC_RING_ID(CHANNEL_SENSOR_FLAG, "flag"),
> +       CROS_EC_RING_AXIS(X),
> +       CROS_EC_RING_AXIS(Y),
> +       CROS_EC_RING_AXIS(Z),
> +       IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_TIMESTAMP)
> +};
> +
> +static int cros_ec_ring_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> +       struct cros_ec_device *ec_device;
> +       struct iio_dev *indio_dev;
> +       struct cros_ec_sensors_ring_state *state;
> +       int ret;
> +
> +       if (!ec_dev || !ec_dev->ec_dev) {
> +               dev_warn(&pdev->dev, "No CROS EC device found.\n");
> +               return -EINVAL;
> +       }
> +       ec_device = ec_dev->ec_dev;
> +
> +       indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
> +       if (!indio_dev)
> +               return -ENOMEM;
> +
> +       platform_set_drvdata(pdev, indio_dev);
> +
> +       ret = cros_ec_sensors_core_init(pdev, indio_dev, false);
> +       if (ret)
> +               return ret;
> +
> +       state = iio_priv(indio_dev);
> +       /* Retrieve FIFO information */
> +       state->core.param.cmd = MOTIONSENSE_CMD_FIFO_INFO;
> +       /* If it fails, just assume the FIFO is not supported.
> +        * For other errors, the other sensor drivers would have noticed
> +        * already.
> +        */
> +       if (cros_ec_motion_send_host_cmd(&state->core, 0))
> +               return -ENODEV;
> +
> +       /* Allocate the full fifo.
> +        * We need to copy the whole FIFO to set timestamps properly *
> +        */
> +       state->ring = devm_kcalloc(&pdev->dev,
> +                       state->core.resp->fifo_info.size,
> +                       sizeof(*state->ring), GFP_KERNEL);
> +       if (!state->ring)
> +               return -ENOMEM;
> +
> +       state->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
> +
> +       indio_dev->channels = cros_ec_ring_channels;
> +       indio_dev->num_channels = ARRAY_SIZE(cros_ec_ring_channels);
> +
> +       indio_dev->info = &ec_sensors_info;
> +
> +       state->trig = devm_iio_trigger_alloc(&pdev->dev,
> +                       "%s-trigger%d", indio_dev->name, indio_dev->id);
> +       if (!state->trig)
> +               return -ENOMEM;
> +       state->trig->dev.parent = &pdev->dev;
> +       iio_trigger_set_drvdata(state->trig, indio_dev);
> +
> +       ret = iio_trigger_register(state->trig);
> +       if (ret)
> +               return ret;
> +
> +       ret = iio_triggered_buffer_setup(indio_dev, NULL,
> +                                        cros_ec_ring_handler, NULL);
> +       if (ret < 0)
> +               goto err_trigger_unregister;
> +
> +       ret = iio_device_register(indio_dev);
> +       if (ret < 0)
> +               goto err_buffer_cleanup;
> +
> +       /* register the notifier that will act as a top half interrupt. */
> +       state->notifier.notifier_call = cros_ec_ring_event;
> +       ret = blocking_notifier_chain_register(&ec_device->event_notifier,
> +                                              &state->notifier);
> +       if (ret < 0) {
> +               dev_warn(&indio_dev->dev, "failed to register notifier\n");
> +               goto err_device_unregister;
> +       }
> +       return ret;
> +
> +err_device_unregister:
> +       iio_device_unregister(indio_dev);
> +err_buffer_cleanup:
> +       iio_triggered_buffer_cleanup(indio_dev);
> +err_trigger_unregister:
> +       iio_trigger_unregister(state->trig);
> +       return ret;
> +}
> +
> +static int cros_ec_ring_remove(struct platform_device *pdev)
> +{
> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +       struct cros_ec_sensors_ring_state *state = iio_priv(indio_dev);
> +       struct cros_ec_device *ec = state->core.ec;
> +
> +       blocking_notifier_chain_unregister(&ec->event_notifier,
> +                                          &state->notifier);
> +       iio_device_unregister(indio_dev);
> +       iio_triggered_buffer_cleanup(indio_dev);
> +       iio_trigger_unregister(state->trig);
> +       return 0;
> +}
> +
> +static const struct platform_device_id cros_ec_ring_ids[] = {
> +       {
> +               .name = "cros-ec-ring",
> +       },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(platform, cros_ec_ring_ids);
> +
> +static struct platform_driver cros_ec_ring_platform_driver = {
> +       .driver = {
> +               .name   = "cros-ec-ring",
> +               .owner  = THIS_MODULE,
> +       },
> +       .probe          = cros_ec_ring_probe,
> +       .remove         = cros_ec_ring_remove,
> +       .id_table       = cros_ec_ring_ids,
> +};
> +module_platform_driver(cros_ec_ring_platform_driver);
> +
> +MODULE_DESCRIPTION("ChromeOS EC sensor hub ring driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0
>

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

end of thread, other threads:[~2016-07-27  0:17 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-18  7:02 [PATCH 00/10] Add support for cros-ec-sensors Enric Balletbo i Serra
2016-07-18  7:02 ` [PATCH 01/10] mfd: cros_ec: add ChromeOS EC sensor platform information Enric Balletbo i Serra
2016-07-18 13:43   ` Jonathan Cameron
2016-07-18  7:02 ` [PATCH 02/10] mfd: cros_ec: update MOTIONSENSE definitions and commands Enric Balletbo i Serra
2016-07-18 13:49   ` Jonathan Cameron
2016-07-19 18:00     ` Enric Balletbo Serra
2016-07-21  6:18       ` Jonathan Cameron
2016-07-18  7:02 ` [PATCH 03/10] iio: core: Add double tap as possible gesture Enric Balletbo i Serra
2016-07-18 13:54   ` Jonathan Cameron
2016-07-18  7:02 ` [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors Enric Balletbo i Serra
2016-07-18  7:29   ` Peter Meerwald-Stadler
2016-07-18 15:51     ` Jonathan Cameron
2016-07-20  7:57       ` Enric Balletbo Serra
2016-07-18  7:02 ` [PATCH 05/10] iio: cros_ec_sensors: add ChromeOS EC Contiguous Sensors driver Enric Balletbo i Serra
2016-07-18 16:09   ` Jonathan Cameron
2016-07-18  7:02 ` [PATCH 06/10] iio: cros_ec_light_prox: add ChromeOS EC Light and Proximity Sensors Enric Balletbo i Serra
2016-07-18 16:11   ` Jonathan Cameron
2016-07-18  7:02 ` [PATCH 07/10] iio: cros_ec_activity: add ChromeOS EC Activity Sensors Enric Balletbo i Serra
2016-07-18 16:16   ` Jonathan Cameron
2016-07-18  7:02 ` [PATCH 08/10] iio: cros_ec_sensors_ring: add ChromeOS EC Sensors Ring Enric Balletbo i Serra
2016-07-18 16:27   ` Jonathan Cameron
2016-07-26 23:29     ` Gwendal Grignou
2016-07-27  0:17   ` Guenter Roeck
2016-07-18  7:02 ` [PATCH 09/10] platform/chrome: Introduce a new function to check EC features Enric Balletbo i Serra
2016-07-18  7:02 ` [PATCH 10/10] platform/chrome: cros_ec_dev - Register cros-ec sensors Enric Balletbo i Serra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).