linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing
@ 2019-11-05 22:26 Gwendal Grignou
  2019-11-05 22:26 ` [PATCH v4 01/17] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
                   ` (16 more replies)
  0 siblings, 17 replies; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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 patch move cros_ec_proto functions documentations into the
code to prevent rot.

The inext 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

That last patch fixes a regression that changes event processing.
Revert the patches that fixed that regression.

The next 3 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

The remaining patches update IIO cros_ec drivers:
The first patch moves cros_ec_sensor_core functions documentation into
the .c file.
Then we can use the FIFO function exposed by cros_ec_sensorhub:
  iio: cros_ec: Use triggered buffer only when EC does not support FIFO

The power management functions are not necessary anymore, since we
shutoff the FIFO from cros_ec_sensorhub:
  iio: cros_ec: Register to cros_ec_sensorhub when EC supports 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 (15):
  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
  Revert "Input: cros_ec_keyb - add back missing mask for event_type"
  Revert "Input: cros_ec_keyb: mask out extra flags in event_type"
  platform: chrome: sensorhub: Add FIFO support
  platform: chrome: sensorhub: Add code to spread timestmap
  platform: chrome: sensorhub: Add median filter
  iio: cros_ec: Move function description to .c file
  iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO
  iio: cros_ec: Remove pm function
  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      |  14 +-
 drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
 .../cros_ec_sensors/cros_ec_lid_angle.c       |   3 +-
 .../common/cros_ec_sensors/cros_ec_sensors.c  |  19 +-
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 363 +++++--
 drivers/iio/light/cros_ec_light_prox.c        |  21 +-
 drivers/iio/pressure/cros_ec_baro.c           |  14 +-
 drivers/input/keyboard/cros_ec_keyb.c         |   6 +-
 drivers/mfd/cros_ec_dev.c                     | 235 +----
 drivers/platform/chrome/Kconfig               |  12 +
 drivers/platform/chrome/Makefile              |   2 +
 drivers/platform/chrome/cros_ec.c             |  51 +-
 drivers/platform/chrome/cros_ec_ishtp.c       |  25 +-
 drivers/platform/chrome/cros_ec_lpc.c         |  17 +-
 drivers/platform/chrome/cros_ec_proto.c       | 198 +++-
 drivers/platform/chrome/cros_ec_rpmsg.c       |  19 +-
 drivers/platform/chrome/cros_ec_sensorhub.c   | 276 +++++
 .../platform/chrome/cros_ec_sensorhub_ring.c  | 984 ++++++++++++++++++
 .../linux/iio/common/cros_ec_sensors_core.h   | 104 +-
 include/linux/platform_data/cros_ec_proto.h   |  41 +-
 .../linux/platform_data/cros_ec_sensorhub.h   | 198 ++++
 21 files changed, 2073 insertions(+), 531 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.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 01/17] mfd: cros_ec: Add sensor_count and make check_features public
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-08 22:03   ` Enric Balletbo Serra
  2019-11-05 22:26 ` [PATCH v4 02/17] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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 what the Embedded Controller supports.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
Changes in v4:
  Check patch with --strict option
    Use sizeof(*obj) instead of sizeof(struct ...obj)
    Alignement
    Remove useless ().
Changes in v3:
  Fix doxygen comments.
Changes in v2:
  Fix spelling in commit message.
  Cleanup the case where DUMP command is not supported.
  Move code from mfd to platform/chrome/

 drivers/mfd/cros_ec_dev.c                   |  32 ------
 drivers/platform/chrome/cros_ec_proto.c     | 117 ++++++++++++++++++++
 include/linux/platform_data/cros_ec_proto.h |   5 +
 3 files changed, 122 insertions(+), 32 deletions(-)

diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index 6e6dfd6c1871..a35104e35cb4 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -112,38 +112,6 @@ 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)
-{
-	struct cros_ec_command *msg;
-	int ret;
-
-	if (ec->features[0] == -1U && ec->features[1] == -1U) {
-		/* features bitmap not read yet */
-		msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
-		if (!msg)
-			return -ENOMEM;
-
-		msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
-		msg->insize = sizeof(ec->features);
-
-		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
-		if (ret < 0) {
-			dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
-				 ret, msg->result);
-			memset(ec->features, 0, sizeof(ec->features));
-		} else {
-			memcpy(ec->features, msg->data, sizeof(ec->features));
-		}
-
-		dev_dbg(ec->dev, "EC features %08x %08x\n",
-			ec->features[0], ec->features[1]);
-
-		kfree(msg);
-	}
-
-	return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
-}
-
 static void cros_ec_class_release(struct device *dev)
 {
 	kfree(to_cros_ec_dev(dev));
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 7db58771ec77..b502933e911b 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -717,3 +717,120 @@ u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
 	return host_event;
 }
 EXPORT_SYMBOL(cros_ec_get_host_event);
+
+/**
+ * cros_ec_check_features - Test for the presence of EC features
+ *
+ * @ec: EC device, does not have to be connected directly to the AP,
+ *      can be daisy chained through another device.
+ * @feature: One of ec_feature_code bit.
+ *
+ * Call this function to test whether the ChromeOS EC supports a feature.
+ *
+ * Return: 1 if supported, 0 if not
+ */
+int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
+{
+	struct cros_ec_command *msg;
+	int ret;
+
+	if (ec->features[0] == -1U && ec->features[1] == -1U) {
+		/* features bitmap not read yet */
+		msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
+		if (!msg)
+			return -ENOMEM;
+
+		msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
+		msg->insize = sizeof(ec->features);
+
+		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
+		if (ret < 0) {
+			dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
+				 ret, msg->result);
+			memset(ec->features, 0, sizeof(ec->features));
+		} else {
+			memcpy(ec->features, msg->data, sizeof(ec->features));
+		}
+
+		dev_dbg(ec->dev, "EC features %08x %08x\n",
+			ec->features[0], ec->features[1]);
+
+		kfree(msg);
+	}
+
+	return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
+}
+EXPORT_SYMBOL_GPL(cros_ec_check_features);
+
+/**
+ * cros_ec_get_sensor_count - Return the number of MEMS sensors supported.
+ *
+ * @ec: EC device, does not have to be connected directly to the AP,
+ *      can be daisy chained through another device.
+ * Return: < 0 in case of error.
+ */
+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(*msg) + max(sizeof(*params), sizeof(*resp)),
+		      GFP_KERNEL);
+	if (!msg)
+		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;
+		} else {
+			/*
+			 * EC uses LPC interface and no sensors are presented.
+			 */
+			sensor_count = 0;
+		}
+	} else if (sensor_count == -EPROTO) {
+		/* EC responded, but does not understand DUMP command. */
+		sensor_count = 0;
+	}
+	return sensor_count;
+}
+EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
index 0d4e4aaed37a..f3de0662135d 100644
--- a/include/linux/platform_data/cros_ec_proto.h
+++ b/include/linux/platform_data/cros_ec_proto.h
@@ -12,6 +12,7 @@
 #include <linux/mutex.h>
 #include <linux/notifier.h>
 
+#include <linux/mfd/cros_ec.h>
 #include <linux/platform_data/cros_ec_commands.h>
 
 #define CROS_EC_DEV_NAME	"cros_ec"
@@ -213,4 +214,8 @@ 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);
 
+int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
+
+int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
+
 #endif /* __LINUX_CROS_EC_PROTO_H */
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 02/17] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
  2019-11-05 22:26 ` [PATCH v4 01/17] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 12:10   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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>
---
Changes in v4:
- Use platform_device_register_data in children registration.
- Free registered pdev children at remove time.
- Remove useless includes
- Check patch with --strict option
    Use sizeof(*obj) instead of sizeof(struct ...obj)
    Alignement
- Describe cros_ec_sensorhub in kernel-doc format.
Changes in v3:
- Fix doxygen comments
- Fix use of ret |=
- Remove unncessary goto.
Changes in v2:
- Remove unerelated changes.
- Fix spelling.
- Use !x instead of x == NULL
- Use platform_ API directly to register IIO sensors from
  cros_ec_sensorhub.

 drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
 drivers/platform/chrome/Kconfig               |  12 +
 drivers/platform/chrome/Makefile              |   1 +
 drivers/platform/chrome/cros_ec_sensorhub.c   | 223 ++++++++++++++++++
 .../linux/platform_data/cros_ec_sensorhub.h   |  33 +++
 5 files changed, 270 insertions(+), 1 deletion(-)
 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..56a25317a6be 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -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 Sensor 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 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..6a0aa84cf092
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_sensorhub.c
@@ -0,0 +1,223 @@
+// 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/module.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_data/cros_ec_sensorhub.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define DRV_NAME		"cros-ec-sensorhub"
+
+static struct platform_device *cros_ec_sensorhub_allocate_single_sensor(
+		struct device *parent,
+		char *sensor_name,
+		int sensor_num)
+{
+	struct cros_ec_sensor_platform sensor_platforms = {
+		.sensor_num = sensor_num,
+	};
+
+	return platform_device_register_data(parent, sensor_name,
+				PLATFORM_DEVID_AUTO,
+				&sensor_platforms,
+				sizeof(sensor_platforms));
+}
+
+static int cros_ec_sensorhub_register(struct device *dev,
+				      struct cros_ec_sensorhub *sensorhub)
+{
+	int ret, i, id, sensor_num;
+	struct cros_ec_dev *ec = sensorhub->ec;
+	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 platform_device *pdev;
+	char *name;
+
+	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;
+	}
+
+	/* Prepare a message to send INFO command to each sensor. */
+	msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
+		      GFP_KERNEL);
+	if (!msg)
+		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;
+	resp = (struct ec_response_motion_sense *)msg->data;
+
+	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:
+			name = "cros-ec-accel";
+			break;
+		case MOTIONSENSE_TYPE_BARO:
+			name = "cros-ec-baro";
+			break;
+		case MOTIONSENSE_TYPE_GYRO:
+			name = "cros-ec-gyro";
+			break;
+		case MOTIONSENSE_TYPE_MAG:
+			name = "cros-ec-mag";
+			break;
+		case MOTIONSENSE_TYPE_PROX:
+			name = "cros-ec-prox";
+			break;
+		case MOTIONSENSE_TYPE_LIGHT:
+			name = "cros-ec-light";
+			break;
+		case MOTIONSENSE_TYPE_ACTIVITY:
+			name = "cros-ec-activity";
+			break;
+		default:
+			dev_warn(dev, "unknown type %d\n", resp->info.type);
+			continue;
+		}
+		pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
+		if (IS_ERR(pdev)) {
+			ret = IS_ERR(pdev);
+			goto error;
+		}
+		sensorhub->sensor_pdev[id++] = pdev;
+		sensor_type[resp->info.type]++;
+	}
+
+	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)) {
+		pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
+							"cros-ec-lid-angle", 0);
+		if (IS_ERR(pdev)) {
+			ret = IS_ERR(pdev);
+			goto error;
+		}
+		sensorhub->sensor_pdev[id++] = pdev;
+	}
+
+error:
+	kfree(msg);
+	return ret;
+}
+
+static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
+{
+	struct device *dev = &sensorhub_pdev->dev;
+	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
+	int ret, i;
+	struct platform_device *pdev;
+	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_sensorhub_register(dev, data);
+		if (ret) {
+			dev_err(dev, "Register failed %d\n", ret);
+			goto unregister_sensors;
+		}
+	} else {
+		/*
+		 * If the device has sensors but does not claim to
+		 * be a sensor hub, we are in legacy mode.
+		 */
+		for (i = 0; i < 2; i++) {
+			pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
+						"cros-ec-accel-legacy", i);
+			if (IS_ERR(pdev)) {
+				ret = IS_ERR(pdev);
+				dev_err(dev, "Legacy %d failed %d\n", i, ret);
+				goto unregister_sensors;
+			} else {
+				data->sensor_pdev[i] = pdev;
+			}
+		}
+	}
+
+	return 0;
+
+unregister_sensors:
+	/*
+	 * Given the probe has failed, we need to unregister all the sensors,
+	 * not jutst the one that did not work: this device will be
+	 * de-allocated.
+	 */
+	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
+		pdev = data->sensor_pdev[i];
+		if (pdev)
+			platform_device_unregister(pdev);
+	}
+	return ret;
+}
+
+static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
+{
+	struct cros_ec_sensorhub *sensorhub =
+		platform_get_drvdata(sensorhub_pdev);
+	struct platform_device *pdev;
+	int i;
+
+	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
+		pdev = sensorhub->sensor_pdev[i];
+		if (pdev)
+			platform_device_unregister(pdev);
+	}
+
+	return 0;
+}
+
+static struct platform_driver cros_ec_sensorhub_driver = {
+	.driver = {
+		.name = DRV_NAME,
+	},
+	.probe = cros_ec_sensorhub_probe,
+	.remove = cros_ec_sensorhub_remove,
+};
+
+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..da0ba1d201e4
--- /dev/null
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -0,0 +1,33 @@
+/* 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>
+
+/* Maximal number of sensors supported by the EC. */
+#define CROS_EC_SENSOR_MAX 16
+
+/*
+ * Maximal number of sensors supported by the hub:
+ * We add one for the lid angle inclinometer sensor.
+ */
+#define CROS_EC_SENSOR_PDEV_MAX (CROS_EC_SENSOR_MAX + 1)
+
+/**
+ * struct cros_ec_sensorhub - Sensor Hub device data.
+ *
+ * @ec:           Embedded Controller where the hub is located.
+ * @sensor_pdev:  Array of platform_device, one per sensor.
+ */
+struct cros_ec_sensorhub {
+	struct cros_ec_dev *ec;
+	struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
+};
+
+#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

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

- 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>
---
No changes in v4, v3.
Changes in v2:
- Remove unerelated changes.
- Remove ec presence test in iio driver, done in cros_ec_sensorhub.

 drivers/iio/accel/cros_ec_accel_legacy.c      |   6 -
 .../common/cros_ec_sensors/cros_ec_sensors.c  |   6 -
 .../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 ++----------------
 include/linux/platform_data/cros_ec_proto.h   |   8 -
 .../linux/platform_data/cros_ec_sensorhub.h   |   8 +
 7 files changed, 23 insertions(+), 218 deletions(-)

diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
index fcc3f999e482..65f85faf6f31 100644
--- a/drivers/iio/accel/cros_ec_accel_legacy.c
+++ b/drivers/iio/accel/cros_ec_accel_legacy.c
@@ -163,16 +163,10 @@ 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 iio_dev *indio_dev;
 	struct cros_ec_sensors_core_state *state;
 	int ret;
 
-	if (!ec || !ec->ec_dev) {
-		dev_warn(&pdev->dev, "No EC device found.\n");
-		return -EINVAL;
-	}
-
 	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
 	if (!indio_dev)
 		return -ENOMEM;
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..7dce04473467 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -222,17 +222,11 @@ 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 iio_dev *indio_dev;
 	struct cros_ec_sensors_state *state;
 	struct iio_chan_spec *channel;
 	int ret, i;
 
-	if (!ec_dev || !ec_dev->ec_dev) {
-		dev_warn(&pdev->dev, "No CROS EC device found.\n");
-		return -EINVAL;
-	}
-
 	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
 	if (!indio_dev)
 		return -ENOMEM;
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..d85a391e50c5 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -169,17 +169,11 @@ 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 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) {
-		dev_warn(dev, "No CROS EC device found.\n");
-		return -EINVAL;
-	}
-
 	indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
 	if (!indio_dev)
 		return -ENOMEM;
diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index a35104e35cb4..c4b977a5dd96 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", },
@@ -117,192 +121,6 @@ static void cros_ec_class_release(struct device *dev)
 	kfree(to_cros_ec_dev(dev));
 }
 
-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;
@@ -358,11 +176,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/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
index f3de0662135d..691f9e953a96 100644
--- a/include/linux/platform_data/cros_ec_proto.h
+++ b/include/linux/platform_data/cros_ec_proto.h
@@ -168,14 +168,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 da0ba1d201e4..2b5a4d81f65f 100644
--- a/include/linux/platform_data/cros_ec_sensorhub.h
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -19,6 +19,14 @@
  */
 #define CROS_EC_SENSOR_PDEV_MAX (CROS_EC_SENSOR_MAX + 1)
 
+/**
+ * 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.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 04/17] platform: chrome: cros-ec: record event timestamp in the hard irq
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (2 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 12:16   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 05/17] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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>
---
Changes in v4:
  Check patch with --strict option
    Alignement
No changes in v3.
Changes in v2:
  Make cros_ec_get_time_ns inline.
  Using ktime_t instead of s64 when dealing with time.
  Added code in ishtp to gather timestamp.

 drivers/platform/chrome/cros_ec.c           | 17 ++++++++++++++---
 drivers/platform/chrome/cros_ec_ishtp.c     | 17 +++++++++++++++--
 drivers/platform/chrome/cros_ec_lpc.c       |  2 ++
 include/linux/platform_data/cros_ec_proto.h | 16 ++++++++++++++++
 4 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index 9b2d07422e17..925f84dbf621 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -31,6 +31,15 @@ static struct cros_ec_platform pd_p = {
 	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
 };
 
+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;
@@ -141,9 +150,11 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
 	}
 
 	if (ec_dev->irq) {
-		err = devm_request_threaded_irq(dev, ec_dev->irq, NULL,
-				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-				"chromeos-ec", ec_dev);
+		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) {
 			dev_err(dev, "Failed to request IRQ %d: %d",
 				ec_dev->irq, err);
diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
index 25ca2c894b4d..5c848f22b44b 100644
--- a/drivers/platform/chrome/cros_ec_ishtp.c
+++ b/drivers/platform/chrome/cros_ec_ishtp.c
@@ -200,13 +200,14 @@ static int ish_send(struct ishtp_cl_data *client_data,
  * process_recv() - Received and parse incoming packet
  * @cros_ish_cl: Client instance to get stats
  * @rb_in_proc: Host interface message buffer
+ * @timestamp: Timestamp of when parent callback started
  *
  * Parse the incoming packet. If it is a response packet then it will
  * update per instance flags and wake up the caller waiting to for the
  * response. If it is an event packet then it will schedule event work.
  */
 static void process_recv(struct ishtp_cl *cros_ish_cl,
-			 struct ishtp_cl_rb *rb_in_proc)
+			 struct ishtp_cl_rb *rb_in_proc, ktime_t timestamp)
 {
 	size_t data_len = rb_in_proc->buf_idx;
 	struct ishtp_cl_data *client_data =
@@ -295,6 +296,11 @@ static void process_recv(struct ishtp_cl *cros_ish_cl,
 		break;
 
 	case CROS_MKBP_EVENT:
+		/*
+		 * Set timestamp from beginning of function since we actually
+		 * got an incoming MKBP event
+		 */
+		client_data->ec_dev->last_event_time = timestamp;
 		/* The event system doesn't send any data in buffer */
 		schedule_work(&client_data->work_ec_evt);
 
@@ -322,10 +328,17 @@ static void ish_event_cb(struct ishtp_cl_device *cl_device)
 {
 	struct ishtp_cl_rb *rb_in_proc;
 	struct ishtp_cl	*cros_ish_cl = ishtp_get_drvdata(cl_device);
+	ktime_t timestamp;
+
+	/*
+	 * Take timestamp as close to hardware interrupt as possible for sensor
+	 * timestamps.
+	 */
+	timestamp = cros_ec_get_time_ns();
 
 	while ((rb_in_proc = ishtp_cl_rx_get_rb(cros_ish_cl)) != NULL) {
 		/* Decide what to do with received data */
-		process_recv(cros_ish_cl, rb_in_proc);
+		process_recv(cros_ish_cl, rb_in_proc, timestamp);
 	}
 }
 
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 691f9e953a96..b183024fef1f 100644
--- a/include/linux/platform_data/cros_ec_proto.h
+++ b/include/linux/platform_data/cros_ec_proto.h
@@ -122,6 +122,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
@@ -162,6 +164,7 @@ struct cros_ec_device {
 	int event_size;
 	u32 host_event_wake_mask;
 	u32 last_resume_result;
+	ktime_t last_event_time;
 
 	/* The platform devices used by the mfd driver */
 	struct platform_device *ec;
@@ -210,4 +213,17 @@ int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
 
 int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
 
+/**
+ * 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.
+ *
+ * Return: ktime_t format since boot.
+ */
+static inline ktime_t cros_ec_get_time_ns(void)
+{
+	return ktime_get_boottime_ns();
+}
+
 #endif /* __LINUX_CROS_EC_PROTO_H */
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

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

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@chromium.org>
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
No changes in v4, v3.
Changes in v2:
  Remove dual Enrico's signature.

 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 925f84dbf621..d3dfa27171e6 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -149,7 +149,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,
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 06/17] platform: chrome: cros_ec: handle MKBP more events flag
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (4 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 05/17] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 12:28   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 07/17] Revert "Input: cros_ec_keyb - add back missing mask for event_type" Gwendal Grignou
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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 (Matrix KeyBoard Protocol)
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>
---
Changes in v4:
- Check patch with --strict option
    Alignement
Changes in v3:
  Fix indentation.
Changes in v2:
  Process flag inside cros_ec_get_next_event, clean flag from event.
  Introduce public function cros_ec_handle_event(), use it in rpmsg and
    ishtp transport layer.
  Remplace dev_info with dev_dbg, call only once.

 drivers/platform/chrome/cros_ec.c           | 34 +++++++--
 drivers/platform/chrome/cros_ec_ishtp.c     |  8 +-
 drivers/platform/chrome/cros_ec_lpc.c       | 15 +++-
 drivers/platform/chrome/cros_ec_proto.c     | 81 +++++++++++++--------
 drivers/platform/chrome/cros_ec_rpmsg.c     | 19 +----
 include/linux/platform_data/cros_ec_proto.h | 12 ++-
 6 files changed, 107 insertions(+), 62 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index d3dfa27171e6..c9f0b9ebcbc1 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -40,13 +40,23 @@ static irqreturn_t ec_irq_handler(int irq, void *data)
 	return IRQ_WAKE_THREAD;
 }
 
-static irqreturn_t ec_irq_thread(int irq, void *data)
+/**
+ * cros_ec_handle_event - process and forward pending events on EC
+ * @ec_dev: Device with events to process.
+ *
+ * Call this function in a loop when the kernel is notified that the EC has
+ * pending events.
+ *
+ * Return: true if more events are still pending and this function should be
+ * called again.
+ */
+bool cros_ec_handle_event(struct cros_ec_device *ec_dev)
 {
-	struct cros_ec_device *ec_dev = data;
-	bool wake_event = true;
+	bool wake_event;
+	bool ec_has_more_events;
 	int ret;
 
-	ret = cros_ec_get_next_event(ec_dev, &wake_event);
+	ret = cros_ec_get_next_event(ec_dev, &wake_event, &ec_has_more_events);
 
 	/*
 	 * Signal only if wake host events or any interrupt if
@@ -59,6 +69,20 @@ static irqreturn_t ec_irq_thread(int irq, void *data)
 	if (ret > 0)
 		blocking_notifier_call_chain(&ec_dev->event_notifier,
 					     0, ec_dev);
+
+	return ec_has_more_events;
+}
+EXPORT_SYMBOL(cros_ec_handle_event);
+
+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 = cros_ec_handle_event(ec_dev);
+	} while (ec_has_more_events);
+
 	return IRQ_HANDLED;
 }
 
@@ -274,7 +298,7 @@ EXPORT_SYMBOL(cros_ec_suspend);
 static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev)
 {
 	while (ec_dev->mkbp_event_supported &&
-	       cros_ec_get_next_event(ec_dev, NULL) > 0)
+	       cros_ec_get_next_event(ec_dev, NULL, NULL) > 0)
 		blocking_notifier_call_chain(&ec_dev->event_notifier,
 					     1, ec_dev);
 }
diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
index 5c848f22b44b..e5996821d08b 100644
--- a/drivers/platform/chrome/cros_ec_ishtp.c
+++ b/drivers/platform/chrome/cros_ec_ishtp.c
@@ -136,11 +136,11 @@ static void ish_evt_handler(struct work_struct *work)
 	struct ishtp_cl_data *client_data =
 		container_of(work, struct ishtp_cl_data, work_ec_evt);
 	struct cros_ec_device *ec_dev = client_data->ec_dev;
+	bool ec_has_more_events;
 
-	if (cros_ec_get_next_event(ec_dev, NULL) > 0) {
-		blocking_notifier_call_chain(&ec_dev->event_notifier,
-					     0, ec_dev);
-	}
+	do {
+		ec_has_more_events = cros_ec_handle_event(ec_dev);
+	} while (ec_has_more_events);
 }
 
 /**
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index 3c77496e164d..dccf479c6625 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -312,13 +312,20 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
 static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
 {
 	struct cros_ec_device *ec_dev = data;
+	bool ec_has_more_events;
+	int ret;
 
 	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,
-					     ec_dev);
+	if (ec_dev->mkbp_event_supported)
+		do {
+			ret = cros_ec_get_next_event(ec_dev, NULL,
+						     &ec_has_more_events);
+			if (ret > 0)
+				blocking_notifier_call_chain(
+						&ec_dev->event_notifier, 0,
+						ec_dev);
+		} while (ec_has_more_events);
 
 	if (value == ACPI_NOTIFY_DEVICE_WAKE)
 		pm_system_wakeup();
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index b502933e911b..03173ca66b1b 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -456,7 +456,10 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
 	if (ret < 0 || ver_mask == 0)
 		ec_dev->mkbp_event_supported = 0;
 	else
-		ec_dev->mkbp_event_supported = 1;
+		ec_dev->mkbp_event_supported = fls(ver_mask);
+
+	dev_dbg(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,
@@ -569,6 +572,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;
@@ -581,7 +585,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;
@@ -589,30 +593,26 @@ 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;
 
+	memset(msg, 0, sizeof(*msg));
 	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)
@@ -639,32 +639,55 @@ static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
  * @ec_dev: Device to fetch event from.
  * @wake_event: Pointer to a bool set to true upon return if the event might be
  *              treated as a wake event. Ignored if null.
+ * @has_more_events: Pointer to bool set to true if more than one event is
+ *              pending.
+ *              Some EC will set this flag to indicate cros_ec_get_next_event()
+ *              can be called multiple times in a row.
+ *              It is an optimization to prevent issuing a EC command for
+ *              nothing or wait for another interrupt from the EC to process
+ *              the next message.
+ *              Ignored if null.
  *
  * Return: negative error code on errors; 0 for no data; or else number of
  * bytes received (i.e., an event was retrieved successfully). Event types are
  * written out to @ec_dev->event_data.event_type on success.
  */
-int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
+int cros_ec_get_next_event(struct cros_ec_device *ec_dev,
+			   bool *wake_event,
+			   bool *has_more_events)
 {
 	u8 event_type;
 	u32 host_event;
 	int ret;
 
-	if (!ec_dev->mkbp_event_supported) {
-		ret = get_keyboard_state_event(ec_dev);
-		if (ret <= 0)
-			return ret;
+	/*
+	 * Default value for wake_event.
+	 * Wake up on keyboard event, wake up for spurious interrupt or link
+	 * error to the EC.
+	 */
+	if (wake_event)
+		*wake_event = true;
 
-		if (wake_event)
-			*wake_event = true;
+	/*
+	 * Default value for has_more_events.
+	 * EC will raise another interrupt if AP does not process all events
+	 * anyway.
+	 */
+	if (has_more_events)
+		*has_more_events = false;
 
-		return ret;
-	}
+	if (!ec_dev->mkbp_event_supported)
+		return get_keyboard_state_event(ec_dev);
 
 	ret = get_next_event(ec_dev);
 	if (ret <= 0)
 		return ret;
 
+	if (has_more_events)
+		*has_more_events = ec_dev->event_data.event_type &
+			EC_MKBP_HAS_MORE_EVENTS;
+	ec_dev->event_data.event_type &= EC_MKBP_EVENT_TYPE_MASK;
+
 	if (wake_event) {
 		event_type = ec_dev->event_data.event_type;
 		host_event = cros_ec_get_host_event(ec_dev);
@@ -679,11 +702,7 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
 		else if (host_event &&
 			 !(host_event & ec_dev->host_event_wake_mask))
 			*wake_event = false;
-		/* Consider all other events as wake events. */
-		else
-			*wake_event = true;
 	}
-
 	return ret;
 }
 EXPORT_SYMBOL(cros_ec_get_next_event);
diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c
index 0c3738c3244d..bd068afe43b5 100644
--- a/drivers/platform/chrome/cros_ec_rpmsg.c
+++ b/drivers/platform/chrome/cros_ec_rpmsg.c
@@ -143,22 +143,11 @@ cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work)
 						      struct cros_ec_rpmsg,
 						      host_event_work);
 	struct cros_ec_device *ec_dev = dev_get_drvdata(&ec_rpmsg->rpdev->dev);
-	bool wake_event = true;
-	int ret;
-
-	ret = cros_ec_get_next_event(ec_dev, &wake_event);
-
-	/*
-	 * 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))
-		pm_wakeup_event(ec_dev->dev, 0);
+	bool ec_has_more_events;
 
-	if (ret > 0)
-		blocking_notifier_call_chain(&ec_dev->event_notifier,
-					     0, ec_dev);
+	do {
+		ec_has_more_events = cros_ec_handle_event(ec_dev);
+	} while (ec_has_more_events);
 }
 
 static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
index b183024fef1f..e238930ae967 100644
--- a/include/linux/platform_data/cros_ec_proto.h
+++ b/include/linux/platform_data/cros_ec_proto.h
@@ -116,7 +116,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.
@@ -156,7 +158,7 @@ struct cros_ec_device {
 	int (*pkt_xfer)(struct cros_ec_device *ec,
 			struct cros_ec_command *msg);
 	struct mutex lock;
-	bool mkbp_event_supported;
+	u8 mkbp_event_supported;
 	bool host_sleep_v1;
 	struct blocking_notifier_head event_notifier;
 
@@ -205,7 +207,9 @@ int cros_ec_unregister(struct cros_ec_device *ec_dev);
 
 int cros_ec_query_all(struct cros_ec_device *ec_dev);
 
-int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event);
+int cros_ec_get_next_event(struct cros_ec_device *ec_dev,
+			   bool *wake_event,
+			   bool *has_more_events);
 
 u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev);
 
@@ -213,6 +217,8 @@ int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
 
 int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
 
+bool cros_ec_handle_event(struct cros_ec_device *ec_dev);
+
 /**
  * cros_ec_get_time_ns - Return time in ns.
  *
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 07/17] Revert "Input: cros_ec_keyb - add back missing mask for event_type"
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (5 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 06/17] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-11  9:20   ` Enric Balletbo i Serra
  2019-11-05 22:26 ` [PATCH v4 08/17] Revert "Input: cros_ec_keyb: mask out extra flags in event_type" Gwendal Grignou
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, jic23, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

This reverts commit 62c3801619e16b68a37ea899b76572145dfe41c9.

This patch is not needed anymore since we clear EC_MKBP_HAS_MORE_EVENTS
flag before calling the notifiers in patch
"9d9518f5b52a (platform: chrome: cros_ec: handle MKBP more events flag)"

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
No changes in v4, v3.
New to v2.

 drivers/input/keyboard/cros_ec_keyb.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 8d4d9786cc74..a29e81fdf186 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -226,8 +226,6 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
 {
 	struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb,
 						  notifier);
-	uint8_t mkbp_event_type = ckdev->ec->event_data.event_type &
-				  EC_MKBP_EVENT_TYPE_MASK;
 	u32 val;
 	unsigned int ev_type;
 
@@ -239,7 +237,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
 	if (queued_during_suspend && !device_may_wakeup(ckdev->dev))
 		return NOTIFY_OK;
 
-	switch (mkbp_event_type) {
+	switch (ckdev->ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK) {
 	case EC_MKBP_EVENT_KEY_MATRIX:
 		pm_wakeup_event(ckdev->dev, 0);
 
@@ -266,7 +264,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
 	case EC_MKBP_EVENT_SWITCH:
 		pm_wakeup_event(ckdev->dev, 0);
 
-		if (mkbp_event_type == EC_MKBP_EVENT_BUTTON) {
+		if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {
 			val = get_unaligned_le32(
 					&ckdev->ec->event_data.data.buttons);
 			ev_type = EV_KEY;
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 08/17] Revert "Input: cros_ec_keyb: mask out extra flags in event_type"
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (6 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 07/17] Revert "Input: cros_ec_keyb - add back missing mask for event_type" Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-11  9:20   ` Enric Balletbo i Serra
  2019-11-05 22:26 ` [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, jic23, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

This reverts commit d096aa3eb6045a6a475a0239f3471c59eedf3d61.

This patch is not needed anymore since we clear EC_MKBP_HAS_MORE_EVENTS
flag before calling the notifiers in patch
"9d9518f5b52a (platform: chrome: cros_ec: handle MKBP more events flag)"

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
No changes in v4, v3.
New to v2.

 drivers/input/keyboard/cros_ec_keyb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index a29e81fdf186..2b71c5a51f90 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -237,7 +237,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
 	if (queued_during_suspend && !device_may_wakeup(ckdev->dev))
 		return NOTIFY_OK;
 
-	switch (ckdev->ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK) {
+	switch (ckdev->ec->event_data.event_type) {
 	case EC_MKBP_EVENT_KEY_MATRIX:
 		pm_wakeup_event(ckdev->dev, 0);
 
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (7 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 08/17] Revert "Input: cros_ec_keyb: mask out extra flags in event_type" Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-06 21:13   ` Gwendal Grignou
  2019-11-05 22:26 ` [PATCH v4 10/17] platform: chrome: sensorhub: Add code to spread timestmap Gwendal Grignou
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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>
---
Changes in v4:
- Keep defining cros_ec_sensorhub in kernel-doc format
- Fix logic error when checking if sensor index outside array.
- Check patch with --strict option
    Use sizeof(*obj) instead of sizeof(struct ...obj)
    Alignement
    Use uX instead of uintX_t
    Use !ptr instead of ptr != NULL
Changes in v3:
- Do not use ret !=
- Simplfy errpr handling by removing a goto
- Fix doxygen comments
- Replace suspend/resume entry points with regular driver entry point;
  There was an issue in the past where the sensor stack was preventing
  device to suspend, but the proper fix has been implemented in cros_ec
  code (6ad16b78a039b "platform/chrome: don't report EC_MKBP_EVENT_SENSOR_FIFO as wakeup")
- Reduce mutex scope by checking return code outside of it.
Changes in v2:
- Do not register a .remove routinge in plaform_driver. A
  devm_action_add is added later patch IIO driver register their
callback.
- Remove double lines, add lines before return calls.
- Handle FLUSH flag from EC.

- Use ktime_t for most timestamp measurements.
- Add doxygen comments
- Cleanup timestamp collection when processing FIFO.
- Rename fifo_toggle to fifo_enable

 drivers/platform/chrome/Makefile              |   3 +-
 drivers/platform/chrome/cros_ec_sensorhub.c   | 117 +++--
 .../platform/chrome/cros_ec_sensorhub_ring.c  | 423 ++++++++++++++++++
 .../linux/platform_data/cros_ec_sensorhub.h   |  82 ++++
 4 files changed, 592 insertions(+), 33 deletions(-)
 create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c

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 6a0aa84cf092..0e34ad2903bb 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub.c
@@ -39,11 +39,9 @@ static int cros_ec_sensorhub_register(struct device *dev,
 	int ret, i, id, sensor_num;
 	struct cros_ec_dev *ec = sensorhub->ec;
 	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 platform_device *pdev;
 	char *name;
+	struct cros_ec_command *msg = sensorhub->msg;
 
 	sensor_num = cros_ec_get_sensor_count(ec);
 	if (sensor_num < 0) {
@@ -58,30 +56,21 @@ static int cros_ec_sensorhub_register(struct device *dev,
 		return -EINVAL;
 	}
 
-	/* Prepare a message to send INFO command to each sensor. */
-	msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
-		      GFP_KERNEL);
-	if (!msg)
-		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;
-	resp = (struct ec_response_motion_sense *)msg->data;
+	msg->insize = sizeof(struct ec_response_motion_sense);
+	msg->outsize = sizeof(struct ec_params_motion_sense);
 
 	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:
 			name = "cros-ec-accel";
 			break;
@@ -104,16 +93,16 @@ static int cros_ec_sensorhub_register(struct device *dev,
 			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;
 		}
 		pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
-		if (IS_ERR(pdev)) {
-			ret = IS_ERR(pdev);
-			goto error;
-		}
+		if (IS_ERR(pdev))
+			return IS_ERR(pdev);
+
 		sensorhub->sensor_pdev[id++] = pdev;
-		sensor_type[resp->info.type]++;
+		sensor_type[sensorhub->resp->info.type]++;
 	}
 
 	if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
@@ -123,16 +112,13 @@ static int cros_ec_sensorhub_register(struct device *dev,
 				   EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
 		pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
 							"cros-ec-lid-angle", 0);
-		if (IS_ERR(pdev)) {
-			ret = IS_ERR(pdev);
-			goto error;
-		}
+		if (IS_ERR(pdev))
+			return IS_ERR(pdev);
+
 		sensorhub->sensor_pdev[id++] = pdev;
 	}
 
-error:
-	kfree(msg);
-	return ret;
+	return 0;
 }
 
 static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
@@ -141,13 +127,29 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
 	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
 	int ret, i;
 	struct platform_device *pdev;
-	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. */
@@ -175,6 +177,20 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
 		}
 	}
 
+	/*
+	 * 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);
+		if (ret)
+			goto unregister_sensors;
+		/*
+		 * The msg and its data is not under the control of the ring
+		 * handler.
+		 */
+	}
+
 	return 0;
 
 unregister_sensors:
@@ -195,9 +211,12 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
 {
 	struct cros_ec_sensorhub *sensorhub =
 		platform_get_drvdata(sensorhub_pdev);
+	struct cros_ec_dev *ec = sensorhub->ec;
 	struct platform_device *pdev;
 	int i;
 
+	if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
+		cros_ec_sensorhub_ring_remove(sensorhub);
 	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
 		pdev = sensorhub->sensor_pdev[i];
 		if (pdev)
@@ -207,9 +226,43 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
 	return 0;
 }
 
+#if CONFIG_PM_SLEEP
+/*
+ * 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 cros_ec_ring_suspend(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_enable(sensorhub, false);
+	return 0;
+}
+
+static int cros_ec_ring_resume(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_enable(sensorhub, true);
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_ring_pm_ops,
+		cros_ec_ring_suspend,
+		cros_ec_ring_resume);
+
 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,
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..f091f2a4ccfe
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -0,0 +1,423 @@
+// 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;
+
+	if (sample->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
+		return 0;
+
+	return cb(indio_dev, sample->vector, sample->timestamp);
+}
+
+/**
+ * cros_ec_sensorhub_register_push_data - register the callback to the hub.
+ *
+ * @sensorhub : Sensor Hub object
+ * @sensor_num : The sensor the caller is interested in.
+ * @indio_dev : The iio device to use when a sample arrives.
+ * @cb : The callback to call when a sample arrives.
+ *
+ * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
+ * from the EC.
+ *
+ * Return: 0 when callback is registered.
+ */
+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_PDEV_MAX)
+		return -EINVAL;
+	if (!sensorhub->push_data[sensor_num].indio_dev)
+		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);
+
+/**
+ * cros_ec_sensorhub_ring_fifo_enable - Enable or disable interrupt generation
+ *  for FIFO events.
+ * @sensorhub : Sensor Hub object
+ * @on : true when events are requested.
+ *
+ * To be called before sleeping or when noone is listening.
+ * Return: 0 on success.
+ */
+int cros_ec_sensorhub_ring_fifo_enable(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);
+	mutex_unlock(&sensorhub->cmd_lock);
+
+	/* We expect to receive a payload of 4 bytes, ignore. */
+	if (ret > 0)
+		ret = 0;
+
+	return ret;
+}
+
+/**
+ * cros_ec_ring_process_event - process one EC FIFO event
+ *
+ * @sensorhub: Sensorhub object.
+ * @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')
+ *
+ * Process one EC event, add it in the ring if necessary.
+ *
+ * Return: true if out event has been populated.
+ */
+static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
+				const struct cros_ec_fifo_info *fifo_info,
+				const ktime_t fifo_timestamp,
+				ktime_t *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;
+	ktime_t 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 if there are lost vectors. */
+	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));
+
+		/*
+		 * Update collection time, will not be as precise as the
+		 * non-error case.
+		 */
+		fifo_timestamp = cros_ec_get_time_ns();
+	} else {
+		fifo_timestamp = sensorhub->fifo_timestamp[
+			CROS_EC_SENSOR_NEW_TS];
+	}
+
+	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;
+	}
+
+	/* Copy elements in the main fifo */
+	current_timestamp = sensorhub->fifo_timestamp[CROS_EC_SENSOR_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;
+		}
+		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;
+		}
+		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) {
+		int total_lost = fifo_info->info.total_lost;
+
+		if (total_lost) {
+			int lost = fifo_info->lost[i];
+
+			if (lost) {
+				dev_warn_ratelimited(sensorhub->dev,
+						     "Sensor %d: lost: %d out of %d\n",
+						     i, lost, 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[CROS_EC_SENSOR_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[CROS_EC_SENSOR_NEW_TS] =
+		ec_dev->last_event_time;
+	cros_ec_sensorhub_ring_handler(sensorhub);
+
+	return NOTIFY_OK;
+}
+
+/**
+ * cros_ec_sensorhub_ring_add - Add/Remove the fifo functionality if the EC
+ *  supports it.
+ *
+ * @sensorhub : Sensor Hub object
+ *
+ * Return: 0 on success.
+ */
+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[CROS_EC_SENSOR_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. */
+	return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true);
+}
+
+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_enable(sensorhub, false);
+	blocking_notifier_chain_unregister(&ec_dev->event_notifier,
+					   &sensorhub->notifier);
+
+	return 0;
+}
diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
index 2b5a4d81f65f..8f9bf9a70701 100644
--- a/include/linux/platform_data/cros_ec_sensorhub.h
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -8,6 +8,9 @@
 #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
 #define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
 
+#include <linux/ktime.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
 #include <linux/platform_data/cros_ec_commands.h>
 
 /* Maximal number of sensors supported by the EC. */
@@ -27,15 +30,94 @@ struct cros_ec_sensor_platform {
 	u8 sensor_num;
 };
 
+struct iio_dev;
+
+/**
+ * typedef cros_ec_sensorhub_push_data_cb_t - Callback function to send datum
+ *     to specific sensors
+ *
+ * @indio_dev: The IIO device that will process the sample.
+ * @data: vector array of the ring sample.
+ * @timestamp: Timestamp in host timespace when the sample was acquired by
+ *             the EC.
+ */
+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 {
+	CROS_EC_SENSOR_LAST_TS,
+	CROS_EC_SENSOR_NEW_TS,
+	CROS_EC_SENSOR_ALL_TS
+};
+
+struct __ec_todo_packed cros_ec_fifo_info {
+	struct ec_response_motion_sense_fifo_info info;
+	u16    lost[CROS_EC_SENSOR_MAX];
+};
+
+struct cros_ec_sensors_ring_sample {
+	u8  sensor_id;
+	u8  flag;
+	s16 vector[3];
+	s64 timestamp;
+} __packed;
+
 /**
  * struct cros_ec_sensorhub - Sensor Hub device data.
  *
+ * @dev:          Device object, mostly used for logging.
  * @ec:           Embedded Controller where the hub is located.
  * @sensor_pdev:  Array of platform_device, one per sensor.
+ * @msg: Structure to send FIFO requests.
+ * @params: pointer to parameters in msg.
+ * @resp: pointer to responses in msg.
+ * @cmd_lock : lock for sending msg.
+ * @notifier: Notifier to kick the FIFO interrupt.
+ * @ring: Preprocessed ring to store events.
+ * @timestamp: array for event timestamp and spreading.
+ * @fifo_info: copy of FIFO information coming from the EC.
+ * @fifo_size: size of the ring.
+ * @pushdata: array of callback to send datums to iio sensor object.
  */
 struct cros_ec_sensorhub {
+	struct device *dev;
 	struct cros_ec_dev *ec;
 	struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
+
+	struct cros_ec_command *msg;
+	struct ec_params_motion_sense *params;
+	struct ec_response_motion_sense *resp;
+	struct mutex cmd_lock;
+
+	struct notifier_block notifier;
+
+	struct cros_ec_sensors_ring_sample *ring;
+
+	ktime_t fifo_timestamp[CROS_EC_SENSOR_ALL_TS];
+	struct cros_ec_fifo_info fifo_info;
+	int    fifo_size;
+
+	struct cros_ec_sensorhub_sensor_push_data push_data[
+		CROS_EC_SENSOR_PDEV_MAX];
 };
 
+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);
+
+void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub *sensorhub,
+					    u8 sensor_num);
+
+int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub);
+int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensorhub);
+int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
+				       bool on);
+
 #endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 10/17] platform: chrome: sensorhub: Add code to spread timestmap
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (8 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 12:57   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 11/17] platform: chrome: sensorhub: Add median filter Gwendal Grignou
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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>
---
Changes in v4:
- Check patch with --strict option
    Alignement
No changes in v3.
Changes in v2:
- Use CROS_EC_SENSOR_LAST_TS instead of LAST_TS to avoid name colisions.

 .../platform/chrome/cros_ec_sensorhub_ring.c  | 98 ++++++++++++++++++-
 1 file changed, 95 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
index f091f2a4ccfe..b22a71406742 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -187,6 +187,96 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
 	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
  *
@@ -322,9 +412,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[CROS_EC_SENSOR_LAST_TS] = current_timestamp;
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 11/17] platform: chrome: sensorhub: Add median filter
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (9 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 10/17] platform: chrome: sensorhub: Add code to spread timestmap Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 13:07   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 12/17] iio: cros_ec: Move function description to .c file Gwendal Grignou
                   ` (5 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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>
---
Changes in v4:
- Keep defining cros_ec_sensorhub in kernel-doc format
- Check patch with --strict option
    Use BIT()
    Add spaces around '-'
    Alignement
Changes in v3:
- Fix doxygen code.
Changes in v2:
- Move some #define in .c to prevent name collisions.
- Add proper doxygen comments.
- Use /* instead of //

 .../platform/chrome/cros_ec_sensorhub_ring.c  | 505 +++++++++++++++++-
 .../linux/platform_data/cros_ec_sensorhub.h   |  75 +++
 2 files changed, 562 insertions(+), 18 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
index b22a71406742..8960081caeba 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -18,6 +18,21 @@
 #include <linux/sort.h>
 #include <linux/slab.h>
 
+/* Precision of fixed point for the m values from the filter */
+#define M_PRECISION BIT(23)
+
+/* 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
+
+/* To measure by how much the filter is overshooting, if it happens. */
+#define FUTURE_TS_ANALYTICS_COUNT_MAX 100
+
 static inline int cros_sensorhub_send_sample(
 		struct cros_ec_sensorhub *sensorhub,
 		struct cros_ec_sensors_ring_sample *sample)
@@ -91,9 +106,11 @@ EXPORT_SYMBOL_GPL(cros_ec_sensorhub_unregister_push_data);
 int cros_ec_sensorhub_ring_fifo_enable(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;
 
@@ -110,6 +127,231 @@ int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
 	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 - Update filter history.
+ *
+ * @state: Filter information.
+ * @b: IRQ timestamp, EC timebase (us)
+ * @c: IRQ timestamp, AP timebase (ns)
+ *
+ * Given a new IRQ timestamp pair (EC and AP timebases), add it to the filter
+ * history.
+ */
+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 < CROS_EC_SENSORHUB_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
+ *
+ * @state: filter information.
+ * @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
+ *
+ * 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
+ *
+ * Return: timestamp in AP timebase (ns)
+ */
+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
  *
@@ -141,32 +383,54 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
 		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;
+		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;
 		/*
-		 * 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.
 		 */
 		return true;
 	}
+
 	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
 		/* If we just have a timestamp, skip this entry. */
 		return false;
@@ -174,7 +438,22 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
 	/* 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;
@@ -184,13 +463,194 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
 	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;
+			}
+
+			/*
+			 * 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) {
+				cros_sensorhub_send_sample(sensorhub,
+							   batch_start);
+				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 = 1;
+				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
@@ -208,7 +668,8 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
  * 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,
+static void cros_ec_ring_spread_add_legacy(
+				struct cros_ec_sensorhub *sensorhub,
 				unsigned long sensor_mask,
 				s64 current_timestamp,
 				struct cros_ec_sensors_ring_sample *last_out)
@@ -277,7 +738,7 @@ static void cros_ec_ring_spread_add(struct cros_ec_sensorhub *sensorhub,
 		cros_sensorhub_send_sample(sensorhub, out);
 }
 
-/*
+/**
  * cros_ec_sensorhub_ring_handler - the trigger handler function
  *
  * @sensorhub: device information.
@@ -394,7 +855,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. */
@@ -408,6 +870,7 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
 				dev_warn_ratelimited(sensorhub->dev,
 						     "Sensor %d: lost: %d out of %d\n",
 						     i, lost, total_lost);
+				sensorhub->last_batch_len[i] = 0;
 			}
 		}
 	}
