1c19ae6beSMike Looijmans // SPDX-License-Identifier: GPL-2.0 2c19ae6beSMike Looijmans /* 3c19ae6beSMike Looijmans * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: 4c19ae6beSMike Looijmans * - BMI088 5c19ae6beSMike Looijmans * 6c19ae6beSMike Looijmans * Copyright (c) 2018-2021, Topic Embedded Products 7c19ae6beSMike Looijmans */ 8c19ae6beSMike Looijmans 973314772SLI Qingwu #include <linux/bitfield.h> 10c19ae6beSMike Looijmans #include <linux/delay.h> 11c19ae6beSMike Looijmans #include <linux/iio/iio.h> 12c19ae6beSMike Looijmans #include <linux/iio/sysfs.h> 13c19ae6beSMike Looijmans #include <linux/interrupt.h> 14c19ae6beSMike Looijmans #include <linux/module.h> 15c19ae6beSMike Looijmans #include <linux/pm.h> 16c19ae6beSMike Looijmans #include <linux/pm_runtime.h> 17c19ae6beSMike Looijmans #include <linux/regmap.h> 18c19ae6beSMike Looijmans #include <linux/slab.h> 19c19ae6beSMike Looijmans #include <asm/unaligned.h> 20c19ae6beSMike Looijmans 21c19ae6beSMike Looijmans #include "bmi088-accel.h" 22c19ae6beSMike Looijmans 23c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_CHIP_ID 0x00 24c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ERROR 0x02 25c19ae6beSMike Looijmans 26c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT_STATUS 0x1D 27c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_STATUS_BIT_DRDY BIT(7) 28c19ae6beSMike Looijmans 29c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_RESET 0x7E 30c19ae6beSMike Looijmans #define BMI088_ACCEL_RESET_VAL 0xB6 31c19ae6beSMike Looijmans 32c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_PWR_CTRL 0x7D 33c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_PWR_CONF 0x7C 34c19ae6beSMike Looijmans 35c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT_MAP_DATA 0x58 36c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_MAP_DATA_BIT_INT1_DRDY BIT(2) 37c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_MAP_DATA_BIT_INT2_FWM BIT(5) 38c19ae6beSMike Looijmans 39c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT1_IO_CONF 0x53 40c19ae6beSMike Looijmans #define BMI088_ACCEL_INT1_IO_CONF_BIT_ENABLE_OUT BIT(3) 41c19ae6beSMike Looijmans #define BMI088_ACCEL_INT1_IO_CONF_BIT_LVL BIT(1) 42c19ae6beSMike Looijmans 43c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT2_IO_CONF 0x54 44c19ae6beSMike Looijmans #define BMI088_ACCEL_INT2_IO_CONF_BIT_ENABLE_OUT BIT(3) 45c19ae6beSMike Looijmans #define BMI088_ACCEL_INT2_IO_CONF_BIT_LVL BIT(1) 46c19ae6beSMike Looijmans 47c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ACC_CONF 0x40 48c19ae6beSMike Looijmans #define BMI088_ACCEL_MODE_ODR_MASK 0x0f 49c19ae6beSMike Looijmans 50c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ACC_RANGE 0x41 51c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_3G 0x00 52c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_6G 0x01 53c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_12G 0x02 54c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_24G 0x03 55c19ae6beSMike Looijmans 56c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_TEMP 0x22 57c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_TEMP_SHIFT 5 58c19ae6beSMike Looijmans #define BMI088_ACCEL_TEMP_UNIT 125 59c19ae6beSMike Looijmans #define BMI088_ACCEL_TEMP_OFFSET 23000 60c19ae6beSMike Looijmans 61c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_XOUT_L 0x12 62c19ae6beSMike Looijmans #define BMI088_ACCEL_AXIS_TO_REG(axis) \ 63c19ae6beSMike Looijmans (BMI088_ACCEL_REG_XOUT_L + (axis * 2)) 64c19ae6beSMike Looijmans 65c19ae6beSMike Looijmans #define BMI088_ACCEL_MAX_STARTUP_TIME_US 1000 66c19ae6beSMike Looijmans #define BMI088_AUTO_SUSPEND_DELAY_MS 2000 67c19ae6beSMike Looijmans 68c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_STATUS 0x0E 69c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_CONFIG0 0x48 70c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_CONFIG1 0x49 71c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_DATA 0x3F 72c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_LENGTH 100 73c19ae6beSMike Looijmans 74c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_MODE_FIFO 0x40 75c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_MODE_STREAM 0x80 76c19ae6beSMike Looijmans 7773314772SLI Qingwu #define BMIO088_ACCEL_ACC_RANGE_MSK GENMASK(1, 0) 7873314772SLI Qingwu 79c19ae6beSMike Looijmans enum bmi088_accel_axis { 80c19ae6beSMike Looijmans AXIS_X, 81c19ae6beSMike Looijmans AXIS_Y, 82c19ae6beSMike Looijmans AXIS_Z, 83c19ae6beSMike Looijmans }; 84c19ae6beSMike Looijmans 85c19ae6beSMike Looijmans static const int bmi088_sample_freqs[] = { 86c19ae6beSMike Looijmans 12, 500000, 87c19ae6beSMike Looijmans 25, 0, 88c19ae6beSMike Looijmans 50, 0, 89c19ae6beSMike Looijmans 100, 0, 90c19ae6beSMike Looijmans 200, 0, 91c19ae6beSMike Looijmans 400, 0, 92c19ae6beSMike Looijmans 800, 0, 93c19ae6beSMike Looijmans 1600, 0, 94c19ae6beSMike Looijmans }; 95c19ae6beSMike Looijmans 96c19ae6beSMike Looijmans /* Available OSR (over sampling rate) sets the 3dB cut-off frequency */ 97c19ae6beSMike Looijmans enum bmi088_osr_modes { 98c19ae6beSMike Looijmans BMI088_ACCEL_MODE_OSR_NORMAL = 0xA, 99c19ae6beSMike Looijmans BMI088_ACCEL_MODE_OSR_2 = 0x9, 100c19ae6beSMike Looijmans BMI088_ACCEL_MODE_OSR_4 = 0x8, 101c19ae6beSMike Looijmans }; 102c19ae6beSMike Looijmans 103c19ae6beSMike Looijmans /* Available ODR (output data rates) in Hz */ 104c19ae6beSMike Looijmans enum bmi088_odr_modes { 105c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_12_5 = 0x5, 106c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_25 = 0x6, 107c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_50 = 0x7, 108c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_100 = 0x8, 109c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_200 = 0x9, 110c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_400 = 0xa, 111c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_800 = 0xb, 112c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_1600 = 0xc, 113c19ae6beSMike Looijmans }; 114c19ae6beSMike Looijmans 115c19ae6beSMike Looijmans struct bmi088_scale_info { 116c19ae6beSMike Looijmans int scale; 117c19ae6beSMike Looijmans u8 reg_range; 118c19ae6beSMike Looijmans }; 119c19ae6beSMike Looijmans 120c19ae6beSMike Looijmans struct bmi088_accel_chip_info { 121c19ae6beSMike Looijmans const char *name; 122c19ae6beSMike Looijmans u8 chip_id; 123c19ae6beSMike Looijmans const struct iio_chan_spec *channels; 124c19ae6beSMike Looijmans int num_channels; 12573314772SLI Qingwu const int scale_table[4][2]; 126c19ae6beSMike Looijmans }; 127c19ae6beSMike Looijmans 128c19ae6beSMike Looijmans struct bmi088_accel_data { 129c19ae6beSMike Looijmans struct regmap *regmap; 130c19ae6beSMike Looijmans const struct bmi088_accel_chip_info *chip_info; 131c19ae6beSMike Looijmans u8 buffer[2] ____cacheline_aligned; /* shared DMA safe buffer */ 132c19ae6beSMike Looijmans }; 133c19ae6beSMike Looijmans 134c19ae6beSMike Looijmans static const struct regmap_range bmi088_volatile_ranges[] = { 135c19ae6beSMike Looijmans /* All registers below 0x40 are volatile, except the CHIP ID. */ 136c19ae6beSMike Looijmans regmap_reg_range(BMI088_ACCEL_REG_ERROR, 0x3f), 137c19ae6beSMike Looijmans /* Mark the RESET as volatile too, it is self-clearing */ 138c19ae6beSMike Looijmans regmap_reg_range(BMI088_ACCEL_REG_RESET, BMI088_ACCEL_REG_RESET), 139c19ae6beSMike Looijmans }; 140c19ae6beSMike Looijmans 141c19ae6beSMike Looijmans static const struct regmap_access_table bmi088_volatile_table = { 142c19ae6beSMike Looijmans .yes_ranges = bmi088_volatile_ranges, 143c19ae6beSMike Looijmans .n_yes_ranges = ARRAY_SIZE(bmi088_volatile_ranges), 144c19ae6beSMike Looijmans }; 145c19ae6beSMike Looijmans 146c19ae6beSMike Looijmans const struct regmap_config bmi088_regmap_conf = { 147c19ae6beSMike Looijmans .reg_bits = 8, 148c19ae6beSMike Looijmans .val_bits = 8, 149c19ae6beSMike Looijmans .max_register = 0x7E, 150c19ae6beSMike Looijmans .volatile_table = &bmi088_volatile_table, 151c19ae6beSMike Looijmans .cache_type = REGCACHE_RBTREE, 152c19ae6beSMike Looijmans }; 1533bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, IIO_BMI088); 154c19ae6beSMike Looijmans 155c19ae6beSMike Looijmans static int bmi088_accel_power_up(struct bmi088_accel_data *data) 156c19ae6beSMike Looijmans { 157c19ae6beSMike Looijmans int ret; 158c19ae6beSMike Looijmans 159c19ae6beSMike Looijmans /* Enable accelerometer and temperature sensor */ 160c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x4); 161c19ae6beSMike Looijmans if (ret) 162c19ae6beSMike Looijmans return ret; 163c19ae6beSMike Looijmans 164c19ae6beSMike Looijmans /* Datasheet recommends to wait at least 5ms before communication */ 165c19ae6beSMike Looijmans usleep_range(5000, 6000); 166c19ae6beSMike Looijmans 167c19ae6beSMike Looijmans /* Disable suspend mode */ 168c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x0); 169c19ae6beSMike Looijmans if (ret) 170c19ae6beSMike Looijmans return ret; 171c19ae6beSMike Looijmans 172c19ae6beSMike Looijmans /* Recommended at least 1ms before further communication */ 173c19ae6beSMike Looijmans usleep_range(1000, 1200); 174c19ae6beSMike Looijmans 175c19ae6beSMike Looijmans return 0; 176c19ae6beSMike Looijmans } 177c19ae6beSMike Looijmans 178c19ae6beSMike Looijmans static int bmi088_accel_power_down(struct bmi088_accel_data *data) 179c19ae6beSMike Looijmans { 180c19ae6beSMike Looijmans int ret; 181c19ae6beSMike Looijmans 182c19ae6beSMike Looijmans /* Enable suspend mode */ 183c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x3); 184c19ae6beSMike Looijmans if (ret) 185c19ae6beSMike Looijmans return ret; 186c19ae6beSMike Looijmans 187c19ae6beSMike Looijmans /* Recommended at least 1ms before further communication */ 188c19ae6beSMike Looijmans usleep_range(1000, 1200); 189c19ae6beSMike Looijmans 190c19ae6beSMike Looijmans /* Disable accelerometer and temperature sensor */ 191c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x0); 192c19ae6beSMike Looijmans if (ret) 193c19ae6beSMike Looijmans return ret; 194c19ae6beSMike Looijmans 195c19ae6beSMike Looijmans /* Datasheet recommends to wait at least 5ms before communication */ 196c19ae6beSMike Looijmans usleep_range(5000, 6000); 197c19ae6beSMike Looijmans 198c19ae6beSMike Looijmans return 0; 199c19ae6beSMike Looijmans } 200c19ae6beSMike Looijmans 201c19ae6beSMike Looijmans static int bmi088_accel_get_sample_freq(struct bmi088_accel_data *data, 202c19ae6beSMike Looijmans int *val, int *val2) 203c19ae6beSMike Looijmans { 204c19ae6beSMike Looijmans unsigned int value; 205c19ae6beSMike Looijmans int ret; 206c19ae6beSMike Looijmans 207c19ae6beSMike Looijmans ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_CONF, 208c19ae6beSMike Looijmans &value); 209c19ae6beSMike Looijmans if (ret) 210c19ae6beSMike Looijmans return ret; 211c19ae6beSMike Looijmans 212c19ae6beSMike Looijmans value &= BMI088_ACCEL_MODE_ODR_MASK; 213c19ae6beSMike Looijmans value -= BMI088_ACCEL_MODE_ODR_12_5; 214c19ae6beSMike Looijmans value <<= 1; 215c19ae6beSMike Looijmans 216c19ae6beSMike Looijmans if (value >= ARRAY_SIZE(bmi088_sample_freqs) - 1) 217c19ae6beSMike Looijmans return -EINVAL; 218c19ae6beSMike Looijmans 219c19ae6beSMike Looijmans *val = bmi088_sample_freqs[value]; 220c19ae6beSMike Looijmans *val2 = bmi088_sample_freqs[value + 1]; 221c19ae6beSMike Looijmans 222c19ae6beSMike Looijmans return IIO_VAL_INT_PLUS_MICRO; 223c19ae6beSMike Looijmans } 224c19ae6beSMike Looijmans 225c19ae6beSMike Looijmans static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val) 226c19ae6beSMike Looijmans { 227c19ae6beSMike Looijmans unsigned int regval; 228c19ae6beSMike Looijmans int index = 0; 229c19ae6beSMike Looijmans 230c19ae6beSMike Looijmans while (index < ARRAY_SIZE(bmi088_sample_freqs) && 231c19ae6beSMike Looijmans bmi088_sample_freqs[index] != val) 232c19ae6beSMike Looijmans index += 2; 233c19ae6beSMike Looijmans 234c19ae6beSMike Looijmans if (index >= ARRAY_SIZE(bmi088_sample_freqs)) 235c19ae6beSMike Looijmans return -EINVAL; 236c19ae6beSMike Looijmans 237c19ae6beSMike Looijmans regval = (index >> 1) + BMI088_ACCEL_MODE_ODR_12_5; 238c19ae6beSMike Looijmans 239c19ae6beSMike Looijmans return regmap_update_bits(data->regmap, BMI088_ACCEL_REG_ACC_CONF, 240c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_MASK, regval); 241c19ae6beSMike Looijmans } 242c19ae6beSMike Looijmans 24348d07b3bSLI Qingwu static int bmi088_accel_set_scale(struct bmi088_accel_data *data, int val, int val2) 24448d07b3bSLI Qingwu { 24548d07b3bSLI Qingwu unsigned int i; 24648d07b3bSLI Qingwu 24748d07b3bSLI Qingwu for (i = 0; i < 4; i++) 24848d07b3bSLI Qingwu if (val == data->chip_info->scale_table[i][0] && 24948d07b3bSLI Qingwu val2 == data->chip_info->scale_table[i][1]) 25048d07b3bSLI Qingwu break; 25148d07b3bSLI Qingwu 25248d07b3bSLI Qingwu if (i == 4) 25348d07b3bSLI Qingwu return -EINVAL; 25448d07b3bSLI Qingwu 25548d07b3bSLI Qingwu return regmap_write(data->regmap, BMI088_ACCEL_REG_ACC_RANGE, i); 25648d07b3bSLI Qingwu } 25748d07b3bSLI Qingwu 258c19ae6beSMike Looijmans static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val) 259c19ae6beSMike Looijmans { 260c19ae6beSMike Looijmans int ret; 261c19ae6beSMike Looijmans s16 temp; 262c19ae6beSMike Looijmans 263c19ae6beSMike Looijmans ret = regmap_bulk_read(data->regmap, BMI088_ACCEL_REG_TEMP, 264c19ae6beSMike Looijmans &data->buffer, sizeof(__be16)); 265c19ae6beSMike Looijmans if (ret) 266c19ae6beSMike Looijmans return ret; 267c19ae6beSMike Looijmans 268c19ae6beSMike Looijmans /* data->buffer is cacheline aligned */ 269c19ae6beSMike Looijmans temp = be16_to_cpu(*(__be16 *)data->buffer); 270c19ae6beSMike Looijmans 271c19ae6beSMike Looijmans *val = temp >> BMI088_ACCEL_REG_TEMP_SHIFT; 272c19ae6beSMike Looijmans 273c19ae6beSMike Looijmans return IIO_VAL_INT; 274c19ae6beSMike Looijmans } 275c19ae6beSMike Looijmans 276c19ae6beSMike Looijmans static int bmi088_accel_get_axis(struct bmi088_accel_data *data, 277c19ae6beSMike Looijmans struct iio_chan_spec const *chan, 278c19ae6beSMike Looijmans int *val) 279c19ae6beSMike Looijmans { 280c19ae6beSMike Looijmans int ret; 281c19ae6beSMike Looijmans s16 raw_val; 282c19ae6beSMike Looijmans 283c19ae6beSMike Looijmans ret = regmap_bulk_read(data->regmap, 284c19ae6beSMike Looijmans BMI088_ACCEL_AXIS_TO_REG(chan->scan_index), 285c19ae6beSMike Looijmans data->buffer, sizeof(__le16)); 286c19ae6beSMike Looijmans if (ret) 287c19ae6beSMike Looijmans return ret; 288c19ae6beSMike Looijmans 289c19ae6beSMike Looijmans raw_val = le16_to_cpu(*(__le16 *)data->buffer); 290c19ae6beSMike Looijmans *val = raw_val; 291c19ae6beSMike Looijmans 292c19ae6beSMike Looijmans return IIO_VAL_INT; 293c19ae6beSMike Looijmans } 294c19ae6beSMike Looijmans 295c19ae6beSMike Looijmans static int bmi088_accel_read_raw(struct iio_dev *indio_dev, 296c19ae6beSMike Looijmans struct iio_chan_spec const *chan, 297c19ae6beSMike Looijmans int *val, int *val2, long mask) 298c19ae6beSMike Looijmans { 299c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev); 300c19ae6beSMike Looijmans struct device *dev = regmap_get_device(data->regmap); 301c19ae6beSMike Looijmans int ret; 30273314772SLI Qingwu int reg; 303c19ae6beSMike Looijmans 304c19ae6beSMike Looijmans switch (mask) { 305c19ae6beSMike Looijmans case IIO_CHAN_INFO_RAW: 306c19ae6beSMike Looijmans switch (chan->type) { 307c19ae6beSMike Looijmans case IIO_TEMP: 3089a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev); 3099a20795cSJonathan Cameron if (ret) 3109a20795cSJonathan Cameron return ret; 3119a20795cSJonathan Cameron 312c19ae6beSMike Looijmans ret = bmi088_accel_get_temp(data, val); 313c19ae6beSMike Looijmans goto out_read_raw_pm_put; 314c19ae6beSMike Looijmans case IIO_ACCEL: 3159a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev); 3169a20795cSJonathan Cameron if (ret) 3179a20795cSJonathan Cameron return ret; 3189a20795cSJonathan Cameron 319c19ae6beSMike Looijmans ret = iio_device_claim_direct_mode(indio_dev); 320c19ae6beSMike Looijmans if (ret) 321c19ae6beSMike Looijmans goto out_read_raw_pm_put; 322c19ae6beSMike Looijmans 323c19ae6beSMike Looijmans ret = bmi088_accel_get_axis(data, chan, val); 324c19ae6beSMike Looijmans iio_device_release_direct_mode(indio_dev); 325c19ae6beSMike Looijmans if (!ret) 326c19ae6beSMike Looijmans ret = IIO_VAL_INT; 327c19ae6beSMike Looijmans 328c19ae6beSMike Looijmans goto out_read_raw_pm_put; 329c19ae6beSMike Looijmans default: 330c19ae6beSMike Looijmans return -EINVAL; 331c19ae6beSMike Looijmans } 332c19ae6beSMike Looijmans case IIO_CHAN_INFO_OFFSET: 333c19ae6beSMike Looijmans switch (chan->type) { 334c19ae6beSMike Looijmans case IIO_TEMP: 335c19ae6beSMike Looijmans /* Offset applies before scale */ 336c19ae6beSMike Looijmans *val = BMI088_ACCEL_TEMP_OFFSET/BMI088_ACCEL_TEMP_UNIT; 337c19ae6beSMike Looijmans return IIO_VAL_INT; 338c19ae6beSMike Looijmans default: 339c19ae6beSMike Looijmans return -EINVAL; 340c19ae6beSMike Looijmans } 341c19ae6beSMike Looijmans case IIO_CHAN_INFO_SCALE: 342c19ae6beSMike Looijmans switch (chan->type) { 343c19ae6beSMike Looijmans case IIO_TEMP: 344c19ae6beSMike Looijmans /* 0.125 degrees per LSB */ 345c19ae6beSMike Looijmans *val = BMI088_ACCEL_TEMP_UNIT; 346c19ae6beSMike Looijmans return IIO_VAL_INT; 347c19ae6beSMike Looijmans case IIO_ACCEL: 3489a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev); 3499a20795cSJonathan Cameron if (ret) 3509a20795cSJonathan Cameron return ret; 3519a20795cSJonathan Cameron 352c19ae6beSMike Looijmans ret = regmap_read(data->regmap, 35373314772SLI Qingwu BMI088_ACCEL_REG_ACC_RANGE, ®); 354c19ae6beSMike Looijmans if (ret) 355c19ae6beSMike Looijmans goto out_read_raw_pm_put; 356c19ae6beSMike Looijmans 35773314772SLI Qingwu reg = FIELD_GET(BMIO088_ACCEL_ACC_RANGE_MSK, reg); 35873314772SLI Qingwu *val = data->chip_info->scale_table[reg][0]; 35973314772SLI Qingwu *val2 = data->chip_info->scale_table[reg][1]; 36073314772SLI Qingwu ret = IIO_VAL_INT_PLUS_MICRO; 361c19ae6beSMike Looijmans 362c19ae6beSMike Looijmans goto out_read_raw_pm_put; 363c19ae6beSMike Looijmans default: 364c19ae6beSMike Looijmans return -EINVAL; 365c19ae6beSMike Looijmans } 366c19ae6beSMike Looijmans case IIO_CHAN_INFO_SAMP_FREQ: 3679a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev); 3689a20795cSJonathan Cameron if (ret) 3699a20795cSJonathan Cameron return ret; 3709a20795cSJonathan Cameron 371c19ae6beSMike Looijmans ret = bmi088_accel_get_sample_freq(data, val, val2); 372c19ae6beSMike Looijmans goto out_read_raw_pm_put; 373c19ae6beSMike Looijmans default: 374c19ae6beSMike Looijmans break; 375c19ae6beSMike Looijmans } 376c19ae6beSMike Looijmans 377c19ae6beSMike Looijmans return -EINVAL; 378c19ae6beSMike Looijmans 379c19ae6beSMike Looijmans out_read_raw_pm_put: 380c19ae6beSMike Looijmans pm_runtime_mark_last_busy(dev); 381c19ae6beSMike Looijmans pm_runtime_put_autosuspend(dev); 382c19ae6beSMike Looijmans 383c19ae6beSMike Looijmans return ret; 384c19ae6beSMike Looijmans } 385c19ae6beSMike Looijmans 386c19ae6beSMike Looijmans static int bmi088_accel_read_avail(struct iio_dev *indio_dev, 387c19ae6beSMike Looijmans struct iio_chan_spec const *chan, 388c19ae6beSMike Looijmans const int **vals, int *type, int *length, 389c19ae6beSMike Looijmans long mask) 390c19ae6beSMike Looijmans { 39148d07b3bSLI Qingwu struct bmi088_accel_data *data = iio_priv(indio_dev); 39267ac266dSLI Qingwu 393c19ae6beSMike Looijmans switch (mask) { 39448d07b3bSLI Qingwu case IIO_CHAN_INFO_SCALE: 39548d07b3bSLI Qingwu *vals = (const int *)data->chip_info->scale_table; 39648d07b3bSLI Qingwu *length = 8; 39748d07b3bSLI Qingwu *type = IIO_VAL_INT_PLUS_MICRO; 39848d07b3bSLI Qingwu return IIO_AVAIL_LIST; 399c19ae6beSMike Looijmans case IIO_CHAN_INFO_SAMP_FREQ: 400c19ae6beSMike Looijmans *type = IIO_VAL_INT_PLUS_MICRO; 401c19ae6beSMike Looijmans *vals = bmi088_sample_freqs; 402c19ae6beSMike Looijmans *length = ARRAY_SIZE(bmi088_sample_freqs); 403c19ae6beSMike Looijmans return IIO_AVAIL_LIST; 404c19ae6beSMike Looijmans default: 405c19ae6beSMike Looijmans return -EINVAL; 406c19ae6beSMike Looijmans } 407c19ae6beSMike Looijmans } 408c19ae6beSMike Looijmans 409c19ae6beSMike Looijmans static int bmi088_accel_write_raw(struct iio_dev *indio_dev, 410c19ae6beSMike Looijmans struct iio_chan_spec const *chan, 411c19ae6beSMike Looijmans int val, int val2, long mask) 412c19ae6beSMike Looijmans { 413c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev); 414c19ae6beSMike Looijmans struct device *dev = regmap_get_device(data->regmap); 415c19ae6beSMike Looijmans int ret; 416c19ae6beSMike Looijmans 417c19ae6beSMike Looijmans switch (mask) { 41848d07b3bSLI Qingwu case IIO_CHAN_INFO_SCALE: 41948d07b3bSLI Qingwu ret = pm_runtime_resume_and_get(dev); 42048d07b3bSLI Qingwu if (ret) 42148d07b3bSLI Qingwu return ret; 42248d07b3bSLI Qingwu 42348d07b3bSLI Qingwu ret = bmi088_accel_set_scale(data, val, val2); 42448d07b3bSLI Qingwu pm_runtime_mark_last_busy(dev); 42548d07b3bSLI Qingwu pm_runtime_put_autosuspend(dev); 42648d07b3bSLI Qingwu return ret; 427c19ae6beSMike Looijmans case IIO_CHAN_INFO_SAMP_FREQ: 4289a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev); 4299a20795cSJonathan Cameron if (ret) 4309a20795cSJonathan Cameron return ret; 4319a20795cSJonathan Cameron 432c19ae6beSMike Looijmans ret = bmi088_accel_set_sample_freq(data, val); 433c19ae6beSMike Looijmans pm_runtime_mark_last_busy(dev); 434c19ae6beSMike Looijmans pm_runtime_put_autosuspend(dev); 435c19ae6beSMike Looijmans return ret; 436c19ae6beSMike Looijmans default: 437c19ae6beSMike Looijmans return -EINVAL; 438c19ae6beSMike Looijmans } 439c19ae6beSMike Looijmans } 440c19ae6beSMike Looijmans 441c19ae6beSMike Looijmans #define BMI088_ACCEL_CHANNEL(_axis) { \ 442c19ae6beSMike Looijmans .type = IIO_ACCEL, \ 443c19ae6beSMike Looijmans .modified = 1, \ 444c19ae6beSMike Looijmans .channel2 = IIO_MOD_##_axis, \ 445c19ae6beSMike Looijmans .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 446c19ae6beSMike Looijmans .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 447c19ae6beSMike Looijmans BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 44848d07b3bSLI Qingwu .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 44948d07b3bSLI Qingwu BIT(IIO_CHAN_INFO_SCALE), \ 450c19ae6beSMike Looijmans .scan_index = AXIS_##_axis, \ 451c19ae6beSMike Looijmans } 452c19ae6beSMike Looijmans 453c19ae6beSMike Looijmans static const struct iio_chan_spec bmi088_accel_channels[] = { 454c19ae6beSMike Looijmans { 455c19ae6beSMike Looijmans .type = IIO_TEMP, 456c19ae6beSMike Looijmans .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 457c19ae6beSMike Looijmans BIT(IIO_CHAN_INFO_SCALE) | 458c19ae6beSMike Looijmans BIT(IIO_CHAN_INFO_OFFSET), 459c19ae6beSMike Looijmans .scan_index = -1, 460c19ae6beSMike Looijmans }, 461c19ae6beSMike Looijmans BMI088_ACCEL_CHANNEL(X), 462c19ae6beSMike Looijmans BMI088_ACCEL_CHANNEL(Y), 463c19ae6beSMike Looijmans BMI088_ACCEL_CHANNEL(Z), 464c19ae6beSMike Looijmans IIO_CHAN_SOFT_TIMESTAMP(3), 465c19ae6beSMike Looijmans }; 466c19ae6beSMike Looijmans 467c19ae6beSMike Looijmans static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = { 4687a61456cSLI Qingwu [BOSCH_BMI085] = { 4697a61456cSLI Qingwu .name = "bmi085-accel", 4707a61456cSLI Qingwu .chip_id = 0x1F, 4717a61456cSLI Qingwu .channels = bmi088_accel_channels, 4727a61456cSLI Qingwu .num_channels = ARRAY_SIZE(bmi088_accel_channels), 4737a61456cSLI Qingwu .scale_table = {{0, 598}, {0, 1196}, {0, 2393}, {0, 4785}}, 4747a61456cSLI Qingwu }, 47567ac266dSLI Qingwu [BOSCH_BMI088] = { 47667ac266dSLI Qingwu .name = "bmi088-accel", 477c19ae6beSMike Looijmans .chip_id = 0x1E, 478c19ae6beSMike Looijmans .channels = bmi088_accel_channels, 479c19ae6beSMike Looijmans .num_channels = ARRAY_SIZE(bmi088_accel_channels), 48073314772SLI Qingwu .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}}, 481c19ae6beSMike Looijmans }, 482*57387d3cSLI Qingwu [BOSCH_BMI090L] = { 483*57387d3cSLI Qingwu .name = "bmi090l-accel", 484*57387d3cSLI Qingwu .chip_id = 0x1A, 485*57387d3cSLI Qingwu .channels = bmi088_accel_channels, 486*57387d3cSLI Qingwu .num_channels = ARRAY_SIZE(bmi088_accel_channels), 487*57387d3cSLI Qingwu .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}}, 488*57387d3cSLI Qingwu }, 489c19ae6beSMike Looijmans }; 490c19ae6beSMike Looijmans 491c19ae6beSMike Looijmans static const struct iio_info bmi088_accel_info = { 492c19ae6beSMike Looijmans .read_raw = bmi088_accel_read_raw, 493c19ae6beSMike Looijmans .write_raw = bmi088_accel_write_raw, 494c19ae6beSMike Looijmans .read_avail = bmi088_accel_read_avail, 495c19ae6beSMike Looijmans }; 496c19ae6beSMike Looijmans 497c19ae6beSMike Looijmans static const unsigned long bmi088_accel_scan_masks[] = { 498c19ae6beSMike Looijmans BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), 499c19ae6beSMike Looijmans 0 500c19ae6beSMike Looijmans }; 501c19ae6beSMike Looijmans 50267ac266dSLI Qingwu static int bmi088_accel_chip_init(struct bmi088_accel_data *data, enum bmi_device_type type) 503c19ae6beSMike Looijmans { 504c19ae6beSMike Looijmans struct device *dev = regmap_get_device(data->regmap); 505c19ae6beSMike Looijmans int ret, i; 506c19ae6beSMike Looijmans unsigned int val; 507c19ae6beSMike Looijmans 50867ac266dSLI Qingwu if (type >= BOSCH_UNKNOWN) 50967ac266dSLI Qingwu return -ENODEV; 51067ac266dSLI Qingwu 511c19ae6beSMike Looijmans /* Do a dummy read to enable SPI interface, won't harm I2C */ 512c19ae6beSMike Looijmans regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); 513c19ae6beSMike Looijmans 514c19ae6beSMike Looijmans /* 515c19ae6beSMike Looijmans * Reset chip to get it in a known good state. A delay of 1ms after 516c19ae6beSMike Looijmans * reset is required according to the data sheet 517c19ae6beSMike Looijmans */ 518c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_RESET, 519c19ae6beSMike Looijmans BMI088_ACCEL_RESET_VAL); 520c19ae6beSMike Looijmans if (ret) 521c19ae6beSMike Looijmans return ret; 522c19ae6beSMike Looijmans 523c19ae6beSMike Looijmans usleep_range(1000, 2000); 524c19ae6beSMike Looijmans 525c19ae6beSMike Looijmans /* Do a dummy read again after a reset to enable the SPI interface */ 526c19ae6beSMike Looijmans regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); 527c19ae6beSMike Looijmans 528c19ae6beSMike Looijmans /* Read chip ID */ 529c19ae6beSMike Looijmans ret = regmap_read(data->regmap, BMI088_ACCEL_REG_CHIP_ID, &val); 530c19ae6beSMike Looijmans if (ret) { 531c19ae6beSMike Looijmans dev_err(dev, "Error: Reading chip id\n"); 532c19ae6beSMike Looijmans return ret; 533c19ae6beSMike Looijmans } 534c19ae6beSMike Looijmans 535c19ae6beSMike Looijmans /* Validate chip ID */ 53667ac266dSLI Qingwu for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++) 53767ac266dSLI Qingwu if (bmi088_accel_chip_info_tbl[i].chip_id == val) 538c19ae6beSMike Looijmans break; 53967ac266dSLI Qingwu 54067ac266dSLI Qingwu if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl)) 54167ac266dSLI Qingwu data->chip_info = &bmi088_accel_chip_info_tbl[type]; 54267ac266dSLI Qingwu else 54367ac266dSLI Qingwu data->chip_info = &bmi088_accel_chip_info_tbl[i]; 54467ac266dSLI Qingwu 54567ac266dSLI Qingwu if (i != type) 54667ac266dSLI Qingwu dev_warn(dev, "unexpected chip id 0x%X\n", val); 547c19ae6beSMike Looijmans 548c19ae6beSMike Looijmans return 0; 549c19ae6beSMike Looijmans } 550c19ae6beSMike Looijmans 551c19ae6beSMike Looijmans int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, 55267ac266dSLI Qingwu int irq, enum bmi_device_type type) 553c19ae6beSMike Looijmans { 554c19ae6beSMike Looijmans struct bmi088_accel_data *data; 555c19ae6beSMike Looijmans struct iio_dev *indio_dev; 556c19ae6beSMike Looijmans int ret; 557c19ae6beSMike Looijmans 558c19ae6beSMike Looijmans indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 559c19ae6beSMike Looijmans if (!indio_dev) 560c19ae6beSMike Looijmans return -ENOMEM; 561c19ae6beSMike Looijmans 562c19ae6beSMike Looijmans data = iio_priv(indio_dev); 563c19ae6beSMike Looijmans dev_set_drvdata(dev, indio_dev); 564c19ae6beSMike Looijmans 565c19ae6beSMike Looijmans data->regmap = regmap; 566c19ae6beSMike Looijmans 56767ac266dSLI Qingwu ret = bmi088_accel_chip_init(data, type); 568c19ae6beSMike Looijmans if (ret) 569c19ae6beSMike Looijmans return ret; 570c19ae6beSMike Looijmans 571c19ae6beSMike Looijmans indio_dev->channels = data->chip_info->channels; 572c19ae6beSMike Looijmans indio_dev->num_channels = data->chip_info->num_channels; 57367ac266dSLI Qingwu indio_dev->name = data->chip_info->name; 574c19ae6beSMike Looijmans indio_dev->available_scan_masks = bmi088_accel_scan_masks; 575c19ae6beSMike Looijmans indio_dev->modes = INDIO_DIRECT_MODE; 576c19ae6beSMike Looijmans indio_dev->info = &bmi088_accel_info; 577c19ae6beSMike Looijmans 578c19ae6beSMike Looijmans /* Enable runtime PM */ 579c19ae6beSMike Looijmans pm_runtime_get_noresume(dev); 580c19ae6beSMike Looijmans pm_runtime_set_suspended(dev); 581c19ae6beSMike Looijmans pm_runtime_enable(dev); 582c19ae6beSMike Looijmans /* We need ~6ms to startup, so set the delay to 6 seconds */ 583c19ae6beSMike Looijmans pm_runtime_set_autosuspend_delay(dev, 6000); 584c19ae6beSMike Looijmans pm_runtime_use_autosuspend(dev); 585c19ae6beSMike Looijmans pm_runtime_put(dev); 586c19ae6beSMike Looijmans 587c19ae6beSMike Looijmans ret = iio_device_register(indio_dev); 588c19ae6beSMike Looijmans if (ret) 589c19ae6beSMike Looijmans dev_err(dev, "Unable to register iio device\n"); 590c19ae6beSMike Looijmans 591c19ae6beSMike Looijmans return ret; 592c19ae6beSMike Looijmans } 5933bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_probe, IIO_BMI088); 594c19ae6beSMike Looijmans 595c19ae6beSMike Looijmans 596bcf9d61aSUwe Kleine-König void bmi088_accel_core_remove(struct device *dev) 597c19ae6beSMike Looijmans { 598c19ae6beSMike Looijmans struct iio_dev *indio_dev = dev_get_drvdata(dev); 599c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev); 600c19ae6beSMike Looijmans 601c19ae6beSMike Looijmans iio_device_unregister(indio_dev); 602c19ae6beSMike Looijmans 603c19ae6beSMike Looijmans pm_runtime_disable(dev); 604c19ae6beSMike Looijmans pm_runtime_set_suspended(dev); 605c19ae6beSMike Looijmans bmi088_accel_power_down(data); 606c19ae6beSMike Looijmans } 6073bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088); 608c19ae6beSMike Looijmans 609c19ae6beSMike Looijmans static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) 610c19ae6beSMike Looijmans { 611c19ae6beSMike Looijmans struct iio_dev *indio_dev = dev_get_drvdata(dev); 612c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev); 613c19ae6beSMike Looijmans 614c19ae6beSMike Looijmans return bmi088_accel_power_down(data); 615c19ae6beSMike Looijmans } 616c19ae6beSMike Looijmans 617c19ae6beSMike Looijmans static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev) 618c19ae6beSMike Looijmans { 619c19ae6beSMike Looijmans struct iio_dev *indio_dev = dev_get_drvdata(dev); 620c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev); 621c19ae6beSMike Looijmans 622c19ae6beSMike Looijmans return bmi088_accel_power_up(data); 623c19ae6beSMike Looijmans } 624c19ae6beSMike Looijmans 625c19ae6beSMike Looijmans const struct dev_pm_ops bmi088_accel_pm_ops = { 626c19ae6beSMike Looijmans SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 627c19ae6beSMike Looijmans pm_runtime_force_resume) 628c19ae6beSMike Looijmans SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend, 629c19ae6beSMike Looijmans bmi088_accel_runtime_resume, NULL) 630c19ae6beSMike Looijmans }; 6313bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088); 632c19ae6beSMike Looijmans 633c19ae6beSMike Looijmans MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>"); 634c19ae6beSMike Looijmans MODULE_LICENSE("GPL v2"); 635c19ae6beSMike Looijmans MODULE_DESCRIPTION("BMI088 accelerometer driver (core)"); 636