// SPDX-License-Identifier: GPL-2.0-only
/*
* Freescale MMA9553L Intelligent Pedometer driver
* Copyright (c) 2014, Intel Corporation.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/pm_runtime.h>
#include "mma9551_core.h"
#define MMA9553_DRV_NAME "mma9553"
#define MMA9553_IRQ_NAME "mma9553_event"
/* Pedometer configuration registers (R/W) */
#define MMA9553_REG_CONF_SLEEPMIN 0x00
#define MMA9553_REG_CONF_SLEEPMAX 0x02
#define MMA9553_REG_CONF_SLEEPTHD 0x04
#define MMA9553_MASK_CONF_WORD GENMASK(15, 0)
#define MMA9553_REG_CONF_CONF_STEPLEN 0x06
#define MMA9553_MASK_CONF_CONFIG BIT(15)
#define MMA9553_MASK_CONF_ACT_DBCNTM BIT(14)
#define MMA9553_MASK_CONF_SLP_DBCNTM BIT(13)
#define MMA9553_MASK_CONF_STEPLEN GENMASK(7, 0)
#define MMA9553_REG_CONF_HEIGHT_WEIGHT 0x08
#define MMA9553_MASK_CONF_HEIGHT GENMASK(15, 8)
#define MMA9553_MASK_CONF_WEIGHT GENMASK(7, 0)
#define MMA9553_REG_CONF_FILTER 0x0A
#define MMA9553_MASK_CONF_FILTSTEP GENMASK(15, 8)
#define MMA9553_MASK_CONF_MALE BIT(7)
#define MMA9553_MASK_CONF_FILTTIME GENMASK(6, 0)
#define MMA9553_REG_CONF_SPEED_STEP 0x0C
#define MMA9553_MASK_CONF_SPDPRD GENMASK(15, 8)
#define MMA9553_MASK_CONF_STEPCOALESCE GENMASK(7, 0)
#define MMA9553_REG_CONF_ACTTHD 0x0E
#define MMA9553_MAX_ACTTHD GENMASK(15, 0)
/* Pedometer status registers (R-only) */
#define MMA9553_REG_STATUS 0x00
#define MMA9553_MASK_STATUS_MRGFL BIT(15)
#define MMA9553_MASK_STATUS_SUSPCHG BIT(14)
#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
#define MMA9553_MASK_STATUS_SUSP BIT(11)
#define MMA9553_MASK_STATUS_ACTIVITY GENMASK(10, 8)
#define MMA9553_MASK_STATUS_VERSION GENMASK(7, 0)
#define MMA9553_REG_STEPCNT 0x02
#define MMA9553_REG_DISTANCE 0x04
#define MMA9553_REG_SPEED 0x06
#define MMA9553_REG_CALORIES 0x08
#define MMA9553_REG_SLEEPCNT 0x0A
/* Pedometer events are always mapped to this pin. */
#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
#define MMA9553_DEFAULT_GPIO_POLARITY 0
/* Bitnum used for GPIO configuration = bit number in high status byte */
#define MMA9553_STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
#define MMA9553_MAX_BITNUM MMA9553_STATUS_TO_BITNUM(BIT(16))
#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
/*
* The internal activity level must be stable for ACTTHD samples before
* ACTIVITY is updated. The ACTIVITY variable contains the current activity
* level and is updated every time a step is detected or once a second
* if there are no steps.
*/
#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) / MMA9553_DEFAULT_SAMPLE_RATE)
#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) * MMA9553_DEFAULT_SAMPLE_RATE)
/*
* Autonomously suspend pedometer if acceleration vector magnitude
* is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
*/
#define MMA9553_DEFAULT_SLEEPMIN 3688 /* 0,9 g */
#define MMA9553_DEFAULT_SLEEPMAX 4508 /* 1,1 g */
#define MMA9553_DEFAULT_SLEEPTHD (MMA9553_DEFAULT_SAMPLE_RATE * 30)
#define MMA9553_CONFIG_RETRIES 2
/* Status register - activity field */
enum activity_level {
ACTIVITY_UNKNOWN,
ACTIVITY_REST,
ACTIVITY_WALKING,
ACTIVITY_JOGGING,
ACTIVITY_RUNNING,
};
static struct mma9553_event_info {
enum iio_chan_type type;
enum iio_modifier mod;
enum iio_event_direction dir;
} mma9553_events_info[] = {
{
.type = IIO_STEPS,
.mod = IIO_NO_MOD,
.dir = IIO_EV_DIR_NONE,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_STILL,
.dir = IIO_EV_DIR_RISING,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_STILL,
.dir = IIO_EV_DIR_FALLING,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_WALKING,
.dir = IIO_EV_DIR_RISING,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_WALKING,
.dir = IIO_EV_DIR_FALLING,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_JOGGING,
.dir = IIO_EV_DIR_RISING,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_JOGGING,
.dir = IIO_EV_DIR_FALLING,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_RUNNING,
.dir = IIO_EV_DIR_RISING,
},
{
.type = IIO_ACTIVITY,
.mod = IIO_MOD_RUNNING,
.dir = IIO_EV_DIR_FALLING,
},
};
#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
struct mma9553_event {
struct mma9553_event_info *info;
bool enabled;
};
struct mma9553_conf_regs {
u16 sleepmin;
u16 sleepmax;
u16 sleepthd;
u16 config;
u16 height_weight;
u16 filter;
u16 speed_step;
u16 actthd;
} __packed;
struct mma9553_data {
struct i2c_client *client;
/*
* 1. Serialize access to HW (requested by mma9551_core API).
* 2. Serialize sequences that power on/off the device and access HW.
*/
struct mutex mutex;
struct mma9553_conf_regs conf;
struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
int num_events;
u8 gpio_bitnum