@@ -415,8 +878,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[CROS_EC_SENSOR_LAST_TS] = current_timestamp;
@@ -489,6 +955,9 @@ int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
 	sensorhub->fifo_timestamp[CROS_EC_SENSOR_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 8f9bf9a70701..e9da92fb0bea 100644
--- a/include/linux/platform_data/cros_ec_sensorhub.h
+++ b/include/linux/platform_data/cros_ec_sensorhub.h
@@ -68,7 +68,44 @@ 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;
+};
+
+/* Length of the filter, how long to remember entries for */
+#define CROS_EC_SENSORHUB_TS_HISTORY_SIZE 64
+
 /**
+ * struct cros_ec_sensors_ts_filter_state - Timestamp filetr state.
+ *
+ * @x_offset: x is EC interrupt time. x_offset its last value.
+ * @y_offset: y is the difference between AP and EC time, y_offset its last
+ *            value.
+ * @x_history: The past history of x, relative to x_offset.
+ * @y_history: The past history of y, relative to y_offset.
+ * @m_history: rate between y and x.
+ * @history_len: Amount of valid historic data in the arrays.
+ * @temp_buf: Temporary buffer used when updating the filter.
+ * @median_m: median value of m_history
+ * @median_error: final error to apply to AP interrupt timestamp to get the
+ *                "true timestamp" the event occurred.
+ */
+struct cros_ec_sensors_ts_filter_state {
+	s64 x_offset, y_offset;
+	s64 x_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
+	s64 y_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
+	s64 m_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
+	int history_len;
+
+	s64 temp_buf[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
+
+	s64 median_m;
+	s64 median_error;
+};
+
+/*
  * struct cros_ec_sensorhub - Sensor Hub device data.
  *
  * @dev:          Device object, mostly used for logging.
@@ -83,6 +120,28 @@ struct cros_ec_sensors_ring_sample {
  * @timestamp: array for event timestamp and spreading.
  * @fifo_info: copy of FIFO information coming from the EC.
  * @fifo_size: size of the ring.
+ *
+ * @penultimate_batch_timestamp: array of last but one batch timestamps.
+ *  Used for timestamp spreading calculations when a batch shows up.
+ * @penultimate_batch_len: array of last but one batch length.
+ * @last_batch_timestamp: last batch timestamp array.
+ * @last_batch_len: last batch length array.
+ * @newest_sensor_event: last sensor timestamp.
+ * @overflow_a: for handling timestamp overflow for a time (sensor events)
+ * @overflow_b: for handling timestamp overflow for b time (ec interrutps)
+ * @filter: medium fileter structure.
+ * @tight_timestamps: Set to truen when EC support tight timestamping:
+ *  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.
+ *
+ * @future_timestamp_count : 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.
+ * @future_timestamp_total_ns: Total amount of time shaved.
+ *
  * @pushdata: array of callback to send datums to iio sensor object.
  */
 struct cros_ec_sensorhub {
@@ -103,6 +162,22 @@ struct cros_ec_sensorhub {
 	struct cros_ec_fifo_info fifo_info;
 	int    fifo_size;
 
+	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;
+
+	bool tight_timestamps;
+
+	s32 future_timestamp_count;
+	s64 future_timestamp_total_ns;
+
 	struct cros_ec_sensorhub_sensor_push_data push_data[
 		CROS_EC_SENSOR_PDEV_MAX];
 };
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 12/17] iio: cros_ec: Move function description to .c file
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (10 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 11/17] platform: chrome: sensorhub: Add median filter Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 13:08   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 13/17] iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO Gwendal Grignou
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, jic23, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

To prevent comment rot, move function description to
cros_ec_sensors_core.c.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
No changes in v4.
Changes in v3:
  fix spelling.
New in v2.

 .../cros_ec_sensors/cros_ec_sensors_core.c    | 69 ++++++++++++++++
 .../linux/iio/common/cros_ec_sensors_core.h   | 80 -------------------
 2 files changed, 69 insertions(+), 80 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 81a7f692de2f..b47da497a3c3 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
@@ -83,6 +83,14 @@ static void get_default_min_max_freq(enum motionsensor_type type,
 	}
 }
 
+/**
+ * cros_ec_sensors_core_init() - basic initialization of the core structure
+ * @pdev:		platform device created for the sensors
+ * @indio_dev:		iio device structure of the device
+ * @physical_device:	true if the device refers to a physical device
+ *
+ * Return: 0 on success, -errno on failure.
+ */
 int cros_ec_sensors_core_init(struct platform_device *pdev,
 			      struct iio_dev *indio_dev,
 			      bool physical_device)
@@ -160,6 +168,16 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
 
+/**
+ * cros_ec_motion_send_host_cmd() - send motion sense host command
+ * @state:		pointer to state information for device
+ * @opt_length:	optional length to reduce the response size, useful on the data
+ *		path. Otherwise, the maximal allowed response size is used
+ *
+ * When called, the sub-command is assumed to be set in param->cmd.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
 int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
 				 u16 opt_length)
 {
@@ -422,6 +440,14 @@ int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
 
+/**
+ * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
+ * @indio_dev:	pointer to IIO device
+ * @scan_mask:	bitmap of the sensor indices to scan
+ * @data:	location to store data
+ *
+ * Return: 0 on success, -errno on failure.
+ */
 int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
 			     unsigned long scan_mask, s16 *data)
 {
@@ -446,6 +472,18 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
 
+/**
+ * cros_ec_sensors_capture() - the trigger handler function
+ * @irq:	the interrupt number.
+ * @p:		a pointer to the poll function.
+ *
+ * On a trigger event occurring, if the pollfunc is attached then this
+ * handler is called as a threaded interrupt (and hence may sleep). It
+ * is responsible for grabbing data from the device and pushing it into
+ * the associated buffer.
+ *
+ * Return: IRQ_HANDLED
+ */
 irqreturn_t cros_ec_sensors_capture(int irq, void *p)
 {
 	struct iio_poll_func *pf = p;
@@ -481,6 +519,16 @@ irqreturn_t cros_ec_sensors_capture(int irq, void *p)
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
 
+/**
+ * cros_ec_sensors_core_read() - function to request a value from the sensor
+ * @st:		pointer to state information for device
+ * @chan:	channel specification structure table
+ * @val:	will contain one element making up the returned value
+ * @val2:	will contain another element making up the returned value
+ * @mask:	specifies which values to be requested
+ *
+ * Return:	the type of value returned by the device
+ */
 int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
 			  struct iio_chan_spec const *chan,
 			  int *val, int *val2, long mask)
@@ -521,6 +569,17 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
 
+/**
+ * cros_ec_sensors_core_read_avail() - get available values
+ * @indio_dev:		pointer to state information for device
+ * @chan:	channel specification structure table
+ * @vals:	list of available values
+ * @type:	type of data returned
+ * @length:	number of data returned in the array
+ * @mask:	specifies which values to be requested
+ *
+ * Return:	an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
+ */
 int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
 				    struct iio_chan_spec const *chan,
 				    const int **vals,
@@ -542,6 +601,16 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read_avail);
 
+/**
+ * cros_ec_sensors_core_write() - function to write a value to the sensor
+ * @st:		pointer to state information for device
+ * @chan:	channel specification structure table
+ * @val:	first part of value to write
+ * @val2:	second part of value to write
+ * @mask:	specifies which values to write
+ *
+ * Return:	the type of value returned by the device
+ */
 int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
 			       struct iio_chan_spec const *chan,
 			       int val, int val2, long mask)
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index bb331e6356a9..0af918978f97 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -79,95 +79,25 @@ struct cros_ec_sensors_core_state {
 	int frequencies[3];
 };
 
-/**
- * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory
- * @indio_dev:	pointer to IIO device
- * @scan_mask:	bitmap of the sensor indices to scan
- * @data:	location to store data
- *
- * This is the safe function for reading the EC data. It guarantees that the
- * data sampled was not modified by the EC while being read.
- *
- * Return: 0 on success, -errno on failure.
- */
 int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
 			     s16 *data);
 
-/**
- * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
- * @indio_dev:	pointer to IIO device
- * @scan_mask:	bitmap of the sensor indices to scan
- * @data:	location to store data
- *
- * Return: 0 on success, -errno on failure.
- */
 int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
 			     s16 *data);
 
 struct platform_device;
-/**
- * cros_ec_sensors_core_init() - basic initialization of the core structure
- * @pdev:		platform device created for the sensors
- * @indio_dev:		iio device structure of the device
- * @physical_device:	true if the device refers to a physical device
- *
- * Return: 0 on success, -errno on failure.
- */
 int cros_ec_sensors_core_init(struct platform_device *pdev,
 			      struct iio_dev *indio_dev, bool physical_device);
 
-/**
- * cros_ec_sensors_capture() - the trigger handler function
- * @irq:	the interrupt number.
- * @p:		a pointer to the poll function.
- *
- * On a trigger event occurring, if the pollfunc is attached then this
- * handler is called as a threaded interrupt (and hence may sleep). It
- * is responsible for grabbing data from the device and pushing it into
- * the associated buffer.
- *
- * Return: IRQ_HANDLED
- */
 irqreturn_t cros_ec_sensors_capture(int irq, void *p);
 
-/**
- * cros_ec_motion_send_host_cmd() - send motion sense host command
- * @st:		pointer to state information for device
- * @opt_length:	optional length to reduce the response size, useful on the data
- *		path. Otherwise, the maximal allowed response size is used
- *
- * When called, the sub-command is assumed to be set in param->cmd.
- *
- * Return: 0 on success, -errno on failure.
- */
 int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
 				 u16 opt_length);
 
-/**
- * cros_ec_sensors_core_read() - function to request a value from the sensor
- * @st:		pointer to state information for device
- * @chan:	channel specification structure table
- * @val:	will contain one element making up the returned value
- * @val2:	will contain another element making up the returned value
- * @mask:	specifies which values to be requested
- *
- * Return:	the type of value returned by the device
- */
 int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
 			      struct iio_chan_spec const *chan,
 			      int *val, int *val2, long mask);
 
-/**
- * cros_ec_sensors_core_read_avail() - get available values
- * @indio_dev:		pointer to state information for device
- * @chan:	channel specification structure table
- * @vals:	list of available values
- * @type:	type of data returned
- * @length:	number of data returned in the array
- * @mask:	specifies which values to be requested
- *
- * Return:	an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
- */
 int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
 				    struct iio_chan_spec const *chan,
 				    const int **vals,
@@ -175,16 +105,6 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
 				    int *length,
 				    long mask);
 
-/**
- * cros_ec_sensors_core_write() - function to write a value to the sensor
- * @st:		pointer to state information for device
- * @chan:	channel specification structure table
- * @val:	first part of value to write
- * @val2:	second part of value to write
- * @mask:	specifies which values to write
- *
- * Return:	the type of value returned by the device
- */
 int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
 			       struct iio_chan_spec const *chan,
 			       int val, int val2, long mask);
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 13/17] iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (11 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 12/17] iio: cros_ec: Move function description to .c file Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 13:17   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 14/17] iio: cros_ec: Remove pm function Gwendal Grignou
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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, each IIO device registers a callback, to put
samples in the buffer when they arrives from the FIFO.
We can still use a trigger to collect samples, but there may be some
duplications in the buffer: EC has a single FIFO, so once one sensor is
using it, all sensors event will be in the FIFO.
To be sure events generated by cros_ec_sensorhub or the trigger uses the
same time domain, current_timestamp_clock must be set to "boottime".

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>
---
Changes in v4:
- Fix a logic error when the sensor is not "physical", for instance lig
  angle: core_init() would return !0 even if there was no error.
- Check patch with --strict option
    Use sizeof(*obj) instead of sizeof(struct ...obj)
    Alignement
Change in v3:
- Remove double line
- Fix indentation
- Add code to support iio clock_id setting. Optimized for
  CLOCK_BOOTTIME.
Change in v2 from "Use triggered buffer only when EC does not support
FIFO":
- Keep trigger all the time.
- Add  devm_add_action to cleanup callback registration.
- EC that "reports" legacy sensors do not have FIFO.
- Use iiio_is_buffer_enabled instead of checking the scan_mask
  before sending samples to buffer.
- Add empty lines for visibility.

 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  |  9 +-
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 85 ++++++++++++++++++-
 drivers/iio/light/cros_ec_light_prox.c        |  9 +-
 drivers/iio/pressure/cros_ec_baro.c           |  9 +-
 .../linux/iio/common/cros_ec_sensors_core.h   | 10 ++-
 7 files changed, 103 insertions(+), 29 deletions(-)

diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
index 65f85faf6f31..b9f651e4ce99 100644
--- a/drivers/iio/accel/cros_ec_accel_legacy.c
+++ b/drivers/iio/accel/cros_ec_accel_legacy.c
@@ -171,7 +171,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, NULL);
 	if (ret)
 		return ret;
 
@@ -191,11 +192,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 7dce04473467..62a0dd970988 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -231,7 +231,9 @@ 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;
 
@@ -293,11 +295,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 b47da497a3c3..904cd26dd31f 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,17 +84,78 @@ 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;
+	s64 delta;
+	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: it is needed if the ODR is
+	 * set but the buffer is not enabled yet.
+	 */
+	if (!iio_buffer_enabled(indio_dev))
+		return 0;
+
+	out = (s16 *)st->samples;
+	for_each_set_bit(i,
+			 indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		*out = data[i];
+		out++;
+	}
+
+	if (iio_device_get_clock(indio_dev) != CLOCK_BOOTTIME)
+		delta = iio_get_time_ns(indio_dev) - cros_ec_get_time_ns();
+	else
+		delta = 0;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
+					   timestamp + delta);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_push_data);
+
+static void cros_ec_sensors_core_clean(void *arg)
+{
+	struct platform_device *pdev = (struct platform_device *)arg;
+	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);
+}
+
 /**
  * cros_ec_sensors_core_init() - basic initialization of the core structure
  * @pdev:		platform device created for the sensors
  * @indio_dev:		iio device structure of the device
  * @physical_device:	true if the device refers to a physical device
+ * @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)
+			      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);
@@ -132,8 +194,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);
@@ -162,6 +222,25 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 			state->frequencies[2] =
 			    state->resp->info_3.max_frequency;
 		}
+
+		ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+						      trigger_capture, NULL);
+		if (ret)
+			return ret;
+
+		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+			ret = cros_ec_sensorhub_register_push_data(sensor_hub,
+						sensor_platform->sensor_num,
+						indio_dev, push_data);
+			if (ret)
+				return ret;
+
+			ret = devm_add_action_or_reset(dev,
+						cros_ec_sensors_core_clean,
+						pdev);
+			if (ret)
+				return ret;
+		}
 	}
 
 	return 0;
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index d85a391e50c5..698b2ee81ebf 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -178,7 +178,9 @@ 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;
 
@@ -237,11 +239,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 2354302375de..e1c86b22676c 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -134,7 +134,9 @@ 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 +182,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 0af918978f97..b8f573ca9dcc 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,8 @@ 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
@@ -87,9 +90,14 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
 
 struct platform_device;
 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);
 
 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);
 
 int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
 				 u16 opt_length);
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 14/17] iio: cros_ec: Remove pm function
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (12 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 13/17] iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 13:18   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 15/17] iio: cros_ec: Expose hwfifo_timeout Gwendal Grignou
                   ` (2 subsequent siblings)
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, jic23, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, Gwendal Grignou

Since cros_ec_sensorhub is shutting down the FIFO when the device
suspends, no need to slow down the EC sampling period rate.
It was necesseary to do that before command CMD_FIFO_INT_ENABLE was
introduced, but now all supported chromebooks have it.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
No changes in v4, v3.
New in v2.

 .../cros_ec_sensors/cros_ec_lid_angle.c       |  1 -
 .../common/cros_ec_sensors/cros_ec_sensors.c  |  1 -
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 47 -------------------
 drivers/iio/light/cros_ec_light_prox.c        |  1 -
 .../linux/iio/common/cros_ec_sensors_core.h   |  5 --
 5 files changed, 55 deletions(-)

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 e30a59fcf0f9..af801e203623 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
@@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids);
 static struct platform_driver cros_ec_lid_angle_platform_driver = {
 	.driver = {
 		.name	= DRV_NAME,
-		.pm	= &cros_ec_sensors_pm_ops,
 	},
 	.probe		= cros_ec_lid_angle_probe,
 	.id_table	= cros_ec_lid_angle_ids,
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 62a0dd970988..9d0b8ad7a0a5 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -315,7 +315,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
 static struct platform_driver cros_ec_sensors_platform_driver = {
 	.driver = {
 		.name	= "cros-ec-sensors",
-		.pm	= &cros_ec_sensors_pm_ops,
 	},
 	.probe		= cros_ec_sensors_probe,
 	.id_table	= cros_ec_sensors_ids,
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 904cd26dd31f..879b69527cae 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
@@ -723,52 +723,5 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
 }
 EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
 
-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);
-
-	if (st->curr_sampl_freq == 0)
-		return 0;
-
-	/*
-	 * If the sensors are sampled at high frequency, we will not be able to
-	 * 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);
-	}
-	return 0;
-}
-
-static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
-{
-	struct iio_dev *indio_dev = dev_get_drvdata(dev);
-	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
-
-	if (st->curr_sampl_freq == 0)
-		return;
-
-	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
-		mutex_lock(&st->cmd_lock);
-		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
-		st->param.ec_rate.data = st->curr_sampl_freq;
-		cros_ec_motion_send_host_cmd(st, 0);
-		mutex_unlock(&st->cmd_lock);
-	}
-}
-
-const struct dev_pm_ops cros_ec_sensors_pm_ops = {
-#ifdef CONFIG_PM_SLEEP
-	.prepare = cros_ec_sensors_prepare,
-	.complete = cros_ec_sensors_complete
-#endif
-};
-EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
-
 MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index 698b2ee81ebf..ccdc6d8958c6 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -256,7 +256,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
 static struct platform_driver cros_ec_light_prox_platform_driver = {
 	.driver = {
 		.name	= "cros-ec-light-prox",
-		.pm	= &cros_ec_sensors_pm_ops,
 	},
 	.probe		= cros_ec_light_prox_probe,
 	.id_table	= cros_ec_light_prox_ids,
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index b8f573ca9dcc..96ea4551945e 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -30,9 +30,6 @@ enum {
  */
 #define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
 
-/* Minimum sampling period to use when device is suspending */
-#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
-
 typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
 
 /**
@@ -117,8 +114,6 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
 			       struct iio_chan_spec const *chan,
 			       int val, int val2, long mask);
 
-extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
-
 /* List of extended channel specification for all sensors */
 extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
 
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

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

Expose EC minimal interrupt period through buffer/hwfifo_timeout:
- Maximal timeout is limited to 65s.
- When timeout for all sensors is set to 0, EC will not send events,
  even if the sensor sampling rate is greater than 0.

Rename frequency to sampling_frequency to match IIO ABI.

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
---
Changes in v4:
- Check patch with --strict option
    Alignement
