1 // SPDX-License-Identifier: GPL-2.0-only
3 * Driver for Chrome OS EC Sensor hub FIFO.
5 * Copyright 2020 Google LLC
8 #include <linux/delay.h>
9 #include <linux/device.h>
10 #include <linux/iio/iio.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/platform_data/cros_ec_commands.h>
14 #include <linux/platform_data/cros_ec_proto.h>
15 #include <linux/platform_data/cros_ec_sensorhub.h>
16 #include <linux/platform_device.h>
17 #include <linux/sort.h>
18 #include <linux/slab.h>
20 /* Precision of fixed point for the m values from the filter */
21 #define M_PRECISION BIT(23)
23 /* Only activate the filter once we have at least this many elements. */
24 #define TS_HISTORY_THRESHOLD 8
27 * If we don't have any history entries for this long, empty the filter to
28 * make sure there are no big discontinuities.
30 #define TS_HISTORY_BORED_US 500000
32 /* To measure by how much the filter is overshooting, if it happens. */
33 #define FUTURE_TS_ANALYTICS_COUNT_MAX 100
36 cros_sensorhub_send_sample(struct cros_ec_sensorhub
*sensorhub
,
37 struct cros_ec_sensors_ring_sample
*sample
)
39 cros_ec_sensorhub_push_data_cb_t cb
;
40 int id
= sample
->sensor_id
;
41 struct iio_dev
*indio_dev
;
43 if (id
>= sensorhub
->sensor_num
)
46 cb
= sensorhub
->push_data
[id
].push_data_cb
;
50 indio_dev
= sensorhub
->push_data
[id
].indio_dev
;
52 if (sample
->flag
& MOTIONSENSE_SENSOR_FLAG_FLUSH
)
55 return cb(indio_dev
, sample
->vector
, sample
->timestamp
);
59 * cros_ec_sensorhub_register_push_data() - register the callback to the hub.
61 * @sensorhub : Sensor Hub object
62 * @sensor_num : The sensor the caller is interested in.
63 * @indio_dev : The iio device to use when a sample arrives.
64 * @cb : The callback to call when a sample arrives.
66 * The callback cb will be used by cros_ec_sensorhub_ring to distribute events
69 * Return: 0 when callback is registered.
70 * EINVAL is the sensor number is invalid or the slot already used.
72 int cros_ec_sensorhub_register_push_data(struct cros_ec_sensorhub
*sensorhub
,
74 struct iio_dev
*indio_dev
,
75 cros_ec_sensorhub_push_data_cb_t cb
)
77 if (sensor_num
>= sensorhub
->sensor_num
)
79 if (sensorhub
->push_data
[sensor_num
].indio_dev
)
82 sensorhub
->push_data
[sensor_num
].indio_dev
= indio_dev
;
83 sensorhub
->push_data
[sensor_num
].push_data_cb
= cb
;
87 EXPORT_SYMBOL_GPL(cros_ec_sensorhub_register_push_data
);
89 void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub
*sensorhub
,
92 sensorhub
->push_data
[sensor_num
].indio_dev
= NULL
;
93 sensorhub
->push_data
[sensor_num
].push_data_cb
= NULL
;
95 EXPORT_SYMBOL_GPL(cros_ec_sensorhub_unregister_push_data
);
98 * cros_ec_sensorhub_ring_fifo_enable() - Enable or disable interrupt generation
100 * @sensorhub: Sensor Hub object
101 * @on: true when events are requested.
103 * To be called before sleeping or when noone is listening.
104 * Return: 0 on success, or an error when we can not communicate with the EC.
107 int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub
*sensorhub
,
112 mutex_lock(&sensorhub
->cmd_lock
);
113 if (sensorhub
->tight_timestamps
)
114 for (i
= 0; i
< sensorhub
->sensor_num
; i
++)
115 sensorhub
->batch_state
[i
].last_len
= 0;
117 sensorhub
->params
->cmd
= MOTIONSENSE_CMD_FIFO_INT_ENABLE
;
118 sensorhub
->params
->fifo_int_enable
.enable
= on
;
120 sensorhub
->msg
->outsize
= sizeof(struct ec_params_motion_sense
);
121 sensorhub
->msg
->insize
= sizeof(struct ec_response_motion_sense
);
123 ret
= cros_ec_cmd_xfer_status(sensorhub
->ec
->ec_dev
, sensorhub
->msg
);
124 mutex_unlock(&sensorhub
->cmd_lock
);
126 /* We expect to receive a payload of 4 bytes, ignore. */
133 static int cros_ec_sensor_ring_median_cmp(const void *pv1
, const void *pv2
)
135 s64 v1
= *(s64
*)pv1
;
136 s64 v2
= *(s64
*)pv2
;
147 * cros_ec_sensor_ring_median: Gets median of an array of numbers
149 * For now it's implemented using an inefficient > O(n) sort then return
150 * the middle element. A more optimal method would be something like
151 * quickselect, but given that n = 64 we can probably live with it in the
154 * Warning: the input array gets modified (sorted)!
156 static s64
cros_ec_sensor_ring_median(s64
*array
, size_t length
)
158 sort(array
, length
, sizeof(s64
), cros_ec_sensor_ring_median_cmp
, NULL
);
159 return array
[length
/ 2];
163 * IRQ Timestamp Filtering
165 * Lower down in cros_ec_sensor_ring_process_event(), for each sensor event
166 * we have to calculate it's timestamp in the AP timebase. There are 3 time
168 * a - EC timebase, sensor event
169 * b - EC timebase, IRQ
170 * c - AP timebase, IRQ
171 * a' - what we want: sensor even in AP timebase
173 * While a and b are recorded at accurate times (due to the EC real time
174 * nature); c is pretty untrustworthy, even though it's recorded the
175 * first thing in ec_irq_handler(). There is a very good change we'll get
176 * added lantency due to:
181 * Normally a' = c - b + a, but if we do that naive math any jitter in c
182 * will get coupled in a', which we don't want. We want a function
183 * a' = cros_ec_sensor_ring_ts_filter(a) which will filter out outliers in c.
185 * Think of a graph of AP time(b) on the y axis vs EC time(c) on the x axis.
186 * The slope of the line won't be exactly 1, there will be some clock drift
187 * between the 2 chips for various reasons (mechanical stress, temperature,
188 * voltage). We need to extrapolate values for a future x, without trusting
189 * recent y values too much.
191 * We use a median filter for the slope, then another median filter for the
192 * y-intercept to calculate this function:
193 * dx[n] = x[n-1] - x[n]
194 * dy[n] = x[n-1] - x[n]
195 * m[n] = dy[n] / dx[n]
196 * median_m = median(m[n-k:n])
197 * error[i] = y[n-i] - median_m * x[n-i]
198 * median_error = median(error[:k])
199 * predicted_y = median_m * x + median_error
201 * Implementation differences from above:
202 * - Redefined y to be actually c - b, this gives us a lot more precision
203 * to do the math. (c-b)/b variations are more obvious than c/b variations.
204 * - Since we don't have floating point, any operations involving slope are
205 * done using fixed point math (*M_PRECISION)
206 * - Since x and y grow with time, we keep zeroing the graph (relative to
207 * the last sample), this way math involving *x[n-i] will not overflow
208 * - EC timestamps are kept in us, it improves the slope calculation precision
212 * cros_ec_sensor_ring_ts_filter_update() - Update filter history.
214 * @state: Filter information.
215 * @b: IRQ timestamp, EC timebase (us)
216 * @c: IRQ timestamp, AP timebase (ns)
218 * Given a new IRQ timestamp pair (EC and AP timebases), add it to the filter
222 cros_ec_sensor_ring_ts_filter_update(struct cros_ec_sensors_ts_filter_state
228 s64 m
; /* stored as *M_PRECISION */
229 s64
*m_history_copy
= state
->temp_buf
;
230 s64
*error
= state
->temp_buf
;
233 /* we trust b the most, that'll be our independent variable */
235 /* y is the offset between AP and EC times, in ns */
238 dx
= (state
->x_history
[0] + state
->x_offset
) - x
;
240 return; /* we already have this irq in the history */
241 dy
= (state
->y_history
[0] + state
->y_offset
) - y
;
242 m
= div64_s64(dy
* M_PRECISION
, dx
);
244 /* Empty filter if we haven't seen any action in a while. */
245 if (-dx
> TS_HISTORY_BORED_US
)
246 state
->history_len
= 0;
248 /* Move everything over, also update offset to all absolute coords .*/
249 for (i
= state
->history_len
- 1; i
>= 1; i
--) {
250 state
->x_history
[i
] = state
->x_history
[i
- 1] + dx
;
251 state
->y_history
[i
] = state
->y_history
[i
- 1] + dy
;
253 state
->m_history
[i
] = state
->m_history
[i
- 1];
255 * Also use the same loop to copy m_history for future
258 m_history_copy
[i
] = state
->m_history
[i
- 1];
261 /* Store the x and y, but remember offset is actually last sample. */
264 state
->x_history
[0] = 0;
265 state
->y_history
[0] = 0;
267 state
->m_history
[0] = m
;
268 m_history_copy
[0] = m
;
270 if (state
->history_len
< CROS_EC_SENSORHUB_TS_HISTORY_SIZE
)
271 state
->history_len
++;
273 /* Precalculate things for the filter. */
274 if (state
->history_len
> TS_HISTORY_THRESHOLD
) {
276 cros_ec_sensor_ring_median(m_history_copy
,
277 state
->history_len
- 1);
280 * Calculate y-intercepts as if m_median is the slope and
281 * points in the history are on the line. median_error will
282 * still be in the offset coordinate system.
284 for (i
= 0; i
< state
->history_len
; i
++)
285 error
[i
] = state
->y_history
[i
] -
286 div_s64(state
->median_m
* state
->x_history
[i
],
288 state
->median_error
=
289 cros_ec_sensor_ring_median(error
, state
->history_len
);
292 state
->median_error
= 0;
297 * cros_ec_sensor_ring_ts_filter() - Translate EC timebase timestamp to AP
300 * @state: filter information.
301 * @x: any ec timestamp (us):
303 * cros_ec_sensor_ring_ts_filter(a) => a' event timestamp, AP timebase
304 * cros_ec_sensor_ring_ts_filter(b) => calculated timestamp when the EC IRQ
305 * should have happened on the AP, with low jitter
307 * Note: The filter will only activate once state->history_len goes
308 * over TS_HISTORY_THRESHOLD. Otherwise it'll just do the naive c - b + a
311 * How to derive the formula, starting from:
312 * f(x) = median_m * x + median_error
313 * That's the calculated AP - EC offset (at the x point in time)
314 * Undo the coordinate system transform:
315 * f(x) = median_m * (x - x_offset) + median_error + y_offset
316 * Remember to undo the "y = c - b * 1000" modification:
317 * f(x) = median_m * (x - x_offset) + median_error + y_offset + x * 1000
319 * Return: timestamp in AP timebase (ns)
322 cros_ec_sensor_ring_ts_filter(struct cros_ec_sensors_ts_filter_state
*state
,
325 return div_s64(state
->median_m
* (x
- state
->x_offset
), M_PRECISION
)
326 + state
->median_error
+ state
->y_offset
+ x
* 1000;
330 * Since a and b were originally 32 bit values from the EC,
331 * they overflow relatively often, casting is not enough, so we need to
335 cros_ec_sensor_ring_fix_overflow(s64
*ts
,
336 const s64 overflow_period
,
337 struct cros_ec_sensors_ec_overflow_state
342 *ts
+= state
->offset
;
343 if (abs(state
->last
- *ts
) > (overflow_period
/ 2)) {
344 adjust
= state
->last
> *ts
? overflow_period
: -overflow_period
;
345 state
->offset
+= adjust
;
352 cros_ec_sensor_ring_check_for_past_timestamp(struct cros_ec_sensorhub
354 struct cros_ec_sensors_ring_sample
357 const u8 sensor_id
= sample
->sensor_id
;
359 /* If this event is earlier than one we saw before... */
360 if (sensorhub
->batch_state
[sensor_id
].newest_sensor_event
>
362 /* mark it for spreading. */
364 sensorhub
->batch_state
[sensor_id
].last_ts
;
366 sensorhub
->batch_state
[sensor_id
].newest_sensor_event
=
371 * cros_ec_sensor_ring_process_event() - Process one EC FIFO event
373 * @sensorhub: Sensor Hub object.
374 * @fifo_info: FIFO information from the EC (includes b point, EC timebase).
375 * @fifo_timestamp: EC IRQ, kernel timebase (aka c).
376 * @current_timestamp: calculated event timestamp, kernel timebase (aka a').
377 * @in: incoming FIFO event from EC (includes a point, EC timebase).
378 * @out: outgoing event to user space (includes a').
380 * Process one EC event, add it in the ring if necessary.
382 * Return: true if out event has been populated.
385 cros_ec_sensor_ring_process_event(struct cros_ec_sensorhub
*sensorhub
,
386 const struct ec_response_motion_sense_fifo_info
388 const ktime_t fifo_timestamp
,
389 ktime_t
*current_timestamp
,
390 struct ec_response_motion_sensor_data
*in
,
391 struct cros_ec_sensors_ring_sample
*out
)
393 const s64 now
= cros_ec_get_time_ns();
394 int axis
, async_flags
;
396 /* Do not populate the filter based on asynchronous events. */
397 async_flags
= in
->flags
&
398 (MOTIONSENSE_SENSOR_FLAG_ODR
| MOTIONSENSE_SENSOR_FLAG_FLUSH
);
400 if (in
->flags
& MOTIONSENSE_SENSOR_FLAG_TIMESTAMP
&& !async_flags
) {
401 s64 a
= in
->timestamp
;
402 s64 b
= fifo_info
->timestamp
;
403 s64 c
= fifo_timestamp
;
405 cros_ec_sensor_ring_fix_overflow(&a
, 1LL << 32,
406 &sensorhub
->overflow_a
);
407 cros_ec_sensor_ring_fix_overflow(&b
, 1LL << 32,
408 &sensorhub
->overflow_b
);
410 if (sensorhub
->tight_timestamps
) {
411 cros_ec_sensor_ring_ts_filter_update(
412 &sensorhub
->filter
, b
, c
);
413 *current_timestamp
= cros_ec_sensor_ring_ts_filter(
414 &sensorhub
->filter
, a
);
419 * Disable filtering since we might add more jitter
420 * if b is in a random point in time.
422 new_timestamp
= fifo_timestamp
-
423 fifo_info
->timestamp
* 1000 +
424 in
->timestamp
* 1000;
426 * The timestamp can be stale if we had to use the fifo
429 if (new_timestamp
- *current_timestamp
> 0)
430 *current_timestamp
= new_timestamp
;
434 if (in
->flags
& MOTIONSENSE_SENSOR_FLAG_ODR
) {
435 if (sensorhub
->tight_timestamps
) {
436 sensorhub
->batch_state
[in
->sensor_num
].last_len
= 0;
437 sensorhub
->batch_state
[in
->sensor_num
].penul_len
= 0;
440 * ODR change is only useful for the sensor_ring, it does not
441 * convey information to clients.
446 if (in
->flags
& MOTIONSENSE_SENSOR_FLAG_FLUSH
) {
447 out
->sensor_id
= in
->sensor_num
;
448 out
->timestamp
= *current_timestamp
;
449 out
->flag
= in
->flags
;
450 if (sensorhub
->tight_timestamps
)
451 sensorhub
->batch_state
[out
->sensor_id
].last_len
= 0;
453 * No other payload information provided with
459 if (in
->flags
& MOTIONSENSE_SENSOR_FLAG_TIMESTAMP
)
460 /* If we just have a timestamp, skip this entry. */
464 out
->sensor_id
= in
->sensor_num
;
465 if (*current_timestamp
- now
> 0) {
467 * This fix is needed to overcome the timestamp filter putting
468 * events in the future.
470 sensorhub
->future_timestamp_total_ns
+=
471 *current_timestamp
- now
;
472 if (++sensorhub
->future_timestamp_count
==
473 FUTURE_TS_ANALYTICS_COUNT_MAX
) {
474 s64 avg
= div_s64(sensorhub
->future_timestamp_total_ns
,
475 sensorhub
->future_timestamp_count
);
476 dev_warn_ratelimited(sensorhub
->dev
,
477 "100 timestamps in the future, %lldns shaved on average\n",
479 sensorhub
->future_timestamp_count
= 0;
480 sensorhub
->future_timestamp_total_ns
= 0;
482 out
->timestamp
= now
;
484 out
->timestamp
= *current_timestamp
;
487 out
->flag
= in
->flags
;
488 for (axis
= 0; axis
< 3; axis
++)
489 out
->vector
[axis
] = in
->data
[axis
];
491 if (sensorhub
->tight_timestamps
)
492 cros_ec_sensor_ring_check_for_past_timestamp(sensorhub
, out
);
497 * cros_ec_sensor_ring_spread_add: Calculate proper timestamps then add to
500 * This is the new spreading code, assumes every sample's timestamp
501 * preceeds the sample. Run if tight_timestamps == true.
503 * Sometimes the EC receives only one interrupt (hence timestamp) for
504 * a batch of samples. Only the first sample will have the correct
505 * timestamp. So we must interpolate the other samples.
506 * We use the previous batch timestamp and our current batch timestamp
507 * as a way to calculate period, then spread the samples evenly.
512 * 30ms point goes by, no interrupt, previous one is still asserted
513 * downloading s2 and s3
514 * s3 sample, 20ms (incorrect timestamp)
517 * The batches are [(s0), (s1), (s2, s3), (s4)]. Since the 3rd batch
518 * has 2 samples in them, we adjust the timestamp of s3.
519 * s2 - s1 = 10ms, so s3 must be s2 + 10ms => 20ms. If s1 would have
520 * been part of a bigger batch things would have gotten a little
523 * Note: we also assume another sensor sample doesn't break up a batch
524 * in 2 or more partitions. Example, there can't ever be a sync sensor
525 * in between S2 and S3. This simplifies the following code.
528 cros_ec_sensor_ring_spread_add(struct cros_ec_sensorhub
*sensorhub
,
529 unsigned long sensor_mask
,
530 struct cros_ec_sensors_ring_sample
*last_out
)
532 struct cros_ec_sensors_ring_sample
*batch_start
, *next_batch_start
;
535 for_each_set_bit(id
, &sensor_mask
, sensorhub
->sensor_num
) {
536 for (batch_start
= sensorhub
->ring
; batch_start
< last_out
;
537 batch_start
= next_batch_start
) {
539 * For each batch (where all samples have the same
542 int batch_len
, sample_idx
;
543 struct cros_ec_sensors_ring_sample
*batch_end
=
545 struct cros_ec_sensors_ring_sample
*s
;
546 s64 batch_timestamp
= batch_start
->timestamp
;
550 * Skip over batches that start with the sensor types
551 * we're not looking at right now.
553 if (batch_start
->sensor_id
!= id
) {
554 next_batch_start
= batch_start
+ 1;
559 * Do not start a batch
560 * from a flush, as it happens asynchronously to the
561 * regular flow of events.
563 if (batch_start
->flag
& MOTIONSENSE_SENSOR_FLAG_FLUSH
) {
564 cros_sensorhub_send_sample(sensorhub
,
566 next_batch_start
= batch_start
+ 1;
570 if (batch_start
->timestamp
<=
571 sensorhub
->batch_state
[id
].last_ts
) {
573 sensorhub
->batch_state
[id
].last_ts
;
574 batch_len
= sensorhub
->batch_state
[id
].last_len
;
576 sample_idx
= batch_len
;
578 sensorhub
->batch_state
[id
].last_ts
=
579 sensorhub
->batch_state
[id
].penul_ts
;
580 sensorhub
->batch_state
[id
].last_len
=
581 sensorhub
->batch_state
[id
].penul_len
;
584 * Push first sample in the batch to the,
585 * kifo, it's guaranteed to be correct, the
586 * rest will follow later on.
590 cros_sensorhub_send_sample(sensorhub
,
595 /* Find all samples have the same timestamp. */
596 for (s
= batch_start
; s
< last_out
; s
++) {
597 if (s
->sensor_id
!= id
)
599 * Skip over other sensor types that
600 * are interleaved, don't count them.
603 if (s
->timestamp
!= batch_timestamp
)
604 /* we discovered the next batch */
606 if (s
->flag
& MOTIONSENSE_SENSOR_FLAG_FLUSH
)
607 /* break on flush packets */
614 goto done_with_this_batch
;
616 /* Can we calculate period? */
617 if (sensorhub
->batch_state
[id
].last_len
== 0) {
618 dev_warn(sensorhub
->dev
, "Sensor %d: lost %d samples when spreading\n",
620 goto done_with_this_batch
;
622 * Note: we're dropping the rest of the samples
623 * in this batch since we have no idea where
624 * they're supposed to go without a period
629 sample_period
= div_s64(batch_timestamp
-
630 sensorhub
->batch_state
[id
].last_ts
,
631 sensorhub
->batch_state
[id
].last_len
);
632 dev_dbg(sensorhub
->dev
,
633 "Adjusting %d samples, sensor %d last_batch @%lld (%d samples) batch_timestamp=%lld => period=%lld\n",
635 sensorhub
->batch_state
[id
].last_ts
,
636 sensorhub
->batch_state
[id
].last_len
,
641 * Adjust timestamps of the samples then push them to
644 for (s
= batch_start
; s
<= batch_end
; s
++) {
645 if (s
->sensor_id
!= id
)
647 * Skip over other sensor types that
648 * are interleaved, don't change them.
652 s
->timestamp
= batch_timestamp
+
653 sample_period
* sample_idx
;
656 cros_sensorhub_send_sample(sensorhub
, s
);
659 done_with_this_batch
:
660 sensorhub
->batch_state
[id
].penul_ts
=
661 sensorhub
->batch_state
[id
].last_ts
;
662 sensorhub
->batch_state
[id
].penul_len
=
663 sensorhub
->batch_state
[id
].last_len
;
665 sensorhub
->batch_state
[id
].last_ts
=
667 sensorhub
->batch_state
[id
].last_len
= batch_len
;
669 next_batch_start
= batch_end
+ 1;
675 * cros_ec_sensor_ring_spread_add_legacy: Calculate proper timestamps then
676 * add to ringbuffer (legacy).
678 * Note: This assumes we're running old firmware, where every sample's timestamp
679 * is after the sample. Run if tight_timestamps == false.
681 * If there is a sample with a proper timestamp
685 * older_unprocess_out --> TS1 | 1
690 * We spread time for the samples [older_unprocess_out .. out]
691 * between TS1 and TS2: [TS1+1/4, TS1+2/4, TS1+3/4, TS2].
693 * If we reach the end of the samples, we compare with the
696 * older_unprocess_out --> TS1 | 1
700 * We know have [TS1+1/3, TS1+2/3, current timestamp]
703 cros_ec_sensor_ring_spread_add_legacy(struct cros_ec_sensorhub
*sensorhub
,
704 unsigned long sensor_mask
,
705 s64 current_timestamp
,
706 struct cros_ec_sensors_ring_sample
709 struct cros_ec_sensors_ring_sample
*out
;
712 for_each_set_bit(i
, &sensor_mask
, sensorhub
->sensor_num
) {
715 struct cros_ec_sensors_ring_sample
*older_unprocess_out
=
717 struct cros_ec_sensors_ring_sample
*next_out
;
720 for (out
= sensorhub
->ring
; out
< last_out
; out
= next_out
) {
724 if (out
->sensor_id
!= i
)
727 /* Timestamp to start with */
728 older_timestamp
= out
->timestamp
;
730 /* Find next sample. */
731 while (next_out
< last_out
&& next_out
->sensor_id
!= i
)
734 if (next_out
>= last_out
) {
735 timestamp
= current_timestamp
;
737 timestamp
= next_out
->timestamp
;
738 if (timestamp
== older_timestamp
) {
745 * The next sample has a new timestamp, spread the
746 * unprocessed samples.
748 if (next_out
< last_out
)
750 time_period
= div_s64(timestamp
- older_timestamp
,
753 for (; older_unprocess_out
<= out
;
754 older_unprocess_out
++) {
755 if (older_unprocess_out
->sensor_id
!= i
)
757 older_timestamp
+= time_period
;
758 older_unprocess_out
->timestamp
=
762 /* The next_out sample has a valid timestamp, skip. */
764 older_unprocess_out
= next_out
;
768 /* Push the event into the kfifo */
769 for (out
= sensorhub
->ring
; out
< last_out
; out
++)
770 cros_sensorhub_send_sample(sensorhub
, out
);
774 * cros_ec_sensorhub_ring_handler() - The trigger handler function
776 * @sensorhub: Sensor Hub object.
778 * Called by the notifier, process the EC sensor FIFO queue.
780 static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub
*sensorhub
)
782 struct ec_response_motion_sense_fifo_info
*fifo_info
=
783 sensorhub
->fifo_info
;
784 struct cros_ec_dev
*ec
= sensorhub
->ec
;
785 ktime_t fifo_timestamp
, current_timestamp
;
786 int i
, j
, number_data
, ret
;
787 unsigned long sensor_mask
= 0;
788 struct ec_response_motion_sensor_data
*in
;
789 struct cros_ec_sensors_ring_sample
*out
, *last_out
;
791 mutex_lock(&sensorhub
->cmd_lock
);
793 /* Get FIFO information if there are lost vectors. */
794 if (fifo_info
->total_lost
) {
795 int fifo_info_length
=
796 sizeof(struct ec_response_motion_sense_fifo_info
) +
797 sizeof(u16
) * sensorhub
->sensor_num
;
799 /* Need to retrieve the number of lost vectors per sensor */
800 sensorhub
->params
->cmd
= MOTIONSENSE_CMD_FIFO_INFO
;
801 sensorhub
->msg
->outsize
= 1;
802 sensorhub
->msg
->insize
= fifo_info_length
;
804 if (cros_ec_cmd_xfer_status(ec
->ec_dev
, sensorhub
->msg
) < 0)
807 memcpy(fifo_info
, &sensorhub
->resp
->fifo_info
,
811 * Update collection time, will not be as precise as the
814 fifo_timestamp
= cros_ec_get_time_ns();
816 fifo_timestamp
= sensorhub
->fifo_timestamp
[
817 CROS_EC_SENSOR_NEW_TS
];
820 if (fifo_info
->count
> sensorhub
->fifo_size
||
821 fifo_info
->size
!= sensorhub
->fifo_size
) {
822 dev_warn(sensorhub
->dev
,
823 "Mismatch EC data: count %d, size %d - expected %d\n",
824 fifo_info
->count
, fifo_info
->size
,
825 sensorhub
->fifo_size
);
829 /* Copy elements in the main fifo */
830 current_timestamp
= sensorhub
->fifo_timestamp
[CROS_EC_SENSOR_LAST_TS
];
831 out
= sensorhub
->ring
;
832 for (i
= 0; i
< fifo_info
->count
; i
+= number_data
) {
833 sensorhub
->params
->cmd
= MOTIONSENSE_CMD_FIFO_READ
;
834 sensorhub
->params
->fifo_read
.max_data_vector
=
835 fifo_info
->count
- i
;
836 sensorhub
->msg
->outsize
=
837 sizeof(struct ec_params_motion_sense
);
838 sensorhub
->msg
->insize
=
839 sizeof(sensorhub
->resp
->fifo_read
) +
840 sensorhub
->params
->fifo_read
.max_data_vector
*
841 sizeof(struct ec_response_motion_sensor_data
);
842 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, sensorhub
->msg
);
844 dev_warn(sensorhub
->dev
, "Fifo error: %d\n", ret
);
847 number_data
= sensorhub
->resp
->fifo_read
.number_data
;
848 if (number_data
== 0) {
849 dev_dbg(sensorhub
->dev
, "Unexpected empty FIFO\n");
852 if (number_data
> fifo_info
->count
- i
) {
853 dev_warn(sensorhub
->dev
,
854 "Invalid EC data: too many entry received: %d, expected %d\n",
855 number_data
, fifo_info
->count
- i
);
858 if (out
+ number_data
>
859 sensorhub
->ring
+ fifo_info
->count
) {
860 dev_warn(sensorhub
->dev
,
861 "Too many samples: %d (%zd data) to %d entries for expected %d entries\n",
862 i
, out
- sensorhub
->ring
, i
+ number_data
,
867 for (in
= sensorhub
->resp
->fifo_read
.data
, j
= 0;
868 j
< number_data
; j
++, in
++) {
869 if (cros_ec_sensor_ring_process_event(
870 sensorhub
, fifo_info
,
874 sensor_mask
|= BIT(in
->sensor_num
);
879 mutex_unlock(&sensorhub
->cmd_lock
);
882 if (out
== sensorhub
->ring
)
883 /* Unexpected empty FIFO. */
884 goto ring_handler_end
;
887 * Check if current_timestamp is ahead of the last sample. Normally,
888 * the EC appends a timestamp after the last sample, but if the AP
889 * is slow to respond to the IRQ, the EC may have added new samples.
890 * Use the FIFO info timestamp as last timestamp then.
892 if (!sensorhub
->tight_timestamps
&&
893 (last_out
- 1)->timestamp
== current_timestamp
)
894 current_timestamp
= fifo_timestamp
;
896 /* Warn on lost samples. */
897 if (fifo_info
->total_lost
)
898 for (i
= 0; i
< sensorhub
->sensor_num
; i
++) {
899 if (fifo_info
->lost
[i
]) {
900 dev_warn_ratelimited(sensorhub
->dev
,
901 "Sensor %d: lost: %d out of %d\n",
902 i
, fifo_info
->lost
[i
],
903 fifo_info
->total_lost
);
904 if (sensorhub
->tight_timestamps
)
905 sensorhub
->batch_state
[i
].last_len
= 0;
910 * Spread samples in case of batching, then add them to the
913 if (sensorhub
->tight_timestamps
)
914 cros_ec_sensor_ring_spread_add(sensorhub
, sensor_mask
,
917 cros_ec_sensor_ring_spread_add_legacy(sensorhub
, sensor_mask
,
922 sensorhub
->fifo_timestamp
[CROS_EC_SENSOR_LAST_TS
] = current_timestamp
;
926 mutex_unlock(&sensorhub
->cmd_lock
);
929 static int cros_ec_sensorhub_event(struct notifier_block
*nb
,
930 unsigned long queued_during_suspend
,
933 struct cros_ec_sensorhub
*sensorhub
;
934 struct cros_ec_device
*ec_dev
;
936 sensorhub
= container_of(nb
, struct cros_ec_sensorhub
, notifier
);
937 ec_dev
= sensorhub
->ec
->ec_dev
;
939 if (ec_dev
->event_data
.event_type
!= EC_MKBP_EVENT_SENSOR_FIFO
)
942 if (ec_dev
->event_size
!= sizeof(ec_dev
->event_data
.data
.sensor_fifo
)) {
943 dev_warn(ec_dev
->dev
, "Invalid fifo info size\n");
947 if (queued_during_suspend
)
950 memcpy(sensorhub
->fifo_info
, &ec_dev
->event_data
.data
.sensor_fifo
.info
,
951 sizeof(*sensorhub
->fifo_info
));
952 sensorhub
->fifo_timestamp
[CROS_EC_SENSOR_NEW_TS
] =
953 ec_dev
->last_event_time
;
954 cros_ec_sensorhub_ring_handler(sensorhub
);
960 * cros_ec_sensorhub_ring_allocate() - Prepare the FIFO functionality if the EC
963 * @sensorhub : Sensor Hub object.
965 * Return: 0 on success.
967 int cros_ec_sensorhub_ring_allocate(struct cros_ec_sensorhub
*sensorhub
)
969 int fifo_info_length
=
970 sizeof(struct ec_response_motion_sense_fifo_info
) +
971 sizeof(u16
) * sensorhub
->sensor_num
;
973 /* Allocate the array for lost events. */
974 sensorhub
->fifo_info
= devm_kzalloc(sensorhub
->dev
, fifo_info_length
,
976 if (!sensorhub
->fifo_info
)
980 * Allocate the callback area based on the number of sensors.
981 * Add one for the sensor ring.
983 sensorhub
->push_data
= devm_kcalloc(sensorhub
->dev
,
984 sensorhub
->sensor_num
,
985 sizeof(*sensorhub
->push_data
),
987 if (!sensorhub
->push_data
)
990 sensorhub
->tight_timestamps
= cros_ec_check_features(
992 EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS
);
994 if (sensorhub
->tight_timestamps
) {
995 sensorhub
->batch_state
= devm_kcalloc(sensorhub
->dev
,
996 sensorhub
->sensor_num
,
997 sizeof(*sensorhub
->batch_state
),
999 if (!sensorhub
->batch_state
)
1007 * cros_ec_sensorhub_ring_add() - Add the FIFO functionality if the EC
1010 * @sensorhub : Sensor Hub object.
1012 * Return: 0 on success.
1014 int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub
*sensorhub
)
1016 struct cros_ec_dev
*ec
= sensorhub
->ec
;
1018 int fifo_info_length
=
1019 sizeof(struct ec_response_motion_sense_fifo_info
) +
1020 sizeof(u16
) * sensorhub
->sensor_num
;
1022 /* Retrieve FIFO information */
1023 sensorhub
->msg
->version
= 2;
1024 sensorhub
->params
->cmd
= MOTIONSENSE_CMD_FIFO_INFO
;
1025 sensorhub
->msg
->outsize
= 1;
1026 sensorhub
->msg
->insize
= fifo_info_length
;
1028 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, sensorhub
->msg
);
1033 * Allocate the full fifo. We need to copy the whole FIFO to set
1034 * timestamps properly.
1036 sensorhub
->fifo_size
= sensorhub
->resp
->fifo_info
.size
;
1037 sensorhub
->ring
= devm_kcalloc(sensorhub
->dev
, sensorhub
->fifo_size
,
1038 sizeof(*sensorhub
->ring
), GFP_KERNEL
);
1039 if (!sensorhub
->ring
)
1042 sensorhub
->fifo_timestamp
[CROS_EC_SENSOR_LAST_TS
] =
1043 cros_ec_get_time_ns();
1045 /* Register the notifier that will act as a top half interrupt. */
1046 sensorhub
->notifier
.notifier_call
= cros_ec_sensorhub_event
;
1047 ret
= blocking_notifier_chain_register(&ec
->ec_dev
->event_notifier
,
1048 &sensorhub
->notifier
);
1052 /* Start collection samples. */
1053 return cros_ec_sensorhub_ring_fifo_enable(sensorhub
, true);
1056 void cros_ec_sensorhub_ring_remove(void *arg
)
1058 struct cros_ec_sensorhub
*sensorhub
= arg
;
1059 struct cros_ec_device
*ec_dev
= sensorhub
->ec
->ec_dev
;
1061 /* Disable the ring, prevent EC interrupt to the AP for nothing. */
1062 cros_ec_sensorhub_ring_fifo_enable(sensorhub
, false);
1063 blocking_notifier_chain_unregister(&ec_dev
->event_notifier
,
1064 &sensorhub
->notifier
);