336 lines
8.6 KiB
C
336 lines
8.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/err.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/math64.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#include "inv_icm42670.h"
|
|
|
|
/**
|
|
* icm42670_update_period() - Update chip internal period estimation
|
|
*
|
|
* @st: driver state
|
|
* @timestamp: the interrupt timestamp
|
|
* @nb: number of data set in the fifo
|
|
*
|
|
* This function uses interrupt timestamps to estimate the chip period and
|
|
* to choose the data timestamp to come.
|
|
*/
|
|
static void icm42670_update_period(struct icm42670_data *st,
|
|
s64 timestamp, size_t nb)
|
|
{
|
|
s64 interval;
|
|
|
|
if (st->it_timestamp != 0)
|
|
st->interrupt_period = div_s64((timestamp - st->it_timestamp), nb);
|
|
|
|
interval = (nb - 1) * st->interrupt_period;
|
|
st->data_timestamp = timestamp - interval;
|
|
/* save it timestamp */
|
|
st->it_timestamp = timestamp;
|
|
}
|
|
|
|
/**
|
|
* icm42670_get_timestamp() - Return the current data timestamp
|
|
*
|
|
* @st: driver state
|
|
* @return: current data timestamp
|
|
*
|
|
* This function returns the current data timestamp and prepares for next one.
|
|
*/
|
|
static s64 icm42670_get_timestamp(struct icm42670_data *st)
|
|
{
|
|
s64 ts;
|
|
|
|
/* return current data timestamp and increment */
|
|
ts = st->data_timestamp;
|
|
st->data_timestamp += st->interrupt_period;
|
|
|
|
return ts;
|
|
}
|
|
|
|
int icm42670_reset_fifo(struct iio_dev *indio_dev)
|
|
{
|
|
int ret;
|
|
struct icm42670_data *st = iio_priv(indio_dev);
|
|
const struct device *dev = regmap_get_device(st->regmap);
|
|
|
|
/* reset it timestamp validation */
|
|
st->it_timestamp = 0;
|
|
|
|
/* Before setting the FIFO water level, make sure the interrupt source is disabled. */
|
|
ret = regmap_write(st->regmap, REG_INT_SOURCE0, BIT_INT_MODE_OFF);
|
|
if (ret) {
|
|
dev_err(dev, "int_enable failed %d\n",
|
|
ret);
|
|
goto reset_fifo_fail;
|
|
}
|
|
|
|
/* disable the sensor output to FIFO */
|
|
ret = regmap_write(st->regmap, REG_FIFO_CONFIG1, BIT_FIFO_MODE_BYPASS);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_FIFO_CONFIG1 register!\n");
|
|
goto reset_fifo_fail;
|
|
}
|
|
|
|
/* reset FIFO*/
|
|
ret = regmap_update_bits(st->regmap, REG_SIGNAL_PATH_RESET,
|
|
BIT_FIFO_FLUSH, BIT_FIFO_FLUSH);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_SIGNAL_PATH_RESET register!\n");
|
|
goto reset_fifo_fail;
|
|
}
|
|
ndelay(1500); //wait for 1.5us
|
|
|
|
/* enable sensor output to FIFO */
|
|
ret = regmap_write(st->regmap, REG_FIFO_CONFIG1,
|
|
BIT_FIFO_MODE_NO_BYPASS | BIT_FIFO_MODE_STOPFULL);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_FIFO_CONFIG1 register!\n");
|
|
goto reset_fifo_fail;
|
|
}
|
|
|
|
ret = icm42670_mreg_single_write(st, REG_TMST_CONFIG1_MREG_TOP1,
|
|
BIT_TMST_EN | BIT_TMST_FSYNC_EN);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_TMST_CONFIG1_MREG_TOP1 register!\n");
|
|
goto reset_fifo_fail;
|
|
}
|
|
|
|
/*
|
|
* FIFO_COUNT_FORMAT setting. FIFO_WM_EN must be zero before writing this register.
|
|
* Interrupt only fires once. This register should be set to nonzero value,
|
|
* before choosing this interrupt source.
|
|
* This field should be changed when FIFO is empty to avoid spurious interrupts
|
|
*/
|
|
ret = regmap_write(st->regmap, REG_FIFO_CONFIG2, BIT_FIFO_WM5);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_FIFO_CONFIG2 register!\n");
|
|
goto reset_fifo_fail;
|
|
}
|
|
|
|
ret = icm42670_mreg_single_write(st, REG_FIFO_CONFIG5_MREG_TOP1,
|
|
BIT_WM_GT_TH | BIT_FIFO_ACCEL_EN |
|
|
BIT_FIFO_GYRO_EN | BIT_FIFO_TMST_FSYNC_EN);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_FIFO_CONFIG5_MREG_TOP1 register!\n");
|
|
goto reset_fifo_fail;
|
|
}
|
|
|
|
/* Set FIFO interrupt source to INT1*/
|
|
ret = regmap_write(st->regmap, REG_INT_SOURCE0,
|
|
BIT_INT_RESET_DONE_INT1_EN | BIT_INT_FIFO_THS_INT1_EN);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_INT_SOURCE0 register!\n");
|
|
goto reset_fifo_fail;
|
|
}
|
|
|
|
st->interrupt_period = st->standard_period;
|
|
|
|
return 0;
|
|
|
|
reset_fifo_fail:
|
|
dev_err(regmap_get_device(st->regmap), "%s :reset fifo failed %d\n", __func__, ret);
|
|
ret = regmap_write(st->regmap, REG_INT_SOURCE0,
|
|
BIT_INT_RESET_DONE_INT1_EN | BIT_INT_FIFO_THS_INT1_EN);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* icm42670_read_fifo() - Transfer data from hardware FIFO to KFIFO.
|
|
*/
|
|
irqreturn_t icm42670_read_fifo(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *indio_dev = pf->indio_dev;
|
|
struct icm42670_data *st = iio_priv(indio_dev);
|
|
const struct device *dev = regmap_get_device(st->regmap);
|
|
size_t bytes_per_datum;
|
|
int result, int_status, int_drdy;
|
|
u8 data[ICM42670_OUTPUT_DATA_SIZE_PULS_ONE], i;
|
|
u16 fifo_count;
|
|
u32 data_len;
|
|
s64 timestamp = 0;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
/* ack interrupt and check status */
|
|
result = regmap_read(st->regmap, REG_INT_STATUS, &int_status);
|
|
if (result) {
|
|
dev_err(dev, "failed to ack interrupt\n");
|
|
goto flush_fifo;
|
|
}
|
|
|
|
result = regmap_read(st->regmap, REG_INT_STATUS_DRDY, &int_drdy);
|
|
if (result) {
|
|
dev_err(dev, "failed to ack interrupt\n");
|
|
goto flush_fifo;
|
|
}
|
|
|
|
if (!(int_status & BIT_INT_STATUS_FIFO_THS)) {
|
|
dev_warn(dev, "spurious interrupt with status 0x%x\n", int_status);
|
|
if (!(int_drdy & (BIT_INT_STATUS_DRDY)))
|
|
goto flush_fifo;
|
|
}
|
|
|
|
if ((int_status & BIT_INT_STATUS_FIFO_FULL)) {
|
|
dev_warn(dev, "the fifo is full\n");
|
|
goto flush_fifo;
|
|
}
|
|
|
|
bytes_per_datum = ICM42670_FIFO_DATUM;
|
|
result = regmap_bulk_read(st->regmap, REG_FIFO_BYTE_COUNT1,
|
|
data, ICM42670_FIFO_COUNT_BYTE);
|
|
if (result) {
|
|
dev_err(dev, "read fifo count fail: %d\n", result);
|
|
goto end_session;
|
|
}
|
|
|
|
fifo_count = get_unaligned_be16(&data[0]);
|
|
if (fifo_count > ICM42670_FIFO_COUNT_LIMIT) {
|
|
dev_warn(dev, "fifo overflow reset, cnt: %u\n", fifo_count);
|
|
goto flush_fifo;
|
|
}
|
|
|
|
icm42670_update_period(st, pf->timestamp, fifo_count);
|
|
|
|
data_len = bytes_per_datum * fifo_count;
|
|
|
|
result = regmap_bulk_read(st->regmap, REG_FIFO_DATA_REG,
|
|
st->data_buff, data_len);
|
|
if (result) {
|
|
dev_err(dev,
|
|
"regmap_bulk_read failed\n");
|
|
goto flush_fifo;
|
|
}
|
|
|
|
for (i = 0; i < fifo_count; ++i) {
|
|
/* skip first samples if needed */
|
|
if (st->skip_samples) {
|
|
st->skip_samples--;
|
|
continue;
|
|
}
|
|
memcpy(data, st->data_buff+i*16, bytes_per_datum);
|
|
timestamp = icm42670_get_timestamp(st);
|
|
iio_push_to_buffers_with_timestamp(indio_dev, &(data[1]), timestamp);
|
|
}
|
|
|
|
end_session:
|
|
mutex_unlock(&st->lock);
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
flush_fifo:
|
|
/* Flush HW and SW FIFOs. */
|
|
dev_info(dev, "flush info\n");
|
|
icm42670_reset_fifo(indio_dev);
|
|
mutex_unlock(&st->lock);
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int icm42670_reset_data(struct iio_dev *indio_dev)
|
|
{
|
|
int ret;
|
|
struct icm42670_data *st = iio_priv(indio_dev);
|
|
const struct device *dev = regmap_get_device(st->regmap);
|
|
|
|
/* reset it timestamp validation */
|
|
st->it_timestamp = 0;
|
|
|
|
ret = regmap_write(st->regmap, REG_INT_SOURCE0, BIT_INT_MODE_OFF);
|
|
if (ret) {
|
|
dev_err(dev, "int_enable failed %d\n",
|
|
ret);
|
|
goto reset_fail;
|
|
}
|
|
|
|
/* disable the sensor output to FIFO */
|
|
ret = regmap_write(st->regmap, REG_FIFO_CONFIG1, BIT_FIFO_MODE_BYPASS);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_FIFO_CONFIG1 register!\n");
|
|
goto reset_fail;
|
|
}
|
|
|
|
ret = regmap_write(st->regmap, REG_INT_SOURCE0,
|
|
BIT_INT_DRDY_INT_EN);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to write to REG_INT_SOURCE0 register!\n");
|
|
goto reset_fail;
|
|
}
|
|
|
|
msleep(50);
|
|
|
|
st->interrupt_period = st->standard_period;
|
|
|
|
return 0;
|
|
|
|
reset_fail:
|
|
dev_err(regmap_get_device(st->regmap), "%s :reset fifo failed %d\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
irqreturn_t icm42670_read_data(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *indio_dev = pf->indio_dev;
|
|
struct icm42670_data *st = iio_priv(indio_dev);
|
|
const struct device *dev = regmap_get_device(st->regmap);
|
|
int ret, int_drdy;
|
|
u8 data[ICM42670_OUTPUT_DATA_SIZE_PULS_ONE];
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = regmap_read(st->regmap, REG_INT_STATUS_DRDY, &int_drdy);
|
|
if (ret) {
|
|
dev_err(dev, "failed to ack interrupt\n");
|
|
goto read_fail;
|
|
}
|
|
|
|
if (!(int_drdy & (BIT_INT_STATUS_DRDY)))
|
|
goto read_fail;
|
|
|
|
ret = regmap_bulk_read(st->regmap, REG_ACCEL_DATA_X0_UI,
|
|
(u8 *)data, ICM42670_ACCEL_GYRO_SIZE);
|
|
if (ret) {
|
|
dev_err(dev, "regmap_bulk_read accel+gyro failed\n");
|
|
goto read_fail;
|
|
}
|
|
|
|
ret = regmap_bulk_read(st->regmap, REG_TEMP_DATA0_UI,
|
|
(u8 *)&data[ICM42670_ACCEL_GYRO_SIZE], 2);
|
|
if (ret) {
|
|
dev_err(dev, "regmap_bulk_read temperature failed\n");
|
|
goto read_fail;
|
|
}
|
|
|
|
iio_push_to_buffers_with_timestamp(indio_dev, &(data[0]), pf->timestamp);
|
|
|
|
mutex_unlock(&st->lock);
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
read_fail:
|
|
dev_info(dev, "read data fail\n");
|
|
mutex_unlock(&st->lock);
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|