No changes in v3.
Changes in v2:
- Register fifo_attributes in sensors drivers that previously advertise
  that feature.

 .../common/cros_ec_sensors/cros_ec_sensors.c  |  4 +-
 .../cros_ec_sensors/cros_ec_sensors_core.c    | 95 ++++++++++++++-----
 drivers/iio/light/cros_ec_light_prox.c        |  5 +-
 drivers/iio/pressure/cros_ec_baro.c           |  5 +-
 .../linux/iio/common/cros_ec_sensors_core.h   |  4 +-
 5 files changed, 82 insertions(+), 30 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 9d0b8ad7a0a5..6f511f9067d9 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -237,6 +237,8 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
+
 	indio_dev->info = &ec_sensors_info;
 	state = iio_priv(indio_dev);
 	for (channel = state->channels, i = CROS_EC_SENSOR_X;
@@ -248,7 +250,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 879b69527cae..62dc1e4aa7a8 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,77 @@ 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;
+
+	if (rate > U16_MAX)
+		rate = U16_MAX;
+
+	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);
+	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);
+
+const struct attribute *cros_ec_sensor_fifo_attributes[] = {
+	&iio_dev_attr_hwfifo_timeout.dev_attr.attr,
+	NULL,
+};
+EXPORT_SYMBOL_GPL(cros_ec_sensor_fifo_attributes);
+
 int cros_ec_sensors_push_data(struct iio_dev *indio_dev,
 			      s16 *data,
 			      s64 timestamp)
@@ -616,18 +688,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;
@@ -697,7 +757,7 @@ 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;
 
@@ -706,15 +766,6 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
 
 		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;
 		break;
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index ccdc6d8958c6..863d01994aae 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -184,6 +184,8 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
+
 	indio_dev->info = &cros_ec_light_prox_info;
 	state = iio_priv(indio_dev);
 	state->core.type = state->core.resp->info.type;
@@ -192,8 +194,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 e1c86b22676c..0dee943f955e 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -140,6 +140,8 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
+
 	indio_dev->info = &cros_ec_baro_info;
 	state = iio_priv(indio_dev);
 	state->core.type = state->core.resp->info.type;
@@ -149,8 +151,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;
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index 96ea4551945e..5b0acc14c891 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -50,7 +50,6 @@ 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
  */
 struct cros_ec_sensors_core_state {
 	struct cros_ec_device *ec;
@@ -73,8 +72,6 @@ struct cros_ec_sensors_core_state {
 	int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
 				    unsigned long scan_mask, s16 *data);
 
-	int curr_sampl_freq;
-
 	/* Table of known available frequencies : 0, Min and Max in mHz */
 	int frequencies[3];
 };
@@ -116,5 +113,6 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
 
 /* List of extended channel specification for all sensors */
 extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
+extern const struct attribute *cros_ec_sensor_fifo_attributes[];
 
 #endif  /* __CROS_EC_SENSORS_CORE_H */
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 16/17] iio: cros_ec: Report hwfifo_watermark_max
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (14 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 15/17] iio: cros_ec: Expose hwfifo_timeout Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 13:22   ` Jonathan Cameron
  2019-11-05 22:26 ` [PATCH v4 17/17] iio: cros_ec: Use Hertz as unit for sampling frequency Gwendal Grignou
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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>
---
Changes in v4:
- Check patch with --strict option
    Alignement
No changes in v3.
Changes in v2:
- Remove double lines, add line before return for visibility.

 .../cros_ec_sensors/cros_ec_sensors_core.c    | 33 +++++++++++++++++--
 .../linux/iio/common/cros_ec_sensors_core.h   |  3 ++
 2 files changed, 34 insertions(+), 2 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 62dc1e4aa7a8..4169c6c055d8 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:
@@ -150,8 +163,21 @@ 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);
+
 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,
 };
 EXPORT_SYMBOL_GPL(cros_ec_sensor_fifo_attributes);
@@ -287,12 +313,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;
 		}
 
 		ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
index 5b0acc14c891..bc26ae2e3272 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -50,6 +50,7 @@ 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
+ * @fifo_max_event_count:	Size of the EC sensor FIFO
  */
 struct cros_ec_sensors_core_state {
 	struct cros_ec_device *ec;
@@ -72,6 +73,8 @@ struct cros_ec_sensors_core_state {
 	int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
 				    unsigned long scan_mask, s16 *data);
 
+	u32 fifo_max_event_count;
+
 	/* Table of known available frequencies : 0, Min and Max in mHz */
 	int frequencies[3];
 };
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* [PATCH v4 17/17] iio: cros_ec: Use Hertz as unit for sampling frequency
  2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
                   ` (15 preceding siblings ...)
  2019-11-05 22:26 ` [PATCH v4 16/17] iio: cros_ec: Report hwfifo_watermark_max Gwendal Grignou
@ 2019-11-05 22:26 ` Gwendal Grignou
  2019-11-10 13:24   ` Jonathan Cameron
  16 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-05 22:26 UTC (permalink / raw)
  To: briannorris, 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>
---
Changes in v4:
- Check patch with --strict option
    Alignement
No changes in v3.
No changes in v2.

 .../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 4169c6c055d8..f91685119cb0 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
@@ -261,6 +261,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
 	struct cros_ec_dev *ec = sensor_hub->ec;
 	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
 	u32 ver_mask;
+	int frequencies[ARRAY_SIZE(state->frequencies) / 2] = { 0 };
 	int ret, i;
 
 	platform_set_drvdata(pdev, indio_dev);
@@ -309,20 +310,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;
+		}
 
 		ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
 						      trigger_capture, NULL);
@@ -713,7 +716,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:
@@ -725,8 +728,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;
@@ -761,7 +766,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;
 	}
 
@@ -783,12 +788,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 bc26ae2e3272..7bc961defa87 100644
--- a/include/linux/iio/common/cros_ec_sensors_core.h
+++ b/include/linux/iio/common/cros_ec_sensors_core.h
@@ -51,6 +51,8 @@ typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
  *				is always 8-byte aligned.
  * @read_ec_sensors_data:	function used for accessing sensors values
  * @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;
@@ -74,9 +76,7 @@ struct cros_ec_sensors_core_state {
 				    unsigned long scan_mask, s16 *data);
 
 	u32 fifo_max_event_count;
-
-	/* Table of known available frequencies : 0, Min and Max in mHz */
-	int frequencies[3];
+	int frequencies[6];
 };
 
 int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


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

* Re: [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support
  2019-11-05 22:26 ` [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
@ 2019-11-06 21:13   ` Gwendal Grignou
  2019-11-10 12:54     ` Jonathan Cameron
  0 siblings, 1 reply; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-06 21:13 UTC (permalink / raw)
  To: Brian Norris, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones,
	Benson Leung, Enric Balletbo i Serra, Doug Anderson,
	Guenter Roeck, Fabien Lahoudere
  Cc: linux-kernel, linux-iio

On Tue, Nov 5, 2019 at 2:27 PM 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>
> ---
> Changes in v4:
> - Keep defining cros_ec_sensorhub in kernel-doc format
> - Fix logic error when checking if sensor index outside array.
> - Check patch with --strict option
>     Use sizeof(*obj) instead of sizeof(struct ...obj)
>     Alignement
>     Use uX instead of uintX_t
>     Use !ptr instead of ptr != NULL
> Changes in v3:
> - Do not use ret !=
> - Simplfy errpr handling by removing a goto
> - Fix doxygen comments
> - Replace suspend/resume entry points with regular driver entry point;
>   There was an issue in the past where the sensor stack was preventing
>   device to suspend, but the proper fix has been implemented in cros_ec
>   code (6ad16b78a039b "platform/chrome: don't report EC_MKBP_EVENT_SENSOR_FIFO as wakeup")
> - Reduce mutex scope by checking return code outside of it.
> Changes in v2:
> - Do not register a .remove routinge in plaform_driver. A
>   devm_action_add is added later patch IIO driver register their
> callback.
> - Remove double lines, add lines before return calls.
> - Handle FLUSH flag from EC.
>
> - Use ktime_t for most timestamp measurements.
> - Add doxygen comments
> - Cleanup timestamp collection when processing FIFO.
> - Rename fifo_toggle to fifo_enable
>
>  drivers/platform/chrome/Makefile              |   3 +-
>  drivers/platform/chrome/cros_ec_sensorhub.c   | 117 +++--
>  .../platform/chrome/cros_ec_sensorhub_ring.c  | 423 ++++++++++++++++++
>  .../linux/platform_data/cros_ec_sensorhub.h   |  82 ++++
>  4 files changed, 592 insertions(+), 33 deletions(-)
>  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c
>
> 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 6a0aa84cf092..0e34ad2903bb 100644
> --- a/drivers/platform/chrome/cros_ec_sensorhub.c
> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> @@ -39,11 +39,9 @@ static int cros_ec_sensorhub_register(struct device *dev,
>         int ret, i, id, sensor_num;
>         struct cros_ec_dev *ec = sensorhub->ec;
>         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 platform_device *pdev;
>         char *name;
> +       struct cros_ec_command *msg = sensorhub->msg;
>
>         sensor_num = cros_ec_get_sensor_count(ec);
>         if (sensor_num < 0) {
> @@ -58,30 +56,21 @@ static int cros_ec_sensorhub_register(struct device *dev,
>                 return -EINVAL;
>         }
>
> -       /* Prepare a message to send INFO command to each sensor. */
> -       msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
> -                     GFP_KERNEL);
> -       if (!msg)
> -               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;
> -       resp = (struct ec_response_motion_sense *)msg->data;
> +       msg->insize = sizeof(struct ec_response_motion_sense);
> +       msg->outsize = sizeof(struct ec_params_motion_sense);
>
>         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:
>                         name = "cros-ec-accel";
>                         break;
> @@ -104,16 +93,16 @@ static int cros_ec_sensorhub_register(struct device *dev,
>                         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;
>                 }
>                 pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
> -               if (IS_ERR(pdev)) {
> -                       ret = IS_ERR(pdev);
> -                       goto error;
> -               }
> +               if (IS_ERR(pdev))
> +                       return IS_ERR(pdev);
> +
>                 sensorhub->sensor_pdev[id++] = pdev;
> -               sensor_type[resp->info.type]++;
> +               sensor_type[sensorhub->resp->info.type]++;
>         }
>
>         if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
> @@ -123,16 +112,13 @@ static int cros_ec_sensorhub_register(struct device *dev,
>                                    EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
>                 pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
>                                                         "cros-ec-lid-angle", 0);
> -               if (IS_ERR(pdev)) {
> -                       ret = IS_ERR(pdev);
> -                       goto error;
> -               }
> +               if (IS_ERR(pdev))
> +                       return IS_ERR(pdev);
> +
>                 sensorhub->sensor_pdev[id++] = pdev;
>         }
>
> -error:
> -       kfree(msg);
> -       return ret;
> +       return 0;
>  }
>
>  static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> @@ -141,13 +127,29 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
>         struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
>         int ret, i;
>         struct platform_device *pdev;
> -       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. */
> @@ -175,6 +177,20 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
>                 }
>         }
>
> +       /*
> +        * 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);
> +               if (ret)
> +                       goto unregister_sensors;
> +               /*
> +                * The msg and its data is not under the control of the ring
> +                * handler.
> +                */
> +       }
> +
>         return 0;
>
>  unregister_sensors:
> @@ -195,9 +211,12 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
>  {
>         struct cros_ec_sensorhub *sensorhub =
>                 platform_get_drvdata(sensorhub_pdev);
> +       struct cros_ec_dev *ec = sensorhub->ec;
>         struct platform_device *pdev;
>         int i;
>
> +       if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> +               cros_ec_sensorhub_ring_remove(sensorhub);
>         for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
>                 pdev = sensorhub->sensor_pdev[i];
>                 if (pdev)
> @@ -207,9 +226,43 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
>         return 0;
>  }
>
> +#if CONFIG_PM_SLEEP
> +/*
> + * 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 cros_ec_ring_suspend(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_enable(sensorhub, false);
> +       return 0;
> +}
> +
> +static int cros_ec_ring_resume(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_enable(sensorhub, true);
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_ring_pm_ops,
> +               cros_ec_ring_suspend,
> +               cros_ec_ring_resume);
> +
>  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,
> 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..f091f2a4ccfe
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> @@ -0,0 +1,423 @@
> +// 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;
> +
> +       if (sample->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
> +               return 0;
> +
> +       return cb(indio_dev, sample->vector, sample->timestamp);
> +}
> +
> +/**
> + * cros_ec_sensorhub_register_push_data - register the callback to the hub.
> + *
> + * @sensorhub : Sensor Hub object
> + * @sensor_num : The sensor the caller is interested in.
> + * @indio_dev : The iio device to use when a sample arrives.
> + * @cb : The callback to call when a sample arrives.
> + *
> + * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
> + * from the EC.
> + *
> + * Return: 0 when callback is registered.
> + */
> +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_PDEV_MAX)
> +               return -EINVAL;
> +       if (!sensorhub->push_data[sensor_num].indio_dev)
I made a logic mistake, it should be:
if (sensorhub->push_data[sensor_num].indio_dev)
[When the slot is already used, return einval.]
> +               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);
> +
> +/**
> + * cros_ec_sensorhub_ring_fifo_enable - Enable or disable interrupt generation
> + *  for FIFO events.
> + * @sensorhub : Sensor Hub object
> + * @on : true when events are requested.
> + *
> + * To be called before sleeping or when noone is listening.
> + * Return: 0 on success.
> + */
> +int cros_ec_sensorhub_ring_fifo_enable(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);
> +       mutex_unlock(&sensorhub->cmd_lock);
> +
> +       /* We expect to receive a payload of 4 bytes, ignore. */
> +       if (ret > 0)
> +               ret = 0;
> +
> +       return ret;
> +}
> +
> +/**
> + * cros_ec_ring_process_event - process one EC FIFO event
> + *
> + * @sensorhub: Sensorhub object.
> + * @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')
> + *
> + * Process one EC event, add it in the ring if necessary.
> + *
> + * Return: true if out event has been populated.
> + */
> +static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
> +                               const struct cros_ec_fifo_info *fifo_info,
> +                               const ktime_t fifo_timestamp,
> +                               ktime_t *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;
> +       ktime_t 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 if there are lost vectors. */
> +       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));
> +
> +               /*
> +                * Update collection time, will not be as precise as the
> +                * non-error case.
> +                */
> +               fifo_timestamp = cros_ec_get_time_ns();
> +       } else {
> +               fifo_timestamp = sensorhub->fifo_timestamp[
> +                       CROS_EC_SENSOR_NEW_TS];
> +       }
> +
> +       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;
> +       }
> +
> +       /* Copy elements in the main fifo */
> +       current_timestamp = sensorhub->fifo_timestamp[CROS_EC_SENSOR_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;
> +               }
> +               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;
> +               }
> +               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) {
> +               int total_lost = fifo_info->info.total_lost;
> +
> +               if (total_lost) {
> +                       int lost = fifo_info->lost[i];
> +
> +                       if (lost) {
> +                               dev_warn_ratelimited(sensorhub->dev,
> +                                                    "Sensor %d: lost: %d out of %d\n",
> +                                                    i, lost, 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[CROS_EC_SENSOR_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[CROS_EC_SENSOR_NEW_TS] =
> +               ec_dev->last_event_time;
> +       cros_ec_sensorhub_ring_handler(sensorhub);
> +
> +       return NOTIFY_OK;
> +}
> +
> +/**
> + * cros_ec_sensorhub_ring_add - Add/Remove the fifo functionality if the EC
> + *  supports it.
> + *
> + * @sensorhub : Sensor Hub object
> + *
> + * Return: 0 on success.
> + */
> +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[CROS_EC_SENSOR_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. */
> +       return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true);
> +}
> +
> +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_enable(sensorhub, false);
> +       blocking_notifier_chain_unregister(&ec_dev->event_notifier,
> +                                          &sensorhub->notifier);
> +
> +       return 0;
> +}
> diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> index 2b5a4d81f65f..8f9bf9a70701 100644
> --- a/include/linux/platform_data/cros_ec_sensorhub.h
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -8,6 +8,9 @@
>  #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
>  #define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
>
> +#include <linux/ktime.h>
> +#include <linux/mutex.h>
> +#include <linux/notifier.h>
>  #include <linux/platform_data/cros_ec_commands.h>
>
>  /* Maximal number of sensors supported by the EC. */
> @@ -27,15 +30,94 @@ struct cros_ec_sensor_platform {
>         u8 sensor_num;
>  };
>
> +struct iio_dev;
> +
> +/**
> + * typedef cros_ec_sensorhub_push_data_cb_t - Callback function to send datum
> + *     to specific sensors
> + *
> + * @indio_dev: The IIO device that will process the sample.
> + * @data: vector array of the ring sample.
> + * @timestamp: Timestamp in host timespace when the sample was acquired by
> + *             the EC.
> + */
> +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 {
> +       CROS_EC_SENSOR_LAST_TS,
> +       CROS_EC_SENSOR_NEW_TS,
> +       CROS_EC_SENSOR_ALL_TS
> +};
> +
> +struct __ec_todo_packed cros_ec_fifo_info {
> +       struct ec_response_motion_sense_fifo_info info;
> +       u16    lost[CROS_EC_SENSOR_MAX];
> +};
> +
> +struct cros_ec_sensors_ring_sample {
> +       u8  sensor_id;
> +       u8  flag;
> +       s16 vector[3];
> +       s64 timestamp;
> +} __packed;
> +
>  /**
>   * struct cros_ec_sensorhub - Sensor Hub device data.
>   *
> + * @dev:          Device object, mostly used for logging.
>   * @ec:           Embedded Controller where the hub is located.
>   * @sensor_pdev:  Array of platform_device, one per sensor.
> + * @msg: Structure to send FIFO requests.
> + * @params: pointer to parameters in msg.
> + * @resp: pointer to responses in msg.
> + * @cmd_lock : lock for sending msg.
> + * @notifier: Notifier to kick the FIFO interrupt.
> + * @ring: Preprocessed ring to store events.
> + * @timestamp: array for event timestamp and spreading.
> + * @fifo_info: copy of FIFO information coming from the EC.
> + * @fifo_size: size of the ring.
> + * @pushdata: array of callback to send datums to iio sensor object.
>   */
>  struct cros_ec_sensorhub {
> +       struct device *dev;
>         struct cros_ec_dev *ec;
>         struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
> +
> +       struct cros_ec_command *msg;
> +       struct ec_params_motion_sense *params;
> +       struct ec_response_motion_sense *resp;
> +       struct mutex cmd_lock;
> +
> +       struct notifier_block notifier;
> +
> +       struct cros_ec_sensors_ring_sample *ring;
> +
> +       ktime_t fifo_timestamp[CROS_EC_SENSOR_ALL_TS];
> +       struct cros_ec_fifo_info fifo_info;
> +       int    fifo_size;
> +
> +       struct cros_ec_sensorhub_sensor_push_data push_data[
> +               CROS_EC_SENSOR_PDEV_MAX];
>  };
>
> +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);
> +
> +void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub *sensorhub,
> +                                           u8 sensor_num);
> +
> +int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub);
> +int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensorhub);
> +int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
> +                                      bool on);
> +
>  #endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
> --
> 2.24.0.rc1.363.gb1bccd3e3d-goog
>

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

* Re: [PATCH v4 01/17] mfd: cros_ec: Add sensor_count and make check_features public
  2019-11-05 22:26 ` [PATCH v4 01/17] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
@ 2019-11-08 22:03   ` Enric Balletbo Serra
  2019-11-11 11:44     ` Lee Jones
  0 siblings, 1 reply; 52+ messages in thread
From: Enric Balletbo Serra @ 2019-11-08 22:03 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: Brian Norris, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones,
	Benson Leung, Enric Balletbo i Serra, Doug Anderson,
	Guenter Roeck, Fabien Lahoudere, linux-kernel, linux-iio

Missatge de Gwendal Grignou <gwendal@chromium.org> del dia dt., 5 de
nov. 2019 a les 23:28:
>
> 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 what the Embedded Controller supports.
>
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

Version 3 was acked and I think we can maintain his ack, so:

Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>

Also,

Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Lee, how you would like to handle this? I think will be safe for
patches 1/2/3 go through the platform-chrome tree without an immutable
branch. Patch 3 thought still needs and Ack from you if is fine.

Thanks,
 Enric


> ---
> Changes in v4:
>   Check patch with --strict option
>     Use sizeof(*obj) instead of sizeof(struct ...obj)
>     Alignement
>     Remove useless ().
> Changes in v3:
>   Fix doxygen comments.
> Changes in v2:
>   Fix spelling in commit message.
>   Cleanup the case where DUMP command is not supported.
>   Move code from mfd to platform/chrome/
>
>  drivers/mfd/cros_ec_dev.c                   |  32 ------
>  drivers/platform/chrome/cros_ec_proto.c     | 117 ++++++++++++++++++++
>  include/linux/platform_data/cros_ec_proto.h |   5 +
>  3 files changed, 122 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
> index 6e6dfd6c1871..a35104e35cb4 100644
> --- a/drivers/mfd/cros_ec_dev.c
> +++ b/drivers/mfd/cros_ec_dev.c
> @@ -112,38 +112,6 @@ 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)
> -{
> -       struct cros_ec_command *msg;
> -       int ret;
> -
> -       if (ec->features[0] == -1U && ec->features[1] == -1U) {
> -               /* features bitmap not read yet */
> -               msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
> -               if (!msg)
> -                       return -ENOMEM;
> -
> -               msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
> -               msg->insize = sizeof(ec->features);
> -
> -               ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
> -               if (ret < 0) {
> -                       dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
> -                                ret, msg->result);
> -                       memset(ec->features, 0, sizeof(ec->features));
> -               } else {
> -                       memcpy(ec->features, msg->data, sizeof(ec->features));
> -               }
> -
> -               dev_dbg(ec->dev, "EC features %08x %08x\n",
> -                       ec->features[0], ec->features[1]);
> -
> -               kfree(msg);
> -       }
> -
> -       return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
> -}
> -
>  static void cros_ec_class_release(struct device *dev)
>  {
>         kfree(to_cros_ec_dev(dev));
> diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
> index 7db58771ec77..b502933e911b 100644
> --- a/drivers/platform/chrome/cros_ec_proto.c
> +++ b/drivers/platform/chrome/cros_ec_proto.c
> @@ -717,3 +717,120 @@ u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
>         return host_event;
>  }
>  EXPORT_SYMBOL(cros_ec_get_host_event);
> +
> +/**
> + * cros_ec_check_features - Test for the presence of EC features
> + *
> + * @ec: EC device, does not have to be connected directly to the AP,
> + *      can be daisy chained through another device.
> + * @feature: One of ec_feature_code bit.
> + *
> + * Call this function to test whether the ChromeOS EC supports a feature.
> + *
> + * Return: 1 if supported, 0 if not
> + */
> +int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
> +{
> +       struct cros_ec_command *msg;
> +       int ret;
> +
> +       if (ec->features[0] == -1U && ec->features[1] == -1U) {
> +               /* features bitmap not read yet */
> +               msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
> +               if (!msg)
> +                       return -ENOMEM;
> +
> +               msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
> +               msg->insize = sizeof(ec->features);
> +
> +               ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
> +               if (ret < 0) {
> +                       dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
> +                                ret, msg->result);
> +                       memset(ec->features, 0, sizeof(ec->features));
> +               } else {
> +                       memcpy(ec->features, msg->data, sizeof(ec->features));
> +               }
> +
> +               dev_dbg(ec->dev, "EC features %08x %08x\n",
> +                       ec->features[0], ec->features[1]);
> +
> +               kfree(msg);
> +       }
> +
> +       return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_check_features);
> +
> +/**
> + * cros_ec_get_sensor_count - Return the number of MEMS sensors supported.
> + *
> + * @ec: EC device, does not have to be connected directly to the AP,
> + *      can be daisy chained through another device.
> + * Return: < 0 in case of error.
> + */
> +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(*msg) + max(sizeof(*params), sizeof(*resp)),
> +                     GFP_KERNEL);
> +       if (!msg)
> +               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;
> +               } else {
> +                       /*
> +                        * EC uses LPC interface and no sensors are presented.
> +                        */
> +                       sensor_count = 0;
> +               }
> +       } else if (sensor_count == -EPROTO) {
> +               /* EC responded, but does not understand DUMP command. */
> +               sensor_count = 0;
> +       }
> +       return sensor_count;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index 0d4e4aaed37a..f3de0662135d 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -12,6 +12,7 @@
>  #include <linux/mutex.h>
>  #include <linux/notifier.h>
>
> +#include <linux/mfd/cros_ec.h>
>  #include <linux/platform_data/cros_ec_commands.h>
>
>  #define CROS_EC_DEV_NAME       "cros_ec"
> @@ -213,4 +214,8 @@ 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);
>
> +int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
> +
> +int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
> +
>  #endif /* __LINUX_CROS_EC_PROTO_H */
> --
> 2.24.0.rc1.363.gb1bccd3e3d-goog
>

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

