// SPDX-License-Identifier: GPL-2.0
/*
* MEMSensing digital 3-Axis accelerometer
*
* MSA311 is a tri-axial, low-g accelerometer with I2C digital output for
* sensitivity consumer applications. It has dynamic user-selectable full
* scales range of +-2g/+-4g/+-8g/+-16g and allows acceleration measurements
* with output data rates from 1Hz to 1000Hz.
*
* MSA311 is available in an ultra small (2mm x 2mm, height 0.95mm) LGA package
* and is guaranteed to operate over -40C to +85C.
*
* This driver supports following MSA311 features:
* - IIO interface
* - Different power modes: NORMAL, SUSPEND
* - ODR (Output Data Rate) selection
* - Scale selection
* - IIO triggered buffer
* - NEW_DATA interrupt + trigger
*
* Below features to be done:
* - Motion Events: ACTIVE, TAP, ORIENT, FREEFALL
* - Low Power mode
*
* Copyright (c) 2022, SberDevices. All Rights Reserved.
*
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/string_choices.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define MSA311_SOFT_RESET_REG 0x00
#define MSA311_PARTID_REG 0x01
#define MSA311_ACC_X_REG 0x02
#define MSA311_ACC_Y_REG 0x04
#define MSA311_ACC_Z_REG 0x06
#define MSA311_MOTION_INT_REG 0x09
#define MSA311_DATA_INT_REG 0x0A
#define MSA311_TAP_ACTIVE_STS_REG 0x0B
#define MSA311_ORIENT_STS_REG 0x0C
#define MSA311_RANGE_REG 0x0F
#define MSA311_ODR_REG 0x10
#define MSA311_PWR_MODE_REG 0x11
#define MSA311_SWAP_POLARITY_REG 0x12
#define MSA311_INT_SET_0_REG 0x16
#define MSA311_INT_SET_1_REG 0x17
#define MSA311_INT_MAP_0_REG 0x19
#define MSA311_INT_MAP_1_REG 0x1A
#define MSA311_INT_CONFIG_REG 0x20
#define MSA311_INT_LATCH_REG 0x21
#define MSA311_FREEFALL_DUR_REG 0x22
#define MSA311_FREEFALL_TH_REG 0x23
#define MSA311_FREEFALL_HY_REG 0x24
#define MSA311_ACTIVE_DUR_REG 0x27
#define MSA311_ACTIVE_TH_REG 0x28
#define MSA311_TAP_DUR_REG 0x2A
#define MSA311_TAP_TH_REG 0x2B
#define MSA311_ORIENT_HY_REG 0x2C
#define MSA311_Z_BLOCK_REG 0x2D
#define MSA311_OFFSET_X_REG 0x38
#define MSA311_OFFSET_Y_REG 0x39
#define MSA311_OFFSET_Z_REG 0x3A
enum msa311_fields {
/* Soft_Reset */
F_SOFT_RESET_I2C, F_SOFT_RESET_SPI,
/* Motion_Interrupt */
F_ORIENT_INT, F_S_TAP_INT, F_D_TAP_INT, F_ACTIVE_INT, F_FREEFALL_INT,
/* Data_Interrupt */
F_NEW_DATA_INT,
/* Tap_Active_Status */
F_TAP_SIGN, F_TAP_FIRST_X, F_TAP_FIRST_Y, F_TAP_FIRST_Z, F_ACTV_SIGN,
F_ACTV_FIRST_X, F_ACTV_FIRST_Y, F_ACTV_FIRST_Z,
/* Orientation_Status */
F_ORIENT_Z, F_ORIENT_X_Y,
/* Range */
F_FS,
/* ODR */
F_X_AXIS_DIS, F_Y_AXIS_DIS, F_Z_AXIS_DIS, F_ODR,
/* Power Mode/Bandwidth */
F_PWR_MODE, F_LOW_POWER_BW,
/* Swap_Polarity */
F_X_POLARITY, F_Y_POLARITY, F_Z_POLARITY, F_X_Y_SWAP,
/* Int_Set_0 */
F_ORIENT_INT_EN, F_S_TAP_INT_EN, F_D_TAP_INT_EN, F_ACTIVE_INT_EN_Z,
F_ACTIVE_INT_EN_Y, F_ACTIVE_INT_EN_X,
/* Int_Set_1 */
F_NEW_DATA_INT_EN, F_FREEFALL_INT_EN,
/* Int_Map_0 */
F_INT1_ORIENT, F_INT1_S_TAP, F_INT1_D_TAP, F_INT1_ACTIVE,
F_INT1_FREEFALL,
/* Int_Map_1 */
F_INT1_NEW_DATA,
/* Int_Config */
F_INT1_OD, F_INT1_LVL,
/* Int_Latch */
F_RESET_INT, F_LATCH_INT,
/* Freefall_Hy */
F_FREEFALL_MODE, F_FREEFALL_HY,
/* Active_Dur */
F_ACTIVE_DUR,
/* Tap_Dur */
F_TAP_QUIET, F_TAP_SHOCK, F_TAP_DUR,
/* Tap_Th */
F_TAP_TH,
/* Orient_Hy */
F_ORIENT_HYST, F_ORIENT_BLOCKING, F_ORIENT_MODE,
/* Z_Block */
F_Z_BLOCKING,
/* End of register map */
F_MAX_FIELDS,
};
static const struct reg_field msa311_reg_fields[] = {
/* Soft_Reset */
[F_SOFT_RESET_I2C] = REG_FIELD(MSA311_SOFT_RESET_REG, 2, 2),
[F_SOFT_RESET_SPI] = REG_FIELD(MSA311_SOFT_RESET_REG, 5, 5),
/* Motion_Interrupt */
[F_ORIENT_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 6, 6),
[F_S_TAP_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 5, 5),