162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IIO driver for Bosch BNO055 IMU 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia 662306a36Sopenharmony_ci * Electronic Design Laboratory 762306a36Sopenharmony_ci * Written by Andrea Merello <andrea.merello@iit.it> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Portions of this driver are taken from the BNO055 driver patch 1062306a36Sopenharmony_ci * from Vlad Dogaru which is Copyright (c) 2016, Intel Corporation. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This driver is also based on BMI160 driver, which is: 1362306a36Sopenharmony_ci * Copyright (c) 2016, Intel Corporation. 1462306a36Sopenharmony_ci * Copyright (c) 2019, Martin Kelly. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/bitfield.h> 1862306a36Sopenharmony_ci#include <linux/bitmap.h> 1962306a36Sopenharmony_ci#include <linux/clk.h> 2062306a36Sopenharmony_ci#include <linux/debugfs.h> 2162306a36Sopenharmony_ci#include <linux/device.h> 2262306a36Sopenharmony_ci#include <linux/firmware.h> 2362306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/mutex.h> 2662306a36Sopenharmony_ci#include <linux/regmap.h> 2762306a36Sopenharmony_ci#include <linux/util_macros.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/iio/buffer.h> 3062306a36Sopenharmony_ci#include <linux/iio/iio.h> 3162306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 3262306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 3362306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "bno055.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define BNO055_FW_UID_FMT "bno055-caldata-%*phN.dat" 3862306a36Sopenharmony_ci#define BNO055_FW_GENERIC_NAME "bno055-caldata.dat" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* common registers */ 4162306a36Sopenharmony_ci#define BNO055_PAGESEL_REG 0x7 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* page 0 registers */ 4462306a36Sopenharmony_ci#define BNO055_CHIP_ID_REG 0x0 4562306a36Sopenharmony_ci#define BNO055_CHIP_ID_MAGIC 0xA0 4662306a36Sopenharmony_ci#define BNO055_SW_REV_LSB_REG 0x4 4762306a36Sopenharmony_ci#define BNO055_SW_REV_MSB_REG 0x5 4862306a36Sopenharmony_ci#define BNO055_ACC_DATA_X_LSB_REG 0x8 4962306a36Sopenharmony_ci#define BNO055_ACC_DATA_Y_LSB_REG 0xA 5062306a36Sopenharmony_ci#define BNO055_ACC_DATA_Z_LSB_REG 0xC 5162306a36Sopenharmony_ci#define BNO055_MAG_DATA_X_LSB_REG 0xE 5262306a36Sopenharmony_ci#define BNO055_MAG_DATA_Y_LSB_REG 0x10 5362306a36Sopenharmony_ci#define BNO055_MAG_DATA_Z_LSB_REG 0x12 5462306a36Sopenharmony_ci#define BNO055_GYR_DATA_X_LSB_REG 0x14 5562306a36Sopenharmony_ci#define BNO055_GYR_DATA_Y_LSB_REG 0x16 5662306a36Sopenharmony_ci#define BNO055_GYR_DATA_Z_LSB_REG 0x18 5762306a36Sopenharmony_ci#define BNO055_EUL_DATA_X_LSB_REG 0x1A 5862306a36Sopenharmony_ci#define BNO055_EUL_DATA_Y_LSB_REG 0x1C 5962306a36Sopenharmony_ci#define BNO055_EUL_DATA_Z_LSB_REG 0x1E 6062306a36Sopenharmony_ci#define BNO055_QUAT_DATA_W_LSB_REG 0x20 6162306a36Sopenharmony_ci#define BNO055_LIA_DATA_X_LSB_REG 0x28 6262306a36Sopenharmony_ci#define BNO055_LIA_DATA_Y_LSB_REG 0x2A 6362306a36Sopenharmony_ci#define BNO055_LIA_DATA_Z_LSB_REG 0x2C 6462306a36Sopenharmony_ci#define BNO055_GRAVITY_DATA_X_LSB_REG 0x2E 6562306a36Sopenharmony_ci#define BNO055_GRAVITY_DATA_Y_LSB_REG 0x30 6662306a36Sopenharmony_ci#define BNO055_GRAVITY_DATA_Z_LSB_REG 0x32 6762306a36Sopenharmony_ci#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2) 6862306a36Sopenharmony_ci#define BNO055_TEMP_REG 0x34 6962306a36Sopenharmony_ci#define BNO055_CALIB_STAT_REG 0x35 7062306a36Sopenharmony_ci#define BNO055_CALIB_STAT_MAGN_SHIFT 0 7162306a36Sopenharmony_ci#define BNO055_CALIB_STAT_ACCEL_SHIFT 2 7262306a36Sopenharmony_ci#define BNO055_CALIB_STAT_GYRO_SHIFT 4 7362306a36Sopenharmony_ci#define BNO055_CALIB_STAT_SYS_SHIFT 6 7462306a36Sopenharmony_ci#define BNO055_SYS_ERR_REG 0x3A 7562306a36Sopenharmony_ci#define BNO055_POWER_MODE_REG 0x3E 7662306a36Sopenharmony_ci#define BNO055_POWER_MODE_NORMAL 0 7762306a36Sopenharmony_ci#define BNO055_SYS_TRIGGER_REG 0x3F 7862306a36Sopenharmony_ci#define BNO055_SYS_TRIGGER_RST_SYS BIT(5) 7962306a36Sopenharmony_ci#define BNO055_SYS_TRIGGER_CLK_SEL BIT(7) 8062306a36Sopenharmony_ci#define BNO055_OPR_MODE_REG 0x3D 8162306a36Sopenharmony_ci#define BNO055_OPR_MODE_CONFIG 0x0 8262306a36Sopenharmony_ci#define BNO055_OPR_MODE_AMG 0x7 8362306a36Sopenharmony_ci#define BNO055_OPR_MODE_FUSION_FMC_OFF 0xB 8462306a36Sopenharmony_ci#define BNO055_OPR_MODE_FUSION 0xC 8562306a36Sopenharmony_ci#define BNO055_UNIT_SEL_REG 0x3B 8662306a36Sopenharmony_ci/* Android orientation mode means: pitch value decreases turning clockwise */ 8762306a36Sopenharmony_ci#define BNO055_UNIT_SEL_ANDROID BIT(7) 8862306a36Sopenharmony_ci#define BNO055_UNIT_SEL_GYR_RPS BIT(1) 8962306a36Sopenharmony_ci#define BNO055_CALDATA_START 0x55 9062306a36Sopenharmony_ci#define BNO055_CALDATA_END 0x6A 9162306a36Sopenharmony_ci#define BNO055_CALDATA_LEN 22 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * The difference in address between the register that contains the 9562306a36Sopenharmony_ci * value and the register that contains the offset. This applies for 9662306a36Sopenharmony_ci * accel, gyro and magn channels. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci#define BNO055_REG_OFFSET_ADDR 0x4D 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* page 1 registers */ 10162306a36Sopenharmony_ci#define BNO055_PG1(x) ((x) | 0x80) 10262306a36Sopenharmony_ci#define BNO055_ACC_CONFIG_REG BNO055_PG1(0x8) 10362306a36Sopenharmony_ci#define BNO055_ACC_CONFIG_LPF_MASK GENMASK(4, 2) 10462306a36Sopenharmony_ci#define BNO055_ACC_CONFIG_RANGE_MASK GENMASK(1, 0) 10562306a36Sopenharmony_ci#define BNO055_MAG_CONFIG_REG BNO055_PG1(0x9) 10662306a36Sopenharmony_ci#define BNO055_MAG_CONFIG_HIGHACCURACY 0x18 10762306a36Sopenharmony_ci#define BNO055_MAG_CONFIG_ODR_MASK GENMASK(2, 0) 10862306a36Sopenharmony_ci#define BNO055_GYR_CONFIG_REG BNO055_PG1(0xA) 10962306a36Sopenharmony_ci#define BNO055_GYR_CONFIG_RANGE_MASK GENMASK(2, 0) 11062306a36Sopenharmony_ci#define BNO055_GYR_CONFIG_LPF_MASK GENMASK(5, 3) 11162306a36Sopenharmony_ci#define BNO055_GYR_AM_SET_REG BNO055_PG1(0x1F) 11262306a36Sopenharmony_ci#define BNO055_UID_LOWER_REG BNO055_PG1(0x50) 11362306a36Sopenharmony_ci#define BNO055_UID_HIGHER_REG BNO055_PG1(0x5F) 11462306a36Sopenharmony_ci#define BNO055_UID_LEN 16 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct bno055_sysfs_attr { 11762306a36Sopenharmony_ci int *vals; 11862306a36Sopenharmony_ci int len; 11962306a36Sopenharmony_ci int *fusion_vals; 12062306a36Sopenharmony_ci int *hw_xlate; 12162306a36Sopenharmony_ci int type; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int bno055_acc_lpf_vals[] = { 12562306a36Sopenharmony_ci 7, 810000, 15, 630000, 31, 250000, 62, 500000, 12662306a36Sopenharmony_ci 125, 0, 250, 0, 500, 0, 1000, 0, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct bno055_sysfs_attr bno055_acc_lpf = { 13062306a36Sopenharmony_ci .vals = bno055_acc_lpf_vals, 13162306a36Sopenharmony_ci .len = ARRAY_SIZE(bno055_acc_lpf_vals), 13262306a36Sopenharmony_ci .fusion_vals = (int[]){62, 500000}, 13362306a36Sopenharmony_ci .type = IIO_VAL_INT_PLUS_MICRO, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int bno055_acc_range_vals[] = { 13762306a36Sopenharmony_ci /* G: 2, 4, 8, 16 */ 13862306a36Sopenharmony_ci 1962, 3924, 7848, 15696 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct bno055_sysfs_attr bno055_acc_range = { 14262306a36Sopenharmony_ci .vals = bno055_acc_range_vals, 14362306a36Sopenharmony_ci .len = ARRAY_SIZE(bno055_acc_range_vals), 14462306a36Sopenharmony_ci .fusion_vals = (int[]){3924}, /* 4G */ 14562306a36Sopenharmony_ci .type = IIO_VAL_INT, 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * Theoretically the IMU should return data in a given (i.e. fixed) unit 15062306a36Sopenharmony_ci * regardless of the range setting. This happens for the accelerometer, but not 15162306a36Sopenharmony_ci * for the gyroscope; the gyroscope range setting affects the scale. 15262306a36Sopenharmony_ci * This is probably due to this[0] bug. 15362306a36Sopenharmony_ci * For this reason we map the internal range setting onto the standard IIO scale 15462306a36Sopenharmony_ci * attribute for gyro. 15562306a36Sopenharmony_ci * Since the bug[0] may be fixed in future, we check for the IMU FW version and 15662306a36Sopenharmony_ci * eventually warn the user. 15762306a36Sopenharmony_ci * Currently we just don't care about "range" attributes for gyro. 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * [0] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* 16362306a36Sopenharmony_ci * dps = hwval * (dps_range/2^15) 16462306a36Sopenharmony_ci * rps = hwval * (rps_range/2^15) 16562306a36Sopenharmony_ci * = hwval * (dps_range/(2^15 * k)) 16662306a36Sopenharmony_ci * where k is rad-to-deg factor 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistatic int bno055_gyr_scale_vals[] = { 16962306a36Sopenharmony_ci 125, 1877467, 250, 1877467, 500, 1877467, 17062306a36Sopenharmony_ci 1000, 1877467, 2000, 1877467, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic struct bno055_sysfs_attr bno055_gyr_scale = { 17462306a36Sopenharmony_ci .vals = bno055_gyr_scale_vals, 17562306a36Sopenharmony_ci .len = ARRAY_SIZE(bno055_gyr_scale_vals), 17662306a36Sopenharmony_ci .fusion_vals = (int[]){1, 900}, 17762306a36Sopenharmony_ci .hw_xlate = (int[]){4, 3, 2, 1, 0}, 17862306a36Sopenharmony_ci .type = IIO_VAL_FRACTIONAL, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int bno055_gyr_lpf_vals[] = {12, 23, 32, 47, 64, 116, 230, 523}; 18262306a36Sopenharmony_cistatic struct bno055_sysfs_attr bno055_gyr_lpf = { 18362306a36Sopenharmony_ci .vals = bno055_gyr_lpf_vals, 18462306a36Sopenharmony_ci .len = ARRAY_SIZE(bno055_gyr_lpf_vals), 18562306a36Sopenharmony_ci .fusion_vals = (int[]){32}, 18662306a36Sopenharmony_ci .hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0}, 18762306a36Sopenharmony_ci .type = IIO_VAL_INT, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30}; 19162306a36Sopenharmony_cistatic struct bno055_sysfs_attr bno055_mag_odr = { 19262306a36Sopenharmony_ci .vals = bno055_mag_odr_vals, 19362306a36Sopenharmony_ci .len = ARRAY_SIZE(bno055_mag_odr_vals), 19462306a36Sopenharmony_ci .fusion_vals = (int[]){20}, 19562306a36Sopenharmony_ci .type = IIO_VAL_INT, 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistruct bno055_priv { 19962306a36Sopenharmony_ci struct regmap *regmap; 20062306a36Sopenharmony_ci struct device *dev; 20162306a36Sopenharmony_ci struct clk *clk; 20262306a36Sopenharmony_ci int operation_mode; 20362306a36Sopenharmony_ci int xfer_burst_break_thr; 20462306a36Sopenharmony_ci struct mutex lock; 20562306a36Sopenharmony_ci u8 uid[BNO055_UID_LEN]; 20662306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 20762306a36Sopenharmony_ci bool sw_reset; 20862306a36Sopenharmony_ci struct { 20962306a36Sopenharmony_ci __le16 chans[BNO055_SCAN_CH_COUNT]; 21062306a36Sopenharmony_ci s64 timestamp __aligned(8); 21162306a36Sopenharmony_ci } buf; 21262306a36Sopenharmony_ci struct dentry *debugfs; 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic bool bno055_regmap_volatile(struct device *dev, unsigned int reg) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci /* data and status registers */ 21862306a36Sopenharmony_ci if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG) 21962306a36Sopenharmony_ci return true; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* when in fusion mode, config is updated by chip */ 22262306a36Sopenharmony_ci if (reg == BNO055_MAG_CONFIG_REG || 22362306a36Sopenharmony_ci reg == BNO055_ACC_CONFIG_REG || 22462306a36Sopenharmony_ci reg == BNO055_GYR_CONFIG_REG) 22562306a36Sopenharmony_ci return true; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* calibration data may be updated by the IMU */ 22862306a36Sopenharmony_ci if (reg >= BNO055_CALDATA_START && reg <= BNO055_CALDATA_END) 22962306a36Sopenharmony_ci return true; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return false; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic bool bno055_regmap_readable(struct device *dev, unsigned int reg) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci /* unnamed PG0 reserved areas */ 23762306a36Sopenharmony_ci if ((reg < BNO055_PG1(0) && reg > BNO055_CALDATA_END) || 23862306a36Sopenharmony_ci reg == 0x3C) 23962306a36Sopenharmony_ci return false; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* unnamed PG1 reserved areas */ 24262306a36Sopenharmony_ci if (reg > BNO055_PG1(BNO055_UID_HIGHER_REG) || 24362306a36Sopenharmony_ci (reg < BNO055_PG1(BNO055_UID_LOWER_REG) && reg > BNO055_PG1(BNO055_GYR_AM_SET_REG)) || 24462306a36Sopenharmony_ci reg == BNO055_PG1(0xE) || 24562306a36Sopenharmony_ci (reg < BNO055_PG1(BNO055_PAGESEL_REG) && reg >= BNO055_PG1(0x0))) 24662306a36Sopenharmony_ci return false; 24762306a36Sopenharmony_ci return true; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic bool bno055_regmap_writeable(struct device *dev, unsigned int reg) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci * Unreadable registers are indeed reserved; there are no WO regs 25462306a36Sopenharmony_ci * (except for a single bit in SYS_TRIGGER register) 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci if (!bno055_regmap_readable(dev, reg)) 25762306a36Sopenharmony_ci return false; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* data and status registers */ 26062306a36Sopenharmony_ci if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG) 26162306a36Sopenharmony_ci return false; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* ID areas */ 26462306a36Sopenharmony_ci if (reg < BNO055_PAGESEL_REG || 26562306a36Sopenharmony_ci (reg <= BNO055_UID_HIGHER_REG && reg >= BNO055_UID_LOWER_REG)) 26662306a36Sopenharmony_ci return false; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return true; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct regmap_range_cfg bno055_regmap_ranges[] = { 27262306a36Sopenharmony_ci { 27362306a36Sopenharmony_ci .range_min = 0, 27462306a36Sopenharmony_ci .range_max = 0x7f * 2, 27562306a36Sopenharmony_ci .selector_reg = BNO055_PAGESEL_REG, 27662306a36Sopenharmony_ci .selector_mask = GENMASK(7, 0), 27762306a36Sopenharmony_ci .selector_shift = 0, 27862306a36Sopenharmony_ci .window_start = 0, 27962306a36Sopenharmony_ci .window_len = 0x80, 28062306a36Sopenharmony_ci }, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciconst struct regmap_config bno055_regmap_config = { 28462306a36Sopenharmony_ci .name = "bno055", 28562306a36Sopenharmony_ci .reg_bits = 8, 28662306a36Sopenharmony_ci .val_bits = 8, 28762306a36Sopenharmony_ci .ranges = bno055_regmap_ranges, 28862306a36Sopenharmony_ci .num_ranges = 1, 28962306a36Sopenharmony_ci .volatile_reg = bno055_regmap_volatile, 29062306a36Sopenharmony_ci .max_register = 0x80 * 2, 29162306a36Sopenharmony_ci .writeable_reg = bno055_regmap_writeable, 29262306a36Sopenharmony_ci .readable_reg = bno055_regmap_readable, 29362306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 29462306a36Sopenharmony_ci}; 29562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(bno055_regmap_config, IIO_BNO055); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* must be called in configuration mode */ 29862306a36Sopenharmony_cistatic int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (len != BNO055_CALDATA_LEN) { 30162306a36Sopenharmony_ci dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)", 30262306a36Sopenharmony_ci len, BNO055_CALDATA_LEN); 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, data); 30762306a36Sopenharmony_ci return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START, 30862306a36Sopenharmony_ci data, BNO055_CALDATA_LEN); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int bno055_operation_mode_do_set(struct bno055_priv *priv, 31262306a36Sopenharmony_ci int operation_mode) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int ret; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG, 31762306a36Sopenharmony_ci operation_mode); 31862306a36Sopenharmony_ci if (ret) 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Following datasheet specifications: sensor takes 7mS up to 19 mS to switch mode */ 32262306a36Sopenharmony_ci msleep(20); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int bno055_system_reset(struct bno055_priv *priv) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (priv->reset_gpio) { 33262306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 0); 33362306a36Sopenharmony_ci usleep_range(5000, 10000); 33462306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 1); 33562306a36Sopenharmony_ci } else if (priv->sw_reset) { 33662306a36Sopenharmony_ci ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG, 33762306a36Sopenharmony_ci BNO055_SYS_TRIGGER_RST_SYS); 33862306a36Sopenharmony_ci if (ret) 33962306a36Sopenharmony_ci return ret; 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci regcache_drop_region(priv->regmap, 0x0, 0xff); 34562306a36Sopenharmony_ci usleep_range(650000, 700000); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int bno055_init(struct bno055_priv *priv, const u8 *caldata, int len) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci int ret; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); 35562306a36Sopenharmony_ci if (ret) 35662306a36Sopenharmony_ci return ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = regmap_write(priv->regmap, BNO055_POWER_MODE_REG, 35962306a36Sopenharmony_ci BNO055_POWER_MODE_NORMAL); 36062306a36Sopenharmony_ci if (ret) 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG, 36462306a36Sopenharmony_ci priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0); 36562306a36Sopenharmony_ci if (ret) 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* use standard SI units */ 36962306a36Sopenharmony_ci ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG, 37062306a36Sopenharmony_ci BNO055_UNIT_SEL_ANDROID | BNO055_UNIT_SEL_GYR_RPS); 37162306a36Sopenharmony_ci if (ret) 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (caldata) { 37562306a36Sopenharmony_ci ret = bno055_calibration_load(priv, caldata, len); 37662306a36Sopenharmony_ci if (ret) 37762306a36Sopenharmony_ci dev_warn(priv->dev, "failed to load calibration data with error %d\n", 37862306a36Sopenharmony_ci ret); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic ssize_t bno055_operation_mode_set(struct bno055_priv *priv, 38562306a36Sopenharmony_ci int operation_mode) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci u8 caldata[BNO055_CALDATA_LEN]; 38862306a36Sopenharmony_ci int ret; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci mutex_lock(&priv->lock); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); 39362306a36Sopenharmony_ci if (ret) 39462306a36Sopenharmony_ci goto exit_unlock; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (operation_mode == BNO055_OPR_MODE_FUSION || 39762306a36Sopenharmony_ci operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF) { 39862306a36Sopenharmony_ci /* for entering fusion mode, reset the chip to clear the algo state */ 39962306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, caldata, 40062306a36Sopenharmony_ci BNO055_CALDATA_LEN); 40162306a36Sopenharmony_ci if (ret) 40262306a36Sopenharmony_ci goto exit_unlock; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci ret = bno055_system_reset(priv); 40562306a36Sopenharmony_ci if (ret) 40662306a36Sopenharmony_ci goto exit_unlock; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ret = bno055_init(priv, caldata, BNO055_CALDATA_LEN); 40962306a36Sopenharmony_ci if (ret) 41062306a36Sopenharmony_ci goto exit_unlock; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ret = bno055_operation_mode_do_set(priv, operation_mode); 41462306a36Sopenharmony_ci if (ret) 41562306a36Sopenharmony_ci goto exit_unlock; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci priv->operation_mode = operation_mode; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciexit_unlock: 42062306a36Sopenharmony_ci mutex_unlock(&priv->lock); 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void bno055_uninit(void *arg) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct bno055_priv *priv = arg; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* stop the IMU */ 42962306a36Sopenharmony_ci bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci#define BNO055_CHANNEL(_type, _axis, _index, _address, _sep, _sh, _avail) { \ 43362306a36Sopenharmony_ci .address = _address, \ 43462306a36Sopenharmony_ci .type = _type, \ 43562306a36Sopenharmony_ci .modified = 1, \ 43662306a36Sopenharmony_ci .channel2 = IIO_MOD_##_axis, \ 43762306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | (_sep), \ 43862306a36Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_sh), \ 43962306a36Sopenharmony_ci .info_mask_shared_by_type_available = _avail, \ 44062306a36Sopenharmony_ci .scan_index = _index, \ 44162306a36Sopenharmony_ci .scan_type = { \ 44262306a36Sopenharmony_ci .sign = 's', \ 44362306a36Sopenharmony_ci .realbits = 16, \ 44462306a36Sopenharmony_ci .storagebits = 16, \ 44562306a36Sopenharmony_ci .endianness = IIO_LE, \ 44662306a36Sopenharmony_ci .repeat = IIO_MOD_##_axis == IIO_MOD_QUATERNION ? 4 : 0, \ 44762306a36Sopenharmony_ci }, \ 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* scan indexes follow DATA register order */ 45162306a36Sopenharmony_cienum bno055_scan_axis { 45262306a36Sopenharmony_ci BNO055_SCAN_ACCEL_X, 45362306a36Sopenharmony_ci BNO055_SCAN_ACCEL_Y, 45462306a36Sopenharmony_ci BNO055_SCAN_ACCEL_Z, 45562306a36Sopenharmony_ci BNO055_SCAN_MAGN_X, 45662306a36Sopenharmony_ci BNO055_SCAN_MAGN_Y, 45762306a36Sopenharmony_ci BNO055_SCAN_MAGN_Z, 45862306a36Sopenharmony_ci BNO055_SCAN_GYRO_X, 45962306a36Sopenharmony_ci BNO055_SCAN_GYRO_Y, 46062306a36Sopenharmony_ci BNO055_SCAN_GYRO_Z, 46162306a36Sopenharmony_ci BNO055_SCAN_YAW, 46262306a36Sopenharmony_ci BNO055_SCAN_ROLL, 46362306a36Sopenharmony_ci BNO055_SCAN_PITCH, 46462306a36Sopenharmony_ci BNO055_SCAN_QUATERNION, 46562306a36Sopenharmony_ci BNO055_SCAN_LIA_X, 46662306a36Sopenharmony_ci BNO055_SCAN_LIA_Y, 46762306a36Sopenharmony_ci BNO055_SCAN_LIA_Z, 46862306a36Sopenharmony_ci BNO055_SCAN_GRAVITY_X, 46962306a36Sopenharmony_ci BNO055_SCAN_GRAVITY_Y, 47062306a36Sopenharmony_ci BNO055_SCAN_GRAVITY_Z, 47162306a36Sopenharmony_ci BNO055_SCAN_TIMESTAMP, 47262306a36Sopenharmony_ci _BNO055_SCAN_MAX 47362306a36Sopenharmony_ci}; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic const struct iio_chan_spec bno055_channels[] = { 47662306a36Sopenharmony_ci /* accelerometer */ 47762306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ACCEL, X, BNO055_SCAN_ACCEL_X, 47862306a36Sopenharmony_ci BNO055_ACC_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 47962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), 48062306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), 48162306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ACCEL, Y, BNO055_SCAN_ACCEL_Y, 48262306a36Sopenharmony_ci BNO055_ACC_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 48362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), 48462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), 48562306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ACCEL, Z, BNO055_SCAN_ACCEL_Z, 48662306a36Sopenharmony_ci BNO055_ACC_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 48762306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), 48862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), 48962306a36Sopenharmony_ci /* gyroscope */ 49062306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ANGL_VEL, X, BNO055_SCAN_GYRO_X, 49162306a36Sopenharmony_ci BNO055_GYR_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 49262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), 49362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | 49462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE)), 49562306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ANGL_VEL, Y, BNO055_SCAN_GYRO_Y, 49662306a36Sopenharmony_ci BNO055_GYR_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 49762306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), 49862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | 49962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE)), 50062306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ANGL_VEL, Z, BNO055_SCAN_GYRO_Z, 50162306a36Sopenharmony_ci BNO055_GYR_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 50262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), 50362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | 50462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE)), 50562306a36Sopenharmony_ci /* magnetometer */ 50662306a36Sopenharmony_ci BNO055_CHANNEL(IIO_MAGN, X, BNO055_SCAN_MAGN_X, 50762306a36Sopenharmony_ci BNO055_MAG_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 50862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), 50962306a36Sopenharmony_ci BNO055_CHANNEL(IIO_MAGN, Y, BNO055_SCAN_MAGN_Y, 51062306a36Sopenharmony_ci BNO055_MAG_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 51162306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), 51262306a36Sopenharmony_ci BNO055_CHANNEL(IIO_MAGN, Z, BNO055_SCAN_MAGN_Z, 51362306a36Sopenharmony_ci BNO055_MAG_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), 51462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), 51562306a36Sopenharmony_ci /* euler angle */ 51662306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ROT, YAW, BNO055_SCAN_YAW, 51762306a36Sopenharmony_ci BNO055_EUL_DATA_X_LSB_REG, 0, 0, 0), 51862306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ROT, ROLL, BNO055_SCAN_ROLL, 51962306a36Sopenharmony_ci BNO055_EUL_DATA_Y_LSB_REG, 0, 0, 0), 52062306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ROT, PITCH, BNO055_SCAN_PITCH, 52162306a36Sopenharmony_ci BNO055_EUL_DATA_Z_LSB_REG, 0, 0, 0), 52262306a36Sopenharmony_ci /* quaternion */ 52362306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ROT, QUATERNION, BNO055_SCAN_QUATERNION, 52462306a36Sopenharmony_ci BNO055_QUAT_DATA_W_LSB_REG, 0, 0, 0), 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* linear acceleration */ 52762306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ACCEL, LINEAR_X, BNO055_SCAN_LIA_X, 52862306a36Sopenharmony_ci BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0), 52962306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ACCEL, LINEAR_Y, BNO055_SCAN_LIA_Y, 53062306a36Sopenharmony_ci BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0), 53162306a36Sopenharmony_ci BNO055_CHANNEL(IIO_ACCEL, LINEAR_Z, BNO055_SCAN_LIA_Z, 53262306a36Sopenharmony_ci BNO055_LIA_DATA_Z_LSB_REG, 0, 0, 0), 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* gravity vector */ 53562306a36Sopenharmony_ci BNO055_CHANNEL(IIO_GRAVITY, X, BNO055_SCAN_GRAVITY_X, 53662306a36Sopenharmony_ci BNO055_GRAVITY_DATA_X_LSB_REG, 0, 0, 0), 53762306a36Sopenharmony_ci BNO055_CHANNEL(IIO_GRAVITY, Y, BNO055_SCAN_GRAVITY_Y, 53862306a36Sopenharmony_ci BNO055_GRAVITY_DATA_Y_LSB_REG, 0, 0, 0), 53962306a36Sopenharmony_ci BNO055_CHANNEL(IIO_GRAVITY, Z, BNO055_SCAN_GRAVITY_Z, 54062306a36Sopenharmony_ci BNO055_GRAVITY_DATA_Z_LSB_REG, 0, 0, 0), 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci { 54362306a36Sopenharmony_ci .type = IIO_TEMP, 54462306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 54562306a36Sopenharmony_ci .scan_index = -1, 54662306a36Sopenharmony_ci }, 54762306a36Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(BNO055_SCAN_TIMESTAMP), 54862306a36Sopenharmony_ci}; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2, 55162306a36Sopenharmony_ci int reg, int mask, struct bno055_sysfs_attr *attr) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci const int shift = __ffs(mask); 55462306a36Sopenharmony_ci int hwval, idx; 55562306a36Sopenharmony_ci int ret; 55662306a36Sopenharmony_ci int i; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci ret = regmap_read(priv->regmap, reg, &hwval); 55962306a36Sopenharmony_ci if (ret) 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci idx = (hwval & mask) >> shift; 56362306a36Sopenharmony_ci if (attr->hw_xlate) 56462306a36Sopenharmony_ci for (i = 0; i < attr->len; i++) 56562306a36Sopenharmony_ci if (attr->hw_xlate[i] == idx) { 56662306a36Sopenharmony_ci idx = i; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci if (attr->type == IIO_VAL_INT) { 57062306a36Sopenharmony_ci *val = attr->vals[idx]; 57162306a36Sopenharmony_ci } else { /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL */ 57262306a36Sopenharmony_ci *val = attr->vals[idx * 2]; 57362306a36Sopenharmony_ci *val2 = attr->vals[idx * 2 + 1]; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return attr->type; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int bno055_set_regmask(struct bno055_priv *priv, int val, int val2, 58062306a36Sopenharmony_ci int reg, int mask, struct bno055_sysfs_attr *attr) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci const int shift = __ffs(mask); 58362306a36Sopenharmony_ci int best_delta; 58462306a36Sopenharmony_ci int req_val; 58562306a36Sopenharmony_ci int tbl_val; 58662306a36Sopenharmony_ci bool first; 58762306a36Sopenharmony_ci int delta; 58862306a36Sopenharmony_ci int hwval; 58962306a36Sopenharmony_ci int ret; 59062306a36Sopenharmony_ci int len; 59162306a36Sopenharmony_ci int i; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* 59462306a36Sopenharmony_ci * The closest value the HW supports is only one in fusion mode, 59562306a36Sopenharmony_ci * and it is autoselected, so don't do anything, just return OK, 59662306a36Sopenharmony_ci * as the closest possible value has been (virtually) selected 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci if (priv->operation_mode != BNO055_OPR_MODE_AMG) 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci len = attr->len; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* 60462306a36Sopenharmony_ci * We always get a request in INT_PLUS_MICRO, but we 60562306a36Sopenharmony_ci * take care of the micro part only when we really have 60662306a36Sopenharmony_ci * non-integer tables. This prevents 32-bit overflow with 60762306a36Sopenharmony_ci * larger integers contained in integer tables. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci req_val = val; 61062306a36Sopenharmony_ci if (attr->type != IIO_VAL_INT) { 61162306a36Sopenharmony_ci len /= 2; 61262306a36Sopenharmony_ci req_val = min(val, 2147) * 1000000 + val2; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci first = true; 61662306a36Sopenharmony_ci for (i = 0; i < len; i++) { 61762306a36Sopenharmony_ci switch (attr->type) { 61862306a36Sopenharmony_ci case IIO_VAL_INT: 61962306a36Sopenharmony_ci tbl_val = attr->vals[i]; 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case IIO_VAL_INT_PLUS_MICRO: 62262306a36Sopenharmony_ci WARN_ON(attr->vals[i * 2] > 2147); 62362306a36Sopenharmony_ci tbl_val = attr->vals[i * 2] * 1000000 + 62462306a36Sopenharmony_ci attr->vals[i * 2 + 1]; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case IIO_VAL_FRACTIONAL: 62762306a36Sopenharmony_ci WARN_ON(attr->vals[i * 2] > 4294); 62862306a36Sopenharmony_ci tbl_val = attr->vals[i * 2] * 1000000 / 62962306a36Sopenharmony_ci attr->vals[i * 2 + 1]; 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci default: 63262306a36Sopenharmony_ci return -EINVAL; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci delta = abs(tbl_val - req_val); 63562306a36Sopenharmony_ci if (first || delta < best_delta) { 63662306a36Sopenharmony_ci best_delta = delta; 63762306a36Sopenharmony_ci hwval = i; 63862306a36Sopenharmony_ci first = false; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (attr->hw_xlate) 64362306a36Sopenharmony_ci hwval = attr->hw_xlate[hwval]; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); 64662306a36Sopenharmony_ci if (ret) 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, reg, mask, hwval << shift); 65062306a36Sopenharmony_ci if (ret) 65162306a36Sopenharmony_ci return ret; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_AMG); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int bno055_read_simple_chan(struct iio_dev *indio_dev, 65762306a36Sopenharmony_ci struct iio_chan_spec const *chan, 65862306a36Sopenharmony_ci int *val, int *val2, long mask) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 66162306a36Sopenharmony_ci __le16 raw_val; 66262306a36Sopenharmony_ci int ret; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci switch (mask) { 66562306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 66662306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, chan->address, 66762306a36Sopenharmony_ci &raw_val, sizeof(raw_val)); 66862306a36Sopenharmony_ci if (ret < 0) 66962306a36Sopenharmony_ci return ret; 67062306a36Sopenharmony_ci *val = sign_extend32(le16_to_cpu(raw_val), 15); 67162306a36Sopenharmony_ci return IIO_VAL_INT; 67262306a36Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 67362306a36Sopenharmony_ci if (priv->operation_mode != BNO055_OPR_MODE_AMG) { 67462306a36Sopenharmony_ci *val = 0; 67562306a36Sopenharmony_ci } else { 67662306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, 67762306a36Sopenharmony_ci chan->address + 67862306a36Sopenharmony_ci BNO055_REG_OFFSET_ADDR, 67962306a36Sopenharmony_ci &raw_val, sizeof(raw_val)); 68062306a36Sopenharmony_ci if (ret < 0) 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci /* 68362306a36Sopenharmony_ci * IMU reports sensor offsets; IIO wants correction 68462306a36Sopenharmony_ci * offsets, thus we need the 'minus' here. 68562306a36Sopenharmony_ci */ 68662306a36Sopenharmony_ci *val = -sign_extend32(le16_to_cpu(raw_val), 15); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci return IIO_VAL_INT; 68962306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 69062306a36Sopenharmony_ci *val = 1; 69162306a36Sopenharmony_ci switch (chan->type) { 69262306a36Sopenharmony_ci case IIO_GRAVITY: 69362306a36Sopenharmony_ci /* Table 3-35: 1 m/s^2 = 100 LSB */ 69462306a36Sopenharmony_ci case IIO_ACCEL: 69562306a36Sopenharmony_ci /* Table 3-17: 1 m/s^2 = 100 LSB */ 69662306a36Sopenharmony_ci *val2 = 100; 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci case IIO_MAGN: 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * Table 3-19: 1 uT = 16 LSB. But we need 70162306a36Sopenharmony_ci * Gauss: 1G = 0.1 uT. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci *val2 = 160; 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci case IIO_ANGL_VEL: 70662306a36Sopenharmony_ci /* 70762306a36Sopenharmony_ci * Table 3-22: 1 Rps = 900 LSB 70862306a36Sopenharmony_ci * .. but this is not exactly true. See comment at the 70962306a36Sopenharmony_ci * beginning of this file. 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_ci if (priv->operation_mode != BNO055_OPR_MODE_AMG) { 71262306a36Sopenharmony_ci *val = bno055_gyr_scale.fusion_vals[0]; 71362306a36Sopenharmony_ci *val2 = bno055_gyr_scale.fusion_vals[1]; 71462306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return bno055_get_regmask(priv, val, val2, 71862306a36Sopenharmony_ci BNO055_GYR_CONFIG_REG, 71962306a36Sopenharmony_ci BNO055_GYR_CONFIG_RANGE_MASK, 72062306a36Sopenharmony_ci &bno055_gyr_scale); 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci case IIO_ROT: 72362306a36Sopenharmony_ci /* Table 3-28: 1 degree = 16 LSB */ 72462306a36Sopenharmony_ci *val2 = 16; 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci default: 72762306a36Sopenharmony_ci return -EINVAL; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 73262306a36Sopenharmony_ci if (chan->type != IIO_MAGN) 73362306a36Sopenharmony_ci return -EINVAL; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return bno055_get_regmask(priv, val, val2, 73662306a36Sopenharmony_ci BNO055_MAG_CONFIG_REG, 73762306a36Sopenharmony_ci BNO055_MAG_CONFIG_ODR_MASK, 73862306a36Sopenharmony_ci &bno055_mag_odr); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 74162306a36Sopenharmony_ci switch (chan->type) { 74262306a36Sopenharmony_ci case IIO_ANGL_VEL: 74362306a36Sopenharmony_ci return bno055_get_regmask(priv, val, val2, 74462306a36Sopenharmony_ci BNO055_GYR_CONFIG_REG, 74562306a36Sopenharmony_ci BNO055_GYR_CONFIG_LPF_MASK, 74662306a36Sopenharmony_ci &bno055_gyr_lpf); 74762306a36Sopenharmony_ci case IIO_ACCEL: 74862306a36Sopenharmony_ci return bno055_get_regmask(priv, val, val2, 74962306a36Sopenharmony_ci BNO055_ACC_CONFIG_REG, 75062306a36Sopenharmony_ci BNO055_ACC_CONFIG_LPF_MASK, 75162306a36Sopenharmony_ci &bno055_acc_lpf); 75262306a36Sopenharmony_ci default: 75362306a36Sopenharmony_ci return -EINVAL; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci default: 75762306a36Sopenharmony_ci return -EINVAL; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr, 76262306a36Sopenharmony_ci const int **vals, int *length) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci if (priv->operation_mode != BNO055_OPR_MODE_AMG) { 76562306a36Sopenharmony_ci /* locked when fusion enabled */ 76662306a36Sopenharmony_ci *vals = attr->fusion_vals; 76762306a36Sopenharmony_ci if (attr->type == IIO_VAL_INT) 76862306a36Sopenharmony_ci *length = 1; 76962306a36Sopenharmony_ci else 77062306a36Sopenharmony_ci *length = 2; /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL*/ 77162306a36Sopenharmony_ci } else { 77262306a36Sopenharmony_ci *vals = attr->vals; 77362306a36Sopenharmony_ci *length = attr->len; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return attr->type; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int bno055_read_avail(struct iio_dev *indio_dev, 78062306a36Sopenharmony_ci struct iio_chan_spec const *chan, 78162306a36Sopenharmony_ci const int **vals, int *type, int *length, 78262306a36Sopenharmony_ci long mask) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci switch (mask) { 78762306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 78862306a36Sopenharmony_ci switch (chan->type) { 78962306a36Sopenharmony_ci case IIO_ANGL_VEL: 79062306a36Sopenharmony_ci *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_scale, 79162306a36Sopenharmony_ci vals, length); 79262306a36Sopenharmony_ci return IIO_AVAIL_LIST; 79362306a36Sopenharmony_ci default: 79462306a36Sopenharmony_ci return -EINVAL; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 79762306a36Sopenharmony_ci switch (chan->type) { 79862306a36Sopenharmony_ci case IIO_ANGL_VEL: 79962306a36Sopenharmony_ci *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_lpf, 80062306a36Sopenharmony_ci vals, length); 80162306a36Sopenharmony_ci return IIO_AVAIL_LIST; 80262306a36Sopenharmony_ci case IIO_ACCEL: 80362306a36Sopenharmony_ci *type = bno055_sysfs_attr_avail(priv, &bno055_acc_lpf, 80462306a36Sopenharmony_ci vals, length); 80562306a36Sopenharmony_ci return IIO_AVAIL_LIST; 80662306a36Sopenharmony_ci default: 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 81262306a36Sopenharmony_ci switch (chan->type) { 81362306a36Sopenharmony_ci case IIO_MAGN: 81462306a36Sopenharmony_ci *type = bno055_sysfs_attr_avail(priv, &bno055_mag_odr, 81562306a36Sopenharmony_ci vals, length); 81662306a36Sopenharmony_ci return IIO_AVAIL_LIST; 81762306a36Sopenharmony_ci default: 81862306a36Sopenharmony_ci return -EINVAL; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci default: 82162306a36Sopenharmony_ci return -EINVAL; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int bno055_read_temp_chan(struct iio_dev *indio_dev, int *val) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 82862306a36Sopenharmony_ci unsigned int raw_val; 82962306a36Sopenharmony_ci int ret; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ret = regmap_read(priv->regmap, BNO055_TEMP_REG, &raw_val); 83262306a36Sopenharmony_ci if (ret < 0) 83362306a36Sopenharmony_ci return ret; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* 83662306a36Sopenharmony_ci * Tables 3-36 and 3-37: one byte of priv, signed, 1 LSB = 1C. 83762306a36Sopenharmony_ci * ABI wants milliC. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci *val = raw_val * 1000; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci return IIO_VAL_INT; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic int bno055_read_quaternion(struct iio_dev *indio_dev, 84562306a36Sopenharmony_ci struct iio_chan_spec const *chan, 84662306a36Sopenharmony_ci int size, int *vals, int *val_len, 84762306a36Sopenharmony_ci long mask) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 85062306a36Sopenharmony_ci __le16 raw_vals[4]; 85162306a36Sopenharmony_ci int i, ret; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci switch (mask) { 85462306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 85562306a36Sopenharmony_ci if (size < 4) 85662306a36Sopenharmony_ci return -EINVAL; 85762306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, 85862306a36Sopenharmony_ci BNO055_QUAT_DATA_W_LSB_REG, 85962306a36Sopenharmony_ci raw_vals, sizeof(raw_vals)); 86062306a36Sopenharmony_ci if (ret < 0) 86162306a36Sopenharmony_ci return ret; 86262306a36Sopenharmony_ci for (i = 0; i < 4; i++) 86362306a36Sopenharmony_ci vals[i] = sign_extend32(le16_to_cpu(raw_vals[i]), 15); 86462306a36Sopenharmony_ci *val_len = 4; 86562306a36Sopenharmony_ci return IIO_VAL_INT_MULTIPLE; 86662306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 86762306a36Sopenharmony_ci /* Table 3-31: 1 quaternion = 2^14 LSB */ 86862306a36Sopenharmony_ci if (size < 2) 86962306a36Sopenharmony_ci return -EINVAL; 87062306a36Sopenharmony_ci vals[0] = 1; 87162306a36Sopenharmony_ci vals[1] = 14; 87262306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL_LOG2; 87362306a36Sopenharmony_ci default: 87462306a36Sopenharmony_ci return -EINVAL; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic bool bno055_is_chan_readable(struct iio_dev *indio_dev, 87962306a36Sopenharmony_ci struct iio_chan_spec const *chan) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (priv->operation_mode != BNO055_OPR_MODE_AMG) 88462306a36Sopenharmony_ci return true; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci switch (chan->type) { 88762306a36Sopenharmony_ci case IIO_GRAVITY: 88862306a36Sopenharmony_ci case IIO_ROT: 88962306a36Sopenharmony_ci return false; 89062306a36Sopenharmony_ci case IIO_ACCEL: 89162306a36Sopenharmony_ci if (chan->channel2 == IIO_MOD_LINEAR_X || 89262306a36Sopenharmony_ci chan->channel2 == IIO_MOD_LINEAR_Y || 89362306a36Sopenharmony_ci chan->channel2 == IIO_MOD_LINEAR_Z) 89462306a36Sopenharmony_ci return false; 89562306a36Sopenharmony_ci return true; 89662306a36Sopenharmony_ci default: 89762306a36Sopenharmony_ci return true; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic int _bno055_read_raw_multi(struct iio_dev *indio_dev, 90262306a36Sopenharmony_ci struct iio_chan_spec const *chan, 90362306a36Sopenharmony_ci int size, int *vals, int *val_len, 90462306a36Sopenharmony_ci long mask) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci if (!bno055_is_chan_readable(indio_dev, chan)) 90762306a36Sopenharmony_ci return -EBUSY; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci switch (chan->type) { 91062306a36Sopenharmony_ci case IIO_MAGN: 91162306a36Sopenharmony_ci case IIO_ACCEL: 91262306a36Sopenharmony_ci case IIO_ANGL_VEL: 91362306a36Sopenharmony_ci case IIO_GRAVITY: 91462306a36Sopenharmony_ci if (size < 2) 91562306a36Sopenharmony_ci return -EINVAL; 91662306a36Sopenharmony_ci *val_len = 2; 91762306a36Sopenharmony_ci return bno055_read_simple_chan(indio_dev, chan, 91862306a36Sopenharmony_ci &vals[0], &vals[1], 91962306a36Sopenharmony_ci mask); 92062306a36Sopenharmony_ci case IIO_TEMP: 92162306a36Sopenharmony_ci *val_len = 1; 92262306a36Sopenharmony_ci return bno055_read_temp_chan(indio_dev, &vals[0]); 92362306a36Sopenharmony_ci case IIO_ROT: 92462306a36Sopenharmony_ci /* 92562306a36Sopenharmony_ci * Rotation is exposed as either a quaternion or three 92662306a36Sopenharmony_ci * Euler angles. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci if (chan->channel2 == IIO_MOD_QUATERNION) 92962306a36Sopenharmony_ci return bno055_read_quaternion(indio_dev, chan, 93062306a36Sopenharmony_ci size, vals, 93162306a36Sopenharmony_ci val_len, mask); 93262306a36Sopenharmony_ci if (size < 2) 93362306a36Sopenharmony_ci return -EINVAL; 93462306a36Sopenharmony_ci *val_len = 2; 93562306a36Sopenharmony_ci return bno055_read_simple_chan(indio_dev, chan, 93662306a36Sopenharmony_ci &vals[0], &vals[1], 93762306a36Sopenharmony_ci mask); 93862306a36Sopenharmony_ci default: 93962306a36Sopenharmony_ci return -EINVAL; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int bno055_read_raw_multi(struct iio_dev *indio_dev, 94462306a36Sopenharmony_ci struct iio_chan_spec const *chan, 94562306a36Sopenharmony_ci int size, int *vals, int *val_len, 94662306a36Sopenharmony_ci long mask) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 94962306a36Sopenharmony_ci int ret; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci mutex_lock(&priv->lock); 95262306a36Sopenharmony_ci ret = _bno055_read_raw_multi(indio_dev, chan, size, 95362306a36Sopenharmony_ci vals, val_len, mask); 95462306a36Sopenharmony_ci mutex_unlock(&priv->lock); 95562306a36Sopenharmony_ci return ret; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cistatic int _bno055_write_raw(struct iio_dev *iio_dev, 95962306a36Sopenharmony_ci struct iio_chan_spec const *chan, 96062306a36Sopenharmony_ci int val, int val2, long mask) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(iio_dev); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci switch (chan->type) { 96562306a36Sopenharmony_ci case IIO_MAGN: 96662306a36Sopenharmony_ci switch (mask) { 96762306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 96862306a36Sopenharmony_ci return bno055_set_regmask(priv, val, val2, 96962306a36Sopenharmony_ci BNO055_MAG_CONFIG_REG, 97062306a36Sopenharmony_ci BNO055_MAG_CONFIG_ODR_MASK, 97162306a36Sopenharmony_ci &bno055_mag_odr); 97262306a36Sopenharmony_ci default: 97362306a36Sopenharmony_ci return -EINVAL; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci case IIO_ACCEL: 97662306a36Sopenharmony_ci switch (mask) { 97762306a36Sopenharmony_ci case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 97862306a36Sopenharmony_ci return bno055_set_regmask(priv, val, val2, 97962306a36Sopenharmony_ci BNO055_ACC_CONFIG_REG, 98062306a36Sopenharmony_ci BNO055_ACC_CONFIG_LPF_MASK, 98162306a36Sopenharmony_ci &bno055_acc_lpf); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci default: 98462306a36Sopenharmony_ci return -EINVAL; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci case IIO_ANGL_VEL: 98762306a36Sopenharmony_ci switch (mask) { 98862306a36Sopenharmony_ci case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 98962306a36Sopenharmony_ci return bno055_set_regmask(priv, val, val2, 99062306a36Sopenharmony_ci BNO055_GYR_CONFIG_REG, 99162306a36Sopenharmony_ci BNO055_GYR_CONFIG_LPF_MASK, 99262306a36Sopenharmony_ci &bno055_gyr_lpf); 99362306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 99462306a36Sopenharmony_ci return bno055_set_regmask(priv, val, val2, 99562306a36Sopenharmony_ci BNO055_GYR_CONFIG_REG, 99662306a36Sopenharmony_ci BNO055_GYR_CONFIG_RANGE_MASK, 99762306a36Sopenharmony_ci &bno055_gyr_scale); 99862306a36Sopenharmony_ci default: 99962306a36Sopenharmony_ci return -EINVAL; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci default: 100262306a36Sopenharmony_ci return -EINVAL; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int bno055_write_raw(struct iio_dev *iio_dev, 100762306a36Sopenharmony_ci struct iio_chan_spec const *chan, 100862306a36Sopenharmony_ci int val, int val2, long mask) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(iio_dev); 101162306a36Sopenharmony_ci int ret; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci mutex_lock(&priv->lock); 101462306a36Sopenharmony_ci ret = _bno055_write_raw(iio_dev, chan, val, val2, mask); 101562306a36Sopenharmony_ci mutex_unlock(&priv->lock); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci return ret; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic ssize_t in_accel_range_raw_available_show(struct device *dev, 102162306a36Sopenharmony_ci struct device_attribute *attr, 102262306a36Sopenharmony_ci char *buf) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); 102562306a36Sopenharmony_ci int len = 0; 102662306a36Sopenharmony_ci int i; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (priv->operation_mode != BNO055_OPR_MODE_AMG) 102962306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", bno055_acc_range.fusion_vals[0]); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci for (i = 0; i < bno055_acc_range.len; i++) 103262306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "%d ", bno055_acc_range.vals[i]); 103362306a36Sopenharmony_ci buf[len - 1] = '\n'; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci return len; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic ssize_t fusion_enable_show(struct device *dev, 103962306a36Sopenharmony_ci struct device_attribute *attr, 104062306a36Sopenharmony_ci char *buf) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", 104562306a36Sopenharmony_ci priv->operation_mode != BNO055_OPR_MODE_AMG); 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic ssize_t fusion_enable_store(struct device *dev, 104962306a36Sopenharmony_ci struct device_attribute *attr, 105062306a36Sopenharmony_ci const char *buf, size_t len) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 105362306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 105462306a36Sopenharmony_ci bool en; 105562306a36Sopenharmony_ci int ret; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (indio_dev->active_scan_mask && 105862306a36Sopenharmony_ci !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX)) 105962306a36Sopenharmony_ci return -EBUSY; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci ret = kstrtobool(buf, &en); 106262306a36Sopenharmony_ci if (ret) 106362306a36Sopenharmony_ci return -EINVAL; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (!en) 106662306a36Sopenharmony_ci return bno055_operation_mode_set(priv, BNO055_OPR_MODE_AMG) ?: len; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* 106962306a36Sopenharmony_ci * Coming from AMG means the FMC was off, just switch to fusion but 107062306a36Sopenharmony_ci * don't change anything that doesn't belong to us (i.e let FMC stay off). 107162306a36Sopenharmony_ci * Coming from any other fusion mode means we don't need to do anything. 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci if (priv->operation_mode == BNO055_OPR_MODE_AMG) 107462306a36Sopenharmony_ci return bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF) ?: len; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci return len; 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic ssize_t in_magn_calibration_fast_enable_show(struct device *dev, 108062306a36Sopenharmony_ci struct device_attribute *attr, 108162306a36Sopenharmony_ci char *buf) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", 108662306a36Sopenharmony_ci priv->operation_mode == BNO055_OPR_MODE_FUSION); 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic ssize_t in_magn_calibration_fast_enable_store(struct device *dev, 109062306a36Sopenharmony_ci struct device_attribute *attr, 109162306a36Sopenharmony_ci const char *buf, size_t len) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 109462306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 109562306a36Sopenharmony_ci int ret; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (indio_dev->active_scan_mask && 109862306a36Sopenharmony_ci !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX)) 109962306a36Sopenharmony_ci return -EBUSY; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (sysfs_streq(buf, "0")) { 110262306a36Sopenharmony_ci if (priv->operation_mode == BNO055_OPR_MODE_FUSION) { 110362306a36Sopenharmony_ci ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF); 110462306a36Sopenharmony_ci if (ret) 110562306a36Sopenharmony_ci return ret; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci } else { 110862306a36Sopenharmony_ci if (priv->operation_mode == BNO055_OPR_MODE_AMG) 110962306a36Sopenharmony_ci return -EINVAL; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (priv->operation_mode != BNO055_OPR_MODE_FUSION) { 111262306a36Sopenharmony_ci ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION); 111362306a36Sopenharmony_ci if (ret) 111462306a36Sopenharmony_ci return ret; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci return len; 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic ssize_t in_accel_range_raw_show(struct device *dev, 112262306a36Sopenharmony_ci struct device_attribute *attr, 112362306a36Sopenharmony_ci char *buf) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); 112662306a36Sopenharmony_ci int val; 112762306a36Sopenharmony_ci int ret; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci ret = bno055_get_regmask(priv, &val, NULL, 113062306a36Sopenharmony_ci BNO055_ACC_CONFIG_REG, 113162306a36Sopenharmony_ci BNO055_ACC_CONFIG_RANGE_MASK, 113262306a36Sopenharmony_ci &bno055_acc_range); 113362306a36Sopenharmony_ci if (ret < 0) 113462306a36Sopenharmony_ci return ret; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", val); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic ssize_t in_accel_range_raw_store(struct device *dev, 114062306a36Sopenharmony_ci struct device_attribute *attr, 114162306a36Sopenharmony_ci const char *buf, size_t len) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); 114462306a36Sopenharmony_ci unsigned long val; 114562306a36Sopenharmony_ci int ret; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 114862306a36Sopenharmony_ci if (ret) 114962306a36Sopenharmony_ci return ret; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci mutex_lock(&priv->lock); 115262306a36Sopenharmony_ci ret = bno055_set_regmask(priv, val, 0, 115362306a36Sopenharmony_ci BNO055_ACC_CONFIG_REG, 115462306a36Sopenharmony_ci BNO055_ACC_CONFIG_RANGE_MASK, 115562306a36Sopenharmony_ci &bno055_acc_range); 115662306a36Sopenharmony_ci mutex_unlock(&priv->lock); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return ret ?: len; 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); 116462306a36Sopenharmony_ci int calib; 116562306a36Sopenharmony_ci int ret; 116662306a36Sopenharmony_ci int val; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (priv->operation_mode == BNO055_OPR_MODE_AMG || 116962306a36Sopenharmony_ci (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF && 117062306a36Sopenharmony_ci which == BNO055_CALIB_STAT_MAGN_SHIFT)) { 117162306a36Sopenharmony_ci calib = 0; 117262306a36Sopenharmony_ci } else { 117362306a36Sopenharmony_ci mutex_lock(&priv->lock); 117462306a36Sopenharmony_ci ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val); 117562306a36Sopenharmony_ci mutex_unlock(&priv->lock); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (ret) 117862306a36Sopenharmony_ci return -EIO; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci calib = ((val >> which) & GENMASK(1, 0)) + 1; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", calib); 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic ssize_t serialnumber_show(struct device *dev, 118762306a36Sopenharmony_ci struct device_attribute *attr, 118862306a36Sopenharmony_ci char *buf) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci return sysfs_emit(buf, "%*ph\n", BNO055_UID_LEN, priv->uid); 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic ssize_t calibration_data_read(struct file *filp, struct kobject *kobj, 119662306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 119762306a36Sopenharmony_ci loff_t pos, size_t count) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(dev_to_iio_dev(kobj_to_dev(kobj))); 120062306a36Sopenharmony_ci u8 data[BNO055_CALDATA_LEN]; 120162306a36Sopenharmony_ci int ret; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci /* 120462306a36Sopenharmony_ci * Calibration data is volatile; reading it in chunks will possibly 120562306a36Sopenharmony_ci * results in inconsistent data. We require the user to read the whole 120662306a36Sopenharmony_ci * blob in a single chunk 120762306a36Sopenharmony_ci */ 120862306a36Sopenharmony_ci if (count < BNO055_CALDATA_LEN || pos) 120962306a36Sopenharmony_ci return -EINVAL; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci mutex_lock(&priv->lock); 121262306a36Sopenharmony_ci ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); 121362306a36Sopenharmony_ci if (ret) 121462306a36Sopenharmony_ci goto exit_unlock; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, data, 121762306a36Sopenharmony_ci BNO055_CALDATA_LEN); 121862306a36Sopenharmony_ci if (ret) 121962306a36Sopenharmony_ci goto exit_unlock; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci ret = bno055_operation_mode_do_set(priv, priv->operation_mode); 122262306a36Sopenharmony_ci if (ret) 122362306a36Sopenharmony_ci goto exit_unlock; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci memcpy(buf, data, BNO055_CALDATA_LEN); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci ret = BNO055_CALDATA_LEN; 122862306a36Sopenharmony_ciexit_unlock: 122962306a36Sopenharmony_ci mutex_unlock(&priv->lock); 123062306a36Sopenharmony_ci return ret; 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic ssize_t sys_calibration_auto_status_show(struct device *dev, 123462306a36Sopenharmony_ci struct device_attribute *a, 123562306a36Sopenharmony_ci char *buf) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_SYS_SHIFT); 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic ssize_t in_accel_calibration_auto_status_show(struct device *dev, 124162306a36Sopenharmony_ci struct device_attribute *a, 124262306a36Sopenharmony_ci char *buf) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_ACCEL_SHIFT); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic ssize_t in_gyro_calibration_auto_status_show(struct device *dev, 124862306a36Sopenharmony_ci struct device_attribute *a, 124962306a36Sopenharmony_ci char *buf) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_GYRO_SHIFT); 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistatic ssize_t in_magn_calibration_auto_status_show(struct device *dev, 125562306a36Sopenharmony_ci struct device_attribute *a, 125662306a36Sopenharmony_ci char *buf) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_MAGN_SHIFT); 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg, 126262306a36Sopenharmony_ci unsigned int writeval, unsigned int *readval) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(iio_dev); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (readval) 126762306a36Sopenharmony_ci return regmap_read(priv->regmap, reg, readval); 126862306a36Sopenharmony_ci else 126962306a36Sopenharmony_ci return regmap_write(priv->regmap, reg, writeval); 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf, 127362306a36Sopenharmony_ci size_t count, loff_t *ppos) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci struct bno055_priv *priv = file->private_data; 127662306a36Sopenharmony_ci int rev, ver; 127762306a36Sopenharmony_ci char *buf; 127862306a36Sopenharmony_ci int ret; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev); 128162306a36Sopenharmony_ci if (ret) 128262306a36Sopenharmony_ci return ret; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver); 128562306a36Sopenharmony_ci if (ret) 128662306a36Sopenharmony_ci return ret; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev); 128962306a36Sopenharmony_ci if (!buf) 129062306a36Sopenharmony_ci return -ENOMEM; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); 129362306a36Sopenharmony_ci kfree(buf); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci return ret; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic const struct file_operations bno055_fw_version_ops = { 129962306a36Sopenharmony_ci .open = simple_open, 130062306a36Sopenharmony_ci .read = bno055_show_fw_version, 130162306a36Sopenharmony_ci .llseek = default_llseek, 130262306a36Sopenharmony_ci .owner = THIS_MODULE, 130362306a36Sopenharmony_ci}; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic void bno055_debugfs_remove(void *_priv) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci struct bno055_priv *priv = _priv; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci debugfs_remove(priv->debugfs); 131062306a36Sopenharmony_ci priv->debugfs = NULL; 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic void bno055_debugfs_init(struct iio_dev *iio_dev) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(iio_dev); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci priv->debugfs = debugfs_create_file("firmware_version", 0400, 131862306a36Sopenharmony_ci iio_get_debugfs_dentry(iio_dev), 131962306a36Sopenharmony_ci priv, &bno055_fw_version_ops); 132062306a36Sopenharmony_ci if (!IS_ERR(priv->debugfs)) 132162306a36Sopenharmony_ci devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, 132262306a36Sopenharmony_ci priv); 132362306a36Sopenharmony_ci if (IS_ERR_OR_NULL(priv->debugfs)) 132462306a36Sopenharmony_ci dev_warn(priv->dev, "failed to setup debugfs"); 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RW(fusion_enable, 0); 132862306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RW(in_magn_calibration_fast_enable, 0); 132962306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RW(in_accel_range_raw, 0); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_accel_range_raw_available, 0); 133262306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0); 133362306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0); 133462306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0); 133562306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0); 133662306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(serialnumber, 0); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic struct attribute *bno055_attrs[] = { 133962306a36Sopenharmony_ci &iio_dev_attr_in_accel_range_raw_available.dev_attr.attr, 134062306a36Sopenharmony_ci &iio_dev_attr_in_accel_range_raw.dev_attr.attr, 134162306a36Sopenharmony_ci &iio_dev_attr_fusion_enable.dev_attr.attr, 134262306a36Sopenharmony_ci &iio_dev_attr_in_magn_calibration_fast_enable.dev_attr.attr, 134362306a36Sopenharmony_ci &iio_dev_attr_sys_calibration_auto_status.dev_attr.attr, 134462306a36Sopenharmony_ci &iio_dev_attr_in_accel_calibration_auto_status.dev_attr.attr, 134562306a36Sopenharmony_ci &iio_dev_attr_in_gyro_calibration_auto_status.dev_attr.attr, 134662306a36Sopenharmony_ci &iio_dev_attr_in_magn_calibration_auto_status.dev_attr.attr, 134762306a36Sopenharmony_ci &iio_dev_attr_serialnumber.dev_attr.attr, 134862306a36Sopenharmony_ci NULL 134962306a36Sopenharmony_ci}; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_cistatic struct bin_attribute *bno055_bin_attrs[] = { 135462306a36Sopenharmony_ci &bin_attr_calibration_data, 135562306a36Sopenharmony_ci NULL 135662306a36Sopenharmony_ci}; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic const struct attribute_group bno055_attrs_group = { 135962306a36Sopenharmony_ci .attrs = bno055_attrs, 136062306a36Sopenharmony_ci .bin_attrs = bno055_bin_attrs, 136162306a36Sopenharmony_ci}; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic const struct iio_info bno055_info = { 136462306a36Sopenharmony_ci .read_raw_multi = bno055_read_raw_multi, 136562306a36Sopenharmony_ci .read_avail = bno055_read_avail, 136662306a36Sopenharmony_ci .write_raw = bno055_write_raw, 136762306a36Sopenharmony_ci .attrs = &bno055_attrs_group, 136862306a36Sopenharmony_ci .debugfs_reg_access = bno055_debugfs_reg_access, 136962306a36Sopenharmony_ci}; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci/* 137262306a36Sopenharmony_ci * Reads len samples from the HW, stores them in buf starting from buf_idx, 137362306a36Sopenharmony_ci * and applies mask to cull (skip) unneeded samples. 137462306a36Sopenharmony_ci * Updates buf_idx incrementing with the number of stored samples. 137562306a36Sopenharmony_ci * Samples from HW are transferred into buf, then in-place copy on buf is 137662306a36Sopenharmony_ci * performed in order to cull samples that need to be skipped. 137762306a36Sopenharmony_ci * This avoids copies of the first samples until we hit the 1st sample to skip, 137862306a36Sopenharmony_ci * and also avoids having an extra bounce buffer. 137962306a36Sopenharmony_ci * buf must be able to contain len elements in spite of how many samples we are 138062306a36Sopenharmony_ci * going to cull. 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_cistatic int bno055_scan_xfer(struct bno055_priv *priv, 138362306a36Sopenharmony_ci int start_ch, int len, unsigned long mask, 138462306a36Sopenharmony_ci __le16 *buf, int *buf_idx) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci const int base = BNO055_ACC_DATA_X_LSB_REG; 138762306a36Sopenharmony_ci bool quat_in_read = false; 138862306a36Sopenharmony_ci int buf_base = *buf_idx; 138962306a36Sopenharmony_ci __le16 *dst, *src; 139062306a36Sopenharmony_ci int offs_fixup = 0; 139162306a36Sopenharmony_ci int xfer_len = len; 139262306a36Sopenharmony_ci int ret; 139362306a36Sopenharmony_ci int i, n; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (!mask) 139662306a36Sopenharmony_ci return 0; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci /* 139962306a36Sopenharmony_ci * All channels are made up 1 16-bit sample, except for quaternion that 140062306a36Sopenharmony_ci * is made up 4 16-bit values. 140162306a36Sopenharmony_ci * For us the quaternion CH is just like 4 regular CHs. 140262306a36Sopenharmony_ci * If our read starts past the quaternion make sure to adjust the 140362306a36Sopenharmony_ci * starting offset; if the quaternion is contained in our scan then make 140462306a36Sopenharmony_ci * sure to adjust the read len. 140562306a36Sopenharmony_ci */ 140662306a36Sopenharmony_ci if (start_ch > BNO055_SCAN_QUATERNION) { 140762306a36Sopenharmony_ci start_ch += 3; 140862306a36Sopenharmony_ci } else if ((start_ch <= BNO055_SCAN_QUATERNION) && 140962306a36Sopenharmony_ci ((start_ch + len) > BNO055_SCAN_QUATERNION)) { 141062306a36Sopenharmony_ci quat_in_read = true; 141162306a36Sopenharmony_ci xfer_len += 3; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, 141562306a36Sopenharmony_ci base + start_ch * sizeof(__le16), 141662306a36Sopenharmony_ci buf + buf_base, 141762306a36Sopenharmony_ci xfer_len * sizeof(__le16)); 141862306a36Sopenharmony_ci if (ret) 141962306a36Sopenharmony_ci return ret; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci for_each_set_bit(i, &mask, len) { 142262306a36Sopenharmony_ci if (quat_in_read && ((start_ch + i) > BNO055_SCAN_QUATERNION)) 142362306a36Sopenharmony_ci offs_fixup = 3; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci dst = buf + *buf_idx; 142662306a36Sopenharmony_ci src = buf + buf_base + offs_fixup + i; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci n = (start_ch + i == BNO055_SCAN_QUATERNION) ? 4 : 1; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (dst != src) 143162306a36Sopenharmony_ci memcpy(dst, src, n * sizeof(__le16)); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci *buf_idx += n; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci return 0; 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic irqreturn_t bno055_trigger_handler(int irq, void *p) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci struct iio_poll_func *pf = p; 144162306a36Sopenharmony_ci struct iio_dev *iio_dev = pf->indio_dev; 144262306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(iio_dev); 144362306a36Sopenharmony_ci int xfer_start, start, end, prev_end; 144462306a36Sopenharmony_ci unsigned long mask; 144562306a36Sopenharmony_ci int quat_extra_len; 144662306a36Sopenharmony_ci bool first = true; 144762306a36Sopenharmony_ci int buf_idx = 0; 144862306a36Sopenharmony_ci bool thr_hit; 144962306a36Sopenharmony_ci int ret; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci mutex_lock(&priv->lock); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* 145462306a36Sopenharmony_ci * Walk the bitmap and eventually perform several transfers. 145562306a36Sopenharmony_ci * Bitmap ones-fields that are separated by gaps <= xfer_burst_break_thr 145662306a36Sopenharmony_ci * will be included in same transfer. 145762306a36Sopenharmony_ci * Every time the bitmap contains a gap wider than xfer_burst_break_thr 145862306a36Sopenharmony_ci * then we split the transfer, skipping the gap. 145962306a36Sopenharmony_ci */ 146062306a36Sopenharmony_ci for_each_set_bitrange(start, end, iio_dev->active_scan_mask, 146162306a36Sopenharmony_ci iio_dev->masklength) { 146262306a36Sopenharmony_ci /* 146362306a36Sopenharmony_ci * First transfer will start from the beginning of the first 146462306a36Sopenharmony_ci * ones-field in the bitmap 146562306a36Sopenharmony_ci */ 146662306a36Sopenharmony_ci if (first) { 146762306a36Sopenharmony_ci xfer_start = start; 146862306a36Sopenharmony_ci } else { 146962306a36Sopenharmony_ci /* 147062306a36Sopenharmony_ci * We found the next ones-field; check whether to 147162306a36Sopenharmony_ci * include it in * the current transfer or not (i.e. 147262306a36Sopenharmony_ci * let's perform the current * transfer and prepare for 147362306a36Sopenharmony_ci * another one). 147462306a36Sopenharmony_ci */ 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* 147762306a36Sopenharmony_ci * In case the zeros-gap contains the quaternion bit, 147862306a36Sopenharmony_ci * then its length is actually 4 words instead of 1 147962306a36Sopenharmony_ci * (i.e. +3 wrt other channels). 148062306a36Sopenharmony_ci */ 148162306a36Sopenharmony_ci quat_extra_len = ((start > BNO055_SCAN_QUATERNION) && 148262306a36Sopenharmony_ci (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* If the gap is wider than xfer_burst_break_thr then.. */ 148562306a36Sopenharmony_ci thr_hit = (start - prev_end + quat_extra_len) > 148662306a36Sopenharmony_ci priv->xfer_burst_break_thr; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci /* 148962306a36Sopenharmony_ci * .. transfer all the data up to the gap. Then set the 149062306a36Sopenharmony_ci * next transfer start index at right after the gap 149162306a36Sopenharmony_ci * (i.e. at the start of this ones-field). 149262306a36Sopenharmony_ci */ 149362306a36Sopenharmony_ci if (thr_hit) { 149462306a36Sopenharmony_ci mask = *iio_dev->active_scan_mask >> xfer_start; 149562306a36Sopenharmony_ci ret = bno055_scan_xfer(priv, xfer_start, 149662306a36Sopenharmony_ci prev_end - xfer_start, 149762306a36Sopenharmony_ci mask, priv->buf.chans, &buf_idx); 149862306a36Sopenharmony_ci if (ret) 149962306a36Sopenharmony_ci goto done; 150062306a36Sopenharmony_ci xfer_start = start; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci first = false; 150462306a36Sopenharmony_ci prev_end = end; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci /* 150862306a36Sopenharmony_ci * We finished walking the bitmap; no more gaps to check for. Just 150962306a36Sopenharmony_ci * perform the current transfer. 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_ci mask = *iio_dev->active_scan_mask >> xfer_start; 151262306a36Sopenharmony_ci ret = bno055_scan_xfer(priv, xfer_start, 151362306a36Sopenharmony_ci prev_end - xfer_start, 151462306a36Sopenharmony_ci mask, priv->buf.chans, &buf_idx); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (!ret) 151762306a36Sopenharmony_ci iio_push_to_buffers_with_timestamp(iio_dev, 151862306a36Sopenharmony_ci &priv->buf, pf->timestamp); 151962306a36Sopenharmony_cidone: 152062306a36Sopenharmony_ci mutex_unlock(&priv->lock); 152162306a36Sopenharmony_ci iio_trigger_notify_done(iio_dev->trig); 152262306a36Sopenharmony_ci return IRQ_HANDLED; 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_cistatic int bno055_buffer_preenable(struct iio_dev *indio_dev) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci struct bno055_priv *priv = iio_priv(indio_dev); 152862306a36Sopenharmony_ci const unsigned long fusion_mask = 152962306a36Sopenharmony_ci BIT(BNO055_SCAN_YAW) | 153062306a36Sopenharmony_ci BIT(BNO055_SCAN_ROLL) | 153162306a36Sopenharmony_ci BIT(BNO055_SCAN_PITCH) | 153262306a36Sopenharmony_ci BIT(BNO055_SCAN_QUATERNION) | 153362306a36Sopenharmony_ci BIT(BNO055_SCAN_LIA_X) | 153462306a36Sopenharmony_ci BIT(BNO055_SCAN_LIA_Y) | 153562306a36Sopenharmony_ci BIT(BNO055_SCAN_LIA_Z) | 153662306a36Sopenharmony_ci BIT(BNO055_SCAN_GRAVITY_X) | 153762306a36Sopenharmony_ci BIT(BNO055_SCAN_GRAVITY_Y) | 153862306a36Sopenharmony_ci BIT(BNO055_SCAN_GRAVITY_Z); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci if (priv->operation_mode == BNO055_OPR_MODE_AMG && 154162306a36Sopenharmony_ci bitmap_intersects(indio_dev->active_scan_mask, &fusion_mask, 154262306a36Sopenharmony_ci _BNO055_SCAN_MAX)) 154362306a36Sopenharmony_ci return -EBUSY; 154462306a36Sopenharmony_ci return 0; 154562306a36Sopenharmony_ci} 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_cistatic const struct iio_buffer_setup_ops bno055_buffer_setup_ops = { 154862306a36Sopenharmony_ci .preenable = bno055_buffer_preenable, 154962306a36Sopenharmony_ci}; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ciint bno055_probe(struct device *dev, struct regmap *regmap, 155262306a36Sopenharmony_ci int xfer_burst_break_thr, bool sw_reset) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci const struct firmware *caldata = NULL; 155562306a36Sopenharmony_ci struct bno055_priv *priv; 155662306a36Sopenharmony_ci struct iio_dev *iio_dev; 155762306a36Sopenharmony_ci char *fw_name_buf; 155862306a36Sopenharmony_ci unsigned int val; 155962306a36Sopenharmony_ci int rev, ver; 156062306a36Sopenharmony_ci int ret; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci iio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); 156362306a36Sopenharmony_ci if (!iio_dev) 156462306a36Sopenharmony_ci return -ENOMEM; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci iio_dev->name = "bno055"; 156762306a36Sopenharmony_ci priv = iio_priv(iio_dev); 156862306a36Sopenharmony_ci mutex_init(&priv->lock); 156962306a36Sopenharmony_ci priv->regmap = regmap; 157062306a36Sopenharmony_ci priv->dev = dev; 157162306a36Sopenharmony_ci priv->xfer_burst_break_thr = xfer_burst_break_thr; 157262306a36Sopenharmony_ci priv->sw_reset = sw_reset; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 157562306a36Sopenharmony_ci if (IS_ERR(priv->reset_gpio)) 157662306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci priv->clk = devm_clk_get_optional_enabled(dev, "clk"); 157962306a36Sopenharmony_ci if (IS_ERR(priv->clk)) 158062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK\n"); 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (priv->reset_gpio) { 158362306a36Sopenharmony_ci usleep_range(5000, 10000); 158462306a36Sopenharmony_ci gpiod_set_value_cansleep(priv->reset_gpio, 1); 158562306a36Sopenharmony_ci usleep_range(650000, 750000); 158662306a36Sopenharmony_ci } else if (!sw_reset) { 158762306a36Sopenharmony_ci dev_warn(dev, "No usable reset method; IMU may be unreliable\n"); 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci ret = regmap_read(priv->regmap, BNO055_CHIP_ID_REG, &val); 159162306a36Sopenharmony_ci if (ret) 159262306a36Sopenharmony_ci return ret; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (val != BNO055_CHIP_ID_MAGIC) 159562306a36Sopenharmony_ci dev_warn(dev, "Unrecognized chip ID 0x%x\n", val); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci /* 159862306a36Sopenharmony_ci * In case we haven't a HW reset pin, we can still reset the chip via 159962306a36Sopenharmony_ci * register write. This is probably nonsense in case we can't even 160062306a36Sopenharmony_ci * communicate with the chip or the chip isn't the one we expect (i.e. 160162306a36Sopenharmony_ci * we don't write to unknown chips), so we perform SW reset only after 160262306a36Sopenharmony_ci * chip magic ID check 160362306a36Sopenharmony_ci */ 160462306a36Sopenharmony_ci if (!priv->reset_gpio) { 160562306a36Sopenharmony_ci ret = bno055_system_reset(priv); 160662306a36Sopenharmony_ci if (ret) 160762306a36Sopenharmony_ci return ret; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev); 161162306a36Sopenharmony_ci if (ret) 161262306a36Sopenharmony_ci return ret; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver); 161562306a36Sopenharmony_ci if (ret) 161662306a36Sopenharmony_ci return ret; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci /* 161962306a36Sopenharmony_ci * The stock FW version contains a bug (see comment at the beginning of 162062306a36Sopenharmony_ci * this file) that causes the anglvel scale to be changed depending on 162162306a36Sopenharmony_ci * the chip range setting. We workaround this, but we don't know what 162262306a36Sopenharmony_ci * other FW versions might do. 162362306a36Sopenharmony_ci */ 162462306a36Sopenharmony_ci if (ver != 0x3 || rev != 0x11) 162562306a36Sopenharmony_ci dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected\n"); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG, 162862306a36Sopenharmony_ci priv->uid, BNO055_UID_LEN); 162962306a36Sopenharmony_ci if (ret) 163062306a36Sopenharmony_ci return ret; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* Sensor calibration data */ 163362306a36Sopenharmony_ci fw_name_buf = kasprintf(GFP_KERNEL, BNO055_FW_UID_FMT, 163462306a36Sopenharmony_ci BNO055_UID_LEN, priv->uid); 163562306a36Sopenharmony_ci if (!fw_name_buf) 163662306a36Sopenharmony_ci return -ENOMEM; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci ret = request_firmware(&caldata, fw_name_buf, dev); 163962306a36Sopenharmony_ci kfree(fw_name_buf); 164062306a36Sopenharmony_ci if (ret) 164162306a36Sopenharmony_ci ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev); 164262306a36Sopenharmony_ci if (ret) { 164362306a36Sopenharmony_ci dev_notice(dev, "Calibration file load failed. See instruction in kernel Documentation/iio/bno055.rst\n"); 164462306a36Sopenharmony_ci ret = bno055_init(priv, NULL, 0); 164562306a36Sopenharmony_ci } else { 164662306a36Sopenharmony_ci ret = bno055_init(priv, caldata->data, caldata->size); 164762306a36Sopenharmony_ci release_firmware(caldata); 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci if (ret) 165062306a36Sopenharmony_ci return ret; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci priv->operation_mode = BNO055_OPR_MODE_FUSION; 165362306a36Sopenharmony_ci ret = bno055_operation_mode_do_set(priv, priv->operation_mode); 165462306a36Sopenharmony_ci if (ret) 165562306a36Sopenharmony_ci return ret; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, bno055_uninit, priv); 165862306a36Sopenharmony_ci if (ret) 165962306a36Sopenharmony_ci return ret; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci iio_dev->channels = bno055_channels; 166262306a36Sopenharmony_ci iio_dev->num_channels = ARRAY_SIZE(bno055_channels); 166362306a36Sopenharmony_ci iio_dev->info = &bno055_info; 166462306a36Sopenharmony_ci iio_dev->modes = INDIO_DIRECT_MODE; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(dev, iio_dev, 166762306a36Sopenharmony_ci iio_pollfunc_store_time, 166862306a36Sopenharmony_ci bno055_trigger_handler, 166962306a36Sopenharmony_ci &bno055_buffer_setup_ops); 167062306a36Sopenharmony_ci if (ret) 167162306a36Sopenharmony_ci return ret; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci ret = devm_iio_device_register(dev, iio_dev); 167462306a36Sopenharmony_ci if (ret) 167562306a36Sopenharmony_ci return ret; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci bno055_debugfs_init(iio_dev); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci return 0; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(bno055_probe, IIO_BNO055); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ciMODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>"); 168462306a36Sopenharmony_ciMODULE_DESCRIPTION("Bosch BNO055 driver"); 168562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1686