* Re: [PATCH v4 02/17] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-11-05 22:26 ` [PATCH v4 02/17] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
@ 2019-11-10 12:10   ` Jonathan Cameron
  2019-11-11  9:24     ` Enric Balletbo i Serra
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 12:10 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio

On Tue,  5 Nov 2019 14:26:37 -0800
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>

Random suggestion for a possible cleanup...

Would a devm_platform_device_register_data make sense?  Drops a
fair bit of boilerplate in here.  If its not a common enough
pattern, could use the devm_add_action_or_reset route
to do the same thing.

I would suggest this as a possible future element, but you
have some other issues around that area that this would cleanup
nicely for you.  See inline.


Thanks,

Jonathan



> ---
> Changes in v4:
> - Use platform_device_register_data in children registration.
> - Free registered pdev children at remove time.
> - Remove useless includes
> - Check patch with --strict option
>     Use sizeof(*obj) instead of sizeof(struct ...obj)
>     Alignement
> - Describe cros_ec_sensorhub in kernel-doc format.
> Changes in v3:
> - Fix doxygen comments
> - Fix use of ret |=
> - Remove unncessary goto.
> Changes in v2:
> - Remove unerelated changes.
> - Fix spelling.
> - Use !x instead of x == NULL
> - Use platform_ API directly to register IIO sensors from
>   cros_ec_sensorhub.
> 
>  drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
>  drivers/platform/chrome/Kconfig               |  12 +
>  drivers/platform/chrome/Makefile              |   1 +
>  drivers/platform/chrome/cros_ec_sensorhub.c   | 223 ++++++++++++++++++
>  .../linux/platform_data/cros_ec_sensorhub.h   |  33 +++
>  5 files changed, 270 insertions(+), 1 deletion(-)
>  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..56a25317a6be 100644
> --- a/drivers/platform/chrome/Kconfig
> +++ b/drivers/platform/chrome/Kconfig
> @@ -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 Sensor Hub"
> +	depends on CROS_EC && IIO

Could relax the IIO dependency I think...  Get you more build coverage.

> +	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 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..6a0aa84cf092
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> @@ -0,0 +1,223 @@
> +// 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/module.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_data/cros_ec_sensorhub.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#define DRV_NAME		"cros-ec-sensorhub"
> +
> +static struct platform_device *cros_ec_sensorhub_allocate_single_sensor(
> +		struct device *parent,
> +		char *sensor_name,
> +		int sensor_num)
> +{
> +	struct cros_ec_sensor_platform sensor_platforms = {
> +		.sensor_num = sensor_num,
> +	};
> +
> +	return platform_device_register_data(parent, sensor_name,
> +				PLATFORM_DEVID_AUTO,
> +				&sensor_platforms,
> +				sizeof(sensor_platforms));
> +}
> +
> +static int cros_ec_sensorhub_register(struct device *dev,
> +				      struct cros_ec_sensorhub *sensorhub)

As noted below, I'd be happier if this function did it's own cleanup on
error rather than leaving that for the caller.

> +{
> +	int ret, i, id, sensor_num;
> +	struct cros_ec_dev *ec = sensorhub->ec;
> +	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 platform_device *pdev;
> +	char *name;
> +
> +	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;
> +	}
> +
> +	/* Prepare a message to send INFO command to each sensor. */
> +	msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
> +		      GFP_KERNEL);
> +	if (!msg)
> +		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;
> +	resp = (struct ec_response_motion_sense *)msg->data;
> +
> +	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:
> +			name = "cros-ec-accel";
> +			break;
> +		case MOTIONSENSE_TYPE_BARO:
> +			name = "cros-ec-baro";
> +			break;
> +		case MOTIONSENSE_TYPE_GYRO:
> +			name = "cros-ec-gyro";
> +			break;
> +		case MOTIONSENSE_TYPE_MAG:
> +			name = "cros-ec-mag";
> +			break;
> +		case MOTIONSENSE_TYPE_PROX:
> +			name = "cros-ec-prox";
> +			break;
> +		case MOTIONSENSE_TYPE_LIGHT:
> +			name = "cros-ec-light";
> +			break;
> +		case MOTIONSENSE_TYPE_ACTIVITY:
> +			name = "cros-ec-activity";
> +			break;
> +		default:
> +			dev_warn(dev, "unknown type %d\n", resp->info.type);
> +			continue;
> +		}
> +		pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
> +		if (IS_ERR(pdev)) {
> +			ret = IS_ERR(pdev);
> +			goto error;
> +		}
> +		sensorhub->sensor_pdev[id++] = pdev;
> +		sensor_type[resp->info.type]++;
> +	}
> +
> +	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)) {
> +		pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
> +							"cros-ec-lid-angle", 0);
> +		if (IS_ERR(pdev)) {
> +			ret = IS_ERR(pdev);
> +			goto error;
> +		}
> +		sensorhub->sensor_pdev[id++] = pdev;
> +	}
> +
> +error:
> +	kfree(msg);
> +	return ret;
> +}
> +
> +static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> +{
> +	struct device *dev = &sensorhub_pdev->dev;
> +	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> +	int ret, i;
> +	struct platform_device *pdev;
> +	struct cros_ec_sensorhub *data =
> +		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);

Do we free this anywhere?  Could just use devm_kzalloc to do it
automatically.

> +
> +	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_sensorhub_register(dev, data);
> +		if (ret) {
> +			dev_err(dev, "Register failed %d\n", ret);
> +			goto unregister_sensors;

From a code structure point of view, cros_ec_sensorhub_register
should have done any cleanup necessary if it returns an error.  Hence
we should be fine doing a direct return here (other than the memory
not being freed comment above.

This may seem an overly restrictive request, but that sort of rule
makes code a lot easier to review as we don't have to go look
to see where the error handling occurs and check for multiple paths
etc.  Note that if you use managed functions then there is no
cleanup to do anyway ;)

> +		}
> +	} else {
> +		/*
> +		 * If the device has sensors but does not claim to
> +		 * be a sensor hub, we are in legacy mode.
> +		 */
> +		for (i = 0; i < 2; i++) {
> +			pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
> +						"cros-ec-accel-legacy", i);
> +			if (IS_ERR(pdev)) {
> +				ret = IS_ERR(pdev);
> +				dev_err(dev, "Legacy %d failed %d\n", i, ret);
> +				goto unregister_sensors;
> +			} else {
> +				data->sensor_pdev[i] = pdev;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +
> +unregister_sensors:
> +	/*
> +	 * Given the probe has failed, we need to unregister all the sensors,
> +	 * not jutst the one that did not work: this device will be
> +	 * de-allocated.
> +	 */
> +	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
> +		pdev = data->sensor_pdev[i];
> +		if (pdev)
> +			platform_device_unregister(pdev);
> +	}
> +	return ret;
> +}
> +
> +static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
> +{
> +	struct cros_ec_sensorhub *sensorhub =
> +		platform_get_drvdata(sensorhub_pdev);
> +	struct platform_device *pdev;
> +	int i;
> +
> +	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
> +		pdev = sensorhub->sensor_pdev[i];
> +		if (pdev)
> +			platform_device_unregister(pdev);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver cros_ec_sensorhub_driver = {
> +	.driver = {
> +		.name = DRV_NAME,
> +	},
> +	.probe = cros_ec_sensorhub_probe,
> +	.remove = cros_ec_sensorhub_remove,
> +};
> +
> +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..da0ba1d201e4
> --- /dev/null
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -0,0 +1,33 @@
> +/* 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>
> +
> +/* Maximal number of sensors supported by the EC. */
> +#define CROS_EC_SENSOR_MAX 16
> +
> +/*
> + * Maximal number of sensors supported by the hub:
> + * We add one for the lid angle inclinometer sensor.
> + */
> +#define CROS_EC_SENSOR_PDEV_MAX (CROS_EC_SENSOR_MAX + 1)
> +
> +/**
> + * struct cros_ec_sensorhub - Sensor Hub device data.
> + *
> + * @ec:           Embedded Controller where the hub is located.
> + * @sensor_pdev:  Array of platform_device, one per sensor.
> + */
> +struct cros_ec_sensorhub {
> +	struct cros_ec_dev *ec;
> +	struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
> +};
> +
> +#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */


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

* Re: [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub
  2019-11-05 22:26 ` [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
@ 2019-11-10 12:13   ` Jonathan Cameron
  2019-11-11  9:25     ` Enric Balletbo i Serra
  2019-11-11 11:43   ` Lee Jones
  1 sibling, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 12:13 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio

On Tue,  5 Nov 2019 14:26:38 -0800
Gwendal Grignou <gwendal@chromium.org> wrote:

> - 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>
Looks good to me.

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

> ---
> No changes in v4, v3.
> Changes in v2:
> - Remove unerelated changes.
> - Remove ec presence test in iio driver, done in cros_ec_sensorhub.
> 
>  drivers/iio/accel/cros_ec_accel_legacy.c      |   6 -
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |   6 -
>  .../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 ++----------------
>  include/linux/platform_data/cros_ec_proto.h   |   8 -
>  .../linux/platform_data/cros_ec_sensorhub.h   |   8 +
>  7 files changed, 23 insertions(+), 218 deletions(-)
> 
> diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
> index fcc3f999e482..65f85faf6f31 100644
> --- a/drivers/iio/accel/cros_ec_accel_legacy.c
> +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
> @@ -163,16 +163,10 @@ 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 iio_dev *indio_dev;
>  	struct cros_ec_sensors_core_state *state;
>  	int ret;
>  
> -	if (!ec || !ec->ec_dev) {
> -		dev_warn(&pdev->dev, "No EC device found.\n");
> -		return -EINVAL;
> -	}
> -
>  	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
>  	if (!indio_dev)
>  		return -ENOMEM;
> 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..7dce04473467 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -222,17 +222,11 @@ 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 iio_dev *indio_dev;
>  	struct cros_ec_sensors_state *state;
>  	struct iio_chan_spec *channel;
>  	int ret, i;
>  
> -	if (!ec_dev || !ec_dev->ec_dev) {
> -		dev_warn(&pdev->dev, "No CROS EC device found.\n");
> -		return -EINVAL;
> -	}
> -
>  	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
>  	if (!indio_dev)
>  		return -ENOMEM;
> 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..d85a391e50c5 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -169,17 +169,11 @@ 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 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) {
> -		dev_warn(dev, "No CROS EC device found.\n");
> -		return -EINVAL;
> -	}
> -
>  	indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
>  	if (!indio_dev)
>  		return -ENOMEM;
> diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
> index a35104e35cb4..c4b977a5dd96 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", },
> @@ -117,192 +121,6 @@ static void cros_ec_class_release(struct device *dev)
>  	kfree(to_cros_ec_dev(dev));
>  }
>  
> -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;
> @@ -358,11 +176,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/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index f3de0662135d..691f9e953a96 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -168,14 +168,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 da0ba1d201e4..2b5a4d81f65f 100644
> --- a/include/linux/platform_data/cros_ec_sensorhub.h
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -19,6 +19,14 @@
>   */
>  #define CROS_EC_SENSOR_PDEV_MAX (CROS_EC_SENSOR_MAX + 1)
>  
> +/**
> + * 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] 52+ messages in thread

* Re: [PATCH v4 04/17] platform: chrome: cros-ec: record event timestamp in the hard irq
  2019-11-05 22:26 ` [PATCH v4 04/17] platform: chrome: cros-ec: record event timestamp in the hard irq Gwendal Grignou
@ 2019-11-10 12:16   ` Jonathan Cameron
  2019-11-11  9:27     ` Enric Balletbo i Serra
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 12:16 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio

On Tue,  5 Nov 2019 14:26:39 -0800
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>
A minor issue with kernel-doc (I think...)

Otherwise,
Acked-by: Jonathan Cameron <Jonathan.Cameron@kernel.org>
> ---
> Changes in v4:
>   Check patch with --strict option
>     Alignement
> No changes in v3.
> Changes in v2:
>   Make cros_ec_get_time_ns inline.
>   Using ktime_t instead of s64 when dealing with time.
>   Added code in ishtp to gather timestamp.
> 
>  drivers/platform/chrome/cros_ec.c           | 17 ++++++++++++++---
>  drivers/platform/chrome/cros_ec_ishtp.c     | 17 +++++++++++++++--
>  drivers/platform/chrome/cros_ec_lpc.c       |  2 ++
>  include/linux/platform_data/cros_ec_proto.h | 16 ++++++++++++++++
>  4 files changed, 47 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
> index 9b2d07422e17..925f84dbf621 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -31,6 +31,15 @@ static struct cros_ec_platform pd_p = {
>  	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
>  };
>  
> +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;
> @@ -141,9 +150,11 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
>  	}
>  
>  	if (ec_dev->irq) {
> -		err = devm_request_threaded_irq(dev, ec_dev->irq, NULL,
> -				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> -				"chromeos-ec", ec_dev);
> +		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) {
>  			dev_err(dev, "Failed to request IRQ %d: %d",
>  				ec_dev->irq, err);
> diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
> index 25ca2c894b4d..5c848f22b44b 100644
> --- a/drivers/platform/chrome/cros_ec_ishtp.c
> +++ b/drivers/platform/chrome/cros_ec_ishtp.c
> @@ -200,13 +200,14 @@ static int ish_send(struct ishtp_cl_data *client_data,
>   * process_recv() - Received and parse incoming packet
>   * @cros_ish_cl: Client instance to get stats
>   * @rb_in_proc: Host interface message buffer
> + * @timestamp: Timestamp of when parent callback started
>   *
>   * Parse the incoming packet. If it is a response packet then it will
>   * update per instance flags and wake up the caller waiting to for the
>   * response. If it is an event packet then it will schedule event work.
>   */
>  static void process_recv(struct ishtp_cl *cros_ish_cl,
> -			 struct ishtp_cl_rb *rb_in_proc)
> +			 struct ishtp_cl_rb *rb_in_proc, ktime_t timestamp)
>  {
>  	size_t data_len = rb_in_proc->buf_idx;
>  	struct ishtp_cl_data *client_data =
> @@ -295,6 +296,11 @@ static void process_recv(struct ishtp_cl *cros_ish_cl,
>  		break;
>  
>  	case CROS_MKBP_EVENT:
> +		/*
> +		 * Set timestamp from beginning of function since we actually
> +		 * got an incoming MKBP event
> +		 */
> +		client_data->ec_dev->last_event_time = timestamp;
>  		/* The event system doesn't send any data in buffer */
>  		schedule_work(&client_data->work_ec_evt);
>  
> @@ -322,10 +328,17 @@ static void ish_event_cb(struct ishtp_cl_device *cl_device)
>  {
>  	struct ishtp_cl_rb *rb_in_proc;
>  	struct ishtp_cl	*cros_ish_cl = ishtp_get_drvdata(cl_device);
> +	ktime_t timestamp;
> +
> +	/*
> +	 * Take timestamp as close to hardware interrupt as possible for sensor
> +	 * timestamps.
> +	 */
> +	timestamp = cros_ec_get_time_ns();
>  
>  	while ((rb_in_proc = ishtp_cl_rx_get_rb(cros_ish_cl)) != NULL) {
>  		/* Decide what to do with received data */
> -		process_recv(cros_ish_cl, rb_in_proc);
> +		process_recv(cros_ish_cl, rb_in_proc, timestamp);
>  	}
>  }
>  
> 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 691f9e953a96..b183024fef1f 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -122,6 +122,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
> @@ -162,6 +164,7 @@ struct cros_ec_device {
>  	int event_size;
>  	u32 host_event_wake_mask;
>  	u32 last_resume_result;
> +	ktime_t last_event_time;
>  
>  	/* The platform devices used by the mfd driver */
>  	struct platform_device *ec;
> @@ -210,4 +213,17 @@ int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
>  
>  int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
>  
> +/**
> + * cros_ec_get_time_ns - Return time in ns.

Fairly sure that's not correct kernel-doc.  Please run
../scripts/kernel-doc over the files and fix the warnings.

 * 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.
> + *
> + * Return: ktime_t format since boot.
> + */
> +static inline ktime_t cros_ec_get_time_ns(void)
> +{
> +	return ktime_get_boottime_ns();
> +}
> +
>  #endif /* __LINUX_CROS_EC_PROTO_H */


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

* Re: [PATCH v4 05/17] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number
  2019-11-05 22:26 ` [PATCH v4 05/17] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
@ 2019-11-10 12:17   ` Jonathan Cameron
  2019-11-11  9:29     ` Enric Balletbo i Serra
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 12:17 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio, Enrico Granata

On Tue,  5 Nov 2019 14:26:40 -0800
Gwendal Grignou <gwendal@chromium.org> wrote:

> 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@chromium.org>
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

This strikes me as something that could be a potential fix to backport?
Any known cases of a negative irq getting to here or is this a by
inspection thing?

Otherwise seems obviously correct.
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Jonathan

> ---
> No changes in v4, v3.
> Changes in v2:
>   Remove dual Enrico's signature.
> 
>  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 925f84dbf621..d3dfa27171e6 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -149,7 +149,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,


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

* Re: [PATCH v4 06/17] platform: chrome: cros_ec: handle MKBP more events flag
  2019-11-05 22:26 ` [PATCH v4 06/17] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
@ 2019-11-10 12:28   ` Jonathan Cameron
  2019-11-11  9:30     ` Enric Balletbo i Serra
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 12:28 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio, Enrico Granata

On Tue,  5 Nov 2019 14:26:41 -0800
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 (Matrix KeyBoard Protocol)
> 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>
A couple of trivial things inline.  
I just checked with the script and it doesn't seem to warn on the ()
but the documentation for kernel-doc suggests it should be there...

I guess things are more relaxed than I though.. Fix them if you are
doing another spin perhaps but don't bother otherwise.

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

> ---
> Changes in v4:
> - Check patch with --strict option
>     Alignement
> Changes in v3:
>   Fix indentation.
> Changes in v2:
>   Process flag inside cros_ec_get_next_event, clean flag from event.
>   Introduce public function cros_ec_handle_event(), use it in rpmsg and
>     ishtp transport layer.
>   Remplace dev_info with dev_dbg, call only once.
> 
>  drivers/platform/chrome/cros_ec.c           | 34 +++++++--
>  drivers/platform/chrome/cros_ec_ishtp.c     |  8 +-
>  drivers/platform/chrome/cros_ec_lpc.c       | 15 +++-
>  drivers/platform/chrome/cros_ec_proto.c     | 81 +++++++++++++--------
>  drivers/platform/chrome/cros_ec_rpmsg.c     | 19 +----
>  include/linux/platform_data/cros_ec_proto.h | 12 ++-
>  6 files changed, 107 insertions(+), 62 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
> index d3dfa27171e6..c9f0b9ebcbc1 100644
> --- a/drivers/platform/chrome/cros_ec.c
> +++ b/drivers/platform/chrome/cros_ec.c
> @@ -40,13 +40,23 @@ static irqreturn_t ec_irq_handler(int irq, void *data)
>  	return IRQ_WAKE_THREAD;
>  }
>  
> -static irqreturn_t ec_irq_thread(int irq, void *data)
> +/**
> + * cros_ec_handle_event - process and forward pending events on EC

cros_ec_handle_event() - 

> + * @ec_dev: Device with events to process.
> + *
> + * Call this function in a loop when the kernel is notified that the EC has
> + * pending events.
> + *
> + * Return: true if more events are still pending and this function should be
> + * called again.
> + */
> +bool cros_ec_handle_event(struct cros_ec_device *ec_dev)
>  {
> -	struct cros_ec_device *ec_dev = data;
> -	bool wake_event = true;
> +	bool wake_event;
> +	bool ec_has_more_events;
>  	int ret;
>  
> -	ret = cros_ec_get_next_event(ec_dev, &wake_event);
> +	ret = cros_ec_get_next_event(ec_dev, &wake_event, &ec_has_more_events);
>  
>  	/*
>  	 * Signal only if wake host events or any interrupt if
> @@ -59,6 +69,20 @@ static irqreturn_t ec_irq_thread(int irq, void *data)
>  	if (ret > 0)
>  		blocking_notifier_call_chain(&ec_dev->event_notifier,
>  					     0, ec_dev);
> +
> +	return ec_has_more_events;
> +}
> +EXPORT_SYMBOL(cros_ec_handle_event);
> +
> +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 = cros_ec_handle_event(ec_dev);
> +	} while (ec_has_more_events);
> +
>  	return IRQ_HANDLED;
>  }
>  
> @@ -274,7 +298,7 @@ EXPORT_SYMBOL(cros_ec_suspend);
>  static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev)
>  {
>  	while (ec_dev->mkbp_event_supported &&
> -	       cros_ec_get_next_event(ec_dev, NULL) > 0)
> +	       cros_ec_get_next_event(ec_dev, NULL, NULL) > 0)
>  		blocking_notifier_call_chain(&ec_dev->event_notifier,
>  					     1, ec_dev);
>  }
> diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
> index 5c848f22b44b..e5996821d08b 100644
> --- a/drivers/platform/chrome/cros_ec_ishtp.c
> +++ b/drivers/platform/chrome/cros_ec_ishtp.c
> @@ -136,11 +136,11 @@ static void ish_evt_handler(struct work_struct *work)
>  	struct ishtp_cl_data *client_data =
>  		container_of(work, struct ishtp_cl_data, work_ec_evt);
>  	struct cros_ec_device *ec_dev = client_data->ec_dev;
> +	bool ec_has_more_events;
>  
> -	if (cros_ec_get_next_event(ec_dev, NULL) > 0) {
> -		blocking_notifier_call_chain(&ec_dev->event_notifier,
> -					     0, ec_dev);
> -	}
> +	do {
> +		ec_has_more_events = cros_ec_handle_event(ec_dev);
> +	} while (ec_has_more_events);
>  }
>  
>  /**
> diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
> index 3c77496e164d..dccf479c6625 100644
> --- a/drivers/platform/chrome/cros_ec_lpc.c
> +++ b/drivers/platform/chrome/cros_ec_lpc.c
> @@ -312,13 +312,20 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
>  static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
>  {
>  	struct cros_ec_device *ec_dev = data;
> +	bool ec_has_more_events;
> +	int ret;
>  
>  	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,
> -					     ec_dev);
> +	if (ec_dev->mkbp_event_supported)
> +		do {
> +			ret = cros_ec_get_next_event(ec_dev, NULL,
> +						     &ec_has_more_events);
> +			if (ret > 0)
> +				blocking_notifier_call_chain(
> +						&ec_dev->event_notifier, 0,
> +						ec_dev);
> +		} while (ec_has_more_events);
>  
>  	if (value == ACPI_NOTIFY_DEVICE_WAKE)
>  		pm_system_wakeup();
> diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
> index b502933e911b..03173ca66b1b 100644
> --- a/drivers/platform/chrome/cros_ec_proto.c
> +++ b/drivers/platform/chrome/cros_ec_proto.c
> @@ -456,7 +456,10 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
>  	if (ret < 0 || ver_mask == 0)
>  		ec_dev->mkbp_event_supported = 0;
>  	else
> -		ec_dev->mkbp_event_supported = 1;
> +		ec_dev->mkbp_event_supported = fls(ver_mask);
> +
> +	dev_dbg(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,
> @@ -569,6 +572,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;
> @@ -581,7 +585,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;
> @@ -589,30 +593,26 @@ 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;
>  
> +	memset(msg, 0, sizeof(*msg));
>  	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)
> @@ -639,32 +639,55 @@ static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
>   * @ec_dev: Device to fetch event from.
>   * @wake_event: Pointer to a bool set to true upon return if the event might be
>   *              treated as a wake event. Ignored if null.
> + * @has_more_events: Pointer to bool set to true if more than one event is
> + *              pending.
> + *              Some EC will set this flag to indicate cros_ec_get_next_event()
> + *              can be called multiple times in a row.
> + *              It is an optimization to prevent issuing a EC command for
> + *              nothing or wait for another interrupt from the EC to process
> + *              the next message.
> + *              Ignored if null.
>   *
>   * Return: negative error code on errors; 0 for no data; or else number of
>   * bytes received (i.e., an event was retrieved successfully). Event types are
>   * written out to @ec_dev->event_data.event_type on success.
>   */
> -int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
> +int cros_ec_get_next_event(struct cros_ec_device *ec_dev,
> +			   bool *wake_event,
> +			   bool *has_more_events)
>  {
>  	u8 event_type;
>  	u32 host_event;
>  	int ret;
>  
> -	if (!ec_dev->mkbp_event_supported) {
> -		ret = get_keyboard_state_event(ec_dev);
> -		if (ret <= 0)
> -			return ret;
> +	/*
> +	 * Default value for wake_event.
> +	 * Wake up on keyboard event, wake up for spurious interrupt or link
> +	 * error to the EC.
> +	 */
> +	if (wake_event)
> +		*wake_event = true;
>  
> -		if (wake_event)
> -			*wake_event = true;
> +	/*
> +	 * Default value for has_more_events.
> +	 * EC will raise another interrupt if AP does not process all events
> +	 * anyway.
> +	 */
> +	if (has_more_events)
> +		*has_more_events = false;
>  
> -		return ret;
> -	}
> +	if (!ec_dev->mkbp_event_supported)
> +		return get_keyboard_state_event(ec_dev);
>  
>  	ret = get_next_event(ec_dev);
>  	if (ret <= 0)
>  		return ret;
>  
> +	if (has_more_events)
> +		*has_more_events = ec_dev->event_data.event_type &
> +			EC_MKBP_HAS_MORE_EVENTS;
> +	ec_dev->event_data.event_type &= EC_MKBP_EVENT_TYPE_MASK;
> +
>  	if (wake_event) {
>  		event_type = ec_dev->event_data.event_type;
>  		host_event = cros_ec_get_host_event(ec_dev);
> @@ -679,11 +702,7 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
>  		else if (host_event &&
>  			 !(host_event & ec_dev->host_event_wake_mask))
>  			*wake_event = false;
> -		/* Consider all other events as wake events. */
> -		else
> -			*wake_event = true;
>  	}
> -

Nitpick. Unrelated whitespace change ;)

>  	return ret;
>  }
>  EXPORT_SYMBOL(cros_ec_get_next_event);
> diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c
> index 0c3738c3244d..bd068afe43b5 100644
> --- a/drivers/platform/chrome/cros_ec_rpmsg.c
> +++ b/drivers/platform/chrome/cros_ec_rpmsg.c
> @@ -143,22 +143,11 @@ cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work)
>  						      struct cros_ec_rpmsg,
>  						      host_event_work);
>  	struct cros_ec_device *ec_dev = dev_get_drvdata(&ec_rpmsg->rpdev->dev);
> -	bool wake_event = true;
> -	int ret;
> -
> -	ret = cros_ec_get_next_event(ec_dev, &wake_event);
> -
> -	/*
> -	 * 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))
> -		pm_wakeup_event(ec_dev->dev, 0);
> +	bool ec_has_more_events;
>  
> -	if (ret > 0)
> -		blocking_notifier_call_chain(&ec_dev->event_notifier,
> -					     0, ec_dev);
> +	do {
> +		ec_has_more_events = cros_ec_handle_event(ec_dev);
> +	} while (ec_has_more_events);
>  }
>  
>  static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
> index b183024fef1f..e238930ae967 100644
> --- a/include/linux/platform_data/cros_ec_proto.h
> +++ b/include/linux/platform_data/cros_ec_proto.h
> @@ -116,7 +116,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.
> @@ -156,7 +158,7 @@ struct cros_ec_device {
>  	int (*pkt_xfer)(struct cros_ec_device *ec,
>  			struct cros_ec_command *msg);
>  	struct mutex lock;
> -	bool mkbp_event_supported;
> +	u8 mkbp_event_supported;
>  	bool host_sleep_v1;
>  	struct blocking_notifier_head event_notifier;
>  
> @@ -205,7 +207,9 @@ int cros_ec_unregister(struct cros_ec_device *ec_dev);
>  
>  int cros_ec_query_all(struct cros_ec_device *ec_dev);
>  
> -int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event);
> +int cros_ec_get_next_event(struct cros_ec_device *ec_dev,
> +			   bool *wake_event,
> +			   bool *has_more_events);
>  
>  u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev);
>  
> @@ -213,6 +217,8 @@ int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
>  
>  int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
>  
> +bool cros_ec_handle_event(struct cros_ec_device *ec_dev);
> +
>  /**
>   * cros_ec_get_time_ns - Return time in ns.
>   *


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

* Re: [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support
  2019-11-06 21:13   ` Gwendal Grignou
@ 2019-11-10 12:54     ` Jonathan Cameron
  2019-11-14  1:01       ` Gwendal Grignou
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 12:54 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: Brian Norris, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones, Benson Leung,
	Enric Balletbo i Serra, Doug Anderson, Guenter Roeck,
	Fabien Lahoudere, linux-kernel, linux-iio

On Wed, 6 Nov 2019 13:13:10 -0800
Gwendal Grignou <gwendal@chromium.org> wrote:

> On Tue, Nov 5, 2019 at 2:27 PM 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>

Various bits inline.   Devm fun rising again + some oddity around
PTR_ERR value returning that looks to be old.

Thanks,

Jonathan

> > ---
> > Changes in v4:
> > - Keep defining cros_ec_sensorhub in kernel-doc format
> > - Fix logic error when checking if sensor index outside array.
> > - Check patch with --strict option
> >     Use sizeof(*obj) instead of sizeof(struct ...obj)
> >     Alignement
> >     Use uX instead of uintX_t
> >     Use !ptr instead of ptr != NULL
> > Changes in v3:
> > - Do not use ret !=
> > - Simplfy errpr handling by removing a goto
> > - Fix doxygen comments
> > - Replace suspend/resume entry points with regular driver entry point;
> >   There was an issue in the past where the sensor stack was preventing
> >   device to suspend, but the proper fix has been implemented in cros_ec
> >   code (6ad16b78a039b "platform/chrome: don't report EC_MKBP_EVENT_SENSOR_FIFO as wakeup")
> > - Reduce mutex scope by checking return code outside of it.
> > Changes in v2:
> > - Do not register a .remove routinge in plaform_driver. A
> >   devm_action_add is added later patch IIO driver register their
> > callback.
> > - Remove double lines, add lines before return calls.
> > - Handle FLUSH flag from EC.
> >
> > - Use ktime_t for most timestamp measurements.
> > - Add doxygen comments
> > - Cleanup timestamp collection when processing FIFO.
> > - Rename fifo_toggle to fifo_enable
> >
> >  drivers/platform/chrome/Makefile              |   3 +-
> >  drivers/platform/chrome/cros_ec_sensorhub.c   | 117 +++--
> >  .../platform/chrome/cros_ec_sensorhub_ring.c  | 423 ++++++++++++++++++
> >  .../linux/platform_data/cros_ec_sensorhub.h   |  82 ++++
> >  4 files changed, 592 insertions(+), 33 deletions(-)
> >  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c
> >
> > 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 6a0aa84cf092..0e34ad2903bb 100644
> > --- a/drivers/platform/chrome/cros_ec_sensorhub.c
> > +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> > @@ -39,11 +39,9 @@ static int cros_ec_sensorhub_register(struct device *dev,
> >         int ret, i, id, sensor_num;
> >         struct cros_ec_dev *ec = sensorhub->ec;
> >         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 platform_device *pdev;
> >         char *name;
> > +       struct cros_ec_command *msg = sensorhub->msg;
> >
> >         sensor_num = cros_ec_get_sensor_count(ec);
> >         if (sensor_num < 0) {
> > @@ -58,30 +56,21 @@ static int cros_ec_sensorhub_register(struct device *dev,
> >                 return -EINVAL;
> >         }
> >
> > -       /* Prepare a message to send INFO command to each sensor. */
> > -       msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
> > -                     GFP_KERNEL);
> > -       if (!msg)
> > -               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;
> > -       resp = (struct ec_response_motion_sense *)msg->data;
> > +       msg->insize = sizeof(struct ec_response_motion_sense);
> > +       msg->outsize = sizeof(struct ec_params_motion_sense);
> >
> >         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:
> >                         name = "cros-ec-accel";
> >                         break;
> > @@ -104,16 +93,16 @@ static int cros_ec_sensorhub_register(struct device *dev,
> >                         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;
> >                 }
> >                 pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
> > -               if (IS_ERR(pdev)) {
> > -                       ret = IS_ERR(pdev);
> > -                       goto error;
> > -               }
> > +               if (IS_ERR(pdev))
> > +                       return IS_ERR(pdev);
> > +
> >                 sensorhub->sensor_pdev[id++] = pdev;
> > -               sensor_type[resp->info.type]++;
> > +               sensor_type[sensorhub->resp->info.type]++;
> >         }
> >
> >         if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
> > @@ -123,16 +112,13 @@ static int cros_ec_sensorhub_register(struct device *dev,
> >                                    EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
> >                 pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
> >                                                         "cros-ec-lid-angle", 0);
> > -               if (IS_ERR(pdev)) {
> > -                       ret = IS_ERR(pdev);
> > -                       goto error;
> > -               }
> > +               if (IS_ERR(pdev))
> > +                       return IS_ERR(pdev);

return PTR_ERR(pdev)?  Elsewhere as well. Looks to be an old issue but
should fix it so we get most relevant error value.

> > +
> >                 sensorhub->sensor_pdev[id++] = pdev;
> >         }
> >
> > -error:
> > -       kfree(msg);
> > -       return ret;
> > +       return 0;
> >  }
> >
> >  static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> > @@ -141,13 +127,29 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> >         struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> >         int ret, i;
> >         struct platform_device *pdev;
> > -       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. */
> > @@ -175,6 +177,20 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> >                 }
> >         }
> >
> > +       /*
> > +        * 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);
> > +               if (ret)
> > +                       goto unregister_sensors;
> > +               /*
> > +                * The msg and its data is not under the control of the ring
> > +                * handler.
> > +                */
> > +       }
> > +
> >         return 0;
> >
> >  unregister_sensors:
> > @@ -195,9 +211,12 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
> >  {
> >         struct cros_ec_sensorhub *sensorhub =
> >                 platform_get_drvdata(sensorhub_pdev);
> > +       struct cros_ec_dev *ec = sensorhub->ec;
> >         struct platform_device *pdev;
> >         int i;
> >
> > +       if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> > +               cros_ec_sensorhub_ring_remove(sensorhub);

This (I think) means that we have non obvious ordering.  Parts of matching add are
handled as managed, and parts are not.  I'd like it to be obvious that the
setup and tear down are precise mirrors of each other.

That generally means that everything in probe after the first non managed thing
that needs cleaning up, must also not use managed interfaces.  Otherwise
the ordering is complex and much harder to review.


> >         for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
> >                 pdev = sensorhub->sensor_pdev[i];
> >                 if (pdev)
> > @@ -207,9 +226,43 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
> >         return 0;
> >  }
> >
> > +#if CONFIG_PM_SLEEP
> > +/*
> > + * 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 cros_ec_ring_suspend(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_enable(sensorhub, false);
> > +       return 0;
> > +}
> > +
> > +static int cros_ec_ring_resume(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_enable(sensorhub, true);
> > +       return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_ring_pm_ops,
> > +               cros_ec_ring_suspend,
> > +               cros_ec_ring_resume);
> > +
> >  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,
> > 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..f091f2a4ccfe
> > --- /dev/null
> > +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> > @@ -0,0 +1,423 @@
> > +// 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;
> > +
> > +       if (sample->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
> > +               return 0;
> > +
> > +       return cb(indio_dev, sample->vector, sample->timestamp);
> > +}
> > +
> > +/**
> > + * cros_ec_sensorhub_register_push_data - register the callback to the hub.
> > + *
> > + * @sensorhub : Sensor Hub object
> > + * @sensor_num : The sensor the caller is interested in.
> > + * @indio_dev : The iio device to use when a sample arrives.
> > + * @cb : The callback to call when a sample arrives.
> > + *
> > + * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
> > + * from the EC.
> > + *
> > + * Return: 0 when callback is registered.
> > + */
> > +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_PDEV_MAX)
> > +               return -EINVAL;
> > +       if (!sensorhub->push_data[sensor_num].indio_dev)  
> I made a logic mistake, it should be:
> if (sensorhub->push_data[sensor_num].indio_dev)
> [When the slot is already used, return einval.]
> > +               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);
> > +
> > +/**
> > + * cros_ec_sensorhub_ring_fifo_enable - Enable or disable interrupt generation
> > + *  for FIFO events.
> > + * @sensorhub : Sensor Hub object
> > + * @on : true when events are requested.
> > + *
> > + * To be called before sleeping or when noone is listening.
> > + * Return: 0 on success.
> > + */
> > +int cros_ec_sensorhub_ring_fifo_enable(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);
> > +       mutex_unlock(&sensorhub->cmd_lock);
> > +
> > +       /* We expect to receive a payload of 4 bytes, ignore. */
> > +       if (ret > 0)
> > +               ret = 0;
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cros_ec_ring_process_event - process one EC FIFO event
> > + *
> > + * @sensorhub: Sensorhub object.
> > + * @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')
> > + *
> > + * Process one EC event, add it in the ring if necessary.
> > + *
> > + * Return: true if out event has been populated.
> > + */
> > +static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
> > +                               const struct cros_ec_fifo_info *fifo_info,
> > +                               const ktime_t fifo_timestamp,
> > +                               ktime_t *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. */
Having this comment in the middle of the definition block is a bit messy.
Perhaps have
	int async_flags;
> > +       const int async_flags = in->flags &
> > +               (MOTIONSENSE_SENSOR_FLAG_ODR | MOTIONSENSE_SENSOR_FLAG_FLUSH);
> > +       const s64 now = cros_ec_get_time_ns();
> > +
...
	/* COMMENT */
	async_flags =...


> > +       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;
> > +       ktime_t 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 if there are lost vectors. */
> > +       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) {

Given you have to be careful to always unlock this mutex, perhaps a goto and single
exit path would be easier to maintain?  However this is a big complex function.
It might be better broken up somewhat so the locked section is in a separate
function with clear error handling etc.  Possibly even several different
functions.

1. Fifo info if lost vectors.
2. Copy elements in main fifo

I've not tried the refactor so might be more trouble than it is worth though!

> > +                       mutex_unlock(&sensorhub->cmd_lock);
> > +                       return;
> > +               }
> > +               memcpy(fifo_info, &sensorhub->resp->fifo_info,
> > +                      sizeof(*fifo_info));
> > +
> > +               /*
> > +                * Update collection time, will not be as precise as the
> > +                * non-error case.
> > +                */
> > +               fifo_timestamp = cros_ec_get_time_ns();
> > +       } else {
> > +               fifo_timestamp = sensorhub->fifo_timestamp[
> > +                       CROS_EC_SENSOR_NEW_TS];
> > +       }
> > +
> > +       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;
> > +       }
> > +
> > +       /* Copy elements in the main fifo */
> > +       current_timestamp = sensorhub->fifo_timestamp[CROS_EC_SENSOR_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;
> > +               }
> > +               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;
> > +               }
> > +               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) {
> > +               int total_lost = fifo_info->info.total_lost;
> > +
> > +               if (total_lost) {
> > +                       int lost = fifo_info->lost[i];
> > +
> > +                       if (lost) {
> > +                               dev_warn_ratelimited(sensorhub->dev,
> > +                                                    "Sensor %d: lost: %d out of %d\n",
> > +                                                    i, lost, 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[CROS_EC_SENSOR_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[CROS_EC_SENSOR_NEW_TS] =
> > +               ec_dev->last_event_time;
> > +       cros_ec_sensorhub_ring_handler(sensorhub);
> > +
> > +       return NOTIFY_OK;
> > +}
> > +
> > +/**
> > + * cros_ec_sensorhub_ring_add - Add/Remove the fifo functionality if the EC
> > + *  supports it.
> > + *
> > + * @sensorhub : Sensor Hub object
> > + *
> > + * Return: 0 on success.
> > + */
> > +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;

Burying managed allocations deep in a function which is only partly managed
tends to lead to hard to review code.  If you can only use partial devm
in such a function I wouldn't use it at all.

Might be worth looking at devm_add_action_or_reset in here to deal with the
notifier chain cleanup and fifo disable, but I've not looked through
all the call paths to check if that makes sense.

> > +
> > +       sensorhub->fifo_timestamp[CROS_EC_SENSOR_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");

dev_err I'd have thought...  I'm guessing the code doesn't work if that
fails.

> > +               return ret;
> > +       }
> > +
> > +       /* Start collection samples. */
> > +       return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true);

If this fails should you not undo the notifier chain register?

> > +}
> > +
> > +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_enable(sensorhub, false);
> > +       blocking_notifier_chain_unregister(&ec_dev->event_notifier,
> > +                                          &sensorhub->notifier);
> > +
> > +       return 0;
> > +}
> > diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> > index 2b5a4d81f65f..8f9bf9a70701 100644
> > --- a/include/linux/platform_data/cros_ec_sensorhub.h
> > +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> > @@ -8,6 +8,9 @@
> >  #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> >  #define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> >
> > +#include <linux/ktime.h>
> > +#include <linux/mutex.h>
> > +#include <linux/notifier.h>
> >  #include <linux/platform_data/cros_ec_commands.h>
> >
> >  /* Maximal number of sensors supported by the EC. */
> > @@ -27,15 +30,94 @@ struct cros_ec_sensor_platform {
> >         u8 sensor_num;
> >  };
> >
> > +struct iio_dev;
> > +
> > +/**
> > + * typedef cros_ec_sensorhub_push_data_cb_t - Callback function to send datum
> > + *     to specific sensors
> > + *
> > + * @indio_dev: The IIO device that will process the sample.
> > + * @data: vector array of the ring sample.
> > + * @timestamp: Timestamp in host timespace when the sample was acquired by
> > + *             the EC.
> > + */
> > +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 {
> > +       CROS_EC_SENSOR_LAST_TS,
> > +       CROS_EC_SENSOR_NEW_TS,
> > +       CROS_EC_SENSOR_ALL_TS
> > +};
> > +
> > +struct __ec_todo_packed cros_ec_fifo_info {
> > +       struct ec_response_motion_sense_fifo_info info;
> > +       u16    lost[CROS_EC_SENSOR_MAX];
> > +};
> > +
> > +struct cros_ec_sensors_ring_sample {
> > +       u8  sensor_id;
> > +       u8  flag;
> > +       s16 vector[3];
> > +       s64 timestamp;
> > +} __packed;
> > +
> >  /**
> >   * struct cros_ec_sensorhub - Sensor Hub device data.
> >   *
> > + * @dev:          Device object, mostly used for logging.
> >   * @ec:           Embedded Controller where the hub is located.
> >   * @sensor_pdev:  Array of platform_device, one per sensor.
> > + * @msg: Structure to send FIFO requests.
> > + * @params: pointer to parameters in msg.
> > + * @resp: pointer to responses in msg.
> > + * @cmd_lock : lock for sending msg.
> > + * @notifier: Notifier to kick the FIFO interrupt.
> > + * @ring: Preprocessed ring to store events.
> > + * @timestamp: array for event timestamp and spreading.

Now I know you didn't check the docs through the kernel-docs script.

It's called fifo_timestamp.

> > + * @fifo_info: copy of FIFO information coming from the EC.
> > + * @fifo_size: size of the ring.
> > + * @pushdata: array of callback to send datums to iio sensor object.
> >   */
> >  struct cros_ec_sensorhub {
> > +       struct device *dev;
> >         struct cros_ec_dev *ec;
> >         struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
> > +
> > +       struct cros_ec_command *msg;
> > +       struct ec_params_motion_sense *params;
> > +       struct ec_response_motion_sense *resp;
> > +       struct mutex cmd_lock;
> > +
> > +       struct notifier_block notifier;
> > +
> > +       struct cros_ec_sensors_ring_sample *ring;
> > +
> > +       ktime_t fifo_timestamp[CROS_EC_SENSOR_ALL_TS];
> > +       struct cros_ec_fifo_info fifo_info;
> > +       int    fifo_size;
> > +
> > +       struct cros_ec_sensorhub_sensor_push_data push_data[
> > +               CROS_EC_SENSOR_PDEV_MAX];
> >  };
> >
> > +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);
> > +
> > +void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub *sensorhub,
> > +                                           u8 sensor_num);
> > +
> > +int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub);
> > +int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensorhub);
> > +int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
> > +                                      bool on);
> > +
> >  #endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
> > --
> > 2.24.0.rc1.363.gb1bccd3e3d-goog
> >  


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

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

