platform/chrome: cros-ec: Record event timestamp in the hard irq
authorGwendal Grignou <gwendal@chromium.org>
Tue, 19 Nov 2019 12:45:46 +0000 (13:45 +0100)
committerEnric Balletbo i Serra <enric.balletbo@collabora.com>
Thu, 21 Nov 2019 10:23:14 +0000 (11:23 +0100)
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>
Acked-by: Jonathan Cameron <Jonathan.Cameron@kernel.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
drivers/platform/chrome/cros_ec.c
drivers/platform/chrome/cros_ec_ishtp.c
drivers/platform/chrome/cros_ec_lpc.c
include/linux/platform_data/cros_ec_proto.h

index 9b2d07422e1751d3ffe8c99b92858f23f848494b..925f84dbf621845277e5d3218c7d1ca461d5f782 100644 (file)
@@ -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);
index 25ca2c894b4de07e78f456d082b9df44ba2a431a..5c848f22b44b4797b77278165a13a5208274c184 100644 (file)
@@ -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 @@ error_wake_up:
                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);
        }
 }
 
index 7d10d909435ffa26f0a4af711c4ba29379d26ec2..3c77496e164daa030182fa09b1c91e199c6ef8b8 100644 (file)
@@ -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,
index 691f9e953a96afd34d804a85e326203f8b318f8e..02dc34f366d7e2047b19928e24a11cafd4f5adfe 100644 (file)
@@ -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 */