614 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2020 Invensense, Inc.4 */5 6#include <linux/kernel.h>7#include <linux/device.h>8#include <linux/mutex.h>9#include <linux/pm_runtime.h>10#include <linux/regmap.h>11#include <linux/delay.h>12 13#include <linux/iio/buffer.h>14#include <linux/iio/common/inv_sensors_timestamp.h>15#include <linux/iio/iio.h>16 17#include "inv_icm42600.h"18#include "inv_icm42600_buffer.h"19 20/* FIFO header: 1 byte */21#define INV_ICM42600_FIFO_HEADER_MSG BIT(7)22#define INV_ICM42600_FIFO_HEADER_ACCEL BIT(6)23#define INV_ICM42600_FIFO_HEADER_GYRO BIT(5)24#define INV_ICM42600_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)25#define INV_ICM42600_FIFO_HEADER_ODR_ACCEL BIT(1)26#define INV_ICM42600_FIFO_HEADER_ODR_GYRO BIT(0)27 28struct inv_icm42600_fifo_1sensor_packet {29 uint8_t header;30 struct inv_icm42600_fifo_sensor_data data;31 int8_t temp;32} __packed;33#define INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE 834 35struct inv_icm42600_fifo_2sensors_packet {36 uint8_t header;37 struct inv_icm42600_fifo_sensor_data accel;38 struct inv_icm42600_fifo_sensor_data gyro;39 int8_t temp;40 __be16 timestamp;41} __packed;42#define INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE 1643 44ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel,45 const void **gyro, const int8_t **temp,46 const void **timestamp, unsigned int *odr)47{48 const struct inv_icm42600_fifo_1sensor_packet *pack1 = packet;49 const struct inv_icm42600_fifo_2sensors_packet *pack2 = packet;50 uint8_t header = *((const uint8_t *)packet);51 52 /* FIFO empty */53 if (header & INV_ICM42600_FIFO_HEADER_MSG) {54 *accel = NULL;55 *gyro = NULL;56 *temp = NULL;57 *timestamp = NULL;58 *odr = 0;59 return 0;60 }61 62 /* handle odr flags */63 *odr = 0;64 if (header & INV_ICM42600_FIFO_HEADER_ODR_GYRO)65 *odr |= INV_ICM42600_SENSOR_GYRO;66 if (header & INV_ICM42600_FIFO_HEADER_ODR_ACCEL)67 *odr |= INV_ICM42600_SENSOR_ACCEL;68 69 /* accel + gyro */70 if ((header & INV_ICM42600_FIFO_HEADER_ACCEL) &&71 (header & INV_ICM42600_FIFO_HEADER_GYRO)) {72 *accel = &pack2->accel;73 *gyro = &pack2->gyro;74 *temp = &pack2->temp;75 *timestamp = &pack2->timestamp;76 return INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;77 }78 79 /* accel only */80 if (header & INV_ICM42600_FIFO_HEADER_ACCEL) {81 *accel = &pack1->data;82 *gyro = NULL;83 *temp = &pack1->temp;84 *timestamp = NULL;85 return INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;86 }87 88 /* gyro only */89 if (header & INV_ICM42600_FIFO_HEADER_GYRO) {90 *accel = NULL;91 *gyro = &pack1->data;92 *temp = &pack1->temp;93 *timestamp = NULL;94 return INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;95 }96 97 /* invalid packet if here */98 return -EINVAL;99}100 101void inv_icm42600_buffer_update_fifo_period(struct inv_icm42600_state *st)102{103 uint32_t period_gyro, period_accel, period;104 105 if (st->fifo.en & INV_ICM42600_SENSOR_GYRO)106 period_gyro = inv_icm42600_odr_to_period(st->conf.gyro.odr);107 else108 period_gyro = U32_MAX;109 110 if (st->fifo.en & INV_ICM42600_SENSOR_ACCEL)111 period_accel = inv_icm42600_odr_to_period(st->conf.accel.odr);112 else113 period_accel = U32_MAX;114 115 if (period_gyro <= period_accel)116 period = period_gyro;117 else118 period = period_accel;119 120 st->fifo.period = period;121}122 123int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st,124 unsigned int fifo_en)125{126 unsigned int mask, val;127 int ret;128 129 /* update only FIFO EN bits */130 mask = INV_ICM42600_FIFO_CONFIG1_TMST_FSYNC_EN |131 INV_ICM42600_FIFO_CONFIG1_TEMP_EN |132 INV_ICM42600_FIFO_CONFIG1_GYRO_EN |133 INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;134 135 val = 0;136 if (fifo_en & INV_ICM42600_SENSOR_GYRO)137 val |= INV_ICM42600_FIFO_CONFIG1_GYRO_EN;138 if (fifo_en & INV_ICM42600_SENSOR_ACCEL)139 val |= INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;140 if (fifo_en & INV_ICM42600_SENSOR_TEMP)141 val |= INV_ICM42600_FIFO_CONFIG1_TEMP_EN;142 143 ret = regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1, mask, val);144 if (ret)145 return ret;146 147 st->fifo.en = fifo_en;148 inv_icm42600_buffer_update_fifo_period(st);149 150 return 0;151}152 153static size_t inv_icm42600_get_packet_size(unsigned int fifo_en)154{155 size_t packet_size;156 157 if ((fifo_en & INV_ICM42600_SENSOR_GYRO) &&158 (fifo_en & INV_ICM42600_SENSOR_ACCEL))159 packet_size = INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;160 else161 packet_size = INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;162 163 return packet_size;164}165 166static unsigned int inv_icm42600_wm_truncate(unsigned int watermark,167 size_t packet_size)168{169 size_t wm_size;170 unsigned int wm;171 172 wm_size = watermark * packet_size;173 if (wm_size > INV_ICM42600_FIFO_WATERMARK_MAX)174 wm_size = INV_ICM42600_FIFO_WATERMARK_MAX;175 176 wm = wm_size / packet_size;177 178 return wm;179}180 181/**182 * inv_icm42600_buffer_update_watermark - update watermark FIFO threshold183 * @st: driver internal state184 *185 * Returns 0 on success, a negative error code otherwise.186 *187 * FIFO watermark threshold is computed based on the required watermark values188 * set for gyro and accel sensors. Since watermark is all about acceptable data189 * latency, use the smallest setting between the 2. It means choosing the190 * smallest latency but this is not as simple as choosing the smallest watermark191 * value. Latency depends on watermark and ODR. It requires several steps:192 * 1) compute gyro and accel latencies and choose the smallest value.193 * 2) adapt the choosen latency so that it is a multiple of both gyro and accel194 * ones. Otherwise it is possible that you don't meet a requirement. (for195 * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the196 * value of 4 will not meet accel latency requirement because 6 is not a197 * multiple of 4. You need to use the value 2.)198 * 3) Since all periods are multiple of each others, watermark is computed by199 * dividing this computed latency by the smallest period, which corresponds200 * to the FIFO frequency. Beware that this is only true because we are not201 * using 500Hz frequency which is not a multiple of the others.202 */203int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)204{205 size_t packet_size, wm_size;206 unsigned int wm_gyro, wm_accel, watermark;207 uint32_t period_gyro, period_accel, period;208 uint32_t latency_gyro, latency_accel, latency;209 bool restore;210 __le16 raw_wm;211 int ret;212 213 packet_size = inv_icm42600_get_packet_size(st->fifo.en);214 215 /* compute sensors latency, depending on sensor watermark and odr */216 wm_gyro = inv_icm42600_wm_truncate(st->fifo.watermark.gyro, packet_size);217 wm_accel = inv_icm42600_wm_truncate(st->fifo.watermark.accel, packet_size);218 /* use us for odr to avoid overflow using 32 bits values */219 period_gyro = inv_icm42600_odr_to_period(st->conf.gyro.odr) / 1000UL;220 period_accel = inv_icm42600_odr_to_period(st->conf.accel.odr) / 1000UL;221 latency_gyro = period_gyro * wm_gyro;222 latency_accel = period_accel * wm_accel;223 224 /* 0 value for watermark means that the sensor is turned off */225 if (wm_gyro == 0 && wm_accel == 0)226 return 0;227 228 if (latency_gyro == 0) {229 watermark = wm_accel;230 st->fifo.watermark.eff_accel = wm_accel;231 } else if (latency_accel == 0) {232 watermark = wm_gyro;233 st->fifo.watermark.eff_gyro = wm_gyro;234 } else {235 /* compute the smallest latency that is a multiple of both */236 if (latency_gyro <= latency_accel)237 latency = latency_gyro - (latency_accel % latency_gyro);238 else239 latency = latency_accel - (latency_gyro % latency_accel);240 /* use the shortest period */241 if (period_gyro <= period_accel)242 period = period_gyro;243 else244 period = period_accel;245 /* all this works because periods are multiple of each others */246 watermark = latency / period;247 if (watermark < 1)248 watermark = 1;249 /* update effective watermark */250 st->fifo.watermark.eff_gyro = latency / period_gyro;251 if (st->fifo.watermark.eff_gyro < 1)252 st->fifo.watermark.eff_gyro = 1;253 st->fifo.watermark.eff_accel = latency / period_accel;254 if (st->fifo.watermark.eff_accel < 1)255 st->fifo.watermark.eff_accel = 1;256 }257 258 /* compute watermark value in bytes */259 wm_size = watermark * packet_size;260 261 /* changing FIFO watermark requires to turn off watermark interrupt */262 ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0,263 INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN,264 0, &restore);265 if (ret)266 return ret;267 268 raw_wm = INV_ICM42600_FIFO_WATERMARK_VAL(wm_size);269 memcpy(st->buffer, &raw_wm, sizeof(raw_wm));270 ret = regmap_bulk_write(st->map, INV_ICM42600_REG_FIFO_WATERMARK,271 st->buffer, sizeof(raw_wm));272 if (ret)273 return ret;274 275 /* restore watermark interrupt */276 if (restore) {277 ret = regmap_set_bits(st->map, INV_ICM42600_REG_INT_SOURCE0,278 INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN);279 if (ret)280 return ret;281 }282 283 return 0;284}285 286static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev)287{288 struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);289 struct device *dev = regmap_get_device(st->map);290 struct inv_icm42600_sensor_state *sensor_st = iio_priv(indio_dev);291 struct inv_sensors_timestamp *ts = &sensor_st->ts;292 293 pm_runtime_get_sync(dev);294 295 mutex_lock(&st->lock);296 inv_sensors_timestamp_reset(ts);297 mutex_unlock(&st->lock);298 299 return 0;300}301 302/*303 * update_scan_mode callback is turning sensors on and setting data FIFO enable304 * bits.305 */306static int inv_icm42600_buffer_postenable(struct iio_dev *indio_dev)307{308 struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);309 int ret;310 311 mutex_lock(&st->lock);312 313 /* exit if FIFO is already on */314 if (st->fifo.on) {315 ret = 0;316 goto out_on;317 }318 319 /* set FIFO threshold interrupt */320 ret = regmap_set_bits(st->map, INV_ICM42600_REG_INT_SOURCE0,321 INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN);322 if (ret)323 goto out_unlock;324 325 /* flush FIFO data */326 ret = regmap_write(st->map, INV_ICM42600_REG_SIGNAL_PATH_RESET,327 INV_ICM42600_SIGNAL_PATH_RESET_FIFO_FLUSH);328 if (ret)329 goto out_unlock;330 331 /* set FIFO in streaming mode */332 ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,333 INV_ICM42600_FIFO_CONFIG_STREAM);334 if (ret)335 goto out_unlock;336 337 /* workaround: first read of FIFO count after reset is always 0 */338 ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT, st->buffer, 2);339 if (ret)340 goto out_unlock;341 342out_on:343 /* increase FIFO on counter */344 st->fifo.on++;345out_unlock:346 mutex_unlock(&st->lock);347 return ret;348}349 350static int inv_icm42600_buffer_predisable(struct iio_dev *indio_dev)351{352 struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);353 int ret;354 355 mutex_lock(&st->lock);356 357 /* exit if there are several sensors using the FIFO */358 if (st->fifo.on > 1) {359 ret = 0;360 goto out_off;361 }362 363 /* set FIFO in bypass mode */364 ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,365 INV_ICM42600_FIFO_CONFIG_BYPASS);366 if (ret)367 goto out_unlock;368 369 /* flush FIFO data */370 ret = regmap_write(st->map, INV_ICM42600_REG_SIGNAL_PATH_RESET,371 INV_ICM42600_SIGNAL_PATH_RESET_FIFO_FLUSH);372 if (ret)373 goto out_unlock;374 375 /* disable FIFO threshold interrupt */376 ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_SOURCE0,377 INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN);378 if (ret)379 goto out_unlock;380 381out_off:382 /* decrease FIFO on counter */383 st->fifo.on--;384out_unlock:385 mutex_unlock(&st->lock);386 return ret;387}388 389static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)390{391 struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);392 struct device *dev = regmap_get_device(st->map);393 unsigned int sensor;394 unsigned int *watermark;395 struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;396 unsigned int sleep_temp = 0;397 unsigned int sleep_sensor = 0;398 unsigned int sleep;399 int ret;400 401 if (indio_dev == st->indio_gyro) {402 sensor = INV_ICM42600_SENSOR_GYRO;403 watermark = &st->fifo.watermark.gyro;404 } else if (indio_dev == st->indio_accel) {405 sensor = INV_ICM42600_SENSOR_ACCEL;406 watermark = &st->fifo.watermark.accel;407 } else {408 return -EINVAL;409 }410 411 mutex_lock(&st->lock);412 413 ret = inv_icm42600_buffer_set_fifo_en(st, st->fifo.en & ~sensor);414 if (ret)415 goto out_unlock;416 417 *watermark = 0;418 ret = inv_icm42600_buffer_update_watermark(st);419 if (ret)420 goto out_unlock;421 422 conf.mode = INV_ICM42600_SENSOR_MODE_OFF;423 if (sensor == INV_ICM42600_SENSOR_GYRO)424 ret = inv_icm42600_set_gyro_conf(st, &conf, &sleep_sensor);425 else426 ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_sensor);427 if (ret)428 goto out_unlock;429 430 /* if FIFO is off, turn temperature off */431 if (!st->fifo.on)432 ret = inv_icm42600_set_temp_conf(st, false, &sleep_temp);433 434out_unlock:435 mutex_unlock(&st->lock);436 437 /* sleep maximum required time */438 sleep = max(sleep_sensor, sleep_temp);439 if (sleep)440 msleep(sleep);441 442 pm_runtime_mark_last_busy(dev);443 pm_runtime_put_autosuspend(dev);444 445 return ret;446}447 448const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = {449 .preenable = inv_icm42600_buffer_preenable,450 .postenable = inv_icm42600_buffer_postenable,451 .predisable = inv_icm42600_buffer_predisable,452 .postdisable = inv_icm42600_buffer_postdisable,453};454 455int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,456 unsigned int max)457{458 size_t max_count;459 __be16 *raw_fifo_count;460 ssize_t i, size;461 const void *accel, *gyro, *timestamp;462 const int8_t *temp;463 unsigned int odr;464 int ret;465 466 /* reset all samples counters */467 st->fifo.count = 0;468 st->fifo.nb.gyro = 0;469 st->fifo.nb.accel = 0;470 st->fifo.nb.total = 0;471 472 /* compute maximum FIFO read size */473 if (max == 0)474 max_count = sizeof(st->fifo.data);475 else476 max_count = max * inv_icm42600_get_packet_size(st->fifo.en);477 478 /* read FIFO count value */479 raw_fifo_count = (__be16 *)st->buffer;480 ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT,481 raw_fifo_count, sizeof(*raw_fifo_count));482 if (ret)483 return ret;484 st->fifo.count = be16_to_cpup(raw_fifo_count);485 486 /* check and clamp FIFO count value */487 if (st->fifo.count == 0)488 return 0;489 if (st->fifo.count > max_count)490 st->fifo.count = max_count;491 492 /* read all FIFO data in internal buffer */493 ret = regmap_noinc_read(st->map, INV_ICM42600_REG_FIFO_DATA,494 st->fifo.data, st->fifo.count);495 if (ret)496 return ret;497 498 /* compute number of samples for each sensor */499 for (i = 0; i < st->fifo.count; i += size) {500 size = inv_icm42600_fifo_decode_packet(&st->fifo.data[i],501 &accel, &gyro, &temp, ×tamp, &odr);502 if (size <= 0)503 break;504 if (gyro != NULL && inv_icm42600_fifo_is_data_valid(gyro))505 st->fifo.nb.gyro++;506 if (accel != NULL && inv_icm42600_fifo_is_data_valid(accel))507 st->fifo.nb.accel++;508 st->fifo.nb.total++;509 }510 511 return 0;512}513 514int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)515{516 struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro);517 struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel);518 struct inv_sensors_timestamp *ts;519 int ret;520 521 if (st->fifo.nb.total == 0)522 return 0;523 524 /* handle gyroscope timestamp and FIFO data parsing */525 if (st->fifo.nb.gyro > 0) {526 ts = &gyro_st->ts;527 inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro,528 st->timestamp.gyro);529 ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);530 if (ret)531 return ret;532 }533 534 /* handle accelerometer timestamp and FIFO data parsing */535 if (st->fifo.nb.accel > 0) {536 ts = &accel_st->ts;537 inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel,538 st->timestamp.accel);539 ret = inv_icm42600_accel_parse_fifo(st->indio_accel);540 if (ret)541 return ret;542 }543 544 return 0;545}546 547int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,548 unsigned int count)549{550 struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro);551 struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel);552 struct inv_sensors_timestamp *ts;553 int64_t gyro_ts, accel_ts;554 int ret;555 556 gyro_ts = iio_get_time_ns(st->indio_gyro);557 accel_ts = iio_get_time_ns(st->indio_accel);558 559 ret = inv_icm42600_buffer_fifo_read(st, count);560 if (ret)561 return ret;562 563 if (st->fifo.nb.total == 0)564 return 0;565 566 if (st->fifo.nb.gyro > 0) {567 ts = &gyro_st->ts;568 inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);569 ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);570 if (ret)571 return ret;572 }573 574 if (st->fifo.nb.accel > 0) {575 ts = &accel_st->ts;576 inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);577 ret = inv_icm42600_accel_parse_fifo(st->indio_accel);578 if (ret)579 return ret;580 }581 582 return 0;583}584 585int inv_icm42600_buffer_init(struct inv_icm42600_state *st)586{587 unsigned int val;588 int ret;589 590 st->fifo.watermark.eff_gyro = 1;591 st->fifo.watermark.eff_accel = 1;592 593 /*594 * Default FIFO configuration (bits 7 to 5)595 * - use invalid value596 * - FIFO count in bytes597 * - FIFO count in big endian598 */599 val = INV_ICM42600_INTF_CONFIG0_FIFO_COUNT_ENDIAN;600 ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG0,601 GENMASK(7, 5), val);602 if (ret)603 return ret;604 605 /*606 * Enable FIFO partial read and continuous watermark interrupt.607 * Disable all FIFO EN bits.608 */609 val = INV_ICM42600_FIFO_CONFIG1_RESUME_PARTIAL_RD |610 INV_ICM42600_FIFO_CONFIG1_WM_GT_TH;611 return regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1,612 GENMASK(6, 5) | GENMASK(3, 0), val);613}614