On Tue,  5 Nov 2019 14:26:45 -0800
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 but I haven't been through this all that closely..
I'm just assuming the maths is right ;)

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

> ---
> Changes in v4:
> - Check patch with --strict option
>     Alignement
> No changes in v3.
> Changes in v2:
> - Use CROS_EC_SENSOR_LAST_TS instead of LAST_TS to avoid name colisions.
> 
>  .../platform/chrome/cros_ec_sensorhub_ring.c  | 98 ++++++++++++++++++-
>  1 file changed, 95 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> index f091f2a4ccfe..b22a71406742 100644
> --- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> @@ -187,6 +187,96 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
>  	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
>   *
> @@ -322,9 +412,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);
> +	cros_ec_ring_spread_add(sensorhub, sensor_mask,	current_timestamp, last_out);

>  
>  ring_handler_end:
>  	sensorhub->fifo_timestamp[CROS_EC_SENSOR_LAST_TS] = current_timestamp;


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

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

On Tue,  5 Nov 2019 14:26:46 -0800
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>
A few minor things inline seeing as you will be respinning.
Again, I've not checked the maths as don't have that much time available!

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

> ---
> Changes in v4:
> - Keep defining cros_ec_sensorhub in kernel-doc format
> - Check patch with --strict option
>     Use BIT()
>     Add spaces around '-'
>     Alignement
> Changes in v3:
> - Fix doxygen code.
> Changes in v2:
> - Move some #define in .c to prevent name collisions.
> - Add proper doxygen comments.
> - Use /* instead of //
> 
>  .../platform/chrome/cros_ec_sensorhub_ring.c  | 505 +++++++++++++++++-
>  .../linux/platform_data/cros_ec_sensorhub.h   |  75 +++
>  2 files changed, 562 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> index b22a71406742..8960081caeba 100644
> --- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> @@ -18,6 +18,21 @@
>  #include <linux/sort.h>
>  #include <linux/slab.h>
>  
> +/* Precision of fixed point for the m values from the filter */
> +#define M_PRECISION BIT(23)
> +
> +/* 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
> +
> +/* To measure by how much the filter is overshooting, if it happens. */
> +#define FUTURE_TS_ANALYTICS_COUNT_MAX 100
> +
>  static inline int cros_sensorhub_send_sample(
>  		struct cros_ec_sensorhub *sensorhub,
>  		struct cros_ec_sensors_ring_sample *sample)
> @@ -91,9 +106,11 @@ EXPORT_SYMBOL_GPL(cros_ec_sensorhub_unregister_push_data);
>  int cros_ec_sensorhub_ring_fifo_enable(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;
>  
> @@ -110,6 +127,231 @@ int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
>  	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 - Update filter history.
> + *
> + * @state: Filter information.
> + * @b: IRQ timestamp, EC timebase (us)
> + * @c: IRQ timestamp, AP timebase (ns)
> + *
> + * Given a new IRQ timestamp pair (EC and AP timebases), add it to the filter
> + * history.
> + */
> +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 < CROS_EC_SENSORHUB_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
> + *
> + * @state: filter information.
> + * @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
> + *
> + * 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
> + *
> + * Return: timestamp in AP timebase (ns)
> + */
> +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
>   *
> @@ -141,32 +383,54 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
>  		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;
> +		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;
>  		/*
> -		 * 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.
>  		 */
>  		return true;
>  	}
> +

Unrelated change.  Would rather not see those in a series doing interesting
things like this.

>  	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
>  		/* If we just have a timestamp, skip this entry. */
>  		return false;
> @@ -174,7 +438,22 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
>  	/* 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);

I'll assume this actually happens from time to time given the effort you are putting
in to handle it.  If so do we want near uncontrolled spamming of the log?

> +			sensorhub->future_timestamp_count = 0;
> +			sensorhub->future_timestamp_total_ns = 0;
> +		}
>  		out->timestamp = now;
>  	} else {
>  		out->timestamp = *current_timestamp;
> @@ -184,13 +463,194 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
>  	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;
> +			}
> +
> +			/*
> +			 * 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) {
> +				cros_sensorhub_send_sample(sensorhub,
> +							   batch_start);
> +				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 = 1;
> +				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
> @@ -208,7 +668,8 @@ static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
>   * 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,
> +static void cros_ec_ring_spread_add_legacy(
> +				struct cros_ec_sensorhub *sensorhub,
>  				unsigned long sensor_mask,
>  				s64 current_timestamp,
>  				struct cros_ec_sensors_ring_sample *last_out)
> @@ -277,7 +738,7 @@ static void cros_ec_ring_spread_add(struct cros_ec_sensorhub *sensorhub,
>  		cros_sensorhub_send_sample(sensorhub, out);
>  }
>  
> -/*
> +/**

Unrelated change in this patch.  Please break it out of here..

>   * cros_ec_sensorhub_ring_handler - the trigger handler function
>   *
>   * @sensorhub: device information.
> @@ -394,7 +855,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. */
> @@ -408,6 +870,7 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
>  				dev_warn_ratelimited(sensorhub->dev,
>  						     "Sensor %d: lost: %d out of %d\n",
>  						     i, lost, total_lost);
> +				sensorhub->last_batch_len[i] = 0;
>  			}
>  		}
>  	}
> @@ -415,8 +878,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[CROS_EC_SENSOR_LAST_TS] = current_timestamp;
> @@ -489,6 +955,9 @@ int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
>  	sensorhub->fifo_timestamp[CROS_EC_SENSOR_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 8f9bf9a70701..e9da92fb0bea 100644
> --- a/include/linux/platform_data/cros_ec_sensorhub.h
> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> @@ -68,7 +68,44 @@ 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;
> +};
> +
> +/* Length of the filter, how long to remember entries for */
> +#define CROS_EC_SENSORHUB_TS_HISTORY_SIZE 64
> +
>  /**
> + * struct cros_ec_sensors_ts_filter_state - Timestamp filetr state.
> + *
> + * @x_offset: x is EC interrupt time. x_offset its last value.
> + * @y_offset: y is the difference between AP and EC time, y_offset its last
> + *            value.
> + * @x_history: The past history of x, relative to x_offset.
> + * @y_history: The past history of y, relative to y_offset.
> + * @m_history: rate between y and x.
> + * @history_len: Amount of valid historic data in the arrays.
> + * @temp_buf: Temporary buffer used when updating the filter.
> + * @median_m: median value of m_history
> + * @median_error: final error to apply to AP interrupt timestamp to get the
> + *                "true timestamp" the event occurred.
> + */
> +struct cros_ec_sensors_ts_filter_state {
> +	s64 x_offset, y_offset;
> +	s64 x_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
> +	s64 y_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
> +	s64 m_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
> +	int history_len;
> +
> +	s64 temp_buf[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
> +
> +	s64 median_m;
> +	s64 median_error;
> +};
> +
> +/*
>   * struct cros_ec_sensorhub - Sensor Hub device data.
>   *
>   * @dev:          Device object, mostly used for logging.
> @@ -83,6 +120,28 @@ struct cros_ec_sensors_ring_sample {
>   * @timestamp: array for event timestamp and spreading.
>   * @fifo_info: copy of FIFO information coming from the EC.
>   * @fifo_size: size of the ring.
> + *
> + * @penultimate_batch_timestamp: array of last but one batch timestamps.
> + *  Used for timestamp spreading calculations when a batch shows up.
> + * @penultimate_batch_len: array of last but one batch length.
> + * @last_batch_timestamp: last batch timestamp array.
> + * @last_batch_len: last batch length array.
> + * @newest_sensor_event: last sensor timestamp.
> + * @overflow_a: for handling timestamp overflow for a time (sensor events)
> + * @overflow_b: for handling timestamp overflow for b time (ec interrutps)

interrupts

> + * @filter: medium fileter structure.
> + * @tight_timestamps: Set to truen when EC support tight timestamping:
> + *  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.
> + *
> + * @future_timestamp_count : 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.
> + * @future_timestamp_total_ns: Total amount of time shaved.
> + *
>   * @pushdata: array of callback to send datums to iio sensor object.
>   */
>  struct cros_ec_sensorhub {
> @@ -103,6 +162,22 @@ struct cros_ec_sensorhub {
>  	struct cros_ec_fifo_info fifo_info;
>  	int    fifo_size;
>  
> +	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;
> +
> +	bool tight_timestamps;
> +
> +	s32 future_timestamp_count;
> +	s64 future_timestamp_total_ns;
> +
>  	struct cros_ec_sensorhub_sensor_push_data push_data[
>  		CROS_EC_SENSOR_PDEV_MAX];
>  };


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

* Re: [PATCH v4 12/17] iio: cros_ec: Move function description to .c file
  2019-11-05 22:26 ` [PATCH v4 12/17] iio: cros_ec: Move function description to .c file Gwendal Grignou
@ 2019-11-10 13:08   ` Jonathan Cameron
  2019-11-11  9:35     ` Enric Balletbo i Serra
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 13:08 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio

On Tue,  5 Nov 2019 14:26:47 -0800
Gwendal Grignou <gwendal@chromium.org> wrote:

> To prevent comment rot, move function description to
> cros_ec_sensors_core.c.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Thanks for tidying this up.

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

> ---
> No changes in v4.
> Changes in v3:
>   fix spelling.
> New in v2.
> 
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 69 ++++++++++++++++
>  .../linux/iio/common/cros_ec_sensors_core.h   | 80 -------------------
>  2 files changed, 69 insertions(+), 80 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 81a7f692de2f..b47da497a3c3 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
> @@ -83,6 +83,14 @@ static void get_default_min_max_freq(enum motionsensor_type type,
>  	}
>  }
>  
> +/**
> + * cros_ec_sensors_core_init() - basic initialization of the core structure
> + * @pdev:		platform device created for the sensors
> + * @indio_dev:		iio device structure of the device
> + * @physical_device:	true if the device refers to a physical device
> + *
> + * Return: 0 on success, -errno on failure.
> + */
>  int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			      struct iio_dev *indio_dev,
>  			      bool physical_device)
> @@ -160,6 +168,16 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
>  
> +/**
> + * cros_ec_motion_send_host_cmd() - send motion sense host command
> + * @state:		pointer to state information for device
> + * @opt_length:	optional length to reduce the response size, useful on the data
> + *		path. Otherwise, the maximal allowed response size is used
> + *
> + * When called, the sub-command is assumed to be set in param->cmd.
> + *
> + * Return: 0 on success, -errno on failure.
> + */
>  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
>  				 u16 opt_length)
>  {
> @@ -422,6 +440,14 @@ int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
>  
> +/**
> + * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
> + * @indio_dev:	pointer to IIO device
> + * @scan_mask:	bitmap of the sensor indices to scan
> + * @data:	location to store data
> + *
> + * Return: 0 on success, -errno on failure.
> + */
>  int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
>  			     unsigned long scan_mask, s16 *data)
>  {
> @@ -446,6 +472,18 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
>  
> +/**
> + * cros_ec_sensors_capture() - the trigger handler function
> + * @irq:	the interrupt number.
> + * @p:		a pointer to the poll function.
> + *
> + * On a trigger event occurring, if the pollfunc is attached then this
> + * handler is called as a threaded interrupt (and hence may sleep). It
> + * is responsible for grabbing data from the device and pushing it into
> + * the associated buffer.
> + *
> + * Return: IRQ_HANDLED
> + */
>  irqreturn_t cros_ec_sensors_capture(int irq, void *p)
>  {
>  	struct iio_poll_func *pf = p;
> @@ -481,6 +519,16 @@ irqreturn_t cros_ec_sensors_capture(int irq, void *p)
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
>  
> +/**
> + * cros_ec_sensors_core_read() - function to request a value from the sensor
> + * @st:		pointer to state information for device
> + * @chan:	channel specification structure table
> + * @val:	will contain one element making up the returned value
> + * @val2:	will contain another element making up the returned value
> + * @mask:	specifies which values to be requested
> + *
> + * Return:	the type of value returned by the device
> + */
>  int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>  			  struct iio_chan_spec const *chan,
>  			  int *val, int *val2, long mask)
> @@ -521,6 +569,17 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
>  
> +/**
> + * cros_ec_sensors_core_read_avail() - get available values
> + * @indio_dev:		pointer to state information for device
> + * @chan:	channel specification structure table
> + * @vals:	list of available values
> + * @type:	type of data returned
> + * @length:	number of data returned in the array
> + * @mask:	specifies which values to be requested
> + *
> + * Return:	an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
> + */
>  int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>  				    struct iio_chan_spec const *chan,
>  				    const int **vals,
> @@ -542,6 +601,16 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read_avail);
>  
> +/**
> + * cros_ec_sensors_core_write() - function to write a value to the sensor
> + * @st:		pointer to state information for device
> + * @chan:	channel specification structure table
> + * @val:	first part of value to write
> + * @val2:	second part of value to write
> + * @mask:	specifies which values to write
> + *
> + * Return:	the type of value returned by the device
> + */
>  int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>  			       struct iio_chan_spec const *chan,
>  			       int val, int val2, long mask)
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index bb331e6356a9..0af918978f97 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -79,95 +79,25 @@ struct cros_ec_sensors_core_state {
>  	int frequencies[3];
>  };
>  
> -/**
> - * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory
> - * @indio_dev:	pointer to IIO device
> - * @scan_mask:	bitmap of the sensor indices to scan
> - * @data:	location to store data
> - *
> - * This is the safe function for reading the EC data. It guarantees that the
> - * data sampled was not modified by the EC while being read.
> - *
> - * Return: 0 on success, -errno on failure.
> - */
>  int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
>  			     s16 *data);
>  
> -/**
> - * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
> - * @indio_dev:	pointer to IIO device
> - * @scan_mask:	bitmap of the sensor indices to scan
> - * @data:	location to store data
> - *
> - * Return: 0 on success, -errno on failure.
> - */
>  int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
>  			     s16 *data);
>  
>  struct platform_device;
> -/**
> - * cros_ec_sensors_core_init() - basic initialization of the core structure
> - * @pdev:		platform device created for the sensors
> - * @indio_dev:		iio device structure of the device
> - * @physical_device:	true if the device refers to a physical device
> - *
> - * Return: 0 on success, -errno on failure.
> - */
>  int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			      struct iio_dev *indio_dev, bool physical_device);
>  
> -/**
> - * cros_ec_sensors_capture() - the trigger handler function
> - * @irq:	the interrupt number.
> - * @p:		a pointer to the poll function.
> - *
> - * On a trigger event occurring, if the pollfunc is attached then this
> - * handler is called as a threaded interrupt (and hence may sleep). It
> - * is responsible for grabbing data from the device and pushing it into
> - * the associated buffer.
> - *
> - * Return: IRQ_HANDLED
> - */
>  irqreturn_t cros_ec_sensors_capture(int irq, void *p);
>  
> -/**
> - * cros_ec_motion_send_host_cmd() - send motion sense host command
> - * @st:		pointer to state information for device
> - * @opt_length:	optional length to reduce the response size, useful on the data
> - *		path. Otherwise, the maximal allowed response size is used
> - *
> - * When called, the sub-command is assumed to be set in param->cmd.
> - *
> - * Return: 0 on success, -errno on failure.
> - */
>  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
>  				 u16 opt_length);
>  
> -/**
> - * cros_ec_sensors_core_read() - function to request a value from the sensor
> - * @st:		pointer to state information for device
> - * @chan:	channel specification structure table
> - * @val:	will contain one element making up the returned value
> - * @val2:	will contain another element making up the returned value
> - * @mask:	specifies which values to be requested
> - *
> - * Return:	the type of value returned by the device
> - */
>  int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>  			      struct iio_chan_spec const *chan,
>  			      int *val, int *val2, long mask);
>  
> -/**
> - * cros_ec_sensors_core_read_avail() - get available values
> - * @indio_dev:		pointer to state information for device
> - * @chan:	channel specification structure table
> - * @vals:	list of available values
> - * @type:	type of data returned
> - * @length:	number of data returned in the array
> - * @mask:	specifies which values to be requested
> - *
> - * Return:	an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
> - */
>  int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>  				    struct iio_chan_spec const *chan,
>  				    const int **vals,
> @@ -175,16 +105,6 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>  				    int *length,
>  				    long mask);
>  
> -/**
> - * cros_ec_sensors_core_write() - function to write a value to the sensor
> - * @st:		pointer to state information for device
> - * @chan:	channel specification structure table
> - * @val:	first part of value to write
> - * @val2:	second part of value to write
> - * @mask:	specifies which values to write
> - *
> - * Return:	the type of value returned by the device
> - */
>  int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>  			       struct iio_chan_spec const *chan,
>  			       int val, int val2, long mask);


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

* Re: [PATCH v4 13/17] iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO
  2019-11-05 22:26 ` [PATCH v4 13/17] iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO Gwendal Grignou
@ 2019-11-10 13:17   ` Jonathan Cameron
  2019-11-14 18:17     ` Gwendal Grignou
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 13:17 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio

On Tue,  5 Nov 2019 14:26:48 -0800
Gwendal Grignou <gwendal@chromium.org> wrote:

> When EC supports FIFO, each IIO device registers a callback, to put
> samples in the buffer when they arrives from the FIFO.
> We can still use a trigger to collect samples, but there may be some
> duplications in the buffer: EC has a single FIFO, so once one sensor is
> using it, all sensors event will be in the FIFO.
> To be sure events generated by cros_ec_sensorhub or the trigger uses the
> same time domain, current_timestamp_clock must be set to "boottime".

How is that enforced?  I'd have no problem with core support for IIO
to allow a driver that has requirements like this to override
the normal flexibility.

> 
> 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>
A question + a comment that I'm still not happy with the fact we have
to protect against a race on startup.  However, we can perhaps make that
a problem for another day.

Assuming the question on the mode mask change is resolved.
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
> Changes in v4:
> - Fix a logic error when the sensor is not "physical", for instance lig
>   angle: core_init() would return !0 even if there was no error.
> - Check patch with --strict option
>     Use sizeof(*obj) instead of sizeof(struct ...obj)
>     Alignement
> Change in v3:
> - Remove double line
> - Fix indentation
> - Add code to support iio clock_id setting. Optimized for
>   CLOCK_BOOTTIME.
> Change in v2 from "Use triggered buffer only when EC does not support
> FIFO":
> - Keep trigger all the time.
> - Add  devm_add_action to cleanup callback registration.
> - EC that "reports" legacy sensors do not have FIFO.
> - Use iiio_is_buffer_enabled instead of checking the scan_mask
>   before sending samples to buffer.
> - Add empty lines for visibility.
> 
>  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  |  9 +-
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 85 ++++++++++++++++++-
>  drivers/iio/light/cros_ec_light_prox.c        |  9 +-
>  drivers/iio/pressure/cros_ec_baro.c           |  9 +-
>  .../linux/iio/common/cros_ec_sensors_core.h   | 10 ++-
>  7 files changed, 103 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
> index 65f85faf6f31..b9f651e4ce99 100644
> --- a/drivers/iio/accel/cros_ec_accel_legacy.c
> +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
> @@ -171,7 +171,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, NULL);
>  	if (ret)
>  		return ret;
>  
> @@ -191,11 +192,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 7dce04473467..62a0dd970988 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -231,7 +231,9 @@ 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;
>  
> @@ -293,11 +295,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 b47da497a3c3..904cd26dd31f 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,17 +84,78 @@ 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;
> +	s64 delta;
> +	unsigned int i;
> +
> +	/*
> +	 * It can happen if we get a samples before the iio device is fully
> +	 * registered.

I'd really like to stop that happening as it seems like a nasty racey corner case.
I'm not convinced there aren't 'half registered' cases we can still hit even
with this protection.  Hardware should be in a clean state before any
interrupts etc are registered.

However if you are sure this protection is enough that can be a job for another day.

I suspect you get away with it because of the next check always failing in
those racey conditions as can't enable the buffer until we have a fully
set up device.

> +	 */
> +	if (!st)
> +		return 0;
> +
> +	/*
> +	 * Ignore samples if the buffer is not set: it is needed if the ODR is
> +	 * set but the buffer is not enabled yet.
> +	 */
> +	if (!iio_buffer_enabled(indio_dev))
> +		return 0;
> +
> +	out = (s16 *)st->samples;
> +	for_each_set_bit(i,
> +			 indio_dev->active_scan_mask,
> +			 indio_dev->masklength) {
> +		*out = data[i];
> +		out++;
> +	}
> +
> +	if (iio_device_get_clock(indio_dev) != CLOCK_BOOTTIME)
> +		delta = iio_get_time_ns(indio_dev) - cros_ec_get_time_ns();
> +	else
> +		delta = 0;
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
> +					   timestamp + delta);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cros_ec_sensors_push_data);
> +
> +static void cros_ec_sensors_core_clean(void *arg)
> +{
> +	struct platform_device *pdev = (struct platform_device *)arg;
> +	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);
> +}
> +
>  /**
>   * cros_ec_sensors_core_init() - basic initialization of the core structure
>   * @pdev:		platform device created for the sensors
>   * @indio_dev:		iio device structure of the device
>   * @physical_device:	true if the device refers to a physical device
> + * @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)
> +			      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);
> @@ -132,8 +194,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;
> -

This particular changes seems odd. Are we dropping access via sysfs?
From what I recall we don't actually do much with this currently in the
IIO core, but we 'might' in future ;)

>  		state->param.cmd = MOTIONSENSE_CMD_INFO;
>  		state->param.info.sensor_num = sensor_platform->sensor_num;
>  		ret = cros_ec_motion_send_host_cmd(state, 0);
> @@ -162,6 +222,25 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  			state->frequencies[2] =
>  			    state->resp->info_3.max_frequency;
>  		}
> +
> +		ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> +						      trigger_capture, NULL);
> +		if (ret)
> +			return ret;
> +
> +		if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> +			ret = cros_ec_sensorhub_register_push_data(sensor_hub,
> +						sensor_platform->sensor_num,
> +						indio_dev, push_data);
> +			if (ret)
> +				return ret;
> +
> +			ret = devm_add_action_or_reset(dev,
> +						cros_ec_sensors_core_clean,
> +						pdev);
> +			if (ret)
> +				return ret;
> +		}
>  	}
>  
>  	return 0;
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index d85a391e50c5..698b2ee81ebf 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -178,7 +178,9 @@ 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;
>  
> @@ -237,11 +239,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 2354302375de..e1c86b22676c 100644
> --- a/drivers/iio/pressure/cros_ec_baro.c
> +++ b/drivers/iio/pressure/cros_ec_baro.c
> @@ -134,7 +134,9 @@ 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 +182,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 0af918978f97..b8f573ca9dcc 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,8 @@ 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
> @@ -87,9 +90,14 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
>  
>  struct platform_device;
>  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);
>  
>  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);
>  
>  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
>  				 u16 opt_length);


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

* Re: [PATCH v4 14/17] iio: cros_ec: Remove pm function
  2019-11-05 22:26 ` [PATCH v4 14/17] iio: cros_ec: Remove pm function Gwendal Grignou
@ 2019-11-10 13:18   ` Jonathan Cameron
  2019-11-11  9:37     ` Enric Balletbo i Serra
  0 siblings, 1 reply; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-10 13:18 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio

On Tue,  5 Nov 2019 14:26:49 -0800
Gwendal Grignou <gwendal@chromium.org> wrote:

> Since cros_ec_sensorhub is shutting down the FIFO when the device
> suspends, no need to slow down the EC sampling period rate.
> It was necesseary to do that before command CMD_FIFO_INT_ENABLE was
> introduced, but now all supported chromebooks have it.
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>

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

> ---
> No changes in v4, v3.
> New in v2.
> 
>  .../cros_ec_sensors/cros_ec_lid_angle.c       |  1 -
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |  1 -
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 47 -------------------
>  drivers/iio/light/cros_ec_light_prox.c        |  1 -
>  .../linux/iio/common/cros_ec_sensors_core.h   |  5 --
>  5 files changed, 55 deletions(-)
> 
> 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 e30a59fcf0f9..af801e203623 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
> @@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids);
>  static struct platform_driver cros_ec_lid_angle_platform_driver = {
>  	.driver = {
>  		.name	= DRV_NAME,
> -		.pm	= &cros_ec_sensors_pm_ops,
>  	},
>  	.probe		= cros_ec_lid_angle_probe,
>  	.id_table	= cros_ec_lid_angle_ids,
> 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 62a0dd970988..9d0b8ad7a0a5 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -315,7 +315,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
>  static struct platform_driver cros_ec_sensors_platform_driver = {
>  	.driver = {
>  		.name	= "cros-ec-sensors",
> -		.pm	= &cros_ec_sensors_pm_ops,
>  	},
>  	.probe		= cros_ec_sensors_probe,
>  	.id_table	= cros_ec_sensors_ids,
> 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 904cd26dd31f..879b69527cae 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
> @@ -723,52 +723,5 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>  }
>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
>  
> -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);
> -
> -	if (st->curr_sampl_freq == 0)
> -		return 0;
> -
> -	/*
> -	 * If the sensors are sampled at high frequency, we will not be able to
> -	 * 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);
> -	}
> -	return 0;
> -}
> -
> -static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
> -{
> -	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> -	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
> -
> -	if (st->curr_sampl_freq == 0)
> -		return;
> -
> -	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
> -		mutex_lock(&st->cmd_lock);
> -		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
> -		st->param.ec_rate.data = st->curr_sampl_freq;
> -		cros_ec_motion_send_host_cmd(st, 0);
> -		mutex_unlock(&st->cmd_lock);
> -	}
> -}
> -
> -const struct dev_pm_ops cros_ec_sensors_pm_ops = {
> -#ifdef CONFIG_PM_SLEEP
> -	.prepare = cros_ec_sensors_prepare,
> -	.complete = cros_ec_sensors_complete
> -#endif
> -};
> -EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
> -
>  MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
>  MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index 698b2ee81ebf..ccdc6d8958c6 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -256,7 +256,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
>  static struct platform_driver cros_ec_light_prox_platform_driver = {
>  	.driver = {
>  		.name	= "cros-ec-light-prox",
> -		.pm	= &cros_ec_sensors_pm_ops,
>  	},
>  	.probe		= cros_ec_light_prox_probe,
>  	.id_table	= cros_ec_light_prox_ids,
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index b8f573ca9dcc..96ea4551945e 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -30,9 +30,6 @@ enum {
>   */
>  #define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
>  
> -/* Minimum sampling period to use when device is suspending */
> -#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
> -
>  typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
>  
>  /**
> @@ -117,8 +114,6 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>  			       struct iio_chan_spec const *chan,
>  			       int val, int val2, long mask);
>  
> -extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
> -
>  /* List of extended channel specification for all sensors */
>  extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
>  


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

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

On Tue,  5 Nov 2019 14:26:50 -0800
Gwendal Grignou <gwendal@chromium.org> wrote:

> Expose EC minimal interrupt period through buffer/hwfifo_timeout:
> - Maximal timeout is limited to 65s.
> - When timeout for all sensors is set to 0, EC will not send events,
>   even if the sensor sampling rate is greater than 0.
> 
> Rename frequency to sampling_frequency to match IIO ABI.
Would have preferred this as two patches, but not that important as one
is really simple.

> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
One note on something else to tidy up inline (not relevant to this patch)
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
> Changes in v4:
> - Check patch with --strict option
>     Alignement
> No changes in v3.
> Changes in v2:
> - Register fifo_attributes in sensors drivers that previously advertise
>   that feature.
> 
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |  4 +-
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 95 ++++++++++++++-----
>  drivers/iio/light/cros_ec_light_prox.c        |  5 +-
>  drivers/iio/pressure/cros_ec_baro.c           |  5 +-
>  .../linux/iio/common/cros_ec_sensors_core.h   |  4 +-
>  5 files changed, 82 insertions(+), 30 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 9d0b8ad7a0a5..6f511f9067d9 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> @@ -237,6 +237,8 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> +	iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
> +
>  	indio_dev->info = &ec_sensors_info;
>  	state = iio_priv(indio_dev);
>  	for (channel = state->channels, i = CROS_EC_SENSOR_X;
> @@ -248,7 +250,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 879b69527cae..62dc1e4aa7a8 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,77 @@ 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;
> +
> +	if (rate > U16_MAX)
> +		rate = U16_MAX;
> +
> +	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);
> +	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);
> +
> +const struct attribute *cros_ec_sensor_fifo_attributes[] = {
> +	&iio_dev_attr_hwfifo_timeout.dev_attr.attr,
> +	NULL,
> +};
> +EXPORT_SYMBOL_GPL(cros_ec_sensor_fifo_attributes);
> +
>  int cros_ec_sensors_push_data(struct iio_dev *indio_dev,
>  			      s16 *data,
>  			      s64 timestamp)
> @@ -616,18 +688,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;
> @@ -697,7 +757,7 @@ 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;
>  
> @@ -706,15 +766,6 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>  
>  		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;
>  		break;
> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> index ccdc6d8958c6..863d01994aae 100644
> --- a/drivers/iio/light/cros_ec_light_prox.c
> +++ b/drivers/iio/light/cros_ec_light_prox.c
> @@ -184,6 +184,8 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> +	iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
> +
>  	indio_dev->info = &cros_ec_light_prox_info;
>  	state = iio_priv(indio_dev);
>  	state->core.type = state->core.resp->info.type;
> @@ -192,8 +194,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 e1c86b22676c..0dee943f955e 100644
> --- a/drivers/iio/pressure/cros_ec_baro.c
> +++ b/drivers/iio/pressure/cros_ec_baro.c
> @@ -140,6 +140,8 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> +	iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
> +
>  	indio_dev->info = &cros_ec_baro_info;
>  	state = iio_priv(indio_dev);
>  	state->core.type = state->core.resp->info.type;
> @@ -149,8 +151,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;
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index 96ea4551945e..5b0acc14c891 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -50,7 +50,6 @@ 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

Side note. Seems that 'frequencies' is not documented... 

>   */
>  struct cros_ec_sensors_core_state {
>  	struct cros_ec_device *ec;
> @@ -73,8 +72,6 @@ struct cros_ec_sensors_core_state {
>  	int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
>  				    unsigned long scan_mask, s16 *data);
>  
> -	int curr_sampl_freq;
> -
>  	/* Table of known available frequencies : 0, Min and Max in mHz */
>  	int frequencies[3];
>  };
> @@ -116,5 +113,6 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>  
>  /* List of extended channel specification for all sensors */
>  extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
> +extern const struct attribute *cros_ec_sensor_fifo_attributes[];
>  
>  #endif  /* __CROS_EC_SENSORS_CORE_H */


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

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

On Tue,  5 Nov 2019 14:26:51 -0800
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>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
> Changes in v4:
> - Check patch with --strict option
>     Alignement
> No changes in v3.
> Changes in v2:
> - Remove double lines, add line before return for visibility.
> 
>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 33 +++++++++++++++++--
>  .../linux/iio/common/cros_ec_sensors_core.h   |  3 ++
>  2 files changed, 34 insertions(+), 2 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 62dc1e4aa7a8..4169c6c055d8 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:
> @@ -150,8 +163,21 @@ 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);
> +
>  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,
>  };
>  EXPORT_SYMBOL_GPL(cros_ec_sensor_fifo_attributes);
> @@ -287,12 +313,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;
>  		}
>  
>  		ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
> index 5b0acc14c891..bc26ae2e3272 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -50,6 +50,7 @@ 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
> + * @fifo_max_event_count:	Size of the EC sensor FIFO
>   */
>  struct cros_ec_sensors_core_state {
>  	struct cros_ec_device *ec;
> @@ -72,6 +73,8 @@ struct cros_ec_sensors_core_state {
>  	int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
>  				    unsigned long scan_mask, s16 *data);
>  
> +	u32 fifo_max_event_count;
> +
>  	/* Table of known available frequencies : 0, Min and Max in mHz */
>  	int frequencies[3];
>  };


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

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

On Tue,  5 Nov 2019 14:26:52 -0800
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 to fix.  Just to check, is this going to cause you problems if it's
marked for stable?

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

I've given tags for patches partly to save me time on next version but
also on the assumption this may well go via someone else's tree.

Realistically it's unlikely it will make it in for the merge window
via IIO and may be similar via other possible paths.

Thanks,

Jonathan

