// SPDX-License-Identifier: GPL-2.0
/*
* Driver for NXP FXAS21002C Gyroscope - Core
*
* Copyright (C) 2019 Linaro Ltd.
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include "fxas21002c.h"
#define FXAS21002C_CHIP_ID_1 0xD6
#define FXAS21002C_CHIP_ID_2 0xD7
enum fxas21002c_mode_state {
FXAS21002C_MODE_STANDBY,
FXAS21002C_MODE_READY,
FXAS21002C_MODE_ACTIVE,
};
#define FXAS21002C_STANDBY_ACTIVE_TIME_MS 62
#define FXAS21002C_READY_ACTIVE_TIME_MS 7
#define FXAS21002C_ODR_LIST_MAX 10
#define FXAS21002C_SCALE_FRACTIONAL 32
#define FXAS21002C_RANGE_LIMIT_DOUBLE 2000
#define FXAS21002C_AXIS_TO_REG(axis) (FXAS21002C_REG_OUT_X_MSB + ((axis) * 2))
static const int fxas21002c_odr_values[] = {
800, 400, 200, 100, 50, 25, 12, 12
};
/*
* These values are taken from the low-pass filter cutoff frequency calculated
* ODR * 0.lpf_values. So, for ODR = 800Hz with a lpf value = 0.32
* => LPF cutoff frequency = 800 * 0.32 = 256 Hz
*/
static const int fxas21002c_lpf_values[] = {
32, 16, 8
};
/*
* These values are taken from the high-pass filter cutoff frequency calculated
* ODR * 0.0hpf_values. So, for ODR = 800Hz with a hpf value = 0.018750
* => HPF cutoff frequency = 800 * 0.018750 = 15 Hz
*/
static const int fxas21002c_hpf_values[] = {
18750, 9625, 4875, 2475
};
static const int fxas21002c_range_values[] = {
4000, 2000, 1000, 500, 250
};
struct fxas21002c_data {
u8 chip_id;
enum fxas21002c_mode_state mode;
enum fxas21002c_mode_state prev_mode;
struct mutex lock; /* serialize data access */
struct regmap *regmap;
struct regmap_field *regmap_fields[F_MAX_FIELDS];
struct iio_trigger *dready_trig;
s64 timestamp;
int irq;
struct regulator *vdd;
struct regulator *vddio;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
s16 buffer[8] ____cacheline_aligned;
};
enum fxas21002c_channel_index {
CHANNEL_SCAN_INDEX_X,
CHANNEL_SCAN_INDEX_Y,
CHANNEL_SCAN_INDEX_Z,
CHANNEL_SCAN_MAX,
};
static int fxas21002c_odr_hz_from_value(struct fxas21002c_data *data, u8 value)
{
int odr_value_max = ARRAY_SIZE(fxas21002c_odr_values) - 1;
value = min_t(u8, value, odr_value_max);
return fxas21002c_odr_values[value];
}
static int fxas21002c_odr_value_from_hz(struct fxas21002c_data *data,
unsigned int hz)
{
int odr_table_size = ARRAY_SIZE(fxas21002c_odr_values);
int i;
for (i = 0; i < odr_table_size; i++)
if (fxas21002c_odr_values[i] == hz)
return i;
return -EINVAL;
}
static int fxas21002c_lpf_bw_from_value(struct fxas21002c_data *data, u8 value)
{
int lpf_value_max = ARRAY_SIZE(fxas21002c_lpf_values) - 1;
value = min_t(u8, value, lpf_value_max);
return fxas21002c_lpf_values[value];
}
static int fxas21002c_lpf_value_from_bw(struct fxas21002c_data *data,
unsigned int hz)
{
int lpf_table_size = ARRAY_SIZE(fxas21002c_lpf_values);
int i;
for (i = 0; i < lpf_table_size; i++)
if (fxas21002c_lpf_values[i] == hz)
return i;
return -EINVAL;
}
static int fxas21002c_hpf_sel_from_value(struct fxas21002c_data *data, u8 value)
{
int hpf_value_max = ARRAY_SIZE(fxas21002c_hpf_values) - 1;
value = min_t(u8, value, hpf_value_max);
return fxas21002c_hpf_values[value];
}
static int fxas21002c_hpf_value_from_sel(struct fxas21002c_data *data,
unsigned int hz)
{
int hpf_table_size = ARRAY_SIZE(fxas21002c_hpf_values);
int i;
for (i = 0; i < hpf_table_size; i++)
if (fxas21002c_hpf_values[i] == hz)
return i;
return -EINVAL;
}
static int fxas21002c_range_fs_from_value(struct fxas21002c_data *data,
u8 value)
{
int range_value_max = ARRAY_SIZE(fxas21002c_range_values) - 1;
unsigned int fs_double;
int ret;
/* We need to check