// SPDX-License-Identifier: GPL-2.0-only
/*
* Bosch BMC150 three-axis magnetic field sensor driver
*
* Copyright (c) 2015, Intel Corporation.
*
* This code is based on bmm050_api.c authored by contact@bosch.sensortec.com:
*
* (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regmap.h>
#include "bmc150_magn.h"
#define BMC150_MAGN_DRV_NAME "bmc150_magn"
#define BMC150_MAGN_IRQ_NAME "bmc150_magn_event"
#define BMC150_MAGN_REG_CHIP_ID 0x40
#define BMC150_MAGN_CHIP_ID_VAL 0x32
#define BMC150_MAGN_REG_X_L 0x42
#define BMC150_MAGN_REG_X_M 0x43
#define BMC150_MAGN_REG_Y_L 0x44
#define BMC150_MAGN_REG_Y_M 0x45
#define BMC150_MAGN_SHIFT_XY_L 3
#define BMC150_MAGN_REG_Z_L 0x46
#define BMC150_MAGN_REG_Z_M 0x47
#define BMC150_MAGN_SHIFT_Z_L 1
#define BMC150_MAGN_REG_RHALL_L 0x48
#define BMC150_MAGN_REG_RHALL_M 0x49
#define BMC150_MAGN_SHIFT_RHALL_L 2
#define BMC150_MAGN_REG_INT_STATUS 0x4A
#define BMC150_MAGN_REG_POWER 0x4B
#define BMC150_MAGN_MASK_POWER_CTL BIT(0)
#define BMC150_MAGN_REG_OPMODE_ODR 0x4C
#define BMC150_MAGN_MASK_OPMODE GENMASK(2, 1)
#define BMC150_MAGN_SHIFT_OPMODE 1
#define BMC150_MAGN_MODE_NORMAL 0x00
#define BMC150_MAGN_MODE_FORCED 0x01
#define BMC150_MAGN_MODE_SLEEP 0x03
#define BMC150_MAGN_MASK_ODR GENMASK(5, 3)
#define BMC150_MAGN_SHIFT_ODR 3
#define BMC150_MAGN_REG_INT 0x4D
#define BMC150_MAGN_REG_INT_DRDY 0x4E
#define BMC150_MAGN_MASK_DRDY_EN BIT(7)
#define BMC150_MAGN_SHIFT_DRDY_EN 7
#define BMC150_MAGN_MASK_DRDY_INT3 BIT(6)
#define BMC150_MAGN_MASK_DRDY_Z_EN BIT(5)
#define BMC150_MAGN_MASK_DRDY_Y_EN BIT(4)
#define BMC150_MAGN_MASK_DRDY_X_EN BIT(3)
#define BMC150_MAGN_MASK_DRDY_DR_POLARITY BIT(2)
#define BMC150_MAGN_MASK_DRDY_LATCHING BIT(1)
#define BMC150_MAGN_MASK_DRDY_INT3_POLARITY BIT(0)
#define BMC150_MAGN_REG_LOW_THRESH 0x4F
#define BMC150_MAGN_REG_HIGH_THRESH 0x50
#define BMC150_MAGN_REG_REP_XY 0x51
#define BMC150_MAGN_REG_REP_Z 0x52
#define BMC150_MAGN_REG_REP_DATAMASK GENMASK(7, 0)
#define BMC150_MAGN_REG_TRIM_START 0x5D
#define BMC150_MAGN_REG_TRIM_END 0x71
#define BMC150_MAGN_XY_OVERFLOW_VAL -4096
#define BMC150_MAGN_Z_OVERFLOW_VAL -16384
/* Time from SUSPEND to SLEEP */
#define BMC150_MAGN_START_UP_TIME_MS 3
#define BMC150_MAGN_AUTO_SUSPEND_DELAY_MS 2000
#define BMC150_MAGN_REGVAL_TO_REPXY(regval) (((regval) * 2) + 1)
#define BMC150_MAGN_REGVAL_TO_REPZ(regval) ((regval) + 1)
#define BMC150_MAGN_REPXY_TO_REGVAL(rep) (((rep) - 1) / 2)
#define BMC150_MAGN_REPZ_TO_REGVAL(rep) ((rep) - 1)
enum bmc150_magn_axis {
AXIS_X,
AXIS_Y,
AXIS_Z,
RHALL,
AXIS_XYZ_MAX = RHALL,
AXIS_XYZR_MAX,
};
enum bmc150_magn_power_modes {
BMC150_MAGN_POWER_MODE_SUSPEND,
BMC150_MAGN_POWER_MODE_SLEEP,
BMC150_MAGN_POWER_MODE_NORMAL,
};
struct bmc150_magn_trim_regs {
s8 x1;
s8 y1;
__le16 reserved1;
u8 reserved2;
__le16 z4;
s8 x2;
s8 y2;
__le16 reserved3;
__le16 z2;
__le16 z1;
__le16 xyz1;
__le16 z3;
s8 xy2;
u8 xy1;
} __packed;
struct bmc150_magn_data {
struct device *dev;
/*
* 1. Protect this structure.
* 2. Serialize sequences that power on/off the device and access HW.
*/
struct mutex mutex;
struct regmap *regmap;
struct iio_mount_matrix orientation;
/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
s32 buffer[6];
struct iio_trigger *dready_trig;
bool dready_trigger_on;
int max_odr;
int irq;
};
static const struct {
int freq;
u8 reg_val;
} bmc150_magn_samp_freq_table[] = { {2, 0x01},
{6, 0x02},
{8, 0x03},
{10, 0x00},
{15, 0x04},
{20, 0x05},
{25, 0x06},
{30, 0x07} };
enum bmc150_magn_presets {
LOW_POWER_PRESET,
REGULAR_PRESET,
ENHANCED_REGULAR_PRESET,
HIGH_ACCURACY_PRESET
};
static const struct bmc150_magn_preset {
u8 rep_xy;
u8 rep_z;
u8 odr;
} bmc150_magn_presets_table[] = {
[LOW_POWER_PRESET] = {3, 3, 10},
[REGULAR_PRESET] = {9, 15, 10},
[ENHANCED_REGULAR_PRESET] = {15, 27, 10},
[HIGH_ACCURACY_PRESET] = {47, 83, 20},
};
#define BMC150_MAGN_DEFAULT_PRESET REGULAR_PRESET
static bool bmc150_magn_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case BMC150_MAGN_REG_POWER:
case BMC150_MAGN_REG_OPMODE_ODR:
case BMC150_MAGN_REG_INT:
case BMC150_MAGN_REG_INT_DRDY:
case BMC150_MAGN_REG_LOW_THRESH:
case BMC150_MAGN_REG_HIGH_THRESH:
case BMC150_MAGN_REG_REP_XY:
case BMC150_MAGN_REG_REP_Z:
return true;
default:
return false;
};
}
static bool bmc150_magn_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case BMC150_MAGN_REG_X_L:
case BMC150_MAGN_REG_X_M:
case BMC150_MAGN_REG_Y_L:
case BMC150_MAGN_REG_Y_M:
case BMC150_MAGN_REG_Z_L:
case BMC150_MAGN_REG_Z_M:
case