> ---
> Changes in v4:
> - Check patch with --strict option
>     Alignement
> No changes in v3.
> No changes in v2.
> 
>  .../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 4169c6c055d8..f91685119cb0 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
> @@ -261,6 +261,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>  	struct cros_ec_dev *ec = sensor_hub->ec;
>  	struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
>  	u32 ver_mask;
> +	int frequencies[ARRAY_SIZE(state->frequencies) / 2] = { 0 };
>  	int ret, i;
>  
>  	platform_set_drvdata(pdev, indio_dev);
> @@ -309,20 +310,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;
> +		}
>  
>  		ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
>  						      trigger_capture, NULL);
> @@ -713,7 +716,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:
> @@ -725,8 +728,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;
> @@ -761,7 +766,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;
>  	}
>  
> @@ -783,12 +788,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 bc26ae2e3272..7bc961defa87 100644
> --- a/include/linux/iio/common/cros_ec_sensors_core.h
> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
> @@ -51,6 +51,8 @@ typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
>   *				is always 8-byte aligned.
>   * @read_ec_sensors_data:	function used for accessing sensors values
>   * @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;
> @@ -74,9 +76,7 @@ struct cros_ec_sensors_core_state {
>  				    unsigned long scan_mask, s16 *data);
>  
>  	u32 fifo_max_event_count;
> -
> -	/* Table of known available frequencies : 0, Min and Max in mHz */
> -	int frequencies[3];
> +	int frequencies[6];
>  };
>  
>  int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,


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

* Re: [PATCH v4 07/17] Revert "Input: cros_ec_keyb - add back missing mask for event_type"
  2019-11-05 22:26 ` [PATCH v4 07/17] Revert "Input: cros_ec_keyb - add back missing mask for event_type" Gwendal Grignou
@ 2019-11-11  9:20   ` Enric Balletbo i Serra
  2019-11-11 19:23     ` Dmitry Torokhov
  0 siblings, 1 reply; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:20 UTC (permalink / raw)
  To: Gwendal Grignou, briannorris, jic23, knaack.h, lars, pmeerw,
	lee.jones, bleung, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, linux-input, Dmitry Torokhov

Hi,

cc'in Dmitry and linux-input list which is missing

On 5/11/19 23:26, Gwendal Grignou wrote:
> This reverts commit 62c3801619e16b68a37ea899b76572145dfe41c9.
> 
> This patch is not needed anymore since we clear EC_MKBP_HAS_MORE_EVENTS
> flag before calling the notifiers in patch
> "9d9518f5b52a (platform: chrome: cros_ec: handle MKBP more events flag)"
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> ---
> No changes in v4, v3.
> New to v2.
> 
>  drivers/input/keyboard/cros_ec_keyb.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
> index 8d4d9786cc74..a29e81fdf186 100644
> --- a/drivers/input/keyboard/cros_ec_keyb.c
> +++ b/drivers/input/keyboard/cros_ec_keyb.c
> @@ -226,8 +226,6 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
>  {
>  	struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb,
>  						  notifier);
> -	uint8_t mkbp_event_type = ckdev->ec->event_data.event_type &
> -				  EC_MKBP_EVENT_TYPE_MASK;
>  	u32 val;
>  	unsigned int ev_type;
>  
> @@ -239,7 +237,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
>  	if (queued_during_suspend && !device_may_wakeup(ckdev->dev))
>  		return NOTIFY_OK;
>  
> -	switch (mkbp_event_type) {
> +	switch (ckdev->ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK) {
>  	case EC_MKBP_EVENT_KEY_MATRIX:
>  		pm_wakeup_event(ckdev->dev, 0);
>  
> @@ -266,7 +264,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
>  	case EC_MKBP_EVENT_SWITCH:
>  		pm_wakeup_event(ckdev->dev, 0);
>  
> -		if (mkbp_event_type == EC_MKBP_EVENT_BUTTON) {
> +		if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {
>  			val = get_unaligned_le32(
>  					&ckdev->ec->event_data.data.buttons);
>  			ev_type = EV_KEY;
> 

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

* Re: [PATCH v4 08/17] Revert "Input: cros_ec_keyb: mask out extra flags in event_type"
  2019-11-05 22:26 ` [PATCH v4 08/17] Revert "Input: cros_ec_keyb: mask out extra flags in event_type" Gwendal Grignou
@ 2019-11-11  9:20   ` Enric Balletbo i Serra
  2019-11-11 19:23     ` Dmitry Torokhov
  0 siblings, 1 reply; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:20 UTC (permalink / raw)
  To: Gwendal Grignou, briannorris, jic23, knaack.h, lars, pmeerw,
	lee.jones, bleung, dianders, groeck, fabien.lahoudere
  Cc: linux-kernel, linux-iio, linux-input, Dmitry Torokhov

Hi,

cc'in Dmitry and linux-input list which is missing

On 5/11/19 23:26, Gwendal Grignou wrote:
> This reverts commit d096aa3eb6045a6a475a0239f3471c59eedf3d61.
> 
> This patch is not needed anymore since we clear EC_MKBP_HAS_MORE_EVENTS
> flag before calling the notifiers in patch
> "9d9518f5b52a (platform: chrome: cros_ec: handle MKBP more events flag)"
> 
> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> ---
> No changes in v4, v3.
> New to v2.
> 
>  drivers/input/keyboard/cros_ec_keyb.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
> index a29e81fdf186..2b71c5a51f90 100644
> --- a/drivers/input/keyboard/cros_ec_keyb.c
> +++ b/drivers/input/keyboard/cros_ec_keyb.c
> @@ -237,7 +237,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
>  	if (queued_during_suspend && !device_may_wakeup(ckdev->dev))
>  		return NOTIFY_OK;
>  
> -	switch (ckdev->ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK) {
> +	switch (ckdev->ec->event_data.event_type) {
>  	case EC_MKBP_EVENT_KEY_MATRIX:
>  		pm_wakeup_event(ckdev->dev, 0);
>  
> 

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

* Re: [PATCH v4 02/17] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-11-10 12:10   ` Jonathan Cameron
@ 2019-11-11  9:24     ` Enric Balletbo i Serra
  2019-11-11 11:55       ` Jonathan Cameron
  0 siblings, 1 reply; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:24 UTC (permalink / raw)
  To: Jonathan Cameron, Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung, dianders,
	groeck, fabien.lahoudere, linux-kernel, linux-iio

Hi,

On 10/11/19 13:10, Jonathan Cameron wrote:
> On Tue,  5 Nov 2019 14:26:37 -0800
> 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>
> 
> Random suggestion for a possible cleanup...
> 
> Would a devm_platform_device_register_data make sense?  Drops a
> fair bit of boilerplate in here.  If its not a common enough
> pattern, could use the devm_add_action_or_reset route
> to do the same thing.
> 

I don't think devm_platform_device_register exists, exist?

After solving the changes pointed by Jonathan the patch looks good to me.

> I would suggest this as a possible future element, but you
> have some other issues around that area that this would cleanup
> nicely for you.  See inline.
> 
> 
> Thanks,
> 
> Jonathan
> 
> 
> 
>> ---
>> Changes in v4:
>> - Use platform_device_register_data in children registration.
>> - Free registered pdev children at remove time.
>> - Remove useless includes
>> - Check patch with --strict option
>>     Use sizeof(*obj) instead of sizeof(struct ...obj)
>>     Alignement
>> - Describe cros_ec_sensorhub in kernel-doc format.
>> Changes in v3:
>> - Fix doxygen comments
>> - Fix use of ret |=
>> - Remove unncessary goto.
>> Changes in v2:
>> - Remove unerelated changes.
>> - Fix spelling.
>> - Use !x instead of x == NULL
>> - Use platform_ API directly to register IIO sensors from
>>   cros_ec_sensorhub.
>>
>>  drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
>>  drivers/platform/chrome/Kconfig               |  12 +
>>  drivers/platform/chrome/Makefile              |   1 +
>>  drivers/platform/chrome/cros_ec_sensorhub.c   | 223 ++++++++++++++++++
>>  .../linux/platform_data/cros_ec_sensorhub.h   |  33 +++
>>  5 files changed, 270 insertions(+), 1 deletion(-)
>>  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..56a25317a6be 100644
>> --- a/drivers/platform/chrome/Kconfig
>> +++ b/drivers/platform/chrome/Kconfig
>> @@ -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 Sensor Hub"
>> +	depends on CROS_EC && IIO
> 
> Could relax the IIO dependency I think...  Get you more build coverage.
> 
>> +	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 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..6a0aa84cf092
>> --- /dev/null
>> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
>> @@ -0,0 +1,223 @@
>> +// 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/module.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_data/cros_ec_sensorhub.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +#define DRV_NAME		"cros-ec-sensorhub"
>> +
>> +static struct platform_device *cros_ec_sensorhub_allocate_single_sensor(
>> +		struct device *parent,
>> +		char *sensor_name,
>> +		int sensor_num)
>> +{
>> +	struct cros_ec_sensor_platform sensor_platforms = {
>> +		.sensor_num = sensor_num,
>> +	};
>> +
>> +	return platform_device_register_data(parent, sensor_name,
>> +				PLATFORM_DEVID_AUTO,
>> +				&sensor_platforms,
>> +				sizeof(sensor_platforms));
>> +}
>> +
>> +static int cros_ec_sensorhub_register(struct device *dev,
>> +				      struct cros_ec_sensorhub *sensorhub)
> 
> As noted below, I'd be happier if this function did it's own cleanup on
> error rather than leaving that for the caller.
> 
>> +{
>> +	int ret, i, id, sensor_num;
>> +	struct cros_ec_dev *ec = sensorhub->ec;
>> +	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 platform_device *pdev;
>> +	char *name;
>> +
>> +	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;
>> +	}
>> +
>> +	/* Prepare a message to send INFO command to each sensor. */
>> +	msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
>> +		      GFP_KERNEL);
>> +	if (!msg)
>> +		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;
>> +	resp = (struct ec_response_motion_sense *)msg->data;
>> +
>> +	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:
>> +			name = "cros-ec-accel";
>> +			break;
>> +		case MOTIONSENSE_TYPE_BARO:
>> +			name = "cros-ec-baro";
>> +			break;
>> +		case MOTIONSENSE_TYPE_GYRO:
>> +			name = "cros-ec-gyro";
>> +			break;
>> +		case MOTIONSENSE_TYPE_MAG:
>> +			name = "cros-ec-mag";
>> +			break;
>> +		case MOTIONSENSE_TYPE_PROX:
>> +			name = "cros-ec-prox";
>> +			break;
>> +		case MOTIONSENSE_TYPE_LIGHT:
>> +			name = "cros-ec-light";
>> +			break;
>> +		case MOTIONSENSE_TYPE_ACTIVITY:
>> +			name = "cros-ec-activity";
>> +			break;
>> +		default:
>> +			dev_warn(dev, "unknown type %d\n", resp->info.type);
>> +			continue;
>> +		}
>> +		pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
>> +		if (IS_ERR(pdev)) {
>> +			ret = IS_ERR(pdev);
>> +			goto error;
>> +		}
>> +		sensorhub->sensor_pdev[id++] = pdev;
>> +		sensor_type[resp->info.type]++;
>> +	}
>> +
>> +	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)) {
>> +		pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
>> +							"cros-ec-lid-angle", 0);
>> +		if (IS_ERR(pdev)) {
>> +			ret = IS_ERR(pdev);
>> +			goto error;
>> +		}
>> +		sensorhub->sensor_pdev[id++] = pdev;
>> +	}
>> +
>> +error:
>> +	kfree(msg);
>> +	return ret;
>> +}
>> +
>> +static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
>> +{
>> +	struct device *dev = &sensorhub_pdev->dev;
>> +	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
>> +	int ret, i;
>> +	struct platform_device *pdev;
>> +	struct cros_ec_sensorhub *data =
>> +		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
> 
> Do we free this anywhere?  Could just use devm_kzalloc to do it
> automatically.
> 
>> +
>> +	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_sensorhub_register(dev, data);
>> +		if (ret) {
>> +			dev_err(dev, "Register failed %d\n", ret);
>> +			goto unregister_sensors;
> 
> From a code structure point of view, cros_ec_sensorhub_register
> should have done any cleanup necessary if it returns an error.  Hence
> we should be fine doing a direct return here (other than the memory
> not being freed comment above.
> 
> This may seem an overly restrictive request, but that sort of rule
> makes code a lot easier to review as we don't have to go look
> to see where the error handling occurs and check for multiple paths
> etc.  Note that if you use managed functions then there is no
> cleanup to do anyway ;)
> 
>> +		}
>> +	} else {
>> +		/*
>> +		 * If the device has sensors but does not claim to
>> +		 * be a sensor hub, we are in legacy mode.
>> +		 */
>> +		for (i = 0; i < 2; i++) {
>> +			pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
>> +						"cros-ec-accel-legacy", i);
>> +			if (IS_ERR(pdev)) {
>> +				ret = IS_ERR(pdev);
>> +				dev_err(dev, "Legacy %d failed %d\n", i, ret);
>> +				goto unregister_sensors;
>> +			} else {
>> +				data->sensor_pdev[i] = pdev;
>> +			}
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +unregister_sensors:
>> +	/*
>> +	 * Given the probe has failed, we need to unregister all the sensors,
>> +	 * not jutst the one that did not work: this device will be
>> +	 * de-allocated.
>> +	 */
>> +	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
>> +		pdev = data->sensor_pdev[i];
>> +		if (pdev)
>> +			platform_device_unregister(pdev);
>> +	}
>> +	return ret;
>> +}
>> +
>> +static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
>> +{
>> +	struct cros_ec_sensorhub *sensorhub =
>> +		platform_get_drvdata(sensorhub_pdev);
>> +	struct platform_device *pdev;
>> +	int i;
>> +
>> +	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
>> +		pdev = sensorhub->sensor_pdev[i];
>> +		if (pdev)
>> +			platform_device_unregister(pdev);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver cros_ec_sensorhub_driver = {
>> +	.driver = {
>> +		.name = DRV_NAME,
>> +	},
>> +	.probe = cros_ec_sensorhub_probe,
>> +	.remove = cros_ec_sensorhub_remove,
>> +};
>> +
>> +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..da0ba1d201e4
>> --- /dev/null
>> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
>> @@ -0,0 +1,33 @@
>> +/* 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>
>> +
>> +/* Maximal number of sensors supported by the EC. */
>> +#define CROS_EC_SENSOR_MAX 16
>> +
>> +/*
>> + * Maximal number of sensors supported by the hub:
>> + * We add one for the lid angle inclinometer sensor.
>> + */
>> +#define CROS_EC_SENSOR_PDEV_MAX (CROS_EC_SENSOR_MAX + 1)
>> +
>> +/**
>> + * struct cros_ec_sensorhub - Sensor Hub device data.
>> + *
>> + * @ec:           Embedded Controller where the hub is located.
>> + * @sensor_pdev:  Array of platform_device, one per sensor.
>> + */
>> +struct cros_ec_sensorhub {
>> +	struct cros_ec_dev *ec;
>> +	struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
>> +};
>> +
>> +#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
> 

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

* Re: [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub
  2019-11-10 12:13   ` Jonathan Cameron
@ 2019-11-11  9:25     ` Enric Balletbo i Serra
  0 siblings, 0 replies; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:25 UTC (permalink / raw)
  To: Jonathan Cameron, Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung, dianders,
	groeck, fabien.lahoudere, linux-kernel, linux-iio

Hi,

On 10/11/19 13:13, Jonathan Cameron wrote:
> On Tue,  5 Nov 2019 14:26:38 -0800
> Gwendal Grignou <gwendal@chromium.org> wrote:
> 
>> - 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>
> Looks good to me.
> 
> Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Thanks,
 Enric
> 
>> ---
>> No changes in v4, v3.
>> Changes in v2:
>> - Remove unerelated changes.
>> - Remove ec presence test in iio driver, done in cros_ec_sensorhub.
>>
>>  drivers/iio/accel/cros_ec_accel_legacy.c      |   6 -
>>  .../common/cros_ec_sensors/cros_ec_sensors.c  |   6 -
>>  .../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 ++----------------
>>  include/linux/platform_data/cros_ec_proto.h   |   8 -
>>  .../linux/platform_data/cros_ec_sensorhub.h   |   8 +
>>  7 files changed, 23 insertions(+), 218 deletions(-)
>>
>> diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
>> index fcc3f999e482..65f85faf6f31 100644
>> --- a/drivers/iio/accel/cros_ec_accel_legacy.c
>> +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
>> @@ -163,16 +163,10 @@ 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 iio_dev *indio_dev;
>>  	struct cros_ec_sensors_core_state *state;
>>  	int ret;
>>  
>> -	if (!ec || !ec->ec_dev) {
>> -		dev_warn(&pdev->dev, "No EC device found.\n");
>> -		return -EINVAL;
>> -	}
>> -
>>  	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
>>  	if (!indio_dev)
>>  		return -ENOMEM;
>> 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..7dce04473467 100644
>> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
>> @@ -222,17 +222,11 @@ 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 iio_dev *indio_dev;
>>  	struct cros_ec_sensors_state *state;
>>  	struct iio_chan_spec *channel;
>>  	int ret, i;
>>  
>> -	if (!ec_dev || !ec_dev->ec_dev) {
>> -		dev_warn(&pdev->dev, "No CROS EC device found.\n");
>> -		return -EINVAL;
>> -	}
>> -
>>  	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
>>  	if (!indio_dev)
>>  		return -ENOMEM;
>> 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..d85a391e50c5 100644
>> --- a/drivers/iio/light/cros_ec_light_prox.c
>> +++ b/drivers/iio/light/cros_ec_light_prox.c
>> @@ -169,17 +169,11 @@ 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 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) {
>> -		dev_warn(dev, "No CROS EC device found.\n");
>> -		return -EINVAL;
>> -	}
>> -
>>  	indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
>>  	if (!indio_dev)
>>  		return -ENOMEM;
>> diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
>> index a35104e35cb4..c4b977a5dd96 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", },
>> @@ -117,192 +121,6 @@ static void cros_ec_class_release(struct device *dev)
>>  	kfree(to_cros_ec_dev(dev));
>>  }
>>  
>> -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;
>> @@ -358,11 +176,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/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
>> index f3de0662135d..691f9e953a96 100644
>> --- a/include/linux/platform_data/cros_ec_proto.h
>> +++ b/include/linux/platform_data/cros_ec_proto.h
>> @@ -168,14 +168,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 da0ba1d201e4..2b5a4d81f65f 100644
>> --- a/include/linux/platform_data/cros_ec_sensorhub.h
>> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
>> @@ -19,6 +19,14 @@
>>   */
>>  #define CROS_EC_SENSOR_PDEV_MAX (CROS_EC_SENSOR_MAX + 1)
>>  
>> +/**
>> + * 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] 52+ messages in thread

* Re: [PATCH v4 04/17] platform: chrome: cros-ec: record event timestamp in the hard irq
  2019-11-10 12:16   ` Jonathan Cameron
@ 2019-11-11  9:27     ` Enric Balletbo i Serra
  0 siblings, 0 replies; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:27 UTC (permalink / raw)
  To: Jonathan Cameron, Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung, dianders,
	groeck, fabien.lahoudere, linux-kernel, linux-iio



On 10/11/19 13:16, Jonathan Cameron wrote:
> On Tue,  5 Nov 2019 14:26:39 -0800
> 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>
> A minor issue with kernel-doc (I think...)
> 
> Otherwise,
> Acked-by: Jonathan Cameron <Jonathan.Cameron@kernel.org>

With the Jonathan's comment solved.

Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Thanks,
 Enric

>> ---
>> Changes in v4:
>>   Check patch with --strict option
>>     Alignement
>> No changes in v3.
>> Changes in v2:
>>   Make cros_ec_get_time_ns inline.
>>   Using ktime_t instead of s64 when dealing with time.
>>   Added code in ishtp to gather timestamp.
>>
>>  drivers/platform/chrome/cros_ec.c           | 17 ++++++++++++++---
>>  drivers/platform/chrome/cros_ec_ishtp.c     | 17 +++++++++++++++--
>>  drivers/platform/chrome/cros_ec_lpc.c       |  2 ++
>>  include/linux/platform_data/cros_ec_proto.h | 16 ++++++++++++++++
>>  4 files changed, 47 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
>> index 9b2d07422e17..925f84dbf621 100644
>> --- a/drivers/platform/chrome/cros_ec.c
>> +++ b/drivers/platform/chrome/cros_ec.c
>> @@ -31,6 +31,15 @@ static struct cros_ec_platform pd_p = {
>>  	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
>>  };
>>  
>> +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;
>> @@ -141,9 +150,11 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
>>  	}
>>  
>>  	if (ec_dev->irq) {
>> -		err = devm_request_threaded_irq(dev, ec_dev->irq, NULL,
>> -				ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>> -				"chromeos-ec", ec_dev);
>> +		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) {
>>  			dev_err(dev, "Failed to request IRQ %d: %d",
>>  				ec_dev->irq, err);
>> diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
>> index 25ca2c894b4d..5c848f22b44b 100644
>> --- a/drivers/platform/chrome/cros_ec_ishtp.c
>> +++ b/drivers/platform/chrome/cros_ec_ishtp.c
>> @@ -200,13 +200,14 @@ static int ish_send(struct ishtp_cl_data *client_data,
>>   * process_recv() - Received and parse incoming packet
>>   * @cros_ish_cl: Client instance to get stats
>>   * @rb_in_proc: Host interface message buffer
>> + * @timestamp: Timestamp of when parent callback started
>>   *
>>   * Parse the incoming packet. If it is a response packet then it will
>>   * update per instance flags and wake up the caller waiting to for the
>>   * response. If it is an event packet then it will schedule event work.
>>   */
>>  static void process_recv(struct ishtp_cl *cros_ish_cl,
>> -			 struct ishtp_cl_rb *rb_in_proc)
>> +			 struct ishtp_cl_rb *rb_in_proc, ktime_t timestamp)
>>  {
>>  	size_t data_len = rb_in_proc->buf_idx;
>>  	struct ishtp_cl_data *client_data =
>> @@ -295,6 +296,11 @@ static void process_recv(struct ishtp_cl *cros_ish_cl,
>>  		break;
>>  
>>  	case CROS_MKBP_EVENT:
>> +		/*
>> +		 * Set timestamp from beginning of function since we actually
>> +		 * got an incoming MKBP event
>> +		 */
>> +		client_data->ec_dev->last_event_time = timestamp;
>>  		/* The event system doesn't send any data in buffer */
>>  		schedule_work(&client_data->work_ec_evt);
>>  
>> @@ -322,10 +328,17 @@ static void ish_event_cb(struct ishtp_cl_device *cl_device)
>>  {
>>  	struct ishtp_cl_rb *rb_in_proc;
>>  	struct ishtp_cl	*cros_ish_cl = ishtp_get_drvdata(cl_device);
>> +	ktime_t timestamp;
>> +
>> +	/*
>> +	 * Take timestamp as close to hardware interrupt as possible for sensor
>> +	 * timestamps.
>> +	 */
>> +	timestamp = cros_ec_get_time_ns();
>>  
>>  	while ((rb_in_proc = ishtp_cl_rx_get_rb(cros_ish_cl)) != NULL) {
>>  		/* Decide what to do with received data */
>> -		process_recv(cros_ish_cl, rb_in_proc);
>> +		process_recv(cros_ish_cl, rb_in_proc, timestamp);
>>  	}
>>  }
>>  
>> 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 691f9e953a96..b183024fef1f 100644
>> --- a/include/linux/platform_data/cros_ec_proto.h
>> +++ b/include/linux/platform_data/cros_ec_proto.h
>> @@ -122,6 +122,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
>> @@ -162,6 +164,7 @@ struct cros_ec_device {
>>  	int event_size;
>>  	u32 host_event_wake_mask;
>>  	u32 last_resume_result;
>> +	ktime_t last_event_time;
>>  
>>  	/* The platform devices used by the mfd driver */
>>  	struct platform_device *ec;
>> @@ -210,4 +213,17 @@ int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
>>  
>>  int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
>>  
>> +/**
>> + * cros_ec_get_time_ns - Return time in ns.
> 
> Fairly sure that's not correct kernel-doc.  Please run
> ../scripts/kernel-doc over the files and fix the warnings.
> 
>  * 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.
>> + *
>> + * Return: ktime_t format since boot.
>> + */
>> +static inline ktime_t cros_ec_get_time_ns(void)
>> +{
>> +	return ktime_get_boottime_ns();
>> +}
>> +
>>  #endif /* __LINUX_CROS_EC_PROTO_H */
> 

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

* Re: [PATCH v4 05/17] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number
  2019-11-10 12:17   ` Jonathan Cameron
@ 2019-11-11  9:29     ` Enric Balletbo i Serra
  2019-11-14  0:58       ` Gwendal Grignou
  0 siblings, 1 reply; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:29 UTC (permalink / raw)
  To: Jonathan Cameron, Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung, dianders,
	groeck, fabien.lahoudere, linux-kernel, linux-iio,
	Enrico Granata



On 10/11/19 13:17, Jonathan Cameron wrote:
> On Tue,  5 Nov 2019 14:26:40 -0800
> Gwendal Grignou <gwendal@chromium.org> wrote:
> 
>> 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@chromium.org>
>> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> 
> This strikes me as something that could be a potential fix to backport?
> Any known cases of a negative irq getting to here or is this a by
> inspection thing?
> 
> Otherwise seems obviously correct.
> Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 

Looks good to me:

Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Thanks,
 Enric

> Jonathan
> 
>> ---
>> No changes in v4, v3.
>> Changes in v2:
>>   Remove dual Enrico's signature.
>>
>>  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 925f84dbf621..d3dfa27171e6 100644
>> --- a/drivers/platform/chrome/cros_ec.c
>> +++ b/drivers/platform/chrome/cros_ec.c
>> @@ -149,7 +149,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,
> 

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

* Re: [PATCH v4 06/17] platform: chrome: cros_ec: handle MKBP more events flag
  2019-11-10 12:28   ` Jonathan Cameron
@ 2019-11-11  9:30     ` Enric Balletbo i Serra
  0 siblings, 0 replies; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:30 UTC (permalink / raw)
  To: Jonathan Cameron, Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung, dianders,
	groeck, fabien.lahoudere, linux-kernel, linux-iio,
	Enrico Granata



On 10/11/19 13:28, Jonathan Cameron wrote:
> On Tue,  5 Nov 2019 14:26:41 -0800
> 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 (Matrix KeyBoard Protocol)
>> 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>
> A couple of trivial things inline.  
> I just checked with the script and it doesn't seem to warn on the ()
> but the documentation for kernel-doc suggests it should be there...
> 
> I guess things are more relaxed than I though.. Fix them if you are
> doing another spin perhaps but don't bother otherwise.
> 
> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 

Looks good to me, please fix the above comments and:

Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Thanks,
 Enric


>> ---
>> Changes in v4:
>> - Check patch with --strict option
>>     Alignement
>> Changes in v3:
>>   Fix indentation.
>> Changes in v2:
>>   Process flag inside cros_ec_get_next_event, clean flag from event.
>>   Introduce public function cros_ec_handle_event(), use it in rpmsg and
>>     ishtp transport layer.
>>   Remplace dev_info with dev_dbg, call only once.
>>
>>  drivers/platform/chrome/cros_ec.c           | 34 +++++++--
>>  drivers/platform/chrome/cros_ec_ishtp.c     |  8 +-
>>  drivers/platform/chrome/cros_ec_lpc.c       | 15 +++-
>>  drivers/platform/chrome/cros_ec_proto.c     | 81 +++++++++++++--------
>>  drivers/platform/chrome/cros_ec_rpmsg.c     | 19 +----
>>  include/linux/platform_data/cros_ec_proto.h | 12 ++-
>>  6 files changed, 107 insertions(+), 62 deletions(-)
>>
>> diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
>> index d3dfa27171e6..c9f0b9ebcbc1 100644
>> --- a/drivers/platform/chrome/cros_ec.c
>> +++ b/drivers/platform/chrome/cros_ec.c
>> @@ -40,13 +40,23 @@ static irqreturn_t ec_irq_handler(int irq, void *data)
>>  	return IRQ_WAKE_THREAD;
>>  }
>>  
>> -static irqreturn_t ec_irq_thread(int irq, void *data)
>> +/**
>> + * cros_ec_handle_event - process and forward pending events on EC
> 
> cros_ec_handle_event() - 
> 
>> + * @ec_dev: Device with events to process.
>> + *
>> + * Call this function in a loop when the kernel is notified that the EC has
>> + * pending events.
>> + *
>> + * Return: true if more events are still pending and this function should be
>> + * called again.
>> + */
>> +bool cros_ec_handle_event(struct cros_ec_device *ec_dev)
>>  {
>> -	struct cros_ec_device *ec_dev = data;
>> -	bool wake_event = true;
>> +	bool wake_event;
>> +	bool ec_has_more_events;
>>  	int ret;
>>  
>> -	ret = cros_ec_get_next_event(ec_dev, &wake_event);
>> +	ret = cros_ec_get_next_event(ec_dev, &wake_event, &ec_has_more_events);
>>  
>>  	/*
>>  	 * Signal only if wake host events or any interrupt if
>> @@ -59,6 +69,20 @@ static irqreturn_t ec_irq_thread(int irq, void *data)
>>  	if (ret > 0)
>>  		blocking_notifier_call_chain(&ec_dev->event_notifier,
>>  					     0, ec_dev);
>> +
>> +	return ec_has_more_events;
>> +}
>> +EXPORT_SYMBOL(cros_ec_handle_event);
>> +
>> +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 = cros_ec_handle_event(ec_dev);
>> +	} while (ec_has_more_events);
>> +
>>  	return IRQ_HANDLED;
>>  }
>>  
>> @@ -274,7 +298,7 @@ EXPORT_SYMBOL(cros_ec_suspend);
>>  static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev)
>>  {
>>  	while (ec_dev->mkbp_event_supported &&
>> -	       cros_ec_get_next_event(ec_dev, NULL) > 0)
>> +	       cros_ec_get_next_event(ec_dev, NULL, NULL) > 0)
>>  		blocking_notifier_call_chain(&ec_dev->event_notifier,
>>  					     1, ec_dev);
>>  }
>> diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
>> index 5c848f22b44b..e5996821d08b 100644
>> --- a/drivers/platform/chrome/cros_ec_ishtp.c
>> +++ b/drivers/platform/chrome/cros_ec_ishtp.c
>> @@ -136,11 +136,11 @@ static void ish_evt_handler(struct work_struct *work)
>>  	struct ishtp_cl_data *client_data =
>>  		container_of(work, struct ishtp_cl_data, work_ec_evt);
>>  	struct cros_ec_device *ec_dev = client_data->ec_dev;
>> +	bool ec_has_more_events;
>>  
>> -	if (cros_ec_get_next_event(ec_dev, NULL) > 0) {
>> -		blocking_notifier_call_chain(&ec_dev->event_notifier,
>> -					     0, ec_dev);
>> -	}
>> +	do {
>> +		ec_has_more_events = cros_ec_handle_event(ec_dev);
>> +	} while (ec_has_more_events);
>>  }
>>  
>>  /**
>> diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
>> index 3c77496e164d..dccf479c6625 100644
>> --- a/drivers/platform/chrome/cros_ec_lpc.c
>> +++ b/drivers/platform/chrome/cros_ec_lpc.c
>> @@ -312,13 +312,20 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
>>  static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
>>  {
>>  	struct cros_ec_device *ec_dev = data;
>> +	bool ec_has_more_events;
>> +	int ret;
>>  
>>  	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,
>> -					     ec_dev);
>> +	if (ec_dev->mkbp_event_supported)
>> +		do {
>> +			ret = cros_ec_get_next_event(ec_dev, NULL,
>> +						     &ec_has_more_events);
>> +			if (ret > 0)
>> +				blocking_notifier_call_chain(
>> +						&ec_dev->event_notifier, 0,
>> +						ec_dev);
>> +		} while (ec_has_more_events);
>>  
>>  	if (value == ACPI_NOTIFY_DEVICE_WAKE)
>>  		pm_system_wakeup();
>> diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
>> index b502933e911b..03173ca66b1b 100644
>> --- a/drivers/platform/chrome/cros_ec_proto.c
>> +++ b/drivers/platform/chrome/cros_ec_proto.c
>> @@ -456,7 +456,10 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
>>  	if (ret < 0 || ver_mask == 0)
>>  		ec_dev->mkbp_event_supported = 0;
>>  	else
>> -		ec_dev->mkbp_event_supported = 1;
>> +		ec_dev->mkbp_event_supported = fls(ver_mask);
>> +
>> +	dev_dbg(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,
>> @@ -569,6 +572,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;
>> @@ -581,7 +585,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;
>> @@ -589,30 +593,26 @@ 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;
>>  
>> +	memset(msg, 0, sizeof(*msg));
>>  	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)
>> @@ -639,32 +639,55 @@ static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
>>   * @ec_dev: Device to fetch event from.
>>   * @wake_event: Pointer to a bool set to true upon return if the event might be
>>   *              treated as a wake event. Ignored if null.
>> + * @has_more_events: Pointer to bool set to true if more than one event is
>> + *              pending.
>> + *              Some EC will set this flag to indicate cros_ec_get_next_event()
>> + *              can be called multiple times in a row.
>> + *              It is an optimization to prevent issuing a EC command for
>> + *              nothing or wait for another interrupt from the EC to process
>> + *              the next message.
>> + *              Ignored if null.
>>   *
>>   * Return: negative error code on errors; 0 for no data; or else number of
>>   * bytes received (i.e., an event was retrieved successfully). Event types are
>>   * written out to @ec_dev->event_data.event_type on success.
>>   */
>> -int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
>> +int cros_ec_get_next_event(struct cros_ec_device *ec_dev,
>> +			   bool *wake_event,
>> +			   bool *has_more_events)
>>  {
>>  	u8 event_type;
>>  	u32 host_event;
>>  	int ret;
>>  
>> -	if (!ec_dev->mkbp_event_supported) {
>> -		ret = get_keyboard_state_event(ec_dev);
>> -		if (ret <= 0)
>> -			return ret;
>> +	/*
>> +	 * Default value for wake_event.
>> +	 * Wake up on keyboard event, wake up for spurious interrupt or link
>> +	 * error to the EC.
>> +	 */
>> +	if (wake_event)
>> +		*wake_event = true;
>>  
>> -		if (wake_event)
>> -			*wake_event = true;
>> +	/*
>> +	 * Default value for has_more_events.
>> +	 * EC will raise another interrupt if AP does not process all events
>> +	 * anyway.
>> +	 */
>> +	if (has_more_events)
>> +		*has_more_events = false;
>>  
>> -		return ret;
>> -	}
>> +	if (!ec_dev->mkbp_event_supported)
>> +		return get_keyboard_state_event(ec_dev);
>>  
>>  	ret = get_next_event(ec_dev);
>>  	if (ret <= 0)
>>  		return ret;
>>  
>> +	if (has_more_events)
>> +		*has_more_events = ec_dev->event_data.event_type &
>> +			EC_MKBP_HAS_MORE_EVENTS;
>> +	ec_dev->event_data.event_type &= EC_MKBP_EVENT_TYPE_MASK;
>> +
>>  	if (wake_event) {
>>  		event_type = ec_dev->event_data.event_type;
>>  		host_event = cros_ec_get_host_event(ec_dev);
>> @@ -679,11 +702,7 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
>>  		else if (host_event &&
>>  			 !(host_event & ec_dev->host_event_wake_mask))
>>  			*wake_event = false;
>> -		/* Consider all other events as wake events. */
>> -		else
>> -			*wake_event = true;
>>  	}
>> -
> 
> Nitpick. Unrelated whitespace change ;)
> 
>>  	return ret;
>>  }
>>  EXPORT_SYMBOL(cros_ec_get_next_event);
>> diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c
>> index 0c3738c3244d..bd068afe43b5 100644
>> --- a/drivers/platform/chrome/cros_ec_rpmsg.c
>> +++ b/drivers/platform/chrome/cros_ec_rpmsg.c
>> @@ -143,22 +143,11 @@ cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work)
>>  						      struct cros_ec_rpmsg,
>>  						      host_event_work);
>>  	struct cros_ec_device *ec_dev = dev_get_drvdata(&ec_rpmsg->rpdev->dev);
>> -	bool wake_event = true;
>> -	int ret;
>> -
>> -	ret = cros_ec_get_next_event(ec_dev, &wake_event);
>> -
>> -	/*
>> -	 * 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))
>> -		pm_wakeup_event(ec_dev->dev, 0);
>> +	bool ec_has_more_events;
>>  
>> -	if (ret > 0)
>> -		blocking_notifier_call_chain(&ec_dev->event_notifier,
>> -					     0, ec_dev);
>> +	do {
>> +		ec_has_more_events = cros_ec_handle_event(ec_dev);
>> +	} while (ec_has_more_events);
>>  }
>>  
>>  static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
>> diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h
>> index b183024fef1f..e238930ae967 100644
>> --- a/include/linux/platform_data/cros_ec_proto.h
>> +++ b/include/linux/platform_data/cros_ec_proto.h
>> @@ -116,7 +116,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.
>> @@ -156,7 +158,7 @@ struct cros_ec_device {
>>  	int (*pkt_xfer)(struct cros_ec_device *ec,
>>  			struct cros_ec_command *msg);
>>  	struct mutex lock;
>> -	bool mkbp_event_supported;
>> +	u8 mkbp_event_supported;
>>  	bool host_sleep_v1;
>>  	struct blocking_notifier_head event_notifier;
>>  
>> @@ -205,7 +207,9 @@ int cros_ec_unregister(struct cros_ec_device *ec_dev);
>>  
>>  int cros_ec_query_all(struct cros_ec_device *ec_dev);
>>  
>> -int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event);
>> +int cros_ec_get_next_event(struct cros_ec_device *ec_dev,
>> +			   bool *wake_event,
>> +			   bool *has_more_events);
>>  
>>  u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev);
>>  
>> @@ -213,6 +217,8 @@ int cros_ec_check_features(struct cros_ec_dev *ec, int feature);
>>  
>>  int cros_ec_get_sensor_count(struct cros_ec_dev *ec);
>>  
>> +bool cros_ec_handle_event(struct cros_ec_device *ec_dev);
>> +
>>  /**
>>   * cros_ec_get_time_ns - Return time in ns.
>>   *
> 

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

* Re: [PATCH v4 12/17] iio: cros_ec: Move function description to .c file
  2019-11-10 13:08   ` Jonathan Cameron
@ 2019-11-11  9:35     ` Enric Balletbo i Serra
  0 siblings, 0 replies; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:35 UTC (permalink / raw)
  To: Jonathan Cameron, Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung, dianders,
	groeck, fabien.lahoudere, linux-kernel, linux-iio



On 10/11/19 14:08, Jonathan Cameron wrote:
> On Tue,  5 Nov 2019 14:26:47 -0800
> Gwendal Grignou <gwendal@chromium.org> wrote:
> 
>> To prevent comment rot, move function description to
>> cros_ec_sensors_core.c.
>>
>> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> Thanks for tidying this up.
> 
> Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 

Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Thanks,
 Enric

>> ---
>> No changes in v4.
>> Changes in v3:
>>   fix spelling.
>> New in v2.
>>
>>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 69 ++++++++++++++++
>>  .../linux/iio/common/cros_ec_sensors_core.h   | 80 -------------------
>>  2 files changed, 69 insertions(+), 80 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 81a7f692de2f..b47da497a3c3 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
>> @@ -83,6 +83,14 @@ static void get_default_min_max_freq(enum motionsensor_type type,
>>  	}
>>  }
>>  
>> +/**
>> + * cros_ec_sensors_core_init() - basic initialization of the core structure
>> + * @pdev:		platform device created for the sensors
>> + * @indio_dev:		iio device structure of the device
>> + * @physical_device:	true if the device refers to a physical device
>> + *
>> + * Return: 0 on success, -errno on failure.
>> + */
>>  int cros_ec_sensors_core_init(struct platform_device *pdev,
>>  			      struct iio_dev *indio_dev,
>>  			      bool physical_device)
>> @@ -160,6 +168,16 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
>>  }
>>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
>>  
>> +/**
>> + * cros_ec_motion_send_host_cmd() - send motion sense host command
>> + * @state:		pointer to state information for device
>> + * @opt_length:	optional length to reduce the response size, useful on the data
>> + *		path. Otherwise, the maximal allowed response size is used
>> + *
>> + * When called, the sub-command is assumed to be set in param->cmd.
>> + *
>> + * Return: 0 on success, -errno on failure.
>> + */
>>  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
>>  				 u16 opt_length)
>>  {
>> @@ -422,6 +440,14 @@ int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
>>  }
>>  EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
>>  
>> +/**
>> + * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
>> + * @indio_dev:	pointer to IIO device
>> + * @scan_mask:	bitmap of the sensor indices to scan
>> + * @data:	location to store data
>> + *
>> + * Return: 0 on success, -errno on failure.
>> + */
>>  int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
>>  			     unsigned long scan_mask, s16 *data)
>>  {
>> @@ -446,6 +472,18 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
>>  }
>>  EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
>>  
>> +/**
>> + * cros_ec_sensors_capture() - the trigger handler function
>> + * @irq:	the interrupt number.
>> + * @p:		a pointer to the poll function.
>> + *
>> + * On a trigger event occurring, if the pollfunc is attached then this
>> + * handler is called as a threaded interrupt (and hence may sleep). It
>> + * is responsible for grabbing data from the device and pushing it into
>> + * the associated buffer.
>> + *
>> + * Return: IRQ_HANDLED
>> + */
>>  irqreturn_t cros_ec_sensors_capture(int irq, void *p)
>>  {
>>  	struct iio_poll_func *pf = p;
>> @@ -481,6 +519,16 @@ irqreturn_t cros_ec_sensors_capture(int irq, void *p)
>>  }
>>  EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
>>  
>> +/**
>> + * cros_ec_sensors_core_read() - function to request a value from the sensor
>> + * @st:		pointer to state information for device
>> + * @chan:	channel specification structure table
>> + * @val:	will contain one element making up the returned value
>> + * @val2:	will contain another element making up the returned value
>> + * @mask:	specifies which values to be requested
>> + *
>> + * Return:	the type of value returned by the device
>> + */
>>  int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>>  			  struct iio_chan_spec const *chan,
>>  			  int *val, int *val2, long mask)
>> @@ -521,6 +569,17 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>>  }
>>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
>>  
>> +/**
>> + * cros_ec_sensors_core_read_avail() - get available values
>> + * @indio_dev:		pointer to state information for device
>> + * @chan:	channel specification structure table
>> + * @vals:	list of available values
>> + * @type:	type of data returned
>> + * @length:	number of data returned in the array
>> + * @mask:	specifies which values to be requested
>> + *
>> + * Return:	an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
>> + */
>>  int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>>  				    struct iio_chan_spec const *chan,
>>  				    const int **vals,
>> @@ -542,6 +601,16 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>>  }
>>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read_avail);
>>  
>> +/**
>> + * cros_ec_sensors_core_write() - function to write a value to the sensor
>> + * @st:		pointer to state information for device
>> + * @chan:	channel specification structure table
>> + * @val:	first part of value to write
>> + * @val2:	second part of value to write
>> + * @mask:	specifies which values to write
>> + *
>> + * Return:	the type of value returned by the device
>> + */
>>  int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>>  			       struct iio_chan_spec const *chan,
>>  			       int val, int val2, long mask)
>> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
>> index bb331e6356a9..0af918978f97 100644
>> --- a/include/linux/iio/common/cros_ec_sensors_core.h
>> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
>> @@ -79,95 +79,25 @@ struct cros_ec_sensors_core_state {
>>  	int frequencies[3];
>>  };
>>  
>> -/**
>> - * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory
>> - * @indio_dev:	pointer to IIO device
>> - * @scan_mask:	bitmap of the sensor indices to scan
>> - * @data:	location to store data
>> - *
>> - * This is the safe function for reading the EC data. It guarantees that the
>> - * data sampled was not modified by the EC while being read.
>> - *
>> - * Return: 0 on success, -errno on failure.
>> - */
>>  int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
>>  			     s16 *data);
>>  
>> -/**
>> - * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
>> - * @indio_dev:	pointer to IIO device
>> - * @scan_mask:	bitmap of the sensor indices to scan
>> - * @data:	location to store data
>> - *
>> - * Return: 0 on success, -errno on failure.
>> - */
>>  int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
>>  			     s16 *data);
>>  
>>  struct platform_device;
>> -/**
>> - * cros_ec_sensors_core_init() - basic initialization of the core structure
>> - * @pdev:		platform device created for the sensors
>> - * @indio_dev:		iio device structure of the device
>> - * @physical_device:	true if the device refers to a physical device
>> - *
>> - * Return: 0 on success, -errno on failure.
>> - */
>>  int cros_ec_sensors_core_init(struct platform_device *pdev,
>>  			      struct iio_dev *indio_dev, bool physical_device);
>>  
>> -/**
>> - * cros_ec_sensors_capture() - the trigger handler function
>> - * @irq:	the interrupt number.
>> - * @p:		a pointer to the poll function.
>> - *
>> - * On a trigger event occurring, if the pollfunc is attached then this
>> - * handler is called as a threaded interrupt (and hence may sleep). It
>> - * is responsible for grabbing data from the device and pushing it into
>> - * the associated buffer.
>> - *
>> - * Return: IRQ_HANDLED
>> - */
>>  irqreturn_t cros_ec_sensors_capture(int irq, void *p);
>>  
>> -/**
>> - * cros_ec_motion_send_host_cmd() - send motion sense host command
>> - * @st:		pointer to state information for device
>> - * @opt_length:	optional length to reduce the response size, useful on the data
>> - *		path. Otherwise, the maximal allowed response size is used
>> - *
>> - * When called, the sub-command is assumed to be set in param->cmd.
>> - *
>> - * Return: 0 on success, -errno on failure.
>> - */
>>  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
>>  				 u16 opt_length);
>>  
>> -/**
>> - * cros_ec_sensors_core_read() - function to request a value from the sensor
>> - * @st:		pointer to state information for device
>> - * @chan:	channel specification structure table
>> - * @val:	will contain one element making up the returned value
>> - * @val2:	will contain another element making up the returned value
>> - * @mask:	specifies which values to be requested
>> - *
>> - * Return:	the type of value returned by the device
>> - */
>>  int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
>>  			      struct iio_chan_spec const *chan,
>>  			      int *val, int *val2, long mask);
>>  
>> -/**
>> - * cros_ec_sensors_core_read_avail() - get available values
>> - * @indio_dev:		pointer to state information for device
>> - * @chan:	channel specification structure table
>> - * @vals:	list of available values
>> - * @type:	type of data returned
>> - * @length:	number of data returned in the array
>> - * @mask:	specifies which values to be requested
>> - *
>> - * Return:	an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
>> - */
>>  int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>>  				    struct iio_chan_spec const *chan,
>>  				    const int **vals,
>> @@ -175,16 +105,6 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
>>  				    int *length,
>>  				    long mask);
>>  
>> -/**
>> - * cros_ec_sensors_core_write() - function to write a value to the sensor
>> - * @st:		pointer to state information for device
>> - * @chan:	channel specification structure table
>> - * @val:	first part of value to write
>> - * @val2:	second part of value to write
>> - * @mask:	specifies which values to write
>> - *
>> - * Return:	the type of value returned by the device
>> - */
>>  int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>>  			       struct iio_chan_spec const *chan,
>>  			       int val, int val2, long mask);
> 

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

