Linux-IIO Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing
@ 2019-09-22 17:50 Gwendal Grignou
  2019-09-22 17:50 ` [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
                   ` (13 more replies)
  0 siblings, 14 replies; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

This patchset adds a sensorhub driver for spreading sensor
events coming from the Embedded controller sensor FIFO:

       +---------------+ +--------------+ +----
       | cros_ec_accel | | cros_ec_gyro | | ...
       +---------------+ +--------------+ +----
           id:0       \        id:1 |       / id:..
                 +------------------------------+
                 |       cros_ec_sensorhub      |
                 +------------------------------+
                 |           cros_ec_dev        |
                 +------------------------------+
                 | cros_ec_i2c, cros_ec_lpc, .. |
                 +------------------------------+
                                 |
                                 EC

When new sensors events are present, the EC raises and interrupt,
sensorhub reads the FIFO and uses the 'id' field to spread the event to
the proper IIO sensors. This stack is similar to the HID sensor input
stack.

The first 3 patches add a primitive cros_ec_sensorhub. MFD just have to
register this driver if at least one sensor is presented by the EC.
cros_ec_sensorhub retrieves more information from the EC to find out
which sensors are actually present:
  mfd: cros_ec: Add sensor_count and make check_features public
  platform: cros_ec: Add cros_ec_sensor_hub driver
  platform/mfd:iio: cros_ec: Register sensor through sensorhub

The next 3 patches prepare for FIFO support:
  platform: chrome: cros-ec: record event timestamp in the hard irq
  platform: chrome: cros_ec: Do not attempt to register a non-positive
  platform: chrome: cros_ec: handle MKBP more events flag

The next 4 patches add FIFO support. An interface is added to connect
the IIO sensors with cros_ec_sensorhub, and filters are needed to spread
the timestamp when the EC send batches of events and deal with variation
in interrupt delay.
  platform: chrome: sensorhub: Add FIFO support
  platform: chrome: sensorhub: Add code to spread timestmap
  platform: chrome: sensorhub: Add median filter
  iio: cros_ec: Use triggered buffer only when EC does not support FIFO

Finally, the last 3 patches present sensor information following the IIO
ABI:
-  Configurable EC timeout to allow batch mode in buffer/hwfifo_timeout,
  in seconds.
-  Hard coded EC FIFO size in buffer/hwfifo_watermark_max
-  Sensor sampling frequency in hertz at sampling_frequency:
  iio: cros_ec: Expose hwfifo_timeout
  iio: cros_ec: Report hwfifo_watermark_max
  iio: cros_ec: Use Hertz as unit for sampling frequency

For testing, libiio test tools can be used:
A iio device link looks like:
iio:device1 ->
...09:00/GOOG0004:00/cros-ec-dev.6.auto/cros-ec-sensorhub.7.auto/
                     cros-ec-accel.15.auto/iio:device1

When FIFO is available, no trigger are presented. Once
sampling_freqeuncy and hwfifo_timeout are set, sensor events flow
when listening to /dev/iio:device1:
echo 12 > sampling_frequency   # Set ODR to at least 12Hz
echo .100 > buffer/hwfifo_timeout  # do not wait more than 100ms to
                                   # to send samples
iio_readdev -b 2 -T 1000 -s 2 iio:device1 2>/dev/null| od -x
0000000 ffd0 2e20 d990 0000 8630 b56c 07ea 0000
0000020 ffc0 2e10 d970 0000 877e b56c 07ea 0000
0000040`

When FIFO is not supported by the EC, a trigger is present in the
directory. After registering a trigger, setting sampling_frequency,
the latest data collected by the sensor will be retrieved by the host
when the trigger expires.

When cros_ec_accel_legacy driver is used, no FIFO is supported and the
sampling frequency for the accelerometers is hard coded at 10Hz.

This set is built upon the master branch of
git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git


Enrico Granata (2):
  platform: chrome: cros_ec: Do not attempt to register a non-positive
    IRQ number
  platform: chrome: cros_ec: handle MKBP more events flag

Gwendal Grignou (11):
  mfd: cros_ec: Add sensor_count and make check_features public
  platform: cros_ec: Add cros_ec_sensor_hub driver
  platform/mfd:iio: cros_ec: Register sensor through sensorhub
  platform: chrome: cros-ec: record event timestamp in the hard irq
  platform: chrome: sensorhub: Add FIFO support
  platform: chrome: sensorhub: Add code to spread timestmap
  platform: chrome: sensorhub: Add median filter
  iio: cros_ec: Use triggered buffer only when EC does not support FIFO
  iio: cros_ec: Expose hwfifo_timeout
  iio: cros_ec: Report hwfifo_watermark_max
  iio: cros_ec: Use Hertz as unit for sampling frequency

 drivers/iio/accel/cros_ec_accel_legacy.c      |  13 +-
 drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
 .../cros_ec_sensors/cros_ec_lid_angle.c       |   2 +-
 .../common/cros_ec_sensors/cros_ec_sensors.c  |  14 +-
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 251 ++++-
 drivers/iio/light/cros_ec_light_prox.c        |  18 +-
 drivers/iio/pressure/cros_ec_baro.c           |  12 +-
 drivers/mfd/cros_ec_dev.c                     | 208 +---
 drivers/platform/chrome/Kconfig               |  18 +-
 drivers/platform/chrome/Makefile              |   2 +
 drivers/platform/chrome/cros_ec.c             |  51 +-
 drivers/platform/chrome/cros_ec_lpc.c         |   2 +
 drivers/platform/chrome/cros_ec_proto.c       |  51 +-
 drivers/platform/chrome/cros_ec_sensorhub.c   | 269 +++++
 .../platform/chrome/cros_ec_sensorhub_ring.c  | 918 ++++++++++++++++++
 .../linux/iio/common/cros_ec_sensors_core.h   |  29 +-
 include/linux/mfd/cros_ec.h                   |  17 +
 include/linux/platform_data/cros_ec_proto.h   |  30 +-
 .../linux/platform_data/cros_ec_sensorhub.h   | 173 ++++
 19 files changed, 1780 insertions(+), 300 deletions(-)
 create mode 100644 drivers/platform/chrome/cros_ec_sensorhub.c
 create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c
 create mode 100644 include/linux/platform_data/cros_ec_sensorhub.h

-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-09-30 13:15   ` Enric Balletbo i Serra
  2019-10-05 15:26   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
                   ` (12 subsequent siblings)
  13 siblings, 2 replies; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

Add a new function to return the number of MEMS sensors available in a
ChromeOS Embedded Controller.
It uses MOTIONSENSE_CMD_DUMP if available or a specific memory map ACPI
registers to find out.

Also, make check_features public as it can be useful for other drivers
to know whant the Embedded Controller supports.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/mfd/cros_ec_dev.c   | 61 ++++++++++++++++++++++++++++++++++++-
 include/linux/mfd/cros_ec.h | 17 +++++++++++
 2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index 6e6dfd6c1871..3be80183ccaa 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -112,7 +112,7 @@ static const struct mfd_cell cros_ec_vbc_cells[] = {
 	{ .name = "cros-ec-vbc", }
 };
 
-static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
+int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
 {
 	struct cros_ec_command *msg;
 	int ret;
@@ -143,12 +143,71 @@ static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
 
 	return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
 }
+EXPORT_SYMBOL_GPL(cros_ec_check_features);
 
 static void cros_ec_class_release(struct device *dev)
 {
 	kfree(to_cros_ec_dev(dev));
 }
 
+int cros_ec_get_sensor_count(struct cros_ec_dev *ec)
+{
+	/*
+	 * Issue a command to get the number of sensor reported.
+	 * If not supported, check for legacy mode.
+	 */
+	int ret, sensor_count;
+	struct ec_params_motion_sense *params;
+	struct ec_response_motion_sense *resp;
+	struct cros_ec_command *msg;
+	struct cros_ec_device *ec_dev = ec->ec_dev;
+	u8 status;
+
+	msg = kzalloc(sizeof(struct cros_ec_command) +
+			max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
+	if (msg == NULL)
+		return -ENOMEM;
+
+	msg->version = 1;
+	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) {
+		sensor_count = ret;
+	} else if (msg->result != EC_RES_SUCCESS) {
+		sensor_count = -EPROTO;
+	} else {
+		resp = (struct ec_response_motion_sense *)msg->data;
+		sensor_count = resp->dump.sensor_count;
+	}
+	kfree(msg);
+
+	/*
+	 * Check legacy mode: Let's find out if sensors are accessible
+	 * via LPC interface.
+	 */
+	if (sensor_count == -EPROTO &&
+	    ec->cmd_offset == 0 &&
+	    ec_dev->cmd_readmem) {
+		ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS,
+				1, &status);
+		if ((ret >= 0) &&
+		    (status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) {
+			/*
+			 * We have 2 sensors, one in the lid, one in the base.
+			 */
+			sensor_count = 2;
+		}
+	}
+	return sensor_count;
+}
+EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
+
 static void cros_ec_sensors_register(struct cros_ec_dev *ec)
 {
 	/*
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index 61c2875c2a40..578e0bbcafdc 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -32,4 +32,21 @@ struct cros_ec_dev {
 
 #define to_cros_ec_dev(dev)  container_of(dev, struct cros_ec_dev, class_dev)
 
+/**
+ * cros_ec_check_features - Test for the presence of EC features
+ *
+ * Call this function to test whether the ChromeOS EC supports a feature.
+ *
+ * @ec_dev: EC device
+ * @msg: One of ec_feature_code values
+ * @return: 1 if supported, 0 if not
+ */
+int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
+
+/*
+ * Return the number of MEMS sensors supported.
+ * Return < 0 in case of error.
+ */
+int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
+
 #endif /* __LINUX_MFD_CROS_EC_H */
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
  2019-09-22 17:50 ` [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-01 10:31   ` Enric Balletbo i Serra
  2019-10-05 15:35   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 03/13] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
                   ` (11 subsequent siblings)
  13 siblings, 2 replies; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

Similar to HID sensor stack, the new driver sits between cros_ec_dev
and the iio device drivers:

EC based iio device topology would be:
iio:device1 ->
...0/0000:00:1f.0/PNP0C09:00/GOOG0004:00/cros-ec-dev.6.auto/
                                         cros-ec-sensorhub.7.auto/
                                         cros-ec-accel.15.auto/
                                         iio:device1

It will be expanded to control EC sensor FIFO.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
 drivers/platform/chrome/Kconfig               |  18 +-
 drivers/platform/chrome/Makefile              |   1 +
 drivers/platform/chrome/cros_ec_sensorhub.c   | 211 ++++++++++++++++++
 .../linux/platform_data/cros_ec_sensorhub.h   |  21 ++
 5 files changed, 249 insertions(+), 4 deletions(-)
 create mode 100644 drivers/platform/chrome/cros_ec_sensorhub.c
 create mode 100644 include/linux/platform_data/cros_ec_sensorhub.h

diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
index cdbb29cfb907..fefad9572790 100644
--- a/drivers/iio/common/cros_ec_sensors/Kconfig
+++ b/drivers/iio/common/cros_ec_sensors/Kconfig
@@ -4,7 +4,7 @@
 #
 config IIO_CROS_EC_SENSORS_CORE
 	tristate "ChromeOS EC Sensors Core"
-	depends on SYSFS && CROS_EC
+	depends on SYSFS && CROS_EC_SENSORHUB
 	select IIO_BUFFER
 	select IIO_TRIGGERED_BUFFER
 	help
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index ee5f08ea57b6..add967236cfb 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -132,9 +132,9 @@ config CROS_EC_LPC
 	  module will be called cros_ec_lpcs.
 
 config CROS_EC_PROTO
-        bool
-        help
-          ChromeOS EC communication protocol helpers.
+	bool
+	help
+	  ChromeOS EC communication protocol helpers.
 
 config CROS_KBD_LED_BACKLIGHT
 	tristate "Backlight LED support for Chrome OS keyboards"
@@ -190,6 +190,18 @@ config CROS_EC_DEBUGFS
 	  To compile this driver as a module, choose M here: the
 	  module will be called cros_ec_debugfs.
 
+config CROS_EC_SENSORHUB
+	tristate "ChromeOS EC MEMS Senosr Hub"
+	depends on CROS_EC && IIO
+	help
+	  Allow loading IIO sensors. This driver is loaded by MFD and will in
+	  turn query the EC and register the sensors.
+	  It also spreads the sensor data coming from the EC to the IIO sensorr
+	  object.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cros_ec_sensorhub.
+
 config CROS_EC_SYSFS
 	tristate "ChromeOS EC control and information through sysfs"
 	depends on MFD_CROS_EC_DEV && SYSFS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 477ec3d1d1c9..a164c40dc099 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o cros_ec_trace.o
 obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
 obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_chardev.o
 obj-$(CONFIG_CROS_EC_LIGHTBAR)		+= cros_ec_lightbar.o
+obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorhub.o
 obj-$(CONFIG_CROS_EC_VBC)		+= cros_ec_vbc.o
 obj-$(CONFIG_CROS_EC_DEBUGFS)		+= cros_ec_debugfs.o
 obj-$(CONFIG_CROS_EC_SYSFS)		+= cros_ec_sysfs.o
diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
new file mode 100644
index 000000000000..80688018ef66
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_sensorhub.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SensorHub: driver that discover sensors behind
+ * a ChromeOS Embedded controller.
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include <linux/platform_data/cros_ec_sensorhub.h>
+
+#define DRV_NAME		"cros-ec-sensorhub"
+
+static int cros_ec_sensors_register(struct device *dev,
+		struct cros_ec_dev *ec)
+{
+	int ret, i, id, sensor_num;
+	struct mfd_cell *sensor_cells;
+	struct cros_ec_sensor_platform *sensor_platforms;
+	int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
+	struct ec_params_motion_sense *params;
+	struct ec_response_motion_sense *resp;
+	struct cros_ec_command *msg;
+
+	sensor_num = cros_ec_get_sensor_count(ec);
+	if (sensor_num < 0) {
+		dev_err(dev,
+			"Unable to retrieve sensor information (err:%d)\n",
+			sensor_num);
+		return sensor_num;
+	}
+
+	if (sensor_num == 0) {
+		dev_err(dev, "Zero sensors reported.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Build an array of sensors driver and register them all.
+	 */
+	msg = kzalloc(sizeof(struct cros_ec_command) +
+		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
+	if (msg == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	msg->version = 1;
+	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;
+	resp = (struct ec_response_motion_sense *)msg->data;
+
+	/*
+	 * Allocate 1 extra sensor if lid angle sensor is needed.
+	 */
+	sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell),
+			       GFP_KERNEL);
+	if (sensor_cells == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	sensor_platforms = kcalloc(sensor_num,
+				   sizeof(struct cros_ec_sensor_platform),
+				   GFP_KERNEL);
+	if (sensor_platforms == NULL) {
+		ret = -ENOMEM;
+		goto error_platforms;
+	}
+
+	id = 0;
+	for (i = 0; i < sensor_num; i++) {
+		params->cmd = MOTIONSENSE_CMD_INFO;
+		params->info.sensor_num = i;
+		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
+		if (ret < 0) {
+			dev_warn(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_BARO:
+			sensor_cells[id].name = "cros-ec-baro";
+			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(dev, "unknown type %d\n", resp->info.type);
+			continue;
+		}
+		sensor_platforms[id].sensor_num = i;
+		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)
+		ec->has_kb_wake_angle = true;
+
+	if (cros_ec_check_features(ec,
+				EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
+		sensor_cells[id].name = "cros-ec-lid-angle";
+		id++;
+	}
+
+	ret = mfd_add_hotplug_devices(dev, sensor_cells, id);
+	kfree(sensor_platforms);
+error_platforms:
+	kfree(sensor_cells);
+error:
+	kfree(msg);
+	return ret;
+}
+
+static struct cros_ec_sensor_platform sensor_platforms[] = {
+	{ .sensor_num = 0 },
+	{ .sensor_num = 1 }
+};
+
+static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
+	{
+		.name = "cros-ec-accel-legacy",
+		.platform_data = &sensor_platforms[0],
+		.pdata_size = sizeof(struct cros_ec_sensor_platform),
+	},
+	{
+		.name = "cros-ec-accel-legacy",
+		.platform_data = &sensor_platforms[1],
+		.pdata_size = sizeof(struct cros_ec_sensor_platform),
+	}
+};
+
+
+
+static int cros_ec_sensorhub_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
+	int ret;
+	struct cros_ec_sensorhub *data =
+		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
+
+	if (!data)
+		return -ENOMEM;
+
+	data->ec = ec;
+	dev_set_drvdata(dev, data);
+
+	/* check whether this EC is a sensor hub. */
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
+		ret = cros_ec_sensors_register(dev, ec);
+	} else {
+		/* Workaroud for older EC firmware */
+		ret = mfd_add_hotplug_devices(dev,
+				cros_ec_accel_legacy_cells,
+				ARRAY_SIZE(cros_ec_accel_legacy_cells));
+	}
+	if (ret)
+		dev_err(dev, "failed to add EC sensors: error %d\n", ret);
+	return ret;
+}
+
+static struct platform_driver cros_ec_sensorhub_driver = {
+	.driver = {
+		.name = DRV_NAME,
+	},
+	.probe = cros_ec_sensorhub_probe,
+};
+
+module_platform_driver(cros_ec_sensorhub_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
+MODULE_DESCRIPTION("ChromeOS EC MEMS Sensor Hub Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
new file mode 100644
index 000000000000..a8b64ecf5b9b
--- /dev/null
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cros_ec_sensorhub- Chrome OS EC MEMS Sensor Hub driver.
+ *
+ * Copyright (C) 2019 Google, Inc
+ *
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
+#define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
+
+#include <linux/platform_data/cros_ec_commands.h>
+
+/**
+ * struct cros_ec_sensorhub - Sensor Hub device data.
+ */
+struct cros_ec_sensorhub {
+	struct cros_ec_dev *ec;
+};
+
+#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 03/13] platform/mfd:iio: cros_ec: Register sensor through sensorhub
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
  2019-09-22 17:50 ` [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
  2019-09-22 17:50 ` [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 15:41   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq Gwendal Grignou
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

- Add sensorhub include file
- Remove duplicate code in mfd, since mfd just register
  cros_ec_sensorhub if at least one sensor is present
- Change iio cros_ec driver to get the pointer to the cros_ec_dev
  through cros_ec_sensorhub.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/iio/accel/cros_ec_accel_legacy.c      |   4 +-
 .../common/cros_ec_sensors/cros_ec_sensors.c  |   4 +-
 .../cros_ec_sensors/cros_ec_sensors_core.c    |   4 +-
 drivers/iio/light/cros_ec_light_prox.c        |   6 +-
 drivers/mfd/cros_ec_dev.c                     | 203 ++----------------
 drivers/platform/chrome/cros_ec_sensorhub.c   |   2 +-
 include/linux/platform_data/cros_ec_proto.h   |   8 -
 .../linux/platform_data/cros_ec_sensorhub.h   |   8 +
 8 files changed, 34 insertions(+), 205 deletions(-)

diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
index fcc3f999e482..c9af6fa0670d 100644
--- a/drivers/iio/accel/cros_ec_accel_legacy.c
+++ b/drivers/iio/accel/cros_ec_accel_legacy.c
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/platform_data/cros_ec_commands.h>
 #include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_data/cros_ec_sensorhub.h>
 #include <linux/platform_device.h>
 
 #define DRV_NAME	"cros-ec-accel-legacy"
@@ -163,7 +164,8 @@ static const struct iio_chan_spec cros_ec_accel_legacy_channels[] = {
 static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
+	struct cros_ec_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
+	struct cros_ec_dev *ec = sensor_hub->ec;
 	struct iio_dev *indio_dev;
 	struct cros_ec_sensors_core_state *state;
 	int ret;
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index a6987726eeb8..5bd6f54afc42 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/platform_data/cros_ec_commands.h>
 #include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_data/cros_ec_sensorhub.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
@@ -222,7 +223,8 @@ static const struct iio_info ec_sensors_info = {
 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_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
+	struct cros_ec_dev *ec_dev = sensor_hub->ec;
 	struct iio_dev *indio_dev;
 	struct cros_ec_sensors_state *state;
 	struct iio_chan_spec *channel;
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
index d2609e6feda4..81a7f692de2f 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/platform_data/cros_ec_commands.h>
 #include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_data/cros_ec_sensorhub.h>
 #include <linux/platform_device.h>
 
 static char *cros_ec_loc[] = {
@@ -88,7 +89,8 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 {
 	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_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
+	struct cros_ec_dev *ec = sensor_hub->ec;
 	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
 	u32 ver_mask;
 	int ret, i;
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index c5263b563fc1..205effc1f404 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/platform_data/cros_ec_commands.h>
 #include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_data/cros_ec_sensorhub.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
@@ -169,13 +170,14 @@ static const struct iio_info cros_ec_light_prox_info = {
 static int cros_ec_light_prox_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+	struct cros_ec_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
+	struct cros_ec_dev *ec = sensor_hub->ec;
 	struct iio_dev *indio_dev;
 	struct cros_ec_light_prox_state *state;
 	struct iio_chan_spec *channel;
 	int ret;
 
-	if (!ec_dev || !ec_dev->ec_dev) {
+	if (!ec || !ec->ec_dev) {
 		dev_warn(dev, "No CROS EC device found.\n");
 		return -EINVAL;
 	}
diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index 3be80183ccaa..3a583d3503ca 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -78,6 +78,10 @@ static const struct mfd_cell cros_ec_rtc_cells[] = {
 	{ .name = "cros-ec-rtc", },
 };
 
+static const struct mfd_cell cros_ec_sensorhub_cells[] = {
+	{ .name = "cros-ec-sensorhub", },
+};
+
 static const struct mfd_cell cros_usbpd_charger_cells[] = {
 	{ .name = "cros-usbpd-charger", },
 	{ .name = "cros-usbpd-logger", },
@@ -208,192 +212,6 @@ int cros_ec_get_sensor_count(struct cros_ec_dev *ec)
 }
 EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
 
-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_status(ec->ec_dev, msg);
-	if (ret < 0) {
-		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 if lid angle sensor and/or FIFO are needed.
-	 */
-	sensor_cells = kcalloc(sensor_num + 2, sizeof(struct mfd_cell),
-			       GFP_KERNEL);
-	if (sensor_cells == NULL)
-		goto error;
-
-	sensor_platforms = kcalloc(sensor_num,
-				   sizeof(struct cros_ec_sensor_platform),
-				   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_status(ec->ec_dev, msg);
-		if (ret < 0) {
-			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_BARO:
-			sensor_cells[id].name = "cros-ec-baro";
-			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)
-		ec->has_kb_wake_angle = true;
-
-	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
-		sensor_cells[id].name = "cros-ec-ring";
-		id++;
-	}
-	if (cros_ec_check_features(ec,
-				EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
-		sensor_cells[id].name = "cros-ec-lid-angle";
-		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 struct cros_ec_sensor_platform sensor_platforms[] = {
-	{ .sensor_num = 0 },
-	{ .sensor_num = 1 }
-};
-
-static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
-	{
-		.name = "cros-ec-accel-legacy",
-		.platform_data = &sensor_platforms[0],
-		.pdata_size = sizeof(struct cros_ec_sensor_platform),
-	},
-	{
-		.name = "cros-ec-accel-legacy",
-		.platform_data = &sensor_platforms[1],
-		.pdata_size = sizeof(struct cros_ec_sensor_platform),
-	}
-};
-
-static void cros_ec_accel_legacy_register(struct cros_ec_dev *ec)
-{
-	struct cros_ec_device *ec_dev = ec->ec_dev;
-	u8 status;
-	int ret;
-
-	/*
-	 * ECs that need legacy support are the main EC, directly connected to
-	 * the AP.
-	 */
-	if (ec->cmd_offset != 0)
-		return;
-
-	/*
-	 * Check if EC supports direct memory reads and if EC has
-	 * accelerometers.
-	 */
-	if (ec_dev->cmd_readmem) {
-		ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, 1,
-					  &status);
-		if (ret < 0) {
-			dev_warn(ec->dev, "EC direct read error.\n");
-			return;
-		}
-
-		/* Check if EC has accelerometers. */
-		if (!(status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) {
-			dev_info(ec->dev, "EC does not have accelerometers.\n");
-			return;
-		}
-	}
-
-	/*
-	 * The device may still support accelerometers:
-	 * it would be an older ARM based device that do not suppor the
-	 * EC_CMD_GET_FEATURES command.
-	 *
-	 * Register 2 accelerometers, we will fail in the IIO driver if there
-	 * are no sensors.
-	 */
-	ret = mfd_add_hotplug_devices(ec->dev, cros_ec_accel_legacy_cells,
-				      ARRAY_SIZE(cros_ec_accel_legacy_cells));
-	if (ret)
-		dev_err(ec_dev->dev, "failed to add EC sensors\n");
-}
-
 static int ec_device_probe(struct platform_device *pdev)
 {
 	int retval = -ENOMEM;
@@ -449,11 +267,14 @@ static int ec_device_probe(struct platform_device *pdev)
 		goto failed;
 
 	/* check whether this EC is a sensor hub. */
-	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
-		cros_ec_sensors_register(ec);
-	else
-		/* Workaroud for older EC firmware */
-		cros_ec_accel_legacy_register(ec);
+	if (cros_ec_get_sensor_count(ec) > 0) {
+		retval = mfd_add_hotplug_devices(ec->dev,
+				cros_ec_sensorhub_cells,
+				ARRAY_SIZE(cros_ec_sensorhub_cells));
+		if (retval)
+			dev_err(ec->dev, "failed to add %s subdevice: %d\n",
+				cros_ec_sensorhub_cells->name, retval);
+	}
 
 	/*
 	 * The following subdevices can be detected by sending the
diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
index 80688018ef66..01f11ed611fb 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub.c
@@ -181,7 +181,7 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
 	data->ec = ec;
 	dev_set_drvdata(dev, data);
 
-	/* check whether this EC is a sensor hub. */
+	/* Check whether this EC is a sensor hub. */
 	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
 		ret = cros_ec_sensors_register(dev, ec);
 	} else {
diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
index eab7036cda09..e91e3fcb0348 100644
--- a/include/linux/platform_data/cros_ec_proto.h
+++ b/include/linux/platform_data/cros_ec_proto.h
@@ -167,14 +167,6 @@ struct cros_ec_device {
 	struct platform_device *pd;
 };
 
-/**
- * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information.
- * @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', ...)
diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
index a8b64ecf5b9b..9295eabb16f6 100644
--- a/include/linux/platform_data/cros_ec_sensorhub.h
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -11,6 +11,14 @@
 
 #include <linux/platform_data/cros_ec_commands.h>
 
+/**
+ * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information.
+ * @sensor_num: Id of the sensor, as reported by the EC.
+ */
+struct cros_ec_sensor_platform {
+	u8 sensor_num;
+};
+
 /**
  * struct cros_ec_sensorhub - Sensor Hub device data.
  */
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (2 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 03/13] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-01 10:32   ` Enric Balletbo i Serra
  2019-10-05 15:44   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 05/13] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
                   ` (9 subsequent siblings)
  13 siblings, 2 replies; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

To improve sensor timestamp precision, given EC and AP are in
different time domains, the AP needs to try to record the exact
moment an event was signalled to the AP by the EC as soon as
possible after it happens.

First thing in the hard irq is the best place for this.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/platform/chrome/cros_ec.c           | 18 +++++++++++++++++-
 drivers/platform/chrome/cros_ec_lpc.c       |  2 ++
 include/linux/platform_data/cros_ec_proto.h | 15 +++++++++++++++
 3 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index fd77e6fa74c2..f49eb1d1e3cd 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -31,6 +31,21 @@ static struct cros_ec_platform pd_p = {
 	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
 };
 
+s64 cros_ec_get_time_ns(void)
+{
+	return ktime_get_boottime_ns();
+}
+EXPORT_SYMBOL(cros_ec_get_time_ns);
+
+static irqreturn_t ec_irq_handler(int irq, void *data)
+{
+	struct cros_ec_device *ec_dev = data;
+
+	ec_dev->last_event_time = cros_ec_get_time_ns();
+
+	return IRQ_WAKE_THREAD;
+}
+
 static irqreturn_t ec_irq_thread(int irq, void *data)
 {
 	struct cros_ec_device *ec_dev = data;
@@ -132,7 +147,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
 	}
 
 	if (ec_dev->irq) {
-		err = devm_request_threaded_irq(dev, ec_dev->irq, NULL,
+		err = devm_request_threaded_irq(
+				dev, ec_dev->irq, ec_irq_handler,
 				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
 				"chromeos-ec", ec_dev);
 		if (err) {
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index 7d10d909435f..3c77496e164d 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -313,6 +313,8 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
 {
 	struct cros_ec_device *ec_dev = data;
 
+	ec_dev->last_event_time = cros_ec_get_time_ns();
+
 	if (ec_dev->mkbp_event_supported &&
 	    cros_ec_get_next_event(ec_dev, NULL) > 0)
 		blocking_notifier_call_chain(&ec_dev->event_notifier, 0,
diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
index e91e3fcb0348..ab12e28f2107 100644
--- a/include/linux/platform_data/cros_ec_proto.h
+++ b/include/linux/platform_data/cros_ec_proto.h
@@ -121,6 +121,8 @@ struct cros_ec_command {
  * @event_data: Raw payload transferred with the MKBP event.
  * @event_size: Size in bytes of the event data.
  * @host_event_wake_mask: Mask of host events that cause wake from suspend.
+ * @last_event_time: exact time from the hard irq when we got notified of
+ *     a new event.
  * @ec: The platform_device used by the mfd driver to interface with the
  *      main EC.
  * @pd: The platform_device used by the mfd driver to interface with the
@@ -161,6 +163,7 @@ struct cros_ec_device {
 	int event_size;
 	u32 host_event_wake_mask;
 	u32 last_resume_result;
+	s64 last_event_time;
 
 	/* The platform devices used by the mfd driver */
 	struct platform_device *ec;
@@ -308,4 +311,16 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event);
  */
 u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev);
 
+/**
+ * cros_ec_get_time_ns - Return time in ns.
+ *
+ * This is the function used to record the time for last_event_time in struct
+ * cros_ec_device during the hard irq.
+ *
+ * This function is probably implemented using ktime_get_boot_ns(), but it's
+ * exposed here to make sure all cros_ec drivers use the same code path to get
+ * the time.
+ */
+s64 cros_ec_get_time_ns(void);
+
 #endif /* __LINUX_CROS_EC_PROTO_H */
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 05/13] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (3 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-01 10:32   ` Enric Balletbo i Serra
  2019-09-22 17:50 ` [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Enrico Granata, Enrico Granata, Gwendal Grignou

From: Enrico Granata <egranata@chromium.org>

Add a layer of sanity checking to cros_ec_register against attempting to
register IRQ values that are not strictly greater than 0.

Signed-off-by: Enrico Granata <egranata@google.com>
Signed-off-by: Enrico Granata <egranata@chromium.org>
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/platform/chrome/cros_ec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index f49eb1d1e3cd..9c8dc7cdb2b7 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -146,7 +146,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
 		return err;
 	}
 
-	if (ec_dev->irq) {
+	if (ec_dev->irq > 0) {
 		err = devm_request_threaded_irq(
 				dev, ec_dev->irq, ec_irq_handler,
 				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (4 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 05/13] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-01 10:32   ` Enric Balletbo i Serra
  2019-10-05 15:52   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 07/13] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
                   ` (7 subsequent siblings)
  13 siblings, 2 replies; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Enrico Granata, Gwendal Grignou

From: Enrico Granata <egranata@chromium.org>

The ChromeOS EC has support for signaling to the host that
a single IRQ can serve multiple MKBP events.

Doing this serves an optimization purpose, as it minimizes the
number of round-trips into the interrupt handling machinery, and
it proves beneficial to sensor timestamping as it keeps the desired
synchronization of event times between the two processors.

This patch adds kernel support for this EC feature, allowing the
ec_irq to loop until all events have been served.

Signed-off-by: Enrico Granata <egranata@chromium.org>
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/platform/chrome/cros_ec.c           | 33 +++++++++----
 drivers/platform/chrome/cros_ec_proto.c     | 51 ++++++++++++---------
 include/linux/platform_data/cros_ec_proto.h |  7 ++-
 3 files changed, 57 insertions(+), 34 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index 9c8dc7cdb2b7..4adc007c357c 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -46,25 +46,38 @@ static irqreturn_t ec_irq_handler(int irq, void *data)
 	return IRQ_WAKE_THREAD;
 }
 
-static irqreturn_t ec_irq_thread(int irq, void *data)
+static bool ec_handle_event(struct cros_ec_device *ec_dev)
 {
-	struct cros_ec_device *ec_dev = data;
 	bool wake_event = true;
-	int ret;
+	bool ec_has_more_events = false;
+	int ret = cros_ec_get_next_event(ec_dev, &wake_event);
 
-	ret = cros_ec_get_next_event(ec_dev, &wake_event);
+	if (ec_dev->mkbp_event_supported) {
+		ec_has_more_events = (ret > 0) &&
+			(ec_dev->event_data.event_type &
+				EC_MKBP_HAS_MORE_EVENTS);
+	}
 
-	/*
-	 * Signal only if wake host events or any interrupt if
-	 * cros_ec_get_next_event() returned an error (default value for
-	 * wake_event is true)
-	 */
-	if (wake_event && device_may_wakeup(ec_dev->dev))
+	if (device_may_wakeup(ec_dev->dev) && wake_event)
 		pm_wakeup_event(ec_dev->dev, 0);
 
 	if (ret > 0)
 		blocking_notifier_call_chain(&ec_dev->event_notifier,
 					     0, ec_dev);
+
+	return ec_has_more_events;
+
+}
+
+static irqreturn_t ec_irq_thread(int irq, void *data)
+{
+	struct cros_ec_device *ec_dev = data;
+	bool ec_has_more_events;
+
+	do {
+		ec_has_more_events = ec_handle_event(ec_dev);
+	} while (ec_has_more_events);
+
 	return IRQ_HANDLED;
 }
 
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index f659f96bda12..70e6d6c93b8d 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -425,10 +425,14 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
 	ret = cros_ec_get_host_command_version_mask(ec_dev,
 						    EC_CMD_GET_NEXT_EVENT,
 						    &ver_mask);
-	if (ret < 0 || ver_mask == 0)
+	if (ret < 0 || ver_mask == 0) {
 		ec_dev->mkbp_event_supported = 0;
-	else
-		ec_dev->mkbp_event_supported = 1;
+		dev_info(ec_dev->dev, "MKBP not supported\n");
+	} else {
+		ec_dev->mkbp_event_supported = fls(ver_mask);
+		dev_info(ec_dev->dev, "MKBP support version %u\n",
+			ec_dev->mkbp_event_supported - 1);
+	}
 
 	/* Probe if host sleep v1 is supported for S0ix failure detection. */
 	ret = cros_ec_get_host_command_version_mask(ec_dev,
@@ -519,6 +523,7 @@ EXPORT_SYMBOL(cros_ec_cmd_xfer_status);
 
 static int get_next_event_xfer(struct cros_ec_device *ec_dev,
 			       struct cros_ec_command *msg,
+			       struct ec_response_get_next_event_v1 *event,
 			       int version, uint32_t size)
 {
 	int ret;
@@ -531,7 +536,7 @@ static int get_next_event_xfer(struct cros_ec_device *ec_dev,
 	ret = cros_ec_cmd_xfer(ec_dev, msg);
 	if (ret > 0) {
 		ec_dev->event_size = ret - 1;
-		memcpy(&ec_dev->event_data, msg->data, ret);
+		ec_dev->event_data = *event;
 	}
 
 	return ret;
@@ -539,30 +544,29 @@ static int get_next_event_xfer(struct cros_ec_device *ec_dev,
 
 static int get_next_event(struct cros_ec_device *ec_dev)
 {
-	u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)];
-	struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
-	static int cmd_version = 1;
-	int ret;
+	struct {
+		struct cros_ec_command msg;
+		struct ec_response_get_next_event_v1 event;
+	} __packed buf;
+	struct cros_ec_command *msg = &buf.msg;
+	struct ec_response_get_next_event_v1 *event = &buf.event;
+	const int cmd_version = ec_dev->mkbp_event_supported - 1;
+
+	BUILD_BUG_ON(sizeof(union ec_response_get_next_data_v1) != 16);
+
+	memset(&buf, 0, sizeof(buf));
 
 	if (ec_dev->suspended) {
 		dev_dbg(ec_dev->dev, "Device suspended.\n");
 		return -EHOSTDOWN;
 	}
 
-	if (cmd_version == 1) {
-		ret = get_next_event_xfer(ec_dev, msg, cmd_version,
-				sizeof(struct ec_response_get_next_event_v1));
-		if (ret < 0 || msg->result != EC_RES_INVALID_VERSION)
-			return ret;
-
-		/* Fallback to version 0 for future send attempts */
-		cmd_version = 0;
-	}
-
-	ret = get_next_event_xfer(ec_dev, msg, cmd_version,
+	if (cmd_version == 0)
+		return get_next_event_xfer(ec_dev, msg, event, 0,
 				  sizeof(struct ec_response_get_next_event));
 
-	return ret;
+	return get_next_event_xfer(ec_dev, msg, event, cmd_version,
+				sizeof(struct ec_response_get_next_event_v1));
 }
 
 static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
@@ -606,7 +610,8 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
 		return ret;
 
 	if (wake_event) {
-		event_type = ec_dev->event_data.event_type;
+		event_type =
+			ec_dev->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK;
 		host_event = cros_ec_get_host_event(ec_dev);
 
 		/*
@@ -631,10 +636,12 @@ EXPORT_SYMBOL(cros_ec_get_next_event);
 u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
 {
 	u32 host_event;
+	const u8 event_type =
+		ec_dev->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK;
 
 	BUG_ON(!ec_dev->mkbp_event_supported);
 
-	if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT)
+	if (event_type != EC_MKBP_EVENT_HOST_EVENT)
 		return 0;
 
 	if (ec_dev->event_size != sizeof(host_event)) {
diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
index ab12e28f2107..63b5597294e7 100644
--- a/include/linux/platform_data/cros_ec_proto.h
+++ b/include/linux/platform_data/cros_ec_proto.h
@@ -115,7 +115,9 @@ struct cros_ec_command {
  *            code.
  * @pkt_xfer: Send packet to EC and get response.
  * @lock: One transaction at a time.
- * @mkbp_event_supported: True if this EC supports the MKBP event protocol.
+ * @mkbp_event_supported: 0 if MKBP not supported. Otherwise its value is
+ *                        the maximum supported version of the MKBP host event
+ *                        command + 1.
  * @host_sleep_v1: True if this EC supports the sleep v1 command.
  * @event_notifier: Interrupt event notifier for transport devices.
  * @event_data: Raw payload transferred with the MKBP event.
@@ -155,7 +157,8 @@ struct cros_ec_device {
 	int (*pkt_xfer)(struct cros_ec_device *ec,
 			struct cros_ec_command *msg);
 	struct mutex lock;
-	bool mkbp_event_supported;
+	/* 0 == not supported, otherwise it supports version x - 1 */
+	u8 mkbp_event_supported;
 	bool host_sleep_v1;
 	struct blocking_notifier_head event_notifier;
 
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 07/13] platform: chrome: sensorhub: Add FIFO support
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (5 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 16:08   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 08/13] platform: chrome: sensorhub: Add code to spread timestmap Gwendal Grignou
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

cros_ec_sensorhub registers a listener and query motion sense FIFO,
spread to iio sensors registers.

To test, we can use libiio:
iiod&
iio_readdev -u ip:localhost -T 10000 -s 25 -b 16 cros-ec-gyro | od -x

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/iio/accel/cros_ec_accel_legacy.c      |   1 +
 .../common/cros_ec_sensors/cros_ec_sensors.c  |   1 +
 .../cros_ec_sensors/cros_ec_sensors_core.c    |  13 +
 drivers/iio/light/cros_ec_light_prox.c        |   1 +
 drivers/iio/pressure/cros_ec_baro.c           |   1 +
 drivers/platform/chrome/Makefile              |   3 +-
 drivers/platform/chrome/cros_ec_sensorhub.c   | 130 ++++--
 .../platform/chrome/cros_ec_sensorhub_ring.c  | 374 ++++++++++++++++++
 .../linux/iio/common/cros_ec_sensors_core.h   |   3 +
 .../linux/platform_data/cros_ec_sensorhub.h   |  79 ++++
 10 files changed, 569 insertions(+), 37 deletions(-)
 create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c

diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
index c9af6fa0670d..591c0d962c44 100644
--- a/drivers/iio/accel/cros_ec_accel_legacy.c
+++ b/drivers/iio/accel/cros_ec_accel_legacy.c
@@ -212,6 +212,7 @@ static struct platform_driver cros_ec_accel_platform_driver = {
 		.name	= DRV_NAME,
 	},
 	.probe		= cros_ec_accel_legacy_probe,
+	.remove		= cros_ec_sensors_core_clean,
 };
 module_platform_driver(cros_ec_accel_platform_driver);
 
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index 5bd6f54afc42..a88dd8deade9 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -329,6 +329,7 @@ static struct platform_driver cros_ec_sensors_platform_driver = {
 		.pm	= &cros_ec_sensors_pm_ops,
 	},
 	.probe		= cros_ec_sensors_probe,
+	.remove		= cros_ec_sensors_core_clean,
 	.id_table	= cros_ec_sensors_ids,
 };
 module_platform_driver(cros_ec_sensors_platform_driver);
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
index 81a7f692de2f..43eb1d42820e 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -160,6 +160,19 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
 
+int cros_ec_sensors_core_clean(struct platform_device *pdev)
+{
+	struct cros_ec_sensorhub *sensor_hub =
+		dev_get_drvdata(pdev->dev.parent);
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	u8 sensor_num = st->param.info.sensor_num;
+
+	cros_ec_sensorhub_unregister_push_data(sensor_hub, sensor_num);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_clean);
+
 int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
 				 u16 opt_length)
 {
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index 205effc1f404..c431e4d1482d 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -270,6 +270,7 @@ static struct platform_driver cros_ec_light_prox_platform_driver = {
 		.pm	= &cros_ec_sensors_pm_ops,
 	},
 	.probe		= cros_ec_light_prox_probe,
+	.remove		= cros_ec_sensors_core_clean,
 	.id_table	= cros_ec_light_prox_ids,
 };
 module_platform_driver(cros_ec_light_prox_platform_driver);
diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
index 2354302375de..2f4d6d3ab41d 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -201,6 +201,7 @@ static struct platform_driver cros_ec_baro_platform_driver = {
 		.name	= "cros-ec-baro",
 	},
 	.probe		= cros_ec_baro_probe,
+	.remove		= cros_ec_sensors_core_clean,
 	.id_table	= cros_ec_baro_ids,
 };
 module_platform_driver(cros_ec_baro_platform_driver);
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index a164c40dc099..cb709048c003 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -17,7 +17,8 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o cros_ec_trace.o
 obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
 obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_chardev.o
 obj-$(CONFIG_CROS_EC_LIGHTBAR)		+= cros_ec_lightbar.o
-obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorhub.o
+cros_ec_sensorsupport-objs			:= cros_ec_sensorhub_ring.o cros_ec_sensorhub.o
+obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorsupport.o
 obj-$(CONFIG_CROS_EC_VBC)		+= cros_ec_vbc.o
 obj-$(CONFIG_CROS_EC_DEBUGFS)		+= cros_ec_debugfs.o
 obj-$(CONFIG_CROS_EC_SYSFS)		+= cros_ec_sysfs.o
diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
index 01f11ed611fb..cf93120719c4 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub.c
@@ -26,15 +26,14 @@
 #define DRV_NAME		"cros-ec-sensorhub"
 
 static int cros_ec_sensors_register(struct device *dev,
-		struct cros_ec_dev *ec)
+		struct cros_ec_sensorhub *sensorhub)
 {
 	int ret, i, id, sensor_num;
 	struct mfd_cell *sensor_cells;
 	struct cros_ec_sensor_platform *sensor_platforms;
 	int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
-	struct ec_params_motion_sense *params;
-	struct ec_response_motion_sense *resp;
-	struct cros_ec_command *msg;
+	struct cros_ec_command *msg = sensorhub->msg;
+	struct cros_ec_dev *ec = sensorhub->ec;
 
 	sensor_num = cros_ec_get_sensor_count(ec);
 	if (sensor_num < 0) {
@@ -49,32 +48,17 @@ static int cros_ec_sensors_register(struct device *dev,
 		return -EINVAL;
 	}
 
-	/*
-	 * Build an array of sensors driver and register them all.
-	 */
-	msg = kzalloc(sizeof(struct cros_ec_command) +
-		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
-	if (msg == NULL) {
-		ret = -ENOMEM;
-		goto error;
-	}
-
 	msg->version = 1;
-	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;
-	resp = (struct ec_response_motion_sense *)msg->data;
+	msg->insize = sizeof(struct ec_response_motion_sense);
+	msg->outsize = sizeof(struct ec_params_motion_sense);
 
 	/*
 	 * Allocate 1 extra sensor if lid angle sensor is needed.
 	 */
 	sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell),
 			       GFP_KERNEL);
-	if (sensor_cells == NULL) {
-		ret = -ENOMEM;
-		goto error;
-	}
+	if (sensor_cells == NULL)
+		return -ENOMEM;
 
 	sensor_platforms = kcalloc(sensor_num,
 				   sizeof(struct cros_ec_sensor_platform),
@@ -86,15 +70,15 @@ static int cros_ec_sensors_register(struct device *dev,
 
 	id = 0;
 	for (i = 0; i < sensor_num; i++) {
-		params->cmd = MOTIONSENSE_CMD_INFO;
-		params->info.sensor_num = i;
+		sensorhub->params->cmd = MOTIONSENSE_CMD_INFO;
+		sensorhub->params->info.sensor_num = i;
 		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
 		if (ret < 0) {
 			dev_warn(dev, "no info for EC sensor %d : %d/%d\n",
 				 i, ret, msg->result);
 			continue;
 		}
-		switch (resp->info.type) {
+		switch (sensorhub->resp->info.type) {
 		case MOTIONSENSE_TYPE_ACCEL:
 			sensor_cells[id].name = "cros-ec-accel";
 			break;
@@ -117,7 +101,8 @@ static int cros_ec_sensors_register(struct device *dev,
 			sensor_cells[id].name = "cros-ec-activity";
 			break;
 		default:
-			dev_warn(dev, "unknown type %d\n", resp->info.type);
+			dev_warn(dev, "unknown type %d\n",
+				 sensorhub->resp->info.type);
 			continue;
 		}
 		sensor_platforms[id].sensor_num = i;
@@ -125,7 +110,7 @@ static int cros_ec_sensors_register(struct device *dev,
 		sensor_cells[id].pdata_size =
 			sizeof(struct cros_ec_sensor_platform);
 
-		sensor_type[resp->info.type]++;
+		sensor_type[sensorhub->resp->info.type]++;
 		id++;
 	}
 
@@ -142,8 +127,6 @@ static int cros_ec_sensors_register(struct device *dev,
 	kfree(sensor_platforms);
 error_platforms:
 	kfree(sensor_cells);
-error:
-	kfree(msg);
 	return ret;
 }
 
@@ -165,41 +148,116 @@ static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
 	}
 };
 
-
-
 static int cros_ec_sensorhub_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
 	int ret;
-	struct cros_ec_sensorhub *data =
-		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
+	struct cros_ec_sensorhub *data;
+	struct cros_ec_command *msg;
+
+	msg = devm_kzalloc(dev, sizeof(struct cros_ec_command) +
+			max((u16)sizeof(struct ec_params_motion_sense),
+			    ec->ec_dev->max_response), GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
 
+	data = devm_kzalloc(dev, sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
+	data->dev = dev;
 	data->ec = ec;
+
+	mutex_init(&data->cmd_lock);
+	data->msg = msg;
+	data->params = (struct ec_params_motion_sense *)msg->data;
+	data->resp = (struct ec_response_motion_sense *)msg->data;
+
 	dev_set_drvdata(dev, data);
 
 	/* Check whether this EC is a sensor hub. */
 	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
-		ret = cros_ec_sensors_register(dev, ec);
+		ret = cros_ec_sensors_register(dev, data);
 	} else {
 		/* Workaroud for older EC firmware */
+		ec->has_kb_wake_angle = true;
 		ret = mfd_add_hotplug_devices(dev,
 				cros_ec_accel_legacy_cells,
 				ARRAY_SIZE(cros_ec_accel_legacy_cells));
 	}
-	if (ret)
+	if (ret) {
 		dev_err(dev, "failed to add EC sensors: error %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * If the EC does not have a FIFO, the sensors will query their data
+	 * themselves via sysfs or a software trigger.
+	 */
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+		ret = cros_ec_sensorhub_ring_add(data);
+		/*
+		 * The msg and its data is not under the control of the ring
+		 * handler.
+		 */
+	}
 	return ret;
 }
 
+static int cros_ec_sensorhub_remove(struct platform_device *pdev)
+{
+	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
+	struct cros_ec_dev *ec = sensorhub->ec;
+
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
+		cros_ec_sensorhub_ring_remove(sensorhub);
+	return 0;
+}
+
+/*
+ * When the EC is suspending, we must stop sending interrupt,
+ * we may use the same interrupt line for waking up the device.
+ * Tell the EC to stop sending non-interrupt event on the iio ring.
+ */
+static int __maybe_unused cros_ec_ring_prepare(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
+	struct cros_ec_dev *ec = sensorhub->ec;
+
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
+		return cros_ec_sensorhub_ring_fifo_toggle(sensorhub, false);
+	return 0;
+}
+
+static void __maybe_unused cros_ec_ring_complete(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
+	struct cros_ec_dev *ec = sensorhub->ec;
+
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
+		cros_ec_sensorhub_ring_fifo_toggle(sensorhub, true);
+}
+
+#if CONFIG_PM_SLEEP
+static const struct dev_pm_ops cros_ec_sensorhub_ring_pm_ops = {
+	.prepare = cros_ec_ring_prepare,
+	.complete = cros_ec_ring_complete
+};
+#else
+static const struct dev_pm_ops cros_ec_sensorhub_ring_pm_ops = { };
+#endif
+
 static struct platform_driver cros_ec_sensorhub_driver = {
 	.driver = {
 		.name = DRV_NAME,
+		.pm = &cros_ec_sensorhub_ring_pm_ops,
 	},
 	.probe = cros_ec_sensorhub_probe,
+	.remove = cros_ec_sensorhub_remove,
 };
 
 module_platform_driver(cros_ec_sensorhub_driver);
diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
new file mode 100644
index 000000000000..8cd533d5542e
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cros_ec_sensorhub_ring - Driver for Chrome OS EC Sensor hub FIFO.
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#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/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_data/cros_ec_sensorhub.h>
+#include <linux/platform_device.h>
+#include <linux/sort.h>
+#include <linux/slab.h>
+
+
+static inline int cros_sensorhub_send_sample(
+		struct cros_ec_sensorhub *sensorhub,
+		struct cros_ec_sensors_ring_sample *sample)
+{
+	int id = sample->sensor_id;
+	cros_ec_sensorhub_push_data_cb_t cb;
+	struct iio_dev *indio_dev;
+
+	if (id > CROS_EC_SENSOR_MAX)
+		return -EINVAL;
+
+	cb = sensorhub->push_data[id].push_data_cb;
+	if (!cb)
+		return 0;
+
+	indio_dev = sensorhub->push_data[id].indio_dev;
+
+	return cb(indio_dev, sample->vector, sample->timestamp);
+}
+
+int cros_ec_sensorhub_register_push_data(
+		struct cros_ec_sensorhub *sensorhub,
+		u8 sensor_num,
+		struct iio_dev *indio_dev,
+		cros_ec_sensorhub_push_data_cb_t cb)
+{
+	if (sensor_num > CROS_EC_SENSOR_MAX)
+		return -EINVAL;
+	if (sensorhub->push_data[sensor_num].indio_dev != NULL)
+		return -EINVAL;
+
+	sensorhub->push_data[sensor_num].indio_dev = indio_dev;
+	sensorhub->push_data[sensor_num].push_data_cb = cb;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensorhub_register_push_data);
+
+void cros_ec_sensorhub_unregister_push_data(
+		struct cros_ec_sensorhub *sensorhub,
+		u8 sensor_num)
+{
+	sensorhub->push_data[sensor_num].indio_dev = NULL;
+	sensorhub->push_data[sensor_num].push_data_cb = NULL;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensorhub_unregister_push_data);
+
+int cros_ec_sensorhub_ring_fifo_toggle(
+		struct cros_ec_sensorhub *sensorhub,
+		bool on)
+{
+	int ret;
+
+	mutex_lock(&sensorhub->cmd_lock);
+	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE;
+	sensorhub->params->fifo_int_enable.enable = on;
+
+	sensorhub->msg->outsize = sizeof(struct ec_params_motion_sense);
+	sensorhub->msg->insize = sizeof(struct ec_response_motion_sense);
+
+	ret = cros_ec_cmd_xfer_status(sensorhub->ec->ec_dev, sensorhub->msg);
+	/* We expect to receive a payload of 4 bytes, ignore. */
+	if (ret > 0)
+		ret = 0;
+	mutex_unlock(&sensorhub->cmd_lock);
+	return ret;
+}
+
+/*
+ * 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 (includes b point, EC timebase).
+ * fifo_timestamp: EC IRQ, kernel timebase (aka c)
+ * current_timestamp: calculated event timestamp, kernel timebase (aka a')
+ * in: incoming FIFO event from EC (includes a point, EC timebase)
+ * out: outgoing event to user space (includes a')
+ */
+static bool cros_ec_ring_process_event(
+				struct cros_ec_sensorhub *sensorhub,
+				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;
+	/* Do not populate the filter based on asynchronous events. */
+	const int async_flags = in->flags &
+		(MOTIONSENSE_SENSOR_FLAG_ODR | MOTIONSENSE_SENSOR_FLAG_FLUSH);
+	const s64 now = cros_ec_get_time_ns();
+
+	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP && !async_flags) {
+		s64 a = in->timestamp;
+		s64 b = fifo_info->info.timestamp;
+		s64 c = fifo_timestamp;
+		s64 new_timestamp;
+
+		/*
+		 * disable filtering since we might add more jitter
+		 * if b is in a random point in time
+		 */
+		new_timestamp = c - b * 1000 + a * 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 = in->flags;
+		/*
+		 * No other payload information provided with
+		 * flush ack.
+		 */
+		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;
+	if (*current_timestamp - now > 0) {
+		/* If the timestamp is in the future. */
+		out->timestamp = now;
+	} else {
+		out->timestamp = *current_timestamp;
+	}
+	out->flag = in->flags;
+	for (axis = 0; axis < 3; axis++)
+		out->vector[axis] = in->data[axis];
+	return true;
+}
+
+/*
+ * cros_ec_sensorhub_ring_handler - the trigger handler function
+ *
+ * @sensorhub: device information.
+ *
+ * Called by the notifier, process the EC sensor FIFO queue.
+ */
+static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
+{
+	struct cros_ec_fifo_info *fifo_info = &sensorhub->fifo_info;
+	struct cros_ec_dev *ec = sensorhub->ec;
+	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;
+
+	mutex_lock(&sensorhub->cmd_lock);
+	/* Get FIFO information */
+	fifo_timestamp = sensorhub->fifo_timestamp[NEW_TS];
+	/* Copy elements in the main fifo */
+	if (fifo_info->info.total_lost) {
+		/* Need to retrieve the number of lost vectors per sensor */
+		sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
+		sensorhub->msg->outsize = 1;
+		sensorhub->msg->insize =
+			sizeof(struct ec_response_motion_sense_fifo_info) +
+			sizeof(u16) * CROS_EC_SENSOR_MAX;
+
+		if (cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg) < 0) {
+			mutex_unlock(&sensorhub->cmd_lock);
+			return;
+		}
+		memcpy(fifo_info, &sensorhub->resp->fifo_info,
+		       sizeof(*fifo_info));
+		fifo_timestamp = cros_ec_get_time_ns();
+	}
+	if (fifo_info->info.count > sensorhub->fifo_size ||
+	    fifo_info->info.size != sensorhub->fifo_size) {
+		dev_warn(sensorhub->dev,
+			 "Mismatch EC data: count %d, size %d - expected %d",
+			 fifo_info->info.count, fifo_info->info.size,
+			 sensorhub->fifo_size);
+		mutex_unlock(&sensorhub->cmd_lock);
+		return;
+	}
+
+	current_timestamp = sensorhub->fifo_timestamp[LAST_TS];
+	out = sensorhub->ring;
+	for (i = 0; i < fifo_info->info.count; i += number_data) {
+		sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_READ;
+		sensorhub->params->fifo_read.max_data_vector =
+			fifo_info->info.count - i;
+		sensorhub->msg->outsize =
+			sizeof(struct ec_params_motion_sense);
+		sensorhub->msg->insize =
+			sizeof(sensorhub->resp->fifo_read) +
+			sensorhub->params->fifo_read.max_data_vector *
+			  sizeof(struct ec_response_motion_sensor_data);
+		ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
+		if (ret < 0) {
+			dev_warn(sensorhub->dev, "Fifo error: %d\n", ret);
+			break;
+		}
+		number_data = sensorhub->resp->fifo_read.number_data;
+		if (number_data == 0) {
+			dev_dbg(sensorhub->dev, "Unexpected empty FIFO\n");
+			break;
+		} else if (number_data > fifo_info->info.count - i) {
+			dev_warn(sensorhub->dev,
+				 "Invalid EC data: too many entry received: %d, expected %d",
+				 number_data, fifo_info->info.count - i);
+			break;
+		} else if (out + number_data >
+			   sensorhub->ring + fifo_info->info.count) {
+			dev_warn(sensorhub->dev,
+				 "Too many samples: %d (%zd data) to %d entries for expected %d entries",
+				 i, out - sensorhub->ring, i + number_data,
+				 fifo_info->info.count);
+			break;
+		}
+		for (in = sensorhub->resp->fifo_read.data, j = 0;
+		     j < number_data; j++, in++) {
+			if (cros_ec_ring_process_event(
+					sensorhub, fifo_info, fifo_timestamp,
+					&current_timestamp, in, out)) {
+				sensor_mask |= (1 << in->sensor_num);
+				out++;
+			}
+		}
+	}
+	mutex_unlock(&sensorhub->cmd_lock);
+	last_out = out;
+
+	if (out == sensorhub->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;
+
+	/* Warn on lost samples. */
+	for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
+		if (fifo_info->info.total_lost) {
+			int lost = fifo_info->lost[i];
+
+			if (lost) {
+				dev_warn(sensorhub->dev,
+					"Sensor %d: lost: %d out of %d\n", i,
+					lost, fifo_info->info.total_lost);
+			}
+		}
+	}
+
+	/* push the event into the kfifo */
+	for (out = sensorhub->ring; out < last_out; out++)
+		cros_sensorhub_send_sample(sensorhub, out);
+
+ring_handler_end:
+	sensorhub->fifo_timestamp[LAST_TS] = current_timestamp;
+}
+
+static int cros_ec_sensorhub_event(struct notifier_block *nb,
+	unsigned long queued_during_suspend, void *_notify)
+{
+	struct cros_ec_sensorhub *sensorhub;
+	struct cros_ec_device *ec_dev;
+
+	sensorhub = container_of(nb, struct cros_ec_sensorhub, notifier);
+	ec_dev = sensorhub->ec->ec_dev;
+
+	if (ec_dev->event_data.event_type != EC_MKBP_EVENT_SENSOR_FIFO)
+		return NOTIFY_DONE;
+
+	if (ec_dev->event_size != sizeof(ec_dev->event_data.data.sensor_fifo)) {
+		dev_warn(ec_dev->dev, "Invalid fifo info size\n");
+		return NOTIFY_DONE;
+	}
+
+	if (queued_during_suspend)
+		return NOTIFY_OK;
+
+	sensorhub->fifo_info.info = ec_dev->event_data.data.sensor_fifo.info;
+	sensorhub->fifo_timestamp[NEW_TS] = ec_dev->last_event_time;
+	cros_ec_sensorhub_ring_handler(sensorhub);
+	return NOTIFY_OK;
+}
+
+int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
+{
+	struct cros_ec_dev *ec = sensorhub->ec;
+	int ret;
+
+	/* Retrieve FIFO information */
+	sensorhub->msg->version = 2;
+	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
+	sensorhub->msg->outsize = 1;
+	sensorhub->msg->insize =
+		sizeof(struct ec_response_motion_sense_fifo_info) +
+		sizeof(u16) * CROS_EC_SENSOR_MAX;
+
+	ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Allocate the full fifo.
+	 * We need to copy the whole FIFO to set timestamps properly *
+	 */
+	sensorhub->fifo_size = sensorhub->resp->fifo_info.size;
+	sensorhub->ring = devm_kcalloc(sensorhub->dev, sensorhub->fifo_size,
+			sizeof(*sensorhub->ring), GFP_KERNEL);
+	if (!sensorhub->ring)
+		return -ENOMEM;
+
+	sensorhub->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
+
+	/* register the notifier that will act as a top half interrupt. */
+	sensorhub->notifier.notifier_call = cros_ec_sensorhub_event;
+	ret = blocking_notifier_chain_register(&ec->ec_dev->event_notifier,
+					       &sensorhub->notifier);
+	if (ret < 0) {
+		dev_warn(sensorhub->dev, "failed to register notifier\n");
+		return ret;
+	}
+
+	/*
+	 * Start collection samples.
+	 */
+	ret = cros_ec_sensorhub_ring_fifo_toggle(sensorhub, true);
+	return ret;
+}
+
+int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensorhub)
+{
+	struct cros_ec_device *ec_dev = sensorhub->ec->ec_dev;
+
+	/*
+	 * Disable the ring, prevent EC interrupt to the AP for nothing.
+	 */
+	cros_ec_sensorhub_ring_fifo_toggle(sensorhub, false);
+	blocking_notifier_chain_unregister(&ec_dev->event_notifier,
+					   &sensorhub->notifier);
+	return 0;
+}
+
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index bb331e6356a9..abef66d0e884 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -116,6 +116,9 @@ struct platform_device;
 int cros_ec_sensors_core_init(struct platform_device *pdev,
 			      struct iio_dev *indio_dev, bool physical_device);
 
+/* To remove association of physical device to cros_ec_sensorhub. */
+int cros_ec_sensors_core_clean(struct platform_device *pdev);
+
 /**
  * cros_ec_sensors_capture() - the trigger handler function
  * @irq:	the interrupt number.
diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
index 9295eabb16f6..18cda568c58a 100644
--- a/include/linux/platform_data/cros_ec_sensorhub.h
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -9,6 +9,7 @@
 #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
 #define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
 
+#include <linux/iio/iio.h>
 #include <linux/platform_data/cros_ec_commands.h>
 
 /**
@@ -19,11 +20,89 @@ struct cros_ec_sensor_platform {
 	u8 sensor_num;
 };
 
+/**
+ * Callback function to send datum to specific sensors.
+ */
+typedef int (*cros_ec_sensorhub_push_data_cb_t)(
+		struct iio_dev *indio_dev,
+		s16 *data,
+		s64 timestamp);
+
+struct cros_ec_sensorhub_sensor_push_data {
+	struct iio_dev *indio_dev;
+	cros_ec_sensorhub_push_data_cb_t push_data_cb;
+};
+
+enum {
+	LAST_TS,
+	NEW_TS,
+	ALL_TS
+};
+
+#define CROS_EC_SENSOR_MAX 16
+
+struct __ec_todo_packed 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[3];
+	s64      timestamp;
+} __packed;
+
 /**
  * struct cros_ec_sensorhub - Sensor Hub device data.
  */
 struct cros_ec_sensorhub {
+	struct device *dev;
 	struct cros_ec_dev *ec;
+
+	/* Structure to send FIFO requests. */
+	struct cros_ec_command *msg;
+	struct ec_params_motion_sense *params;
+	struct ec_response_motion_sense *resp;
+	struct mutex cmd_lock;
+
+	/* Notifier to kick the FIFO interrupt */
+	struct notifier_block notifier;
+
+	/* Preprocessed ring to send to kfifos */
+	struct cros_ec_sensors_ring_sample *ring;
+
+	s64    fifo_timestamp[ALL_TS];
+	struct cros_ec_fifo_info fifo_info;
+	int    fifo_size;
+
+	/*
+	 * Dynamic array to be able to spread datum to iio sensor objects.
+	 */
+	struct cros_ec_sensorhub_sensor_push_data push_data[CROS_EC_SENSOR_MAX];
 };
 
+/**
+ * Function to register the callback to the sensor hub.
+ * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
+ * from the EC.
+ */
+int cros_ec_sensorhub_register_push_data(
+		struct cros_ec_sensorhub *sensor_hub,
+		u8 sensor_num,
+		struct iio_dev *indio_dev,
+		cros_ec_sensorhub_push_data_cb_t cb);
+void cros_ec_sensorhub_unregister_push_data(
+		struct cros_ec_sensorhub *sensor_hub,
+		u8 sensor_num);
+
+/**
+ * Add/Remove the fifo functionality if the EC supports it.
+ */
+int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensor_hub);
+int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensor_hub);
+int cros_ec_sensorhub_ring_fifo_toggle(
+		struct cros_ec_sensorhub *sensorhub,
+		bool on);
+
 #endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 08/13] platform: chrome: sensorhub: Add code to spread timestmap
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (6 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 07/13] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 16:16   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 09/13] platform: chrome: sensorhub: Add median filter Gwendal Grignou
                   ` (5 subsequent siblings)
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

EC FIFO can send sensor events in batch. Spread them based on
previous (TSa) and currnet timestamp (TSb)

  EC FIFO                             iio events
+-----------+
| TSa       |
+-----------+             +---------------------------------------+
| event 1   |             | event 1 | TSb - (TSb - TSa)/n * (n-1) |
+-----------+             +---------------------------------------+
| event 2   |             | event 2 | TSb - (TSb - TSa)/n * (n-2) |
+-----------+             +---------------------------------------+
|  ...      |  ------>    |  ....   |                             |
+-----------+             +---------------------------------------+
| event n-1 |             | event 2 | TSb - (TSb - TSa)/n         |
+-----------+             +---------------------------------------+
| event n   |             | event 2 | TSb                         |
+-----------+             +---------------------------------------+
| TSb       |
+-----------+

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 .../platform/chrome/cros_ec_sensorhub_ring.c  | 99 ++++++++++++++++++-
 1 file changed, 96 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
index 8cd533d5542e..48327e80a5a3 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -160,6 +160,97 @@ static bool cros_ec_ring_process_event(
 	return true;
 }
 
+/*
+ * cros_ec_ring_spread_add: Calculate proper timestamps then
+ * add to ringbuffer (legacy).
+ *
+ * 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]
+ */
+static void cros_ec_ring_spread_add(
+				struct cros_ec_sensorhub *sensorhub,
+				unsigned long sensor_mask,
+				s64 current_timestamp,
+				struct cros_ec_sensors_ring_sample *last_out)
+{
+	struct cros_ec_sensors_ring_sample *out;
+	int i;
+
+	for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
+		s64 older_timestamp;
+		s64 timestamp;
+		struct cros_ec_sensors_ring_sample *older_unprocess_out =
+			sensorhub->ring;
+		struct cros_ec_sensors_ring_sample *next_out;
+		int count = 1;
+
+		for (out = sensorhub->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 = sensorhub->ring; out < last_out; out++)
+		cros_sensorhub_send_sample(sensorhub, out);
+}
+
 /*
  * cros_ec_sensorhub_ring_handler - the trigger handler function
  *
@@ -280,9 +371,11 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
 		}
 	}
 
-	/* push the event into the kfifo */
-	for (out = sensorhub->ring; out < last_out; out++)
-		cros_sensorhub_send_sample(sensorhub, out);
+	/*
+	 * Spread samples in case of batching, then add them to the ringbuffer.
+	 */
+	cros_ec_ring_spread_add(sensorhub, sensor_mask,
+			current_timestamp, last_out);
 
 ring_handler_end:
 	sensorhub->fifo_timestamp[LAST_TS] = current_timestamp;
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 09/13] platform: chrome: sensorhub: Add median filter
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (7 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 08/13] platform: chrome: sensorhub: Add code to spread timestmap Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 16:24   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 10/13] iio: cros_ec: Use triggered buffer only when EC does not support FIFO Gwendal Grignou
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

Events are timestamped in EC time space, their timestamps need to be
converted in host time space.
The assumption is the time delta between when the interrupt is sent
by the EC and when it is receive by the host is a [small] constant.
This is not always true, even with hard-wired interrupt. To mitigate
worst offenders, add a median filter to weed out bigger than expected
delays.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 .../platform/chrome/cros_ec_sensorhub_ring.c  | 485 +++++++++++++++++-
 .../linux/platform_data/cros_ec_sensorhub.h   |  65 +++
 2 files changed, 533 insertions(+), 17 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
index 48327e80a5a3..9955c80d0907 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -69,9 +69,11 @@ int cros_ec_sensorhub_ring_fifo_toggle(
 		struct cros_ec_sensorhub *sensorhub,
 		bool on)
 {
-	int ret;
+	int ret, i;
 
 	mutex_lock(&sensorhub->cmd_lock);
+	for (i = 0; i < CROS_EC_SENSOR_MAX; i++)
+		sensorhub->last_batch_len[i] = 0;
 	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE;
 	sensorhub->params->fifo_int_enable.enable = on;
 
@@ -86,6 +88,227 @@ int cros_ec_sensorhub_ring_fifo_toggle(
 	return ret;
 }
 
+static int cros_ec_ring_median_cmp(const void *pv1, const void *pv2)
+{
+	s64 v1 = *(s64 *)pv1;
+	s64 v2 = *(s64 *)pv2;
+
+	if (v1 > v2)
+		return 1;
+	else if (v1 < v2)
+		return -1;
+	else
+		return 0;
+}
+
+/*
+ * cros_ec_ring_median: Gets median of an array of numbers
+ *
+ * For now it's implemented using an inefficient > O(n) sort then return
+ * the middle element. A more optimal method would be something like
+ * quickselect, but given that n = 64 we can probably live with it in the
+ * name of clarity.
+ *
+ * Warning: the input array gets modified (sorted)!
+ */
+static s64 cros_ec_ring_median(s64 *array, size_t length)
+{
+	sort(array, length, sizeof(s64), cros_ec_ring_median_cmp, NULL);
+	return array[length / 2];
+}
+
+/*
+ * IRQ Timestamp Filtering
+ *
+ * Lower down in cros_ec_ring_process_event(), for each sensor event we have to
+ * calculate it's timestamp in the AP timebase. There are 3 time points:
+ *   a - EC timebase, sensor event
+ *   b - EC timebase, IRQ
+ *   c - AP timebase, IRQ
+ *   a' - what we want: sensor even in AP timebase
+ *
+ * While a and b are recorded at accurate times (due to the EC real time
+ * nature); c is pretty untrustworthy, even though it's recorded the
+ * first thing in ec_irq_handler(). There is a very good change we'll get
+ * added lantency due to:
+ *   other irqs
+ *   ddrfreq
+ *   cpuidle
+ *
+ * Normally a' = c - b + a, but if we do that naive math any jitter in c
+ * will get coupled in a', which we don't want. We want a function
+ * a' = cros_ec_ring_ts_filter(a) which will filter out outliers in c.
+ *
+ * Think of a graph of AP time(b) on the y axis vs EC time(c) on the x axis.
+ * The slope of the line won't be exactly 1, there will be some clock drift
+ * between the 2 chips for various reasons (mechanical stress, temperature,
+ * voltage). We need to extrapolate values for a future x, without trusting
+ * recent y values too much.
+ *
+ * We use a median filter for the slope, then another median filter for the
+ * y-intercept to calculate this function:
+ *   dx[n] = x[n-1] - x[n]
+ *   dy[n] = x[n-1] - x[n]
+ *   m[n] = dy[n] / dx[n]
+ *   median_m = median(m[n-k:n])
+ *   error[i] = y[n-i] - median_m * x[n-i]
+ *   median_error = median(error[:k])
+ *   predicted_y = median_m * x + median_error
+ *
+ * Implementation differences from above:
+ * - Redefined y to be actually c - b, this gives us a lot more precision
+ * to do the math. (c-b)/b variations are more obvious than c/b variations.
+ * - Since we don't have floating point, any operations involving slope are
+ * done using fixed point math (*M_PRECISION)
+ * - Since x and y grow with time, we keep zeroing the graph (relative to
+ * the last sample), this way math involving *x[n-i] will not overflow
+ * - EC timestamps are kept in us, it improves the slope calculation precision
+ */
+
+/*
+ * cros_ec_ring_ts_filter_update: Given a new IRQ timestamp pair (EC and
+ * AP timebases), add it to the filter history.
+ *
+ * @b IRQ timestamp, EC timebase (us)
+ * @c IRQ timestamp, AP timebase (ns)
+ */
+static void cros_ec_ring_ts_filter_update(
+			struct cros_ec_sensors_ts_filter_state *state,
+			s64 b, s64 c)
+{
+	s64 x, y;
+	s64 dx, dy;
+	s64 m; /* stored as *M_PRECISION */
+	s64 *m_history_copy = state->temp_buf;
+	s64 *error = state->temp_buf;
+	int i;
+
+	/* we trust b the most, that'll be our independent variable */
+	x = b;
+	/* y is the offset between AP and EC times, in ns */
+	y = c - b * 1000;
+
+	dx = (state->x_history[0] + state->x_offset) - x;
+	if (dx == 0)
+		return; /* we already have this irq in the history */
+	dy = (state->y_history[0] + state->y_offset) - y;
+	m = div64_s64(dy * M_PRECISION, dx);
+
+	/* Empty filter if we haven't seen any action in a while. */
+	if (-dx > TS_HISTORY_BORED_US)
+		state->history_len = 0;
+
+	/* Move everything over, also update offset to all absolute coords .*/
+	for (i = state->history_len - 1; i >= 1; i--) {
+		state->x_history[i] = state->x_history[i-1] + dx;
+		state->y_history[i] = state->y_history[i-1] + dy;
+
+		state->m_history[i] = state->m_history[i-1];
+		/*
+		 * Also use the same loop to copy m_history for future
+		 * median extraction.
+		 */
+		m_history_copy[i] = state->m_history[i-1];
+	}
+
+	/* Store the x and y, but remember offset is actually last sample. */
+	state->x_offset = x;
+	state->y_offset = y;
+	state->x_history[0] = 0;
+	state->y_history[0] = 0;
+
+	state->m_history[0] = m;
+	m_history_copy[0] = m;
+
+	if (state->history_len < TS_HISTORY_SIZE)
+		state->history_len++;
+
+	/* Precalculate things for the filter. */
+	if (state->history_len > TS_HISTORY_THRESHOLD) {
+		state->median_m =
+		    cros_ec_ring_median(m_history_copy, state->history_len - 1);
+
+		/*
+		 * Calculate y-intercepts as if m_median is the slope and
+		 * points in the history are on the line. median_error will
+		 * still be in the offset coordinate system.
+		 */
+		for (i = 0; i < state->history_len; i++)
+			error[i] = state->y_history[i] -
+				div_s64(state->median_m * state->x_history[i],
+					M_PRECISION);
+		state->median_error =
+			cros_ec_ring_median(error, state->history_len);
+	} else {
+		state->median_m = 0;
+		state->median_error = 0;
+	}
+}
+
+/*
+ * cros_ec_ring_ts_filter: Translate EC timebase timestamp to AP timebase
+ *
+ * @x any ec timestamp (us):
+ *
+ * cros_ec_ring_ts_filter(a) => a' event timestamp, AP timebase
+ * cros_ec_ring_ts_filter(b) => calculated timestamp when the EC IRQ
+ *                           should have happened on the AP, with low jitter
+ *
+ * @returns timestamp in AP timebase (ns)
+ *
+ * Note: The filter will only activate once state->history_len goes
+ * over TS_HISTORY_THRESHOLD. Otherwise it'll just do the naive c - b + a
+ * transform.
+ *
+ * How to derive the formula, starting from:
+ *   f(x) = median_m * x + median_error
+ * That's the calculated AP - EC offset (at the x point in time)
+ * Undo the coordinate system transform:
+ *   f(x) = median_m * (x - x_offset) + median_error + y_offset
+ * Remember to undo the "y = c - b * 1000" modification:
+ *   f(x) = median_m * (x - x_offset) + median_error + y_offset + x * 1000
+ */
+static s64 cros_ec_ring_ts_filter(struct cros_ec_sensors_ts_filter_state *state,
+				  s64 x)
+{
+	return div_s64(state->median_m * (x - state->x_offset), M_PRECISION)
+	       + state->median_error + state->y_offset + x * 1000;
+}
+
+/*
+ * Since a and b were originally 32 bit values from the EC,
+ * they overflow relatively often, casting is not enough, so we need to
+ * add an offset.
+ */
+static void cros_ec_ring_fix_overflow(s64 *ts,
+		const s64 overflow_period,
+		struct cros_ec_sensors_ec_overflow_state *state)
+{
+	s64 adjust;
+
+	*ts += state->offset;
+	if (abs(state->last - *ts) > (overflow_period / 2)) {
+		adjust = state->last > *ts ? overflow_period : -overflow_period;
+		state->offset += adjust;
+		*ts += adjust;
+	}
+	state->last = *ts;
+}
+
+static void cros_ec_ring_check_for_past_timestamp(
+				struct cros_ec_sensorhub *sensorhub,
+				struct cros_ec_sensors_ring_sample *sample)
+{
+	const u8 sensor_id = sample->sensor_id;
+
+	// if this event is earlier than one we saw before...
+	if (sensorhub->newest_sensor_event[sensor_id] > sample->timestamp)
+		// mark it for spreading
+		sample->timestamp = sensorhub->last_batch_timestamp[sensor_id];
+	else
+		sensorhub->newest_sensor_event[sensor_id] = sample->timestamp;
+}
+
 /*
  * cros_ec_ring_process_event: process one EC FIFO event
  *
@@ -117,25 +340,47 @@ static bool cros_ec_ring_process_event(
 		s64 a = in->timestamp;
 		s64 b = fifo_info->info.timestamp;
 		s64 c = fifo_timestamp;
-		s64 new_timestamp;
 
+		cros_ec_ring_fix_overflow(&a, 1LL << 32,
+				&sensorhub->overflow_a);
+		cros_ec_ring_fix_overflow(&b, 1LL << 32,
+				&sensorhub->overflow_b);
+
+		if (sensorhub->tight_timestamps) {
+			cros_ec_ring_ts_filter_update(&sensorhub->filter, b, c);
+			*current_timestamp =
+				cros_ec_ring_ts_filter(&sensorhub->filter, a);
+		} else {
+			s64 new_timestamp;
+			/*
+			 * disable filtering since we might add more jitter
+			 * if b is in a random point in time
+			 */
+			new_timestamp = c - b * 1000 + a * 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_ODR) {
+		sensorhub->last_batch_len[in->sensor_num] =
+			sensorhub->penultimate_batch_len[in->sensor_num] = 0;
 		/*
-		 * disable filtering since we might add more jitter
-		 * if b is in a random point in time
-		 */
-		new_timestamp = c - b * 1000 + a * 1000;
-		/*
-		 * The timestamp can be stale if we had to use the fifo
-		 * info timestamp.
+		 * ODR change is only useful for the sensor_ring, it does not
+		 * convey information to clients.
 		 */
-		if (new_timestamp - *current_timestamp > 0)
-			*current_timestamp = new_timestamp;
+		return false;
 	}
 
 	if (in->flags & MOTIONSENSE_SENSOR_FLAG_FLUSH) {
 		out->sensor_id = in->sensor_num;
 		out->timestamp = *current_timestamp;
 		out->flag = in->flags;
+		sensorhub->last_batch_len[out->sensor_id] = 0;
 		/*
 		 * No other payload information provided with
 		 * flush ack.
@@ -149,7 +394,22 @@ static bool cros_ec_ring_process_event(
 	/* Regular sample */
 	out->sensor_id = in->sensor_num;
 	if (*current_timestamp - now > 0) {
-		/* If the timestamp is in the future. */
+		/*
+		 * This fix is needed to overcome the timestamp filter putting
+		 * events in the future.
+		 */
+		sensorhub->future_timestamp_total_ns +=
+			*current_timestamp - now;
+		if (++sensorhub->future_timestamp_count ==
+				FUTURE_TS_ANALYTICS_COUNT_MAX) {
+			s64 avg = div_s64(sensorhub->future_timestamp_total_ns,
+					sensorhub->future_timestamp_count);
+			dev_warn(sensorhub->dev,
+					"100 timestamps in the future, %lldns shaved on average\n",
+					avg);
+			sensorhub->future_timestamp_count = 0;
+			sensorhub->future_timestamp_total_ns = 0;
+		}
 		out->timestamp = now;
 	} else {
 		out->timestamp = *current_timestamp;
@@ -157,13 +417,195 @@ static bool cros_ec_ring_process_event(
 	out->flag = in->flags;
 	for (axis = 0; axis < 3; axis++)
 		out->vector[axis] = in->data[axis];
+	if (sensorhub->tight_timestamps)
+		cros_ec_ring_check_for_past_timestamp(sensorhub, out);
 	return true;
 }
 
 /*
- * cros_ec_ring_spread_add: Calculate proper timestamps then
+ * cros_ec_ring_spread_add: Calculate proper timestamps then add to ringbuffer.
+ *
+ * Note: This is the new spreading code, assumes every sample's timestamp
+ * preceeds the sample. Run if tight_timestamps == true.
+ *
+ * Sometimes the EC receives only one interrupt (hence timestamp) for
+ * a batch of samples. Only the first sample will have the correct
+ * timestamp. So we must interpolate the other samples.
+ * We use the previous batch timestamp and our current batch timestamp
+ * as a way to calculate period, then spread the samples evenly.
+ *
+ * s0 int, 0ms
+ * s1 int, 10ms
+ * s2 int, 20ms
+ * 30ms point goes by, no interrupt, previous one is still asserted
+ * downloading s2 and s3
+ * s3 sample, 20ms (incorrect timestamp)
+ * s4 int, 40ms
+ *
+ * The batches are [(s0), (s1), (s2, s3), (s4)]. Since the 3rd batch
+ * has 2 samples in them, we adjust the timestamp of s3.
+ * s2 - s1 = 10ms, so s3 must be s2 + 10ms => 20ms. If s1 would have
+ * been part of a bigger batch things would have gotten a little
+ * more complicated.
+ *
+ * Note: we also assume another sensor sample doesn't break up a batch
+ * in 2 or more partitions. Example, there can't ever be a sync sensor
+ * in between S2 and S3. This simplifies the following code.
+ */
+static void cros_ec_ring_spread_add(
+				struct cros_ec_sensorhub *sensorhub,
+				unsigned long sensor_mask,
+				struct cros_ec_sensors_ring_sample *last_out)
+{
+	struct cros_ec_sensors_ring_sample *batch_start, *next_batch_start;
+	int id;
+
+	for_each_set_bit(id, &sensor_mask, BITS_PER_LONG) {
+		for (batch_start = sensorhub->ring; batch_start < last_out;
+		     batch_start = next_batch_start) {
+			/*
+			 * For each batch (where all samples have the same
+			 * timestamp).
+			 */
+			int batch_len, sample_idx;
+			struct cros_ec_sensors_ring_sample *batch_end =
+				batch_start;
+			struct cros_ec_sensors_ring_sample *s;
+			s64 batch_timestamp = batch_start->timestamp;
+			s64 sample_period;
+
+			/*
+			 * Skip over batches that start with the sensor types
+			 * we're not looking at right now.
+			 */
+			if (batch_start->sensor_id != id) {
+				next_batch_start = batch_start + 1;
+				continue;
+			}
+
+			/*
+			 * TODO(gwendal): can not send out flush packets
+			 * anymore.
+			 * Do not start a batch
+			 * from a flush, as it happens asynchronously to the
+			 * regular flow of events.
+			 */
+			if (batch_start->flag &
+				MOTIONSENSE_SENSOR_FLAG_FLUSH) {
+				next_batch_start = batch_start + 1;
+				continue;
+			}
+
+			if (batch_start->timestamp <=
+				sensorhub->last_batch_timestamp[id]) {
+
+				batch_timestamp =
+					sensorhub->last_batch_timestamp[id];
+				batch_len = sensorhub->last_batch_len[id];
+
+				sample_idx = batch_len;
+
+				sensorhub->last_batch_timestamp[id] =
+				  sensorhub->penultimate_batch_timestamp[id];
+				sensorhub->last_batch_len[id] =
+				  sensorhub->penultimate_batch_len[id];
+			} else {
+				/*
+				 * Push first sample in the batch to the,
+				 * kifo, it's guaranteed to be correct, the
+				 * rest will follow later on.
+				 */
+				sample_idx = batch_len = 1;
+				cros_sensorhub_send_sample(
+						sensorhub, batch_start);
+				batch_start++;
+			}
+
+			/* Find all samples have the same timestamp. */
+			for (s = batch_start; s < last_out; s++) {
+				if (s->sensor_id != id)
+					/*
+					 * Skip over other sensor types that
+					 * are interleaved, don't count them.
+					 */
+					continue;
+				if (s->timestamp != batch_timestamp)
+					/* we discovered the next batch */
+					break;
+				if (s->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
+					/* break on flush packets */
+					break;
+				batch_end = s;
+				batch_len++;
+			}
+
+			if (batch_len == 1)
+				goto done_with_this_batch;
+
+			/* Can we calculate period? */
+			if (sensorhub->last_batch_len[id] == 0) {
+				dev_warn(sensorhub->dev, "Sensor %d: lost %d samples when spreading\n",
+						id, batch_len - 1);
+				goto done_with_this_batch;
+				/*
+				 * Note: we're dropping the rest of the samples
+				 * in this batch since we have no idea where
+				 * they're supposed to go without a period
+				 * calculation.
+				 */
+			}
+
+			sample_period = div_s64(batch_timestamp -
+					sensorhub->last_batch_timestamp[id],
+					sensorhub->last_batch_len[id]);
+			dev_dbg(sensorhub->dev,
+					"Adjusting %d samples, sensor %d last_batch @%lld (%d samples) batch_timestamp=%lld => period=%lld\n",
+					batch_len, id,
+					sensorhub->last_batch_timestamp[id],
+					sensorhub->last_batch_len[id],
+					batch_timestamp,
+					sample_period);
+
+			/*
+			 * Adjust timestamps of the samples then push them to
+			 * kfifo.
+			 */
+			for (s = batch_start; s <= batch_end; s++) {
+				if (s->sensor_id != id)
+					/*
+					 * Skip over other sensor types that
+					 * are interleaved, don't change them.
+					 */
+					continue;
+
+				s->timestamp = batch_timestamp +
+					sample_period * sample_idx;
+				sample_idx++;
+
+				cros_sensorhub_send_sample(sensorhub, s);
+			}
+
+done_with_this_batch:
+			sensorhub->penultimate_batch_timestamp[id] =
+				sensorhub->last_batch_timestamp[id];
+			sensorhub->penultimate_batch_len[id] =
+				sensorhub->last_batch_len[id];
+
+			sensorhub->last_batch_timestamp[id] = batch_timestamp;
+			sensorhub->last_batch_len[id] = batch_len;
+
+			next_batch_start = batch_end + 1;
+		}
+	}
+}
+
+/*
+ * cros_ec_ring_spread_add_legacy: Calculate proper timestamps then
  * add to ringbuffer (legacy).
  *
+ * Note: This assumes we're running old firmware, where every sample's timestamp
+ * is after the sample. Run if tight_timestamps == false.
+ *
  * If there is a sample with a proper timestamp
  *                        timestamp | count
  * older_unprocess_out --> TS1      | 1
@@ -181,7 +623,7 @@ static bool cros_ec_ring_process_event(
  * out -->                 TS1      | 3
  * We know have [TS1+1/3, TS1+2/3, current timestamp]
  */
-static void cros_ec_ring_spread_add(
+static void cros_ec_ring_spread_add_legacy(
 				struct cros_ec_sensorhub *sensorhub,
 				unsigned long sensor_mask,
 				s64 current_timestamp,
@@ -355,7 +797,8 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
 	 * 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)
+	if (!sensorhub->tight_timestamps &&
+	    (last_out-1)->timestamp == current_timestamp)
 		current_timestamp = fifo_timestamp;
 
 	/* Warn on lost samples. */
@@ -367,6 +810,7 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
 				dev_warn(sensorhub->dev,
 					"Sensor %d: lost: %d out of %d\n", i,
 					lost, fifo_info->info.total_lost);
+				sensorhub->last_batch_len[i] = 0;
 			}
 		}
 	}
@@ -374,8 +818,11 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
 	/*
 	 * Spread samples in case of batching, then add them to the ringbuffer.
 	 */
-	cros_ec_ring_spread_add(sensorhub, sensor_mask,
-			current_timestamp, last_out);
+	if (sensorhub->tight_timestamps)
+		cros_ec_ring_spread_add(sensorhub, sensor_mask, last_out);
+	else
+		cros_ec_ring_spread_add_legacy(sensorhub, sensor_mask,
+					       current_timestamp, last_out);
 
 ring_handler_end:
 	sensorhub->fifo_timestamp[LAST_TS] = current_timestamp;
@@ -436,6 +883,10 @@ int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
 
 	sensorhub->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
 
+	sensorhub->tight_timestamps = cros_ec_check_features(ec,
+		EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS);
+
+
 	/* register the notifier that will act as a top half interrupt. */
 	sensorhub->notifier.notifier_call = cros_ec_sensorhub_event;
 	ret = blocking_notifier_chain_register(&ec->ec_dev->event_notifier,
diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
index 18cda568c58a..df67f2015da9 100644
--- a/include/linux/platform_data/cros_ec_sensorhub.h
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -53,6 +53,42 @@ struct cros_ec_sensors_ring_sample {
 	s64      timestamp;
 } __packed;
 
+/* State used for cros_ec_ring_fix_overflow */
+struct cros_ec_sensors_ec_overflow_state {
+	s64 offset;
+	s64 last;
+};
+
+/* Precision of fixed point for the m values from the filter */
+#define M_PRECISION (1 << 23)
+
+/* Length of the filter, how long to remember entries for */
+#define TS_HISTORY_SIZE 64
+
+/* Only activate the filter once we have at least this many elements. */
+#define TS_HISTORY_THRESHOLD 8
+
+/*
+ * If we don't have any history entries for this long, empty the filter to
+ * make sure there are no big discontinuities.
+ */
+#define TS_HISTORY_BORED_US 500000
+
+struct cros_ec_sensors_ts_filter_state {
+	s64 x_offset, y_offset;
+	s64 x_history[TS_HISTORY_SIZE]; /* stored relative to x_offset */
+	s64 y_history[TS_HISTORY_SIZE]; /* stored relative to y_offset */
+	s64 m_history[TS_HISTORY_SIZE]; /* stored as *M_PRECISION */
+	int history_len;
+
+	s64 temp_buf[TS_HISTORY_SIZE];
+
+	s64 median_m;
+	s64 median_error;
+};
+
+#define FUTURE_TS_ANALYTICS_COUNT_MAX 100
+
 /**
  * struct cros_ec_sensorhub - Sensor Hub device data.
  */
@@ -76,6 +112,35 @@ struct cros_ec_sensorhub {
 	struct cros_ec_fifo_info fifo_info;
 	int    fifo_size;
 
+	/* Used for timestamp spreading calculations when a batch shows up */
+	s64 penultimate_batch_timestamp[CROS_EC_SENSOR_MAX];
+	int penultimate_batch_len[CROS_EC_SENSOR_MAX];
+	s64 last_batch_timestamp[CROS_EC_SENSOR_MAX];
+	int last_batch_len[CROS_EC_SENSOR_MAX];
+	s64 newest_sensor_event[CROS_EC_SENSOR_MAX];
+
+	struct cros_ec_sensors_ec_overflow_state overflow_a;
+	struct cros_ec_sensors_ec_overflow_state overflow_b;
+
+	struct cros_ec_sensors_ts_filter_state filter;
+
+	/*
+	 * The timestamps reported from the EC have low jitter.
+	 * Timestamps also come before every sample.
+	 * Set either by feature bits coming from the EC or userspace.
+	 */
+	bool tight_timestamps;
+
+	/*
+	 * Statistics used to compute shaved time. This occures when
+	 * timestamp interpolation from EC time to AP time accidentally
+	 * puts timestamps in the future. These timestamps are clamped
+	 * to `now` and these count/total_ns maintain the statistics for
+	 * how much time was removed in a given period..
+	 */
+	s32 future_timestamp_count;
+	s64 future_timestamp_total_ns;
+
 	/*
 	 * Dynamic array to be able to spread datum to iio sensor objects.
 	 */
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 10/13] iio: cros_ec: Use triggered buffer only when EC does not support FIFO
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (8 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 09/13] platform: chrome: sensorhub: Add median filter Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 16:30   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 11/13] iio: cros_ec: Expose hwfifo_timeout Gwendal Grignou
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

When EC supports FIFO, the samples will flow from the kernel by
themselves.
When no FIFO, the user space app needs to call trigger_new, or better
register a high precision timer.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 drivers/iio/accel/cros_ec_accel_legacy.c      |  8 +--
 .../cros_ec_sensors/cros_ec_lid_angle.c       |  2 +-
 .../common/cros_ec_sensors/cros_ec_sensors.c  |  8 +--
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 61 +++++++++++++++++--
 drivers/iio/light/cros_ec_light_prox.c        |  8 +--
 drivers/iio/pressure/cros_ec_baro.c           |  8 +--
 .../linux/iio/common/cros_ec_sensors_core.h   | 16 ++++-
 7 files changed, 80 insertions(+), 31 deletions(-)

diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
index 591c0d962c44..d607fbc52c95 100644
--- a/drivers/iio/accel/cros_ec_accel_legacy.c
+++ b/drivers/iio/accel/cros_ec_accel_legacy.c
@@ -179,7 +179,8 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
 	if (!indio_dev)
 		return -ENOMEM;
 
-	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
+			cros_ec_sensors_capture, cros_ec_sensors_push_data);
 	if (ret)
 		return ret;
 
@@ -199,11 +200,6 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
 		state->sign[CROS_EC_SENSOR_Z] = -1;
 	}
 
-	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
-			cros_ec_sensors_capture, NULL);
-	if (ret)
-		return ret;
-
 	return devm_iio_device_register(dev, indio_dev);
 }
 
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
index 1dcc2a16ab2d..e30a59fcf0f9 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
@@ -97,7 +97,7 @@ static int cros_ec_lid_angle_probe(struct platform_device *pdev)
 	if (!indio_dev)
 		return -ENOMEM;
 
-	ret = cros_ec_sensors_core_init(pdev, indio_dev, false);
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, false, NULL, NULL);
 	if (ret)
 		return ret;
 
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index a88dd8deade9..b78a942ac8e5 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -239,7 +239,8 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
 	if (!indio_dev)
 		return -ENOMEM;
 
-	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
+			cros_ec_sensors_capture, cros_ec_sensors_push_data);
 	if (ret)
 		return ret;
 
@@ -301,11 +302,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
 	else
 		state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
 
-	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
-			cros_ec_sensors_capture, NULL);
-	if (ret)
-		return ret;
-
 	return devm_iio_device_register(dev, indio_dev);
 }
 
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
index 43eb1d42820e..c4c37c6df301 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -12,6 +12,7 @@
 #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/module.h>
@@ -83,15 +84,50 @@ static void get_default_min_max_freq(enum motionsensor_type type,
 	}
 }
 
+int cros_ec_sensors_push_data(
+		struct iio_dev *indio_dev,
+		s16 *data,
+		s64 timestamp)
+{
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	s16 *out;
+	unsigned int i;
+
+	/*
+	 * It can happen if we get a samples before the iio device is fully
+	 * registered.
+	 */
+	if (!st)
+		return 0;
+
+	/* Ignore samples if the buffer is not set. */
+	if (!indio_dev->active_scan_mask)
+		return 0;
+
+	out = (s16 *)st->samples;
+	for_each_set_bit(i,
+			 indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		*out = data[i];
+		out++;
+	}
+	iio_push_to_buffers_with_timestamp(indio_dev, st->samples, timestamp);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_push_data);
+
 int cros_ec_sensors_core_init(struct platform_device *pdev,
 			      struct iio_dev *indio_dev,
-			      bool physical_device)
+			      bool physical_device,
+			      cros_ec_sensors_capture_t trigger_capture,
+			      cros_ec_sensorhub_push_data_cb_t push_data)
 {
 	struct device *dev = &pdev->dev;
 	struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
 	struct cros_ec_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
 	struct cros_ec_dev *ec = sensor_hub->ec;
 	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
+	struct iio_buffer *buffer;
 	u32 ver_mask;
 	int ret, i;
 
@@ -124,8 +160,6 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 	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;
 		ret = cros_ec_motion_send_host_cmd(state, 0);
@@ -154,9 +188,26 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 			state->frequencies[2] =
 			    state->resp->info_3.max_frequency;
 		}
-	}
 
-	return 0;
+		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+			buffer = devm_iio_kfifo_allocate(dev);
+			if (!buffer)
+				return -ENOMEM;
+
+			iio_device_attach_buffer(indio_dev, buffer);
+			indio_dev->modes = INDIO_BUFFER_SOFTWARE;
+			ret = cros_ec_sensorhub_register_push_data(
+					sensor_hub,
+					sensor_platform->sensor_num,
+					indio_dev, push_data);
+		} else {
+			indio_dev->modes = INDIO_DIRECT_MODE;
+			ret = devm_iio_triggered_buffer_setup(
+					dev, indio_dev, NULL,
+					trigger_capture, NULL);
+		}
+	}
+	return ret;
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
 
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index c431e4d1482d..d58f010880ce 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -186,7 +186,8 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
 	if (!indio_dev)
 		return -ENOMEM;
 
-	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
+			cros_ec_sensors_capture, cros_ec_sensors_push_data);
 	if (ret)
 		return ret;
 
@@ -245,11 +246,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
 
 	state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
 
-	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
-					      cros_ec_sensors_capture, NULL);
-	if (ret)
-		return ret;
-
 	return devm_iio_device_register(dev, indio_dev);
 }
 
diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
index 2f4d6d3ab41d..c7538e93d24f 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -134,7 +134,8 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
 	if (!indio_dev)
 		return -ENOMEM;
 
-	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
+			cros_ec_sensors_capture, cros_ec_sensors_push_data);
 	if (ret)
 		return ret;
 
@@ -180,11 +181,6 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
 
 	state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
 
-	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
-					      cros_ec_sensors_capture, NULL);
-	if (ret)
-		return ret;
-
 	return devm_iio_device_register(dev, indio_dev);
 }
 
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index abef66d0e884..60f78f0e4884 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -12,6 +12,7 @@
 #include <linux/irqreturn.h>
 #include <linux/platform_data/cros_ec_commands.h>
 #include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_data/cros_ec_sensorhub.h>
 
 enum {
 	CROS_EC_SENSOR_X,
@@ -32,6 +33,9 @@ enum {
 /* Minimum sampling period to use when device is suspending */
 #define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
 
+typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
+
+
 /**
  * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
  * @ec:				cros EC device structure
@@ -110,11 +114,17 @@ struct platform_device;
  * @pdev:		platform device created for the sensors
  * @indio_dev:		iio device structure of the device
  * @physical_device:	true if the device refers to a physical device
+ * @trigger_capture:    function pointer to call buffer is triggered,
+ *    for backward compatibility.
+ * @push_data:          function to call when cros_ec_sensorhub receives
+ *    a sample for that sensor.
  *
  * Return: 0 on success, -errno on failure.
  */
 int cros_ec_sensors_core_init(struct platform_device *pdev,
-			      struct iio_dev *indio_dev, bool physical_device);
+			      struct iio_dev *indio_dev, bool physical_device,
+			      cros_ec_sensors_capture_t trigger_capture,
+			      cros_ec_sensorhub_push_data_cb_t push_data);
 
 /* To remove association of physical device to cros_ec_sensorhub. */
 int cros_ec_sensors_core_clean(struct platform_device *pdev);
@@ -132,6 +142,10 @@ int cros_ec_sensors_core_clean(struct platform_device *pdev);
  * Return: IRQ_HANDLED
  */
 irqreturn_t cros_ec_sensors_capture(int irq, void *p);
+int cros_ec_sensors_push_data(
+		struct iio_dev *indio_dev,
+		s16 *data,
+		s64 timestamp);
 
 /**
  * cros_ec_motion_send_host_cmd() - send motion sense host command
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 11/13] iio: cros_ec: Expose hwfifo_timeout
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (9 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 10/13] iio: cros_ec: Use triggered buffer only when EC does not support FIFO Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 16:35   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 12/13] iio: cros_ec: Report hwfifo_watermark_max Gwendal Grignou
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

Expose EC sampling rate through buffer/hwfifo_timeout.
Rename frequency to sampling_frequency to match IIO ABI.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 .../common/cros_ec_sensors/cros_ec_sensors.c  |   1 -
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 110 +++++++++++++-----
 drivers/iio/light/cros_ec_light_prox.c        |   3 +-
 drivers/iio/pressure/cros_ec_baro.c           |   3 +-
 4 files changed, 81 insertions(+), 36 deletions(-)

diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index b78a942ac8e5..95a4b4cf8f87 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -255,7 +255,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
 			BIT(IIO_CHAN_INFO_CALIBSCALE);
 		channel->info_mask_shared_by_all =
 			BIT(IIO_CHAN_INFO_SCALE) |
-			BIT(IIO_CHAN_INFO_FREQUENCY) |
 			BIT(IIO_CHAN_INFO_SAMP_FREQ);
 		channel->info_mask_shared_by_all_available =
 			BIT(IIO_CHAN_INFO_SAMP_FREQ);
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
index c4c37c6df301..a31a761e3a81 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -11,6 +11,7 @@
 #include <linux/iio/common/cros_ec_sensors_core.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
 #include <linux/iio/trigger_consumer.h>
 #include <linux/iio/triggered_buffer.h>
 #include <linux/kernel.h>
@@ -84,6 +85,75 @@ static void get_default_min_max_freq(enum motionsensor_type type,
 	}
 }
 
+static int cros_ec_sensor_set_ec_rate(struct cros_ec_sensors_core_state *st,
+				      int rate)
+{
+	int ret;
+
+	mutex_lock(&st->cmd_lock);
+	st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+	st->param.ec_rate.data = rate;
+	ret = cros_ec_motion_send_host_cmd(st, 0);
+	if (ret == 0)
+		st->curr_sampl_freq = rate;
+	mutex_unlock(&st->cmd_lock);
+	return ret;
+}
+
+static ssize_t cros_ec_sensor_set_report_latency(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int integer, fract, ret;
+	int latency;
+
+	ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract);
+	if (ret)
+		return ret;
+
+	/* EC rate is in ms. */
+	latency = integer * 1000 + fract / 1000;
+	ret = cros_ec_sensor_set_ec_rate(st, latency);
+	if (ret < 0)
+		return ret;
+
+	return len;
+}
+
+static ssize_t cros_ec_sensor_get_report_latency(struct device *dev,
+					      struct device_attribute *attr,
+					      char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int latency, ret;
+
+	mutex_lock(&st->cmd_lock);
+	st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+	st->param.ec_rate.data = EC_MOTION_SENSE_NO_VALUE;
+
+	ret = cros_ec_motion_send_host_cmd(st, 0);
+	latency = st->resp->ec_rate.ret;
+	mutex_unlock(&st->cmd_lock);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d.%06u\n",
+		       latency / 1000,
+		       (latency % 1000) * 1000);
+}
+
+static IIO_DEVICE_ATTR(hwfifo_timeout, 0644,
+		       cros_ec_sensor_get_report_latency,
+		       cros_ec_sensor_set_report_latency, 0);
+
+static const struct attribute *cros_ec_sensor_fifo_attributes[] = {
+	&iio_dev_attr_hwfifo_timeout.dev_attr.attr,
+	NULL,
+};
+
 int cros_ec_sensors_push_data(
 		struct iio_dev *indio_dev,
 		s16 *data,
@@ -194,6 +264,9 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 			if (!buffer)
 				return -ENOMEM;
 
+			iio_buffer_set_attrs(buffer,
+					     cros_ec_sensor_fifo_attributes);
+
 			iio_device_attach_buffer(indio_dev, buffer);
 			indio_dev->modes = INDIO_BUFFER_SOFTWARE;
 			ret = cros_ec_sensorhub_register_push_data(
@@ -553,18 +626,6 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
 
 	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;
-
-		ret = cros_ec_motion_send_host_cmd(st, 0);
-		if (ret)
-			break;
-
-		*val = st->resp->ec_rate.ret;
-		ret = IIO_VAL_INT;
-		break;
-	case IIO_CHAN_INFO_FREQUENCY:
 		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
 		st->param.sensor_odr.data =
 			EC_MOTION_SENSE_NO_VALUE;
@@ -613,23 +674,16 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
 	int ret;
 
 	switch (mask) {
-	case IIO_CHAN_INFO_FREQUENCY:
+	case IIO_CHAN_INFO_SAMP_FREQ:
 		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;
 
-		ret = cros_ec_motion_send_host_cmd(st, 0);
-		break;
-	case IIO_CHAN_INFO_SAMP_FREQ:
-		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
-		st->param.ec_rate.data = val;
-
 		ret = cros_ec_motion_send_host_cmd(st, 0);
 		if (ret)
 			break;
-		st->curr_sampl_freq = val;
 		break;
 	default:
 		ret = -EINVAL;
@@ -643,6 +697,7 @@ static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
 {
 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
 	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+	int ret = 0;
 
 	if (st->curr_sampl_freq == 0)
 		return 0;
@@ -652,13 +707,10 @@ static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
 	 * sleep. Set 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);
+		ret = cros_ec_sensor_set_ec_rate(st,
+				CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY);
 	}
-	return 0;
+	return ret;
 }
 
 static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
@@ -670,11 +722,7 @@ static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
 		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);
+		cros_ec_sensor_set_ec_rate(st, st->curr_sampl_freq);
 	}
 }
 
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index d58f010880ce..e9b0072d5c88 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -199,8 +199,7 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
 
 	/* Common part */
 	channel->info_mask_shared_by_all =
-		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
-		BIT(IIO_CHAN_INFO_FREQUENCY);
+		BIT(IIO_CHAN_INFO_SAMP_FREQ);
 	channel->info_mask_shared_by_all_available =
 		BIT(IIO_CHAN_INFO_SAMP_FREQ);
 	channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
index c7538e93d24f..aa3cae5a1a15 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -148,8 +148,7 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
 	channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
 	channel->info_mask_shared_by_all =
 		BIT(IIO_CHAN_INFO_SCALE) |
-		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
-		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;
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 12/13] iio: cros_ec: Report hwfifo_watermark_max
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (10 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 11/13] iio: cros_ec: Expose hwfifo_timeout Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 16:37   ` Jonathan Cameron
  2019-09-22 17:50 ` [PATCH 13/13] iio: cros_ec: Use Hertz as unit for sampling frequency Gwendal Grignou
  2019-10-05 15:39 ` [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Jonathan Cameron
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

Report the maximum amount of sample the EC can hold.
This is not tunable, but can be useful for application to find out the
maximum amount of time it can sleep when hwfifo_timeout is set to a
large number.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 35 +++++++++++++++++--
 .../linux/iio/common/cros_ec_sensors_core.h   |  4 ++-
 2 files changed, 36 insertions(+), 3 deletions(-)

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
index a31a761e3a81..a32260c9bc09 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -23,6 +23,12 @@
 #include <linux/platform_data/cros_ec_sensorhub.h>
 #include <linux/platform_device.h>
 
+/*
+ * Hard coded to the first device to support sensor fifo.  The EC has a 2048
+ * byte fifo and will trigger an interrupt when fifo is 2/3 full.
+ */
+#define CROS_EC_FIFO_SIZE (2048 * 2 / 3)
+
 static char *cros_ec_loc[] = {
 	[MOTIONSENSE_LOC_BASE] = "base",
 	[MOTIONSENSE_LOC_LID] = "lid",
@@ -56,8 +62,15 @@ static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev,
 
 static void get_default_min_max_freq(enum motionsensor_type type,
 				     u32 *min_freq,
-				     u32 *max_freq)
+				     u32 *max_freq,
+				     u32 *max_fifo_events)
 {
+	/*
+	 * We don't know fifo size, set to size previously used by older
+	 * hardware.
+	 */
+	*max_fifo_events = CROS_EC_FIFO_SIZE;
+
 	switch (type) {
 	case MOTIONSENSE_TYPE_ACCEL:
 	case MOTIONSENSE_TYPE_GYRO:
@@ -122,6 +135,7 @@ static ssize_t cros_ec_sensor_set_report_latency(struct device *dev,
 	return len;
 }
 
+
 static ssize_t cros_ec_sensor_get_report_latency(struct device *dev,
 					      struct device_attribute *attr,
 					      char *buf)
@@ -149,8 +163,22 @@ static IIO_DEVICE_ATTR(hwfifo_timeout, 0644,
 		       cros_ec_sensor_get_report_latency,
 		       cros_ec_sensor_set_report_latency, 0);
 
+static ssize_t hwfifo_watermark_max_show(
+		struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n", st->fifo_max_event_count);
+}
+
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0);
+
 static const struct attribute *cros_ec_sensor_fifo_attributes[] = {
 	&iio_dev_attr_hwfifo_timeout.dev_attr.attr,
+	&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
 	NULL,
 };
 
@@ -251,12 +279,15 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 		if (state->msg->version < 3) {
 			get_default_min_max_freq(state->resp->info.type,
 						 &state->frequencies[1],
-						 &state->frequencies[2]);
+						 &state->frequencies[2],
+						 &state->fifo_max_event_count);
 		} else {
 			state->frequencies[1] =
 			    state->resp->info_3.min_frequency;
 			state->frequencies[2] =
 			    state->resp->info_3.max_frequency;
+			state->fifo_max_event_count =
+			    state->resp->info_3.fifo_max_event_count;
 		}
 
 		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index 60f78f0e4884..2c6acbde0d02 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -54,7 +54,8 @@ typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
  *				the timestamp. The timestamp is always last and
  *				is always 8-byte aligned.
  * @read_ec_sensors_data:	function used for accessing sensors values
- * @cuur_sampl_freq:		current sampling period
+ * @curr_sampl_freq:		current sampling period
+ * @fifo_max_event_count:	Size of the EC sensor FIFO
  */
 struct cros_ec_sensors_core_state {
 	struct cros_ec_device *ec;
@@ -78,6 +79,7 @@ struct cros_ec_sensors_core_state {
 				    unsigned long scan_mask, s16 *data);
 
 	int curr_sampl_freq;
+	u32 fifo_max_event_count;
 
 	/* Table of known available frequencies : 0, Min and Max in mHz */
 	int frequencies[3];
-- 
2.23.0.351.gc4317032e6-goog


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

* [PATCH 13/13] iio: cros_ec: Use Hertz as unit for sampling frequency
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (11 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 12/13] iio: cros_ec: Report hwfifo_watermark_max Gwendal Grignou
@ 2019-09-22 17:50 ` Gwendal Grignou
  2019-10-05 16:39   ` Jonathan Cameron
  2019-10-05 15:39 ` [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Jonathan Cameron
  13 siblings, 1 reply; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-22 17:50 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

To be compliant with other sensors, set and get sensor sampling
frequency in Hz, not mHz.

Fixes: ae7b02ad2f32 ("iio: common: cros_ec_sensors: Expose
cros_ec_sensors frequency range via iio sysfs")

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 32 +++++++++++--------
 .../linux/iio/common/cros_ec_sensors_core.h   |  6 ++--
 2 files changed, 22 insertions(+), 16 deletions(-)

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
index a32260c9bc09..f7e6827bd4cb 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -227,6 +227,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
 	struct iio_buffer *buffer;
 	u32 ver_mask;
+	int frequencies[ARRAY_SIZE(state->frequencies) / 2] = { 0 };
 	int ret, i;
 
 	platform_set_drvdata(pdev, indio_dev);
@@ -275,20 +276,22 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 			state->calib[i].scale = MOTION_SENSE_DEFAULT_SCALE;
 
 		/* 0 is a correct value used to stop the device */
-		state->frequencies[0] = 0;
 		if (state->msg->version < 3) {
 			get_default_min_max_freq(state->resp->info.type,
-						 &state->frequencies[1],
-						 &state->frequencies[2],
+						 &frequencies[1],
+						 &frequencies[2],
 						 &state->fifo_max_event_count);
 		} else {
-			state->frequencies[1] =
-			    state->resp->info_3.min_frequency;
-			state->frequencies[2] =
-			    state->resp->info_3.max_frequency;
+			frequencies[1] = state->resp->info_3.min_frequency;
+			frequencies[2] = state->resp->info_3.max_frequency;
 			state->fifo_max_event_count =
 			    state->resp->info_3.fifo_max_event_count;
 		}
+		for (i = 0; i < ARRAY_SIZE(frequencies); i++) {
+			state->frequencies[2 * i] = frequencies[i] / 1000;
+			state->frequencies[2 * i + 1] =
+				(frequencies[i] % 1000) * 1000;
+		}
 
 		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
 			buffer = devm_iio_kfifo_allocate(dev);
@@ -653,7 +656,7 @@ 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;
+	int ret, frequency;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_SAMP_FREQ:
@@ -665,8 +668,10 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
 		if (ret)
 			break;
 
-		*val = st->resp->sensor_odr.ret;
-		ret = IIO_VAL_INT;
+		frequency = st->resp->sensor_odr.ret;
+		*val = frequency / 1000;
+		*val2 = (frequency % 1000) * 1000;
+		ret = IIO_VAL_INT_PLUS_MICRO;
 		break;
 	default:
 		ret = -EINVAL;
@@ -690,7 +695,7 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		*length = ARRAY_SIZE(state->frequencies);
 		*vals = (const int *)&state->frequencies;
-		*type = IIO_VAL_INT;
+		*type = IIO_VAL_INT_PLUS_MICRO;
 		return IIO_AVAIL_LIST;
 	}
 
@@ -702,12 +707,13 @@ 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;
+	int ret, frequency;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_SAMP_FREQ:
+		frequency = val * 1000 + val2 / 1000;
 		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
-		st->param.sensor_odr.data = val;
+		st->param.sensor_odr.data = frequency;
 
 		/* Always roundup, so caller gets at least what it asks for. */
 		st->param.sensor_odr.roundup = 1;
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index 2c6acbde0d02..46d5110b2fe4 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -56,6 +56,8 @@ typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
  * @read_ec_sensors_data:	function used for accessing sensors values
  * @curr_sampl_freq:		current sampling period
  * @fifo_max_event_count:	Size of the EC sensor FIFO
+ * @frequencies:		Table of known available frequencies:
+ *				0, Min and Max in mHz.
  */
 struct cros_ec_sensors_core_state {
 	struct cros_ec_device *ec;
@@ -80,9 +82,7 @@ struct cros_ec_sensors_core_state {
 
 	int curr_sampl_freq;
 	u32 fifo_max_event_count;
-
-	/* Table of known available frequencies : 0, Min and Max in mHz */
-	int frequencies[3];
+	int frequencies[6];
 };
 
 /**
-- 
2.23.0.351.gc4317032e6-goog


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

* Re: [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public
  2019-09-22 17:50 ` [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
@ 2019-09-30 13:15   ` Enric Balletbo i Serra
  2019-09-30 16:24     ` Gwendal Grignou
  2019-10-05 15:26   ` Jonathan Cameron
  1 sibling, 1 reply; 35+ messages in thread
From: Enric Balletbo i Serra @ 2019-09-30 13:15 UTC (permalink / raw)
  To: Gwendal Grignou, jic23, knaack.h, lars, pmeerw, lee.jones,
	bleung, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio

Hi Gwendal,

Many thanks for the patches.

On 22/9/19 19:50, Gwendal Grignou wrote:
> Add a new function to return the number of MEMS sensors available in a
> ChromeOS Embedded Controller.
> It uses MOTIONSENSE_CMD_DUMP if available or a specific memory map ACPI
> registers to find out.
> 
> Also, make check_features public as it can be useful for other drivers
> to know whant the Embedded Controller supports.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> ---
>  drivers/mfd/cros_ec_dev.c   | 61 ++++++++++++++++++++++++++++++++++++-
>  include/linux/mfd/cros_ec.h | 17 +++++++++++
>  2 files changed, 77 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
> index 6e6dfd6c1871..3be80183ccaa 100644
> --- a/drivers/mfd/cros_ec_dev.c
> +++ b/drivers/mfd/cros_ec_dev.c
> @@ -112,7 +112,7 @@ static const struct mfd_cell cros_ec_vbc_cells[] = {
>  	{ .name = "cros-ec-vbc", }
>  };
>  
> -static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
> +int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
>  {
>  	struct cros_ec_command *msg;
>  	int ret;
> @@ -143,12 +143,71 @@ static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
>  
>  	return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
>  }
> +EXPORT_SYMBOL_GPL(cros_ec_check_features);
>  
>  static void cros_ec_class_release(struct device *dev)
>  {
>  	kfree(to_cros_ec_dev(dev));
>  }
>  
> +int cros_ec_get_sensor_count(struct cros_ec_dev *ec)
> +{
> +	/*
> +	 * Issue a command to get the number of sensor reported.
> +	 * If not supported, check for legacy mode.
> +	 */
> +	int ret, sensor_count;
> +	struct ec_params_motion_sense *params;
> +	struct ec_response_motion_sense *resp;
> +	struct cros_ec_command *msg;
> +	struct cros_ec_device *ec_dev = ec->ec_dev;
> +	u8 status;
> +
> +	msg = kzalloc(sizeof(struct cros_ec_command) +
> +			max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> +	if (msg == NULL)
> +		return -ENOMEM;
> +
> +	msg->version = 1;
> +	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) {
> +		sensor_count = ret;
> +	} else if (msg->result != EC_RES_SUCCESS) {
> +		sensor_count = -EPROTO;
> +	} else {
> +		resp = (struct ec_response_motion_sense *)msg->data;
> +		sensor_count = resp->dump.sensor_count;
> +	}
> +	kfree(msg);
> +
> +	/*
> +	 * Check legacy mode: Let's find out if sensors are accessible
> +	 * via LPC interface.
> +	 */
> +	if (sensor_count == -EPROTO &&
> +	    ec->cmd_offset == 0 &&
> +	    ec_dev->cmd_readmem) {
> +		ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS,
> +				1, &status);
> +		if ((ret >= 0) &&
> +		    (status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) {
> +			/*
> +			 * We have 2 sensors, one in the lid, one in the base.
> +			 */
> +			sensor_count = 2;
> +		}
> +	}
> +	return sensor_count;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
> +
>  static void cros_ec_sensors_register(struct cros_ec_dev *ec)
>  {
>  	/*
> diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
> index 61c2875c2a40..578e0bbcafdc 100644
> --- a/include/linux/mfd/cros_ec.h
> +++ b/include/linux/mfd/cros_ec.h
> @@ -32,4 +32,21 @@ struct cros_ec_dev {
>  
>  #define to_cros_ec_dev(dev)  container_of(dev, struct cros_ec_dev, class_dev)
>  
> +/**
> + * cros_ec_check_features - Test for the presence of EC features
> + *
> + * Call this function to test whether the ChromeOS EC supports a feature.
> + *
> + * @ec_dev: EC device
> + * @msg: One of ec_feature_code values
> + * @return: 1 if supported, 0 if not
> + */
> +int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
> +
> +/*
> + * Return the number of MEMS sensors supported.
> + * Return < 0 in case of error.
> + */
> +int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
> +

I am wondering if we should take this opportunity to move these two functions
from drivers/mfd/cros_ec_dev to drivers/platform/chrome/cros_ec_proto.c and
include/linux/platform_data/cros_ec_proto.h

I think this part is more suitable for core part that actually resides in
platform/chrome than the mfd device itself. What do you think? Do you see any
problem on doing this?

Thanks,
 Enric

>  #endif /* __LINUX_MFD_CROS_EC_H */
> 

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

* Re: [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public
  2019-09-30 13:15   ` Enric Balletbo i Serra
@ 2019-09-30 16:24     ` Gwendal Grignou
  0 siblings, 0 replies; 35+ messages in thread
From: Gwendal Grignou @ 2019-09-30 16:24 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones, Benson Leung, Doug Anderson,
	Guenter Roeck, Fabien Lahoudere, linux-kernel, linux-iio

Sounds good to me, I will make the change in rev2.

On Mon, Sep 30, 2019 at 6:15 AM Enric Balletbo i Serra
<enric.balletbo@collabora.com> wrote:
>
> Hi Gwendal,
>
> Many thanks for the patches.
>
> On 22/9/19 19:50, Gwendal Grignou wrote:
> > Add a new function to return the number of MEMS sensors available in a
> > ChromeOS Embedded Controller.
> > It uses MOTIONSENSE_CMD_DUMP if available or a specific memory map ACPI
> > registers to find out.
> >
> > Also, make check_features public as it can be useful for other drivers
> > to know whant the Embedded Controller supports.
> >
> > Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> > ---
> >  drivers/mfd/cros_ec_dev.c   | 61 ++++++++++++++++++++++++++++++++++++-
> >  include/linux/mfd/cros_ec.h | 17 +++++++++++
> >  2 files changed, 77 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
> > index 6e6dfd6c1871..3be80183ccaa 100644
> > --- a/drivers/mfd/cros_ec_dev.c
> > +++ b/drivers/mfd/cros_ec_dev.c
> > @@ -112,7 +112,7 @@ static const struct mfd_cell cros_ec_vbc_cells[] = {
> >       { .name = "cros-ec-vbc", }
> >  };
> >
> > -static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
> > +int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
> >  {
> >       struct cros_ec_command *msg;
> >       int ret;
> > @@ -143,12 +143,71 @@ static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
> >
> >       return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
> >  }
> > +EXPORT_SYMBOL_GPL(cros_ec_check_features);
> >
> >  static void cros_ec_class_release(struct device *dev)
> >  {
> >       kfree(to_cros_ec_dev(dev));
> >  }
> >
> > +int cros_ec_get_sensor_count(struct cros_ec_dev *ec)
> > +{
> > +     /*
> > +      * Issue a command to get the number of sensor reported.
> > +      * If not supported, check for legacy mode.
> > +      */
> > +     int ret, sensor_count;
> > +     struct ec_params_motion_sense *params;
> > +     struct ec_response_motion_sense *resp;
> > +     struct cros_ec_command *msg;
> > +     struct cros_ec_device *ec_dev = ec->ec_dev;
> > +     u8 status;
> > +
> > +     msg = kzalloc(sizeof(struct cros_ec_command) +
> > +                     max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> > +     if (msg == NULL)
> > +             return -ENOMEM;
> > +
> > +     msg->version = 1;
> > +     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) {
> > +             sensor_count = ret;
> > +     } else if (msg->result != EC_RES_SUCCESS) {
> > +             sensor_count = -EPROTO;
> > +     } else {
> > +             resp = (struct ec_response_motion_sense *)msg->data;
> > +             sensor_count = resp->dump.sensor_count;
> > +     }
> > +     kfree(msg);
> > +
> > +     /*
> > +      * Check legacy mode: Let's find out if sensors are accessible
> > +      * via LPC interface.
> > +      */
> > +     if (sensor_count == -EPROTO &&
> > +         ec->cmd_offset == 0 &&
> > +         ec_dev->cmd_readmem) {
> > +             ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS,
> > +                             1, &status);
> > +             if ((ret >= 0) &&
> > +                 (status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) {
> > +                     /*
> > +                      * We have 2 sensors, one in the lid, one in the base.
> > +                      */
> > +                     sensor_count = 2;
> > +             }
> > +     }
> > +     return sensor_count;
> > +}
> > +EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
> > +
> >  static void cros_ec_sensors_register(struct cros_ec_dev *ec)
> >  {
> >       /*
> > diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
> > index 61c2875c2a40..578e0bbcafdc 100644
> > --- a/include/linux/mfd/cros_ec.h
> > +++ b/include/linux/mfd/cros_ec.h
> > @@ -32,4 +32,21 @@ struct cros_ec_dev {
> >
> >  #define to_cros_ec_dev(dev)  container_of(dev, struct cros_ec_dev, class_dev)
> >
> > +/**
> > + * cros_ec_check_features - Test for the presence of EC features
> > + *
> > + * Call this function to test whether the ChromeOS EC supports a feature.
> > + *
> > + * @ec_dev: EC device
> > + * @msg: One of ec_feature_code values
> > + * @return: 1 if supported, 0 if not
> > + */
> > +int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
> > +
> > +/*
> > + * Return the number of MEMS sensors supported.
> > + * Return < 0 in case of error.
> > + */
> > +int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
> > +
>
> I am wondering if we should take this opportunity to move these two functions
> from drivers/mfd/cros_ec_dev to drivers/platform/chrome/cros_ec_proto.c and
> include/linux/platform_data/cros_ec_proto.h
>
> I think this part is more suitable for core part that actually resides in
> platform/chrome than the mfd device itself. What do you think? Do you see any
> problem on doing this?
>
> Thanks,
>  Enric
>
> >  #endif /* __LINUX_MFD_CROS_EC_H */
> >

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

* Re: [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-09-22 17:50 ` [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
@ 2019-10-01 10:31   ` Enric Balletbo i Serra
  2019-10-05 15:35   ` Jonathan Cameron
  1 sibling, 0 replies; 35+ messages in thread
From: Enric Balletbo i Serra @ 2019-10-01 10:31 UTC (permalink / raw)
  To: Gwendal Grignou, jic23, knaack.h, lars, pmeerw, lee.jones,
	bleung, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio

Hi Gwendal,

Some comments below.

On 22/9/19 19:50, Gwendal Grignou wrote:
> Similar to HID sensor stack, the new driver sits between cros_ec_dev
> and the iio device drivers:
> 
> EC based iio device topology would be:
> iio:device1 ->
> ...0/0000:00:1f.0/PNP0C09:00/GOOG0004:00/cros-ec-dev.6.auto/
>                                          cros-ec-sensorhub.7.auto/
>                                          cros-ec-accel.15.auto/
>                                          iio:device1
> 
> It will be expanded to control EC sensor FIFO.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> ---
>  drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
>  drivers/platform/chrome/Kconfig               |  18 +-
>  drivers/platform/chrome/Makefile              |   1 +
>  drivers/platform/chrome/cros_ec_sensorhub.c   | 211 ++++++++++++++++++
>  .../linux/platform_data/cros_ec_sensorhub.h   |  21 ++
>  5 files changed, 249 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub.c
>  create mode 100644 include/linux/platform_data/cros_ec_sensorhub.h
> 
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> index cdbb29cfb907..fefad9572790 100644
> --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -4,7 +4,7 @@
>  #
>  config IIO_CROS_EC_SENSORS_CORE
>  	tristate "ChromeOS EC Sensors Core"
> -	depends on SYSFS && CROS_EC
> +	depends on SYSFS && CROS_EC_SENSORHUB
>  	select IIO_BUFFER
>  	select IIO_TRIGGERED_BUFFER
>  	help
> diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
> index ee5f08ea57b6..add967236cfb 100644
> --- a/drivers/platform/chrome/Kconfig
> +++ b/drivers/platform/chrome/Kconfig
> @@ -132,9 +132,9 @@ config CROS_EC_LPC
>  	  module will be called cros_ec_lpcs.
>  
>  config CROS_EC_PROTO
> -        bool
> -        help
> -          ChromeOS EC communication protocol helpers.
> +	bool
> +	help
> +	  ChromeOS EC communication protocol helpers.
>  

That change is unrelated to the patch.

>  config CROS_KBD_LED_BACKLIGHT
>  	tristate "Backlight LED support for Chrome OS keyboards"
> @@ -190,6 +190,18 @@ config CROS_EC_DEBUGFS
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called cros_ec_debugfs.
>  
> +config CROS_EC_SENSORHUB
> +	tristate "ChromeOS EC MEMS Senosr Hub"

Typo: s/Senosr/Sensor/

> +	depends on CROS_EC && IIO
> +	help
> +	  Allow loading IIO sensors. This driver is loaded by MFD and will in
> +	  turn query the EC and register the sensors.
> +	  It also spreads the sensor data coming from the EC to the IIO sensorr

Typo : s/sensorr/Sensor

> +	  object.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called cros_ec_sensorhub.
> +
>  config CROS_EC_SYSFS
>  	tristate "ChromeOS EC control and information through sysfs"
>  	depends on MFD_CROS_EC_DEV && SYSFS
> diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> index 477ec3d1d1c9..a164c40dc099 100644
> --- a/drivers/platform/chrome/Makefile
> +++ b/drivers/platform/chrome/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o cros_ec_trace.o
>  obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
>  obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_chardev.o
>  obj-$(CONFIG_CROS_EC_LIGHTBAR)		+= cros_ec_lightbar.o
> +obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorhub.o
>  obj-$(CONFIG_CROS_EC_VBC)		+= cros_ec_vbc.o
>  obj-$(CONFIG_CROS_EC_DEBUGFS)		+= cros_ec_debugfs.o
>  obj-$(CONFIG_CROS_EC_SYSFS)		+= cros_ec_sysfs.o
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
> new file mode 100644
> index 000000000000..80688018ef66
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> @@ -0,0 +1,211 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * SensorHub: driver that discover sensors behind
> + * a ChromeOS Embedded controller.
> + *
> + * Copyright 2019 Google LLC
> + */
> +
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/fs.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/platform_data/cros_ec_commands.h>
> +#include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_device.h>
> +#include <linux/poll.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/platform_data/cros_ec_sensorhub.h>
> +
> +#define DRV_NAME		"cros-ec-sensorhub"
> +
> +static int cros_ec_sensors_register(struct device *dev,
> +		struct cros_ec_dev *ec)
> +{
> +	int ret, i, id, sensor_num;
> +	struct mfd_cell *sensor_cells;
> +	struct cros_ec_sensor_platform *sensor_platforms;
> +	int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
> +	struct ec_params_motion_sense *params;
> +	struct ec_response_motion_sense *resp;
> +	struct cros_ec_command *msg;
> +
> +	sensor_num = cros_ec_get_sensor_count(ec);
> +	if (sensor_num < 0) {
> +		dev_err(dev,
> +			"Unable to retrieve sensor information (err:%d)\n",
> +			sensor_num);
> +		return sensor_num;
> +	}
> +
> +	if (sensor_num == 0) {
> +		dev_err(dev, "Zero sensors reported.\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Build an array of sensors driver and register them all.
> +	 */
> +	msg = kzalloc(sizeof(struct cros_ec_command) +
> +		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> +	if (msg == NULL) {
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	msg->version = 1;
> +	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;
> +	resp = (struct ec_response_motion_sense *)msg->data;
> +
> +	/*
> +	 * Allocate 1 extra sensor if lid angle sensor is needed.
> +	 */
> +	sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell),
> +			       GFP_KERNEL);
> +	if (sensor_cells == NULL) {
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	sensor_platforms = kcalloc(sensor_num,
> +				   sizeof(struct cros_ec_sensor_platform),
> +				   GFP_KERNEL);
> +	if (sensor_platforms == NULL) {
> +		ret = -ENOMEM;
> +		goto error_platforms;
> +	}
> +
> +	id = 0;
> +	for (i = 0; i < sensor_num; i++) {
> +		params->cmd = MOTIONSENSE_CMD_INFO;
> +		params->info.sensor_num = i;
> +		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
> +		if (ret < 0) {
> +			dev_warn(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_BARO:
> +			sensor_cells[id].name = "cros-ec-baro";
> +			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(dev, "unknown type %d\n", resp->info.type);
> +			continue;
> +		}
> +		sensor_platforms[id].sensor_num = i;
> +		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)
> +		ec->has_kb_wake_angle = true;
> +
> +	if (cros_ec_check_features(ec,
> +				EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
> +		sensor_cells[id].name = "cros-ec-lid-angle";
> +		id++;
> +	}
> +
> +	ret = mfd_add_hotplug_devices(dev, sensor_cells, id);

That's problematic.

In general using mfd_* in a !MFD driver is considered a hack. I know that the
Intel Sensor Hub and few other drivers does this but it's not correct and I know
Lee won't be happy with this change.

I think we should be using the platform_device_add() API here.

Thanks,
 Enric

> +	kfree(sensor_platforms);
> +error_platforms:
> +	kfree(sensor_cells);
> +error:
> +	kfree(msg);
> +	return ret;
> +}
> +
> +static struct cros_ec_sensor_platform sensor_platforms[] = {
> +	{ .sensor_num = 0 },
> +	{ .sensor_num = 1 }
> +};
> +
> +static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
> +	{
> +		.name = "cros-ec-accel-legacy",
> +		.platform_data = &sensor_platforms[0],
> +		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> +	},
> +	{
> +		.name = "cros-ec-accel-legacy",
> +		.platform_data = &sensor_platforms[1],
> +		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> +	}
> +};
> +
> +
> +
> +static int cros_ec_sensorhub_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> +	int ret;
> +	struct cros_ec_sensorhub *data =
> +		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
> +
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->ec = ec;
> +	dev_set_drvdata(dev, data);
> +
> +	/* check whether this EC is a sensor hub. */
> +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
> +		ret = cros_ec_sensors_register(dev, ec);
> +	} else {
> +		/* Workaroud for older EC firmware */
> +		ret = mfd_add_hotplug_devices(dev,
> +				cros_ec_accel_legacy_cells,
> +				ARRAY_SIZE(cros_ec_accel_legacy_cells));
> +	}
> +	if (ret)
> +		dev_err(dev, "failed to add EC sensors: error %d\n", ret);
> +	return ret;
> +}
> +
> +static struct platform_driver cros_ec_sensorhub_driver = {
> +	.driver = {
> +		.name = DRV_NAME,
> +	},
> +	.probe = cros_ec_sensorhub_probe,
> +};
> +
> +module_platform_driver(cros_ec_sensorhub_driver);
> +
> +MODULE_ALIAS("platform:" DRV_NAME);
> +MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
> +MODULE_DESCRIPTION("ChromeOS EC MEMS Sensor Hub Driver");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> new file mode 100644
> index 000000000000..a8b64ecf5b9b
> --- /dev/null
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * cros_ec_sensorhub- Chrome OS EC MEMS Sensor Hub driver.
> + *
> + * Copyright (C) 2019 Google, Inc
> + *
> + */
> +
> +#ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> +#define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> +
> +#include <linux/platform_data/cros_ec_commands.h>
> +
> +/**
> + * struct cros_ec_sensorhub - Sensor Hub device data.
> + */
> +struct cros_ec_sensorhub {
> +	struct cros_ec_dev *ec;
> +};
> +
> +#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
> 

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

* Re: [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq
  2019-09-22 17:50 ` [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq Gwendal Grignou
@ 2019-10-01 10:32   ` Enric Balletbo i Serra
  2019-10-05 15:44   ` Jonathan Cameron
  1 sibling, 0 replies; 35+ messages in thread
From: Enric Balletbo i Serra @ 2019-10-01 10:32 UTC (permalink / raw)
  To: Gwendal Grignou, jic23, knaack.h, lars, pmeerw, lee.jones,
	bleung, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio

Hi Gwendal,

Some comments below.

On 22/9/19 19:50, Gwendal Grignou wrote:
> To improve sensor timestamp precision, given EC and AP are in
> different time domains, the AP needs to try to record the exact
> moment an event was signalled to the AP by the EC as soon as
> possible after it happens.
> 
> First thing in the hard irq is the best place for this.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> ---
>  drivers/platform/chrome/cros_ec.c           | 18 +++++++++++++++++-
>  drivers/platform/chrome/cros_ec_lpc.c       |  2 ++
>  include/linux/platform_data/cros_ec_proto.h | 15 +++++++++++++++
>  3 files changed, 34 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
> index fd77e6fa74c2..f49eb1d1e3cd 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -31,6 +31,21 @@ static struct cros_ec_platform pd_p = {
>  	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
>  };
>  
> +s64 cros_ec_get_time_ns(void)
> +{
> +	return ktime_get_boottime_ns();
> +}
> +EXPORT_SYMBOL(cros_ec_get_time_ns);
> +

That's a simple wrapper only to make sure all cros_ec drivers use the same code
path, right? So maybe instead of doing this we can just add an inline function
in the header like this

/**
 * cros_ec_get_time_ns - Get time since boot including time spend in suspend
 *
 * Return: ktime_t format since boot.
 */
static inline ktime_t cros_ec_get_time_ns(void)
{
	return ktime_get_boottime_ns();
}

I'd use ktime_t instead of s64 also.

Thanks,
 Enric

> +static irqreturn_t ec_irq_handler(int irq, void *data)
> +{
> +	struct cros_ec_device *ec_dev = data;
> +
> +	ec_dev->last_event_time = cros_ec_get_time_ns();
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
>  static irqreturn_t ec_irq_thread(int irq, void *data)
>  {
>  	struct cros_ec_device *ec_dev = data;
> @@ -132,7 +147,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
>  	}
>  
>  	if (ec_dev->irq) {
> -		err = devm_request_threaded_irq(dev, ec_dev->irq, NULL,
> +		err = devm_request_threaded_irq(
> +				dev, ec_dev->irq, ec_irq_handler,
>  				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>  				"chromeos-ec", ec_dev);
>  		if (err) {
> diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
> index 7d10d909435f..3c77496e164d 100644
> --- a/drivers/platform/chrome/cros_ec_lpc.c
> +++ b/drivers/platform/chrome/cros_ec_lpc.c
> @@ -313,6 +313,8 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
>  {
>  	struct cros_ec_device *ec_dev = data;
>  
> +	ec_dev->last_event_time = cros_ec_get_time_ns();
> +
>  	if (ec_dev->mkbp_event_supported &&
>  	    cros_ec_get_next_event(ec_dev, NULL) > 0)
>  		blocking_notifier_call_chain(&ec_dev->event_notifier, 0,
> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index e91e3fcb0348..ab12e28f2107 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -121,6 +121,8 @@ struct cros_ec_command {
>   * @event_data: Raw payload transferred with the MKBP event.
>   * @event_size: Size in bytes of the event data.
>   * @host_event_wake_mask: Mask of host events that cause wake from suspend.
> + * @last_event_time: exact time from the hard irq when we got notified of
> + *     a new event.
>   * @ec: The platform_device used by the mfd driver to interface with the
>   *      main EC.
>   * @pd: The platform_device used by the mfd driver to interface with the
> @@ -161,6 +163,7 @@ struct cros_ec_device {
>  	int event_size;
>  	u32 host_event_wake_mask;
>  	u32 last_resume_result;
> +	s64 last_event_time;
>  
>  	/* The platform devices used by the mfd driver */
>  	struct platform_device *ec;
> @@ -308,4 +311,16 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event);
>   */
>  u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev);
>  
> +/**
> + * cros_ec_get_time_ns - Return time in ns.
> + *
> + * This is the function used to record the time for last_event_time in struct
> + * cros_ec_device during the hard irq.
> + *
> + * This function is probably implemented using ktime_get_boot_ns(), but it's
> + * exposed here to make sure all cros_ec drivers use the same code path to get
> + * the time.
> + */
> +s64 cros_ec_get_time_ns(void);
> +
>  #endif /* __LINUX_CROS_EC_PROTO_H */
> 

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

* Re: [PATCH 05/13] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number
  2019-09-22 17:50 ` [PATCH 05/13] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
@ 2019-10-01 10:32   ` Enric Balletbo i Serra
  0 siblings, 0 replies; 35+ messages in thread
From: Enric Balletbo i Serra @ 2019-10-01 10:32 UTC (permalink / raw)
  To: Gwendal Grignou, jic23, knaack.h, lars, pmeerw, lee.jones,
	bleung, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Enrico Granata, Enrico Granata

Hi Gwendal and Enrico

On 22/9/19 19:50, Gwendal Grignou wrote:
> From: Enrico Granata <egranata@chromium.org>
> 
> Add a layer of sanity checking to cros_ec_register against attempting to
> register IRQ values that are not strictly greater than 0.
> 
> Signed-off-by: Enrico Granata <egranata@google.com>

I'll get rid of this signed-off as is the same as following

> Signed-off-by: Enrico Granata <egranata@chromium.org>
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

For my own reference:

Reviewed-for-chrome-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

> ---
>  drivers/platform/chrome/cros_ec.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
> index f49eb1d1e3cd..9c8dc7cdb2b7 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -146,7 +146,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
>  		return err;
>  	}
>  
> -	if (ec_dev->irq) {
> +	if (ec_dev->irq > 0) {
>  		err = devm_request_threaded_irq(
>  				dev, ec_dev->irq, ec_irq_handler,
>  				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> 

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

* Re: [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag
  2019-09-22 17:50 ` [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
@ 2019-10-01 10:32   ` Enric Balletbo i Serra
  2019-10-05 15:52   ` Jonathan Cameron
  1 sibling, 0 replies; 35+ messages in thread
From: Enric Balletbo i Serra @ 2019-10-01 10:32 UTC (permalink / raw)
  To: Gwendal Grignou, jic23, knaack.h, lars, pmeerw, lee.jones,
	bleung, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Enrico Granata

Hi Gwendal and Enrico,

Some comments below.

On 22/9/19 19:50, Gwendal Grignou wrote:
> From: Enrico Granata <egranata@chromium.org>
> 
> The ChromeOS EC has support for signaling to the host that
> a single IRQ can serve multiple MKBP events.
> 
> Doing this serves an optimization purpose, as it minimizes the
> number of round-trips into the interrupt handling machinery, and
> it proves beneficial to sensor timestamping as it keeps the desired
> synchronization of event times between the two processors.
> 
> This patch adds kernel support for this EC feature, allowing the
> ec_irq to loop until all events have been served.
> 
> Signed-off-by: Enrico Granata <egranata@chromium.org>
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> ---
>  drivers/platform/chrome/cros_ec.c           | 33 +++++++++----
>  drivers/platform/chrome/cros_ec_proto.c     | 51 ++++++++++++---------
>  include/linux/platform_data/cros_ec_proto.h |  7 ++-
>  3 files changed, 57 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
> index 9c8dc7cdb2b7..4adc007c357c 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -46,25 +46,38 @@ static irqreturn_t ec_irq_handler(int irq, void *data)
>  	return IRQ_WAKE_THREAD;
>  }
>  
> -static irqreturn_t ec_irq_thread(int irq, void *data)
> +static bool ec_handle_event(struct cros_ec_device *ec_dev)
>  {
> -	struct cros_ec_device *ec_dev = data;
>  	bool wake_event = true;
> -	int ret;
> +	bool ec_has_more_events = false;
> +	int ret = cros_ec_get_next_event(ec_dev, &wake_event);
>  
> -	ret = cros_ec_get_next_event(ec_dev, &wake_event);
> +	if (ec_dev->mkbp_event_supported) {
> +		ec_has_more_events = (ret > 0) &&
> +			(ec_dev->event_data.event_type &
> +				EC_MKBP_HAS_MORE_EVENTS);
> +	}
>  
> -	/*
> -	 * Signal only if wake host events or any interrupt if
> -	 * cros_ec_get_next_event() returned an error (default value for
> -	 * wake_event is true)
> -	 */
> -	if (wake_event && device_may_wakeup(ec_dev->dev))
> +	if (device_may_wakeup(ec_dev->dev) && wake_event)
>  		pm_wakeup_event(ec_dev->dev, 0);
>  
>  	if (ret > 0)
>  		blocking_notifier_call_chain(&ec_dev->event_notifier,
>  					     0, ec_dev);
> +
> +	return ec_has_more_events;
> +
> +}
> +
> +static irqreturn_t ec_irq_thread(int irq, void *data)
> +{
> +	struct cros_ec_device *ec_dev = data;
> +	bool ec_has_more_events;
> +
> +	do {
> +		ec_has_more_events = ec_handle_event(ec_dev);

ec_handle_event is only called once, right? I'd prefer add the code directly
here instead of calling that function. I don't think it helps readability like
it is now.


> +	} while (ec_has_more_events);
> +
>  	return IRQ_HANDLED;
>  }
>  
> diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
> index f659f96bda12..70e6d6c93b8d 100644
> --- a/drivers/platform/chrome/cros_ec_proto.c
> +++ b/drivers/platform/chrome/cros_ec_proto.c
> @@ -425,10 +425,14 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
>  	ret = cros_ec_get_host_command_version_mask(ec_dev,
>  						    EC_CMD_GET_NEXT_EVENT,
>  						    &ver_mask);
> -	if (ret < 0 || ver_mask == 0)
> +	if (ret < 0 || ver_mask == 0) {
>  		ec_dev->mkbp_event_supported = 0;
> -	else
> -		ec_dev->mkbp_event_supported = 1;
> +		dev_info(ec_dev->dev, "MKBP not supported\n");
> +	} else {
> +		ec_dev->mkbp_event_supported = fls(ver_mask);
> +		dev_info(ec_dev->dev, "MKBP support version %u\n",
> +			ec_dev->mkbp_event_supported - 1);
> +	}

I'm not a big fan of having dev_info prints, unless the information is really a
good reason for it. In this case looks to me that fits more as debug message.

Remove the messages or low down the level to dbg, in that casepreferably just
use one message. I.e.

dev_info(ec_dev->dev, "MKBP support version %u\n", ec_dev->mkbp_event_supported
- 1);

-1, means no support.


>  
>  	/* Probe if host sleep v1 is supported for S0ix failure detection. */
>  	ret = cros_ec_get_host_command_version_mask(ec_dev,
> @@ -519,6 +523,7 @@ EXPORT_SYMBOL(cros_ec_cmd_xfer_status);
>  
>  static int get_next_event_xfer(struct cros_ec_device *ec_dev,
>  			       struct cros_ec_command *msg,
> +			       struct ec_response_get_next_event_v1 *event,
>  			       int version, uint32_t size)
>  {
>  	int ret;
> @@ -531,7 +536,7 @@ static int get_next_event_xfer(struct cros_ec_device *ec_dev,
>  	ret = cros_ec_cmd_xfer(ec_dev, msg);
>  	if (ret > 0) {
>  		ec_dev->event_size = ret - 1;
> -		memcpy(&ec_dev->event_data, msg->data, ret);
> +		ec_dev->event_data = *event;
>  	}
>  
>  	return ret;
> @@ -539,30 +544,29 @@ static int get_next_event_xfer(struct cros_ec_device *ec_dev,
>  
>  static int get_next_event(struct cros_ec_device *ec_dev)
>  {
> -	u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)];
> -	struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
> -	static int cmd_version = 1;
> -	int ret;
> +	struct {
> +		struct cros_ec_command msg;
> +		struct ec_response_get_next_event_v1 event;
> +	} __packed buf;
> +	struct cros_ec_command *msg = &buf.msg;
> +	struct ec_response_get_next_event_v1 *event = &buf.event;
> +	const int cmd_version = ec_dev->mkbp_event_supported - 1;
> +
> +	BUILD_BUG_ON(sizeof(union ec_response_get_next_data_v1) != 16);
> +
> +	memset(&buf, 0, sizeof(buf));
>  
>  	if (ec_dev->suspended) {
>  		dev_dbg(ec_dev->dev, "Device suspended.\n");
>  		return -EHOSTDOWN;
>  	}
>  
> -	if (cmd_version == 1) {
> -		ret = get_next_event_xfer(ec_dev, msg, cmd_version,
> -				sizeof(struct ec_response_get_next_event_v1));
> -		if (ret < 0 || msg->result != EC_RES_INVALID_VERSION)
> -			return ret;
> -
> -		/* Fallback to version 0 for future send attempts */
> -		cmd_version = 0;
> -	}
> -
> -	ret = get_next_event_xfer(ec_dev, msg, cmd_version,
> +	if (cmd_version == 0)
> +		return get_next_event_xfer(ec_dev, msg, event, 0,
>  				  sizeof(struct ec_response_get_next_event));
>  
> -	return ret;
> +	return get_next_event_xfer(ec_dev, msg, event, cmd_version,
> +				sizeof(struct ec_response_get_next_event_v1));
>  }
>  
>  static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
> @@ -606,7 +610,8 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
>  		return ret;
>  
>  	if (wake_event) {
> -		event_type = ec_dev->event_data.event_type;
> +		event_type =
> +			ec_dev->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK;
>  		host_event = cros_ec_get_host_event(ec_dev);
>  
>  		/*
> @@ -631,10 +636,12 @@ EXPORT_SYMBOL(cros_ec_get_next_event);
>  u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
>  {
>  	u32 host_event;
> +	const u8 event_type =
> +		ec_dev->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK;
>  
>  	BUG_ON(!ec_dev->mkbp_event_supported);
>  
> -	if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT)
> +	if (event_type != EC_MKBP_EVENT_HOST_EVENT)
>  		return 0;
>  
>  	if (ec_dev->event_size != sizeof(host_event)) {
> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index ab12e28f2107..63b5597294e7 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -115,7 +115,9 @@ struct cros_ec_command {
>   *            code.
>   * @pkt_xfer: Send packet to EC and get response.
>   * @lock: One transaction at a time.
> - * @mkbp_event_supported: True if this EC supports the MKBP event protocol.
> + * @mkbp_event_supported: 0 if MKBP not supported. Otherwise its value is
> + *                        the maximum supported version of the MKBP host event
> + *                        command + 1.
>   * @host_sleep_v1: True if this EC supports the sleep v1 command.
>   * @event_notifier: Interrupt event notifier for transport devices.
>   * @event_data: Raw payload transferred with the MKBP event.
> @@ -155,7 +157,8 @@ struct cros_ec_device {
>  	int (*pkt_xfer)(struct cros_ec_device *ec,
>  			struct cros_ec_command *msg);
>  	struct mutex lock;
> -	bool mkbp_event_supported;
> +	/* 0 == not supported, otherwise it supports version x - 1 */

You can remove this comment, is already in the doc.

> +	u8 mkbp_event_supported;
>  	bool host_sleep_v1;
>  	struct blocking_notifier_head event_notifier;
>  
> 

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

* Re: [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public
  2019-09-22 17:50 ` [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
  2019-09-30 13:15   ` Enric Balletbo i Serra
@ 2019-10-05 15:26   ` Jonathan Cameron
  1 sibling, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 15:26 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:09 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> Add a new function to return the number of MEMS sensors available in a
> ChromeOS Embedded Controller.
> It uses MOTIONSENSE_CMD_DUMP if available or a specific memory map ACPI
> registers to find out.
> 
> Also, make check_features public as it can be useful for other drivers
> to know whant the Embedded Controller supports.

what

> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Comments inline,

Thanks,

Jonathan

> ---
>  drivers/mfd/cros_ec_dev.c   | 61 ++++++++++++++++++++++++++++++++++++-
>  include/linux/mfd/cros_ec.h | 17 +++++++++++
>  2 files changed, 77 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
> index 6e6dfd6c1871..3be80183ccaa 100644
> --- a/drivers/mfd/cros_ec_dev.c
> +++ b/drivers/mfd/cros_ec_dev.c
> @@ -112,7 +112,7 @@ static const struct mfd_cell cros_ec_vbc_cells[] = {
>  	{ .name = "cros-ec-vbc", }
>  };
>  
> -static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
> +int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
>  {
>  	struct cros_ec_command *msg;
>  	int ret;
> @@ -143,12 +143,71 @@ static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
>  
>  	return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
>  }
> +EXPORT_SYMBOL_GPL(cros_ec_check_features);
>  
>  static void cros_ec_class_release(struct device *dev)
>  {
>  	kfree(to_cros_ec_dev(dev));
>  }
>  
> +int cros_ec_get_sensor_count(struct cros_ec_dev *ec)
> +{
> +	/*
> +	 * Issue a command to get the number of sensor reported.
> +	 * If not supported, check for legacy mode.
> +	 */
> +	int ret, sensor_count;
> +	struct ec_params_motion_sense *params;
> +	struct ec_response_motion_sense *resp;
> +	struct cros_ec_command *msg;
> +	struct cros_ec_device *ec_dev = ec->ec_dev;
> +	u8 status;
> +
> +	msg = kzalloc(sizeof(struct cros_ec_command) +
> +			max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> +	if (msg == NULL)

if (!msg) perhaps. I've not checked the driver for which version is commonly
used.

> +		return -ENOMEM;
> +
> +	msg->version = 1;
> +	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) {
> +		sensor_count = ret;
> +	} else if (msg->result != EC_RES_SUCCESS) {
> +		sensor_count = -EPROTO;
> +	} else {
> +		resp = (struct ec_response_motion_sense *)msg->data;
> +		sensor_count = resp->dump.sensor_count;
> +	}
> +	kfree(msg);
> +
> +	/*
> +	 * Check legacy mode: Let's find out if sensors are accessible
> +	 * via LPC interface.
> +	 */
> +	if (sensor_count == -EPROTO &&
> +	    ec->cmd_offset == 0 &&
> +	    ec_dev->cmd_readmem) {

Why not flip the logic here and return early if we have the value?
if (sensor_count > 0)
	return sensor_count;

	
> +		ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS,
> +				1, &status);
> +		if ((ret >= 0) &&
> +		    (status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) {
> +			/*
> +			 * We have 2 sensors, one in the lid, one in the base.
> +			 */
> +			sensor_count = 2;
> +		}

		If this failed do we not want to return that error?
		Doesn't this also return -EPROTO if there is only one sensor?
> +	}
> +	return sensor_count;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
> +
>  static void cros_ec_sensors_register(struct cros_ec_dev *ec)
>  {
>  	/*
> diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
> index 61c2875c2a40..578e0bbcafdc 100644
> --- a/include/linux/mfd/cros_ec.h
> +++ b/include/linux/mfd/cros_ec.h
> @@ -32,4 +32,21 @@ struct cros_ec_dev {
>  
>  #define to_cros_ec_dev(dev)  container_of(dev, struct cros_ec_dev, class_dev)
>  
> +/**
> + * cros_ec_check_features - Test for the presence of EC features
> + *
> + * Call this function to test whether the ChromeOS EC supports a feature.
> + *
> + * @ec_dev: EC device
> + * @msg: One of ec_feature_code values
> + * @return: 1 if supported, 0 if not
> + */
> +int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
> +
> +/*

Seems like we should be consistent and have kernel-doc style comments
for everything if we are going to do them for some functions.

However, docs should be with the code, not in the header where they
tend to rot.

> + * Return the number of MEMS sensors supported.
> + * Return < 0 in case of error.
> + */
> +int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
> +
>  #endif /* __LINUX_MFD_CROS_EC_H */


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

* Re: [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-09-22 17:50 ` [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
  2019-10-01 10:31   ` Enric Balletbo i Serra
@ 2019-10-05 15:35   ` Jonathan Cameron
  2019-10-05 15:36     ` Jonathan Cameron
  1 sibling, 1 reply; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 15:35 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:10 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> Similar to HID sensor stack, the new driver sits between cros_ec_dev
> and the iio device drivers:
> 
> EC based iio device topology would be:
> iio:device1 ->
> ...0/0000:00:1f.0/PNP0C09:00/GOOG0004:00/cros-ec-dev.6.auto/
>                                          cros-ec-sensorhub.7.auto/
>                                          cros-ec-accel.15.auto/
>                                          iio:device1
> 
> It will be expanded to control EC sensor FIFO.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

Minor bits inline.

My assumption for this sensor hub is it just acts as a
combiner of data and doesn't guarantee that the sample
rates are the same or anything like that?  If it does
it would be much nicer to support as a single sensor, but
if not this approach is the best way.

> ---
>  drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
>  drivers/platform/chrome/Kconfig               |  18 +-
>  drivers/platform/chrome/Makefile              |   1 +
>  drivers/platform/chrome/cros_ec_sensorhub.c   | 211 ++++++++++++++++++
>  .../linux/platform_data/cros_ec_sensorhub.h   |  21 ++
>  5 files changed, 249 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub.c
>  create mode 100644 include/linux/platform_data/cros_ec_sensorhub.h
> 
> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> index cdbb29cfb907..fefad9572790 100644
> --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> @@ -4,7 +4,7 @@
>  #
>  config IIO_CROS_EC_SENSORS_CORE
>  	tristate "ChromeOS EC Sensors Core"
> -	depends on SYSFS && CROS_EC
> +	depends on SYSFS && CROS_EC_SENSORHUB
>  	select IIO_BUFFER
>  	select IIO_TRIGGERED_BUFFER
>  	help
> diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
> index ee5f08ea57b6..add967236cfb 100644
> --- a/drivers/platform/chrome/Kconfig
> +++ b/drivers/platform/chrome/Kconfig
> @@ -132,9 +132,9 @@ config CROS_EC_LPC
>  	  module will be called cros_ec_lpcs.
>  
>  config CROS_EC_PROTO
> -        bool
> -        help
> -          ChromeOS EC communication protocol helpers.
> +	bool
> +	help
> +	  ChromeOS EC communication protocol helpers.
As already pointed out. Not relevant.
>  
>  config CROS_KBD_LED_BACKLIGHT
>  	tristate "Backlight LED support for Chrome OS keyboards"
> @@ -190,6 +190,18 @@ config CROS_EC_DEBUGFS
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called cros_ec_debugfs.
>  
> +config CROS_EC_SENSORHUB
> +	tristate "ChromeOS EC MEMS Senosr Hub"
> +	depends on CROS_EC && IIO
> +	help
> +	  Allow loading IIO sensors. This driver is loaded by MFD and will in
> +	  turn query the EC and register the sensors.
> +	  It also spreads the sensor data coming from the EC to the IIO sensorr
> +	  object.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called cros_ec_sensorhub.
> +
>  config CROS_EC_SYSFS
>  	tristate "ChromeOS EC control and information through sysfs"
>  	depends on MFD_CROS_EC_DEV && SYSFS
> diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> index 477ec3d1d1c9..a164c40dc099 100644
> --- a/drivers/platform/chrome/Makefile
> +++ b/drivers/platform/chrome/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o cros_ec_trace.o
>  obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
>  obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_chardev.o
>  obj-$(CONFIG_CROS_EC_LIGHTBAR)		+= cros_ec_lightbar.o
> +obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorhub.o
>  obj-$(CONFIG_CROS_EC_VBC)		+= cros_ec_vbc.o
>  obj-$(CONFIG_CROS_EC_DEBUGFS)		+= cros_ec_debugfs.o
>  obj-$(CONFIG_CROS_EC_SYSFS)		+= cros_ec_sysfs.o
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
> new file mode 100644
> index 000000000000..80688018ef66
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> @@ -0,0 +1,211 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * SensorHub: driver that discover sensors behind
> + * a ChromeOS Embedded controller.
> + *
> + * Copyright 2019 Google LLC
> + */
> +
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/fs.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/platform_data/cros_ec_commands.h>
> +#include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_device.h>
> +#include <linux/poll.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/platform_data/cros_ec_sensorhub.h>
> +
> +#define DRV_NAME		"cros-ec-sensorhub"
> +
> +static int cros_ec_sensors_register(struct device *dev,
> +		struct cros_ec_dev *ec)
> +{
> +	int ret, i, id, sensor_num;
> +	struct mfd_cell *sensor_cells;
> +	struct cros_ec_sensor_platform *sensor_platforms;
> +	int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
> +	struct ec_params_motion_sense *params;
> +	struct ec_response_motion_sense *resp;
> +	struct cros_ec_command *msg;
> +
> +	sensor_num = cros_ec_get_sensor_count(ec);
> +	if (sensor_num < 0) {
> +		dev_err(dev,
> +			"Unable to retrieve sensor information (err:%d)\n",
> +			sensor_num);
> +		return sensor_num;
> +	}
> +
> +	if (sensor_num == 0) {
> +		dev_err(dev, "Zero sensors reported.\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Build an array of sensors driver and register them all.

Single line comment syntax.

> +	 */
> +	msg = kzalloc(sizeof(struct cros_ec_command) +
> +		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> +	if (msg == NULL) {
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	msg->version = 1;
> +	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;
> +	resp = (struct ec_response_motion_sense *)msg->data;
> +
> +	/*
> +	 * Allocate 1 extra sensor if lid angle sensor is needed.

Single line comment is enough.

> +	 */
> +	sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell),
> +			       GFP_KERNEL);
> +	if (sensor_cells == NULL) {
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	sensor_platforms = kcalloc(sensor_num,
> +				   sizeof(struct cros_ec_sensor_platform),
> +				   GFP_KERNEL);
> +	if (sensor_platforms == NULL) {
> +		ret = -ENOMEM;
> +		goto error_platforms;
> +	}
> +
> +	id = 0;
> +	for (i = 0; i < sensor_num; i++) {
> +		params->cmd = MOTIONSENSE_CMD_INFO;
> +		params->info.sensor_num = i;
> +		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
> +		if (ret < 0) {
> +			dev_warn(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_BARO:
> +			sensor_cells[id].name = "cros-ec-baro";
> +			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(dev, "unknown type %d\n", resp->info.type);
> +			continue;
> +		}
> +		sensor_platforms[id].sensor_num = i;
> +		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)
> +		ec->has_kb_wake_angle = true;
> +
> +	if (cros_ec_check_features(ec,
> +				EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
> +		sensor_cells[id].name = "cros-ec-lid-angle";
> +		id++;
> +	}
> +
> +	ret = mfd_add_hotplug_devices(dev, sensor_cells, id);
> +	kfree(sensor_platforms);
> +error_platforms:
> +	kfree(sensor_cells);
> +error:
> +	kfree(msg);
> +	return ret;
> +}
> +
> +static struct cros_ec_sensor_platform sensor_platforms[] = {
> +	{ .sensor_num = 0 },
> +	{ .sensor_num = 1 }
> +};
> +
> +static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
> +	{
> +		.name = "cros-ec-accel-legacy",
> +		.platform_data = &sensor_platforms[0],
> +		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> +	},
> +	{
> +		.name = "cros-ec-accel-legacy",
> +		.platform_data = &sensor_platforms[1],
> +		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> +	}
> +};
> +
one line is normally enough ;)
> +
> +
> +static int cros_ec_sensorhub_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> +	int ret;
> +	struct cros_ec_sensorhub *data =
> +		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
> +
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->ec = ec;
> +	dev_set_drvdata(dev, data);
> +
> +	/* check whether this EC is a sensor hub. */
> +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
> +		ret = cros_ec_sensors_register(dev, ec);
> +	} else {
> +		/* Workaroud for older EC firmware */
> +		ret = mfd_add_hotplug_devices(dev,
> +				cros_ec_accel_legacy_cells,
> +				ARRAY_SIZE(cros_ec_accel_legacy_cells));
> +	}
> +	if (ret)
> +		dev_err(dev, "failed to add EC sensors: error %d\n", ret);
> +	return ret;
> +}
> +
> +static struct platform_driver cros_ec_sensorhub_driver = {
> +	.driver = {
> +		.name = DRV_NAME,
> +	},
> +	.probe = cros_ec_sensorhub_probe,
> +};
> +
> +module_platform_driver(cros_ec_sensorhub_driver);
> +
> +MODULE_ALIAS("platform:" DRV_NAME);
> +MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
> +MODULE_DESCRIPTION("ChromeOS EC MEMS Sensor Hub Driver");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> new file mode 100644
> index 000000000000..a8b64ecf5b9b
> --- /dev/null
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * cros_ec_sensorhub- Chrome OS EC MEMS Sensor Hub driver.
> + *
> + * Copyright (C) 2019 Google, Inc

My favourite moan of the day. what's the point of this blank line?

> + *
> + */
> +
> +#ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> +#define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> +
> +#include <linux/platform_data/cros_ec_commands.h>
> +
> +/**
> + * struct cros_ec_sensorhub - Sensor Hub device data.

If it's kernel-doc should be complete (include the elements).

> + */
> +struct cros_ec_sensorhub {
> +	struct cros_ec_dev *ec;
> +};
> +
> +#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */


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

* Re: [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-10-05 15:35   ` Jonathan Cameron
@ 2019-10-05 15:36     ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 15:36 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sat, 5 Oct 2019 16:35:05 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> On Sun, 22 Sep 2019 10:50:10 -0700
> Gwendal Grignou <gwendal@chromium.org> wrote:
> 
> > Similar to HID sensor stack, the new driver sits between cros_ec_dev
> > and the iio device drivers:
> > 
> > EC based iio device topology would be:
> > iio:device1 ->
> > ...0/0000:00:1f.0/PNP0C09:00/GOOG0004:00/cros-ec-dev.6.auto/
> >                                          cros-ec-sensorhub.7.auto/
> >                                          cros-ec-accel.15.auto/
> >                                          iio:device1
> > 
> > It will be expanded to control EC sensor FIFO.
> > 
> > Signed-off-by: Gwendal Grignou <gwendal@chromium.org>  
> 
> Minor bits inline.
> 
> My assumption for this sensor hub is it just acts as a
> combiner of data and doesn't guarantee that the sample
> rates are the same or anything like that?  If it does
> it would be much nicer to support as a single sensor, but
> if not this approach is the best way.
Ah. I'd missed the point.  This is really a bit rework of
existing functionality, not a new device type.  Ignore me ;)
> 
> > ---
> >  drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
> >  drivers/platform/chrome/Kconfig               |  18 +-
> >  drivers/platform/chrome/Makefile              |   1 +
> >  drivers/platform/chrome/cros_ec_sensorhub.c   | 211 ++++++++++++++++++
> >  .../linux/platform_data/cros_ec_sensorhub.h   |  21 ++
> >  5 files changed, 249 insertions(+), 4 deletions(-)
> >  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub.c
> >  create mode 100644 include/linux/platform_data/cros_ec_sensorhub.h
> > 
> > diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
> > index cdbb29cfb907..fefad9572790 100644
> > --- a/drivers/iio/common/cros_ec_sensors/Kconfig
> > +++ b/drivers/iio/common/cros_ec_sensors/Kconfig
> > @@ -4,7 +4,7 @@
> >  #
> >  config IIO_CROS_EC_SENSORS_CORE
> >  	tristate "ChromeOS EC Sensors Core"
> > -	depends on SYSFS && CROS_EC
> > +	depends on SYSFS && CROS_EC_SENSORHUB
> >  	select IIO_BUFFER
> >  	select IIO_TRIGGERED_BUFFER
> >  	help
> > diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
> > index ee5f08ea57b6..add967236cfb 100644
> > --- a/drivers/platform/chrome/Kconfig
> > +++ b/drivers/platform/chrome/Kconfig
> > @@ -132,9 +132,9 @@ config CROS_EC_LPC
> >  	  module will be called cros_ec_lpcs.
> >  
> >  config CROS_EC_PROTO
> > -        bool
> > -        help
> > -          ChromeOS EC communication protocol helpers.
> > +	bool
> > +	help
> > +	  ChromeOS EC communication protocol helpers.  
> As already pointed out. Not relevant.
> >  
> >  config CROS_KBD_LED_BACKLIGHT
> >  	tristate "Backlight LED support for Chrome OS keyboards"
> > @@ -190,6 +190,18 @@ config CROS_EC_DEBUGFS
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called cros_ec_debugfs.
> >  
> > +config CROS_EC_SENSORHUB
> > +	tristate "ChromeOS EC MEMS Senosr Hub"
> > +	depends on CROS_EC && IIO
> > +	help
> > +	  Allow loading IIO sensors. This driver is loaded by MFD and will in
> > +	  turn query the EC and register the sensors.
> > +	  It also spreads the sensor data coming from the EC to the IIO sensorr
> > +	  object.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > +	  module will be called cros_ec_sensorhub.
> > +
> >  config CROS_EC_SYSFS
> >  	tristate "ChromeOS EC control and information through sysfs"
> >  	depends on MFD_CROS_EC_DEV && SYSFS
> > diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> > index 477ec3d1d1c9..a164c40dc099 100644
> > --- a/drivers/platform/chrome/Makefile
> > +++ b/drivers/platform/chrome/Makefile
> > @@ -17,6 +17,7 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o cros_ec_trace.o
> >  obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
> >  obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_chardev.o
> >  obj-$(CONFIG_CROS_EC_LIGHTBAR)		+= cros_ec_lightbar.o
> > +obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorhub.o
> >  obj-$(CONFIG_CROS_EC_VBC)		+= cros_ec_vbc.o
> >  obj-$(CONFIG_CROS_EC_DEBUGFS)		+= cros_ec_debugfs.o
> >  obj-$(CONFIG_CROS_EC_SYSFS)		+= cros_ec_sysfs.o
> > diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
> > new file mode 100644
> > index 000000000000..80688018ef66
> > --- /dev/null
> > +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> > @@ -0,0 +1,211 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * SensorHub: driver that discover sensors behind
> > + * a ChromeOS Embedded controller.
> > + *
> > + * Copyright 2019 Google LLC
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/device.h>
> > +#include <linux/fs.h>
> > +#include <linux/miscdevice.h>
> > +#include <linux/module.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/mfd/cros_ec.h>
> > +#include <linux/platform_data/cros_ec_commands.h>
> > +#include <linux/platform_data/cros_ec_proto.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/poll.h>
> > +#include <linux/slab.h>
> > +#include <linux/types.h>
> > +#include <linux/uaccess.h>
> > +
> > +#include <linux/platform_data/cros_ec_sensorhub.h>
> > +
> > +#define DRV_NAME		"cros-ec-sensorhub"
> > +
> > +static int cros_ec_sensors_register(struct device *dev,
> > +		struct cros_ec_dev *ec)
> > +{
> > +	int ret, i, id, sensor_num;
> > +	struct mfd_cell *sensor_cells;
> > +	struct cros_ec_sensor_platform *sensor_platforms;
> > +	int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
> > +	struct ec_params_motion_sense *params;
> > +	struct ec_response_motion_sense *resp;
> > +	struct cros_ec_command *msg;
> > +
> > +	sensor_num = cros_ec_get_sensor_count(ec);
> > +	if (sensor_num < 0) {
> > +		dev_err(dev,
> > +			"Unable to retrieve sensor information (err:%d)\n",
> > +			sensor_num);
> > +		return sensor_num;
> > +	}
> > +
> > +	if (sensor_num == 0) {
> > +		dev_err(dev, "Zero sensors reported.\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Build an array of sensors driver and register them all.  
> 
> Single line comment syntax.
> 
> > +	 */
> > +	msg = kzalloc(sizeof(struct cros_ec_command) +
> > +		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> > +	if (msg == NULL) {
> > +		ret = -ENOMEM;
> > +		goto error;
> > +	}
> > +
> > +	msg->version = 1;
> > +	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;
> > +	resp = (struct ec_response_motion_sense *)msg->data;
> > +
> > +	/*
> > +	 * Allocate 1 extra sensor if lid angle sensor is needed.  
> 
> Single line comment is enough.
> 
> > +	 */
> > +	sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell),
> > +			       GFP_KERNEL);
> > +	if (sensor_cells == NULL) {
> > +		ret = -ENOMEM;
> > +		goto error;
> > +	}
> > +
> > +	sensor_platforms = kcalloc(sensor_num,
> > +				   sizeof(struct cros_ec_sensor_platform),
> > +				   GFP_KERNEL);
> > +	if (sensor_platforms == NULL) {
> > +		ret = -ENOMEM;
> > +		goto error_platforms;
> > +	}
> > +
> > +	id = 0;
> > +	for (i = 0; i < sensor_num; i++) {
> > +		params->cmd = MOTIONSENSE_CMD_INFO;
> > +		params->info.sensor_num = i;
> > +		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
> > +		if (ret < 0) {
> > +			dev_warn(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_BARO:
> > +			sensor_cells[id].name = "cros-ec-baro";
> > +			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(dev, "unknown type %d\n", resp->info.type);
> > +			continue;
> > +		}
> > +		sensor_platforms[id].sensor_num = i;
> > +		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)
> > +		ec->has_kb_wake_angle = true;
> > +
> > +	if (cros_ec_check_features(ec,
> > +				EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
> > +		sensor_cells[id].name = "cros-ec-lid-angle";
> > +		id++;
> > +	}
> > +
> > +	ret = mfd_add_hotplug_devices(dev, sensor_cells, id);
> > +	kfree(sensor_platforms);
> > +error_platforms:
> > +	kfree(sensor_cells);
> > +error:
> > +	kfree(msg);
> > +	return ret;
> > +}
> > +
> > +static struct cros_ec_sensor_platform sensor_platforms[] = {
> > +	{ .sensor_num = 0 },
> > +	{ .sensor_num = 1 }
> > +};
> > +
> > +static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
> > +	{
> > +		.name = "cros-ec-accel-legacy",
> > +		.platform_data = &sensor_platforms[0],
> > +		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> > +	},
> > +	{
> > +		.name = "cros-ec-accel-legacy",
> > +		.platform_data = &sensor_platforms[1],
> > +		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> > +	}
> > +};
> > +  
> one line is normally enough ;)
> > +
> > +
> > +static int cros_ec_sensorhub_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> > +	int ret;
> > +	struct cros_ec_sensorhub *data =
> > +		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
> > +
> > +	if (!data)
> > +		return -ENOMEM;
> > +
> > +	data->ec = ec;
> > +	dev_set_drvdata(dev, data);
> > +
> > +	/* check whether this EC is a sensor hub. */
> > +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
> > +		ret = cros_ec_sensors_register(dev, ec);
> > +	} else {
> > +		/* Workaroud for older EC firmware */
> > +		ret = mfd_add_hotplug_devices(dev,
> > +				cros_ec_accel_legacy_cells,
> > +				ARRAY_SIZE(cros_ec_accel_legacy_cells));
> > +	}
> > +	if (ret)
> > +		dev_err(dev, "failed to add EC sensors: error %d\n", ret);
> > +	return ret;
> > +}
> > +
> > +static struct platform_driver cros_ec_sensorhub_driver = {
> > +	.driver = {
> > +		.name = DRV_NAME,
> > +	},
> > +	.probe = cros_ec_sensorhub_probe,
> > +};
> > +
> > +module_platform_driver(cros_ec_sensorhub_driver);
> > +
> > +MODULE_ALIAS("platform:" DRV_NAME);
> > +MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
> > +MODULE_DESCRIPTION("ChromeOS EC MEMS Sensor Hub Driver");
> > +MODULE_LICENSE("GPL");
> > +
> > diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> > new file mode 100644
> > index 000000000000..a8b64ecf5b9b
> > --- /dev/null
> > +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> > @@ -0,0 +1,21 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * cros_ec_sensorhub- Chrome OS EC MEMS Sensor Hub driver.
> > + *
> > + * Copyright (C) 2019 Google, Inc  
> 
> My favourite moan of the day. what's the point of this blank line?
> 
> > + *
> > + */
> > +
> > +#ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> > +#define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> > +
> > +#include <linux/platform_data/cros_ec_commands.h>
> > +
> > +/**
> > + * struct cros_ec_sensorhub - Sensor Hub device data.  
> 
> If it's kernel-doc should be complete (include the elements).
> 
> > + */
> > +struct cros_ec_sensorhub {
> > +	struct cros_ec_dev *ec;
> > +};
> > +
> > +#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */  
> 


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

* Re: [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing
  2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (12 preceding siblings ...)
  2019-09-22 17:50 ` [PATCH 13/13] iio: cros_ec: Use Hertz as unit for sampling frequency Gwendal Grignou
@ 2019-10-05 15:39 ` Jonathan Cameron
  13 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 15:39 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:08 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> This patchset adds a sensorhub driver for spreading sensor
> events coming from the Embedded controller sensor FIFO:
> 
>        +---------------+ +--------------+ +----
>        | cros_ec_accel | | cros_ec_gyro | | ...
>        +---------------+ +--------------+ +----
>            id:0       \        id:1 |       / id:..
>                  +------------------------------+
>                  |       cros_ec_sensorhub      |
>                  +------------------------------+
>                  |           cros_ec_dev        |
>                  +------------------------------+
>                  | cros_ec_i2c, cros_ec_lpc, .. |
>                  +------------------------------+
>                                  |
>                                  EC
> 
> When new sensors events are present, the EC raises and interrupt,
> sensorhub reads the FIFO and uses the 'id' field to spread the event to
> the proper IIO sensors. This stack is similar to the HID sensor input
> stack.
> 
> The first 3 patches add a primitive cros_ec_sensorhub. MFD just have to
> register this driver if at least one sensor is presented by the EC.
> cros_ec_sensorhub retrieves more information from the EC to find out
> which sensors are actually present:
>   mfd: cros_ec: Add sensor_count and make check_features public
>   platform: cros_ec: Add cros_ec_sensor_hub driver
>   platform/mfd:iio: cros_ec: Register sensor through sensorhub
> 
> The next 3 patches prepare for FIFO support:
>   platform: chrome: cros-ec: record event timestamp in the hard irq
>   platform: chrome: cros_ec: Do not attempt to register a non-positive
>   platform: chrome: cros_ec: handle MKBP more events flag
> 
> The next 4 patches add FIFO support. An interface is added to connect
> the IIO sensors with cros_ec_sensorhub, and filters are needed to spread
> the timestamp when the EC send batches of events and deal with variation
> in interrupt delay.
>   platform: chrome: sensorhub: Add FIFO support
>   platform: chrome: sensorhub: Add code to spread timestmap
>   platform: chrome: sensorhub: Add median filter
>   iio: cros_ec: Use triggered buffer only when EC does not support FIFO
> 
> Finally, the last 3 patches present sensor information following the IIO
> ABI:
> -  Configurable EC timeout to allow batch mode in buffer/hwfifo_timeout,
>   in seconds.
> -  Hard coded EC FIFO size in buffer/hwfifo_watermark_max
> -  Sensor sampling frequency in hertz at sampling_frequency:
>   iio: cros_ec: Expose hwfifo_timeout
>   iio: cros_ec: Report hwfifo_watermark_max
>   iio: cros_ec: Use Hertz as unit for sampling frequency
> 
> For testing, libiio test tools can be used:
> A iio device link looks like:
> iio:device1 ->
> ...09:00/GOOG0004:00/cros-ec-dev.6.auto/cros-ec-sensorhub.7.auto/
>                      cros-ec-accel.15.auto/iio:device1
> 
> When FIFO is available, no trigger are presented. Once
> sampling_freqeuncy and hwfifo_timeout are set, sensor events flow
> when listening to /dev/iio:device1:
> echo 12 > sampling_frequency   # Set ODR to at least 12Hz
> echo .100 > buffer/hwfifo_timeout  # do not wait more than 100ms to
>                                    # to send samples
> iio_readdev -b 2 -T 1000 -s 2 iio:device1 2>/dev/null| od -x
> 0000000 ffd0 2e20 d990 0000 8630 b56c 07ea 0000
> 0000020 ffc0 2e10 d970 0000 877e b56c 07ea 0000
> 0000040`
> 
> When FIFO is not supported by the EC, a trigger is present in the
> directory. After registering a trigger, setting sampling_frequency,
> the latest data collected by the sensor will be retrieved by the host
> when the trigger expires.

This is the only bit that makes me wonder... For other devices we have
supported allowing triggers even when there is a fifo present.  I assume
we can disable the fifo if necessary?  In that case we use the hwfifo
path if no trigger is provided, but if there is one switch back to
the handling for when the hardware doesn't support such a fifo.

Could that model be useful here?

Thanks,

Jonathan

> 
> When cros_ec_accel_legacy driver is used, no FIFO is supported and the
> sampling frequency for the accelerometers is hard coded at 10Hz.
> 
> This set is built upon the master branch of
> git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
> 
> 
> Enrico Granata (2):
>   platform: chrome: cros_ec: Do not attempt to register a non-positive
>     IRQ number
>   platform: chrome: cros_ec: handle MKBP more events flag
> 
> Gwendal Grignou (11):
>   mfd: cros_ec: Add sensor_count and make check_features public
>   platform: cros_ec: Add cros_ec_sensor_hub driver
>   platform/mfd:iio: cros_ec: Register sensor through sensorhub
>   platform: chrome: cros-ec: record event timestamp in the hard irq
>   platform: chrome: sensorhub: Add FIFO support
>   platform: chrome: sensorhub: Add code to spread timestmap
>   platform: chrome: sensorhub: Add median filter
>   iio: cros_ec: Use triggered buffer only when EC does not support FIFO
>   iio: cros_ec: Expose hwfifo_timeout
>   iio: cros_ec: Report hwfifo_watermark_max
>   iio: cros_ec: Use Hertz as unit for sampling frequency
> 
>  drivers/iio/accel/cros_ec_accel_legacy.c      |  13 +-
>  drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
>  .../cros_ec_sensors/cros_ec_lid_angle.c       |   2 +-
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |  14 +-
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 251 ++++-
>  drivers/iio/light/cros_ec_light_prox.c        |  18 +-
>  drivers/iio/pressure/cros_ec_baro.c           |  12 +-
>  drivers/mfd/cros_ec_dev.c                     | 208 +---
>  drivers/platform/chrome/Kconfig               |  18 +-
>  drivers/platform/chrome/Makefile              |   2 +
>  drivers/platform/chrome/cros_ec.c             |  51 +-
>  drivers/platform/chrome/cros_ec_lpc.c         |   2 +
>  drivers/platform/chrome/cros_ec_proto.c       |  51 +-
>  drivers/platform/chrome/cros_ec_sensorhub.c   | 269 +++++
>  .../platform/chrome/cros_ec_sensorhub_ring.c  | 918 ++++++++++++++++++
>  .../linux/iio/common/cros_ec_sensors_core.h   |  29 +-
>  include/linux/mfd/cros_ec.h                   |  17 +
>  include/linux/platform_data/cros_ec_proto.h   |  30 +-
>  .../linux/platform_data/cros_ec_sensorhub.h   | 173 ++++
>  19 files changed, 1780 insertions(+), 300 deletions(-)
>  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub.c
>  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c
>  create mode 100644 include/linux/platform_data/cros_ec_sensorhub.h
> 


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

* Re: [PATCH 03/13] platform/mfd:iio: cros_ec: Register sensor through sensorhub
  2019-09-22 17:50 ` [PATCH 03/13] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
@ 2019-10-05 15:41   ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 15:41 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:11 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> - Add sensorhub include file
> - Remove duplicate code in mfd, since mfd just register
>   cros_ec_sensorhub if at least one sensor is present
> - Change iio cros_ec driver to get the pointer to the cros_ec_dev
>   through cros_ec_sensorhub.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Trivial comment inline.

Thanks,

Jonathan

> ---
>  drivers/iio/accel/cros_ec_accel_legacy.c      |   4 +-
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |   4 +-
>  .../cros_ec_sensors/cros_ec_sensors_core.c    |   4 +-
>  drivers/iio/light/cros_ec_light_prox.c        |   6 +-
>  drivers/mfd/cros_ec_dev.c                     | 203 ++----------------
>  drivers/platform/chrome/cros_ec_sensorhub.c   |   2 +-
>  include/linux/platform_data/cros_ec_proto.h   |   8 -
>  .../linux/platform_data/cros_ec_sensorhub.h   |   8 +
>  8 files changed, 34 insertions(+), 205 deletions(-)
> 
> diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
> index fcc3f999e482..c9af6fa0670d 100644
> --- a/drivers/iio/accel/cros_ec_accel_legacy.c
> +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
> @@ -23,6 +23,7 @@
>  #include <linux/slab.h>
>  #include <linux/platform_data/cros_ec_commands.h>
>  #include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_data/cros_ec_sensorhub.h>
>  #include <linux/platform_device.h>
>  
>  #define DRV_NAME	"cros-ec-accel-legacy"
> @@ -163,7 +164,8 @@ static const struct iio_chan_spec cros_ec_accel_legacy_channels[] = {
>  static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> -	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> +	struct cros_ec_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
> +	struct cros_ec_dev *ec = sensor_hub->ec;
>  	struct iio_dev *indio_dev;
>  	struct cros_ec_sensors_core_state *state;
>  	int ret;
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> index a6987726eeb8..5bd6f54afc42 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -20,6 +20,7 @@
>  #include <linux/module.h>
>  #include <linux/platform_data/cros_ec_commands.h>
>  #include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_data/cros_ec_sensorhub.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>  
> @@ -222,7 +223,8 @@ static const struct iio_info ec_sensors_info = {
>  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_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
> +	struct cros_ec_dev *ec_dev = sensor_hub->ec;
>  	struct iio_dev *indio_dev;
>  	struct cros_ec_sensors_state *state;
>  	struct iio_chan_spec *channel;
> 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
> index d2609e6feda4..81a7f692de2f 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -18,6 +18,7 @@
>  #include <linux/slab.h>
>  #include <linux/platform_data/cros_ec_commands.h>
>  #include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_data/cros_ec_sensorhub.h>
>  #include <linux/platform_device.h>
>  
>  static char *cros_ec_loc[] = {
> @@ -88,7 +89,8 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  {
>  	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_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
> +	struct cros_ec_dev *ec = sensor_hub->ec;
>  	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
>  	u32 ver_mask;
>  	int ret, i;
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index c5263b563fc1..205effc1f404 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -18,6 +18,7 @@
>  #include <linux/module.h>
>  #include <linux/platform_data/cros_ec_commands.h>
>  #include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_data/cros_ec_sensorhub.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>  
> @@ -169,13 +170,14 @@ static const struct iio_info cros_ec_light_prox_info = {
>  static int cros_ec_light_prox_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> -	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
> +	struct cros_ec_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
> +	struct cros_ec_dev *ec = sensor_hub->ec;
>  	struct iio_dev *indio_dev;
>  	struct cros_ec_light_prox_state *state;
>  	struct iio_chan_spec *channel;
>  	int ret;
>  
> -	if (!ec_dev || !ec_dev->ec_dev) {
> +	if (!ec || !ec->ec_dev) {
>  		dev_warn(dev, "No CROS EC device found.\n");
>  		return -EINVAL;
>  	}
> diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
> index 3be80183ccaa..3a583d3503ca 100644
> --- a/drivers/mfd/cros_ec_dev.c
> +++ b/drivers/mfd/cros_ec_dev.c
> @@ -78,6 +78,10 @@ static const struct mfd_cell cros_ec_rtc_cells[] = {
>  	{ .name = "cros-ec-rtc", },
>  };
>  
> +static const struct mfd_cell cros_ec_sensorhub_cells[] = {
> +	{ .name = "cros-ec-sensorhub", },
> +};
> +
>  static const struct mfd_cell cros_usbpd_charger_cells[] = {
>  	{ .name = "cros-usbpd-charger", },
>  	{ .name = "cros-usbpd-logger", },
> @@ -208,192 +212,6 @@ int cros_ec_get_sensor_count(struct cros_ec_dev *ec)
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
>  
> -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_status(ec->ec_dev, msg);
> -	if (ret < 0) {
> -		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 if lid angle sensor and/or FIFO are needed.
> -	 */
> -	sensor_cells = kcalloc(sensor_num + 2, sizeof(struct mfd_cell),
> -			       GFP_KERNEL);
> -	if (sensor_cells == NULL)
> -		goto error;
> -
> -	sensor_platforms = kcalloc(sensor_num,
> -				   sizeof(struct cros_ec_sensor_platform),
> -				   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_status(ec->ec_dev, msg);
> -		if (ret < 0) {
> -			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_BARO:
> -			sensor_cells[id].name = "cros-ec-baro";
> -			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)
> -		ec->has_kb_wake_angle = true;
> -
> -	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> -		sensor_cells[id].name = "cros-ec-ring";
> -		id++;
> -	}
> -	if (cros_ec_check_features(ec,
> -				EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
> -		sensor_cells[id].name = "cros-ec-lid-angle";
> -		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 struct cros_ec_sensor_platform sensor_platforms[] = {
> -	{ .sensor_num = 0 },
> -	{ .sensor_num = 1 }
> -};
> -
> -static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
> -	{
> -		.name = "cros-ec-accel-legacy",
> -		.platform_data = &sensor_platforms[0],
> -		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> -	},
> -	{
> -		.name = "cros-ec-accel-legacy",
> -		.platform_data = &sensor_platforms[1],
> -		.pdata_size = sizeof(struct cros_ec_sensor_platform),
> -	}
> -};
> -
> -static void cros_ec_accel_legacy_register(struct cros_ec_dev *ec)
> -{
> -	struct cros_ec_device *ec_dev = ec->ec_dev;
> -	u8 status;
> -	int ret;
> -
> -	/*
> -	 * ECs that need legacy support are the main EC, directly connected to
> -	 * the AP.
> -	 */
> -	if (ec->cmd_offset != 0)
> -		return;
> -
> -	/*
> -	 * Check if EC supports direct memory reads and if EC has
> -	 * accelerometers.
> -	 */
> -	if (ec_dev->cmd_readmem) {
> -		ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, 1,
> -					  &status);
> -		if (ret < 0) {
> -			dev_warn(ec->dev, "EC direct read error.\n");
> -			return;
> -		}
> -
> -		/* Check if EC has accelerometers. */
> -		if (!(status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) {
> -			dev_info(ec->dev, "EC does not have accelerometers.\n");
> -			return;
> -		}
> -	}
> -
> -	/*
> -	 * The device may still support accelerometers:
> -	 * it would be an older ARM based device that do not suppor the
> -	 * EC_CMD_GET_FEATURES command.
> -	 *
> -	 * Register 2 accelerometers, we will fail in the IIO driver if there
> -	 * are no sensors.
> -	 */
> -	ret = mfd_add_hotplug_devices(ec->dev, cros_ec_accel_legacy_cells,
> -				      ARRAY_SIZE(cros_ec_accel_legacy_cells));
> -	if (ret)
> -		dev_err(ec_dev->dev, "failed to add EC sensors\n");
> -}
> -
>  static int ec_device_probe(struct platform_device *pdev)
>  {
>  	int retval = -ENOMEM;
> @@ -449,11 +267,14 @@ static int ec_device_probe(struct platform_device *pdev)
>  		goto failed;
>  
>  	/* check whether this EC is a sensor hub. */
> -	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
> -		cros_ec_sensors_register(ec);
> -	else
> -		/* Workaroud for older EC firmware */
> -		cros_ec_accel_legacy_register(ec);
> +	if (cros_ec_get_sensor_count(ec) > 0) {
> +		retval = mfd_add_hotplug_devices(ec->dev,
> +				cros_ec_sensorhub_cells,
> +				ARRAY_SIZE(cros_ec_sensorhub_cells));
> +		if (retval)
> +			dev_err(ec->dev, "failed to add %s subdevice: %d\n",
> +				cros_ec_sensorhub_cells->name, retval);
> +	}
>  
>  	/*
>  	 * The following subdevices can be detected by sending the
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
> index 80688018ef66..01f11ed611fb 100644
> --- a/drivers/platform/chrome/cros_ec_sensorhub.c
> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> @@ -181,7 +181,7 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
>  	data->ec = ec;
>  	dev_set_drvdata(dev, data);
>  
> -	/* check whether this EC is a sensor hub. */
> +	/* Check whether this EC is a sensor hub. */

Change not related to this patches main purpose... 

>  	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
>  		ret = cros_ec_sensors_register(dev, ec);
>  	} else {
> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index eab7036cda09..e91e3fcb0348 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -167,14 +167,6 @@ struct cros_ec_device {
>  	struct platform_device *pd;
>  };
>  
> -/**
> - * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information.
> - * @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', ...)
> diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> index a8b64ecf5b9b..9295eabb16f6 100644
> --- a/include/linux/platform_data/cros_ec_sensorhub.h
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -11,6 +11,14 @@
>  
>  #include <linux/platform_data/cros_ec_commands.h>
>  
> +/**
> + * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information.
> + * @sensor_num: Id of the sensor, as reported by the EC.
> + */
> +struct cros_ec_sensor_platform {
> +	u8 sensor_num;
> +};
> +
>  /**
>   * struct cros_ec_sensorhub - Sensor Hub device data.
>   */


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

* Re: [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq
  2019-09-22 17:50 ` [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq Gwendal Grignou
  2019-10-01 10:32   ` Enric Balletbo i Serra
@ 2019-10-05 15:44   ` Jonathan Cameron
  1 sibling, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 15:44 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:12 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> To improve sensor timestamp precision, given EC and AP are in
> different time domains, the AP needs to try to record the exact
> moment an event was signalled to the AP by the EC as soon as
> possible after it happens.
> 
> First thing in the hard irq is the best place for this.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
If this is only going to be used in IIO paths, the there is a control
to set which clock is used.  This is really a legacy thing due to
me once picking a silly default, but we may be causing confusion by
having that control but it having no effect.

Otherwise looks good to me.

> ---
>  drivers/platform/chrome/cros_ec.c           | 18 +++++++++++++++++-
>  drivers/platform/chrome/cros_ec_lpc.c       |  2 ++
>  include/linux/platform_data/cros_ec_proto.h | 15 +++++++++++++++
>  3 files changed, 34 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
> index fd77e6fa74c2..f49eb1d1e3cd 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -31,6 +31,21 @@ static struct cros_ec_platform pd_p = {
>  	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
>  };
>  
> +s64 cros_ec_get_time_ns(void)
> +{
> +	return ktime_get_boottime_ns();
> +}
> +EXPORT_SYMBOL(cros_ec_get_time_ns);
> +
> +static irqreturn_t ec_irq_handler(int irq, void *data)
> +{
> +	struct cros_ec_device *ec_dev = data;
> +
> +	ec_dev->last_event_time = cros_ec_get_time_ns();
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
>  static irqreturn_t ec_irq_thread(int irq, void *data)
>  {
>  	struct cros_ec_device *ec_dev = data;
> @@ -132,7 +147,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
>  	}
>  
>  	if (ec_dev->irq) {
> -		err = devm_request_threaded_irq(dev, ec_dev->irq, NULL,
> +		err = devm_request_threaded_irq(
> +				dev, ec_dev->irq, ec_irq_handler,
>  				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>  				"chromeos-ec", ec_dev);
>  		if (err) {
> diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
> index 7d10d909435f..3c77496e164d 100644
> --- a/drivers/platform/chrome/cros_ec_lpc.c
> +++ b/drivers/platform/chrome/cros_ec_lpc.c
> @@ -313,6 +313,8 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
>  {
>  	struct cros_ec_device *ec_dev = data;
>  
> +	ec_dev->last_event_time = cros_ec_get_time_ns();
> +
>  	if (ec_dev->mkbp_event_supported &&
>  	    cros_ec_get_next_event(ec_dev, NULL) > 0)
>  		blocking_notifier_call_chain(&ec_dev->event_notifier, 0,
> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index e91e3fcb0348..ab12e28f2107 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -121,6 +121,8 @@ struct cros_ec_command {
>   * @event_data: Raw payload transferred with the MKBP event.
>   * @event_size: Size in bytes of the event data.
>   * @host_event_wake_mask: Mask of host events that cause wake from suspend.
> + * @last_event_time: exact time from the hard irq when we got notified of
> + *     a new event.
>   * @ec: The platform_device used by the mfd driver to interface with the
>   *      main EC.
>   * @pd: The platform_device used by the mfd driver to interface with the
> @@ -161,6 +163,7 @@ struct cros_ec_device {
>  	int event_size;
>  	u32 host_event_wake_mask;
>  	u32 last_resume_result;
> +	s64 last_event_time;
>  
>  	/* The platform devices used by the mfd driver */
>  	struct platform_device *ec;
> @@ -308,4 +311,16 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event);
>   */
>  u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev);
>  
> +/**
> + * cros_ec_get_time_ns - Return time in ns.
> + *
> + * This is the function used to record the time for last_event_time in struct
> + * cros_ec_device during the hard irq.
> + *
> + * This function is probably implemented using ktime_get_boot_ns(), but it's
> + * exposed here to make sure all cros_ec drivers use the same code path to get
> + * the time.
> + */
> +s64 cros_ec_get_time_ns(void);
> +
>  #endif /* __LINUX_CROS_EC_PROTO_H */


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

* Re: [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag
  2019-09-22 17:50 ` [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
  2019-10-01 10:32   ` Enric Balletbo i Serra
@ 2019-10-05 15:52   ` Jonathan Cameron
  1 sibling, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 15:52 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio,
	Enrico Granata

On Sun, 22 Sep 2019 10:50:14 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> From: Enrico Granata <egranata@chromium.org>
> 
> The ChromeOS EC has support for signaling to the host that
> a single IRQ can serve multiple MKBP events.

Be nice to those of us who don't know the system that well.
What are MKBP events?

> 
> Doing this serves an optimization purpose, as it minimizes the
> number of round-trips into the interrupt handling machinery, and
> it proves beneficial to sensor timestamping as it keeps the desired
> synchronization of event times between the two processors.
> 
> This patch adds kernel support for this EC feature, allowing the
> ec_irq to loop until all events have been served.
> 
> Signed-off-by: Enrico Granata <egranata@chromium.org>
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

Minor comments inline.

> ---
>  drivers/platform/chrome/cros_ec.c           | 33 +++++++++----
>  drivers/platform/chrome/cros_ec_proto.c     | 51 ++++++++++++---------
>  include/linux/platform_data/cros_ec_proto.h |  7 ++-
>  3 files changed, 57 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
> index 9c8dc7cdb2b7..4adc007c357c 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -46,25 +46,38 @@ static irqreturn_t ec_irq_handler(int irq, void *data)
>  	return IRQ_WAKE_THREAD;
>  }
>  
> -static irqreturn_t ec_irq_thread(int irq, void *data)
> +static bool ec_handle_event(struct cros_ec_device *ec_dev)
>  {
> -	struct cros_ec_device *ec_dev = data;
>  	bool wake_event = true;
> -	int ret;
> +	bool ec_has_more_events = false;
> +	int ret = cros_ec_get_next_event(ec_dev, &wake_event);
>  
> -	ret = cros_ec_get_next_event(ec_dev, &wake_event);

Moving definition of int to this line just added noise to the
patch.  I would leave that as it was to make reviewers life
ever so slightly simpler ;)

> +	if (ec_dev->mkbp_event_supported) {
> +		ec_has_more_events = (ret > 0) &&
> +			(ec_dev->event_data.event_type &
> +				EC_MKBP_HAS_MORE_EVENTS);
> +	}
>  
> -	/*
> -	 * Signal only if wake host events or any interrupt if
> -	 * cros_ec_get_next_event() returned an error (default value for
> -	 * wake_event is true)
> -	 */
> -	if (wake_event && device_may_wakeup(ec_dev->dev))
> +	if (device_may_wakeup(ec_dev->dev) && wake_event)
>  		pm_wakeup_event(ec_dev->dev, 0);
>  
>  	if (ret > 0)
>  		blocking_notifier_call_chain(&ec_dev->event_notifier,
>  					     0, ec_dev);
> +
> +	return ec_has_more_events;
> +

Drop this blank line.

> +}
> +
> +static irqreturn_t ec_irq_thread(int irq, void *data)
> +{
> +	struct cros_ec_device *ec_dev = data;
> +	bool ec_has_more_events;
> +
> +	do {
> +		ec_has_more_events = ec_handle_event(ec_dev);
> +	} while (ec_has_more_events);
> +
>  	return IRQ_HANDLED;
>  }
>  
> diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
> index f659f96bda12..70e6d6c93b8d 100644
> --- a/drivers/platform/chrome/cros_ec_proto.c
> +++ b/drivers/platform/chrome/cros_ec_proto.c
> @@ -425,10 +425,14 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
>  	ret = cros_ec_get_host_command_version_mask(ec_dev,
>  						    EC_CMD_GET_NEXT_EVENT,
>  						    &ver_mask);
> -	if (ret < 0 || ver_mask == 0)
> +	if (ret < 0 || ver_mask == 0) {
>  		ec_dev->mkbp_event_supported = 0;
> -	else
> -		ec_dev->mkbp_event_supported = 1;
> +		dev_info(ec_dev->dev, "MKBP not supported\n");
> +	} else {
> +		ec_dev->mkbp_event_supported = fls(ver_mask);
> +		dev_info(ec_dev->dev, "MKBP support version %u\n",
> +			ec_dev->mkbp_event_supported - 1);

dev_dbg perhaps?

> +	}
>  
>  	/* Probe if host sleep v1 is supported for S0ix failure detection. */
>  	ret = cros_ec_get_host_command_version_mask(ec_dev,
> @@ -519,6 +523,7 @@ EXPORT_SYMBOL(cros_ec_cmd_xfer_status);
>  
>  static int get_next_event_xfer(struct cros_ec_device *ec_dev,
>  			       struct cros_ec_command *msg,
> +			       struct ec_response_get_next_event_v1 *event,
>  			       int version, uint32_t size)
>  {
>  	int ret;
> @@ -531,7 +536,7 @@ static int get_next_event_xfer(struct cros_ec_device *ec_dev,
>  	ret = cros_ec_cmd_xfer(ec_dev, msg);
>  	if (ret > 0) {
>  		ec_dev->event_size = ret - 1;
> -		memcpy(&ec_dev->event_data, msg->data, ret);
> +		ec_dev->event_data = *event;
>  	}
>  
>  	return ret;
> @@ -539,30 +544,29 @@ static int get_next_event_xfer(struct cros_ec_device *ec_dev,
>  
>  static int get_next_event(struct cros_ec_device *ec_dev)
>  {
> -	u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)];
> -	struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
> -	static int cmd_version = 1;
> -	int ret;
> +	struct {
> +		struct cros_ec_command msg;
> +		struct ec_response_get_next_event_v1 event;
> +	} __packed buf;

Nice tidy up, but I don't think directly relevant to what this patch is doing..
I think that should be a precursor patch.

> +	struct cros_ec_command *msg = &buf.msg;
> +	struct ec_response_get_next_event_v1 *event = &buf.event;
> +	const int cmd_version = ec_dev->mkbp_event_supported - 1;
> +
> +	BUILD_BUG_ON(sizeof(union ec_response_get_next_data_v1) != 16);
> +
> +	memset(&buf, 0, sizeof(buf));
>  
>  	if (ec_dev->suspended) {
>  		dev_dbg(ec_dev->dev, "Device suspended.\n");
>  		return -EHOSTDOWN;
>  	}
>  
> -	if (cmd_version == 1) {
> -		ret = get_next_event_xfer(ec_dev, msg, cmd_version,
> -				sizeof(struct ec_response_get_next_event_v1));
> -		if (ret < 0 || msg->result != EC_RES_INVALID_VERSION)
> -			return ret;
> -
> -		/* Fallback to version 0 for future send attempts */
> -		cmd_version = 0;
> -	}
> -
> -	ret = get_next_event_xfer(ec_dev, msg, cmd_version,
> +	if (cmd_version == 0)
> +		return get_next_event_xfer(ec_dev, msg, event, 0,
>  				  sizeof(struct ec_response_get_next_event));
>  
> -	return ret;
> +	return get_next_event_xfer(ec_dev, msg, event, cmd_version,
> +				sizeof(struct ec_response_get_next_event_v1));
>  }
>  
>  static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
> @@ -606,7 +610,8 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
>  		return ret;
>  
>  	if (wake_event) {
> -		event_type = ec_dev->event_data.event_type;
> +		event_type =
> +			ec_dev->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK;
>  		host_event = cros_ec_get_host_event(ec_dev);
>  
>  		/*
> @@ -631,10 +636,12 @@ EXPORT_SYMBOL(cros_ec_get_next_event);
>  u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
>  {
>  	u32 host_event;
> +	const u8 event_type =
> +		ec_dev->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK;
>  
>  	BUG_ON(!ec_dev->mkbp_event_supported);
>  
> -	if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT)
> +	if (event_type != EC_MKBP_EVENT_HOST_EVENT)
>  		return 0;
>  
>  	if (ec_dev->event_size != sizeof(host_event)) {
> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index ab12e28f2107..63b5597294e7 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -115,7 +115,9 @@ struct cros_ec_command {
>   *            code.
>   * @pkt_xfer: Send packet to EC and get response.
>   * @lock: One transaction at a time.
> - * @mkbp_event_supported: True if this EC supports the MKBP event protocol.
> + * @mkbp_event_supported: 0 if MKBP not supported. Otherwise its value is
> + *                        the maximum supported version of the MKBP host event
> + *                        command + 1.
>   * @host_sleep_v1: True if this EC supports the sleep v1 command.
>   * @event_notifier: Interrupt event notifier for transport devices.
>   * @event_data: Raw payload transferred with the MKBP event.
> @@ -155,7 +157,8 @@ struct cros_ec_device {
>  	int (*pkt_xfer)(struct cros_ec_device *ec,
>  			struct cros_ec_command *msg);
>  	struct mutex lock;
> -	bool mkbp_event_supported;
> +	/* 0 == not supported, otherwise it supports version x - 1 */
> +	u8 mkbp_event_supported;
>  	bool host_sleep_v1;
>  	struct blocking_notifier_head event_notifier;
>  


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

* Re: [PATCH 07/13] platform: chrome: sensorhub: Add FIFO support
  2019-09-22 17:50 ` [PATCH 07/13] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
@ 2019-10-05 16:08   ` Jonathan Cameron
  2019-10-05 16:14     ` Jonathan Cameron
  0 siblings, 1 reply; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:08 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:15 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> cros_ec_sensorhub registers a listener and query motion sense FIFO,
> spread to iio sensors registers.
> 
> To test, we can use libiio:
> iiod&
> iio_readdev -u ip:localhost -T 10000 -s 25 -b 16 cros-ec-gyro | od -x
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
I haven't fully gotten my head around this but it looks more or less correct
to me.  I'm in a fussy mode though so various nitpicks inline.

Thanks,

Jonathan

> ---
>  drivers/iio/accel/cros_ec_accel_legacy.c      |   1 +
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |   1 +
>  .../cros_ec_sensors/cros_ec_sensors_core.c    |  13 +
>  drivers/iio/light/cros_ec_light_prox.c        |   1 +
>  drivers/iio/pressure/cros_ec_baro.c           |   1 +
>  drivers/platform/chrome/Makefile              |   3 +-
>  drivers/platform/chrome/cros_ec_sensorhub.c   | 130 ++++--
>  .../platform/chrome/cros_ec_sensorhub_ring.c  | 374 ++++++++++++++++++
>  .../linux/iio/common/cros_ec_sensors_core.h   |   3 +
>  .../linux/platform_data/cros_ec_sensorhub.h   |  79 ++++
>  10 files changed, 569 insertions(+), 37 deletions(-)
>  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c
> 
> diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
> index c9af6fa0670d..591c0d962c44 100644
> --- a/drivers/iio/accel/cros_ec_accel_legacy.c
> +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
> @@ -212,6 +212,7 @@ static struct platform_driver cros_ec_accel_platform_driver = {
>  		.name	= DRV_NAME,
>  	},
>  	.probe		= cros_ec_accel_legacy_probe,
> +	.remove		= cros_ec_sensors_core_clean,
>  };
>  module_platform_driver(cros_ec_accel_platform_driver);
>  
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> index 5bd6f54afc42..a88dd8deade9 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -329,6 +329,7 @@ static struct platform_driver cros_ec_sensors_platform_driver = {
>  		.pm	= &cros_ec_sensors_pm_ops,
>  	},
>  	.probe		= cros_ec_sensors_probe,
> +	.remove		= cros_ec_sensors_core_clean,
>  	.id_table	= cros_ec_sensors_ids,
>  };
>  module_platform_driver(cros_ec_sensors_platform_driver);
> 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
> index 81a7f692de2f..43eb1d42820e 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -160,6 +160,19 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
>  
> +int cros_ec_sensors_core_clean(struct platform_device *pdev)
> +{
> +	struct cros_ec_sensorhub *sensor_hub =
> +		dev_get_drvdata(pdev->dev.parent);
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	u8 sensor_num = st->param.info.sensor_num;
> +
> +	cros_ec_sensorhub_unregister_push_data(sensor_hub, sensor_num);

blank line here.

> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_clean);
> +
>  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
>  				 u16 opt_length)
>  {
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index 205effc1f404..c431e4d1482d 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -270,6 +270,7 @@ static struct platform_driver cros_ec_light_prox_platform_driver = {
>  		.pm	= &cros_ec_sensors_pm_ops,
>  	},
>  	.probe		= cros_ec_light_prox_probe,
> +	.remove		= cros_ec_sensors_core_clean,
>  	.id_table	= cros_ec_light_prox_ids,
>  };
>  module_platform_driver(cros_ec_light_prox_platform_driver);
> diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
> index 2354302375de..2f4d6d3ab41d 100644
> --- a/drivers/iio/pressure/cros_ec_baro.c
> +++ b/drivers/iio/pressure/cros_ec_baro.c
> @@ -201,6 +201,7 @@ static struct platform_driver cros_ec_baro_platform_driver = {
>  		.name	= "cros-ec-baro",
>  	},
>  	.probe		= cros_ec_baro_probe,
> +	.remove		= cros_ec_sensors_core_clean,
>  	.id_table	= cros_ec_baro_ids,
>  };
>  module_platform_driver(cros_ec_baro_platform_driver);
> diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> index a164c40dc099..cb709048c003 100644
> --- a/drivers/platform/chrome/Makefile
> +++ b/drivers/platform/chrome/Makefile
> @@ -17,7 +17,8 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o cros_ec_trace.o
>  obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
>  obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_chardev.o
>  obj-$(CONFIG_CROS_EC_LIGHTBAR)		+= cros_ec_lightbar.o
> -obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorhub.o
> +cros_ec_sensorsupport-objs			:= cros_ec_sensorhub_ring.o cros_ec_sensorhub.o
> +obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorsupport.o
>  obj-$(CONFIG_CROS_EC_VBC)		+= cros_ec_vbc.o
>  obj-$(CONFIG_CROS_EC_DEBUGFS)		+= cros_ec_debugfs.o
>  obj-$(CONFIG_CROS_EC_SYSFS)		+= cros_ec_sysfs.o
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
> index 01f11ed611fb..cf93120719c4 100644
> --- a/drivers/platform/chrome/cros_ec_sensorhub.c
> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> @@ -26,15 +26,14 @@
>  #define DRV_NAME		"cros-ec-sensorhub"
>  
>  static int cros_ec_sensors_register(struct device *dev,
> -		struct cros_ec_dev *ec)
> +		struct cros_ec_sensorhub *sensorhub)
>  {
>  	int ret, i, id, sensor_num;
>  	struct mfd_cell *sensor_cells;
>  	struct cros_ec_sensor_platform *sensor_platforms;
>  	int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
> -	struct ec_params_motion_sense *params;
> -	struct ec_response_motion_sense *resp;
> -	struct cros_ec_command *msg;
> +	struct cros_ec_command *msg = sensorhub->msg;
> +	struct cros_ec_dev *ec = sensorhub->ec;
>  
>  	sensor_num = cros_ec_get_sensor_count(ec);
>  	if (sensor_num < 0) {
> @@ -49,32 +48,17 @@ static int cros_ec_sensors_register(struct device *dev,
>  		return -EINVAL;
>  	}
>  
> -	/*
> -	 * Build an array of sensors driver and register them all.
> -	 */
> -	msg = kzalloc(sizeof(struct cros_ec_command) +
> -		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> -	if (msg == NULL) {
> -		ret = -ENOMEM;
> -		goto error;
> -	}
> -
>  	msg->version = 1;
> -	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;
> -	resp = (struct ec_response_motion_sense *)msg->data;
> +	msg->insize = sizeof(struct ec_response_motion_sense);
> +	msg->outsize = sizeof(struct ec_params_motion_sense);
>  
>  	/*
>  	 * Allocate 1 extra sensor if lid angle sensor is needed.
>  	 */
>  	sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell),
>  			       GFP_KERNEL);
> -	if (sensor_cells == NULL) {
> -		ret = -ENOMEM;
> -		goto error;
> -	}
> +	if (sensor_cells == NULL)
> +		return -ENOMEM;
>  
>  	sensor_platforms = kcalloc(sensor_num,
>  				   sizeof(struct cros_ec_sensor_platform),
> @@ -86,15 +70,15 @@ static int cros_ec_sensors_register(struct device *dev,
>  
>  	id = 0;
>  	for (i = 0; i < sensor_num; i++) {
> -		params->cmd = MOTIONSENSE_CMD_INFO;
> -		params->info.sensor_num = i;
> +		sensorhub->params->cmd = MOTIONSENSE_CMD_INFO;
> +		sensorhub->params->info.sensor_num = i;
>  		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
>  		if (ret < 0) {
>  			dev_warn(dev, "no info for EC sensor %d : %d/%d\n",
>  				 i, ret, msg->result);
>  			continue;
>  		}
> -		switch (resp->info.type) {
> +		switch (sensorhub->resp->info.type) {
>  		case MOTIONSENSE_TYPE_ACCEL:
>  			sensor_cells[id].name = "cros-ec-accel";
>  			break;
> @@ -117,7 +101,8 @@ static int cros_ec_sensors_register(struct device *dev,
>  			sensor_cells[id].name = "cros-ec-activity";
>  			break;
>  		default:
> -			dev_warn(dev, "unknown type %d\n", resp->info.type);
> +			dev_warn(dev, "unknown type %d\n",
> +				 sensorhub->resp->info.type);
>  			continue;
>  		}
>  		sensor_platforms[id].sensor_num = i;
> @@ -125,7 +110,7 @@ static int cros_ec_sensors_register(struct device *dev,
>  		sensor_cells[id].pdata_size =
>  			sizeof(struct cros_ec_sensor_platform);
>  
> -		sensor_type[resp->info.type]++;
> +		sensor_type[sensorhub->resp->info.type]++;
>  		id++;
>  	}
>  
> @@ -142,8 +127,6 @@ static int cros_ec_sensors_register(struct device *dev,
>  	kfree(sensor_platforms);
>  error_platforms:
>  	kfree(sensor_cells);
> -error:
> -	kfree(msg);
>  	return ret;
>  }
>  
> @@ -165,41 +148,116 @@ static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
>  	}
>  };
>  
> -
> -
>  static int cros_ec_sensorhub_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
>  	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
>  	int ret;
> -	struct cros_ec_sensorhub *data =
> -		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
> +	struct cros_ec_sensorhub *data;
> +	struct cros_ec_command *msg;
> +
> +	msg = devm_kzalloc(dev, sizeof(struct cros_ec_command) +
> +			max((u16)sizeof(struct ec_params_motion_sense),
> +			    ec->ec_dev->max_response), GFP_KERNEL);
> +	if (!msg)
> +		return -ENOMEM;
> +	msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
>  
> +	data = devm_kzalloc(dev, sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
>  	if (!data)
>  		return -ENOMEM;
>  
> +	data->dev = dev;
>  	data->ec = ec;
> +
> +	mutex_init(&data->cmd_lock);
> +	data->msg = msg;
> +	data->params = (struct ec_params_motion_sense *)msg->data;
> +	data->resp = (struct ec_response_motion_sense *)msg->data;
> +
>  	dev_set_drvdata(dev, data);
>  
>  	/* Check whether this EC is a sensor hub. */
>  	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
> -		ret = cros_ec_sensors_register(dev, ec);
> +		ret = cros_ec_sensors_register(dev, data);
>  	} else {
>  		/* Workaroud for older EC firmware */
> +		ec->has_kb_wake_angle = true;
>  		ret = mfd_add_hotplug_devices(dev,
>  				cros_ec_accel_legacy_cells,
>  				ARRAY_SIZE(cros_ec_accel_legacy_cells));
>  	}
> -	if (ret)
> +	if (ret) {
>  		dev_err(dev, "failed to add EC sensors: error %d\n", ret);
> +		return ret;
> +	}
> +
> +	/*
> +	 * If the EC does not have a FIFO, the sensors will query their data
> +	 * themselves via sysfs or a software trigger.
> +	 */
> +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> +		ret = cros_ec_sensorhub_ring_add(data);
> +		/*
> +		 * The msg and its data is not under the control of the ring
> +		 * handler.
> +		 */
> +	}
>  	return ret;
>  }
>  
> +static int cros_ec_sensorhub_remove(struct platform_device *pdev)
> +{
> +	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
> +	struct cros_ec_dev *ec = sensorhub->ec;
> +
> +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> +		cros_ec_sensorhub_ring_remove(sensorhub);
> +	return 0;
> +}
> +
> +/*
> + * When the EC is suspending, we must stop sending interrupt,
> + * we may use the same interrupt line for waking up the device.
> + * Tell the EC to stop sending non-interrupt event on the iio ring.
> + */
> +static int __maybe_unused cros_ec_ring_prepare(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
> +	struct cros_ec_dev *ec = sensorhub->ec;
> +
> +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> +		return cros_ec_sensorhub_ring_fifo_toggle(sensorhub, false);
> +	return 0;
> +}
> +
> +static void __maybe_unused cros_ec_ring_complete(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
> +	struct cros_ec_dev *ec = sensorhub->ec;
> +
> +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> +		cros_ec_sensorhub_ring_fifo_toggle(sensorhub, true);
> +}
> +
> +#if CONFIG_PM_SLEEP
> +static const struct dev_pm_ops cros_ec_sensorhub_ring_pm_ops = {
> +	.prepare = cros_ec_ring_prepare,
> +	.complete = cros_ec_ring_complete
> +};
> +#else
> +static const struct dev_pm_ops cros_ec_sensorhub_ring_pm_ops = { };
> +#endif
> +
>  static struct platform_driver cros_ec_sensorhub_driver = {
>  	.driver = {
>  		.name = DRV_NAME,
> +		.pm = &cros_ec_sensorhub_ring_pm_ops,
>  	},
>  	.probe = cros_ec_sensorhub_probe,
> +	.remove = cros_ec_sensorhub_remove,
>  };
>  
>  module_platform_driver(cros_ec_sensorhub_driver);
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> new file mode 100644
> index 000000000000..8cd533d5542e
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> @@ -0,0 +1,374 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * cros_ec_sensorhub_ring - Driver for Chrome OS EC Sensor hub FIFO.
> + *
> + * Copyright 2019 Google LLC
> + */
> +
> +#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/module.h>
> +#include <linux/platform_data/cros_ec_commands.h>
> +#include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_data/cros_ec_sensorhub.h>
> +#include <linux/platform_device.h>
> +#include <linux/sort.h>
> +#include <linux/slab.h>
> +

One blank line almost always enough. 

> +
> +static inline int cros_sensorhub_send_sample(
> +		struct cros_ec_sensorhub *sensorhub,
> +		struct cros_ec_sensors_ring_sample *sample)
> +{
> +	int id = sample->sensor_id;
> +	cros_ec_sensorhub_push_data_cb_t cb;
> +	struct iio_dev *indio_dev;
> +
> +	if (id > CROS_EC_SENSOR_MAX)
> +		return -EINVAL;
> +
> +	cb = sensorhub->push_data[id].push_data_cb;
> +	if (!cb)
> +		return 0;
> +
> +	indio_dev = sensorhub->push_data[id].indio_dev;
> +
> +	return cb(indio_dev, sample->vector, sample->timestamp);
> +}
> +
> +int cros_ec_sensorhub_register_push_data(
> +		struct cros_ec_sensorhub *sensorhub,
> +		u8 sensor_num,
> +		struct iio_dev *indio_dev,
> +		cros_ec_sensorhub_push_data_cb_t cb)
> +{
> +	if (sensor_num > CROS_EC_SENSOR_MAX)
> +		return -EINVAL;
> +	if (sensorhub->push_data[sensor_num].indio_dev != NULL)
> +		return -EINVAL;
> +
> +	sensorhub->push_data[sensor_num].indio_dev = indio_dev;
> +	sensorhub->push_data[sensor_num].push_data_cb = cb;

I always like a blank line before a simple return. Looks nice and
helps me sport the end of the function.

> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensorhub_register_push_data);
> +
> +void cros_ec_sensorhub_unregister_push_data(
> +		struct cros_ec_sensorhub *sensorhub,
> +		u8 sensor_num)
> +{
> +	sensorhub->push_data[sensor_num].indio_dev = NULL;
> +	sensorhub->push_data[sensor_num].push_data_cb = NULL;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensorhub_unregister_push_data);
> +
> +int cros_ec_sensorhub_ring_fifo_toggle(
> +		struct cros_ec_sensorhub *sensorhub,
> +		bool on)
> +{
> +	int ret;
> +
> +	mutex_lock(&sensorhub->cmd_lock);
> +	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE;
> +	sensorhub->params->fifo_int_enable.enable = on;
> +
> +	sensorhub->msg->outsize = sizeof(struct ec_params_motion_sense);
> +	sensorhub->msg->insize = sizeof(struct ec_response_motion_sense);
> +
> +	ret = cros_ec_cmd_xfer_status(sensorhub->ec->ec_dev, sensorhub->msg);
> +	/* We expect to receive a payload of 4 bytes, ignore. */
> +	if (ret > 0)
> +		ret = 0;
> +	mutex_unlock(&sensorhub->cmd_lock);
> +	return ret;
> +}
> +
> +/*
> + * 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 (includes b point, EC timebase).
> + * fifo_timestamp: EC IRQ, kernel timebase (aka c)
> + * current_timestamp: calculated event timestamp, kernel timebase (aka a')
> + * in: incoming FIFO event from EC (includes a point, EC timebase)
> + * out: outgoing event to user space (includes a')
Almost kernel-doc.  Nice to make it compliant.
> + */
> +static bool cros_ec_ring_process_event(
> +				struct cros_ec_sensorhub *sensorhub,
> +				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;
> +	/* Do not populate the filter based on asynchronous events. */
> +	const int async_flags = in->flags &
> +		(MOTIONSENSE_SENSOR_FLAG_ODR | MOTIONSENSE_SENSOR_FLAG_FLUSH);
> +	const s64 now = cros_ec_get_time_ns();
> +
> +	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP && !async_flags) {
> +		s64 a = in->timestamp;
> +		s64 b = fifo_info->info.timestamp;
> +		s64 c = fifo_timestamp;
> +		s64 new_timestamp;
> +
> +		/*
> +		 * disable filtering since we might add more jitter
> +		 * if b is in a random point in time
> +		 */
> +		new_timestamp = c - b * 1000 + a * 1000;

White space is cheap. Add a line here to make it easier
for the eye to parse.

> +		/*
> +		 * 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 = in->flags;
> +		/*
> +		 * No other payload information provided with
> +		 * flush ack.
> +		 */
> +		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;
> +	if (*current_timestamp - now > 0) {
> +		/* If the timestamp is in the future. */
> +		out->timestamp = now;
> +	} else {
> +		out->timestamp = *current_timestamp;
> +	}

Blank line here breaks up logic blocks.

> +	out->flag = in->flags;
> +	for (axis = 0; axis < 3; axis++)
> +		out->vector[axis] = in->data[axis];

blank line here helps me to read it.

> +	return true;
> +}
> +
> +/*
> + * cros_ec_sensorhub_ring_handler - the trigger handler function
> + *
> + * @sensorhub: device information.
> + *
> + * Called by the notifier, process the EC sensor FIFO queue.
> + */
> +static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
> +{
> +	struct cros_ec_fifo_info *fifo_info = &sensorhub->fifo_info;
> +	struct cros_ec_dev *ec = sensorhub->ec;
> +	s64    fifo_timestamp, current_timestamp;

Tabs here are ugly and not consistent with surrounding lines.

> +	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;
> +
> +	mutex_lock(&sensorhub->cmd_lock);
> +	/* Get FIFO information */

This comment seems to be wrong.. It is just reading a timestamp.

> +	fifo_timestamp = sensorhub->fifo_timestamp[NEW_TS];
> +	/* Copy elements in the main fifo */
> +	if (fifo_info->info.total_lost) {
> +		/* Need to retrieve the number of lost vectors per sensor */
> +		sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
> +		sensorhub->msg->outsize = 1;
> +		sensorhub->msg->insize =
> +			sizeof(struct ec_response_motion_sense_fifo_info) +
> +			sizeof(u16) * CROS_EC_SENSOR_MAX;
> +
> +		if (cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg) < 0) {
> +			mutex_unlock(&sensorhub->cmd_lock);
> +			return;
> +		}
> +		memcpy(fifo_info, &sensorhub->resp->fifo_info,
> +		       sizeof(*fifo_info));
> +		fifo_timestamp = cros_ec_get_time_ns();
> +	}
> +	if (fifo_info->info.count > sensorhub->fifo_size ||
> +	    fifo_info->info.size != sensorhub->fifo_size) {
> +		dev_warn(sensorhub->dev,
> +			 "Mismatch EC data: count %d, size %d - expected %d",
> +			 fifo_info->info.count, fifo_info->info.size,
> +			 sensorhub->fifo_size);
> +		mutex_unlock(&sensorhub->cmd_lock);
> +		return;
> +	}
> +
> +	current_timestamp = sensorhub->fifo_timestamp[LAST_TS];
> +	out = sensorhub->ring;
> +	for (i = 0; i < fifo_info->info.count; i += number_data) {
> +		sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_READ;
> +		sensorhub->params->fifo_read.max_data_vector =
> +			fifo_info->info.count - i;
> +		sensorhub->msg->outsize =
> +			sizeof(struct ec_params_motion_sense);
> +		sensorhub->msg->insize =
> +			sizeof(sensorhub->resp->fifo_read) +
> +			sensorhub->params->fifo_read.max_data_vector *
> +			  sizeof(struct ec_response_motion_sensor_data);
> +		ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
> +		if (ret < 0) {
> +			dev_warn(sensorhub->dev, "Fifo error: %d\n", ret);
> +			break;
> +		}
> +		number_data = sensorhub->resp->fifo_read.number_data;
> +		if (number_data == 0) {
> +			dev_dbg(sensorhub->dev, "Unexpected empty FIFO\n");
> +			break;

Given you break from here, might as well drop the else in the next line.

> +		} else if (number_data > fifo_info->info.count - i) {
> +			dev_warn(sensorhub->dev,
> +				 "Invalid EC data: too many entry received: %d, expected %d",
> +				 number_data, fifo_info->info.count - i);
> +			break;

Same as above. The else is a bit pointless as flow won't go beyond here if the
earlier statements have broken out.

> +		} else if (out + number_data >
> +			   sensorhub->ring + fifo_info->info.count) {
> +			dev_warn(sensorhub->dev,
> +				 "Too many samples: %d (%zd data) to %d entries for expected %d entries",
> +				 i, out - sensorhub->ring, i + number_data,
> +				 fifo_info->info.count);
> +			break;
> +		}

Blank line here to separate the error cases from where we go next.

> +		for (in = sensorhub->resp->fifo_read.data, j = 0;
> +		     j < number_data; j++, in++) {
> +			if (cros_ec_ring_process_event(
> +					sensorhub, fifo_info, fifo_timestamp,
> +					&current_timestamp, in, out)) {
> +				sensor_mask |= (1 << in->sensor_num);
> +				out++;
> +			}
> +		}
> +	}
> +	mutex_unlock(&sensorhub->cmd_lock);
> +	last_out = out;
> +
> +	if (out == sensorhub->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;
> +
> +	/* Warn on lost samples. */
> +	for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
> +		if (fifo_info->info.total_lost) {
> +			int lost = fifo_info->lost[i];
> +
> +			if (lost) {
> +				dev_warn(sensorhub->dev,
> +					"Sensor %d: lost: %d out of %d\n", i,
> +					lost, fifo_info->info.total_lost);

Is there potential to fill the log with this? I'm not sure if dev_warn
is rate limited.

> +			}
> +		}
> +	}
> +
> +	/* push the event into the kfifo */
> +	for (out = sensorhub->ring; out < last_out; out++)
> +		cros_sensorhub_send_sample(sensorhub, out);
> +
> +ring_handler_end:
> +	sensorhub->fifo_timestamp[LAST_TS] = current_timestamp;
> +}
> +
> +static int cros_ec_sensorhub_event(struct notifier_block *nb,
> +	unsigned long queued_during_suspend, void *_notify)
> +{
> +	struct cros_ec_sensorhub *sensorhub;
> +	struct cros_ec_device *ec_dev;
> +
> +	sensorhub = container_of(nb, struct cros_ec_sensorhub, notifier);
> +	ec_dev = sensorhub->ec->ec_dev;
> +
> +	if (ec_dev->event_data.event_type != EC_MKBP_EVENT_SENSOR_FIFO)
> +		return NOTIFY_DONE;
> +
> +	if (ec_dev->event_size != sizeof(ec_dev->event_data.data.sensor_fifo)) {
> +		dev_warn(ec_dev->dev, "Invalid fifo info size\n");
> +		return NOTIFY_DONE;
> +	}
> +
> +	if (queued_during_suspend)
> +		return NOTIFY_OK;
> +
> +	sensorhub->fifo_info.info = ec_dev->event_data.data.sensor_fifo.info;
> +	sensorhub->fifo_timestamp[NEW_TS] = ec_dev->last_event_time;
> +	cros_ec_sensorhub_ring_handler(sensorhub);
> +	return NOTIFY_OK;
> +}
> +
> +int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
> +{
> +	struct cros_ec_dev *ec = sensorhub->ec;
> +	int ret;
> +
> +	/* Retrieve FIFO information */
> +	sensorhub->msg->version = 2;
> +	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
> +	sensorhub->msg->outsize = 1;
> +	sensorhub->msg->insize =
> +		sizeof(struct ec_response_motion_sense_fifo_info) +
> +		sizeof(u16) * CROS_EC_SENSOR_MAX;
> +
> +	ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * Allocate the full fifo.
> +	 * We need to copy the whole FIFO to set timestamps properly *
> +	 */
> +	sensorhub->fifo_size = sensorhub->resp->fifo_info.size;
> +	sensorhub->ring = devm_kcalloc(sensorhub->dev, sensorhub->fifo_size,
> +			sizeof(*sensorhub->ring), GFP_KERNEL);
> +	if (!sensorhub->ring)
> +		return -ENOMEM;
> +
> +	sensorhub->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
> +
> +	/* register the notifier that will act as a top half interrupt. */
> +	sensorhub->notifier.notifier_call = cros_ec_sensorhub_event;
> +	ret = blocking_notifier_chain_register(&ec->ec_dev->event_notifier,
> +					       &sensorhub->notifier);
> +	if (ret < 0) {
> +		dev_warn(sensorhub->dev, "failed to register notifier\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * Start collection samples.

Single line comment. Also with appropriate rename of that function will
be fairly obvious.

> +	 */
> +	ret = cros_ec_sensorhub_ring_fifo_toggle(sensorhub, true);
> +	return ret;
	return cros_ec_sensorhub...

> +}
> +
> +int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensorhub)
> +{
> +	struct cros_ec_device *ec_dev = sensorhub->ec->ec_dev;
> +
> +	/*
> +	 * Disable the ring, prevent EC interrupt to the AP for nothing.

If will fit on a single line, stick to a single line.

> +	 */
> +	cros_ec_sensorhub_ring_fifo_toggle(sensorhub, false);

Odd name for an enable disable function.  I'd expect 'toggle' to take no
parameter but to flip from which ever state we are in to the other one.
That's not what is happening here.

> +	blocking_notifier_chain_unregister(&ec_dev->event_notifier,
> +					   &sensorhub->notifier);
> +	return 0;
> +}
> +
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index bb331e6356a9..abef66d0e884 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -116,6 +116,9 @@ struct platform_device;
>  int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			      struct iio_dev *indio_dev, bool physical_device);
>  
> +/* To remove association of physical device to cros_ec_sensorhub. */
> +int cros_ec_sensors_core_clean(struct platform_device *pdev);
> +
>  /**
>   * cros_ec_sensors_capture() - the trigger handler function
>   * @irq:	the interrupt number.
> diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> index 9295eabb16f6..18cda568c58a 100644
> --- a/include/linux/platform_data/cros_ec_sensorhub.h
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -9,6 +9,7 @@
>  #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
>  #define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
>  
> +#include <linux/iio/iio.h>

Comment below. I wouldn't expect to see this include here.

>  #include <linux/platform_data/cros_ec_commands.h>
>  
>  /**
> @@ -19,11 +20,89 @@ struct cros_ec_sensor_platform {
>  	u8 sensor_num;
>  };
>  
> +/**
> + * Callback function to send datum to specific sensors.

It's not kernel-doc.

> + */
> +typedef int (*cros_ec_sensorhub_push_data_cb_t)(
> +		struct iio_dev *indio_dev,

I would add a forwards def of struct iio_dev and not
include the iio header.

> +		s16 *data,
> +		s64 timestamp);
> +
> +struct cros_ec_sensorhub_sensor_push_data {
> +	struct iio_dev *indio_dev;
> +	cros_ec_sensorhub_push_data_cb_t push_data_cb;
> +};
> +
> +enum {
> +	LAST_TS,
> +	NEW_TS,
> +	ALL_TS
> +};
> +
> +#define CROS_EC_SENSOR_MAX 16
> +
> +struct __ec_todo_packed 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[3];
> +	s64      timestamp;
> +} __packed;
> +
>  /**
>   * struct cros_ec_sensorhub - Sensor Hub device data.
>   */
>  struct cros_ec_sensorhub {
> +	struct device *dev;
>  	struct cros_ec_dev *ec;
> +
> +	/* Structure to send FIFO requests. */
> +	struct cros_ec_command *msg;
> +	struct ec_params_motion_sense *params;
> +	struct ec_response_motion_sense *resp;
> +	struct mutex cmd_lock;
> +
> +	/* Notifier to kick the FIFO interrupt */
> +	struct notifier_block notifier;
> +
> +	/* Preprocessed ring to send to kfifos */
> +	struct cros_ec_sensors_ring_sample *ring;
> +
> +	s64    fifo_timestamp[ALL_TS];
> +	struct cros_ec_fifo_info fifo_info;
> +	int    fifo_size;
> +
> +	/*
> +	 * Dynamic array to be able to spread datum to iio sensor objects.
> +	 */
> +	struct cros_ec_sensorhub_sensor_push_data push_data[CROS_EC_SENSOR_MAX];
>  };
>  
> +/**
> + * Function to register the callback to the sensor hub.
> + * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
> + * from the EC.

Looks like kernel-doc but isn't. + should be with the code, not in the header
where it will tend to rot.

> + */
> +int cros_ec_sensorhub_register_push_data(
> +		struct cros_ec_sensorhub *sensor_hub,
> +		u8 sensor_num,
> +		struct iio_dev *indio_dev,
> +		cros_ec_sensorhub_push_data_cb_t cb);
> +void cros_ec_sensorhub_unregister_push_data(
> +		struct cros_ec_sensorhub *sensor_hub,
> +		u8 sensor_num);
> +
> +/**

/* otherwise it's kernel-doc...

> + * Add/Remove the fifo functionality if the EC supports it.
> + */
> +int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensor_hub);
> +int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensor_hub);
> +int cros_ec_sensorhub_ring_fifo_toggle(
> +		struct cros_ec_sensorhub *sensorhub,
> +		bool on);
> +
>  #endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */


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

* Re: [PATCH 07/13] platform: chrome: sensorhub: Add FIFO support
  2019-10-05 16:08   ` Jonathan Cameron
@ 2019-10-05 16:14     ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:14 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sat, 5 Oct 2019 17:08:41 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> On Sun, 22 Sep 2019 10:50:15 -0700
> Gwendal Grignou <gwendal@chromium.org> wrote:
> 
> > cros_ec_sensorhub registers a listener and query motion sense FIFO,
> > spread to iio sensors registers.
> > 
> > To test, we can use libiio:
> > iiod&
> > iio_readdev -u ip:localhost -T 10000 -s 25 -b 16 cros-ec-gyro | od -x
> > 
> > Signed-off-by: Gwendal Grignou <gwendal@chromium.org>  
> I haven't fully gotten my head around this but it looks more or less correct
> to me.  I'm in a fussy mode though so various nitpicks inline.

Oops. I realised I'd missed a likely race just after closing this.

> 
> Thanks,
> 
> Jonathan
> 
> > ---
> >  drivers/iio/accel/cros_ec_accel_legacy.c      |   1 +
> >  .../common/cros_ec_sensors/cros_ec_sensors.c  |   1 +
> >  .../cros_ec_sensors/cros_ec_sensors_core.c    |  13 +
> >  drivers/iio/light/cros_ec_light_prox.c        |   1 +
> >  drivers/iio/pressure/cros_ec_baro.c           |   1 +
> >  drivers/platform/chrome/Makefile              |   3 +-
> >  drivers/platform/chrome/cros_ec_sensorhub.c   | 130 ++++--
> >  .../platform/chrome/cros_ec_sensorhub_ring.c  | 374 ++++++++++++++++++
> >  .../linux/iio/common/cros_ec_sensors_core.h   |   3 +
> >  .../linux/platform_data/cros_ec_sensorhub.h   |  79 ++++
> >  10 files changed, 569 insertions(+), 37 deletions(-)
> >  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c
> > 
> > diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
> > index c9af6fa0670d..591c0d962c44 100644
> > --- a/drivers/iio/accel/cros_ec_accel_legacy.c
> > +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
> > @@ -212,6 +212,7 @@ static struct platform_driver cros_ec_accel_platform_driver = {
> >  		.name	= DRV_NAME,
> >  	},
> >  	.probe		= cros_ec_accel_legacy_probe,
As the whole of probe is done with devm_ we have a possible issue.
The userspace interfaces are added right at the end of probe, by
devm_iio_device_register, ensuring the hardware is definitely ready for
interaction.   By having remove here we don't run into issues on the
remove path.  
cros_ec_sensors_core_clean is called 'before' the iio_device_unregister
that effectively occurs in managed unwind.  That means userspace can
still be issuing new stuff after core_clean has been called.

I'm not totally sure that's an issue, but I'd much rather not
have to think about it.  Can we change the init code to
use managed registration so that the stuff that we are
doing in core_clean can occur automatically via the devm framework?
devm_add_action_or_reset may help here.

Thanks,

Jonathan


> > +	.remove		= cros_ec_sensors_core_clean,
> >  };
> >  module_platform_driver(cros_ec_accel_platform_driver);
> >  
> > diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> > index 5bd6f54afc42..a88dd8deade9 100644
> > --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> > +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> > @@ -329,6 +329,7 @@ static struct platform_driver cros_ec_sensors_platform_driver = {
> >  		.pm	= &cros_ec_sensors_pm_ops,
> >  	},
> >  	.probe		= cros_ec_sensors_probe,
> > +	.remove		= cros_ec_sensors_core_clean,
> >  	.id_table	= cros_ec_sensors_ids,
> >  };
> >  module_platform_driver(cros_ec_sensors_platform_driver);
> > 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
> > index 81a7f692de2f..43eb1d42820e 100644
> > --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > @@ -160,6 +160,19 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
> >  }
> >  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
> >  
> > +int cros_ec_sensors_core_clean(struct platform_device *pdev)
> > +{
> > +	struct cros_ec_sensorhub *sensor_hub =
> > +		dev_get_drvdata(pdev->dev.parent);
> > +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> > +	u8 sensor_num = st->param.info.sensor_num;
> > +
> > +	cros_ec_sensorhub_unregister_push_data(sensor_hub, sensor_num);  
> 
> blank line here.
> 
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_clean);
> > +
> >  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
> >  				 u16 opt_length)
> >  {
> > diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> > index 205effc1f404..c431e4d1482d 100644
> > --- a/drivers/iio/light/cros_ec_light_prox.c
> > +++ b/drivers/iio/light/cros_ec_light_prox.c
> > @@ -270,6 +270,7 @@ static struct platform_driver cros_ec_light_prox_platform_driver = {
> >  		.pm	= &cros_ec_sensors_pm_ops,
> >  	},
> >  	.probe		= cros_ec_light_prox_probe,
> > +	.remove		= cros_ec_sensors_core_clean,
> >  	.id_table	= cros_ec_light_prox_ids,
> >  };
> >  module_platform_driver(cros_ec_light_prox_platform_driver);
> > diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
> > index 2354302375de..2f4d6d3ab41d 100644
> > --- a/drivers/iio/pressure/cros_ec_baro.c
> > +++ b/drivers/iio/pressure/cros_ec_baro.c
> > @@ -201,6 +201,7 @@ static struct platform_driver cros_ec_baro_platform_driver = {
> >  		.name	= "cros-ec-baro",
> >  	},
> >  	.probe		= cros_ec_baro_probe,
> > +	.remove		= cros_ec_sensors_core_clean,
> >  	.id_table	= cros_ec_baro_ids,
> >  };
> >  module_platform_driver(cros_ec_baro_platform_driver);
> > diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> > index a164c40dc099..cb709048c003 100644
> > --- a/drivers/platform/chrome/Makefile
> > +++ b/drivers/platform/chrome/Makefile
> > @@ -17,7 +17,8 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o cros_ec_trace.o
> >  obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
> >  obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_chardev.o
> >  obj-$(CONFIG_CROS_EC_LIGHTBAR)		+= cros_ec_lightbar.o
> > -obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorhub.o
> > +cros_ec_sensorsupport-objs			:= cros_ec_sensorhub_ring.o cros_ec_sensorhub.o
> > +obj-$(CONFIG_CROS_EC_SENSORHUB)		+= cros_ec_sensorsupport.o
> >  obj-$(CONFIG_CROS_EC_VBC)		+= cros_ec_vbc.o
> >  obj-$(CONFIG_CROS_EC_DEBUGFS)		+= cros_ec_debugfs.o
> >  obj-$(CONFIG_CROS_EC_SYSFS)		+= cros_ec_sysfs.o
> > diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
> > index 01f11ed611fb..cf93120719c4 100644
> > --- a/drivers/platform/chrome/cros_ec_sensorhub.c
> > +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> > @@ -26,15 +26,14 @@
> >  #define DRV_NAME		"cros-ec-sensorhub"
> >  
> >  static int cros_ec_sensors_register(struct device *dev,
> > -		struct cros_ec_dev *ec)
> > +		struct cros_ec_sensorhub *sensorhub)
> >  {
> >  	int ret, i, id, sensor_num;
> >  	struct mfd_cell *sensor_cells;
> >  	struct cros_ec_sensor_platform *sensor_platforms;
> >  	int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
> > -	struct ec_params_motion_sense *params;
> > -	struct ec_response_motion_sense *resp;
> > -	struct cros_ec_command *msg;
> > +	struct cros_ec_command *msg = sensorhub->msg;
> > +	struct cros_ec_dev *ec = sensorhub->ec;
> >  
> >  	sensor_num = cros_ec_get_sensor_count(ec);
> >  	if (sensor_num < 0) {
> > @@ -49,32 +48,17 @@ static int cros_ec_sensors_register(struct device *dev,
> >  		return -EINVAL;
> >  	}
> >  
> > -	/*
> > -	 * Build an array of sensors driver and register them all.
> > -	 */
> > -	msg = kzalloc(sizeof(struct cros_ec_command) +
> > -		      max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
> > -	if (msg == NULL) {
> > -		ret = -ENOMEM;
> > -		goto error;
> > -	}
> > -
> >  	msg->version = 1;
> > -	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;
> > -	resp = (struct ec_response_motion_sense *)msg->data;
> > +	msg->insize = sizeof(struct ec_response_motion_sense);
> > +	msg->outsize = sizeof(struct ec_params_motion_sense);
> >  
> >  	/*
> >  	 * Allocate 1 extra sensor if lid angle sensor is needed.
> >  	 */
> >  	sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell),
> >  			       GFP_KERNEL);
> > -	if (sensor_cells == NULL) {
> > -		ret = -ENOMEM;
> > -		goto error;
> > -	}
> > +	if (sensor_cells == NULL)
> > +		return -ENOMEM;
> >  
> >  	sensor_platforms = kcalloc(sensor_num,
> >  				   sizeof(struct cros_ec_sensor_platform),
> > @@ -86,15 +70,15 @@ static int cros_ec_sensors_register(struct device *dev,
> >  
> >  	id = 0;
> >  	for (i = 0; i < sensor_num; i++) {
> > -		params->cmd = MOTIONSENSE_CMD_INFO;
> > -		params->info.sensor_num = i;
> > +		sensorhub->params->cmd = MOTIONSENSE_CMD_INFO;
> > +		sensorhub->params->info.sensor_num = i;
> >  		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
> >  		if (ret < 0) {
> >  			dev_warn(dev, "no info for EC sensor %d : %d/%d\n",
> >  				 i, ret, msg->result);
> >  			continue;
> >  		}
> > -		switch (resp->info.type) {
> > +		switch (sensorhub->resp->info.type) {
> >  		case MOTIONSENSE_TYPE_ACCEL:
> >  			sensor_cells[id].name = "cros-ec-accel";
> >  			break;
> > @@ -117,7 +101,8 @@ static int cros_ec_sensors_register(struct device *dev,
> >  			sensor_cells[id].name = "cros-ec-activity";
> >  			break;
> >  		default:
> > -			dev_warn(dev, "unknown type %d\n", resp->info.type);
> > +			dev_warn(dev, "unknown type %d\n",
> > +				 sensorhub->resp->info.type);
> >  			continue;
> >  		}
> >  		sensor_platforms[id].sensor_num = i;
> > @@ -125,7 +110,7 @@ static int cros_ec_sensors_register(struct device *dev,
> >  		sensor_cells[id].pdata_size =
> >  			sizeof(struct cros_ec_sensor_platform);
> >  
> > -		sensor_type[resp->info.type]++;
> > +		sensor_type[sensorhub->resp->info.type]++;
> >  		id++;
> >  	}
> >  
> > @@ -142,8 +127,6 @@ static int cros_ec_sensors_register(struct device *dev,
> >  	kfree(sensor_platforms);
> >  error_platforms:
> >  	kfree(sensor_cells);
> > -error:
> > -	kfree(msg);
> >  	return ret;
> >  }
> >  
> > @@ -165,41 +148,116 @@ static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
> >  	}
> >  };
> >  
> > -
> > -
> >  static int cros_ec_sensorhub_probe(struct platform_device *pdev)
> >  {
> >  	struct device *dev = &pdev->dev;
> >  	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> >  	int ret;
> > -	struct cros_ec_sensorhub *data =
> > -		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
> > +	struct cros_ec_sensorhub *data;
> > +	struct cros_ec_command *msg;
> > +
> > +	msg = devm_kzalloc(dev, sizeof(struct cros_ec_command) +
> > +			max((u16)sizeof(struct ec_params_motion_sense),
> > +			    ec->ec_dev->max_response), GFP_KERNEL);
> > +	if (!msg)
> > +		return -ENOMEM;
> > +	msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
> >  
> > +	data = devm_kzalloc(dev, sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
> >  	if (!data)
> >  		return -ENOMEM;
> >  
> > +	data->dev = dev;
> >  	data->ec = ec;
> > +
> > +	mutex_init(&data->cmd_lock);
> > +	data->msg = msg;
> > +	data->params = (struct ec_params_motion_sense *)msg->data;
> > +	data->resp = (struct ec_response_motion_sense *)msg->data;
> > +
> >  	dev_set_drvdata(dev, data);
> >  
> >  	/* Check whether this EC is a sensor hub. */
> >  	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
> > -		ret = cros_ec_sensors_register(dev, ec);
> > +		ret = cros_ec_sensors_register(dev, data);
> >  	} else {
> >  		/* Workaroud for older EC firmware */
> > +		ec->has_kb_wake_angle = true;
> >  		ret = mfd_add_hotplug_devices(dev,
> >  				cros_ec_accel_legacy_cells,
> >  				ARRAY_SIZE(cros_ec_accel_legacy_cells));
> >  	}
> > -	if (ret)
> > +	if (ret) {
> >  		dev_err(dev, "failed to add EC sensors: error %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	/*
> > +	 * If the EC does not have a FIFO, the sensors will query their data
> > +	 * themselves via sysfs or a software trigger.
> > +	 */
> > +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> > +		ret = cros_ec_sensorhub_ring_add(data);
> > +		/*
> > +		 * The msg and its data is not under the control of the ring
> > +		 * handler.
> > +		 */
> > +	}
> >  	return ret;
> >  }
> >  
> > +static int cros_ec_sensorhub_remove(struct platform_device *pdev)
> > +{
> > +	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
> > +	struct cros_ec_dev *ec = sensorhub->ec;
> > +
> > +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> > +		cros_ec_sensorhub_ring_remove(sensorhub);
> > +	return 0;
> > +}
> > +
> > +/*
> > + * When the EC is suspending, we must stop sending interrupt,
> > + * we may use the same interrupt line for waking up the device.
> > + * Tell the EC to stop sending non-interrupt event on the iio ring.
> > + */
> > +static int __maybe_unused cros_ec_ring_prepare(struct device *dev)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
> > +	struct cros_ec_dev *ec = sensorhub->ec;
> > +
> > +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> > +		return cros_ec_sensorhub_ring_fifo_toggle(sensorhub, false);
> > +	return 0;
> > +}
> > +
> > +static void __maybe_unused cros_ec_ring_complete(struct device *dev)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
> > +	struct cros_ec_dev *ec = sensorhub->ec;
> > +
> > +	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> > +		cros_ec_sensorhub_ring_fifo_toggle(sensorhub, true);
> > +}
> > +
> > +#if CONFIG_PM_SLEEP
> > +static const struct dev_pm_ops cros_ec_sensorhub_ring_pm_ops = {
> > +	.prepare = cros_ec_ring_prepare,
> > +	.complete = cros_ec_ring_complete
> > +};
> > +#else
> > +static const struct dev_pm_ops cros_ec_sensorhub_ring_pm_ops = { };
> > +#endif
> > +
> >  static struct platform_driver cros_ec_sensorhub_driver = {
> >  	.driver = {
> >  		.name = DRV_NAME,
> > +		.pm = &cros_ec_sensorhub_ring_pm_ops,
> >  	},
> >  	.probe = cros_ec_sensorhub_probe,
> > +	.remove = cros_ec_sensorhub_remove,
> >  };
> >  
> >  module_platform_driver(cros_ec_sensorhub_driver);
> > diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> > new file mode 100644
> > index 000000000000..8cd533d5542e
> > --- /dev/null
> > +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> > @@ -0,0 +1,374 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * cros_ec_sensorhub_ring - Driver for Chrome OS EC Sensor hub FIFO.
> > + *
> > + * Copyright 2019 Google LLC
> > + */
> > +
> > +#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/module.h>
> > +#include <linux/platform_data/cros_ec_commands.h>
> > +#include <linux/platform_data/cros_ec_proto.h>
> > +#include <linux/platform_data/cros_ec_sensorhub.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/sort.h>
> > +#include <linux/slab.h>
> > +  
> 
> One blank line almost always enough. 
> 
> > +
> > +static inline int cros_sensorhub_send_sample(
> > +		struct cros_ec_sensorhub *sensorhub,
> > +		struct cros_ec_sensors_ring_sample *sample)
> > +{
> > +	int id = sample->sensor_id;
> > +	cros_ec_sensorhub_push_data_cb_t cb;
> > +	struct iio_dev *indio_dev;
> > +
> > +	if (id > CROS_EC_SENSOR_MAX)
> > +		return -EINVAL;
> > +
> > +	cb = sensorhub->push_data[id].push_data_cb;
> > +	if (!cb)
> > +		return 0;
> > +
> > +	indio_dev = sensorhub->push_data[id].indio_dev;
> > +
> > +	return cb(indio_dev, sample->vector, sample->timestamp);
> > +}
> > +
> > +int cros_ec_sensorhub_register_push_data(
> > +		struct cros_ec_sensorhub *sensorhub,
> > +		u8 sensor_num,
> > +		struct iio_dev *indio_dev,
> > +		cros_ec_sensorhub_push_data_cb_t cb)
> > +{
> > +	if (sensor_num > CROS_EC_SENSOR_MAX)
> > +		return -EINVAL;
> > +	if (sensorhub->push_data[sensor_num].indio_dev != NULL)
> > +		return -EINVAL;
> > +
> > +	sensorhub->push_data[sensor_num].indio_dev = indio_dev;
> > +	sensorhub->push_data[sensor_num].push_data_cb = cb;  
> 
> I always like a blank line before a simple return. Looks nice and
> helps me sport the end of the function.
> 
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cros_ec_sensorhub_register_push_data);
> > +
> > +void cros_ec_sensorhub_unregister_push_data(
> > +		struct cros_ec_sensorhub *sensorhub,
> > +		u8 sensor_num)
> > +{
> > +	sensorhub->push_data[sensor_num].indio_dev = NULL;
> > +	sensorhub->push_data[sensor_num].push_data_cb = NULL;
> > +}
> > +EXPORT_SYMBOL_GPL(cros_ec_sensorhub_unregister_push_data);
> > +
> > +int cros_ec_sensorhub_ring_fifo_toggle(
> > +		struct cros_ec_sensorhub *sensorhub,
> > +		bool on)
> > +{
> > +	int ret;
> > +
> > +	mutex_lock(&sensorhub->cmd_lock);
> > +	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE;
> > +	sensorhub->params->fifo_int_enable.enable = on;
> > +
> > +	sensorhub->msg->outsize = sizeof(struct ec_params_motion_sense);
> > +	sensorhub->msg->insize = sizeof(struct ec_response_motion_sense);
> > +
> > +	ret = cros_ec_cmd_xfer_status(sensorhub->ec->ec_dev, sensorhub->msg);
> > +	/* We expect to receive a payload of 4 bytes, ignore. */
> > +	if (ret > 0)
> > +		ret = 0;
> > +	mutex_unlock(&sensorhub->cmd_lock);
> > +	return ret;
> > +}
> > +
> > +/*
> > + * 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 (includes b point, EC timebase).
> > + * fifo_timestamp: EC IRQ, kernel timebase (aka c)
> > + * current_timestamp: calculated event timestamp, kernel timebase (aka a')
> > + * in: incoming FIFO event from EC (includes a point, EC timebase)
> > + * out: outgoing event to user space (includes a')  
> Almost kernel-doc.  Nice to make it compliant.
> > + */
> > +static bool cros_ec_ring_process_event(
> > +				struct cros_ec_sensorhub *sensorhub,
> > +				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;
> > +	/* Do not populate the filter based on asynchronous events. */
> > +	const int async_flags = in->flags &
> > +		(MOTIONSENSE_SENSOR_FLAG_ODR | MOTIONSENSE_SENSOR_FLAG_FLUSH);
> > +	const s64 now = cros_ec_get_time_ns();
> > +
> > +	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP && !async_flags) {
> > +		s64 a = in->timestamp;
> > +		s64 b = fifo_info->info.timestamp;
> > +		s64 c = fifo_timestamp;
> > +		s64 new_timestamp;
> > +
> > +		/*
> > +		 * disable filtering since we might add more jitter
> > +		 * if b is in a random point in time
> > +		 */
> > +		new_timestamp = c - b * 1000 + a * 1000;  
> 
> White space is cheap. Add a line here to make it easier
> for the eye to parse.
> 
> > +		/*
> > +		 * 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 = in->flags;
> > +		/*
> > +		 * No other payload information provided with
> > +		 * flush ack.
> > +		 */
> > +		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;
> > +	if (*current_timestamp - now > 0) {
> > +		/* If the timestamp is in the future. */
> > +		out->timestamp = now;
> > +	} else {
> > +		out->timestamp = *current_timestamp;
> > +	}  
> 
> Blank line here breaks up logic blocks.
> 
> > +	out->flag = in->flags;
> > +	for (axis = 0; axis < 3; axis++)
> > +		out->vector[axis] = in->data[axis];  
> 
> blank line here helps me to read it.
> 
> > +	return true;
> > +}
> > +
> > +/*
> > + * cros_ec_sensorhub_ring_handler - the trigger handler function
> > + *
> > + * @sensorhub: device information.
> > + *
> > + * Called by the notifier, process the EC sensor FIFO queue.
> > + */
> > +static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
> > +{
> > +	struct cros_ec_fifo_info *fifo_info = &sensorhub->fifo_info;
> > +	struct cros_ec_dev *ec = sensorhub->ec;
> > +	s64    fifo_timestamp, current_timestamp;  
> 
> Tabs here are ugly and not consistent with surrounding lines.
> 
> > +	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;
> > +
> > +	mutex_lock(&sensorhub->cmd_lock);
> > +	/* Get FIFO information */  
> 
> This comment seems to be wrong.. It is just reading a timestamp.
> 
> > +	fifo_timestamp = sensorhub->fifo_timestamp[NEW_TS];
> > +	/* Copy elements in the main fifo */
> > +	if (fifo_info->info.total_lost) {
> > +		/* Need to retrieve the number of lost vectors per sensor */
> > +		sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
> > +		sensorhub->msg->outsize = 1;
> > +		sensorhub->msg->insize =
> > +			sizeof(struct ec_response_motion_sense_fifo_info) +
> > +			sizeof(u16) * CROS_EC_SENSOR_MAX;
> > +
> > +		if (cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg) < 0) {
> > +			mutex_unlock(&sensorhub->cmd_lock);
> > +			return;
> > +		}
> > +		memcpy(fifo_info, &sensorhub->resp->fifo_info,
> > +		       sizeof(*fifo_info));
> > +		fifo_timestamp = cros_ec_get_time_ns();
> > +	}
> > +	if (fifo_info->info.count > sensorhub->fifo_size ||
> > +	    fifo_info->info.size != sensorhub->fifo_size) {
> > +		dev_warn(sensorhub->dev,
> > +			 "Mismatch EC data: count %d, size %d - expected %d",
> > +			 fifo_info->info.count, fifo_info->info.size,
> > +			 sensorhub->fifo_size);
> > +		mutex_unlock(&sensorhub->cmd_lock);
> > +		return;
> > +	}
> > +
> > +	current_timestamp = sensorhub->fifo_timestamp[LAST_TS];
> > +	out = sensorhub->ring;
> > +	for (i = 0; i < fifo_info->info.count; i += number_data) {
> > +		sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_READ;
> > +		sensorhub->params->fifo_read.max_data_vector =
> > +			fifo_info->info.count - i;
> > +		sensorhub->msg->outsize =
> > +			sizeof(struct ec_params_motion_sense);
> > +		sensorhub->msg->insize =
> > +			sizeof(sensorhub->resp->fifo_read) +
> > +			sensorhub->params->fifo_read.max_data_vector *
> > +			  sizeof(struct ec_response_motion_sensor_data);
> > +		ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
> > +		if (ret < 0) {
> > +			dev_warn(sensorhub->dev, "Fifo error: %d\n", ret);
> > +			break;
> > +		}
> > +		number_data = sensorhub->resp->fifo_read.number_data;
> > +		if (number_data == 0) {
> > +			dev_dbg(sensorhub->dev, "Unexpected empty FIFO\n");
> > +			break;  
> 
> Given you break from here, might as well drop the else in the next line.
> 
> > +		} else if (number_data > fifo_info->info.count - i) {
> > +			dev_warn(sensorhub->dev,
> > +				 "Invalid EC data: too many entry received: %d, expected %d",
> > +				 number_data, fifo_info->info.count - i);
> > +			break;  
> 
> Same as above. The else is a bit pointless as flow won't go beyond here if the
> earlier statements have broken out.
> 
> > +		} else if (out + number_data >
> > +			   sensorhub->ring + fifo_info->info.count) {
> > +			dev_warn(sensorhub->dev,
> > +				 "Too many samples: %d (%zd data) to %d entries for expected %d entries",
> > +				 i, out - sensorhub->ring, i + number_data,
> > +				 fifo_info->info.count);
> > +			break;
> > +		}  
> 
> Blank line here to separate the error cases from where we go next.
> 
> > +		for (in = sensorhub->resp->fifo_read.data, j = 0;
> > +		     j < number_data; j++, in++) {
> > +			if (cros_ec_ring_process_event(
> > +					sensorhub, fifo_info, fifo_timestamp,
> > +					&current_timestamp, in, out)) {
> > +				sensor_mask |= (1 << in->sensor_num);
> > +				out++;
> > +			}
> > +		}
> > +	}
> > +	mutex_unlock(&sensorhub->cmd_lock);
> > +	last_out = out;
> > +
> > +	if (out == sensorhub->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;
> > +
> > +	/* Warn on lost samples. */
> > +	for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
> > +		if (fifo_info->info.total_lost) {
> > +			int lost = fifo_info->lost[i];
> > +
> > +			if (lost) {
> > +				dev_warn(sensorhub->dev,
> > +					"Sensor %d: lost: %d out of %d\n", i,
> > +					lost, fifo_info->info.total_lost);  
> 
> Is there potential to fill the log with this? I'm not sure if dev_warn
> is rate limited.
> 
> > +			}
> > +		}
> > +	}
> > +
> > +	/* push the event into the kfifo */
> > +	for (out = sensorhub->ring; out < last_out; out++)
> > +		cros_sensorhub_send_sample(sensorhub, out);
> > +
> > +ring_handler_end:
> > +	sensorhub->fifo_timestamp[LAST_TS] = current_timestamp;
> > +}
> > +
> > +static int cros_ec_sensorhub_event(struct notifier_block *nb,
> > +	unsigned long queued_during_suspend, void *_notify)
> > +{
> > +	struct cros_ec_sensorhub *sensorhub;
> > +	struct cros_ec_device *ec_dev;
> > +
> > +	sensorhub = container_of(nb, struct cros_ec_sensorhub, notifier);
> > +	ec_dev = sensorhub->ec->ec_dev;
> > +
> > +	if (ec_dev->event_data.event_type != EC_MKBP_EVENT_SENSOR_FIFO)
> > +		return NOTIFY_DONE;
> > +
> > +	if (ec_dev->event_size != sizeof(ec_dev->event_data.data.sensor_fifo)) {
> > +		dev_warn(ec_dev->dev, "Invalid fifo info size\n");
> > +		return NOTIFY_DONE;
> > +	}
> > +
> > +	if (queued_during_suspend)
> > +		return NOTIFY_OK;
> > +
> > +	sensorhub->fifo_info.info = ec_dev->event_data.data.sensor_fifo.info;
> > +	sensorhub->fifo_timestamp[NEW_TS] = ec_dev->last_event_time;
> > +	cros_ec_sensorhub_ring_handler(sensorhub);
> > +	return NOTIFY_OK;
> > +}
> > +
> > +int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
> > +{
> > +	struct cros_ec_dev *ec = sensorhub->ec;
> > +	int ret;
> > +
> > +	/* Retrieve FIFO information */
> > +	sensorhub->msg->version = 2;
> > +	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
> > +	sensorhub->msg->outsize = 1;
> > +	sensorhub->msg->insize =
> > +		sizeof(struct ec_response_motion_sense_fifo_info) +
> > +		sizeof(u16) * CROS_EC_SENSOR_MAX;
> > +
> > +	ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/*
> > +	 * Allocate the full fifo.
> > +	 * We need to copy the whole FIFO to set timestamps properly *
> > +	 */
> > +	sensorhub->fifo_size = sensorhub->resp->fifo_info.size;
> > +	sensorhub->ring = devm_kcalloc(sensorhub->dev, sensorhub->fifo_size,
> > +			sizeof(*sensorhub->ring), GFP_KERNEL);
> > +	if (!sensorhub->ring)
> > +		return -ENOMEM;
> > +
> > +	sensorhub->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
> > +
> > +	/* register the notifier that will act as a top half interrupt. */
> > +	sensorhub->notifier.notifier_call = cros_ec_sensorhub_event;
> > +	ret = blocking_notifier_chain_register(&ec->ec_dev->event_notifier,
> > +					       &sensorhub->notifier);
> > +	if (ret < 0) {
> > +		dev_warn(sensorhub->dev, "failed to register notifier\n");
> > +		return ret;
> > +	}
> > +
> > +	/*
> > +	 * Start collection samples.  
> 
> Single line comment. Also with appropriate rename of that function will
> be fairly obvious.
> 
> > +	 */
> > +	ret = cros_ec_sensorhub_ring_fifo_toggle(sensorhub, true);
> > +	return ret;  
> 	return cros_ec_sensorhub...
> 
> > +}
> > +
> > +int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensorhub)
> > +{
> > +	struct cros_ec_device *ec_dev = sensorhub->ec->ec_dev;
> > +
> > +	/*
> > +	 * Disable the ring, prevent EC interrupt to the AP for nothing.  
> 
> If will fit on a single line, stick to a single line.
> 
> > +	 */
> > +	cros_ec_sensorhub_ring_fifo_toggle(sensorhub, false);  
> 
> Odd name for an enable disable function.  I'd expect 'toggle' to take no
> parameter but to flip from which ever state we are in to the other one.
> That's not what is happening here.
> 
> > +	blocking_notifier_chain_unregister(&ec_dev->event_notifier,
> > +					   &sensorhub->notifier);
> > +	return 0;
> > +}
> > +
> > diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> > index bb331e6356a9..abef66d0e884 100644
> > --- a/include/linux/iio/common/cros_ec_sensors_core.h
> > +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> > @@ -116,6 +116,9 @@ struct platform_device;
> >  int cros_ec_sensors_core_init(struct platform_device *pdev,
> >  			      struct iio_dev *indio_dev, bool physical_device);
> >  
> > +/* To remove association of physical device to cros_ec_sensorhub. */
> > +int cros_ec_sensors_core_clean(struct platform_device *pdev);
> > +
> >  /**
> >   * cros_ec_sensors_capture() - the trigger handler function
> >   * @irq:	the interrupt number.
> > diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> > index 9295eabb16f6..18cda568c58a 100644
> > --- a/include/linux/platform_data/cros_ec_sensorhub.h
> > +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> > @@ -9,6 +9,7 @@
> >  #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> >  #define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> >  
> > +#include <linux/iio/iio.h>  
> 
> Comment below. I wouldn't expect to see this include here.
> 
> >  #include <linux/platform_data/cros_ec_commands.h>
> >  
> >  /**
> > @@ -19,11 +20,89 @@ struct cros_ec_sensor_platform {
> >  	u8 sensor_num;
> >  };
> >  
> > +/**
> > + * Callback function to send datum to specific sensors.  
> 
> It's not kernel-doc.
> 
> > + */
> > +typedef int (*cros_ec_sensorhub_push_data_cb_t)(
> > +		struct iio_dev *indio_dev,  
> 
> I would add a forwards def of struct iio_dev and not
> include the iio header.
> 
> > +		s16 *data,
> > +		s64 timestamp);
> > +
> > +struct cros_ec_sensorhub_sensor_push_data {
> > +	struct iio_dev *indio_dev;
> > +	cros_ec_sensorhub_push_data_cb_t push_data_cb;
> > +};
> > +
> > +enum {
> > +	LAST_TS,
> > +	NEW_TS,
> > +	ALL_TS
> > +};
> > +
> > +#define CROS_EC_SENSOR_MAX 16
> > +
> > +struct __ec_todo_packed 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[3];
> > +	s64      timestamp;
> > +} __packed;
> > +
> >  /**
> >   * struct cros_ec_sensorhub - Sensor Hub device data.
> >   */
> >  struct cros_ec_sensorhub {
> > +	struct device *dev;
> >  	struct cros_ec_dev *ec;
> > +
> > +	/* Structure to send FIFO requests. */
> > +	struct cros_ec_command *msg;
> > +	struct ec_params_motion_sense *params;
> > +	struct ec_response_motion_sense *resp;
> > +	struct mutex cmd_lock;
> > +
> > +	/* Notifier to kick the FIFO interrupt */
> > +	struct notifier_block notifier;
> > +
> > +	/* Preprocessed ring to send to kfifos */
> > +	struct cros_ec_sensors_ring_sample *ring;
> > +
> > +	s64    fifo_timestamp[ALL_TS];
> > +	struct cros_ec_fifo_info fifo_info;
> > +	int    fifo_size;
> > +
> > +	/*
> > +	 * Dynamic array to be able to spread datum to iio sensor objects.
> > +	 */
> > +	struct cros_ec_sensorhub_sensor_push_data push_data[CROS_EC_SENSOR_MAX];
> >  };
> >  
> > +/**
> > + * Function to register the callback to the sensor hub.
> > + * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
> > + * from the EC.  
> 
> Looks like kernel-doc but isn't. + should be with the code, not in the header
> where it will tend to rot.
> 
> > + */
> > +int cros_ec_sensorhub_register_push_data(
> > +		struct cros_ec_sensorhub *sensor_hub,
> > +		u8 sensor_num,
> > +		struct iio_dev *indio_dev,
> > +		cros_ec_sensorhub_push_data_cb_t cb);
> > +void cros_ec_sensorhub_unregister_push_data(
> > +		struct cros_ec_sensorhub *sensor_hub,
> > +		u8 sensor_num);
> > +
> > +/**  
> 
> /* otherwise it's kernel-doc...
> 
> > + * Add/Remove the fifo functionality if the EC supports it.
> > + */
> > +int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensor_hub);
> > +int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensor_hub);
> > +int cros_ec_sensorhub_ring_fifo_toggle(
> > +		struct cros_ec_sensorhub *sensorhub,
> > +		bool on);
> > +
> >  #endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */  
> 


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

* Re: [PATCH 08/13] platform: chrome: sensorhub: Add code to spread timestmap
  2019-09-22 17:50 ` [PATCH 08/13] platform: chrome: sensorhub: Add code to spread timestmap Gwendal Grignou
@ 2019-10-05 16:16   ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:16 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:16 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> EC FIFO can send sensor events in batch. Spread them based on
> previous (TSa) and currnet timestamp (TSb)
> 
>   EC FIFO                             iio events
> +-----------+
> | TSa       |
> +-----------+             +---------------------------------------+
> | event 1   |             | event 1 | TSb - (TSb - TSa)/n * (n-1) |
> +-----------+             +---------------------------------------+
> | event 2   |             | event 2 | TSb - (TSb - TSa)/n * (n-2) |
> +-----------+             +---------------------------------------+
> |  ...      |  ------>    |  ....   |                             |
> +-----------+             +---------------------------------------+
> | event n-1 |             | event 2 | TSb - (TSb - TSa)/n         |
> +-----------+             +---------------------------------------+
> | event n   |             | event 2 | TSb                         |
> +-----------+             +---------------------------------------+
> | TSb       |
> +-----------+
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Looks fine to me and the docs are nice :)
Timestamps for batches are always a pita.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
>  .../platform/chrome/cros_ec_sensorhub_ring.c  | 99 ++++++++++++++++++-
>  1 file changed, 96 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> index 8cd533d5542e..48327e80a5a3 100644
> --- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> @@ -160,6 +160,97 @@ static bool cros_ec_ring_process_event(
>  	return true;
>  }
>  
> +/*
> + * cros_ec_ring_spread_add: Calculate proper timestamps then
> + * add to ringbuffer (legacy).
> + *
> + * 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]
> + */
> +static void cros_ec_ring_spread_add(
> +				struct cros_ec_sensorhub *sensorhub,
> +				unsigned long sensor_mask,
> +				s64 current_timestamp,
> +				struct cros_ec_sensors_ring_sample *last_out)
> +{
> +	struct cros_ec_sensors_ring_sample *out;
> +	int i;
> +
> +	for_each_set_bit(i, &sensor_mask, BITS_PER_LONG) {
> +		s64 older_timestamp;
> +		s64 timestamp;
> +		struct cros_ec_sensors_ring_sample *older_unprocess_out =
> +			sensorhub->ring;
> +		struct cros_ec_sensors_ring_sample *next_out;
> +		int count = 1;
> +
> +		for (out = sensorhub->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 = sensorhub->ring; out < last_out; out++)
> +		cros_sensorhub_send_sample(sensorhub, out);
> +}
> +
>  /*
>   * cros_ec_sensorhub_ring_handler - the trigger handler function
>   *
> @@ -280,9 +371,11 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
>  		}
>  	}
>  
> -	/* push the event into the kfifo */
> -	for (out = sensorhub->ring; out < last_out; out++)
> -		cros_sensorhub_send_sample(sensorhub, out);
> +	/*
> +	 * Spread samples in case of batching, then add them to the ringbuffer.
> +	 */
> +	cros_ec_ring_spread_add(sensorhub, sensor_mask,
> +			current_timestamp, last_out);
>  
>  ring_handler_end:
>  	sensorhub->fifo_timestamp[LAST_TS] = current_timestamp;


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

* Re: [PATCH 09/13] platform: chrome: sensorhub: Add median filter
  2019-09-22 17:50 ` [PATCH 09/13] platform: chrome: sensorhub: Add median filter Gwendal Grignou
@ 2019-10-05 16:24   ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:24 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:17 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> Events are timestamped in EC time space, their timestamps need to be
> converted in host time space.
> The assumption is the time delta between when the interrupt is sent
> by the EC and when it is receive by the host is a [small] constant.
> This is not always true, even with hard-wired interrupt. To mitigate
> worst offenders, add a median filter to weed out bigger than expected
> delays.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
This stuff is always hard to get right. I'll assume you've tested
this extensively :)

I'll be honest, I'm going to assume you have this right as it would be a
while before I'd find time to go through this with a fine toothed comb
and I'd probably need right unit tests etc.  Hence my comments
are superficial + there is a worrying looking todo in there I don't
understand ;)

Thanks,

Jonathan


> ---
>  .../platform/chrome/cros_ec_sensorhub_ring.c  | 485 +++++++++++++++++-
>  .../linux/platform_data/cros_ec_sensorhub.h   |  65 +++
>  2 files changed, 533 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> index 48327e80a5a3..9955c80d0907 100644
> --- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> @@ -69,9 +69,11 @@ int cros_ec_sensorhub_ring_fifo_toggle(
>  		struct cros_ec_sensorhub *sensorhub,
>  		bool on)
>  {
> -	int ret;
> +	int ret, i;
>  
>  	mutex_lock(&sensorhub->cmd_lock);
> +	for (i = 0; i < CROS_EC_SENSOR_MAX; i++)
> +		sensorhub->last_batch_len[i] = 0;
>  	sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE;
>  	sensorhub->params->fifo_int_enable.enable = on;
>  
> @@ -86,6 +88,227 @@ int cros_ec_sensorhub_ring_fifo_toggle(
>  	return ret;
>  }
>  
> +static int cros_ec_ring_median_cmp(const void *pv1, const void *pv2)
> +{
> +	s64 v1 = *(s64 *)pv1;
> +	s64 v2 = *(s64 *)pv2;
> +
> +	if (v1 > v2)
> +		return 1;
> +	else if (v1 < v2)
> +		return -1;
> +	else
> +		return 0;
> +}
> +
> +/*
> + * cros_ec_ring_median: Gets median of an array of numbers
> + *
> + * For now it's implemented using an inefficient > O(n) sort then return
> + * the middle element. A more optimal method would be something like
> + * quickselect, but given that n = 64 we can probably live with it in the
> + * name of clarity.
> + *
> + * Warning: the input array gets modified (sorted)!
> + */
> +static s64 cros_ec_ring_median(s64 *array, size_t length)
> +{
> +	sort(array, length, sizeof(s64), cros_ec_ring_median_cmp, NULL);
> +	return array[length / 2];
> +}
> +
> +/*
> + * IRQ Timestamp Filtering
> + *
> + * Lower down in cros_ec_ring_process_event(), for each sensor event we have to
> + * calculate it's timestamp in the AP timebase. There are 3 time points:
> + *   a - EC timebase, sensor event
> + *   b - EC timebase, IRQ
> + *   c - AP timebase, IRQ
> + *   a' - what we want: sensor even in AP timebase
> + *
> + * While a and b are recorded at accurate times (due to the EC real time
> + * nature); c is pretty untrustworthy, even though it's recorded the
> + * first thing in ec_irq_handler(). There is a very good change we'll get
> + * added lantency due to:
> + *   other irqs
> + *   ddrfreq
> + *   cpuidle
> + *
> + * Normally a' = c - b + a, but if we do that naive math any jitter in c
> + * will get coupled in a', which we don't want. We want a function
> + * a' = cros_ec_ring_ts_filter(a) which will filter out outliers in c.
> + *
> + * Think of a graph of AP time(b) on the y axis vs EC time(c) on the x axis.
> + * The slope of the line won't be exactly 1, there will be some clock drift
> + * between the 2 chips for various reasons (mechanical stress, temperature,
> + * voltage). We need to extrapolate values for a future x, without trusting
> + * recent y values too much.
> + *
> + * We use a median filter for the slope, then another median filter for the
> + * y-intercept to calculate this function:
> + *   dx[n] = x[n-1] - x[n]
> + *   dy[n] = x[n-1] - x[n]
> + *   m[n] = dy[n] / dx[n]
> + *   median_m = median(m[n-k:n])
> + *   error[i] = y[n-i] - median_m * x[n-i]
> + *   median_error = median(error[:k])
> + *   predicted_y = median_m * x + median_error
> + *
> + * Implementation differences from above:
> + * - Redefined y to be actually c - b, this gives us a lot more precision
> + * to do the math. (c-b)/b variations are more obvious than c/b variations.
> + * - Since we don't have floating point, any operations involving slope are
> + * done using fixed point math (*M_PRECISION)
> + * - Since x and y grow with time, we keep zeroing the graph (relative to
> + * the last sample), this way math involving *x[n-i] will not overflow
> + * - EC timestamps are kept in us, it improves the slope calculation precision
> + */
> +
> +/*
> + * cros_ec_ring_ts_filter_update: Given a new IRQ timestamp pair (EC and
> + * AP timebases), add it to the filter history.
> + *
> + * @b IRQ timestamp, EC timebase (us)
> + * @c IRQ timestamp, AP timebase (ns)
> + */
> +static void cros_ec_ring_ts_filter_update(
> +			struct cros_ec_sensors_ts_filter_state *state,
> +			s64 b, s64 c)
> +{
> +	s64 x, y;
> +	s64 dx, dy;
> +	s64 m; /* stored as *M_PRECISION */
> +	s64 *m_history_copy = state->temp_buf;
> +	s64 *error = state->temp_buf;
> +	int i;
> +
> +	/* we trust b the most, that'll be our independent variable */
> +	x = b;
> +	/* y is the offset between AP and EC times, in ns */
> +	y = c - b * 1000;
> +
> +	dx = (state->x_history[0] + state->x_offset) - x;
> +	if (dx == 0)
> +		return; /* we already have this irq in the history */
> +	dy = (state->y_history[0] + state->y_offset) - y;
> +	m = div64_s64(dy * M_PRECISION, dx);
> +
> +	/* Empty filter if we haven't seen any action in a while. */
> +	if (-dx > TS_HISTORY_BORED_US)
> +		state->history_len = 0;
> +
> +	/* Move everything over, also update offset to all absolute coords .*/
> +	for (i = state->history_len - 1; i >= 1; i--) {
> +		state->x_history[i] = state->x_history[i-1] + dx;
> +		state->y_history[i] = state->y_history[i-1] + dy;
> +
> +		state->m_history[i] = state->m_history[i-1];
> +		/*
> +		 * Also use the same loop to copy m_history for future
> +		 * median extraction.
> +		 */
> +		m_history_copy[i] = state->m_history[i-1];
> +	}
> +
> +	/* Store the x and y, but remember offset is actually last sample. */
> +	state->x_offset = x;
> +	state->y_offset = y;
> +	state->x_history[0] = 0;
> +	state->y_history[0] = 0;
> +
> +	state->m_history[0] = m;
> +	m_history_copy[0] = m;
> +
> +	if (state->history_len < TS_HISTORY_SIZE)
> +		state->history_len++;
> +
> +	/* Precalculate things for the filter. */
> +	if (state->history_len > TS_HISTORY_THRESHOLD) {
> +		state->median_m =
> +		    cros_ec_ring_median(m_history_copy, state->history_len - 1);
> +
> +		/*
> +		 * Calculate y-intercepts as if m_median is the slope and
> +		 * points in the history are on the line. median_error will
> +		 * still be in the offset coordinate system.
> +		 */
> +		for (i = 0; i < state->history_len; i++)
> +			error[i] = state->y_history[i] -
> +				div_s64(state->median_m * state->x_history[i],
> +					M_PRECISION);
> +		state->median_error =
> +			cros_ec_ring_median(error, state->history_len);
> +	} else {
> +		state->median_m = 0;
> +		state->median_error = 0;
> +	}
> +}
> +
> +/*
> + * cros_ec_ring_ts_filter: Translate EC timebase timestamp to AP timebase
> + *
> + * @x any ec timestamp (us):
> + *
> + * cros_ec_ring_ts_filter(a) => a' event timestamp, AP timebase
> + * cros_ec_ring_ts_filter(b) => calculated timestamp when the EC IRQ
> + *                           should have happened on the AP, with low jitter
> + *
> + * @returns timestamp in AP timebase (ns)
> + *
> + * Note: The filter will only activate once state->history_len goes
> + * over TS_HISTORY_THRESHOLD. Otherwise it'll just do the naive c - b + a
> + * transform.
> + *
> + * How to derive the formula, starting from:
> + *   f(x) = median_m * x + median_error
> + * That's the calculated AP - EC offset (at the x point in time)
> + * Undo the coordinate system transform:
> + *   f(x) = median_m * (x - x_offset) + median_error + y_offset
> + * Remember to undo the "y = c - b * 1000" modification:
> + *   f(x) = median_m * (x - x_offset) + median_error + y_offset + x * 1000
> + */
> +static s64 cros_ec_ring_ts_filter(struct cros_ec_sensors_ts_filter_state *state,
> +				  s64 x)
> +{
> +	return div_s64(state->median_m * (x - state->x_offset), M_PRECISION)
> +	       + state->median_error + state->y_offset + x * 1000;
> +}
> +
> +/*
> + * Since a and b were originally 32 bit values from the EC,
> + * they overflow relatively often, casting is not enough, so we need to
> + * add an offset.
> + */
> +static void cros_ec_ring_fix_overflow(s64 *ts,
> +		const s64 overflow_period,
> +		struct cros_ec_sensors_ec_overflow_state *state)
> +{
> +	s64 adjust;
> +
> +	*ts += state->offset;
> +	if (abs(state->last - *ts) > (overflow_period / 2)) {
> +		adjust = state->last > *ts ? overflow_period : -overflow_period;
> +		state->offset += adjust;
> +		*ts += adjust;
> +	}
> +	state->last = *ts;
> +}
> +
> +static void cros_ec_ring_check_for_past_timestamp(
> +				struct cros_ec_sensorhub *sensorhub,
> +				struct cros_ec_sensors_ring_sample *sample)
> +{
> +	const u8 sensor_id = sample->sensor_id;
> +
> +	// if this event is earlier than one we saw before...
/* please :)
> +	if (sensorhub->newest_sensor_event[sensor_id] > sample->timestamp)
> +		// mark it for spreading
> +		sample->timestamp = sensorhub->last_batch_timestamp[sensor_id];
> +	else
> +		sensorhub->newest_sensor_event[sensor_id] = sample->timestamp;
> +}
> +
>  /*
>   * cros_ec_ring_process_event: process one EC FIFO event
>   *
> @@ -117,25 +340,47 @@ static bool cros_ec_ring_process_event(
>  		s64 a = in->timestamp;
>  		s64 b = fifo_info->info.timestamp;
>  		s64 c = fifo_timestamp;
> -		s64 new_timestamp;
>  
> +		cros_ec_ring_fix_overflow(&a, 1LL << 32,
> +				&sensorhub->overflow_a);
> +		cros_ec_ring_fix_overflow(&b, 1LL << 32,
> +				&sensorhub->overflow_b);
> +
> +		if (sensorhub->tight_timestamps) {
> +			cros_ec_ring_ts_filter_update(&sensorhub->filter, b, c);
> +			*current_timestamp =
> +				cros_ec_ring_ts_filter(&sensorhub->filter, a);
> +		} else {
> +			s64 new_timestamp;
> +			/*
> +			 * disable filtering since we might add more jitter
> +			 * if b is in a random point in time
> +			 */
> +			new_timestamp = c - b * 1000 + a * 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_ODR) {
> +		sensorhub->last_batch_len[in->sensor_num] =
> +			sensorhub->penultimate_batch_len[in->sensor_num] = 0;
>  		/*
> -		 * disable filtering since we might add more jitter
> -		 * if b is in a random point in time
> -		 */
> -		new_timestamp = c - b * 1000 + a * 1000;
> -		/*
> -		 * The timestamp can be stale if we had to use the fifo
> -		 * info timestamp.
> +		 * ODR change is only useful for the sensor_ring, it does not
> +		 * convey information to clients.
>  		 */
> -		if (new_timestamp - *current_timestamp > 0)
> -			*current_timestamp = new_timestamp;
> +		return false;
>  	}
>  
>  	if (in->flags & MOTIONSENSE_SENSOR_FLAG_FLUSH) {
>  		out->sensor_id = in->sensor_num;
>  		out->timestamp = *current_timestamp;
>  		out->flag = in->flags;
> +		sensorhub->last_batch_len[out->sensor_id] = 0;
>  		/*
>  		 * No other payload information provided with
>  		 * flush ack.
> @@ -149,7 +394,22 @@ static bool cros_ec_ring_process_event(
>  	/* Regular sample */
>  	out->sensor_id = in->sensor_num;
>  	if (*current_timestamp - now > 0) {
> -		/* If the timestamp is in the future. */
> +		/*
> +		 * This fix is needed to overcome the timestamp filter putting
> +		 * events in the future.
> +		 */
> +		sensorhub->future_timestamp_total_ns +=
> +			*current_timestamp - now;
> +		if (++sensorhub->future_timestamp_count ==
> +				FUTURE_TS_ANALYTICS_COUNT_MAX) {
> +			s64 avg = div_s64(sensorhub->future_timestamp_total_ns,
> +					sensorhub->future_timestamp_count);
> +			dev_warn(sensorhub->dev,
> +					"100 timestamps in the future, %lldns shaved on average\n",
> +					avg);
> +			sensorhub->future_timestamp_count = 0;
> +			sensorhub->future_timestamp_total_ns = 0;
> +		}
>  		out->timestamp = now;
>  	} else {
>  		out->timestamp = *current_timestamp;
> @@ -157,13 +417,195 @@ static bool cros_ec_ring_process_event(
>  	out->flag = in->flags;
>  	for (axis = 0; axis < 3; axis++)
>  		out->vector[axis] = in->data[axis];
> +	if (sensorhub->tight_timestamps)
> +		cros_ec_ring_check_for_past_timestamp(sensorhub, out);
>  	return true;
>  }
>  
>  /*
> - * cros_ec_ring_spread_add: Calculate proper timestamps then
> + * cros_ec_ring_spread_add: Calculate proper timestamps then add to ringbuffer.
> + *
> + * Note: This is the new spreading code, assumes every sample's timestamp
> + * preceeds the sample. Run if tight_timestamps == true.
> + *
> + * Sometimes the EC receives only one interrupt (hence timestamp) for
> + * a batch of samples. Only the first sample will have the correct
> + * timestamp. So we must interpolate the other samples.
> + * We use the previous batch timestamp and our current batch timestamp
> + * as a way to calculate period, then spread the samples evenly.
> + *
> + * s0 int, 0ms
> + * s1 int, 10ms
> + * s2 int, 20ms
> + * 30ms point goes by, no interrupt, previous one is still asserted
> + * downloading s2 and s3
> + * s3 sample, 20ms (incorrect timestamp)
> + * s4 int, 40ms
> + *
> + * The batches are [(s0), (s1), (s2, s3), (s4)]. Since the 3rd batch
> + * has 2 samples in them, we adjust the timestamp of s3.
> + * s2 - s1 = 10ms, so s3 must be s2 + 10ms => 20ms. If s1 would have
> + * been part of a bigger batch things would have gotten a little
> + * more complicated.
> + *
> + * Note: we also assume another sensor sample doesn't break up a batch
> + * in 2 or more partitions. Example, there can't ever be a sync sensor
> + * in between S2 and S3. This simplifies the following code.
> + */
> +static void cros_ec_ring_spread_add(
> +				struct cros_ec_sensorhub *sensorhub,
> +				unsigned long sensor_mask,
> +				struct cros_ec_sensors_ring_sample *last_out)
> +{
> +	struct cros_ec_sensors_ring_sample *batch_start, *next_batch_start;
> +	int id;
> +
> +	for_each_set_bit(id, &sensor_mask, BITS_PER_LONG) {
> +		for (batch_start = sensorhub->ring; batch_start < last_out;
> +		     batch_start = next_batch_start) {
> +			/*
> +			 * For each batch (where all samples have the same
> +			 * timestamp).
> +			 */
> +			int batch_len, sample_idx;
> +			struct cros_ec_sensors_ring_sample *batch_end =
> +				batch_start;
> +			struct cros_ec_sensors_ring_sample *s;
> +			s64 batch_timestamp = batch_start->timestamp;
> +			s64 sample_period;
> +
> +			/*
> +			 * Skip over batches that start with the sensor types
> +			 * we're not looking at right now.
> +			 */
> +			if (batch_start->sensor_id != id) {
> +				next_batch_start = batch_start + 1;
> +				continue;
> +			}
> +
> +			/*
> +			 * TODO(gwendal): can not send out flush packets
> +			 * anymore.
> +			 * Do not start a batch
> +			 * from a flush, as it happens asynchronously to the
> +			 * regular flow of events.

Slightly worrying TODO... Fixed later?

> +			 */
> +			if (batch_start->flag &
> +				MOTIONSENSE_SENSOR_FLAG_FLUSH) {
> +				next_batch_start = batch_start + 1;
> +				continue;
> +			}
> +
> +			if (batch_start->timestamp <=
> +				sensorhub->last_batch_timestamp[id]) {
> +
> +				batch_timestamp =
> +					sensorhub->last_batch_timestamp[id];
> +				batch_len = sensorhub->last_batch_len[id];
> +
> +				sample_idx = batch_len;
> +
> +				sensorhub->last_batch_timestamp[id] =
> +				  sensorhub->penultimate_batch_timestamp[id];
> +				sensorhub->last_batch_len[id] =
> +				  sensorhub->penultimate_batch_len[id];
> +			} else {
> +				/*
> +				 * Push first sample in the batch to the,
> +				 * kifo, it's guaranteed to be correct, the
> +				 * rest will follow later on.
> +				 */
> +				sample_idx = batch_len = 1;
> +				cros_sensorhub_send_sample(
> +						sensorhub, batch_start);
> +				batch_start++;
> +			}
> +
> +			/* Find all samples have the same timestamp. */
> +			for (s = batch_start; s < last_out; s++) {
> +				if (s->sensor_id != id)
> +					/*
> +					 * Skip over other sensor types that
> +					 * are interleaved, don't count them.
> +					 */
> +					continue;
> +				if (s->timestamp != batch_timestamp)
> +					/* we discovered the next batch */
> +					break;
> +				if (s->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
> +					/* break on flush packets */
> +					break;
> +				batch_end = s;
> +				batch_len++;
> +			}
> +
> +			if (batch_len == 1)
> +				goto done_with_this_batch;
> +
> +			/* Can we calculate period? */
> +			if (sensorhub->last_batch_len[id] == 0) {
> +				dev_warn(sensorhub->dev, "Sensor %d: lost %d samples when spreading\n",
> +						id, batch_len - 1);
> +				goto done_with_this_batch;
> +				/*
> +				 * Note: we're dropping the rest of the samples
> +				 * in this batch since we have no idea where
> +				 * they're supposed to go without a period
> +				 * calculation.
> +				 */
> +			}
> +
> +			sample_period = div_s64(batch_timestamp -
> +					sensorhub->last_batch_timestamp[id],
> +					sensorhub->last_batch_len[id]);
> +			dev_dbg(sensorhub->dev,
> +					"Adjusting %d samples, sensor %d last_batch @%lld (%d samples) batch_timestamp=%lld => period=%lld\n",
> +					batch_len, id,
> +					sensorhub->last_batch_timestamp[id],
> +					sensorhub->last_batch_len[id],
> +					batch_timestamp,
> +					sample_period);
> +
> +			/*
> +			 * Adjust timestamps of the samples then push them to
> +			 * kfifo.
> +			 */
> +			for (s = batch_start; s <= batch_end; s++) {
> +				if (s->sensor_id != id)
> +					/*
> +					 * Skip over other sensor types that
> +					 * are interleaved, don't change them.
> +					 */
> +					continue;
> +
> +				s->timestamp = batch_timestamp +
> +					sample_period * sample_idx;
> +				sample_idx++;
> +
> +				cros_sensorhub_send_sample(sensorhub, s);
> +			}
> +
> +done_with_this_batch:
> +			sensorhub->penultimate_batch_timestamp[id] =
> +				sensorhub->last_batch_timestamp[id];
> +			sensorhub->penultimate_batch_len[id] =
> +				sensorhub->last_batch_len[id];
> +
> +			sensorhub->last_batch_timestamp[id] = batch_timestamp;
> +			sensorhub->last_batch_len[id] = batch_len;
> +
> +			next_batch_start = batch_end + 1;
> +		}
> +	}
> +}
> +
> +/*
> + * cros_ec_ring_spread_add_legacy: Calculate proper timestamps then
>   * add to ringbuffer (legacy).
>   *
> + * Note: This assumes we're running old firmware, where every sample's timestamp
> + * is after the sample. Run if tight_timestamps == false.
> + *
>   * If there is a sample with a proper timestamp
>   *                        timestamp | count
>   * older_unprocess_out --> TS1      | 1
> @@ -181,7 +623,7 @@ static bool cros_ec_ring_process_event(
>   * out -->                 TS1      | 3
>   * We know have [TS1+1/3, TS1+2/3, current timestamp]
>   */
> -static void cros_ec_ring_spread_add(
> +static void cros_ec_ring_spread_add_legacy(
>  				struct cros_ec_sensorhub *sensorhub,
>  				unsigned long sensor_mask,
>  				s64 current_timestamp,
> @@ -355,7 +797,8 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
>  	 * 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)
> +	if (!sensorhub->tight_timestamps &&
> +	    (last_out-1)->timestamp == current_timestamp)
>  		current_timestamp = fifo_timestamp;
>  
>  	/* Warn on lost samples. */
> @@ -367,6 +810,7 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
>  				dev_warn(sensorhub->dev,
>  					"Sensor %d: lost: %d out of %d\n", i,
>  					lost, fifo_info->info.total_lost);
> +				sensorhub->last_batch_len[i] = 0;
>  			}
>  		}
>  	}
> @@ -374,8 +818,11 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
>  	/*
>  	 * Spread samples in case of batching, then add them to the ringbuffer.
>  	 */
> -	cros_ec_ring_spread_add(sensorhub, sensor_mask,
> -			current_timestamp, last_out);
> +	if (sensorhub->tight_timestamps)
> +		cros_ec_ring_spread_add(sensorhub, sensor_mask, last_out);
> +	else
> +		cros_ec_ring_spread_add_legacy(sensorhub, sensor_mask,
> +					       current_timestamp, last_out);
>  
>  ring_handler_end:
>  	sensorhub->fifo_timestamp[LAST_TS] = current_timestamp;
> @@ -436,6 +883,10 @@ int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
>  
>  	sensorhub->fifo_timestamp[LAST_TS] = cros_ec_get_time_ns();
>  
> +	sensorhub->tight_timestamps = cros_ec_check_features(ec,
> +		EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS);
> +
> +
>  	/* register the notifier that will act as a top half interrupt. */
>  	sensorhub->notifier.notifier_call = cros_ec_sensorhub_event;
>  	ret = blocking_notifier_chain_register(&ec->ec_dev->event_notifier,
> diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> index 18cda568c58a..df67f2015da9 100644
> --- a/include/linux/platform_data/cros_ec_sensorhub.h
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -53,6 +53,42 @@ struct cros_ec_sensors_ring_sample {
>  	s64      timestamp;
>  } __packed;
>  
> +/* State used for cros_ec_ring_fix_overflow */
> +struct cros_ec_sensors_ec_overflow_state {
> +	s64 offset;
> +	s64 last;
> +};
> +
> +/* Precision of fixed point for the m values from the filter */
> +#define M_PRECISION (1 << 23)

These are a bit generic names.  Prefix them so we don't get a clash
sometime in the future.

> +
> +/* Length of the filter, how long to remember entries for */
> +#define TS_HISTORY_SIZE 64
> +
> +/* Only activate the filter once we have at least this many elements. */
> +#define TS_HISTORY_THRESHOLD 8
> +
> +/*
> + * If we don't have any history entries for this long, empty the filter to
> + * make sure there are no big discontinuities.
> + */
> +#define TS_HISTORY_BORED_US 500000
> +
> +struct cros_ec_sensors_ts_filter_state {
> +	s64 x_offset, y_offset;
> +	s64 x_history[TS_HISTORY_SIZE]; /* stored relative to x_offset */
> +	s64 y_history[TS_HISTORY_SIZE]; /* stored relative to y_offset */
> +	s64 m_history[TS_HISTORY_SIZE]; /* stored as *M_PRECISION */
> +	int history_len;
> +
> +	s64 temp_buf[TS_HISTORY_SIZE];
> +
> +	s64 median_m;
> +	s64 median_error;
> +};
> +
> +#define FUTURE_TS_ANALYTICS_COUNT_MAX 100
> +
>  /**
>   * struct cros_ec_sensorhub - Sensor Hub device data.
>   */
> @@ -76,6 +112,35 @@ struct cros_ec_sensorhub {
>  	struct cros_ec_fifo_info fifo_info;
>  	int    fifo_size;
>  
> +	/* Used for timestamp spreading calculations when a batch shows up */
> +	s64 penultimate_batch_timestamp[CROS_EC_SENSOR_MAX];
> +	int penultimate_batch_len[CROS_EC_SENSOR_MAX];
> +	s64 last_batch_timestamp[CROS_EC_SENSOR_MAX];
> +	int last_batch_len[CROS_EC_SENSOR_MAX];
> +	s64 newest_sensor_event[CROS_EC_SENSOR_MAX];
> +
> +	struct cros_ec_sensors_ec_overflow_state overflow_a;
> +	struct cros_ec_sensors_ec_overflow_state overflow_b;
> +
> +	struct cros_ec_sensors_ts_filter_state filter;
> +
> +	/*
> +	 * The timestamps reported from the EC have low jitter.
> +	 * Timestamps also come before every sample.
> +	 * Set either by feature bits coming from the EC or userspace.
> +	 */
> +	bool tight_timestamps;
> +
> +	/*
> +	 * Statistics used to compute shaved time. This occures when
> +	 * timestamp interpolation from EC time to AP time accidentally
> +	 * puts timestamps in the future. These timestamps are clamped
> +	 * to `now` and these count/total_ns maintain the statistics for
> +	 * how much time was removed in a given period..
> +	 */
> +	s32 future_timestamp_count;
> +	s64 future_timestamp_total_ns;
> +
>  	/*
>  	 * Dynamic array to be able to spread datum to iio sensor objects.
>  	 */


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

* Re: [PATCH 10/13] iio: cros_ec: Use triggered buffer only when EC does not support FIFO
  2019-09-22 17:50 ` [PATCH 10/13] iio: cros_ec: Use triggered buffer only when EC does not support FIFO Gwendal Grignou
@ 2019-10-05 16:30   ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:30 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:18 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> When EC supports FIFO, the samples will flow from the kernel by
> themselves.
> When no FIFO, the user space app needs to call trigger_new, or better
> register a high precision timer.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

Trivial comments inline.

> ---
>  drivers/iio/accel/cros_ec_accel_legacy.c      |  8 +--
>  .../cros_ec_sensors/cros_ec_lid_angle.c       |  2 +-
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |  8 +--
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 61 +++++++++++++++++--
>  drivers/iio/light/cros_ec_light_prox.c        |  8 +--
>  drivers/iio/pressure/cros_ec_baro.c           |  8 +--
>  .../linux/iio/common/cros_ec_sensors_core.h   | 16 ++++-
>  7 files changed, 80 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
> index 591c0d962c44..d607fbc52c95 100644
> --- a/drivers/iio/accel/cros_ec_accel_legacy.c
> +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
> @@ -179,7 +179,8 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
>  	if (!indio_dev)
>  		return -ENOMEM;
>  
> -	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
> +			cros_ec_sensors_capture, cros_ec_sensors_push_data);
>  	if (ret)
>  		return ret;
>  
> @@ -199,11 +200,6 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
>  		state->sign[CROS_EC_SENSOR_Z] = -1;
>  	}
>  
> -	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> -			cros_ec_sensors_capture, NULL);
> -	if (ret)
> -		return ret;
> -
>  	return devm_iio_device_register(dev, indio_dev);
>  }
>  
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
> index 1dcc2a16ab2d..e30a59fcf0f9 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
> @@ -97,7 +97,7 @@ static int cros_ec_lid_angle_probe(struct platform_device *pdev)
>  	if (!indio_dev)
>  		return -ENOMEM;
>  
> -	ret = cros_ec_sensors_core_init(pdev, indio_dev, false);
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, false, NULL, NULL);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> index a88dd8deade9..b78a942ac8e5 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -239,7 +239,8 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
>  	if (!indio_dev)
>  		return -ENOMEM;
>  
> -	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
> +			cros_ec_sensors_capture, cros_ec_sensors_push_data);
>  	if (ret)
>  		return ret;
>  
> @@ -301,11 +302,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
>  	else
>  		state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
>  
> -	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> -			cros_ec_sensors_capture, NULL);
> -	if (ret)
> -		return ret;
> -
>  	return devm_iio_device_register(dev, indio_dev);
>  }
>  
> 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
> index 43eb1d42820e..c4c37c6df301 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -12,6 +12,7 @@
>  #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/module.h>
> @@ -83,15 +84,50 @@ static void get_default_min_max_freq(enum motionsensor_type type,
>  	}
>  }
>  
> +int cros_ec_sensors_push_data(
> +		struct iio_dev *indio_dev,
> +		s16 *data,
> +		s64 timestamp)
> +{
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	s16 *out;
> +	unsigned int i;
> +
> +	/*
> +	 * It can happen if we get a samples before the iio device is fully
> +	 * registered.
> +	 */
> +	if (!st)
> +		return 0;
> +
> +	/* Ignore samples if the buffer is not set. */
> +	if (!indio_dev->active_scan_mask)

Odd test.  That tests if the core has created the bitmask.
Is the intent to check if any bits are enabled?

> +		return 0;
> +
> +	out = (s16 *)st->samples;
> +	for_each_set_bit(i,
> +			 indio_dev->active_scan_mask,
> +			 indio_dev->masklength) {
> +		*out = data[i];
> +		out++;
> +	}

blank line.

> +	iio_push_to_buffers_with_timestamp(indio_dev, st->samples, timestamp);

blank line.

> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_push_data);
> +
>  int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			      struct iio_dev *indio_dev,
> -			      bool physical_device)
> +			      bool physical_device,
> +			      cros_ec_sensors_capture_t trigger_capture,
> +			      cros_ec_sensorhub_push_data_cb_t push_data)
>  {
>  	struct device *dev = &pdev->dev;
>  	struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
>  	struct cros_ec_sensorhub *sensor_hub = dev_get_drvdata(dev->parent);
>  	struct cros_ec_dev *ec = sensor_hub->ec;
>  	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
> +	struct iio_buffer *buffer;
>  	u32 ver_mask;
>  	int ret, i;
>  
> @@ -124,8 +160,6 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  	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;
>  		ret = cros_ec_motion_send_host_cmd(state, 0);
> @@ -154,9 +188,26 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			state->frequencies[2] =
>  			    state->resp->info_3.max_frequency;
>  		}
> -	}
>  
> -	return 0;
> +		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> +			buffer = devm_iio_kfifo_allocate(dev);
> +			if (!buffer)
> +				return -ENOMEM;
> +
> +			iio_device_attach_buffer(indio_dev, buffer);
> +			indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> +			ret = cros_ec_sensorhub_register_push_data(
> +					sensor_hub,
> +					sensor_platform->sensor_num,
> +					indio_dev, push_data);
> +		} else {
> +			indio_dev->modes = INDIO_DIRECT_MODE;
> +			ret = devm_iio_triggered_buffer_setup(
> +					dev, indio_dev, NULL,
> +					trigger_capture, NULL);
> +		}
> +	}
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
>  
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index c431e4d1482d..d58f010880ce 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -186,7 +186,8 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
>  	if (!indio_dev)
>  		return -ENOMEM;
>  
> -	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
> +			cros_ec_sensors_capture, cros_ec_sensors_push_data);
>  	if (ret)
>  		return ret;
>  
> @@ -245,11 +246,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
>  
>  	state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
>  
> -	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> -					      cros_ec_sensors_capture, NULL);
> -	if (ret)
> -		return ret;
> -
>  	return devm_iio_device_register(dev, indio_dev);
>  }
>  
> diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
> index 2f4d6d3ab41d..c7538e93d24f 100644
> --- a/drivers/iio/pressure/cros_ec_baro.c
> +++ b/drivers/iio/pressure/cros_ec_baro.c
> @@ -134,7 +134,8 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
>  	if (!indio_dev)
>  		return -ENOMEM;
>  
> -	ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
> +	ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
> +			cros_ec_sensors_capture, cros_ec_sensors_push_data);
>  	if (ret)
>  		return ret;
>  
> @@ -180,11 +181,6 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
>  
>  	state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
>  
> -	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> -					      cros_ec_sensors_capture, NULL);
> -	if (ret)
> -		return ret;
> -
>  	return devm_iio_device_register(dev, indio_dev);
>  }
>  
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index abef66d0e884..60f78f0e4884 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -12,6 +12,7 @@
>  #include <linux/irqreturn.h>
>  #include <linux/platform_data/cros_ec_commands.h>
>  #include <linux/platform_data/cros_ec_proto.h>
> +#include <linux/platform_data/cros_ec_sensorhub.h>
>  
>  enum {
>  	CROS_EC_SENSOR_X,
> @@ -32,6 +33,9 @@ enum {
>  /* Minimum sampling period to use when device is suspending */
>  #define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
>  
> +typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
> +
> +
>  /**
>   * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
>   * @ec:				cros EC device structure
> @@ -110,11 +114,17 @@ struct platform_device;
>   * @pdev:		platform device created for the sensors
>   * @indio_dev:		iio device structure of the device
>   * @physical_device:	true if the device refers to a physical device
> + * @trigger_capture:    function pointer to call buffer is triggered,
> + *    for backward compatibility.
> + * @push_data:          function to call when cros_ec_sensorhub receives
> + *    a sample for that sensor.
>   *
>   * Return: 0 on success, -errno on failure.
>   */
>  int cros_ec_sensors_core_init(struct platform_device *pdev,
> -			      struct iio_dev *indio_dev, bool physical_device);
> +			      struct iio_dev *indio_dev, bool physical_device,
> +			      cros_ec_sensors_capture_t trigger_capture,
> +			      cros_ec_sensorhub_push_data_cb_t push_data);
>  
>  /* To remove association of physical device to cros_ec_sensorhub. */
>  int cros_ec_sensors_core_clean(struct platform_device *pdev);
> @@ -132,6 +142,10 @@ int cros_ec_sensors_core_clean(struct platform_device *pdev);
>   * Return: IRQ_HANDLED
>   */
>  irqreturn_t cros_ec_sensors_capture(int irq, void *p);
> +int cros_ec_sensors_push_data(
> +		struct iio_dev *indio_dev,
> +		s16 *data,
> +		s64 timestamp);
>  
>  /**
>   * cros_ec_motion_send_host_cmd() - send motion sense host command


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

* Re: [PATCH 11/13] iio: cros_ec: Expose hwfifo_timeout
  2019-09-22 17:50 ` [PATCH 11/13] iio: cros_ec: Expose hwfifo_timeout Gwendal Grignou
@ 2019-10-05 16:35   ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:35 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:19 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> Expose EC sampling rate through buffer/hwfifo_timeout.

This seems a bit odd unless EC term "sampling rate"
is rather different from what I'd expect. Code looks
fine though ;)

> Rename frequency to sampling_frequency to match IIO ABI.

That looks like a straight forward bug.  We might
want to think about pushing that back to older kernels.

> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |   1 -
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 110 +++++++++++++-----
>  drivers/iio/light/cros_ec_light_prox.c        |   3 +-
>  drivers/iio/pressure/cros_ec_baro.c           |   3 +-
>  4 files changed, 81 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> index b78a942ac8e5..95a4b4cf8f87 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -255,7 +255,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
>  			BIT(IIO_CHAN_INFO_CALIBSCALE);
>  		channel->info_mask_shared_by_all =
>  			BIT(IIO_CHAN_INFO_SCALE) |
> -			BIT(IIO_CHAN_INFO_FREQUENCY) |
>  			BIT(IIO_CHAN_INFO_SAMP_FREQ);
>  		channel->info_mask_shared_by_all_available =
>  			BIT(IIO_CHAN_INFO_SAMP_FREQ);
> 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
> index c4c37c6df301..a31a761e3a81 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -11,6 +11,7 @@
>  #include <linux/iio/common/cros_ec_sensors_core.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/sysfs.h>
>  #include <linux/iio/trigger_consumer.h>
>  #include <linux/iio/triggered_buffer.h>
>  #include <linux/kernel.h>
> @@ -84,6 +85,75 @@ static void get_default_min_max_freq(enum motionsensor_type type,
>  	}
>  }
>  
> +static int cros_ec_sensor_set_ec_rate(struct cros_ec_sensors_core_state *st,
> +				      int rate)
> +{
> +	int ret;
> +
> +	mutex_lock(&st->cmd_lock);
> +	st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> +	st->param.ec_rate.data = rate;
> +	ret = cros_ec_motion_send_host_cmd(st, 0);
> +	if (ret == 0)
> +		st->curr_sampl_freq = rate;
> +	mutex_unlock(&st->cmd_lock);
> +	return ret;
> +}
> +
> +static ssize_t cros_ec_sensor_set_report_latency(struct device *dev,
> +					      struct device_attribute *attr,
> +					      const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	int integer, fract, ret;
> +	int latency;
> +
> +	ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract);
> +	if (ret)
> +		return ret;
> +
> +	/* EC rate is in ms. */
> +	latency = integer * 1000 + fract / 1000;
> +	ret = cros_ec_sensor_set_ec_rate(st, latency);
> +	if (ret < 0)
> +		return ret;
> +
> +	return len;
> +}
> +
> +static ssize_t cros_ec_sensor_get_report_latency(struct device *dev,
> +					      struct device_attribute *attr,
> +					      char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	int latency, ret;
> +
> +	mutex_lock(&st->cmd_lock);
> +	st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> +	st->param.ec_rate.data = EC_MOTION_SENSE_NO_VALUE;
> +
> +	ret = cros_ec_motion_send_host_cmd(st, 0);
> +	latency = st->resp->ec_rate.ret;
> +	mutex_unlock(&st->cmd_lock);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d.%06u\n",
> +		       latency / 1000,
> +		       (latency % 1000) * 1000);
> +}
> +
> +static IIO_DEVICE_ATTR(hwfifo_timeout, 0644,
> +		       cros_ec_sensor_get_report_latency,
> +		       cros_ec_sensor_set_report_latency, 0);
> +
> +static const struct attribute *cros_ec_sensor_fifo_attributes[] = {
> +	&iio_dev_attr_hwfifo_timeout.dev_attr.attr,
> +	NULL,
> +};
> +
>  int cros_ec_sensors_push_data(
>  		struct iio_dev *indio_dev,
>  		s16 *data,
> @@ -194,6 +264,9 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			if (!buffer)
>  				return -ENOMEM;
>  
> +			iio_buffer_set_attrs(buffer,
> +					     cros_ec_sensor_fifo_attributes);
> +
>  			iio_device_attach_buffer(indio_dev, buffer);
>  			indio_dev->modes = INDIO_BUFFER_SOFTWARE;
>  			ret = cros_ec_sensorhub_register_push_data(
> @@ -553,18 +626,6 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>  
>  	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;
> -
> -		ret = cros_ec_motion_send_host_cmd(st, 0);
> -		if (ret)
> -			break;
> -
> -		*val = st->resp->ec_rate.ret;
> -		ret = IIO_VAL_INT;
> -		break;
> -	case IIO_CHAN_INFO_FREQUENCY:
>  		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
>  		st->param.sensor_odr.data =
>  			EC_MOTION_SENSE_NO_VALUE;
> @@ -613,23 +674,16 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>  	int ret;
>  
>  	switch (mask) {
> -	case IIO_CHAN_INFO_FREQUENCY:
> +	case IIO_CHAN_INFO_SAMP_FREQ:
>  		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;
>  
> -		ret = cros_ec_motion_send_host_cmd(st, 0);
> -		break;
> -	case IIO_CHAN_INFO_SAMP_FREQ:
> -		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> -		st->param.ec_rate.data = val;
> -
>  		ret = cros_ec_motion_send_host_cmd(st, 0);
>  		if (ret)
>  			break;
> -		st->curr_sampl_freq = val;
>  		break;
>  	default:
>  		ret = -EINVAL;
> @@ -643,6 +697,7 @@ static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
>  {
>  	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>  	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +	int ret = 0;
>  
>  	if (st->curr_sampl_freq == 0)
>  		return 0;
> @@ -652,13 +707,10 @@ static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
>  	 * sleep. Set 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);
> +		ret = cros_ec_sensor_set_ec_rate(st,
> +				CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY);
>  	}
> -	return 0;
> +	return ret;
>  }
>  
>  static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
> @@ -670,11 +722,7 @@ static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
>  		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);
> +		cros_ec_sensor_set_ec_rate(st, st->curr_sampl_freq);
>  	}
>  }
>  
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index d58f010880ce..e9b0072d5c88 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -199,8 +199,7 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
>  
>  	/* Common part */
>  	channel->info_mask_shared_by_all =
> -		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> -		BIT(IIO_CHAN_INFO_FREQUENCY);
> +		BIT(IIO_CHAN_INFO_SAMP_FREQ);
>  	channel->info_mask_shared_by_all_available =
>  		BIT(IIO_CHAN_INFO_SAMP_FREQ);
>  	channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
> diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
> index c7538e93d24f..aa3cae5a1a15 100644
> --- a/drivers/iio/pressure/cros_ec_baro.c
> +++ b/drivers/iio/pressure/cros_ec_baro.c
> @@ -148,8 +148,7 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
>  	channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>  	channel->info_mask_shared_by_all =
>  		BIT(IIO_CHAN_INFO_SCALE) |
> -		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> -		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;


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

* Re: [PATCH 12/13] iio: cros_ec: Report hwfifo_watermark_max
  2019-09-22 17:50 ` [PATCH 12/13] iio: cros_ec: Report hwfifo_watermark_max Gwendal Grignou
@ 2019-10-05 16:37   ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:37 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:20 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> Report the maximum amount of sample the EC can hold.
> This is not tunable, but can be useful for application to find out the
> maximum amount of time it can sleep when hwfifo_timeout is set to a
> large number.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Looks good to me. I'm adding tags because I have no idea what route
this will eventually take and it saves me trying to remember which
patches need another look in v2.
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 35 +++++++++++++++++--
>  .../linux/iio/common/cros_ec_sensors_core.h   |  4 ++-
>  2 files changed, 36 insertions(+), 3 deletions(-)
> 
> 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
> index a31a761e3a81..a32260c9bc09 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -23,6 +23,12 @@
>  #include <linux/platform_data/cros_ec_sensorhub.h>
>  #include <linux/platform_device.h>
>  
> +/*
> + * Hard coded to the first device to support sensor fifo.  The EC has a 2048
> + * byte fifo and will trigger an interrupt when fifo is 2/3 full.
> + */
> +#define CROS_EC_FIFO_SIZE (2048 * 2 / 3)
> +
>  static char *cros_ec_loc[] = {
>  	[MOTIONSENSE_LOC_BASE] = "base",
>  	[MOTIONSENSE_LOC_LID] = "lid",
> @@ -56,8 +62,15 @@ static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev,
>  
>  static void get_default_min_max_freq(enum motionsensor_type type,
>  				     u32 *min_freq,
> -				     u32 *max_freq)
> +				     u32 *max_freq,
> +				     u32 *max_fifo_events)
>  {
> +	/*
> +	 * We don't know fifo size, set to size previously used by older
> +	 * hardware.
> +	 */
> +	*max_fifo_events = CROS_EC_FIFO_SIZE;
> +
>  	switch (type) {
>  	case MOTIONSENSE_TYPE_ACCEL:
>  	case MOTIONSENSE_TYPE_GYRO:
> @@ -122,6 +135,7 @@ static ssize_t cros_ec_sensor_set_report_latency(struct device *dev,
>  	return len;
>  }
>  
> +
>  static ssize_t cros_ec_sensor_get_report_latency(struct device *dev,
>  					      struct device_attribute *attr,
>  					      char *buf)
> @@ -149,8 +163,22 @@ static IIO_DEVICE_ATTR(hwfifo_timeout, 0644,
>  		       cros_ec_sensor_get_report_latency,
>  		       cros_ec_sensor_set_report_latency, 0);
>  
> +static ssize_t hwfifo_watermark_max_show(
> +		struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> +
> +	return sprintf(buf, "%d\n", st->fifo_max_event_count);
> +}
> +
> +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0);
> +
>  static const struct attribute *cros_ec_sensor_fifo_attributes[] = {
>  	&iio_dev_attr_hwfifo_timeout.dev_attr.attr,
> +	&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
>  	NULL,
>  };
>  
> @@ -251,12 +279,15 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  		if (state->msg->version < 3) {
>  			get_default_min_max_freq(state->resp->info.type,
>  						 &state->frequencies[1],
> -						 &state->frequencies[2]);
> +						 &state->frequencies[2],
> +						 &state->fifo_max_event_count);
>  		} else {
>  			state->frequencies[1] =
>  			    state->resp->info_3.min_frequency;
>  			state->frequencies[2] =
>  			    state->resp->info_3.max_frequency;
> +			state->fifo_max_event_count =
> +			    state->resp->info_3.fifo_max_event_count;
>  		}
>  
>  		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index 60f78f0e4884..2c6acbde0d02 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -54,7 +54,8 @@ typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
>   *				the timestamp. The timestamp is always last and
>   *				is always 8-byte aligned.
>   * @read_ec_sensors_data:	function used for accessing sensors values
> - * @cuur_sampl_freq:		current sampling period
> + * @curr_sampl_freq:		current sampling period
> + * @fifo_max_event_count:	Size of the EC sensor FIFO
>   */
>  struct cros_ec_sensors_core_state {
>  	struct cros_ec_device *ec;
> @@ -78,6 +79,7 @@ struct cros_ec_sensors_core_state {
>  				    unsigned long scan_mask, s16 *data);
>  
>  	int curr_sampl_freq;
> +	u32 fifo_max_event_count;
>  
>  	/* Table of known available frequencies : 0, Min and Max in mHz */
>  	int frequencies[3];


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

* Re: [PATCH 13/13] iio: cros_ec: Use Hertz as unit for sampling frequency
  2019-09-22 17:50 ` [PATCH 13/13] iio: cros_ec: Use Hertz as unit for sampling frequency Gwendal Grignou
@ 2019-10-05 16:39   ` Jonathan Cameron
  0 siblings, 0 replies; 35+ messages in thread
From: Jonathan Cameron @ 2019-10-05 16:39 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: knaack.h, lars, pmeerw, lee.jones, bleung, enric.balletbo,
	dianders, groeck, fabien.lahoudere, linux-kernel, linux-iio

On Sun, 22 Sep 2019 10:50:21 -0700
Gwendal Grignou <gwendal@chromium.org> wrote:

> To be compliant with other sensors, set and get sensor sampling
> frequency in Hz, not mHz.
> 
> Fixes: ae7b02ad2f32 ("iio: common: cros_ec_sensors: Expose
> cros_ec_sensors frequency range via iio sysfs")
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

Good catch.

Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 32 +++++++++++--------
>  .../linux/iio/common/cros_ec_sensors_core.h   |  6 ++--
>  2 files changed, 22 insertions(+), 16 deletions(-)
> 
> 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
> index a32260c9bc09..f7e6827bd4cb 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -227,6 +227,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
>  	struct iio_buffer *buffer;
>  	u32 ver_mask;
> +	int frequencies[ARRAY_SIZE(state->frequencies) / 2] = { 0 };
>  	int ret, i;
>  
>  	platform_set_drvdata(pdev, indio_dev);
> @@ -275,20 +276,22 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			state->calib[i].scale = MOTION_SENSE_DEFAULT_SCALE;
>  
>  		/* 0 is a correct value used to stop the device */
> -		state->frequencies[0] = 0;
>  		if (state->msg->version < 3) {
>  			get_default_min_max_freq(state->resp->info.type,
> -						 &state->frequencies[1],
> -						 &state->frequencies[2],
> +						 &frequencies[1],
> +						 &frequencies[2],
>  						 &state->fifo_max_event_count);
>  		} else {
> -			state->frequencies[1] =
> -			    state->resp->info_3.min_frequency;
> -			state->frequencies[2] =
> -			    state->resp->info_3.max_frequency;
> +			frequencies[1] = state->resp->info_3.min_frequency;
> +			frequencies[2] = state->resp->info_3.max_frequency;
>  			state->fifo_max_event_count =
>  			    state->resp->info_3.fifo_max_event_count;
>  		}
> +		for (i = 0; i < ARRAY_SIZE(frequencies); i++) {
> +			state->frequencies[2 * i] = frequencies[i] / 1000;
> +			state->frequencies[2 * i + 1] =
> +				(frequencies[i] % 1000) * 1000;
> +		}
>  
>  		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
>  			buffer = devm_iio_kfifo_allocate(dev);
> @@ -653,7 +656,7 @@ 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;
> +	int ret, frequency;
>  
>  	switch (mask) {
>  	case IIO_CHAN_INFO_SAMP_FREQ:
> @@ -665,8 +668,10 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>  		if (ret)
>  			break;
>  
> -		*val = st->resp->sensor_odr.ret;
> -		ret = IIO_VAL_INT;
> +		frequency = st->resp->sensor_odr.ret;
> +		*val = frequency / 1000;
> +		*val2 = (frequency % 1000) * 1000;
> +		ret = IIO_VAL_INT_PLUS_MICRO;
>  		break;
>  	default:
>  		ret = -EINVAL;
> @@ -690,7 +695,7 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>  	case IIO_CHAN_INFO_SAMP_FREQ:
>  		*length = ARRAY_SIZE(state->frequencies);
>  		*vals = (const int *)&state->frequencies;
> -		*type = IIO_VAL_INT;
> +		*type = IIO_VAL_INT_PLUS_MICRO;
>  		return IIO_AVAIL_LIST;
>  	}
>  
> @@ -702,12 +707,13 @@ 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;
> +	int ret, frequency;
>  
>  	switch (mask) {
>  	case IIO_CHAN_INFO_SAMP_FREQ:
> +		frequency = val * 1000 + val2 / 1000;
>  		st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
> -		st->param.sensor_odr.data = val;
> +		st->param.sensor_odr.data = frequency;
>  
>  		/* Always roundup, so caller gets at least what it asks for. */
>  		st->param.sensor_odr.roundup = 1;
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index 2c6acbde0d02..46d5110b2fe4 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -56,6 +56,8 @@ typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
>   * @read_ec_sensors_data:	function used for accessing sensors values
>   * @curr_sampl_freq:		current sampling period
>   * @fifo_max_event_count:	Size of the EC sensor FIFO
> + * @frequencies:		Table of known available frequencies:
> + *				0, Min and Max in mHz.
>   */
>  struct cros_ec_sensors_core_state {
>  	struct cros_ec_device *ec;
> @@ -80,9 +82,7 @@ struct cros_ec_sensors_core_state {
>  
>  	int curr_sampl_freq;
>  	u32 fifo_max_event_count;
> -
> -	/* Table of known available frequencies : 0, Min and Max in mHz */
> -	int frequencies[3];
> +	int frequencies[6];
>  };
>  
>  /**


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

end of thread, back to index

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-22 17:50 [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
2019-09-22 17:50 ` [PATCH 01/13] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
2019-09-30 13:15   ` Enric Balletbo i Serra
2019-09-30 16:24     ` Gwendal Grignou
2019-10-05 15:26   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 02/13] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
2019-10-01 10:31   ` Enric Balletbo i Serra
2019-10-05 15:35   ` Jonathan Cameron
2019-10-05 15:36     ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 03/13] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
2019-10-05 15:41   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 04/13] platform: chrome: cros-ec: record event timestamp in the hard irq Gwendal Grignou
2019-10-01 10:32   ` Enric Balletbo i Serra
2019-10-05 15:44   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 05/13] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
2019-10-01 10:32   ` Enric Balletbo i Serra
2019-09-22 17:50 ` [PATCH 06/13] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
2019-10-01 10:32   ` Enric Balletbo i Serra
2019-10-05 15:52   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 07/13] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
2019-10-05 16:08   ` Jonathan Cameron
2019-10-05 16:14     ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 08/13] platform: chrome: sensorhub: Add code to spread timestmap Gwendal Grignou
2019-10-05 16:16   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 09/13] platform: chrome: sensorhub: Add median filter Gwendal Grignou
2019-10-05 16:24   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 10/13] iio: cros_ec: Use triggered buffer only when EC does not support FIFO Gwendal Grignou
2019-10-05 16:30   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 11/13] iio: cros_ec: Expose hwfifo_timeout Gwendal Grignou
2019-10-05 16:35   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 12/13] iio: cros_ec: Report hwfifo_watermark_max Gwendal Grignou
2019-10-05 16:37   ` Jonathan Cameron
2019-09-22 17:50 ` [PATCH 13/13] iio: cros_ec: Use Hertz as unit for sampling frequency Gwendal Grignou
2019-10-05 16:39   ` Jonathan Cameron
2019-10-05 15:39 ` [PATCH 00/13] cros_ec: Add sensorhub driver and FIFO processing Jonathan Cameron

Linux-IIO Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-iio/0 linux-iio/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-iio linux-iio/ https://lore.kernel.org/linux-iio \
		linux-iio@vger.kernel.org linux-iio@archiver.kernel.org
	public-inbox-index linux-iio

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-iio


AGPL code for this site: git clone https://public-inbox.org/ public-inbox