diff options
Diffstat (limited to 'drivers/iio/accel')
-rw-r--r-- | drivers/iio/accel/Kconfig | 20 | ||||
-rw-r--r-- | drivers/iio/accel/Makefile | 2 | ||||
-rw-r--r-- | drivers/iio/accel/adis16201.c | 8 | ||||
-rw-r--r-- | drivers/iio/accel/adis16209.c | 8 | ||||
-rw-r--r-- | drivers/iio/accel/bma180.c | 225 | ||||
-rw-r--r-- | drivers/iio/accel/bma400.h | 99 | ||||
-rw-r--r-- | drivers/iio/accel/bma400_core.c | 853 | ||||
-rw-r--r-- | drivers/iio/accel/bma400_i2c.c | 61 | ||||
-rw-r--r-- | drivers/iio/accel/cros_ec_accel_legacy.c | 1 | ||||
-rw-r--r-- | drivers/iio/accel/kxcjk-1013.c | 27 | ||||
-rw-r--r-- | drivers/iio/accel/st_accel.h | 2 | ||||
-rw-r--r-- | drivers/iio/accel/st_accel_i2c.c | 8 | ||||
-rw-r--r-- | drivers/iio/accel/st_accel_spi.c | 9 |
13 files changed, 1259 insertions, 64 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index d4ef35aeb579..5d91a6dda894 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -89,13 +89,13 @@ config ADXL372_I2C module will be called adxl372_i2c. config BMA180 - tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver" + tristate "Bosch BMA180/BMA25x 3-Axis Accelerometer Driver" depends on I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER help Say Y here if you want to build a driver for the Bosch BMA180 or - BMA250 triaxial acceleration sensor. + BMA25x triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called bma180. @@ -112,6 +112,22 @@ config BMA220 To compile this driver as a module, choose M here: the module will be called bma220_spi. +config BMA400 + tristate "Bosch BMA400 3-Axis Accelerometer Driver" + select REGMAP + select BMA400_I2C if I2C + help + Say Y here if you want to build a driver for the Bosch BMA400 + triaxial acceleration sensor. + + To compile this driver as a module, choose M here: the + module will be called bma400_core and you will also get + bma400_i2c if I2C is enabled. + +config BMA400_I2C + tristate + depends on BMA400 + config BMC150_ACCEL tristate "Bosch BMC150 Accelerometer Driver" select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 56bd0215e0d4..3a051cf37f40 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_BMA220) += bma220_spi.o +obj-$(CONFIG_BMA400) += bma400_core.o +obj-$(CONFIG_BMA400_I2C) += bma400_i2c.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index c4810c73b2a2..0f0f27a8184e 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -233,6 +233,12 @@ static const char * const adis16201_status_error_msgs[] = { [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", }; +static const struct adis_timeout adis16201_timeouts = { + .reset_ms = ADIS16201_STARTUP_DELAY_MS, + .sw_reset_ms = ADIS16201_STARTUP_DELAY_MS, + .self_test_ms = ADIS16201_STARTUP_DELAY_MS, +}; + static const struct adis_data adis16201_data = { .read_delay = 20, .msc_ctrl_reg = ADIS16201_MSC_CTRL_REG, @@ -241,7 +247,7 @@ static const struct adis_data adis16201_data = { .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, .self_test_no_autoclear = true, - .startup_delay = ADIS16201_STARTUP_DELAY_MS, + .timeouts = &adis16201_timeouts, .status_error_msgs = adis16201_status_error_msgs, .status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) | diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index 98d77af8a2b0..c6dbd2424e10 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -243,6 +243,12 @@ static const char * const adis16209_status_error_msgs[] = { [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", }; +static const struct adis_timeout adis16209_timeouts = { + .reset_ms = ADIS16209_STARTUP_DELAY_MS, + .self_test_ms = ADIS16209_STARTUP_DELAY_MS, + .sw_reset_ms = ADIS16209_STARTUP_DELAY_MS, +}; + static const struct adis_data adis16209_data = { .read_delay = 30, .msc_ctrl_reg = ADIS16209_MSC_CTRL_REG, @@ -251,7 +257,7 @@ static const struct adis_data adis16209_data = { .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, .self_test_no_autoclear = true, - .startup_delay = ADIS16209_STARTUP_DELAY_MS, + .timeouts = &adis16209_timeouts, .status_error_msgs = adis16209_status_error_msgs, .status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) | diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 1574e4604a4f..fcd91d5f05fd 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -9,6 +9,7 @@ * SPI is not supported by driver * BMA180: 7-bit I2C slave address 0x40 or 0x41 * BMA250: 7-bit I2C slave address 0x18 or 0x19 + * BMA254: 7-bit I2C slave address 0x18 or 0x19 */ #include <linux/module.h> @@ -18,6 +19,7 @@ #include <linux/of_device.h> #include <linux/of.h> #include <linux/bitops.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/iio/iio.h> @@ -33,17 +35,20 @@ enum chip_ids { BMA180, BMA250, + BMA254, }; struct bma180_data; struct bma180_part_info { + u8 chip_id; const struct iio_chan_spec *channels; unsigned int num_channels; const int *scale_table; unsigned int num_scales; const int *bw_table; unsigned int num_bw; + int center_temp; u8 int_reset_reg, int_reset_mask; u8 sleep_reg, sleep_mask; @@ -51,6 +56,7 @@ struct bma180_part_info { u8 scale_reg, scale_mask; u8 power_reg, power_mask, lowpower_val; u8 int_enable_reg, int_enable_mask; + u8 int_map_reg, int_enable_dataready_int1_mask; u8 softreset_reg; int (*chip_config)(struct bma180_data *data); @@ -89,6 +95,8 @@ struct bma180_part_info { #define BMA180_RESET_VAL 0xb6 #define BMA180_ID_REG_VAL 0x03 +#define BMA250_ID_REG_VAL 0x03 +#define BMA254_ID_REG_VAL 0xfa /* 250 decimal */ /* Chip power modes */ #define BMA180_LOW_POWER 0x03 @@ -109,7 +117,26 @@ struct bma180_part_info { #define BMA250_INT1_DATA_MASK BIT(0) #define BMA250_INT_RESET_MASK BIT(7) /* Reset pending interrupts */ +#define BMA254_RANGE_REG 0x0f +#define BMA254_BW_REG 0x10 +#define BMA254_POWER_REG 0x11 +#define BMA254_RESET_REG 0x14 +#define BMA254_INT_ENABLE_REG 0x17 +#define BMA254_INT_MAP_REG 0x1a +#define BMA254_INT_RESET_REG 0x21 + +#define BMA254_RANGE_MASK GENMASK(3, 0) /* Range of accel values */ +#define BMA254_BW_MASK GENMASK(4, 0) /* Accel bandwidth */ +#define BMA254_SUSPEND_MASK BIT(7) /* chip will sleep */ +#define BMA254_LOWPOWER_MASK BIT(6) +#define BMA254_DATA_INTEN_MASK BIT(4) +#define BMA254_INT2_DATA_MASK BIT(7) +#define BMA254_INT1_DATA_MASK BIT(0) +#define BMA254_INT_RESET_MASK BIT(7) /* Reset pending interrupts */ + struct bma180_data { + struct regulator *vdd_supply; + struct regulator *vddio_supply; struct i2c_client *client; struct iio_trigger *trig; const struct bma180_part_info *part_info; @@ -132,8 +159,8 @@ enum bma180_chan { static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */ static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 }; -static int bma250_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */ -static int bma250_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0, +static int bma25x_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */ +static int bma25x_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0, 0, 0, 306458 }; static int bma180_get_data_reg(struct bma180_data *data, enum bma180_chan chan) @@ -307,8 +334,11 @@ static int bma180_chip_init(struct bma180_data *data) if (ret < 0) return ret; - if (ret != BMA180_ID_REG_VAL) + if (ret != data->part_info->chip_id) { + dev_err(&data->client->dev, "wrong chip ID %d expected %d\n", + ret, data->part_info->chip_id); return -ENODEV; + } ret = bma180_soft_reset(data); if (ret) @@ -355,7 +385,7 @@ err: return ret; } -static int bma250_chip_config(struct bma180_data *data) +static int bma25x_chip_config(struct bma180_data *data) { int ret = bma180_chip_init(data); @@ -367,8 +397,12 @@ static int bma250_chip_config(struct bma180_data *data) ret = bma180_set_scale(data, 38344); /* 2 G */ if (ret) goto err; - ret = bma180_set_bits(data, BMA250_INT_MAP_REG, - BMA250_INT1_DATA_MASK, 1); + /* + * This enables dataready interrupt on the INT1 pin + * FIXME: support using the INT2 pin + */ + ret = bma180_set_bits(data, data->part_info->int_map_reg, + data->part_info->int_enable_dataready_int1_mask, 1); if (ret) goto err; @@ -394,7 +428,7 @@ err: dev_err(&data->client->dev, "failed to disable the chip\n"); } -static void bma250_chip_disable(struct bma180_data *data) +static void bma25x_chip_disable(struct bma180_data *data) { if (bma180_set_new_data_intr_state(data, false)) goto err; @@ -497,7 +531,7 @@ static int bma180_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - *val = 48; /* 0 LSB @ 24 degree C */ + *val = data->part_info->center_temp; return IIO_VAL_INT; default: return -EINVAL; @@ -627,34 +661,96 @@ static const struct iio_chan_spec bma250_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; +static const struct iio_chan_spec bma254_channels[] = { + BMA180_ACC_CHANNEL(X, 12), + BMA180_ACC_CHANNEL(Y, 12), + BMA180_ACC_CHANNEL(Z, 12), + BMA180_TEMP_CHANNEL, + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + static const struct bma180_part_info bma180_part_info[] = { [BMA180] = { - bma180_channels, ARRAY_SIZE(bma180_channels), - bma180_scale_table, ARRAY_SIZE(bma180_scale_table), - bma180_bw_table, ARRAY_SIZE(bma180_bw_table), - BMA180_CTRL_REG0, BMA180_RESET_INT, - BMA180_CTRL_REG0, BMA180_SLEEP, - BMA180_BW_TCS, BMA180_BW, - BMA180_OFFSET_LSB1, BMA180_RANGE, - BMA180_TCO_Z, BMA180_MODE_CONFIG, BMA180_LOW_POWER, - BMA180_CTRL_REG3, BMA180_NEW_DATA_INT, - BMA180_RESET, - bma180_chip_config, - bma180_chip_disable, + .chip_id = BMA180_ID_REG_VAL, + .channels = bma180_channels, + .num_channels = ARRAY_SIZE(bma180_channels), + .scale_table = bma180_scale_table, + .num_scales = ARRAY_SIZE(bma180_scale_table), + .bw_table = bma180_bw_table, + .num_bw = ARRAY_SIZE(bma180_bw_table), + .center_temp = 48, /* 0 LSB @ 24 degree C */ + .int_reset_reg = BMA180_CTRL_REG0, + .int_reset_mask = BMA180_RESET_INT, + .sleep_reg = BMA180_CTRL_REG0, + .sleep_mask = BMA180_SLEEP, + .bw_reg = BMA180_BW_TCS, + .bw_mask = BMA180_BW, + .scale_reg = BMA180_OFFSET_LSB1, + .scale_mask = BMA180_RANGE, + .power_reg = BMA180_TCO_Z, + .power_mask = BMA180_MODE_CONFIG, + .lowpower_val = BMA180_LOW_POWER, + .int_enable_reg = BMA180_CTRL_REG3, + .int_enable_mask = BMA180_NEW_DATA_INT, + .softreset_reg = BMA180_RESET, + .chip_config = bma180_chip_config, + .chip_disable = bma180_chip_disable, }, [BMA250] = { - bma250_channels, ARRAY_SIZE(bma250_channels), - bma250_scale_table, ARRAY_SIZE(bma250_scale_table), - bma250_bw_table, ARRAY_SIZE(bma250_bw_table), - BMA250_INT_RESET_REG, BMA250_INT_RESET_MASK, - BMA250_POWER_REG, BMA250_SUSPEND_MASK, - BMA250_BW_REG, BMA250_BW_MASK, - BMA250_RANGE_REG, BMA250_RANGE_MASK, - BMA250_POWER_REG, BMA250_LOWPOWER_MASK, 1, - BMA250_INT_ENABLE_REG, BMA250_DATA_INTEN_MASK, - BMA250_RESET_REG, - bma250_chip_config, - bma250_chip_disable, + .chip_id = BMA250_ID_REG_VAL, + .channels = bma250_channels, + .num_channels = ARRAY_SIZE(bma250_channels), + .scale_table = bma25x_scale_table, + .num_scales = ARRAY_SIZE(bma25x_scale_table), + .bw_table = bma25x_bw_table, + .num_bw = ARRAY_SIZE(bma25x_bw_table), + .center_temp = 48, /* 0 LSB @ 24 degree C */ + .int_reset_reg = BMA250_INT_RESET_REG, + .int_reset_mask = BMA250_INT_RESET_MASK, + .sleep_reg = BMA250_POWER_REG, + .sleep_mask = BMA250_SUSPEND_MASK, + .bw_reg = BMA250_BW_REG, + .bw_mask = BMA250_BW_MASK, + .scale_reg = BMA250_RANGE_REG, + .scale_mask = BMA250_RANGE_MASK, + .power_reg = BMA250_POWER_REG, + .power_mask = BMA250_LOWPOWER_MASK, + .lowpower_val = 1, + .int_enable_reg = BMA250_INT_ENABLE_REG, + .int_enable_mask = BMA250_DATA_INTEN_MASK, + .int_map_reg = BMA250_INT_MAP_REG, + .int_enable_dataready_int1_mask = BMA250_INT1_DATA_MASK, + .softreset_reg = BMA250_RESET_REG, + .chip_config = bma25x_chip_config, + .chip_disable = bma25x_chip_disable, + }, + [BMA254] = { + .chip_id = BMA254_ID_REG_VAL, + .channels = bma254_channels, + .num_channels = ARRAY_SIZE(bma254_channels), + .scale_table = bma25x_scale_table, + .num_scales = ARRAY_SIZE(bma25x_scale_table), + .bw_table = bma25x_bw_table, + .num_bw = ARRAY_SIZE(bma25x_bw_table), + .center_temp = 46, /* 0 LSB @ 23 degree C */ + .int_reset_reg = BMA254_INT_RESET_REG, + .int_reset_mask = BMA254_INT_RESET_MASK, + .sleep_reg = BMA254_POWER_REG, + .sleep_mask = BMA254_SUSPEND_MASK, + .bw_reg = BMA254_BW_REG, + .bw_mask = BMA254_BW_MASK, + .scale_reg = BMA254_RANGE_REG, + .scale_mask = BMA254_RANGE_MASK, + .power_reg = BMA254_POWER_REG, + .power_mask = BMA254_LOWPOWER_MASK, + .lowpower_val = 1, + .int_enable_reg = BMA254_INT_ENABLE_REG, + .int_enable_mask = BMA254_DATA_INTEN_MASK, + .int_map_reg = BMA254_INT_MAP_REG, + .int_enable_dataready_int1_mask = BMA254_INT1_DATA_MASK, + .softreset_reg = BMA254_RESET_REG, + .chip_config = bma25x_chip_config, + .chip_disable = bma25x_chip_disable, }, }; @@ -712,12 +808,13 @@ static const struct iio_trigger_ops bma180_trigger_ops = { static int bma180_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct bma180_data *data; struct iio_dev *indio_dev; enum chip_ids chip; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -725,22 +822,56 @@ static int bma180_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; if (client->dev.of_node) - chip = (enum chip_ids)of_device_get_match_data(&client->dev); + chip = (enum chip_ids)of_device_get_match_data(dev); else chip = id->driver_data; data->part_info = &bma180_part_info[chip]; - ret = iio_read_mount_matrix(&client->dev, "mount-matrix", + ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation); if (ret) return ret; + data->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd_supply)) { + if (PTR_ERR(data->vdd_supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vdd regulator %d\n", + (int)PTR_ERR(data->vdd_supply)); + return PTR_ERR(data->vdd_supply); + } + data->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(data->vddio_supply)) { + if (PTR_ERR(data->vddio_supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vddio regulator %d\n", + (int)PTR_ERR(data->vddio_supply)); + return PTR_ERR(data->vddio_supply); + } + /* Typical voltage 2.4V these are min and max */ + ret = regulator_set_voltage(data->vdd_supply, 1620000, 3600000); + if (ret) + return ret; + ret = regulator_set_voltage(data->vddio_supply, 1200000, 3600000); + if (ret) + return ret; + ret = regulator_enable(data->vdd_supply); + if (ret) { + dev_err(dev, "Failed to enable vdd regulator: %d\n", ret); + return ret; + } + ret = regulator_enable(data->vddio_supply); + if (ret) { + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret); + goto err_disable_vdd; + } + /* Wait to make sure we started up properly (3 ms at least) */ + usleep_range(3000, 5000); + ret = data->part_info->chip_config(data); if (ret < 0) goto err_chip_disable; mutex_init(&data->mutex); - indio_dev->dev.parent = &client->dev; + indio_dev->dev.parent = dev; indio_dev->channels = data->part_info->channels; indio_dev->num_channels = data->part_info->num_channels; indio_dev->name = id->name; @@ -755,15 +886,15 @@ static int bma180_probe(struct i2c_client *client, goto err_chip_disable; } - ret = devm_request_irq(&client->dev, client->irq, + ret = devm_request_irq(dev, client->irq, iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING, "bma180_event", data->trig); if (ret) { - dev_err(&client->dev, "unable to request IRQ\n"); + dev_err(dev, "unable to request IRQ\n"); goto err_trigger_free; } - data->trig->dev.parent = &client->dev; + data->trig->dev.parent = dev; data->trig->ops = &bma180_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); indio_dev->trig = iio_trigger_get(data->trig); @@ -776,13 +907,13 @@ static int bma180_probe(struct i2c_client *client, ret = iio_triggered_buffer_setup(indio_dev, NULL, bma180_trigger_handler, NULL); if (ret < 0) { - dev_err(&client->dev, "unable to setup iio triggered buffer\n"); + dev_err(dev, "unable to setup iio triggered buffer\n"); goto err_trigger_unregister; } ret = iio_device_register(indio_dev); if (ret < 0) { - dev_err(&client->dev, "unable to register iio device\n"); + dev_err(dev, "unable to register iio device\n"); goto err_buffer_cleanup; } @@ -797,6 +928,9 @@ err_trigger_free: iio_trigger_free(data->trig); err_chip_disable: data->part_info->chip_disable(data); + regulator_disable(data->vddio_supply); +err_disable_vdd: + regulator_disable(data->vdd_supply); return ret; } @@ -816,6 +950,8 @@ static int bma180_remove(struct i2c_client *client) mutex_lock(&data->mutex); data->part_info->chip_disable(data); mutex_unlock(&data->mutex); + regulator_disable(data->vddio_supply); + regulator_disable(data->vdd_supply); return 0; } @@ -856,6 +992,7 @@ static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); static const struct i2c_device_id bma180_ids[] = { { "bma180", BMA180 }, { "bma250", BMA250 }, + { "bma254", BMA254 }, { } }; @@ -870,6 +1007,10 @@ static const struct of_device_id bma180_of_match[] = { .compatible = "bosch,bma250", .data = (void *)BMA250 }, + { + .compatible = "bosch,bma254", + .data = (void *)BMA254 + }, { } }; MODULE_DEVICE_TABLE(of, bma180_of_match); @@ -889,5 +1030,5 @@ module_i2c_driver(bma180_driver); MODULE_AUTHOR("Kravchenko Oleksandr <x0199363@ti.com>"); MODULE_AUTHOR("Texas Instruments, Inc."); -MODULE_DESCRIPTION("Bosch BMA180/BMA250 triaxial acceleration sensor"); +MODULE_DESCRIPTION("Bosch BMA180/BMA25x triaxial acceleration sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h new file mode 100644 index 000000000000..5ad10db9819f --- /dev/null +++ b/drivers/iio/accel/bma400.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Register constants and other forward declarations needed by the bma400 + * sources. + * + * Copyright 2019 Dan Robertson <dan@dlrobertson.com> + */ + +#ifndef _BMA400_H_ +#define _BMA400_H_ + +#include <linux/bits.h> +#include <linux/regmap.h> + +/* + * Read-Only Registers + */ + +/* Status and ID registers */ +#define BMA400_CHIP_ID_REG 0x00 +#define BMA400_ERR_REG 0x02 +#define BMA400_STATUS_REG 0x03 + +/* Acceleration registers */ +#define BMA400_X_AXIS_LSB_REG 0x04 +#define BMA400_X_AXIS_MSB_REG 0x05 +#define BMA400_Y_AXIS_LSB_REG 0x06 +#define BMA400_Y_AXIS_MSB_REG 0x07 +#define BMA400_Z_AXIS_LSB_REG 0x08 +#define BMA400_Z_AXIS_MSB_REG 0x09 + +/* Sensor time registers */ +#define BMA400_SENSOR_TIME0 0x0a +#define BMA400_SENSOR_TIME1 0x0b +#define BMA400_SENSOR_TIME2 0x0c + +/* Event and interrupt registers */ +#define BMA400_EVENT_REG 0x0d +#define BMA400_INT_STAT0_REG 0x0e +#define BMA400_INT_STAT1_REG 0x0f +#define BMA400_INT_STAT2_REG 0x10 + +/* Temperature register */ +#define BMA400_TEMP_DATA_REG 0x11 + +/* FIFO length and data registers */ +#define BMA400_FIFO_LENGTH0_REG 0x12 +#define BMA400_FIFO_LENGTH1_REG 0x13 +#define BMA400_FIFO_DATA_REG 0x14 + +/* Step count registers */ +#define BMA400_STEP_CNT0_REG 0x15 +#define BMA400_STEP_CNT1_REG 0x16 +#define BMA400_STEP_CNT3_REG 0x17 +#define BMA400_STEP_STAT_REG 0x18 + +/* + * Read-write configuration registers + */ +#define BMA400_ACC_CONFIG0_REG 0x19 +#define BMA400_ACC_CONFIG1_REG 0x1a +#define BMA400_ACC_CONFIG2_REG 0x1b +#define BMA400_CMD_REG 0x7e + +/* Chip ID of BMA 400 devices found in the chip ID register. */ +#define BMA400_ID_REG_VAL 0x90 + +#define BMA400_LP_OSR_SHIFT 5 +#define BMA400_NP_OSR_SHIFT 4 +#define BMA400_SCALE_SHIFT 6 + +#define BMA400_TWO_BITS_MASK GENMASK(1, 0) +#define BMA400_LP_OSR_MASK GENMASK(6, 5) +#define BMA400_NP_OSR_MASK GENMASK(5, 4) +#define BMA400_ACC_ODR_MASK GENMASK(3, 0) +#define BMA400_ACC_SCALE_MASK GENMASK(7, 6) + +#define BMA400_ACC_ODR_MIN_RAW 0x05 +#define BMA400_ACC_ODR_LP_RAW 0x06 +#define BMA400_ACC_ODR_MAX_RAW 0x0b + +#define BMA400_ACC_ODR_MAX_HZ 800 +#define BMA400_ACC_ODR_MIN_WHOLE_HZ 25 +#define BMA400_ACC_ODR_MIN_HZ 12 + +#define BMA400_SCALE_MIN 38357 +#define BMA400_SCALE_MAX 306864 + +#define BMA400_NUM_REGULATORS 2 +#define BMA400_VDD_REGULATOR 0 +#define BMA400_VDDIO_REGULATOR 1 + +extern const struct regmap_config bma400_regmap_config; + +int bma400_probe(struct device *dev, struct regmap *regmap, const char *name); + +int bma400_remove(struct device *dev); + +#endif diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c new file mode 100644 index 000000000000..cc77f89c048b --- /dev/null +++ b/drivers/iio/accel/bma400_core.c @@ -0,0 +1,853 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Core IIO driver for Bosch BMA400 triaxial acceleration sensor. + * + * Copyright 2019 Dan Robertson <dan@dlrobertson.com> + * + * TODO: + * - Support for power management + * - Support events and interrupts + * - Create channel for step count + * - Create channel for sensor time + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include "bma400.h" + +/* + * The G-range selection may be one of 2g, 4g, 8, or 16g. The scale may + * be selected with the acc_range bits of the ACC_CONFIG1 register. + * NB: This buffer is populated in the device init. + */ +static int bma400_scales[8]; + +/* + * See the ACC_CONFIG1 section of the datasheet. + * NB: This buffer is populated in the device init. + */ +static int bma400_sample_freqs[14]; + +static const int bma400_osr_range[] = { 0, 1, 3 }; + +/* See the ACC_CONFIG0 section of the datasheet */ +enum bma400_power_mode { + POWER_MODE_SLEEP = 0x00, + POWER_MODE_LOW = 0x01, + POWER_MODE_NORMAL = 0x02, + POWER_MODE_INVALID = 0x03, +}; + +struct bma400_sample_freq { + int hz; + int uhz; +}; + +struct bma400_data { + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data regulators[BMA400_NUM_REGULATORS]; + struct mutex mutex; /* data register lock */ + struct iio_mount_matrix orientation; + enum bma400_power_mode power_mode; + struct bma400_sample_freq sample_freq; + int oversampling_ratio; + int scale; +}; + +static bool bma400_is_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMA400_CHIP_ID_REG: + case BMA400_ERR_REG: + case BMA400_STATUS_REG: + case BMA400_X_AXIS_LSB_REG: + case BMA400_X_AXIS_MSB_REG: + case BMA400_Y_AXIS_LSB_REG: + case BMA400_Y_AXIS_MSB_REG: + case BMA400_Z_AXIS_LSB_REG: + case BMA400_Z_AXIS_MSB_REG: + case BMA400_SENSOR_TIME0: + case BMA400_SENSOR_TIME1: + case BMA400_SENSOR_TIME2: + case BMA400_EVENT_REG: + case BMA400_INT_STAT0_REG: + case BMA400_INT_STAT1_REG: + case BMA400_INT_STAT2_REG: + case BMA400_TEMP_DATA_REG: + case BMA400_FIFO_LENGTH0_REG: + case BMA400_FIFO_LENGTH1_REG: + case BMA400_FIFO_DATA_REG: + case BMA400_STEP_CNT0_REG: + case BMA400_STEP_CNT1_REG: + case BMA400_STEP_CNT3_REG: + case BMA400_STEP_STAT_REG: + return false; + default: + return true; + } +} + +static bool bma400_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMA400_ERR_REG: + case BMA400_STATUS_REG: + case BMA400_X_AXIS_LSB_REG: + case BMA400_X_AXIS_MSB_REG: + case BMA400_Y_AXIS_LSB_REG: + case BMA400_Y_AXIS_MSB_REG: + case BMA400_Z_AXIS_LSB_REG: + case BMA400_Z_AXIS_MSB_REG: + case BMA400_SENSOR_TIME0: + case BMA400_SENSOR_TIME1: + case BMA400_SENSOR_TIME2: + case BMA400_EVENT_REG: + case BMA400_INT_STAT0_REG: + case BMA400_INT_STAT1_REG: + case BMA400_INT_STAT2_REG: + case BMA400_TEMP_DATA_REG: + case BMA400_FIFO_LENGTH0_REG: + case BMA400_FIFO_LENGTH1_REG: + case BMA400_FIFO_DATA_REG: + case BMA400_STEP_CNT0_REG: + case BMA400_STEP_CNT1_REG: + case BMA400_STEP_CNT3_REG: + case BMA400_STEP_STAT_REG: + return true; + default: + return false; + } +} + +const struct regmap_config bma400_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BMA400_CMD_REG, + .cache_type = REGCACHE_RBTREE, + .writeable_reg = bma400_is_writable_reg, + .volatile_reg = bma400_is_volatile_reg, +}; +EXPORT_SYMBOL(bma400_regmap_config); + +static const struct iio_mount_matrix * +bma400_accel_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct bma400_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info bma400_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma400_accel_get_mount_matrix), + { } +}; + +#define BMA400_ACC_CHANNEL(_axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = bma400_ext_info, \ +} + +static const struct iio_chan_spec bma400_channels[] = { + BMA400_ACC_CHANNEL(X), + BMA400_ACC_CHANNEL(Y), + BMA400_ACC_CHANNEL(Z), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, +}; + +static int bma400_get_temp_reg(struct bma400_data *data, int *val, int *val2) +{ + unsigned int raw_temp; + int host_temp; + int ret; + + if (data->power_mode == POWER_MODE_SLEEP) + return -EBUSY; + + ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &raw_temp); + if (ret) + return ret; + + host_temp = sign_extend32(raw_temp, 7); + /* + * The formula for the TEMP_DATA register in the datasheet + * is: x * 0.5 + 23 + */ + *val = (host_temp >> 1) + 23; + *val2 = (host_temp & 0x1) * 500000; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int bma400_get_accel_reg(struct bma400_data *data, + const struct iio_chan_spec *chan, + int *val) +{ + __le16 raw_accel; + int lsb_reg; + int ret; + + if (data->power_mode == POWER_MODE_SLEEP) + return -EBUSY; + + switch (chan->channel2) { + case IIO_MOD_X: + lsb_reg = BMA400_X_AXIS_LSB_REG; + break; + case IIO_MOD_Y: + lsb_reg = BMA400_Y_AXIS_LSB_REG; + break; + case IIO_MOD_Z: + lsb_reg = BMA400_Z_AXIS_LSB_REG; + break; + default: + dev_err(data->dev, "invalid axis channel modifier\n"); + return -EINVAL; + } + + /* bulk read two registers, with the base being the LSB register */ + ret = regmap_bulk_read(data->regmap, lsb_reg, &raw_accel, + sizeof(raw_accel)); + if (ret) + return ret; + + *val = sign_extend32(le16_to_cpu(raw_accel), 11); + return IIO_VAL_INT; +} + +static void bma400_output_data_rate_from_raw(int raw, unsigned int *val, + unsigned int *val2) +{ + *val = BMA400_ACC_ODR_MAX_HZ >> (BMA400_ACC_ODR_MAX_RAW - raw); + if (raw > BMA400_ACC_ODR_MIN_RAW) + *val2 = 0; + else + *val2 = 500000; +} + +static int bma400_get_accel_output_data_rate(struct bma400_data *data) +{ + unsigned int val; + unsigned int odr; + int ret; + + switch (data->power_mode) { + case POWER_MODE_LOW: + /* + * Runs at a fixed rate in low-power mode. See section 4.3 + * in the datasheet. + */ + bma400_output_data_rate_from_raw(BMA400_ACC_ODR_LP_RAW, + &data->sample_freq.hz, + &data->sample_freq.uhz); + return 0; + case POWER_MODE_NORMAL: + /* + * In normal mode the ODR can be found in the ACC_CONFIG1 + * register. + */ + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) + goto error; + + odr = val & BMA400_ACC_ODR_MASK; + if (odr < BMA400_ACC_ODR_MIN_RAW || + odr > BMA400_ACC_ODR_MAX_RAW) { + ret = -EINVAL; + goto error; + } + + bma400_output_data_rate_from_raw(odr, &data->sample_freq.hz, + &data->sample_freq.uhz); + return 0; + case POWER_MODE_SLEEP: + data->sample_freq.hz = 0; + data->sample_freq.uhz = 0; + return 0; + default: + ret = 0; + goto error; + } +error: + data->sample_freq.hz = -1; + data->sample_freq.uhz = -1; + return ret; +} + +static int bma400_set_accel_output_data_rate(struct bma400_data *data, + int hz, int uhz) +{ + unsigned int idx; + unsigned int odr; + unsigned int val; + int ret; + + if (hz >= BMA400_ACC_ODR_MIN_WHOLE_HZ) { + if (uhz || hz > BMA400_ACC_ODR_MAX_HZ) + return -EINVAL; + + /* Note this works because MIN_WHOLE_HZ is odd */ + idx = __ffs(hz); + + if (hz >> idx != BMA400_ACC_ODR_MIN_WHOLE_HZ) + return -EINVAL; + + idx += BMA400_ACC_ODR_MIN_RAW + 1; + } else if (hz == BMA400_ACC_ODR_MIN_HZ && uhz == 500000) { + idx = BMA400_ACC_ODR_MIN_RAW; + } else { + return -EINVAL; + } + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) + return ret; + + /* preserve the range and normal mode osr */ + odr = (~BMA400_ACC_ODR_MASK & val) | idx; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG, odr); + if (ret) + return ret; + + bma400_output_data_rate_from_raw(idx, &data->sample_freq.hz, + &data->sample_freq.uhz); + return 0; +} + +static int bma400_get_accel_oversampling_ratio(struct bma400_data *data) +{ + unsigned int val; + unsigned int osr; + int ret; + + /* + * The oversampling ratio is stored in a different register + * based on the power-mode. In normal mode the OSR is stored + * in ACC_CONFIG1. In low-power mode it is stored in + * ACC_CONFIG0. + */ + switch (data->power_mode) { + case POWER_MODE_LOW: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, &val); + if (ret) { + data->oversampling_ratio = -1; + return ret; + } + + osr = (val & BMA400_LP_OSR_MASK) >> BMA400_LP_OSR_SHIFT; + + data->oversampling_ratio = osr; + return 0; + case POWER_MODE_NORMAL: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) { + data->oversampling_ratio = -1; + return ret; + } + + osr = (val & BMA400_NP_OSR_MASK) >> BMA400_NP_OSR_SHIFT; + + data->oversampling_ratio = osr; + return 0; + case POWER_MODE_SLEEP: + data->oversampling_ratio = 0; + return 0; + default: + data->oversampling_ratio = -1; + return -EINVAL; + } +} + +static int bma400_set_accel_oversampling_ratio(struct bma400_data *data, + int val) +{ + unsigned int acc_config; + int ret; + + if (val & ~BMA400_TWO_BITS_MASK) + return -EINVAL; + + /* + * The oversampling ratio is stored in a different register + * based on the power-mode. + */ + switch (data->power_mode) { + case POWER_MODE_LOW: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, + &acc_config); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG0_REG, + (acc_config & ~BMA400_LP_OSR_MASK) | + (val << BMA400_LP_OSR_SHIFT)); + if (ret) { + dev_err(data->dev, "Failed to write out OSR\n"); + return ret; + } + + data->oversampling_ratio = val; + return 0; + case POWER_MODE_NORMAL: + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, + &acc_config); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG, + (acc_config & ~BMA400_NP_OSR_MASK) | + (val << BMA400_NP_OSR_SHIFT)); + if (ret) { + dev_err(data->dev, "Failed to write out OSR\n"); + return ret; + } + + data->oversampling_ratio = val; + return 0; + default: + return -EINVAL; + } + return ret; +} + +static int bma400_accel_scale_to_raw(struct bma400_data *data, + unsigned int val) +{ + int raw; + + if (val == 0) + return -EINVAL; + + /* Note this works because BMA400_SCALE_MIN is odd */ + raw = __ffs(val); + + if (val >> raw != BMA400_SCALE_MIN) + return -EINVAL; + + return raw; +} + +static int bma400_get_accel_scale(struct bma400_data *data) +{ + unsigned int raw_scale; + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val); + if (ret) + return ret; + + raw_scale = (val & BMA400_ACC_SCALE_MASK) >> BMA400_SCALE_SHIFT; + if (raw_scale > BMA400_TWO_BITS_MASK) + return -EINVAL; + + data->scale = BMA400_SCALE_MIN << raw_scale; + + return 0; +} + +static int bma400_set_accel_scale(struct bma400_data *data, unsigned int val) +{ + unsigned int acc_config; + int raw; + int ret; + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &acc_config); + if (ret) + return ret; + + raw = bma400_accel_scale_to_raw(data, val); + if (raw < 0) + return raw; + + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG, + (acc_config & ~BMA400_ACC_SCALE_MASK) | + (raw << BMA400_SCALE_SHIFT)); + if (ret) + return ret; + + data->scale = val; + return 0; +} + +static int bma400_get_power_mode(struct bma400_data *data) +{ + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, BMA400_STATUS_REG, &val); + if (ret) { + dev_err(data->dev, "Failed to read status register\n"); + return ret; + } + + data->power_mode = (val >> 1) & BMA400_TWO_BITS_MASK; + return 0; +} + +static int bma400_set_power_mode(struct bma400_data *data, + enum bma400_power_mode mode) +{ + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, &val); + if (ret) + return ret; + + if (data->power_mode == mode) + return 0; + + if (mode == POWER_MODE_INVALID) + return -EINVAL; + + /* Preserve the low-power oversample ratio etc */ + ret = regmap_write(data->regmap, BMA400_ACC_CONFIG0_REG, + mode | (val & ~BMA400_TWO_BITS_MASK)); + if (ret) { + dev_err(data->dev, "Failed to write to power-mode\n"); + return ret; + } + + data->power_mode = mode; + + /* + * Update our cached osr and odr based on the new + * power-mode. + */ + bma400_get_accel_output_data_rate(data); + bma400_get_accel_oversampling_ratio(data); + return 0; +} + +static void bma400_init_tables(void) +{ + int raw; + int i; + + for (i = 0; i + 1 < ARRAY_SIZE(bma400_sample_freqs); i += 2) { + raw = (i / 2) + 5; + bma400_output_data_rate_from_raw(raw, &bma400_sample_freqs[i], + &bma400_sample_freqs[i + 1]); + } + + for (i = 0; i + 1 < ARRAY_SIZE(bma400_scales); i += 2) { + raw = i / 2; + bma400_scales[i] = 0; + bma400_scales[i + 1] = BMA400_SCALE_MIN << raw; + } +} + +static int bma400_init(struct bma400_data *data) +{ + unsigned int val; + int ret; + + /* Try to read chip_id register. It must return 0x90. */ + ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val); + if (ret) { + dev_err(data->dev, "Failed to read chip id register\n"); + goto out; + } + + if (val != BMA400_ID_REG_VAL) { + dev_err(data->dev, "Chip ID mismatch\n"); + ret = -ENODEV; + goto out; + } + + data->regulators[BMA400_VDD_REGULATOR].supply = "vdd"; + data->regulators[BMA400_VDDIO_REGULATOR].supply = "vddio"; + ret = devm_regulator_bulk_get(data->dev, + ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(data->dev, + "Failed to get regulators: %d\n", + ret); + + goto out; + } + ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) { + dev_err(data->dev, "Failed to enable regulators: %d\n", + ret); + goto out; + } + + ret = bma400_get_power_mode(data); + if (ret) { + dev_err(data->dev, "Failed to get the initial power-mode\n"); + goto err_reg_disable; + } + + if (data->power_mode != POWER_MODE_NORMAL) { + ret = bma400_set_power_mode(data, POWER_MODE_NORMAL); + if (ret) { + dev_err(data->dev, "Failed to wake up the device\n"); + goto err_reg_disable; + } + /* + * TODO: The datasheet waits 1500us here in the example, but + * lists 2/ODR as the wakeup time. + */ + usleep_range(1500, 2000); + } + + bma400_init_tables(); + + ret = bma400_get_accel_output_data_rate(data); + if (ret) + goto err_reg_disable; + + ret = bma400_get_accel_oversampling_ratio(data); + if (ret) + goto err_reg_disable; + + ret = bma400_get_accel_scale(data); + if (ret) + goto err_reg_disable; + + /* + * Once the interrupt engine is supported we might use the + * data_src_reg, but for now ensure this is set to the + * variable ODR filter selectable by the sample frequency + * channel. + */ + return regmap_write(data->regmap, BMA400_ACC_CONFIG2_REG, 0x00); + +err_reg_disable: + regulator_bulk_disable(ARRAY_SIZE(data->regulators), + data->regulators); +out: + return ret; +} + +static int bma400_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&data->mutex); + ret = bma400_get_temp_reg(data, val, val2); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->mutex); + ret = bma400_get_accel_reg(data, chan, val); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_ACCEL: + if (data->sample_freq.hz < 0) + return -EINVAL; + + *val = data->sample_freq.hz; + *val2 = data->sample_freq.uhz; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + /* + * Runs at a fixed sampling frequency. See Section 4.4 + * of the datasheet. + */ + *val = 6; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = data->scale; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + /* + * TODO: We could avoid this logic and returning -EINVAL here if + * we set both the low-power and normal mode OSR registers when + * we configure the device. + */ + if (data->oversampling_ratio < 0) + return -EINVAL; + + *val = data->oversampling_ratio; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int bma400_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = bma400_scales; + *length = ARRAY_SIZE(bma400_scales); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = bma400_osr_range; + *length = ARRAY_SIZE(bma400_osr_range); + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = bma400_sample_freqs; + *length = ARRAY_SIZE(bma400_sample_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int bma400_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * The sample frequency is readonly for the temperature + * register and a fixed value in low-power mode. + */ + if (chan->type != IIO_ACCEL) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = bma400_set_accel_output_data_rate(data, val, val2); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_SCALE: + if (val != 0 || + val2 < BMA400_SCALE_MIN || val2 > BMA400_SCALE_MAX) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = bma400_set_accel_scale(data, val2); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + mutex_lock(&data->mutex); + ret = bma400_set_accel_oversampling_ratio(data, val); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } +} + +static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info bma400_info = { + .read_raw = bma400_read_raw, + .read_avail = bma400_read_avail, + .write_raw = bma400_write_raw, + .write_raw_get_fmt = bma400_write_raw_get_fmt, +}; + +int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) +{ + struct iio_dev *indio_dev; + struct bma400_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->regmap = regmap; + data->dev = dev; + + ret = bma400_init(data); + if (ret) + return ret; + + ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation); + if (ret) + return ret; + + mutex_init(&data->mutex); + indio_dev->dev.parent = dev; + indio_dev->name = name; + indio_dev->info = &bma400_info; + indio_dev->channels = bma400_channels; + indio_dev->num_channels = ARRAY_SIZE(bma400_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + dev_set_drvdata(dev, indio_dev); + + return iio_device_register(indio_dev); +} +EXPORT_SYMBOL(bma400_probe); + +int bma400_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + ret = bma400_set_power_mode(data, POWER_MODE_SLEEP); + mutex_unlock(&data->mutex); + + regulator_bulk_disable(ARRAY_SIZE(data->regulators), + data->regulators); + + iio_device_unregister(indio_dev); + + return ret; +} +EXPORT_SYMBOL(bma400_remove); + +MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); +MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c new file mode 100644 index 000000000000..9dcb7cc9996e --- /dev/null +++ b/drivers/iio/accel/bma400_i2c.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C IIO driver for Bosch BMA400 triaxial acceleration sensor. + * + * Copyright 2019 Dan Robertson <dan@dlrobertson.com> + * + * I2C address is either 0x14 or 0x15 depending on SDO + */ +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "bma400.h" + +static int bma400_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &bma400_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "failed to create regmap\n"); + return PTR_ERR(regmap); + } + + return bma400_probe(&client->dev, regmap, id->name); +} + +static int bma400_i2c_remove(struct i2c_client *client) +{ + return bma400_remove(&client->dev); +} + +static const struct i2c_device_id bma400_i2c_ids[] = { + { "bma400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bma400_i2c_ids); + +static const struct of_device_id bma400_of_i2c_match[] = { + { .compatible = "bosch,bma400" }, + { } +}; +MODULE_DEVICE_TABLE(of, bma400_of_i2c_match); + +static struct i2c_driver bma400_i2c_driver = { + .driver = { + .name = "bma400", + .of_match_table = bma400_of_i2c_match, + }, + .probe = bma400_i2c_probe, + .remove = bma400_i2c_remove, + .id_table = bma400_i2c_ids, +}; + +module_i2c_driver(bma400_i2c_driver); + +MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); +MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (I2C)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c index 65f85faf6f31..68e847c6255e 100644 --- a/drivers/iio/accel/cros_ec_accel_legacy.c +++ b/drivers/iio/accel/cros_ec_accel_legacy.c @@ -18,7 +18,6 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> #include <linux/kernel.h> -#include <linux/mfd/cros_ec.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/platform_data/cros_ec_commands.h> diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index fee535d6e45b..c9924a65c32a 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -130,6 +130,7 @@ struct kxcjk1013_data { struct i2c_client *client; struct iio_trigger *dready_trig; struct iio_trigger *motion_trig; + struct iio_mount_matrix orientation; struct mutex mutex; s16 buffer[8]; u8 odr_bits; @@ -983,6 +984,20 @@ static const struct iio_event_spec kxcjk1013_event = { BIT(IIO_EV_INFO_PERIOD) }; +static const struct iio_mount_matrix * +kxcjk1013_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct kxcjk1013_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info kxcjk1013_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kxcjk1013_get_mount_matrix), + { } +}; + #define KXCJK1013_CHANNEL(_axis) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -999,6 +1014,7 @@ static const struct iio_event_spec kxcjk1013_event = { .endianness = IIO_LE, \ }, \ .event_spec = &kxcjk1013_event, \ + .ext_info = kxcjk1013_ext_info, \ .num_event_specs = 1 \ } @@ -1267,11 +1283,18 @@ static int kxcjk1013_probe(struct i2c_client *client, data->client = client; pdata = dev_get_platdata(&client->dev); - if (pdata) + if (pdata) { data->active_high_intr = pdata->active_high_intr; - else + data->orientation = pdata->orientation; + } else { data->active_high_intr = true; /* default polarity */ + ret = iio_read_mount_matrix(&client->dev, "mount-matrix", + &data->orientation); + if (ret) + return ret; + } + if (id) { data->chipset = (enum kx_chipset)(id->driver_data); name = id->name; diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index af09943f38c9..5b13e293cade 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -64,7 +64,7 @@ enum st_accel_type { * struct st_sensors_platform_data - default accel platform data * @drdy_int_pin: default accel DRDY is available on INT1 pin. */ -static const struct st_sensors_platform_data default_accel_pdata = { +static __maybe_unused const struct st_sensors_platform_data default_accel_pdata = { .drdy_int_pin = 1, }; diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 50fa0fc32baa..633955d764cc 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -18,7 +18,6 @@ #include <linux/iio/common/st_sensors_i2c.h> #include "st_accel.h" -#ifdef CONFIG_OF static const struct of_device_id st_accel_of_match[] = { { /* An older compatible */ @@ -108,9 +107,6 @@ static const struct of_device_id st_accel_of_match[] = { {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); -#else -#define st_accel_of_match NULL -#endif #ifdef CONFIG_ACPI static const struct acpi_device_id st_accel_acpi_match[] = { @@ -119,8 +115,6 @@ static const struct acpi_device_id st_accel_acpi_match[] = { { }, }; MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); -#else -#define st_accel_acpi_match NULL #endif static const struct i2c_device_id st_accel_id_table[] = { @@ -195,7 +189,7 @@ static int st_accel_i2c_remove(struct i2c_client *client) static struct i2c_driver st_accel_driver = { .driver = { .name = "st-accel-i2c", - .of_match_table = of_match_ptr(st_accel_of_match), + .of_match_table = st_accel_of_match, .acpi_match_table = ACPI_PTR(st_accel_acpi_match), }, .probe_new = st_accel_i2c_probe, diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 8af7027d5598..568ff1bae0ee 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -17,7 +17,6 @@ #include <linux/iio/common/st_sensors_spi.h> #include "st_accel.h" -#ifdef CONFIG_OF /* * For new single-chip sensors use <device_name> as compatible string. * For old single-chip devices keep <device_name>-accel to maintain @@ -96,9 +95,6 @@ static const struct of_device_id st_accel_of_match[] = { {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); -#else -#define st_accel_of_match NULL -#endif static int st_accel_spi_probe(struct spi_device *spi) { @@ -107,8 +103,7 @@ static int st_accel_spi_probe(struct spi_device *spi) struct iio_dev *indio_dev; int err; - st_sensors_of_name_probe(&spi->dev, st_accel_of_match, - spi->modalias, sizeof(spi->modalias)); + st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias)); settings = st_accel_get_settings(spi->modalias); if (!settings) { @@ -166,7 +161,7 @@ MODULE_DEVICE_TABLE(spi, st_accel_id_table); static struct spi_driver st_accel_driver = { .driver = { .name = "st-accel-spi", - .of_match_table = of_match_ptr(st_accel_of_match), + .of_match_table = st_accel_of_match, }, .probe = st_accel_spi_probe, .remove = st_accel_spi_remove, |