* Re: [PATCH v4 14/17] iio: cros_ec: Remove pm function
  2019-11-10 13:18   ` Jonathan Cameron
@ 2019-11-11  9:37     ` Enric Balletbo i Serra
  0 siblings, 0 replies; 52+ messages in thread
From: Enric Balletbo i Serra @ 2019-11-11  9:37 UTC (permalink / raw)
  To: Jonathan Cameron, Gwendal Grignou
  Cc: briannorris, knaack.h, lars, pmeerw, lee.jones, bleung, dianders,
	groeck, fabien.lahoudere, linux-kernel, linux-iio



On 10/11/19 14:18, Jonathan Cameron wrote:
> On Tue,  5 Nov 2019 14:26:49 -0800
> Gwendal Grignou <gwendal@chromium.org> wrote:
> 
>> Since cros_ec_sensorhub is shutting down the FIFO when the device
>> suspends, no need to slow down the EC sampling period rate.
>> It was necesseary to do that before command CMD_FIFO_INT_ENABLE was
>> introduced, but now all supported chromebooks have it.
>>
>> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> 
> Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 

Also looks good to me

Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Thanks,
 Enric

>> ---
>> No changes in v4, v3.
>> New in v2.
>>
>>  .../cros_ec_sensors/cros_ec_lid_angle.c       |  1 -
>>  .../common/cros_ec_sensors/cros_ec_sensors.c  |  1 -
>>  .../cros_ec_sensors/cros_ec_sensors_core.c    | 47 -------------------
>>  drivers/iio/light/cros_ec_light_prox.c        |  1 -
>>  .../linux/iio/common/cros_ec_sensors_core.h   |  5 --
>>  5 files changed, 55 deletions(-)
>>
>> 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 e30a59fcf0f9..af801e203623 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
>> @@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids);
>>  static struct platform_driver cros_ec_lid_angle_platform_driver = {
>>  	.driver = {
>>  		.name	= DRV_NAME,
>> -		.pm	= &cros_ec_sensors_pm_ops,
>>  	},
>>  	.probe		= cros_ec_lid_angle_probe,
>>  	.id_table	= cros_ec_lid_angle_ids,
>> 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 62a0dd970988..9d0b8ad7a0a5 100644
>> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
>> @@ -315,7 +315,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
>>  static struct platform_driver cros_ec_sensors_platform_driver = {
>>  	.driver = {
>>  		.name	= "cros-ec-sensors",
>> -		.pm	= &cros_ec_sensors_pm_ops,
>>  	},
>>  	.probe		= cros_ec_sensors_probe,
>>  	.id_table	= cros_ec_sensors_ids,
>> 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 904cd26dd31f..879b69527cae 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
>> @@ -723,52 +723,5 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>>  }
>>  EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
>>  
>> -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);
>> -
>> -	if (st->curr_sampl_freq == 0)
>> -		return 0;
>> -
>> -	/*
>> -	 * If the sensors are sampled at high frequency, we will not be able to
>> -	 * 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);
>> -	}
>> -	return 0;
>> -}
>> -
>> -static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
>> -{
>> -	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> -	struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
>> -
>> -	if (st->curr_sampl_freq == 0)
>> -		return;
>> -
>> -	if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
>> -		mutex_lock(&st->cmd_lock);
>> -		st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
>> -		st->param.ec_rate.data = st->curr_sampl_freq;
>> -		cros_ec_motion_send_host_cmd(st, 0);
>> -		mutex_unlock(&st->cmd_lock);
>> -	}
>> -}
>> -
>> -const struct dev_pm_ops cros_ec_sensors_pm_ops = {
>> -#ifdef CONFIG_PM_SLEEP
>> -	.prepare = cros_ec_sensors_prepare,
>> -	.complete = cros_ec_sensors_complete
>> -#endif
>> -};
>> -EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
>> -
>>  MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
>>  MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
>> index 698b2ee81ebf..ccdc6d8958c6 100644
>> --- a/drivers/iio/light/cros_ec_light_prox.c
>> +++ b/drivers/iio/light/cros_ec_light_prox.c
>> @@ -256,7 +256,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
>>  static struct platform_driver cros_ec_light_prox_platform_driver = {
>>  	.driver = {
>>  		.name	= "cros-ec-light-prox",
>> -		.pm	= &cros_ec_sensors_pm_ops,
>>  	},
>>  	.probe		= cros_ec_light_prox_probe,
>>  	.id_table	= cros_ec_light_prox_ids,
>> diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h
>> index b8f573ca9dcc..96ea4551945e 100644
>> --- a/include/linux/iio/common/cros_ec_sensors_core.h
>> +++ b/include/linux/iio/common/cros_ec_sensors_core.h
>> @@ -30,9 +30,6 @@ enum {
>>   */
>>  #define CROS_EC_SAMPLE_SIZE  (sizeof(s64) * 2)
>>  
>> -/* Minimum sampling period to use when device is suspending */
>> -#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000  /* 1 second */
>> -
>>  typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
>>  
>>  /**
>> @@ -117,8 +114,6 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
>>  			       struct iio_chan_spec const *chan,
>>  			       int val, int val2, long mask);
>>  
>> -extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
>> -
>>  /* List of extended channel specification for all sensors */
>>  extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
>>  
> 

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

* Re: [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub
  2019-11-05 22:26 ` [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
  2019-11-10 12:13   ` Jonathan Cameron
@ 2019-11-11 11:43   ` Lee Jones
  1 sibling, 0 replies; 52+ messages in thread
From: Lee Jones @ 2019-11-11 11:43 UTC (permalink / raw)
  To: Gwendal Grignou
  Cc: briannorris, jic23, knaack.h, lars, pmeerw, bleung,
	enric.balletbo, dianders, groeck, fabien.lahoudere, linux-kernel,
	linux-iio

On Tue, 05 Nov 2019, Gwendal Grignou wrote:

> - 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>
> ---
> No changes in v4, v3.
> Changes in v2:
> - Remove unerelated changes.
> - Remove ec presence test in iio driver, done in cros_ec_sensorhub.
> 
>  drivers/iio/accel/cros_ec_accel_legacy.c      |   6 -
>  .../common/cros_ec_sensors/cros_ec_sensors.c  |   6 -
>  .../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 ++----------------

Acked-by: Lee Jones <lee.jones@linaro.org>

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v4 01/17] mfd: cros_ec: Add sensor_count and make check_features public
  2019-11-08 22:03   ` Enric Balletbo Serra
@ 2019-11-11 11:44     ` Lee Jones
  2019-11-16 11:49       ` Jonathan Cameron
  0 siblings, 1 reply; 52+ messages in thread
From: Lee Jones @ 2019-11-11 11:44 UTC (permalink / raw)
  To: Enric Balletbo Serra
  Cc: Gwendal Grignou, Brian Norris, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Benson Leung,
	Enric Balletbo i Serra, Doug Anderson, Guenter Roeck,
	Fabien Lahoudere, linux-kernel, linux-iio

On Fri, 08 Nov 2019, Enric Balletbo Serra wrote:

> Missatge de Gwendal Grignou <gwendal@chromium.org> del dia dt., 5 de
> nov. 2019 a les 23:28:
> >
> > 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 what the Embedded Controller supports.
> >
> > Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> 
> Version 3 was acked and I think we can maintain his ack, so:
> 
> Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> 
> Also,
> 
> Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> 
> Lee, how you would like to handle this? I think will be safe for
> patches 1/2/3 go through the platform-chrome tree without an immutable
> branch. Patch 3 thought still needs and Ack from you if is fine.

Please take the entire set, converting:

  s/Acked-for-MFD-by/Acked-by/

... and send me a pull-request to an immutable branch.

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v4 02/17] platform: cros_ec: Add cros_ec_sensor_hub driver
  2019-11-11  9:24     ` Enric Balletbo i Serra
@ 2019-11-11 11:55       ` Jonathan Cameron
  0 siblings, 0 replies; 52+ messages in thread
From: Jonathan Cameron @ 2019-11-11 11:55 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Jonathan Cameron, Gwendal Grignou, briannorris, knaack.h, lars,
	pmeerw, lee.jones, bleung, dianders, groeck, fabien.lahoudere,
	linux-kernel, linux-iio

On Mon, 11 Nov 2019 10:24:01 +0100
Enric Balletbo i Serra <enric.balletbo@collabora.com> wrote:

> Hi,
> 
> On 10/11/19 13:10, Jonathan Cameron wrote:
> > On Tue,  5 Nov 2019 14:26:37 -0800
> > 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>  
> > 
> > Random suggestion for a possible cleanup...
> > 
> > Would a devm_platform_device_register_data make sense?  Drops a
> > fair bit of boilerplate in here.  If its not a common enough
> > pattern, could use the devm_add_action_or_reset route
> > to do the same thing.
> >   
> 
> I don't think devm_platform_device_register exists, exist?

It doesn't.  I was rather unclear :(  Suggestion was that we might want
to think about adding one if this pattern is reasonably common.

In meantime devm_add_action_or_reset will give us much the same
with changes only in the driver.

Jonathan

> 
> After solving the changes pointed by Jonathan the patch looks good to me.
> 
> > I would suggest this as a possible future element, but you
> > have some other issues around that area that this would cleanup
> > nicely for you.  See inline.
> > 
> > 
> > Thanks,
> > 
> > Jonathan
> > 
> > 
> >   
> >> ---
> >> Changes in v4:
> >> - Use platform_device_register_data in children registration.
> >> - Free registered pdev children at remove time.
> >> - Remove useless includes
> >> - Check patch with --strict option
> >>     Use sizeof(*obj) instead of sizeof(struct ...obj)
> >>     Alignement
> >> - Describe cros_ec_sensorhub in kernel-doc format.
> >> Changes in v3:
> >> - Fix doxygen comments
> >> - Fix use of ret |=
> >> - Remove unncessary goto.
> >> Changes in v2:
> >> - Remove unerelated changes.
> >> - Fix spelling.
> >> - Use !x instead of x == NULL
> >> - Use platform_ API directly to register IIO sensors from
> >>   cros_ec_sensorhub.
> >>
> >>  drivers/iio/common/cros_ec_sensors/Kconfig    |   2 +-
> >>  drivers/platform/chrome/Kconfig               |  12 +
> >>  drivers/platform/chrome/Makefile              |   1 +
> >>  drivers/platform/chrome/cros_ec_sensorhub.c   | 223 ++++++++++++++++++
> >>  .../linux/platform_data/cros_ec_sensorhub.h   |  33 +++
> >>  5 files changed, 270 insertions(+), 1 deletion(-)
> >>  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..56a25317a6be 100644
> >> --- a/drivers/platform/chrome/Kconfig
> >> +++ b/drivers/platform/chrome/Kconfig
> >> @@ -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 Sensor Hub"
> >> +	depends on CROS_EC && IIO  
> > 
> > Could relax the IIO dependency I think...  Get you more build coverage.
> >   
> >> +	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 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..6a0aa84cf092
> >> --- /dev/null
> >> +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> >> @@ -0,0 +1,223 @@
> >> +// 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/module.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_data/cros_ec_sensorhub.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/slab.h>
> >> +
> >> +#define DRV_NAME		"cros-ec-sensorhub"
> >> +
> >> +static struct platform_device *cros_ec_sensorhub_allocate_single_sensor(
> >> +		struct device *parent,
> >> +		char *sensor_name,
> >> +		int sensor_num)
> >> +{
> >> +	struct cros_ec_sensor_platform sensor_platforms = {
> >> +		.sensor_num = sensor_num,
> >> +	};
> >> +
> >> +	return platform_device_register_data(parent, sensor_name,
> >> +				PLATFORM_DEVID_AUTO,
> >> +				&sensor_platforms,
> >> +				sizeof(sensor_platforms));
> >> +}
> >> +
> >> +static int cros_ec_sensorhub_register(struct device *dev,
> >> +				      struct cros_ec_sensorhub *sensorhub)  
> > 
> > As noted below, I'd be happier if this function did it's own cleanup on
> > error rather than leaving that for the caller.
> >   
> >> +{
> >> +	int ret, i, id, sensor_num;
> >> +	struct cros_ec_dev *ec = sensorhub->ec;
> >> +	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 platform_device *pdev;
> >> +	char *name;
> >> +
> >> +	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;
> >> +	}
> >> +
> >> +	/* Prepare a message to send INFO command to each sensor. */
> >> +	msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
> >> +		      GFP_KERNEL);
> >> +	if (!msg)
> >> +		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;
> >> +	resp = (struct ec_response_motion_sense *)msg->data;
> >> +
> >> +	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:
> >> +			name = "cros-ec-accel";
> >> +			break;
> >> +		case MOTIONSENSE_TYPE_BARO:
> >> +			name = "cros-ec-baro";
> >> +			break;
> >> +		case MOTIONSENSE_TYPE_GYRO:
> >> +			name = "cros-ec-gyro";
> >> +			break;
> >> +		case MOTIONSENSE_TYPE_MAG:
> >> +			name = "cros-ec-mag";
> >> +			break;
> >> +		case MOTIONSENSE_TYPE_PROX:
> >> +			name = "cros-ec-prox";
> >> +			break;
> >> +		case MOTIONSENSE_TYPE_LIGHT:
> >> +			name = "cros-ec-light";
> >> +			break;
> >> +		case MOTIONSENSE_TYPE_ACTIVITY:
> >> +			name = "cros-ec-activity";
> >> +			break;
> >> +		default:
> >> +			dev_warn(dev, "unknown type %d\n", resp->info.type);
> >> +			continue;
> >> +		}
> >> +		pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
> >> +		if (IS_ERR(pdev)) {
> >> +			ret = IS_ERR(pdev);
> >> +			goto error;
> >> +		}
> >> +		sensorhub->sensor_pdev[id++] = pdev;
> >> +		sensor_type[resp->info.type]++;
> >> +	}
> >> +
> >> +	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)) {
> >> +		pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
> >> +							"cros-ec-lid-angle", 0);
> >> +		if (IS_ERR(pdev)) {
> >> +			ret = IS_ERR(pdev);
> >> +			goto error;
> >> +		}
> >> +		sensorhub->sensor_pdev[id++] = pdev;
> >> +	}
> >> +
> >> +error:
> >> +	kfree(msg);
> >> +	return ret;
> >> +}
> >> +
> >> +static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> >> +{
> >> +	struct device *dev = &sensorhub_pdev->dev;
> >> +	struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> >> +	int ret, i;
> >> +	struct platform_device *pdev;
> >> +	struct cros_ec_sensorhub *data =
> >> +		kzalloc(sizeof(struct cros_ec_sensorhub), GFP_KERNEL);  
> > 
> > Do we free this anywhere?  Could just use devm_kzalloc to do it
> > automatically.
> >   
> >> +
> >> +	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_sensorhub_register(dev, data);
> >> +		if (ret) {
> >> +			dev_err(dev, "Register failed %d\n", ret);
> >> +			goto unregister_sensors;  
> > 
> > From a code structure point of view, cros_ec_sensorhub_register
> > should have done any cleanup necessary if it returns an error.  Hence
> > we should be fine doing a direct return here (other than the memory
> > not being freed comment above.
> > 
> > This may seem an overly restrictive request, but that sort of rule
> > makes code a lot easier to review as we don't have to go look
> > to see where the error handling occurs and check for multiple paths
> > etc.  Note that if you use managed functions then there is no
> > cleanup to do anyway ;)
> >   
> >> +		}
> >> +	} else {
> >> +		/*
> >> +		 * If the device has sensors but does not claim to
> >> +		 * be a sensor hub, we are in legacy mode.
> >> +		 */
> >> +		for (i = 0; i < 2; i++) {
> >> +			pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
> >> +						"cros-ec-accel-legacy", i);
> >> +			if (IS_ERR(pdev)) {
> >> +				ret = IS_ERR(pdev);
> >> +				dev_err(dev, "Legacy %d failed %d\n", i, ret);
> >> +				goto unregister_sensors;
> >> +			} else {
> >> +				data->sensor_pdev[i] = pdev;
> >> +			}
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +
> >> +unregister_sensors:
> >> +	/*
> >> +	 * Given the probe has failed, we need to unregister all the sensors,
> >> +	 * not jutst the one that did not work: this device will be
> >> +	 * de-allocated.
> >> +	 */
> >> +	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
> >> +		pdev = data->sensor_pdev[i];
> >> +		if (pdev)
> >> +			platform_device_unregister(pdev);
> >> +	}
> >> +	return ret;
> >> +}
> >> +
> >> +static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
> >> +{
> >> +	struct cros_ec_sensorhub *sensorhub =
> >> +		platform_get_drvdata(sensorhub_pdev);
> >> +	struct platform_device *pdev;
> >> +	int i;
> >> +
> >> +	for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
> >> +		pdev = sensorhub->sensor_pdev[i];
> >> +		if (pdev)
> >> +			platform_device_unregister(pdev);
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static struct platform_driver cros_ec_sensorhub_driver = {
> >> +	.driver = {
> >> +		.name = DRV_NAME,
> >> +	},
> >> +	.probe = cros_ec_sensorhub_probe,
> >> +	.remove = cros_ec_sensorhub_remove,
> >> +};
> >> +
> >> +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..da0ba1d201e4
> >> --- /dev/null
> >> +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> >> @@ -0,0 +1,33 @@
> >> +/* 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>
> >> +
> >> +/* Maximal number of sensors supported by the EC. */
> >> +#define CROS_EC_SENSOR_MAX 16
> >> +
> >> +/*
> >> + * Maximal number of sensors supported by the hub:
> >> + * We add one for the lid angle inclinometer sensor.
> >> + */
> >> +#define CROS_EC_SENSOR_PDEV_MAX (CROS_EC_SENSOR_MAX + 1)
> >> +
> >> +/**
> >> + * struct cros_ec_sensorhub - Sensor Hub device data.
> >> + *
> >> + * @ec:           Embedded Controller where the hub is located.
> >> + * @sensor_pdev:  Array of platform_device, one per sensor.
> >> + */
> >> +struct cros_ec_sensorhub {
> >> +	struct cros_ec_dev *ec;
> >> +	struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
> >> +};
> >> +
> >> +#endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */  
> >   



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

* Re: [PATCH v4 07/17] Revert "Input: cros_ec_keyb - add back missing mask for event_type"
  2019-11-11  9:20   ` Enric Balletbo i Serra
@ 2019-11-11 19:23     ` Dmitry Torokhov
  0 siblings, 0 replies; 52+ messages in thread
From: Dmitry Torokhov @ 2019-11-11 19:23 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Gwendal Grignou, briannorris, jic23, knaack.h, lars, pmeerw,
	lee.jones, bleung, dianders, groeck, fabien.lahoudere,
	linux-kernel, linux-iio, linux-input, Dmitry Torokhov

On Mon, Nov 11, 2019 at 10:20:05AM +0100, Enric Balletbo i Serra wrote:
> Hi,
> 
> cc'in Dmitry and linux-input list which is missing

Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Enric, please use my @gmail address for upstream stuff. Thanks!

-- 
Dmitry

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

* Re: [PATCH v4 08/17] Revert "Input: cros_ec_keyb: mask out extra flags in event_type"
  2019-11-11  9:20   ` Enric Balletbo i Serra
@ 2019-11-11 19:23     ` Dmitry Torokhov
  0 siblings, 0 replies; 52+ messages in thread
From: Dmitry Torokhov @ 2019-11-11 19:23 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Gwendal Grignou, briannorris, jic23, knaack.h, lars, pmeerw,
	lee.jones, bleung, dianders, groeck, fabien.lahoudere,
	linux-kernel, linux-iio, linux-input, Dmitry Torokhov

On Mon, Nov 11, 2019 at 10:20:41AM +0100, Enric Balletbo i Serra wrote:
> Hi,
> 
> cc'in Dmitry and linux-input list which is missing

Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

> 
> On 5/11/19 23:26, Gwendal Grignou wrote:
> > This reverts commit d096aa3eb6045a6a475a0239f3471c59eedf3d61.
> > 
> > This patch is not needed anymore since we clear EC_MKBP_HAS_MORE_EVENTS
> > flag before calling the notifiers in patch
> > "9d9518f5b52a (platform: chrome: cros_ec: handle MKBP more events flag)"
> > 
> > Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> > ---
> > No changes in v4, v3.
> > New to v2.
> > 
> >  drivers/input/keyboard/cros_ec_keyb.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
> > index a29e81fdf186..2b71c5a51f90 100644
> > --- a/drivers/input/keyboard/cros_ec_keyb.c
> > +++ b/drivers/input/keyboard/cros_ec_keyb.c
> > @@ -237,7 +237,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
> >  	if (queued_during_suspend && !device_may_wakeup(ckdev->dev))
> >  		return NOTIFY_OK;
> >  
> > -	switch (ckdev->ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK) {
> > +	switch (ckdev->ec->event_data.event_type) {
> >  	case EC_MKBP_EVENT_KEY_MATRIX:
> >  		pm_wakeup_event(ckdev->dev, 0);
> >  
> > 

-- 
Dmitry

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

* Re: [PATCH v4 05/17] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number
  2019-11-11  9:29     ` Enric Balletbo i Serra
@ 2019-11-14  0:58       ` Gwendal Grignou
  0 siblings, 0 replies; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-14  0:58 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Jonathan Cameron, Brian Norris, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones,
	Benson Leung, Doug Anderson, Guenter Roeck, Fabien Lahoudere,
	linux-kernel, linux-iio, Enrico Granata

On Mon, Nov 11, 2019 at 1:29 AM Enric Balletbo i Serra
<enric.balletbo@collabora.com> wrote:
>
>
>
> On 10/11/19 13:17, Jonathan Cameron wrote:
> > On Tue,  5 Nov 2019 14:26:40 -0800
> > Gwendal Grignou <gwendal@chromium.org> wrote:
> >
> >> 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@chromium.org>
> >> Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
> >
> > This strikes me as something that could be a potential fix to backport?
> > Any known cases of a negative irq getting to here or is this a by
> > inspection thing?
inspection only.

> >
> > Otherwise seems obviously correct.
> > Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> >
>
> Looks good to me:
>
> Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>
> Thanks,
>  Enric
>
> > Jonathan
> >
> >> ---
> >> No changes in v4, v3.
> >> Changes in v2:
> >>   Remove dual Enrico's signature.
> >>
> >>  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 925f84dbf621..d3dfa27171e6 100644
> >> --- a/drivers/platform/chrome/cros_ec.c
> >> +++ b/drivers/platform/chrome/cros_ec.c
> >> @@ -149,7 +149,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,
> >

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

* Re: [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support
  2019-11-10 12:54     ` Jonathan Cameron
@ 2019-11-14  1:01       ` Gwendal Grignou
  0 siblings, 0 replies; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-14  1:01 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Brian Norris, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones, Benson Leung,
	Enric Balletbo i Serra, Doug Anderson, Guenter Roeck,
	Fabien Lahoudere, linux-kernel, linux-iio

On Sun, Nov 10, 2019 at 4:55 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Wed, 6 Nov 2019 13:13:10 -0800
> Gwendal Grignou <gwendal@chromium.org> wrote:
>
> > On Tue, Nov 5, 2019 at 2:27 PM 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>
>
> Various bits inline.   Devm fun rising again + some oddity around
> PTR_ERR value returning that looks to be old.
>
> Thanks,
>
> Jonathan
>
> > > ---
> > > Changes in v4:
> > > - Keep defining cros_ec_sensorhub in kernel-doc format
> > > - Fix logic error when checking if sensor index outside array.
> > > - Check patch with --strict option
> > >     Use sizeof(*obj) instead of sizeof(struct ...obj)
> > >     Alignement
> > >     Use uX instead of uintX_t
> > >     Use !ptr instead of ptr != NULL
> > > Changes in v3:
> > > - Do not use ret !=
> > > - Simplfy errpr handling by removing a goto
> > > - Fix doxygen comments
> > > - Replace suspend/resume entry points with regular driver entry point;
> > >   There was an issue in the past where the sensor stack was preventing
> > >   device to suspend, but the proper fix has been implemented in cros_ec
> > >   code (6ad16b78a039b "platform/chrome: don't report EC_MKBP_EVENT_SENSOR_FIFO as wakeup")
> > > - Reduce mutex scope by checking return code outside of it.
> > > Changes in v2:
> > > - Do not register a .remove routinge in plaform_driver. A
> > >   devm_action_add is added later patch IIO driver register their
> > > callback.
> > > - Remove double lines, add lines before return calls.
> > > - Handle FLUSH flag from EC.
> > >
> > > - Use ktime_t for most timestamp measurements.
> > > - Add doxygen comments
> > > - Cleanup timestamp collection when processing FIFO.
> > > - Rename fifo_toggle to fifo_enable
> > >
> > >  drivers/platform/chrome/Makefile              |   3 +-
> > >  drivers/platform/chrome/cros_ec_sensorhub.c   | 117 +++--
> > >  .../platform/chrome/cros_ec_sensorhub_ring.c  | 423 ++++++++++++++++++
> > >  .../linux/platform_data/cros_ec_sensorhub.h   |  82 ++++
> > >  4 files changed, 592 insertions(+), 33 deletions(-)
> > >  create mode 100644 drivers/platform/chrome/cros_ec_sensorhub_ring.c
> > >
> > > 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 6a0aa84cf092..0e34ad2903bb 100644
> > > --- a/drivers/platform/chrome/cros_ec_sensorhub.c
> > > +++ b/drivers/platform/chrome/cros_ec_sensorhub.c
> > > @@ -39,11 +39,9 @@ static int cros_ec_sensorhub_register(struct device *dev,
> > >         int ret, i, id, sensor_num;
> > >         struct cros_ec_dev *ec = sensorhub->ec;
> > >         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 platform_device *pdev;
> > >         char *name;
> > > +       struct cros_ec_command *msg = sensorhub->msg;
> > >
> > >         sensor_num = cros_ec_get_sensor_count(ec);
> > >         if (sensor_num < 0) {
> > > @@ -58,30 +56,21 @@ static int cros_ec_sensorhub_register(struct device *dev,
> > >                 return -EINVAL;
> > >         }
> > >
> > > -       /* Prepare a message to send INFO command to each sensor. */
> > > -       msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
> > > -                     GFP_KERNEL);
> > > -       if (!msg)
> > > -               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;
> > > -       resp = (struct ec_response_motion_sense *)msg->data;
> > > +       msg->insize = sizeof(struct ec_response_motion_sense);
> > > +       msg->outsize = sizeof(struct ec_params_motion_sense);
> > >
> > >         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:
> > >                         name = "cros-ec-accel";
> > >                         break;
> > > @@ -104,16 +93,16 @@ static int cros_ec_sensorhub_register(struct device *dev,
> > >                         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;
> > >                 }
> > >                 pdev = cros_ec_sensorhub_allocate_single_sensor(dev, name, i);
> > > -               if (IS_ERR(pdev)) {
> > > -                       ret = IS_ERR(pdev);
> > > -                       goto error;
> > > -               }
> > > +               if (IS_ERR(pdev))
> > > +                       return IS_ERR(pdev);
> > > +
> > >                 sensorhub->sensor_pdev[id++] = pdev;
> > > -               sensor_type[resp->info.type]++;
> > > +               sensor_type[sensorhub->resp->info.type]++;
> > >         }
> > >
> > >         if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
> > > @@ -123,16 +112,13 @@ static int cros_ec_sensorhub_register(struct device *dev,
> > >                                    EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
> > >                 pdev = cros_ec_sensorhub_allocate_single_sensor(dev,
> > >                                                         "cros-ec-lid-angle", 0);
> > > -               if (IS_ERR(pdev)) {
> > > -                       ret = IS_ERR(pdev);
> > > -                       goto error;
> > > -               }
> > > +               if (IS_ERR(pdev))
> > > +                       return IS_ERR(pdev);
>
> return PTR_ERR(pdev)?  Elsewhere as well. Looks to be an old issue but
> should fix it so we get most relevant error value.
Yes, I introduced this error with "platform: cros_ec: Add
cros_ec_sensor_hub driver" patch. I fix it when I add devm for
children registration.
>
> > > +
> > >                 sensorhub->sensor_pdev[id++] = pdev;
> > >         }
> > >
> > > -error:
> > > -       kfree(msg);
> > > -       return ret;
> > > +       return 0;
> > >  }
> > >
> > >  static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> > > @@ -141,13 +127,29 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> > >         struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
> > >         int ret, i;
> > >         struct platform_device *pdev;
> > > -       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. */
> > > @@ -175,6 +177,20 @@ static int cros_ec_sensorhub_probe(struct platform_device *sensorhub_pdev)
> > >                 }
> > >         }
> > >
> > > +       /*
> > > +        * 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);
> > > +               if (ret)
> > > +                       goto unregister_sensors;
> > > +               /*
> > > +                * The msg and its data is not under the control of the ring
> > > +                * handler.
> > > +                */
> > > +       }
> > > +
> > >         return 0;
> > >
> > >  unregister_sensors:
> > > @@ -195,9 +211,12 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
> > >  {
> > >         struct cros_ec_sensorhub *sensorhub =
> > >                 platform_get_drvdata(sensorhub_pdev);
> > > +       struct cros_ec_dev *ec = sensorhub->ec;
> > >         struct platform_device *pdev;
> > >         int i;
> > >
> > > +       if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
> > > +               cros_ec_sensorhub_ring_remove(sensorhub);
>
> This (I think) means that we have non obvious ordering.  Parts of matching add are
> handled as managed, and parts are not.  I'd like it to be obvious that the
> setup and tear down are precise mirrors of each other.
>
> That generally means that everything in probe after the first non managed thing
> that needs cleaning up, must also not use managed interfaces.  Otherwise
> the ordering is complex and much harder to review.
>
>
I add another dev_add_action_or_reset for removing the sensorhub ring,
so we don't need a .remove entry point anymore.

> > >         for (i = 0; i < CROS_EC_SENSOR_PDEV_MAX; i++) {
> > >                 pdev = sensorhub->sensor_pdev[i];
> > >                 if (pdev)
> > > @@ -207,9 +226,43 @@ static int cros_ec_sensorhub_remove(struct platform_device *sensorhub_pdev)
> > >         return 0;
> > >  }
> > >
> > > +#if CONFIG_PM_SLEEP
> > > +/*
> > > + * 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 cros_ec_ring_suspend(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_enable(sensorhub, false);
> > > +       return 0;
> > > +}
> > > +
> > > +static int cros_ec_ring_resume(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_enable(sensorhub, true);
> > > +       return 0;
> > > +}
> > > +#endif
> > > +
> > > +static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_ring_pm_ops,
> > > +               cros_ec_ring_suspend,
> > > +               cros_ec_ring_resume);
> > > +
> > >  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,
> > > 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..f091f2a4ccfe
> > > --- /dev/null
> > > +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
> > > @@ -0,0 +1,423 @@
> > > +// 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;
> > > +
> > > +       if (sample->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
> > > +               return 0;
> > > +
> > > +       return cb(indio_dev, sample->vector, sample->timestamp);
> > > +}
> > > +
> > > +/**
> > > + * cros_ec_sensorhub_register_push_data - register the callback to the hub.
> > > + *
> > > + * @sensorhub : Sensor Hub object
> > > + * @sensor_num : The sensor the caller is interested in.
> > > + * @indio_dev : The iio device to use when a sample arrives.
> > > + * @cb : The callback to call when a sample arrives.
> > > + *
> > > + * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
> > > + * from the EC.
> > > + *
> > > + * Return: 0 when callback is registered.
> > > + */
> > > +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_PDEV_MAX)
> > > +               return -EINVAL;
> > > +       if (!sensorhub->push_data[sensor_num].indio_dev)
> > I made a logic mistake, it should be:
> > if (sensorhub->push_data[sensor_num].indio_dev)
> > [When the slot is already used, return einval.]
> > > +               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);
> > > +
> > > +/**
> > > + * cros_ec_sensorhub_ring_fifo_enable - Enable or disable interrupt generation
> > > + *  for FIFO events.
> > > + * @sensorhub : Sensor Hub object
> > > + * @on : true when events are requested.
> > > + *
> > > + * To be called before sleeping or when noone is listening.
> > > + * Return: 0 on success.
> > > + */
> > > +int cros_ec_sensorhub_ring_fifo_enable(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);
> > > +       mutex_unlock(&sensorhub->cmd_lock);
> > > +
> > > +       /* We expect to receive a payload of 4 bytes, ignore. */
> > > +       if (ret > 0)
> > > +               ret = 0;
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +/**
> > > + * cros_ec_ring_process_event - process one EC FIFO event
> > > + *
> > > + * @sensorhub: Sensorhub object.
> > > + * @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')
> > > + *
> > > + * Process one EC event, add it in the ring if necessary.
> > > + *
> > > + * Return: true if out event has been populated.
> > > + */
> > > +static bool cros_ec_ring_process_event(struct cros_ec_sensorhub *sensorhub,
> > > +                               const struct cros_ec_fifo_info *fifo_info,
> > > +                               const ktime_t fifo_timestamp,
> > > +                               ktime_t *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. */
> Having this comment in the middle of the definition block is a bit messy.
> Perhaps have
>         int async_flags;
> > > +       const int async_flags = in->flags &
> > > +               (MOTIONSENSE_SENSOR_FLAG_ODR | MOTIONSENSE_SENSOR_FLAG_FLUSH);
> > > +       const s64 now = cros_ec_get_time_ns();
> > > +
> ...
>         /* COMMENT */
>         async_flags =...
>
>
> > > +       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;
> > > +       ktime_t 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 if there are lost vectors. */
> > > +       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) {
>
> Given you have to be careful to always unlock this mutex, perhaps a goto and single
> exit path would be easier to maintain?  However this is a big complex function.
> It might be better broken up somewhat so the locked section is in a separate
> function with clear error handling etc.  Possibly even several different
> functions.
>
> 1. Fifo info if lost vectors.
> 2. Copy elements in main fifo
>
> I've not tried the refactor so might be more trouble than it is worth though!
>
> > > +                       mutex_unlock(&sensorhub->cmd_lock);
> > > +                       return;
> > > +               }
> > > +               memcpy(fifo_info, &sensorhub->resp->fifo_info,
> > > +                      sizeof(*fifo_info));
> > > +
> > > +               /*
> > > +                * Update collection time, will not be as precise as the
> > > +                * non-error case.
> > > +                */
> > > +               fifo_timestamp = cros_ec_get_time_ns();
> > > +       } else {
> > > +               fifo_timestamp = sensorhub->fifo_timestamp[
> > > +                       CROS_EC_SENSOR_NEW_TS];
> > > +       }
> > > +
> > > +       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;
> > > +       }
> > > +
> > > +       /* Copy elements in the main fifo */
> > > +       current_timestamp = sensorhub->fifo_timestamp[CROS_EC_SENSOR_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;
> > > +               }
> > > +               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;
> > > +               }
> > > +               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) {
> > > +               int total_lost = fifo_info->info.total_lost;
> > > +
> > > +               if (total_lost) {
> > > +                       int lost = fifo_info->lost[i];
> > > +
> > > +                       if (lost) {
> > > +                               dev_warn_ratelimited(sensorhub->dev,
> > > +                                                    "Sensor %d: lost: %d out of %d\n",
> > > +                                                    i, lost, 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[CROS_EC_SENSOR_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[CROS_EC_SENSOR_NEW_TS] =
> > > +               ec_dev->last_event_time;
> > > +       cros_ec_sensorhub_ring_handler(sensorhub);
> > > +
> > > +       return NOTIFY_OK;
> > > +}
> > > +
> > > +/**
> > > + * cros_ec_sensorhub_ring_add - Add/Remove the fifo functionality if the EC
> > > + *  supports it.
> > > + *
> > > + * @sensorhub : Sensor Hub object
> > > + *
> > > + * Return: 0 on success.
> > > + */
> > > +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;
>
> Burying managed allocations deep in a function which is only partly managed
> tends to lead to hard to review code.  If you can only use partial devm
> in such a function I wouldn't use it at all.
>
> Might be worth looking at devm_add_action_or_reset in here to deal with the
> notifier chain cleanup and fifo disable, but I've not looked through
> all the call paths to check if that makes sense.
>
> > > +
> > > +       sensorhub->fifo_timestamp[CROS_EC_SENSOR_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");
>
> dev_err I'd have thought...  I'm guessing the code doesn't work if that
> fails.
>
> > > +               return ret;
> > > +       }
> > > +
> > > +       /* Start collection samples. */
> > > +       return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true);
>
> If this fails should you not undo the notifier chain register?
>
> > > +}
> > > +
> > > +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_enable(sensorhub, false);
> > > +       blocking_notifier_chain_unregister(&ec_dev->event_notifier,
> > > +                                          &sensorhub->notifier);
> > > +
> > > +       return 0;
> > > +}
> > > diff --git a/include/linux/platform_data/cros_ec_sensorhub.h b/include/linux/platform_data/cros_ec_sensorhub.h
> > > index 2b5a4d81f65f..8f9bf9a70701 100644
> > > --- a/include/linux/platform_data/cros_ec_sensorhub.h
> > > +++ b/include/linux/platform_data/cros_ec_sensorhub.h
> > > @@ -8,6 +8,9 @@
> > >  #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> > >  #define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
> > >
> > > +#include <linux/ktime.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/notifier.h>
> > >  #include <linux/platform_data/cros_ec_commands.h>
> > >
> > >  /* Maximal number of sensors supported by the EC. */
> > > @@ -27,15 +30,94 @@ struct cros_ec_sensor_platform {
> > >         u8 sensor_num;
> > >  };
> > >
> > > +struct iio_dev;
> > > +
> > > +/**
> > > + * typedef cros_ec_sensorhub_push_data_cb_t - Callback function to send datum
> > > + *     to specific sensors
> > > + *
> > > + * @indio_dev: The IIO device that will process the sample.
> > > + * @data: vector array of the ring sample.
> > > + * @timestamp: Timestamp in host timespace when the sample was acquired by
> > > + *             the EC.
> > > + */
> > > +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 {
> > > +       CROS_EC_SENSOR_LAST_TS,
> > > +       CROS_EC_SENSOR_NEW_TS,
> > > +       CROS_EC_SENSOR_ALL_TS
> > > +};
> > > +
> > > +struct __ec_todo_packed cros_ec_fifo_info {
> > > +       struct ec_response_motion_sense_fifo_info info;
> > > +       u16    lost[CROS_EC_SENSOR_MAX];
> > > +};
> > > +
> > > +struct cros_ec_sensors_ring_sample {
> > > +       u8  sensor_id;
> > > +       u8  flag;
> > > +       s16 vector[3];
> > > +       s64 timestamp;
> > > +} __packed;
> > > +
> > >  /**
> > >   * struct cros_ec_sensorhub - Sensor Hub device data.
> > >   *
> > > + * @dev:          Device object, mostly used for logging.
> > >   * @ec:           Embedded Controller where the hub is located.
> > >   * @sensor_pdev:  Array of platform_device, one per sensor.
> > > + * @msg: Structure to send FIFO requests.
> > > + * @params: pointer to parameters in msg.
> > > + * @resp: pointer to responses in msg.
> > > + * @cmd_lock : lock for sending msg.
> > > + * @notifier: Notifier to kick the FIFO interrupt.
> > > + * @ring: Preprocessed ring to store events.
> > > + * @timestamp: array for event timestamp and spreading.
>
> Now I know you didn't check the docs through the kernel-docs script.
>
> It's called fifo_timestamp.
>
> > > + * @fifo_info: copy of FIFO information coming from the EC.
> > > + * @fifo_size: size of the ring.
> > > + * @pushdata: array of callback to send datums to iio sensor object.
> > >   */
> > >  struct cros_ec_sensorhub {
> > > +       struct device *dev;
> > >         struct cros_ec_dev *ec;
> > >         struct platform_device *sensor_pdev[CROS_EC_SENSOR_PDEV_MAX];
> > > +
> > > +       struct cros_ec_command *msg;
> > > +       struct ec_params_motion_sense *params;
> > > +       struct ec_response_motion_sense *resp;
> > > +       struct mutex cmd_lock;
> > > +
> > > +       struct notifier_block notifier;
> > > +
> > > +       struct cros_ec_sensors_ring_sample *ring;
> > > +
> > > +       ktime_t fifo_timestamp[CROS_EC_SENSOR_ALL_TS];
> > > +       struct cros_ec_fifo_info fifo_info;
> > > +       int    fifo_size;
> > > +
> > > +       struct cros_ec_sensorhub_sensor_push_data push_data[
> > > +               CROS_EC_SENSOR_PDEV_MAX];
> > >  };
> > >
> > > +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);
> > > +
> > > +void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub *sensorhub,
> > > +                                           u8 sensor_num);
> > > +
> > > +int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub);
> > > +int cros_ec_sensorhub_ring_remove(struct cros_ec_sensorhub *sensorhub);
> > > +int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
> > > +                                      bool on);
> > > +
> > >  #endif   /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
> > > --
> > > 2.24.0.rc1.363.gb1bccd3e3d-goog
> > >
>

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

* Re: [PATCH v4 13/17] iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO
  2019-11-10 13:17   ` Jonathan Cameron
@ 2019-11-14 18:17     ` Gwendal Grignou
  0 siblings, 0 replies; 52+ messages in thread
From: Gwendal Grignou @ 2019-11-14 18:17 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Brian Norris, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Lee Jones, Benson Leung,
	Enric Balletbo i Serra, Doug Anderson, Guenter Roeck,
	Fabien Lahoudere, linux-kernel, linux-iio

The problem are:- in case of a kernel crash while collecting sensor
data: when the kernel restart, the sensors are still sending events. -
we can also enable a sensor behind the iiio driver back through debug
tools: we don

On Sun, Nov 10, 2019 at 5:17 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Tue,  5 Nov 2019 14:26:48 -0800
> Gwendal Grignou <gwendal@chromium.org> wrote:
>
> > When EC supports FIFO, each IIO device registers a callback, to put
> > samples in the buffer when they arrives from the FIFO.
> > We can still use a trigger to collect samples, but there may be some
> > duplications in the buffer: EC has a single FIFO, so once one sensor is
> > using it, all sensors event will be in the FIFO.
> > To be sure events generated by cros_ec_sensorhub or the trigger uses the
> > same time domain, current_timestamp_clock must be set to "boottime".
>
> How is that enforced?
I should have removed this comment: cros_ec_sensors_push_data() now
checks for the iio dev clock_id and add an offset if necessary.
I add a patch for IIO to allow changing timestamp_clock from the
driver itself: I set to default for cros ec sensor to boottime, since
it is the default timestamp source coming from the FIFO.

>  I'd have no problem with core support for IIO
> to allow a driver that has requirements like this to override
> the normal flexibility.
>
> >
> > 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>
> A question + a comment that I'm still not happy with the fact we have
> to protect against a race on startup.  However, we can perhaps make that
> a problem for another day.
>
> Assuming the question on the mode mask change is resolved.
> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>
> > ---
> > Changes in v4:
> > - Fix a logic error when the sensor is not "physical", for instance lig
> >   angle: core_init() would return !0 even if there was no error.
> > - Check patch with --strict option
> >     Use sizeof(*obj) instead of sizeof(struct ...obj)
> >     Alignement
> > Change in v3:
> > - Remove double line
> > - Fix indentation
> > - Add code to support iio clock_id setting. Optimized for
> >   CLOCK_BOOTTIME.
> > Change in v2 from "Use triggered buffer only when EC does not support
> > FIFO":
> > - Keep trigger all the time.
> > - Add  devm_add_action to cleanup callback registration.
> > - EC that "reports" legacy sensors do not have FIFO.
> > - Use iiio_is_buffer_enabled instead of checking the scan_mask
> >   before sending samples to buffer.
> > - Add empty lines for visibility.
> >
> >  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  |  9 +-
> >  .../cros_ec_sensors/cros_ec_sensors_core.c    | 85 ++++++++++++++++++-
> >  drivers/iio/light/cros_ec_light_prox.c        |  9 +-
> >  drivers/iio/pressure/cros_ec_baro.c           |  9 +-
> >  .../linux/iio/common/cros_ec_sensors_core.h   | 10 ++-
> >  7 files changed, 103 insertions(+), 29 deletions(-)
> >
> > diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
> > index 65f85faf6f31..b9f651e4ce99 100644
> > --- a/drivers/iio/accel/cros_ec_accel_legacy.c
> > +++ b/drivers/iio/accel/cros_ec_accel_legacy.c
> > @@ -171,7 +171,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, NULL);
> >       if (ret)
> >               return ret;
> >
> > @@ -191,11 +192,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 7dce04473467..62a0dd970988 100644
> > --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> > +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
> > @@ -231,7 +231,9 @@ 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;
> >
> > @@ -293,11 +295,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 b47da497a3c3..904cd26dd31f 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,17 +84,78 @@ 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;
> > +     s64 delta;
> > +     unsigned int i;
> > +
> > +     /*
> > +      * It can happen if we get a samples before the iio device is fully
> > +      * registered.
>
> I'd really like to stop that happening as it seems like a nasty racey corner case.
> I'm not convinced there aren't 'half registered' cases we can still hit even
> with this protection.  Hardware should be in a clean state before any
> interrupts etc are registered.
You're right, st can not be null, since devm_iio_device_alloc is
called before cros_ec_sensors_core_init that register the callback.
>
> However if you are sure this protection is enough that can be a job for another day.
>
> I suspect you get away with it because of the next check always failing in
> those racey conditions as can't enable the buffer until we have a fully
> set up device.
>
> > +      */
> > +     if (!st)
> > +             return 0;
> > +
> > +     /*
> > +      * Ignore samples if the buffer is not set: it is needed if the ODR is
> > +      * set but the buffer is not enabled yet.
> > +      */
> > +     if (!iio_buffer_enabled(indio_dev))
> > +             return 0;
This check is necessary:
- in case of a kernel crash while collecting sensor data: when the
kernel restart, the sensors are still sending events.
- we can also enable a sensor behind the iiio driver back through
debug tools: the sensor will send sample, but the iio driver is not
ready for them.
> > +
> > +     out = (s16 *)st->samples;
> > +     for_each_set_bit(i,
> > +                      indio_dev->active_scan_mask,
> > +                      indio_dev->masklength) {
> > +             *out = data[i];
> > +             out++;
> > +     }
> > +
> > +     if (iio_device_get_clock(indio_dev) != CLOCK_BOOTTIME)
> > +             delta = iio_get_time_ns(indio_dev) - cros_ec_get_time_ns();
> > +     else
> > +             delta = 0;
> > +
> > +     iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
> > +                                        timestamp + delta);
> > +
> > +     return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cros_ec_sensors_push_data);
> > +
> > +static void cros_ec_sensors_core_clean(void *arg)
> > +{
> > +     struct platform_device *pdev = (struct platform_device *)arg;
> > +     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);
> > +}
> > +
> >  /**
> >   * cros_ec_sensors_core_init() - basic initialization of the core structure
> >   * @pdev:            platform device created for the sensors
> >   * @indio_dev:               iio device structure of the device
> >   * @physical_device: true if the device refers to a physical device
> > + * @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)
> > +                           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);
> > @@ -132,8 +194,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;
> > -
>
> This particular changes seems odd. Are we dropping access via sysfs?
> From what I recall we don't actually do much with this currently in the
> IIO core, but we 'might' in future ;)
Should not have been removed.

>
> >               state->param.cmd = MOTIONSENSE_CMD_INFO;
> >               state->param.info.sensor_num = sensor_platform->sensor_num;
> >               ret = cros_ec_motion_send_host_cmd(state, 0);
> > @@ -162,6 +222,25 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
> >                       state->frequencies[2] =
> >                           state->resp->info_3.max_frequency;
> >               }
> > +
> > +             ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> > +                                                   trigger_capture, NULL);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
> > +                     ret = cros_ec_sensorhub_register_push_data(sensor_hub,
> > +                                             sensor_platform->sensor_num,
> > +                                             indio_dev, push_data);
> > +                     if (ret)
> > +                             return ret;
> > +
> > +                     ret = devm_add_action_or_reset(dev,
> > +                                             cros_ec_sensors_core_clean,
> > +                                             pdev);
> > +                     if (ret)
> > +                             return ret;
> > +             }
> >       }
> >
> >       return 0;
> > diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
> > index d85a391e50c5..698b2ee81ebf 100644
> > --- a/drivers/iio/light/cros_ec_light_prox.c
> > +++ b/drivers/iio/light/cros_ec_light_prox.c
> > @@ -178,7 +178,9 @@ 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;
> >
> > @@ -237,11 +239,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 2354302375de..e1c86b22676c 100644
> > --- a/drivers/iio/pressure/cros_ec_baro.c
> > +++ b/drivers/iio/pressure/cros_ec_baro.c
> > @@ -134,7 +134,9 @@ 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 +182,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 0af918978f97..b8f573ca9dcc 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,8 @@ 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
> > @@ -87,9 +90,14 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
> >
> >  struct platform_device;
> >  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);
> >
> >  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);
> >
> >  int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
> >                                u16 opt_length);
>

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

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

On Mon, 11 Nov 2019 11:44:23 +0000
Lee Jones <lee.jones@linaro.org> wrote:

> On Fri, 08 Nov 2019, Enric Balletbo Serra wrote:
> 
> > Missatge de Gwendal Grignou <gwendal@chromium.org> del dia dt., 5 de
> > nov. 2019 a les 23:28:  
> > >
> > > 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 what the Embedded Controller supports.
> > >
> > > Signed-off-by: Gwendal Grignou <gwendal@chromium.org>  
> > 
> > Version 3 was acked and I think we can maintain his ack, so:
> > 
> > Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> > 
> > Also,
> > 
> > Acked-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> > 
> > Lee, how you would like to handle this? I think will be safe for
> > patches 1/2/3 go through the platform-chrome tree without an immutable
> > branch. Patch 3 thought still needs and Ack from you if is fine.  
> 
> Please take the entire set, converting:
> 
>   s/Acked-for-MFD-by/Acked-by/
> 
> ... and send me a pull-request to an immutable branch.
> 

Agreed.  Please do an immutable branch for the whole series v5 if everyone
else is happy with the changes in V5. (I think it was mostly my
comments on v4 so fingers crossed :)

I'll pull it if / when some follow up changes touch the resulting code
on the IIO side. Gwendall, if you can highlight if that occurs that
would be great as well!

Thanks,

Jonathan


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

end of thread, other threads:[~2019-11-16 11:49 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-05 22:26 [PATCH v4 00/17] cros_ec: Add sensorhub driver and FIFO processing Gwendal Grignou
2019-11-05 22:26 ` [PATCH v4 01/17] mfd: cros_ec: Add sensor_count and make check_features public Gwendal Grignou
2019-11-08 22:03   ` Enric Balletbo Serra
2019-11-11 11:44     ` Lee Jones
2019-11-16 11:49       ` Jonathan Cameron
2019-11-05 22:26 ` [PATCH v4 02/17] platform: cros_ec: Add cros_ec_sensor_hub driver Gwendal Grignou
2019-11-10 12:10   ` Jonathan Cameron
2019-11-11  9:24     ` Enric Balletbo i Serra
2019-11-11 11:55       ` Jonathan Cameron
2019-11-05 22:26 ` [PATCH v4 03/17] platform/mfd:iio: cros_ec: Register sensor through sensorhub Gwendal Grignou
2019-11-10 12:13   ` Jonathan Cameron
2019-11-11  9:25     ` Enric Balletbo i Serra
2019-11-11 11:43   ` Lee Jones
2019-11-05 22:26 ` [PATCH v4 04/17] platform: chrome: cros-ec: record event timestamp in the hard irq Gwendal Grignou
2019-11-10 12:16   ` Jonathan Cameron
2019-11-11  9:27     ` Enric Balletbo i Serra
2019-11-05 22:26 ` [PATCH v4 05/17] platform: chrome: cros_ec: Do not attempt to register a non-positive IRQ number Gwendal Grignou
2019-11-10 12:17   ` Jonathan Cameron
2019-11-11  9:29     ` Enric Balletbo i Serra
2019-11-14  0:58       ` Gwendal Grignou
2019-11-05 22:26 ` [PATCH v4 06/17] platform: chrome: cros_ec: handle MKBP more events flag Gwendal Grignou
2019-11-10 12:28   ` Jonathan Cameron
2019-11-11  9:30     ` Enric Balletbo i Serra
2019-11-05 22:26 ` [PATCH v4 07/17] Revert "Input: cros_ec_keyb - add back missing mask for event_type" Gwendal Grignou
2019-11-11  9:20   ` Enric Balletbo i Serra
2019-11-11 19:23     ` Dmitry Torokhov
2019-11-05 22:26 ` [PATCH v4 08/17] Revert "Input: cros_ec_keyb: mask out extra flags in event_type" Gwendal Grignou
2019-11-11  9:20   ` Enric Balletbo i Serra
2019-11-11 19:23     ` Dmitry Torokhov
2019-11-05 22:26 ` [PATCH v4 09/17] platform: chrome: sensorhub: Add FIFO support Gwendal Grignou
2019-11-06 21:13   ` Gwendal Grignou
2019-11-10 12:54     ` Jonathan Cameron
2019-11-14  1:01       ` Gwendal Grignou
2019-11-05 22:26 ` [PATCH v4 10/17] platform: chrome: sensorhub: Add code to spread timestmap Gwendal Grignou
2019-11-10 12:57   ` Jonathan Cameron
2019-11-05 22:26 ` [PATCH v4 11/17] platform: chrome: sensorhub: Add median filter Gwendal Grignou
2019-11-10 13:07   ` Jonathan Cameron
2019-11-05 22:26 ` [PATCH v4 12/17] iio: cros_ec: Move function description to .c file Gwendal Grignou
2019-11-10 13:08   ` Jonathan Cameron
2019-11-11  9:35     ` Enric Balletbo i Serra
2019-11-05 22:26 ` [PATCH v4 13/17] iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO Gwendal Grignou
2019-11-10 13:17   ` Jonathan Cameron
2019-11-14 18:17     ` Gwendal Grignou
2019-11-05 22:26 ` [PATCH v4 14/17] iio: cros_ec: Remove pm function Gwendal Grignou
2019-11-10 13:18   ` Jonathan Cameron
2019-11-11  9:37     ` Enric Balletbo i Serra
2019-11-05 22:26 ` [PATCH v4 15/17] iio: cros_ec: Expose hwfifo_timeout Gwendal Grignou
2019-11-10 13:21   ` Jonathan Cameron
2019-11-05 22:26 ` [PATCH v4 16/17] iio: cros_ec: Report hwfifo_watermark_max Gwendal Grignou
2019-11-10 13:22   ` Jonathan Cameron
2019-11-05 22:26 ` [PATCH v4 17/17] iio: cros_ec: Use Hertz as unit for sampling frequency Gwendal Grignou
2019-11-10 13:24   ` Jonathan Cameron

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