diff options
Diffstat (limited to 'drivers/iio/accel')
75 files changed, 11603 insertions, 1509 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 5d91a6dda894..ffac66db7ac9 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -30,6 +30,35 @@ config ADIS16209 To compile this driver as a module, say M here: the module will be called adis16209. +config ADXL313 + tristate + +config ADXL313_I2C + tristate "Analog Devices ADXL313 3-Axis Digital Accelerometer I2C Driver" + depends on I2C + select ADXL313 + select REGMAP_I2C + help + Say Y here if you want to build i2c support for the Analog Devices + ADXL313 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl313_i2c and you will also get adxl313_core + for the core module. + +config ADXL313_SPI + tristate "Analog Devices ADXL313 3-Axis Digital Accelerometer SPI Driver" + depends on SPI + select ADXL313 + select REGMAP_SPI + help + Say Y here if you want to build spi support for the Analog Devices + ADXL313 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl313_spi and you will also get adxl313_core + for the core module. + config ADXL345 tristate @@ -61,6 +90,66 @@ config ADXL345_SPI will be called adxl345_spi and you will also get adxl345_core for the core module. +config ADXL355 + tristate + +config ADXL355_I2C + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer I2C Driver" + depends on I2C + select ADXL355 + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build i2c support for the Analog Devices + ADXL355 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl355_i2c and you will also get adxl355_core + for the core module. + +config ADXL355_SPI + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer SPI Driver" + depends on SPI + select ADXL355 + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build spi support for the Analog Devices + ADXL355 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl355_spi and you will also get adxl355_core + for the core module. + +config ADXL367 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config ADXL367_SPI + tristate "Analog Devices ADXL367 3-Axis Accelerometer SPI Driver" + depends on SPI + select ADXL367 + select REGMAP_SPI + help + Say yes here to add support for the Analog Devices ADXL367 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl367_spi. + +config ADXL367_I2C + tristate "Analog Devices ADXL367 3-Axis Accelerometer I2C Driver" + depends on I2C + select ADXL367 + select REGMAP_I2C + help + Say yes here to add support for the Analog Devices ADXL367 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl367_i2c. + config ADXL372 tristate select IIO_BUFFER @@ -89,13 +178,13 @@ config ADXL372_I2C module will be called adxl372_i2c. config BMA180 - tristate "Bosch BMA180/BMA25x 3-Axis Accelerometer Driver" - depends on I2C + tristate "Bosch BMA023/BMA1x0/BMA250 3-Axis Accelerometer Driver" + depends on I2C && INPUT_BMA150=n select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say Y here if you want to build a driver for the Bosch BMA180 or - BMA25x triaxial acceleration sensor. + Say Y here if you want to build a driver for the Bosch BMA023, BMA150 + BMA180, BMA250 or SMB380 triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called bma180. @@ -115,19 +204,27 @@ config BMA220 config BMA400 tristate "Bosch BMA400 3-Axis Accelerometer Driver" select REGMAP + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER select BMA400_I2C if I2C + select BMA400_SPI if SPI 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. + bma400_i2c if I2C is enabled and bma400_spi if SPI is + enabled. config BMA400_I2C tristate depends on BMA400 +config BMA400_SPI + tristate + depends on BMA400 + config BMC150_ACCEL tristate "Bosch BMC150 Accelerometer Driver" select IIO_BUFFER @@ -137,9 +234,13 @@ config BMC150_ACCEL select BMC150_ACCEL_SPI if SPI help Say yes here to build support for the following Bosch accelerometers: - BMC150, BMI055, BMA250E, BMA222E, BMA255, BMA280. + BMA222, BMA222E, BMA250E, BMA253, BMA254, BMA255, BMA280, BMC150, BMC156 + BMI055. + + Note that some of these are combo modules: + - BMC150/BMC156: accelerometer and magnetometer + - BMI055: accelerometer and gyroscope - This is a combo module with both accelerometer and magnetometer. This driver is only implementing accelerometer part, which has its own address and register map. @@ -151,6 +252,24 @@ config BMC150_ACCEL_SPI tristate select REGMAP_SPI +config BMI088_ACCEL + tristate "Bosch BMI088 Accelerometer Driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP + select BMI088_ACCEL_SPI + help + Say yes here to build support for the Bosch BMI088 accelerometer. + + This is a combo module with both accelerometer and gyroscope. This + driver only implements the accelerometer part, which has its own + address and register map. BMG160 provides the gyroscope driver. + +config BMI088_ACCEL_SPI + tristate + select REGMAP_SPI + config DA280 tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver" depends on I2C @@ -173,7 +292,6 @@ config DA311 config DMARD06 tristate "Domintech DMARD06 Digital Accelerometer Driver" - depends on OF || COMPILE_TEST depends on I2C help Say yes here to build support for the Domintech low-g tri-axial @@ -202,10 +320,38 @@ config DMARD10 Choosing M will build the driver as a module. If so, the module will be called dmard10. +config FXLS8962AF + tristate + depends on I2C || !I2C # cannot be built-in for modular I2C + +config FXLS8962AF_I2C + tristate "NXP FXLS8962AF/FXLS8964AF Accelerometer I2C Driver" + depends on I2C + select FXLS8962AF + select REGMAP_I2C + help + Say yes here to build support for the NXP 3-axis automotive + accelerometer FXLS8962AF/FXLS8964AF with I2C support. + + To compile this driver as a module, choose M here: the module + will be called fxls8962af_i2c. + +config FXLS8962AF_SPI + tristate "NXP FXLS8962AF/FXLS8964AF Accelerometer SPI Driver" + depends on SPI + depends on I2C || !I2C + select FXLS8962AF + select REGMAP_SPI + help + Say yes here to build support for the NXP 3-axis automotive + accelerometer FXLS8962AF/FXLS8964AF with SPI support. + + To compile this driver as a module, choose M here: the module + will be called fxls8962af_spi. + config HID_SENSOR_ACCEL_3D depends on HID_SENSOR_HUB select IIO_BUFFER - select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON select HID_SENSOR_IIO_TRIGGER tristate "HID Accelerometers 3D" @@ -231,32 +377,37 @@ config IIO_ST_ACCEL_3AXIS depends on !SENSORS_LIS3_I2C depends on !SENSORS_LIS3_SPI select IIO_ST_SENSORS_CORE - select IIO_ST_ACCEL_I2C_3AXIS if (I2C) - select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics accelerometers: LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL, - LNG2DM, LIS3DE, LIS2DE12 - - This driver can also be built as a module. If so, these modules - will be created: - - st_accel (core functions for the driver [it is mandatory]); - - st_accel_i2c (necessary for the I2C devices [optional*]); - - st_accel_spi (necessary for the SPI devices [optional*]); + LNG2DM, LIS3DE, LIS2DE12, LIS2HH12 - (*) one of these is necessary to do something. + Also need to enable at least one of I2C and SPI interface drivers + below. config IIO_ST_ACCEL_I2C_3AXIS - tristate - depends on IIO_ST_ACCEL_3AXIS - depends on IIO_ST_SENSORS_I2C + tristate "STMicroelectronics accelerometers 3-Axis I2C Interface" + depends on I2C && IIO_ST_ACCEL_3AXIS + default I2C && IIO_ST_ACCEL_3AXIS + select IIO_ST_SENSORS_I2C + help + Build support for STMicroelectronics accelerometers I2C interface. + + To compile this driver as a module, choose M here. The module + will be called st_accel_i2c. config IIO_ST_ACCEL_SPI_3AXIS - tristate - depends on IIO_ST_ACCEL_3AXIS - depends on IIO_ST_SENSORS_SPI + tristate "STMicroelectronics accelerometers 3-Axis SPI Interface" + depends on SPI_MASTER && IIO_ST_ACCEL_3AXIS + default SPI_MASTER && IIO_ST_ACCEL_3AXIS + select IIO_ST_SENSORS_SPI + help + Build support for STMicroelectronics accelerometers SPI interface. + + To compile this driver as a module, choose M here. The module + will be called st_accel_spi. config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" @@ -390,6 +541,19 @@ config MMA9553 To compile this driver as a module, choose M here: the module will be called mma9553. +config MSA311 + tristate "MEMSensing Digital 3-Axis Accelerometer Driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + help + Say yes here to build support for the MEMSensing MSA311 + accelerometer driver. + + To compile this driver as a module, choose M here: the module will be + called msa311. + config MXC4005 tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver" depends on I2C @@ -426,6 +590,19 @@ config SCA3000 To compile this driver as a module, say M here: the module will be called sca3000. +config SCA3300 + tristate "Murata SCA3300 3-Axis Accelerometer Driver" + depends on SPI + select CRC8 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Murata SCA3300 3-Axis + accelerometer. + + To compile this driver as a module, choose M here: the module will be + called sca3300. + config STK8312 tristate "Sensortek STK8312 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 3a051cf37f40..5e45b5fa5ab5 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -6,9 +6,18 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ADIS16201) += adis16201.o obj-$(CONFIG_ADIS16209) += adis16209.o +obj-$(CONFIG_ADXL313) += adxl313_core.o +obj-$(CONFIG_ADXL313_I2C) += adxl313_i2c.o +obj-$(CONFIG_ADXL313_SPI) += adxl313_spi.o obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o +obj-$(CONFIG_ADXL355) += adxl355_core.o +obj-$(CONFIG_ADXL355_I2C) += adxl355_i2c.o +obj-$(CONFIG_ADXL355_SPI) += adxl355_spi.o +obj-$(CONFIG_ADXL367) += adxl367.o +obj-$(CONFIG_ADXL367_I2C) += adxl367_i2c.o +obj-$(CONFIG_ADXL367_SPI) += adxl367_spi.o obj-$(CONFIG_ADXL372) += adxl372.o obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o @@ -16,14 +25,20 @@ 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_BMA400_SPI) += bma400_spi.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 +obj-$(CONFIG_BMI088_ACCEL) += bmi088-accel-core.o +obj-$(CONFIG_BMI088_ACCEL_SPI) += bmi088-accel-spi.o obj-$(CONFIG_DA280) += da280.o obj-$(CONFIG_DA311) += da311.o obj-$(CONFIG_DMARD06) += dmard06.o obj-$(CONFIG_DMARD09) += dmard09.o obj-$(CONFIG_DMARD10) += dmard10.o +obj-$(CONFIG_FXLS8962AF) += fxls8962af-core.o +obj-$(CONFIG_FXLS8962AF_I2C) += fxls8962af-i2c.o +obj-$(CONFIG_FXLS8962AF_SPI) += fxls8962af-spi.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o @@ -43,10 +58,13 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o obj-$(CONFIG_MMA9551) += mma9551.o obj-$(CONFIG_MMA9553) += mma9553.o +obj-$(CONFIG_MSA311) += msa311.o + obj-$(CONFIG_MXC4005) += mxc4005.o obj-$(CONFIG_MXC6255) += mxc6255.o obj-$(CONFIG_SCA3000) += sca3000.o +obj-$(CONFIG_SCA3300) += sca3300.o obj-$(CONFIG_STK8312) += stk8312.o obj-$(CONFIG_STK8BA50) += stk8ba50.o diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 0f0f27a8184e..dfb8e2e5bdf5 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -8,10 +8,7 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mutex.h> -#include <linux/slab.h> #include <linux/spi/spi.h> -#include <linux/sysfs.h> #include <linux/iio/iio.h> #include <linux/iio/imu/adis.h> @@ -215,7 +212,7 @@ static const struct iio_chan_spec adis16201_channels[] = { ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), - ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, + ADIS_INCLI_CHAN(Y, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), IIO_CHAN_SOFT_TIMESTAMP(7) }; @@ -246,6 +243,7 @@ static const struct adis_data adis16201_data = { .diag_stat_reg = ADIS16201_DIAG_STAT_REG, .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16201_MSC_CTRL_REG, .self_test_no_autoclear = true, .timeouts = &adis16201_timeouts, @@ -267,10 +265,8 @@ static int adis16201_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); indio_dev->name = spi->dev.driver->name; - indio_dev->dev.parent = &spi->dev; indio_dev->info = &adis16201_info; indio_dev->channels = adis16201_channels; @@ -281,34 +277,15 @@ static int adis16201_probe(struct spi_device *spi) if (ret) return ret; - ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + ret = devm_adis_setup_buffer_and_trigger(st, indio_dev, NULL); if (ret) return ret; ret = adis_initial_startup(st); if (ret) - goto error_cleanup_buffer_trigger; - - ret = iio_device_register(indio_dev); - if (ret < 0) - goto error_cleanup_buffer_trigger; - - return 0; - -error_cleanup_buffer_trigger: - adis_cleanup_buffer_and_trigger(st, indio_dev); - return ret; -} - -static int adis16201_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct adis *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static struct spi_driver adis16201_driver = { @@ -316,7 +293,6 @@ static struct spi_driver adis16201_driver = { .name = "adis16201", }, .probe = adis16201_probe, - .remove = adis16201_remove, }; module_spi_driver(adis16201_driver); @@ -324,3 +300,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16201"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index c6dbd2424e10..5a9c6e2296f1 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -7,11 +7,8 @@ #include <linux/device.h> #include <linux/kernel.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/iio/iio.h> #include <linux/iio/imu/adis.h> @@ -256,6 +253,7 @@ static const struct adis_data adis16209_data = { .diag_stat_reg = ADIS16209_STAT_REG, .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16209_MSC_CTRL_REG, .self_test_no_autoclear = true, .timeouts = &adis16209_timeouts, @@ -278,10 +276,8 @@ static int adis16209_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); indio_dev->name = spi->dev.driver->name; - indio_dev->dev.parent = &spi->dev; indio_dev->info = &adis16209_info; indio_dev->channels = adis16209_channels; indio_dev->num_channels = ARRAY_SIZE(adis16209_channels); @@ -291,33 +287,15 @@ static int adis16209_probe(struct spi_device *spi) if (ret) return ret; - ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + ret = devm_adis_setup_buffer_and_trigger(st, indio_dev, NULL); if (ret) return ret; ret = adis_initial_startup(st); if (ret) - goto error_cleanup_buffer_trigger; - ret = iio_device_register(indio_dev); - if (ret) - goto error_cleanup_buffer_trigger; - - return 0; - -error_cleanup_buffer_trigger: - adis_cleanup_buffer_and_trigger(st, indio_dev); - return ret; -} - -static int adis16209_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct adis *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static struct spi_driver adis16209_driver = { @@ -325,7 +303,6 @@ static struct spi_driver adis16209_driver = { .name = "adis16209", }, .probe = adis16209_probe, - .remove = adis16209_remove, }; module_spi_driver(adis16209_driver); @@ -333,3 +310,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16209"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h new file mode 100644 index 000000000000..72f624af4686 --- /dev/null +++ b/drivers/iio/accel/adxl313.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + */ + +#ifndef _ADXL313_H_ +#define _ADXL313_H_ + +#include <linux/iio/iio.h> + +/* ADXL313 register definitions */ +#define ADXL313_REG_DEVID0 0x00 +#define ADXL313_REG_DEVID1 0x01 +#define ADXL313_REG_PARTID 0x02 +#define ADXL313_REG_XID 0x04 +#define ADXL313_REG_SOFT_RESET 0x18 +#define ADXL313_REG_OFS_AXIS(index) (0x1E + (index)) +#define ADXL313_REG_THRESH_ACT 0x24 +#define ADXL313_REG_ACT_INACT_CTL 0x27 +#define ADXL313_REG_BW_RATE 0x2C +#define ADXL313_REG_POWER_CTL 0x2D +#define ADXL313_REG_INT_MAP 0x2F +#define ADXL313_REG_DATA_FORMAT 0x31 +#define ADXL313_REG_DATA_AXIS(index) (0x32 + ((index) * 2)) +#define ADXL313_REG_FIFO_CTL 0x38 +#define ADXL313_REG_FIFO_STATUS 0x39 + +#define ADXL313_DEVID0 0xAD +#define ADXL313_DEVID0_ADXL312_314 0xE5 +#define ADXL313_DEVID1 0x1D +#define ADXL313_PARTID 0xCB +#define ADXL313_SOFT_RESET 0x52 + +#define ADXL313_RATE_MSK GENMASK(3, 0) +#define ADXL313_RATE_BASE 6 + +#define ADXL313_POWER_CTL_MSK GENMASK(3, 2) +#define ADXL313_MEASUREMENT_MODE BIT(3) + +#define ADXL313_RANGE_MSK GENMASK(1, 0) +#define ADXL313_RANGE_MAX 3 + +#define ADXL313_FULL_RES BIT(3) +#define ADXL313_SPI_3WIRE BIT(6) +#define ADXL313_I2C_DISABLE BIT(6) + +extern const struct regmap_access_table adxl312_readable_regs_table; +extern const struct regmap_access_table adxl313_readable_regs_table; +extern const struct regmap_access_table adxl314_readable_regs_table; + +extern const struct regmap_access_table adxl312_writable_regs_table; +extern const struct regmap_access_table adxl313_writable_regs_table; +extern const struct regmap_access_table adxl314_writable_regs_table; + +enum adxl313_device_type { + ADXL312, + ADXL313, + ADXL314, +}; + +struct adxl313_data { + struct regmap *regmap; + const struct adxl313_chip_info *chip_info; + struct mutex lock; /* lock to protect transf_buf */ + __le16 transf_buf __aligned(IIO_DMA_MINALIGN); +}; + +struct adxl313_chip_info { + const char *name; + enum adxl313_device_type type; + int scale_factor; + bool variable_range; + bool soft_reset; + int (*check_id)(struct device *dev, struct adxl313_data *data); +}; + +extern const struct adxl313_chip_info adxl31x_chip_info[]; + +int adxl313_core_probe(struct device *dev, + struct regmap *regmap, + const struct adxl313_chip_info *chip_info, + int (*setup)(struct device *, struct regmap *)); +#endif /* _ADXL313_H_ */ diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c new file mode 100644 index 000000000000..4de0a41bd679 --- /dev/null +++ b/drivers/iio/accel/adxl313_core.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl313.h" + +static const struct regmap_range adxl312_readable_reg_range[] = { + regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0), + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS), +}; + +static const struct regmap_range adxl313_readable_reg_range[] = { + regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID), + regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS), +}; + +const struct regmap_access_table adxl312_readable_regs_table = { + .yes_ranges = adxl312_readable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL313); + +const struct regmap_access_table adxl313_readable_regs_table = { + .yes_ranges = adxl313_readable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313); + +const struct regmap_access_table adxl314_readable_regs_table = { + .yes_ranges = adxl312_readable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL313); + +static int adxl312_check_id(struct device *dev, + struct adxl313_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID0_ADXL312_314) + dev_warn(dev, "Invalid manufacturer ID: %#02x\n", regval); + + return 0; +} + +static int adxl313_check_id(struct device *dev, + struct adxl313_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID0) + dev_warn(dev, "Invalid manufacturer ID: 0x%02x\n", regval); + + /* Check DEVID1 and PARTID */ + if (regval == ADXL313_DEVID0) { + ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID1) + dev_warn(dev, "Invalid mems ID: 0x%02x\n", regval); + + ret = regmap_read(data->regmap, ADXL313_REG_PARTID, ®val); + if (ret) + return ret; + + if (regval != ADXL313_PARTID) + dev_warn(dev, "Invalid device ID: 0x%02x\n", regval); + } + + return 0; +} + +const struct adxl313_chip_info adxl31x_chip_info[] = { + [ADXL312] = { + .name = "adxl312", + .type = ADXL312, + .scale_factor = 28425072, + .variable_range = true, + .soft_reset = false, + .check_id = &adxl312_check_id, + }, + [ADXL313] = { + .name = "adxl313", + .type = ADXL313, + .scale_factor = 9576806, + .variable_range = true, + .soft_reset = true, + .check_id = &adxl313_check_id, + }, + [ADXL314] = { + .name = "adxl314", + .type = ADXL314, + .scale_factor = 478858719, + .variable_range = false, + .soft_reset = false, + .check_id = &adxl312_check_id, + }, +}; +EXPORT_SYMBOL_NS_GPL(adxl31x_chip_info, IIO_ADXL313); + +static const struct regmap_range adxl312_writable_reg_range[] = { + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP), + regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT), + regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL), +}; + +static const struct regmap_range adxl313_writable_reg_range[] = { + regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP), + regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT), + regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL), +}; + +const struct regmap_access_table adxl312_writable_regs_table = { + .yes_ranges = adxl312_writable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL313); + +const struct regmap_access_table adxl313_writable_regs_table = { + .yes_ranges = adxl313_writable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313); + +const struct regmap_access_table adxl314_writable_regs_table = { + .yes_ranges = adxl312_writable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL313); + +static const int adxl313_odr_freqs[][2] = { + [0] = { 6, 250000 }, + [1] = { 12, 500000 }, + [2] = { 25, 0 }, + [3] = { 50, 0 }, + [4] = { 100, 0 }, + [5] = { 200, 0 }, + [6] = { 400, 0 }, + [7] = { 800, 0 }, + [8] = { 1600, 0 }, + [9] = { 3200, 0 }, +}; + +#define ADXL313_ACCEL_CHANNEL(index, axis) { \ + .type = IIO_ACCEL, \ + .address = index, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = { \ + .realbits = 13, \ + }, \ +} + +static const struct iio_chan_spec adxl313_channels[] = { + ADXL313_ACCEL_CHANNEL(0, X), + ADXL313_ACCEL_CHANNEL(1, Y), + ADXL313_ACCEL_CHANNEL(2, Z), +}; + +static int adxl313_set_odr(struct adxl313_data *data, + unsigned int freq1, unsigned int freq2) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adxl313_odr_freqs); i++) { + if (adxl313_odr_freqs[i][0] == freq1 && + adxl313_odr_freqs[i][1] == freq2) + break; + } + + if (i == ARRAY_SIZE(adxl313_odr_freqs)) + return -EINVAL; + + return regmap_update_bits(data->regmap, ADXL313_REG_BW_RATE, + ADXL313_RATE_MSK, + FIELD_PREP(ADXL313_RATE_MSK, ADXL313_RATE_BASE + i)); +} + +static int adxl313_read_axis(struct adxl313_data *data, + struct iio_chan_spec const *chan) +{ + int ret; + + mutex_lock(&data->lock); + + ret = regmap_bulk_read(data->regmap, + ADXL313_REG_DATA_AXIS(chan->address), + &data->transf_buf, sizeof(data->transf_buf)); + if (ret) + goto unlock_ret; + + ret = le16_to_cpu(data->transf_buf); + +unlock_ret: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl313_read_freq_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_SAMP_FREQ: + *vals = (const int *)adxl313_odr_freqs; + *length = ARRAY_SIZE(adxl313_odr_freqs) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int adxl313_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adxl313_data *data = iio_priv(indio_dev); + unsigned int regval; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = adxl313_read_axis(data, chan); + if (ret < 0) + return ret; + + *val = sign_extend32(ret, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + + *val2 = data->chip_info->scale_factor; + + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(data->regmap, + ADXL313_REG_OFS_AXIS(chan->address), ®val); + if (ret) + return ret; + + /* + * 8-bit resolution at minimum range, that is 4x accel data scale + * factor at full resolution + */ + *val = sign_extend32(regval, 7) * 4; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = regmap_read(data->regmap, ADXL313_REG_BW_RATE, ®val); + if (ret) + return ret; + + ret = FIELD_GET(ADXL313_RATE_MSK, regval) - ADXL313_RATE_BASE; + *val = adxl313_odr_freqs[ret][0]; + *val2 = adxl313_odr_freqs[ret][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl313_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adxl313_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + /* + * 8-bit resolution at minimum range, that is 4x accel data scale + * factor at full resolution + */ + if (clamp_val(val, -128 * 4, 127 * 4) != val) + return -EINVAL; + + return regmap_write(data->regmap, + ADXL313_REG_OFS_AXIS(chan->address), + val / 4); + case IIO_CHAN_INFO_SAMP_FREQ: + return adxl313_set_odr(data, val, val2); + default: + return -EINVAL; + } +} + +static const struct iio_info adxl313_info = { + .read_raw = adxl313_read_raw, + .write_raw = adxl313_write_raw, + .read_avail = adxl313_read_freq_avail, +}; + +static int adxl313_setup(struct device *dev, struct adxl313_data *data, + int (*setup)(struct device *, struct regmap *)) +{ + int ret; + + /* + * If sw reset available, ensures the device is in a consistent + * state after start up + */ + if (data->chip_info->soft_reset) { + ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET, + ADXL313_SOFT_RESET); + if (ret) + return ret; + } + + if (setup) { + ret = setup(dev, data->regmap); + if (ret) + return ret; + } + + ret = data->chip_info->check_id(dev, data); + if (ret) + return ret; + + /* Sets the range to maximum, full resolution, if applicable */ + if (data->chip_info->variable_range) { + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_RANGE_MSK, + FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_MAX)); + if (ret) + return ret; + + /* Enables full resolution */ + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_FULL_RES, ADXL313_FULL_RES); + if (ret) + return ret; + } + + /* Enables measurement mode */ + return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL, + ADXL313_POWER_CTL_MSK, + ADXL313_MEASUREMENT_MODE); +} + +/** + * adxl313_core_probe() - probe and setup for adxl313 accelerometer + * @dev: Driver model representation of the device + * @regmap: Register map of the device + * @chip_info: Structure containing device specific data + * @setup: Setup routine to be executed right before the standard device + * setup, can also be set to NULL if not required + * + * Return: 0 on success, negative errno on error cases + */ +int adxl313_core_probe(struct device *dev, + struct regmap *regmap, + const struct adxl313_chip_info *chip_info, + int (*setup)(struct device *, struct regmap *)) +{ + struct adxl313_data *data; + struct iio_dev *indio_dev; + 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->chip_info = chip_info; + + mutex_init(&data->lock); + + indio_dev->name = chip_info->name; + indio_dev->info = &adxl313_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adxl313_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl313_channels); + + ret = adxl313_setup(dev, data, setup); + if (ret) { + dev_err(dev, "ADXL313 setup failed\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313); + +MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c new file mode 100644 index 000000000000..99cc7fc29488 --- /dev/null +++ b/drivers/iio/accel/adxl313_i2c.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl313.h" + +static const struct regmap_config adxl31x_i2c_regmap_config[] = { + [ADXL312] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl312_readable_regs_table, + .wr_table = &adxl312_writable_regs_table, + .max_register = 0x39, + }, + [ADXL313] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl313_readable_regs_table, + .wr_table = &adxl313_writable_regs_table, + .max_register = 0x39, + }, + [ADXL314] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl314_readable_regs_table, + .wr_table = &adxl314_writable_regs_table, + .max_register = 0x39, + }, +}; + +static const struct i2c_device_id adxl313_i2c_id[] = { + { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, adxl313_i2c_id); + +static const struct of_device_id adxl313_of_match[] = { + { .compatible = "adi,adxl312", .data = &adxl31x_chip_info[ADXL312] }, + { .compatible = "adi,adxl313", .data = &adxl31x_chip_info[ADXL313] }, + { .compatible = "adi,adxl314", .data = &adxl31x_chip_info[ADXL314] }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl313_of_match); + +static int adxl313_i2c_probe(struct i2c_client *client) +{ + const struct adxl313_chip_info *chip_data; + struct regmap *regmap; + + /* + * Retrieves device specific data as a pointer to a + * adxl313_chip_info structure + */ + chip_data = device_get_match_data(&client->dev); + if (!chip_data) + chip_data = (const struct adxl313_chip_info *)i2c_match_id(adxl313_i2c_id, client)->driver_data; + + regmap = devm_regmap_init_i2c(client, + &adxl31x_i2c_regmap_config[chip_data->type]); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return adxl313_core_probe(&client->dev, regmap, chip_data, NULL); +} + +static struct i2c_driver adxl313_i2c_driver = { + .driver = { + .name = "adxl313_i2c", + .of_match_table = adxl313_of_match, + }, + .probe_new = adxl313_i2c_probe, + .id_table = adxl313_i2c_id, +}; + +module_i2c_driver(adxl313_i2c_driver); + +MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer I2C driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL313); diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c new file mode 100644 index 000000000000..b7cc15678a2b --- /dev/null +++ b/drivers/iio/accel/adxl313_spi.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/property.h> + +#include "adxl313.h" + +static const struct regmap_config adxl31x_spi_regmap_config[] = { + [ADXL312] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl312_readable_regs_table, + .wr_table = &adxl312_writable_regs_table, + .max_register = 0x39, + /* Setting bits 7 and 6 enables multiple-byte read */ + .read_flag_mask = BIT(7) | BIT(6), + }, + [ADXL313] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl313_readable_regs_table, + .wr_table = &adxl313_writable_regs_table, + .max_register = 0x39, + /* Setting bits 7 and 6 enables multiple-byte read */ + .read_flag_mask = BIT(7) | BIT(6), + }, + [ADXL314] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl314_readable_regs_table, + .wr_table = &adxl314_writable_regs_table, + .max_register = 0x39, + /* Setting bits 7 and 6 enables multiple-byte read */ + .read_flag_mask = BIT(7) | BIT(6), + }, +}; + +static int adxl313_spi_setup(struct device *dev, struct regmap *regmap) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + int ret; + + if (spi->mode & SPI_3WIRE) { + ret = regmap_write(regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_SPI_3WIRE); + if (ret) + return ret; + } + + return regmap_update_bits(regmap, ADXL313_REG_POWER_CTL, + ADXL313_I2C_DISABLE, ADXL313_I2C_DISABLE); +} + +static int adxl313_spi_probe(struct spi_device *spi) +{ + const struct adxl313_chip_info *chip_data; + struct regmap *regmap; + int ret; + + spi->mode |= SPI_MODE_3; + ret = spi_setup(spi); + if (ret) + return ret; + + /* + * Retrieves device specific data as a pointer to a + * adxl313_chip_info structure + */ + chip_data = device_get_match_data(&spi->dev); + if (!chip_data) + chip_data = (const struct adxl313_chip_info *)spi_get_device_id(spi)->driver_data; + + regmap = devm_regmap_init_spi(spi, + &adxl31x_spi_regmap_config[chip_data->type]); + + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return adxl313_core_probe(&spi->dev, regmap, + chip_data, &adxl313_spi_setup); +} + +static const struct spi_device_id adxl313_spi_id[] = { + { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL313] }, + { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL314] }, + { } +}; + +MODULE_DEVICE_TABLE(spi, adxl313_spi_id); + +static const struct of_device_id adxl313_of_match[] = { + { .compatible = "adi,adxl312", .data = &adxl31x_chip_info[ADXL312] }, + { .compatible = "adi,adxl313", .data = &adxl31x_chip_info[ADXL313] }, + { .compatible = "adi,adxl314", .data = &adxl31x_chip_info[ADXL314] }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl313_of_match); + +static struct spi_driver adxl313_spi_driver = { + .driver = { + .name = "adxl313_spi", + .of_match_table = adxl313_of_match, + }, + .probe = adxl313_spi_probe, + .id_table = adxl313_spi_id, +}; + +module_spi_driver(adxl313_spi_driver); + +MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL313); diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 384497776b67..d7e67cb08538 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -9,12 +9,10 @@ #define _ADXL345_H_ enum adxl345_device_type { - ADXL345, - ADXL375, + ADXL345 = 1, + ADXL375 = 2, }; -int adxl345_core_probe(struct device *dev, struct regmap *regmap, - enum adxl345_device_type type, const char *name); -int adxl345_core_remove(struct device *dev); +int adxl345_core_probe(struct device *dev, struct regmap *regmap); #endif /* _ADXL345_H_ */ diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 9c269799e6c1..1919e0089c11 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -4,11 +4,13 @@ * * Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com> * - * Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf */ #include <linux/module.h> +#include <linux/property.h> #include <linux/regmap.h> +#include <linux/units.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -31,7 +33,6 @@ #define ADXL345_BW_RATE GENMASK(3, 0) #define ADXL345_BASE_RATE_NANO_HZ 97656250LL -#define NHZ_PER_HZ 1000000000LL #define ADXL345_POWER_CTL_MEASURE BIT(3) #define ADXL345_POWER_CTL_STANDBY 0x00 @@ -138,7 +139,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ << (regval & ADXL345_BW_RATE); - *val = div_s64_rem(samp_freq_nhz, NHZ_PER_HZ, val2); + *val = div_s64_rem(samp_freq_nhz, NANOHZ_PER_HZ, val2); return IIO_VAL_INT_PLUS_NANO; } @@ -163,7 +164,8 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, ADXL345_REG_OFS_AXIS(chan->address), val / 4); case IIO_CHAN_INFO_SAMP_FREQ: - n = div_s64(val * NHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ); + n = div_s64(val * NANOHZ_PER_HZ + val2, + ADXL345_BASE_RATE_NANO_HZ); return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE, ADXL345_BW_RATE, @@ -194,7 +196,7 @@ static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( static struct attribute *adxl345_attrs[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, - NULL, + NULL }; static const struct attribute_group adxl345_attrs_group = { @@ -208,32 +210,50 @@ static const struct iio_info adxl345_info = { .write_raw_get_fmt = adxl345_write_raw_get_fmt, }; -int adxl345_core_probe(struct device *dev, struct regmap *regmap, - enum adxl345_device_type type, const char *name) +static int adxl345_powerup(void *regmap) { + return regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_MEASURE); +} + +static void adxl345_powerdown(void *regmap) +{ + regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY); +} + +int adxl345_core_probe(struct device *dev, struct regmap *regmap) +{ + enum adxl345_device_type type; struct adxl345_data *data; struct iio_dev *indio_dev; + const char *name; u32 regval; int ret; - ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val); - if (ret < 0) { - dev_err(dev, "Error reading device ID: %d\n", ret); - return ret; + type = (uintptr_t)device_get_match_data(dev); + switch (type) { + case ADXL345: + name = "adxl345"; + break; + case ADXL375: + name = "adxl375"; + break; + default: + return -EINVAL; } - if (regval != ADXL345_DEVID) { - dev_err(dev, "Invalid device ID: %x, expected %x\n", - regval, ADXL345_DEVID); - return -ENODEV; - } + ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val); + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading device ID\n"); + + if (regval != ADXL345_DEVID) + return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n", + regval, ADXL345_DEVID); indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); - dev_set_drvdata(dev, indio_dev); data->regmap = regmap; data->type = type; /* Enable full-resolution mode */ @@ -241,12 +261,9 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, data->data_range); - if (ret < 0) { - dev_err(dev, "Failed to set data range: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to set data range\n"); - indio_dev->dev.parent = dev; indio_dev->name = name; indio_dev->info = &adxl345_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -254,35 +271,17 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, indio_dev->num_channels = ARRAY_SIZE(adxl345_channels); /* Enable measurement mode */ - ret = regmap_write(data->regmap, ADXL345_REG_POWER_CTL, - ADXL345_POWER_CTL_MEASURE); - if (ret < 0) { - dev_err(dev, "Failed to enable measurement mode: %d\n", ret); - return ret; - } - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(dev, "iio_device_register failed: %d\n", ret); - regmap_write(data->regmap, ADXL345_REG_POWER_CTL, - ADXL345_POWER_CTL_STANDBY); - } + ret = adxl345_powerup(data->regmap); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable measurement mode\n"); - return ret; -} -EXPORT_SYMBOL_GPL(adxl345_core_probe); - -int adxl345_core_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct adxl345_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); + ret = devm_add_action_or_reset(dev, adxl345_powerdown, data->regmap); + if (ret < 0) + return ret; - return regmap_write(data->regmap, ADXL345_REG_POWER_CTL, - ADXL345_POWER_CTL_STANDBY); + return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(adxl345_core_remove); +EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, IIO_ADXL345); MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>"); MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver"); diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index 1561364ae296..098cd83f95b2 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -19,28 +19,15 @@ static const struct regmap_config adxl345_i2c_regmap_config = { .val_bits = 8, }; -static int adxl345_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adxl345_i2c_probe(struct i2c_client *client) { struct regmap *regmap; - if (!id) - return -ENODEV; - regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } - - return adxl345_core_probe(&client->dev, regmap, id->driver_data, - id->name); -} + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n"); -static int adxl345_i2c_remove(struct i2c_client *client) -{ - return adxl345_core_remove(&client->dev); + return adxl345_core_probe(&client->dev, regmap); } static const struct i2c_device_id adxl345_i2c_id[] = { @@ -48,29 +35,33 @@ static const struct i2c_device_id adxl345_i2c_id[] = { { "adxl375", ADXL375 }, { } }; - MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id); static const struct of_device_id adxl345_of_match[] = { - { .compatible = "adi,adxl345" }, - { .compatible = "adi,adxl375" }, - { }, + { .compatible = "adi,adxl345", .data = (const void *)ADXL345 }, + { .compatible = "adi,adxl375", .data = (const void *)ADXL375 }, + { } }; - MODULE_DEVICE_TABLE(of, adxl345_of_match); +static const struct acpi_device_id adxl345_acpi_match[] = { + { "ADS0345", ADXL345 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match); + static struct i2c_driver adxl345_i2c_driver = { .driver = { .name = "adxl345_i2c", .of_match_table = adxl345_of_match, + .acpi_match_table = adxl345_acpi_match, }, - .probe = adxl345_i2c_probe, - .remove = adxl345_i2c_remove, + .probe_new = adxl345_i2c_probe, .id_table = adxl345_i2c_id, }; - module_i2c_driver(adxl345_i2c_driver); MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>"); MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer I2C driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL345); diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index da4591c7ef23..aaade5808657 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -22,29 +22,18 @@ static const struct regmap_config adxl345_spi_regmap_config = { static int adxl345_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); struct regmap *regmap; /* Bail out if max_speed_hz exceeds 5 MHz */ - if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) { - dev_err(&spi->dev, "SPI CLK, %d Hz exceeds 5 MHz\n", - spi->max_speed_hz); - return -EINVAL; - } + if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) + return dev_err_probe(&spi->dev, -EINVAL, "SPI CLK, %d Hz exceeds 5 MHz\n", + spi->max_speed_hz); regmap = devm_regmap_init_spi(spi, &adxl345_spi_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n"); - return adxl345_core_probe(&spi->dev, regmap, id->driver_data, id->name); -} - -static int adxl345_spi_remove(struct spi_device *spi) -{ - return adxl345_core_remove(&spi->dev); + return adxl345_core_probe(&spi->dev, regmap); } static const struct spi_device_id adxl345_spi_id[] = { @@ -52,29 +41,33 @@ static const struct spi_device_id adxl345_spi_id[] = { { "adxl375", ADXL375 }, { } }; - MODULE_DEVICE_TABLE(spi, adxl345_spi_id); static const struct of_device_id adxl345_of_match[] = { - { .compatible = "adi,adxl345" }, - { .compatible = "adi,adxl375" }, - { }, + { .compatible = "adi,adxl345", .data = (const void *)ADXL345 }, + { .compatible = "adi,adxl375", .data = (const void *)ADXL375 }, + { } }; - MODULE_DEVICE_TABLE(of, adxl345_of_match); +static const struct acpi_device_id adxl345_acpi_match[] = { + { "ADS0345", ADXL345 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match); + static struct spi_driver adxl345_spi_driver = { .driver = { .name = "adxl345_spi", .of_match_table = adxl345_of_match, + .acpi_match_table = adxl345_acpi_match, }, .probe = adxl345_spi_probe, - .remove = adxl345_spi_remove, .id_table = adxl345_spi_id, }; - module_spi_driver(adxl345_spi_driver); MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>"); MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL345); diff --git a/drivers/iio/accel/adxl355.h b/drivers/iio/accel/adxl355.h new file mode 100644 index 000000000000..6dd49b13e4fd --- /dev/null +++ b/drivers/iio/accel/adxl355.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADXL355 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + */ + +#ifndef _ADXL355_H_ +#define _ADXL355_H_ + +#include <linux/regmap.h> + +struct device; + +extern const struct regmap_access_table adxl355_readable_regs_tbl; +extern const struct regmap_access_table adxl355_writeable_regs_tbl; + +int adxl355_core_probe(struct device *dev, struct regmap *regmap, + const char *name); + +#endif /* _ADXL355_H_ */ diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c new file mode 100644 index 000000000000..4bc648eac8b2 --- /dev/null +++ b/drivers/iio/accel/adxl355_core.c @@ -0,0 +1,761 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer IIO core driver + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/limits.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/units.h> + +#include <asm/unaligned.h> + +#include "adxl355.h" + +/* ADXL355 Register Definitions */ +#define ADXL355_DEVID_AD_REG 0x00 +#define ADXL355_DEVID_MST_REG 0x01 +#define ADXL355_PARTID_REG 0x02 +#define ADXL355_STATUS_REG 0x04 +#define ADXL355_FIFO_ENTRIES_REG 0x05 +#define ADXL355_TEMP2_REG 0x06 +#define ADXL355_XDATA3_REG 0x08 +#define ADXL355_YDATA3_REG 0x0B +#define ADXL355_ZDATA3_REG 0x0E +#define ADXL355_FIFO_DATA_REG 0x11 +#define ADXL355_OFFSET_X_H_REG 0x1E +#define ADXL355_OFFSET_Y_H_REG 0x20 +#define ADXL355_OFFSET_Z_H_REG 0x22 +#define ADXL355_ACT_EN_REG 0x24 +#define ADXL355_ACT_THRESH_H_REG 0x25 +#define ADXL355_ACT_THRESH_L_REG 0x26 +#define ADXL355_ACT_COUNT_REG 0x27 +#define ADXL355_FILTER_REG 0x28 +#define ADXL355_FILTER_ODR_MSK GENMASK(3, 0) +#define ADXL355_FILTER_HPF_MSK GENMASK(6, 4) +#define ADXL355_FIFO_SAMPLES_REG 0x29 +#define ADXL355_INT_MAP_REG 0x2A +#define ADXL355_SYNC_REG 0x2B +#define ADXL355_RANGE_REG 0x2C +#define ADXL355_POWER_CTL_REG 0x2D +#define ADXL355_POWER_CTL_MODE_MSK GENMASK(1, 0) +#define ADXL355_POWER_CTL_DRDY_MSK BIT(2) +#define ADXL355_SELF_TEST_REG 0x2E +#define ADXL355_RESET_REG 0x2F + +#define ADXL355_DEVID_AD_VAL 0xAD +#define ADXL355_DEVID_MST_VAL 0x1D +#define ADXL355_PARTID_VAL 0xED +#define ADXL355_RESET_CODE 0x52 + +static const struct regmap_range adxl355_read_reg_range[] = { + regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG), + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG), +}; + +const struct regmap_access_table adxl355_readable_regs_tbl = { + .yes_ranges = adxl355_read_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl355_readable_regs_tbl, IIO_ADXL355); + +static const struct regmap_range adxl355_write_reg_range[] = { + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG), +}; + +const struct regmap_access_table adxl355_writeable_regs_tbl = { + .yes_ranges = adxl355_write_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl355_writeable_regs_tbl, IIO_ADXL355); + +enum adxl355_op_mode { + ADXL355_MEASUREMENT, + ADXL355_STANDBY, + ADXL355_TEMP_OFF, +}; + +enum adxl355_odr { + ADXL355_ODR_4000HZ, + ADXL355_ODR_2000HZ, + ADXL355_ODR_1000HZ, + ADXL355_ODR_500HZ, + ADXL355_ODR_250HZ, + ADXL355_ODR_125HZ, + ADXL355_ODR_62_5HZ, + ADXL355_ODR_31_25HZ, + ADXL355_ODR_15_625HZ, + ADXL355_ODR_7_813HZ, + ADXL355_ODR_3_906HZ, +}; + +enum adxl355_hpf_3db { + ADXL355_HPF_OFF, + ADXL355_HPF_24_7, + ADXL355_HPF_6_2084, + ADXL355_HPF_1_5545, + ADXL355_HPF_0_3862, + ADXL355_HPF_0_0954, + ADXL355_HPF_0_0238, +}; + +static const int adxl355_odr_table[][2] = { + [0] = {4000, 0}, + [1] = {2000, 0}, + [2] = {1000, 0}, + [3] = {500, 0}, + [4] = {250, 0}, + [5] = {125, 0}, + [6] = {62, 500000}, + [7] = {31, 250000}, + [8] = {15, 625000}, + [9] = {7, 813000}, + [10] = {3, 906000}, +}; + +static const int adxl355_hpf_3db_multipliers[] = { + 0, + 247000, + 62084, + 15545, + 3862, + 954, + 238, +}; + +enum adxl355_chans { + chan_x, chan_y, chan_z, +}; + +struct adxl355_chan_info { + u8 data_reg; + u8 offset_reg; +}; + +static const struct adxl355_chan_info adxl355_chans[] = { + [chan_x] = { + .data_reg = ADXL355_XDATA3_REG, + .offset_reg = ADXL355_OFFSET_X_H_REG + }, + [chan_y] = { + .data_reg = ADXL355_YDATA3_REG, + .offset_reg = ADXL355_OFFSET_Y_H_REG + }, + [chan_z] = { + .data_reg = ADXL355_ZDATA3_REG, + .offset_reg = ADXL355_OFFSET_Z_H_REG + }, +}; + +struct adxl355_data { + struct regmap *regmap; + struct device *dev; + struct mutex lock; /* lock to protect op_mode */ + enum adxl355_op_mode op_mode; + enum adxl355_odr odr; + enum adxl355_hpf_3db hpf_3db; + int calibbias[3]; + int adxl355_hpf_3db_table[7][2]; + struct iio_trigger *dready_trig; + union { + u8 transf_buf[3]; + struct { + u8 buf[14]; + s64 ts; + } buffer; + } __aligned(IIO_DMA_MINALIGN); +}; + +static int adxl355_set_op_mode(struct adxl355_data *data, + enum adxl355_op_mode op_mode) +{ + int ret; + + if (data->op_mode == op_mode) + return 0; + + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_MODE_MSK, op_mode); + if (ret) + return ret; + + data->op_mode = op_mode; + + return ret; +} + +static int adxl355_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_DRDY_MSK, + FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, + state ? 0 : 1)); + mutex_unlock(&data->lock); + + return ret; +} + +static void adxl355_fill_3db_frequency_table(struct adxl355_data *data) +{ + u32 multiplier; + u64 div, rem; + u64 odr; + int i; + + odr = mul_u64_u32_shr(adxl355_odr_table[data->odr][0], MEGA, 0) + + adxl355_odr_table[data->odr][1]; + + for (i = 0; i < ARRAY_SIZE(adxl355_hpf_3db_multipliers); i++) { + multiplier = adxl355_hpf_3db_multipliers[i]; + div = div64_u64_rem(mul_u64_u32_shr(odr, multiplier, 0), + TERA * 100, &rem); + + data->adxl355_hpf_3db_table[i][0] = div; + data->adxl355_hpf_3db_table[i][1] = div_u64(rem, MEGA * 100); + } +} + +static int adxl355_setup(struct adxl355_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_DEVID_AD_VAL) { + dev_err(data->dev, "Invalid ADI ID 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL355_DEVID_MST_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_DEVID_MST_VAL) { + dev_err(data->dev, "Invalid MEMS ID 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL355_PARTID_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_PARTID_VAL) { + dev_err(data->dev, "Invalid DEV ID 0x%02x\n", regval); + return -ENODEV; + } + + /* + * Perform a software reset to make sure the device is in a consistent + * state after start-up. + */ + ret = regmap_write(data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_DRDY_MSK, + FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, 1)); + if (ret) + return ret; + + adxl355_fill_3db_frequency_table(data); + + return adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +} + +static int adxl355_get_temp_data(struct adxl355_data *data, u8 addr) +{ + return regmap_bulk_read(data->regmap, addr, data->transf_buf, 2); +} + +static int adxl355_read_axis(struct adxl355_data *data, u8 addr) +{ + int ret; + + ret = regmap_bulk_read(data->regmap, addr, data->transf_buf, + ARRAY_SIZE(data->transf_buf)); + if (ret) + return ret; + + return get_unaligned_be24(data->transf_buf); +} + +static int adxl355_find_match(const int (*freq_tbl)[2], const int n, + const int val, const int val2) +{ + int i; + + for (i = 0; i < n; i++) { + if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2) + return i; + } + + return -EINVAL; +} + +static int adxl355_set_odr(struct adxl355_data *data, + enum adxl355_odr odr) +{ + int ret; + + mutex_lock(&data->lock); + + if (data->odr == odr) { + mutex_unlock(&data->lock); + return 0; + } + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret) + goto err_unlock; + + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, + ADXL355_FILTER_ODR_MSK, + FIELD_PREP(ADXL355_FILTER_ODR_MSK, odr)); + if (ret) + goto err_set_opmode; + + data->odr = odr; + adxl355_fill_3db_frequency_table(data); + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_set_hpf_3db(struct adxl355_data *data, + enum adxl355_hpf_3db hpf) +{ + int ret; + + mutex_lock(&data->lock); + + if (data->hpf_3db == hpf) { + mutex_unlock(&data->lock); + return 0; + } + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret) + goto err_unlock; + + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, + ADXL355_FILTER_HPF_MSK, + FIELD_PREP(ADXL355_FILTER_HPF_MSK, hpf)); + if (ret) + goto err_set_opmode; + + data->hpf_3db = hpf; + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_set_calibbias(struct adxl355_data *data, + enum adxl355_chans chan, int calibbias) +{ + int ret; + + mutex_lock(&data->lock); + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret) + goto err_unlock; + + put_unaligned_be16(calibbias, data->transf_buf); + ret = regmap_bulk_write(data->regmap, + adxl355_chans[chan].offset_reg, + data->transf_buf, 2); + if (ret) + goto err_set_opmode; + + data->calibbias[chan] = calibbias; + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + ret = adxl355_get_temp_data(data, chan->address); + if (ret < 0) + return ret; + *val = get_unaligned_be16(data->transf_buf); + + return IIO_VAL_INT; + case IIO_ACCEL: + ret = adxl355_read_axis(data, adxl355_chans[ + chan->address].data_reg); + if (ret < 0) + return ret; + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + /* + * The datasheet defines an intercept of 1885 LSB at 25 degC + * and a slope of -9.05 LSB/C. The following formula can be used + * to find the temperature: + * Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow + * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE. + * Hence using some rearranging we get the scale as -110.497238 + * and offset as -2111.25. + */ + case IIO_TEMP: + *val = -110; + *val2 = 497238; + return IIO_VAL_INT_PLUS_MICRO; + /* + * At +/- 2g with 20-bit resolution, scale is given in datasheet + * as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2. + */ + case IIO_ACCEL: + *val = 0; + *val2 = 38245; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = -2111; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + *val = sign_extend32(data->calibbias[chan->address], 15); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adxl355_odr_table[data->odr][0]; + *val2 = adxl355_odr_table[data->odr][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *val = data->adxl355_hpf_3db_table[data->hpf_3db][0]; + *val2 = data->adxl355_hpf_3db_table[data->hpf_3db][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl355_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int odr_idx, hpf_idx, calibbias; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + odr_idx = adxl355_find_match(adxl355_odr_table, + ARRAY_SIZE(adxl355_odr_table), + val, val2); + if (odr_idx < 0) + return odr_idx; + + return adxl355_set_odr(data, odr_idx); + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + hpf_idx = adxl355_find_match(data->adxl355_hpf_3db_table, + ARRAY_SIZE(data->adxl355_hpf_3db_table), + val, val2); + if (hpf_idx < 0) + return hpf_idx; + + return adxl355_set_hpf_3db(data, hpf_idx); + case IIO_CHAN_INFO_CALIBBIAS: + calibbias = clamp_t(int, val, S16_MIN, S16_MAX); + + return adxl355_set_calibbias(data, chan->address, calibbias); + default: + return -EINVAL; + } +} + +static int adxl355_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)adxl355_odr_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(adxl355_odr_table) * 2; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *vals = (const int *)data->adxl355_hpf_3db_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(data->adxl355_hpf_3db_table) * 2; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const unsigned long adxl355_avail_scan_masks[] = { + GENMASK(3, 0), + 0 +}; + +static const struct iio_info adxl355_info = { + .read_raw = adxl355_read_raw, + .write_raw = adxl355_write_raw, + .read_avail = &adxl355_read_avail, +}; + +static const struct iio_trigger_ops adxl355_trigger_ops = { + .set_trigger_state = &adxl355_data_rdy_trigger_set_state, + .validate_device = &iio_trigger_validate_own_device, +}; + +static irqreturn_t adxl355_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + + /* + * data->buffer is used both for triggered buffer support + * and read/write_raw(), hence, it has to be zeroed here before usage. + */ + data->buffer.buf[0] = 0; + + /* + * The acceleration data is 24 bits and big endian. It has to be saved + * in 32 bits, hence, it is saved in the 2nd byte of the 4 byte buffer. + * The buf array is 14 bytes as it includes 3x4=12 bytes for + * accelaration data of x, y, and z axis. It also includes 2 bytes for + * temperature data. + */ + ret = regmap_bulk_read(data->regmap, ADXL355_XDATA3_REG, + &data->buffer.buf[1], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_YDATA3_REG, + &data->buffer.buf[5], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_ZDATA3_REG, + &data->buffer.buf[9], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_TEMP2_REG, + &data->buffer.buf[12], 2); + if (ret) + goto out_unlock_notify; + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + pf->timestamp); + +out_unlock_notify: + mutex_unlock(&data->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +#define ADXL355_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 20, \ + .storagebits = 32, \ + .shift = 4, \ + .endianness = IIO_BE, \ + } \ +} + +static const struct iio_chan_spec adxl355_channels[] = { + ADXL355_ACCEL_CHANNEL(0, chan_x, X), + ADXL355_ACCEL_CHANNEL(1, chan_y, Y), + ADXL355_ACCEL_CHANNEL(2, chan_z, Z), + { + .type = IIO_TEMP, + .address = ADXL355_TEMP2_REG, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 3, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + data->dready_trig = devm_iio_trigger_alloc(data->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->dready_trig) + return -ENOMEM; + + data->dready_trig->ops = &adxl355_trigger_ops; + iio_trigger_set_drvdata(data->dready_trig, indio_dev); + + ret = devm_request_irq(data->dev, irq, + &iio_trigger_generic_data_rdy_poll, + IRQF_ONESHOT, "adxl355_irq", data->dready_trig); + if (ret) + return dev_err_probe(data->dev, ret, "request irq %d failed\n", + irq); + + ret = devm_iio_trigger_register(data->dev, data->dready_trig); + if (ret) { + dev_err(data->dev, "iio trigger register failed\n"); + return ret; + } + + indio_dev->trig = iio_trigger_get(data->dready_trig); + + return 0; +} + +int adxl355_core_probe(struct device *dev, struct regmap *regmap, + const char *name) +{ + struct adxl355_data *data; + struct iio_dev *indio_dev; + int ret; + int irq; + + 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; + data->op_mode = ADXL355_STANDBY; + mutex_init(&data->lock); + + indio_dev->name = name; + indio_dev->info = &adxl355_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adxl355_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl355_channels); + indio_dev->available_scan_masks = adxl355_avail_scan_masks; + + ret = adxl355_setup(data); + if (ret) { + dev_err(dev, "ADXL355 setup failed\n"); + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &adxl355_trigger_handler, NULL); + if (ret) { + dev_err(dev, "iio triggered buffer setup failed\n"); + return ret; + } + + irq = fwnode_irq_get_byname(dev_fwnode(dev), "DRDY"); + if (irq > 0) { + ret = adxl355_probe_trigger(indio_dev, irq); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(adxl355_core_probe, IIO_ADXL355); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c new file mode 100644 index 000000000000..f67d57921c81 --- /dev/null +++ b/drivers/iio/accel/adxl355_i2c.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer I2C driver + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> + +#include "adxl355.h" + +static const struct regmap_config adxl355_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x2F, + .rd_table = &adxl355_readable_regs_tbl, + .wr_table = &adxl355_writeable_regs_tbl, +}; + +static int adxl355_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", + PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return adxl355_core_probe(&client->dev, regmap, client->name); +} + +static const struct i2c_device_id adxl355_i2c_id[] = { + { "adxl355", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adxl355_i2c_id); + +static const struct of_device_id adxl355_of_match[] = { + { .compatible = "adi,adxl355" }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl355_of_match); + +static struct i2c_driver adxl355_i2c_driver = { + .driver = { + .name = "adxl355_i2c", + .of_match_table = adxl355_of_match, + }, + .probe_new = adxl355_i2c_probe, + .id_table = adxl355_i2c_id, +}; +module_i2c_driver(adxl355_i2c_driver); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer I2C driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL355); diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c new file mode 100644 index 000000000000..5fe986ae03f6 --- /dev/null +++ b/drivers/iio/accel/adxl355_spi.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer SPI driver + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + */ + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "adxl355.h" + +static const struct regmap_config adxl355_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = BIT(0), + .max_register = 0x2F, + .rd_table = &adxl355_readable_regs_tbl, + .wr_table = &adxl355_writeable_regs_tbl, +}; + +static int adxl355_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &adxl355_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return adxl355_core_probe(&spi->dev, regmap, id->name); +} + +static const struct spi_device_id adxl355_spi_id[] = { + { "adxl355", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adxl355_spi_id); + +static const struct of_device_id adxl355_of_match[] = { + { .compatible = "adi,adxl355" }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl355_of_match); + +static struct spi_driver adxl355_spi_driver = { + .driver = { + .name = "adxl355_spi", + .of_match_table = adxl355_of_match, + }, + .probe = adxl355_spi_probe, + .id_table = adxl355_spi_id, +}; +module_spi_driver(adxl355_spi_driver); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer SPI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADXL355); diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c new file mode 100644 index 000000000000..7c7d78040793 --- /dev/null +++ b/drivers/iio/accel/adxl367.c @@ -0,0 +1,1572 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <asm/unaligned.h> + +#include "adxl367.h" + +#define ADXL367_REG_DEVID 0x00 +#define ADXL367_DEVID_AD 0xAD + +#define ADXL367_REG_STATUS 0x0B +#define ADXL367_STATUS_INACT_MASK BIT(5) +#define ADXL367_STATUS_ACT_MASK BIT(4) +#define ADXL367_STATUS_FIFO_FULL_MASK BIT(2) + +#define ADXL367_FIFO_ENT_H_MASK GENMASK(1, 0) + +#define ADXL367_REG_X_DATA_H 0x0E +#define ADXL367_REG_Y_DATA_H 0x10 +#define ADXL367_REG_Z_DATA_H 0x12 +#define ADXL367_REG_TEMP_DATA_H 0x14 +#define ADXL367_REG_EX_ADC_DATA_H 0x16 +#define ADXL367_DATA_MASK GENMASK(15, 2) + +#define ADXL367_TEMP_25C 165 +#define ADXL367_TEMP_PER_C 54 + +#define ADXL367_VOLTAGE_OFFSET 8192 +#define ADXL367_VOLTAGE_MAX_MV 1000 +#define ADXL367_VOLTAGE_MAX_RAW GENMASK(13, 0) + +#define ADXL367_REG_RESET 0x1F +#define ADXL367_RESET_CODE 0x52 + +#define ADXL367_REG_THRESH_ACT_H 0x20 +#define ADXL367_REG_THRESH_INACT_H 0x23 +#define ADXL367_THRESH_MAX GENMASK(12, 0) +#define ADXL367_THRESH_VAL_H_MASK GENMASK(12, 6) +#define ADXL367_THRESH_H_MASK GENMASK(6, 0) +#define ADXL367_THRESH_VAL_L_MASK GENMASK(5, 0) +#define ADXL367_THRESH_L_MASK GENMASK(7, 2) + +#define ADXL367_REG_TIME_ACT 0x22 +#define ADXL367_REG_TIME_INACT_H 0x25 +#define ADXL367_TIME_ACT_MAX GENMASK(7, 0) +#define ADXL367_TIME_INACT_MAX GENMASK(15, 0) +#define ADXL367_TIME_INACT_VAL_H_MASK GENMASK(15, 8) +#define ADXL367_TIME_INACT_H_MASK GENMASK(7, 0) +#define ADXL367_TIME_INACT_VAL_L_MASK GENMASK(7, 0) +#define ADXL367_TIME_INACT_L_MASK GENMASK(7, 0) + +#define ADXL367_REG_ACT_INACT_CTL 0x27 +#define ADXL367_ACT_EN_MASK GENMASK(1, 0) +#define ADXL367_ACT_LINKLOOP_MASK GENMASK(5, 4) + +#define ADXL367_REG_FIFO_CTL 0x28 +#define ADXL367_FIFO_CTL_FORMAT_MASK GENMASK(6, 3) +#define ADXL367_FIFO_CTL_MODE_MASK GENMASK(1, 0) + +#define ADXL367_REG_FIFO_SAMPLES 0x29 +#define ADXL367_FIFO_SIZE 512 +#define ADXL367_FIFO_MAX_WATERMARK 511 + +#define ADXL367_SAMPLES_VAL_H_MASK BIT(8) +#define ADXL367_SAMPLES_H_MASK BIT(2) +#define ADXL367_SAMPLES_VAL_L_MASK GENMASK(7, 0) +#define ADXL367_SAMPLES_L_MASK GENMASK(7, 0) + +#define ADXL367_REG_INT1_MAP 0x2A +#define ADXL367_INT_INACT_MASK BIT(5) +#define ADXL367_INT_ACT_MASK BIT(4) +#define ADXL367_INT_FIFO_WATERMARK_MASK BIT(2) + +#define ADXL367_REG_FILTER_CTL 0x2C +#define ADXL367_FILTER_CTL_RANGE_MASK GENMASK(7, 6) +#define ADXL367_2G_RANGE_1G 4095 +#define ADXL367_2G_RANGE_100MG 409 +#define ADXL367_FILTER_CTL_ODR_MASK GENMASK(2, 0) + +#define ADXL367_REG_POWER_CTL 0x2D +#define ADXL367_POWER_CTL_MODE_MASK GENMASK(1, 0) + +#define ADXL367_REG_ADC_CTL 0x3C +#define ADXL367_REG_TEMP_CTL 0x3D +#define ADXL367_ADC_EN_MASK BIT(0) + +enum adxl367_range { + ADXL367_2G_RANGE, + ADXL367_4G_RANGE, + ADXL367_8G_RANGE, +}; + +enum adxl367_fifo_mode { + ADXL367_FIFO_MODE_DISABLED = 0b00, + ADXL367_FIFO_MODE_STREAM = 0b10, +}; + +enum adxl367_fifo_format { + ADXL367_FIFO_FORMAT_XYZ, + ADXL367_FIFO_FORMAT_X, + ADXL367_FIFO_FORMAT_Y, + ADXL367_FIFO_FORMAT_Z, + ADXL367_FIFO_FORMAT_XYZT, + ADXL367_FIFO_FORMAT_XT, + ADXL367_FIFO_FORMAT_YT, + ADXL367_FIFO_FORMAT_ZT, + ADXL367_FIFO_FORMAT_XYZA, + ADXL367_FIFO_FORMAT_XA, + ADXL367_FIFO_FORMAT_YA, + ADXL367_FIFO_FORMAT_ZA, +}; + +enum adxl367_op_mode { + ADXL367_OP_STANDBY = 0b00, + ADXL367_OP_MEASURE = 0b10, +}; + +enum adxl367_act_proc_mode { + ADXL367_LOOPED = 0b11, +}; + +enum adxl367_act_en_mode { + ADXL367_ACT_DISABLED = 0b00, + ADCL367_ACT_REF_ENABLED = 0b11, +}; + +enum adxl367_activity_type { + ADXL367_ACTIVITY, + ADXL367_INACTIVITY, +}; + +enum adxl367_odr { + ADXL367_ODR_12P5HZ, + ADXL367_ODR_25HZ, + ADXL367_ODR_50HZ, + ADXL367_ODR_100HZ, + ADXL367_ODR_200HZ, + ADXL367_ODR_400HZ, +}; + +struct adxl367_state { + const struct adxl367_ops *ops; + void *context; + + struct device *dev; + struct regmap *regmap; + + struct regulator_bulk_data regulators[2]; + + /* + * Synchronize access to members of driver state, and ensure atomicity + * of consecutive regmap operations. + */ + struct mutex lock; + + enum adxl367_odr odr; + enum adxl367_range range; + + unsigned int act_threshold; + unsigned int act_time_ms; + unsigned int inact_threshold; + unsigned int inact_time_ms; + + unsigned int fifo_set_size; + unsigned int fifo_watermark; + + __be16 fifo_buf[ADXL367_FIFO_SIZE] __aligned(IIO_DMA_MINALIGN); + __be16 sample_buf; + u8 act_threshold_buf[2]; + u8 inact_time_buf[2]; + u8 status_buf[3]; +}; + +static const unsigned int adxl367_threshold_h_reg_tbl[] = { + [ADXL367_ACTIVITY] = ADXL367_REG_THRESH_ACT_H, + [ADXL367_INACTIVITY] = ADXL367_REG_THRESH_INACT_H, +}; + +static const unsigned int adxl367_act_en_shift_tbl[] = { + [ADXL367_ACTIVITY] = 0, + [ADXL367_INACTIVITY] = 2, +}; + +static const unsigned int adxl367_act_int_mask_tbl[] = { + [ADXL367_ACTIVITY] = ADXL367_INT_ACT_MASK, + [ADXL367_INACTIVITY] = ADXL367_INT_INACT_MASK, +}; + +static const int adxl367_samp_freq_tbl[][2] = { + [ADXL367_ODR_12P5HZ] = {12, 500000}, + [ADXL367_ODR_25HZ] = {25, 0}, + [ADXL367_ODR_50HZ] = {50, 0}, + [ADXL367_ODR_100HZ] = {100, 0}, + [ADXL367_ODR_200HZ] = {200, 0}, + [ADXL367_ODR_400HZ] = {400, 0}, +}; + +/* (g * 2) * 9.80665 * 1000000 / (2^14 - 1) */ +static const int adxl367_range_scale_tbl[][2] = { + [ADXL367_2G_RANGE] = {0, 2394347}, + [ADXL367_4G_RANGE] = {0, 4788695}, + [ADXL367_8G_RANGE] = {0, 9577391}, +}; + +static const int adxl367_range_scale_factor_tbl[] = { + [ADXL367_2G_RANGE] = 1, + [ADXL367_4G_RANGE] = 2, + [ADXL367_8G_RANGE] = 4, +}; + +enum { + ADXL367_X_CHANNEL_INDEX, + ADXL367_Y_CHANNEL_INDEX, + ADXL367_Z_CHANNEL_INDEX, + ADXL367_TEMP_CHANNEL_INDEX, + ADXL367_EX_ADC_CHANNEL_INDEX +}; + +#define ADXL367_X_CHANNEL_MASK BIT(ADXL367_X_CHANNEL_INDEX) +#define ADXL367_Y_CHANNEL_MASK BIT(ADXL367_Y_CHANNEL_INDEX) +#define ADXL367_Z_CHANNEL_MASK BIT(ADXL367_Z_CHANNEL_INDEX) +#define ADXL367_TEMP_CHANNEL_MASK BIT(ADXL367_TEMP_CHANNEL_INDEX) +#define ADXL367_EX_ADC_CHANNEL_MASK BIT(ADXL367_EX_ADC_CHANNEL_INDEX) + +static const enum adxl367_fifo_format adxl367_fifo_formats[] = { + ADXL367_FIFO_FORMAT_X, + ADXL367_FIFO_FORMAT_Y, + ADXL367_FIFO_FORMAT_Z, + ADXL367_FIFO_FORMAT_XT, + ADXL367_FIFO_FORMAT_YT, + ADXL367_FIFO_FORMAT_ZT, + ADXL367_FIFO_FORMAT_XA, + ADXL367_FIFO_FORMAT_YA, + ADXL367_FIFO_FORMAT_ZA, + ADXL367_FIFO_FORMAT_XYZ, + ADXL367_FIFO_FORMAT_XYZT, + ADXL367_FIFO_FORMAT_XYZA, +}; + +static const unsigned long adxl367_channel_masks[] = { + ADXL367_X_CHANNEL_MASK, + ADXL367_Y_CHANNEL_MASK, + ADXL367_Z_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, + ADXL367_Y_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, + ADXL367_Z_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, + ADXL367_Y_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, + ADXL367_Z_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK | + ADXL367_TEMP_CHANNEL_MASK, + ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK | + ADXL367_EX_ADC_CHANNEL_MASK, + 0, +}; + +static int adxl367_set_measure_en(struct adxl367_state *st, bool en) +{ + enum adxl367_op_mode op_mode = en ? ADXL367_OP_MEASURE + : ADXL367_OP_STANDBY; + int ret; + + ret = regmap_update_bits(st->regmap, ADXL367_REG_POWER_CTL, + ADXL367_POWER_CTL_MODE_MASK, + FIELD_PREP(ADXL367_POWER_CTL_MODE_MASK, + op_mode)); + if (ret) + return ret; + + /* + * Wait for acceleration output to settle after entering + * measure mode. + */ + if (en) + msleep(100); + + return 0; +} + +static void adxl367_scale_act_thresholds(struct adxl367_state *st, + enum adxl367_range old_range, + enum adxl367_range new_range) +{ + st->act_threshold = st->act_threshold + * adxl367_range_scale_factor_tbl[old_range] + / adxl367_range_scale_factor_tbl[new_range]; + st->inact_threshold = st->inact_threshold + * adxl367_range_scale_factor_tbl[old_range] + / adxl367_range_scale_factor_tbl[new_range]; +} + +static int _adxl367_set_act_threshold(struct adxl367_state *st, + enum adxl367_activity_type act, + unsigned int threshold) +{ + u8 reg = adxl367_threshold_h_reg_tbl[act]; + int ret; + + if (threshold > ADXL367_THRESH_MAX) + return -EINVAL; + + st->act_threshold_buf[0] = FIELD_PREP(ADXL367_THRESH_H_MASK, + FIELD_GET(ADXL367_THRESH_VAL_H_MASK, + threshold)); + st->act_threshold_buf[1] = FIELD_PREP(ADXL367_THRESH_L_MASK, + FIELD_GET(ADXL367_THRESH_VAL_L_MASK, + threshold)); + + ret = regmap_bulk_write(st->regmap, reg, st->act_threshold_buf, + sizeof(st->act_threshold_buf)); + if (ret) + return ret; + + if (act == ADXL367_ACTIVITY) + st->act_threshold = threshold; + else + st->inact_threshold = threshold; + + return 0; +} + +static int adxl367_set_act_threshold(struct adxl367_state *st, + enum adxl367_activity_type act, + unsigned int threshold) +{ + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = _adxl367_set_act_threshold(st, act, threshold); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int adxl367_set_act_proc_mode(struct adxl367_state *st, + enum adxl367_act_proc_mode mode) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL, + ADXL367_ACT_LINKLOOP_MASK, + FIELD_PREP(ADXL367_ACT_LINKLOOP_MASK, + mode)); +} + +static int adxl367_set_act_interrupt_en(struct adxl367_state *st, + enum adxl367_activity_type act, + bool en) +{ + unsigned int mask = adxl367_act_int_mask_tbl[act]; + + return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, + mask, en ? mask : 0); +} + +static int adxl367_get_act_interrupt_en(struct adxl367_state *st, + enum adxl367_activity_type act, + bool *en) +{ + unsigned int mask = adxl367_act_int_mask_tbl[act]; + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, ADXL367_REG_INT1_MAP, &val); + if (ret) + return ret; + + *en = !!(val & mask); + + return 0; +} + +static int adxl367_set_act_en(struct adxl367_state *st, + enum adxl367_activity_type act, + enum adxl367_act_en_mode en) +{ + unsigned int ctl_shift = adxl367_act_en_shift_tbl[act]; + + return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL, + ADXL367_ACT_EN_MASK << ctl_shift, + en << ctl_shift); +} + +static int adxl367_set_fifo_watermark_interrupt_en(struct adxl367_state *st, + bool en) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, + ADXL367_INT_FIFO_WATERMARK_MASK, + en ? ADXL367_INT_FIFO_WATERMARK_MASK : 0); +} + +static int adxl367_get_fifo_mode(struct adxl367_state *st, + enum adxl367_fifo_mode *fifo_mode) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, ADXL367_REG_FIFO_CTL, &val); + if (ret) + return ret; + + *fifo_mode = FIELD_GET(ADXL367_FIFO_CTL_MODE_MASK, val); + + return 0; +} + +static int adxl367_set_fifo_mode(struct adxl367_state *st, + enum adxl367_fifo_mode fifo_mode) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, + ADXL367_FIFO_CTL_MODE_MASK, + FIELD_PREP(ADXL367_FIFO_CTL_MODE_MASK, + fifo_mode)); +} + +static int adxl367_set_fifo_format(struct adxl367_state *st, + enum adxl367_fifo_format fifo_format) +{ + return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, + ADXL367_FIFO_CTL_FORMAT_MASK, + FIELD_PREP(ADXL367_FIFO_CTL_FORMAT_MASK, + fifo_format)); +} + +static int adxl367_set_fifo_watermark(struct adxl367_state *st, + unsigned int fifo_watermark) +{ + unsigned int fifo_samples = fifo_watermark * st->fifo_set_size; + unsigned int fifo_samples_h, fifo_samples_l; + int ret; + + if (fifo_samples > ADXL367_FIFO_MAX_WATERMARK) + fifo_samples = ADXL367_FIFO_MAX_WATERMARK; + + fifo_samples /= st->fifo_set_size; + + fifo_samples_h = FIELD_PREP(ADXL367_SAMPLES_H_MASK, + FIELD_GET(ADXL367_SAMPLES_VAL_H_MASK, + fifo_samples)); + fifo_samples_l = FIELD_PREP(ADXL367_SAMPLES_L_MASK, + FIELD_GET(ADXL367_SAMPLES_VAL_L_MASK, + fifo_samples)); + + ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, + ADXL367_SAMPLES_H_MASK, fifo_samples_h); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_SAMPLES, + ADXL367_SAMPLES_L_MASK, fifo_samples_l); + if (ret) + return ret; + + st->fifo_watermark = fifo_watermark; + + return 0; +} + +static int adxl367_set_range(struct iio_dev *indio_dev, + enum adxl367_range range) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, + ADXL367_FILTER_CTL_RANGE_MASK, + FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK, + range)); + if (ret) + goto out; + + adxl367_scale_act_thresholds(st, st->range, range); + + /* Activity thresholds depend on range */ + ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, + st->act_threshold); + if (ret) + goto out; + + ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, + st->inact_threshold); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + if (ret) + goto out; + + st->range = range; + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms) +{ + int freq_hz = adxl367_samp_freq_tbl[st->odr][0]; + int freq_microhz = adxl367_samp_freq_tbl[st->odr][1]; + /* Scale to decihertz to prevent precision loss in 12.5Hz case. */ + int freq_dhz = freq_hz * 10 + freq_microhz / 100000; + + return DIV_ROUND_CLOSEST(ms * freq_dhz, 10000); +} + +static int _adxl367_set_act_time_ms(struct adxl367_state *st, unsigned int ms) +{ + unsigned int val = adxl367_time_ms_to_samples(st, ms); + int ret; + + if (val > ADXL367_TIME_ACT_MAX) + val = ADXL367_TIME_ACT_MAX; + + ret = regmap_write(st->regmap, ADXL367_REG_TIME_ACT, val); + if (ret) + return ret; + + st->act_time_ms = ms; + + return 0; +} + +static int _adxl367_set_inact_time_ms(struct adxl367_state *st, unsigned int ms) +{ + unsigned int val = adxl367_time_ms_to_samples(st, ms); + int ret; + + if (val > ADXL367_TIME_INACT_MAX) + val = ADXL367_TIME_INACT_MAX; + + st->inact_time_buf[0] = FIELD_PREP(ADXL367_TIME_INACT_H_MASK, + FIELD_GET(ADXL367_TIME_INACT_VAL_H_MASK, + val)); + st->inact_time_buf[1] = FIELD_PREP(ADXL367_TIME_INACT_L_MASK, + FIELD_GET(ADXL367_TIME_INACT_VAL_L_MASK, + val)); + + ret = regmap_bulk_write(st->regmap, ADXL367_REG_TIME_INACT_H, + st->inact_time_buf, sizeof(st->inact_time_buf)); + if (ret) + return ret; + + st->inact_time_ms = ms; + + return 0; +} + +static int adxl367_set_act_time_ms(struct adxl367_state *st, + enum adxl367_activity_type act, + unsigned int ms) +{ + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + if (act == ADXL367_ACTIVITY) + ret = _adxl367_set_act_time_ms(st, ms); + else + ret = _adxl367_set_inact_time_ms(st, ms); + + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, + ADXL367_FILTER_CTL_ODR_MASK, + FIELD_PREP(ADXL367_FILTER_CTL_ODR_MASK, + odr)); + if (ret) + return ret; + + /* Activity timers depend on ODR */ + ret = _adxl367_set_act_time_ms(st, st->act_time_ms); + if (ret) + return ret; + + ret = _adxl367_set_inact_time_ms(st, st->inact_time_ms); + if (ret) + return ret; + + st->odr = odr; + + return 0; +} + +static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = _adxl367_set_odr(st, odr); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg, + bool en) +{ + return regmap_update_bits(st->regmap, reg, ADXL367_ADC_EN_MASK, + en ? ADXL367_ADC_EN_MASK : 0); +} + +static int adxl367_set_temp_adc_reg_en(struct adxl367_state *st, + unsigned int reg, bool en) +{ + int ret; + + switch (reg) { + case ADXL367_REG_TEMP_DATA_H: + ret = adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en); + break; + case ADXL367_REG_EX_ADC_DATA_H: + ret = adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en); + break; + default: + return 0; + } + + if (ret) + return ret; + + if (en) + msleep(100); + + return 0; +} + +static int adxl367_set_temp_adc_mask_en(struct adxl367_state *st, + const unsigned long *active_scan_mask, + bool en) +{ + if (*active_scan_mask & ADXL367_TEMP_CHANNEL_MASK) + return adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en); + else if (*active_scan_mask & ADXL367_EX_ADC_CHANNEL_MASK) + return adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en); + + return 0; +} + +static int adxl367_find_odr(struct adxl367_state *st, int val, int val2, + enum adxl367_odr *odr) +{ + size_t size = ARRAY_SIZE(adxl367_samp_freq_tbl); + int i; + + for (i = 0; i < size; i++) + if (val == adxl367_samp_freq_tbl[i][0] && + val2 == adxl367_samp_freq_tbl[i][1]) + break; + + if (i == size) + return -EINVAL; + + *odr = i; + + return 0; +} + +static int adxl367_find_range(struct adxl367_state *st, int val, int val2, + enum adxl367_range *range) +{ + size_t size = ARRAY_SIZE(adxl367_range_scale_tbl); + int i; + + for (i = 0; i < size; i++) + if (val == adxl367_range_scale_tbl[i][0] && + val2 == adxl367_range_scale_tbl[i][1]) + break; + + if (i == size) + return -EINVAL; + + *range = i; + + return 0; +} + +static int adxl367_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct adxl367_state *st = iio_priv(indio_dev); + u16 sample; + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_temp_adc_reg_en(st, chan->address, true); + if (ret) + goto out; + + ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf, + sizeof(st->sample_buf)); + if (ret) + goto out; + + sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf)); + *val = sign_extend32(sample, chan->scan_type.realbits - 1); + + ret = adxl367_set_temp_adc_reg_en(st, chan->address, false); + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret ?: IIO_VAL_INT; +} + +static int adxl367_get_status(struct adxl367_state *st, u8 *status, + u16 *fifo_entries) +{ + int ret; + + /* Read STATUS, FIFO_ENT_L and FIFO_ENT_H */ + ret = regmap_bulk_read(st->regmap, ADXL367_REG_STATUS, + st->status_buf, sizeof(st->status_buf)); + if (ret) + return ret; + + st->status_buf[2] &= ADXL367_FIFO_ENT_H_MASK; + + *status = st->status_buf[0]; + *fifo_entries = get_unaligned_le16(&st->status_buf[1]); + + return 0; +} + +static bool adxl367_push_event(struct iio_dev *indio_dev, u8 status) +{ + unsigned int ev_dir; + + if (FIELD_GET(ADXL367_STATUS_ACT_MASK, status)) + ev_dir = IIO_EV_DIR_RISING; + else if (FIELD_GET(ADXL367_STATUS_INACT_MASK, status)) + ev_dir = IIO_EV_DIR_FALLING; + else + return false; + + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_THRESH, ev_dir), + iio_get_time_ns(indio_dev)); + + return true; +} + +static bool adxl367_push_fifo_data(struct iio_dev *indio_dev, u8 status, + u16 fifo_entries) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + int i; + + if (!FIELD_GET(ADXL367_STATUS_FIFO_FULL_MASK, status)) + return false; + + fifo_entries -= fifo_entries % st->fifo_set_size; + + ret = st->ops->read_fifo(st->context, st->fifo_buf, fifo_entries); + if (ret) { + dev_err(st->dev, "Failed to read FIFO: %d\n", ret); + return true; + } + + for (i = 0; i < fifo_entries; i += st->fifo_set_size) + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + + return true; +} + +static irqreturn_t adxl367_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct adxl367_state *st = iio_priv(indio_dev); + u16 fifo_entries; + bool handled; + u8 status; + int ret; + + ret = adxl367_get_status(st, &status, &fifo_entries); + if (ret) + return IRQ_NONE; + + handled = adxl367_push_event(indio_dev, status); + handled |= adxl367_push_fifo_data(indio_dev, status, fifo_entries); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static int adxl367_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct adxl367_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static int adxl367_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adxl367_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + return adxl367_read_sample(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + mutex_lock(&st->lock); + *val = adxl367_range_scale_tbl[st->range][0]; + *val2 = adxl367_range_scale_tbl[st->range][1]; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + *val = 1000; + *val2 = ADXL367_TEMP_PER_C; + return IIO_VAL_FRACTIONAL; + case IIO_VOLTAGE: + *val = ADXL367_VOLTAGE_MAX_MV; + *val2 = ADXL367_VOLTAGE_MAX_RAW; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = 25 * ADXL367_TEMP_PER_C - ADXL367_TEMP_25C; + return IIO_VAL_INT; + case IIO_VOLTAGE: + *val = ADXL367_VOLTAGE_OFFSET; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&st->lock); + *val = adxl367_samp_freq_tbl[st->odr][0]; + *val2 = adxl367_samp_freq_tbl[st->odr][1]; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl367_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: { + enum adxl367_odr odr; + + ret = adxl367_find_odr(st, val, val2, &odr); + if (ret) + return ret; + + return adxl367_set_odr(indio_dev, odr); + } + case IIO_CHAN_INFO_SCALE: { + enum adxl367_range range; + + ret = adxl367_find_range(st, val, val2, &range); + if (ret) + return ret; + + return adxl367_set_range(indio_dev, range); + } + default: + return -EINVAL; + } +} + +static int adxl367_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int adxl367_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + + *vals = (int *)adxl367_range_scale_tbl; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(adxl367_range_scale_tbl) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)adxl367_samp_freq_tbl; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(adxl367_samp_freq_tbl) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int adxl367_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct adxl367_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: { + switch (dir) { + case IIO_EV_DIR_RISING: + mutex_lock(&st->lock); + *val = st->act_threshold; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + mutex_lock(&st->lock); + *val = st->inact_threshold; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + mutex_lock(&st->lock); + *val = st->act_time_ms; + mutex_unlock(&st->lock); + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_DIR_FALLING: + mutex_lock(&st->lock); + *val = st->inact_time_ms; + mutex_unlock(&st->lock); + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl367_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct adxl367_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0) + return -EINVAL; + + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl367_set_act_threshold(st, ADXL367_ACTIVITY, val); + case IIO_EV_DIR_FALLING: + return adxl367_set_act_threshold(st, ADXL367_INACTIVITY, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + if (val < 0) + return -EINVAL; + + val = val * 1000 + DIV_ROUND_UP(val2, 1000); + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl367_set_act_time_ms(st, ADXL367_ACTIVITY, val); + case IIO_EV_DIR_FALLING: + return adxl367_set_act_time_ms(st, ADXL367_INACTIVITY, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl367_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct adxl367_state *st = iio_priv(indio_dev); + bool en; + int ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = adxl367_get_act_interrupt_en(st, ADXL367_ACTIVITY, &en); + return ret ?: en; + case IIO_EV_DIR_FALLING: + ret = adxl367_get_act_interrupt_en(st, ADXL367_INACTIVITY, &en); + return ret ?: en; + default: + return -EINVAL; + } +} + +static int adxl367_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct adxl367_state *st = iio_priv(indio_dev); + enum adxl367_activity_type act; + int ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + act = ADXL367_ACTIVITY; + break; + case IIO_EV_DIR_FALLING: + act = ADXL367_INACTIVITY; + break; + default: + return -EINVAL; + } + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_act_interrupt_en(st, act, state); + if (ret) + goto out; + + ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED + : ADXL367_ACT_DISABLED); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static ssize_t adxl367_get_fifo_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev)); + enum adxl367_fifo_mode fifo_mode; + int ret; + + ret = adxl367_get_fifo_mode(st, &fifo_mode); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", fifo_mode != ADXL367_FIFO_MODE_DISABLED); +} + +static ssize_t adxl367_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int fifo_watermark; + + mutex_lock(&st->lock); + fifo_watermark = st->fifo_watermark; + mutex_unlock(&st->lock); + + return sysfs_emit(buf, "%d\n", fifo_watermark); +} + +static ssize_t hwfifo_watermark_min_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", "1"); +} + +static ssize_t hwfifo_watermark_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", __stringify(ADXL367_FIFO_MAX_WATERMARK)); +} + +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + adxl367_get_fifo_watermark, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + adxl367_get_fifo_enabled, NULL, 0); + +static const struct attribute *adxl367_fifo_attributes[] = { + &iio_dev_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static int adxl367_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + if (val > ADXL367_FIFO_MAX_WATERMARK) + return -EINVAL; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_watermark(st, val); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static bool adxl367_find_mask_fifo_format(const unsigned long *scan_mask, + enum adxl367_fifo_format *fifo_format) +{ + size_t size = ARRAY_SIZE(adxl367_fifo_formats); + int i; + + for (i = 0; i < size; i++) + if (*scan_mask == adxl367_channel_masks[i]) + break; + + if (i == size) + return false; + + *fifo_format = adxl367_fifo_formats[i]; + + return true; +} + +static int adxl367_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct adxl367_state *st = iio_priv(indio_dev); + enum adxl367_fifo_format fifo_format; + int ret; + + if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format)) + return -EINVAL; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_format(st, fifo_format); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + if (ret) + goto out; + + st->fifo_set_size = bitmap_weight(active_scan_mask, + indio_dev->masklength); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int adxl367_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, + true); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_watermark_interrupt_en(st, true); + if (ret) + goto out; + + ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int adxl367_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl367_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED); + if (ret) + goto out; + + ret = adxl367_set_fifo_watermark_interrupt_en(st, false); + if (ret) + goto out; + + ret = adxl367_set_measure_en(st, true); + if (ret) + goto out; + + ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, + false); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static const struct iio_buffer_setup_ops adxl367_buffer_ops = { + .postenable = adxl367_buffer_postenable, + .predisable = adxl367_buffer_predisable, +}; + +static const struct iio_info adxl367_info = { + .read_raw = adxl367_read_raw, + .write_raw = adxl367_write_raw, + .write_raw_get_fmt = adxl367_write_raw_get_fmt, + .read_avail = adxl367_read_avail, + .read_event_config = adxl367_read_event_config, + .write_event_config = adxl367_write_event_config, + .read_event_value = adxl367_read_event_value, + .write_event_value = adxl367_write_event_value, + .debugfs_reg_access = adxl367_reg_access, + .hwfifo_set_watermark = adxl367_set_watermark, + .update_scan_mode = adxl367_update_scan_mode, +}; + +static const struct iio_event_spec adxl367_events[] = { + { + .type = IIO_EV_TYPE_MAG_REFERENCED, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_MAG_REFERENCED, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_VALUE), + }, +}; + +#define ADXL367_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = (reg), \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .event_spec = adxl367_events, \ + .num_event_specs = ARRAY_SIZE(adxl367_events), \ + .scan_index = (index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 14, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADXL367_CHANNEL(index, reg, _type) { \ + .type = (_type), \ + .address = (reg), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 14, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec adxl367_channels[] = { + ADXL367_ACCEL_CHANNEL(ADXL367_X_CHANNEL_INDEX, ADXL367_REG_X_DATA_H, X), + ADXL367_ACCEL_CHANNEL(ADXL367_Y_CHANNEL_INDEX, ADXL367_REG_Y_DATA_H, Y), + ADXL367_ACCEL_CHANNEL(ADXL367_Z_CHANNEL_INDEX, ADXL367_REG_Z_DATA_H, Z), + ADXL367_CHANNEL(ADXL367_TEMP_CHANNEL_INDEX, ADXL367_REG_TEMP_DATA_H, + IIO_TEMP), + ADXL367_CHANNEL(ADXL367_EX_ADC_CHANNEL_INDEX, ADXL367_REG_EX_ADC_DATA_H, + IIO_VOLTAGE), +}; + +static int adxl367_verify_devid(struct adxl367_state *st) +{ + unsigned int val; + int ret; + + ret = regmap_read_poll_timeout(st->regmap, ADXL367_REG_DEVID, val, + val == ADXL367_DEVID_AD, 1000, 10000); + if (ret) + return dev_err_probe(st->dev, -ENODEV, + "Invalid dev id 0x%02X, expected 0x%02X\n", + val, ADXL367_DEVID_AD); + + return 0; +} + +static int adxl367_setup(struct adxl367_state *st) +{ + int ret; + + ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, + ADXL367_2G_RANGE_1G); + if (ret) + return ret; + + ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, + ADXL367_2G_RANGE_100MG); + if (ret) + return ret; + + ret = adxl367_set_act_proc_mode(st, ADXL367_LOOPED); + if (ret) + return ret; + + ret = _adxl367_set_odr(st, ADXL367_ODR_400HZ); + if (ret) + return ret; + + ret = _adxl367_set_act_time_ms(st, 10); + if (ret) + return ret; + + ret = _adxl367_set_inact_time_ms(st, 10000); + if (ret) + return ret; + + return adxl367_set_measure_en(st, true); +} + +static void adxl367_disable_regulators(void *data) +{ + struct adxl367_state *st = data; + + regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); +} + +int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, + void *context, struct regmap *regmap, int irq) +{ + struct iio_dev *indio_dev; + struct adxl367_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->dev = dev; + st->regmap = regmap; + st->context = context; + st->ops = ops; + + mutex_init(&st->lock); + + indio_dev->channels = adxl367_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl367_channels); + indio_dev->available_scan_masks = adxl367_channel_masks; + indio_dev->name = "adxl367"; + indio_dev->info = &adxl367_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + st->regulators[0].supply = "vdd"; + st->regulators[1].supply = "vddio"; + + ret = devm_regulator_bulk_get(st->dev, ARRAY_SIZE(st->regulators), + st->regulators); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(st->dev, adxl367_disable_regulators, st); + if (ret) + return dev_err_probe(st->dev, ret, + "Failed to add regulators disable action\n"); + + ret = regmap_write(st->regmap, ADXL367_REG_RESET, ADXL367_RESET_CODE); + if (ret) + return ret; + + ret = adxl367_verify_devid(st); + if (ret) + return ret; + + ret = adxl367_setup(st); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup_ext(st->dev, indio_dev, + &adxl367_buffer_ops, + adxl367_fifo_attributes); + if (ret) + return ret; + + ret = devm_request_threaded_irq(st->dev, irq, NULL, + adxl367_irq_handler, IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(adxl367_probe, IIO_ADXL367); + +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl367.h b/drivers/iio/accel/adxl367.h new file mode 100644 index 000000000000..4a42622149b1 --- /dev/null +++ b/drivers/iio/accel/adxl367.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#ifndef _ADXL367_H_ +#define _ADXL367_H_ + +#include <linux/types.h> + +struct device; +struct regmap; + +struct adxl367_ops { + int (*read_fifo)(void *context, __be16 *fifo_buf, + unsigned int fifo_entries); +}; + +int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, + void *context, struct regmap *regmap, int irq); + +#endif /* _ADXL367_H_ */ diff --git a/drivers/iio/accel/adxl367_i2c.c b/drivers/iio/accel/adxl367_i2c.c new file mode 100644 index 000000000000..3606efa25835 --- /dev/null +++ b/drivers/iio/accel/adxl367_i2c.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl367.h" + +#define ADXL367_I2C_FIFO_DATA 0x42 + +struct adxl367_i2c_state { + struct regmap *regmap; +}; + +static bool adxl367_readable_noinc_reg(struct device *dev, unsigned int reg) +{ + return reg == ADXL367_I2C_FIFO_DATA; +} + +static int adxl367_i2c_read_fifo(void *context, __be16 *fifo_buf, + unsigned int fifo_entries) +{ + struct adxl367_i2c_state *st = context; + + return regmap_noinc_read(st->regmap, ADXL367_I2C_FIFO_DATA, fifo_buf, + fifo_entries * sizeof(*fifo_buf)); +} + +static const struct regmap_config adxl367_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .readable_noinc_reg = adxl367_readable_noinc_reg, +}; + +static const struct adxl367_ops adxl367_i2c_ops = { + .read_fifo = adxl367_i2c_read_fifo, +}; + +static int adxl367_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adxl367_i2c_state *st; + struct regmap *regmap; + + st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &adxl367_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + st->regmap = regmap; + + return adxl367_probe(&client->dev, &adxl367_i2c_ops, st, regmap, + client->irq); +} + +static const struct i2c_device_id adxl367_i2c_id[] = { + { "adxl367", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, adxl367_i2c_id); + +static const struct of_device_id adxl367_of_match[] = { + { .compatible = "adi,adxl367" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adxl367_of_match); + +static struct i2c_driver adxl367_i2c_driver = { + .driver = { + .name = "adxl367_i2c", + .of_match_table = adxl367_of_match, + }, + .probe = adxl367_i2c_probe, + .id_table = adxl367_i2c_id, +}; + +module_i2c_driver(adxl367_i2c_driver); + +MODULE_IMPORT_NS(IIO_ADXL367); +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c new file mode 100644 index 000000000000..118c894015a5 --- /dev/null +++ b/drivers/iio/accel/adxl367_spi.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> + +#include "adxl367.h" + +#define ADXL367_SPI_WRITE_COMMAND 0x0A +#define ADXL367_SPI_READ_COMMAND 0x0B +#define ADXL367_SPI_FIFO_COMMAND 0x0D + +struct adxl367_spi_state { + struct spi_device *spi; + + struct spi_message reg_write_msg; + struct spi_transfer reg_write_xfer[2]; + + struct spi_message reg_read_msg; + struct spi_transfer reg_read_xfer[2]; + + struct spi_message fifo_msg; + struct spi_transfer fifo_xfer[2]; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers live in their own cache lines. + */ + u8 reg_write_tx_buf[1] __aligned(IIO_DMA_MINALIGN); + u8 reg_read_tx_buf[2]; + u8 fifo_tx_buf[1]; +}; + +static int adxl367_read_fifo(void *context, __be16 *fifo_buf, + unsigned int fifo_entries) +{ + struct adxl367_spi_state *st = context; + + st->fifo_xfer[1].rx_buf = fifo_buf; + st->fifo_xfer[1].len = fifo_entries * sizeof(*fifo_buf); + + return spi_sync(st->spi, &st->fifo_msg); +} + +static int adxl367_read(void *context, const void *reg_buf, size_t reg_size, + void *val_buf, size_t val_size) +{ + struct adxl367_spi_state *st = context; + u8 reg = ((const u8 *)reg_buf)[0]; + + st->reg_read_tx_buf[1] = reg; + st->reg_read_xfer[1].rx_buf = val_buf; + st->reg_read_xfer[1].len = val_size; + + return spi_sync(st->spi, &st->reg_read_msg); +} + +static int adxl367_write(void *context, const void *val_buf, size_t val_size) +{ + struct adxl367_spi_state *st = context; + + st->reg_write_xfer[1].tx_buf = val_buf; + st->reg_write_xfer[1].len = val_size; + + return spi_sync(st->spi, &st->reg_write_msg); +} + +static struct regmap_bus adxl367_spi_regmap_bus = { + .read = adxl367_read, + .write = adxl367_write, +}; + +static const struct regmap_config adxl367_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct adxl367_ops adxl367_spi_ops = { + .read_fifo = adxl367_read_fifo, +}; + +static int adxl367_spi_probe(struct spi_device *spi) +{ + struct adxl367_spi_state *st; + struct regmap *regmap; + + st = devm_kzalloc(&spi->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->spi = spi; + + /* + * Xfer: [XFR1] [ XFR2 ] + * Master: 0x0A ADDR DATA0 DATA1 ... DATAN + * Slave: .... .......................... + */ + st->reg_write_tx_buf[0] = ADXL367_SPI_WRITE_COMMAND; + st->reg_write_xfer[0].tx_buf = st->reg_write_tx_buf; + st->reg_write_xfer[0].len = sizeof(st->reg_write_tx_buf); + spi_message_init_with_transfers(&st->reg_write_msg, + st->reg_write_xfer, 2); + + /* + * Xfer: [ XFR1 ] [ XFR2 ] + * Master: 0x0B ADDR ..................... + * Slave: ......... DATA0 DATA1 ... DATAN + */ + st->reg_read_tx_buf[0] = ADXL367_SPI_READ_COMMAND; + st->reg_read_xfer[0].tx_buf = st->reg_read_tx_buf; + st->reg_read_xfer[0].len = sizeof(st->reg_read_tx_buf); + spi_message_init_with_transfers(&st->reg_read_msg, + st->reg_read_xfer, 2); + + /* + * Xfer: [XFR1] [ XFR2 ] + * Master: 0x0D ..................... + * Slave: .... DATA0 DATA1 ... DATAN + */ + st->fifo_tx_buf[0] = ADXL367_SPI_FIFO_COMMAND; + st->fifo_xfer[0].tx_buf = st->fifo_tx_buf; + st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf); + spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, 2); + + regmap = devm_regmap_init(&spi->dev, &adxl367_spi_regmap_bus, st, + &adxl367_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return adxl367_probe(&spi->dev, &adxl367_spi_ops, st, regmap, spi->irq); +} + +static const struct spi_device_id adxl367_spi_id[] = { + { "adxl367", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, adxl367_spi_id); + +static const struct of_device_id adxl367_of_match[] = { + { .compatible = "adi,adxl367" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adxl367_of_match); + +static struct spi_driver adxl367_spi_driver = { + .driver = { + .name = "adxl367_spi", + .of_match_table = adxl367_of_match, + }, + .probe = adxl367_spi_probe, + .id_table = adxl367_spi_id, +}; + +module_spi_driver(adxl367_spi_driver); + +MODULE_IMPORT_NS(IIO_ADXL367); +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 60daf04ce188..bc53af809d5d 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -5,6 +5,7 @@ * Copyright 2018 Analog Devices Inc. */ +#include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/interrupt.h> #include <linux/irq.h> @@ -113,6 +114,11 @@ #define ADXL372_STATUS_1_AWAKE(x) (((x) >> 6) & 0x1) #define ADXL372_STATUS_1_ERR_USR_REGS(x) (((x) >> 7) & 0x1) +/* ADXL372_STATUS_2 */ +#define ADXL372_STATUS_2_INACT(x) (((x) >> 4) & 0x1) +#define ADXL372_STATUS_2_ACT(x) (((x) >> 5) & 0x1) +#define ADXL372_STATUS_2_AC2(x) (((x) >> 6) & 0x1) + /* ADXL372_INT1_MAP */ #define ADXL372_INT1_MAP_DATA_RDY_MSK BIT(0) #define ADXL372_INT1_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0) @@ -131,8 +137,17 @@ #define ADXL372_INT1_MAP_LOW_MSK BIT(7) #define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7) +/* ADX372_THRESH */ +#define ADXL372_THRESH_VAL_H_MSK GENMASK(10, 3) +#define ADXL372_THRESH_VAL_H_SEL(x) FIELD_GET(ADXL372_THRESH_VAL_H_MSK, x) +#define ADXL372_THRESH_VAL_L_MSK GENMASK(2, 0) +#define ADXL372_THRESH_VAL_L_SEL(x) FIELD_GET(ADXL372_THRESH_VAL_L_MSK, x) + /* The ADXL372 includes a deep, 512 sample FIFO buffer */ #define ADXL372_FIFO_SIZE 512 +#define ADXL372_X_AXIS_EN(x) ((x) & BIT(0)) +#define ADXL372_Y_AXIS_EN(x) ((x) & BIT(1)) +#define ADXL372_Z_AXIS_EN(x) ((x) & BIT(2)) /* * At +/- 200g with 12-bit resolution, scale is computed as: @@ -222,6 +237,20 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { { BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO }, }; +static const struct iio_event_spec adxl372_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) | BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) | BIT(IIO_EV_INFO_ENABLE), + }, +}; + #define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .address = reg, \ @@ -239,6 +268,8 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { .shift = 4, \ .endianness = IIO_BE, \ }, \ + .event_spec = adxl372_events, \ + .num_event_specs = ARRAY_SIZE(adxl372_events) \ } static const struct iio_chan_spec adxl372_channels[] = { @@ -252,8 +283,10 @@ struct adxl372_state { struct device *dev; struct regmap *regmap; struct iio_trigger *dready_trig; + struct iio_trigger *peak_datardy_trig; enum adxl372_fifo_mode fifo_mode; enum adxl372_fifo_format fifo_format; + unsigned int fifo_axis_mask; enum adxl372_op_mode op_mode; enum adxl372_act_proc_mode act_proc_mode; enum adxl372_odr odr; @@ -261,10 +294,12 @@ struct adxl372_state { u32 act_time_ms; u32 inact_time_ms; u8 fifo_set_size; - u8 int1_bitmask; - u8 int2_bitmask; + unsigned long int1_bitmask; + unsigned long int2_bitmask; u16 watermark; __be16 fifo_buf[ADXL372_FIFO_SIZE]; + bool peak_fifo_mode_en; + struct mutex threshold_m; /* lock for threshold */ }; static const unsigned long adxl372_channel_masks[] = { @@ -276,6 +311,46 @@ static const unsigned long adxl372_channel_masks[] = { 0 }; +static ssize_t adxl372_read_threshold_value(struct iio_dev *indio_dev, unsigned int addr, + u16 *threshold) +{ + struct adxl372_state *st = iio_priv(indio_dev); + __be16 raw_regval; + u16 regval; + int ret; + + ret = regmap_bulk_read(st->regmap, addr, &raw_regval, sizeof(raw_regval)); + if (ret < 0) + return ret; + + regval = be16_to_cpu(raw_regval); + regval >>= 5; + + *threshold = regval; + + return 0; +} + +static ssize_t adxl372_write_threshold_value(struct iio_dev *indio_dev, unsigned int addr, + u16 threshold) +{ + struct adxl372_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->threshold_m); + ret = regmap_write(st->regmap, addr, ADXL372_THRESH_VAL_H_SEL(threshold)); + if (ret < 0) + goto unlock; + + ret = regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5), + ADXL372_THRESH_VAL_L_SEL(threshold) << 5); + +unlock: + mutex_unlock(&st->threshold_m); + + return ret; +} + static int adxl372_read_axis(struct adxl372_state *st, u8 addr) { __be16 regval; @@ -453,8 +528,8 @@ static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, } static int adxl372_set_interrupts(struct adxl372_state *st, - unsigned char int1_bitmask, - unsigned char int2_bitmask) + unsigned long int1_bitmask, + unsigned long int2_bitmask) { int ret; @@ -523,6 +598,39 @@ static int adxl372_get_status(struct adxl372_state *st, return ret; } +static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample) +{ + __be16 axis_sample[3]; + int i = 0; + + memset(axis_sample, 0, 3 * sizeof(__be16)); + if (ADXL372_X_AXIS_EN(st->fifo_axis_mask)) + axis_sample[i++] = sample[0]; + if (ADXL372_Y_AXIS_EN(st->fifo_axis_mask)) + axis_sample[i++] = sample[1]; + if (ADXL372_Z_AXIS_EN(st->fifo_axis_mask)) + axis_sample[i++] = sample[2]; + + memcpy(sample, axis_sample, 3 * sizeof(__be16)); +} + +static void adxl372_push_event(struct iio_dev *indio_dev, s64 timestamp, u8 status2) +{ + unsigned int ev_dir = IIO_EV_DIR_NONE; + + if (ADXL372_STATUS_2_ACT(status2)) + ev_dir = IIO_EV_DIR_RISING; + + if (ADXL372_STATUS_2_INACT(status2)) + ev_dir = IIO_EV_DIR_FALLING; + + if (ev_dir != IIO_EV_DIR_NONE) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_THRESH, ev_dir), + timestamp); +} + static irqreturn_t adxl372_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -536,6 +644,8 @@ static irqreturn_t adxl372_trigger_handler(int irq, void *p) if (ret < 0) goto err; + adxl372_push_event(indio_dev, iio_get_time_ns(indio_dev), status2); + if (st->fifo_mode != ADXL372_FIFO_BYPASSED && ADXL372_STATUS_1_FIFO_FULL(status1)) { /* @@ -554,8 +664,12 @@ static irqreturn_t adxl372_trigger_handler(int irq, void *p) goto err; /* Each sample is 2 bytes */ - for (i = 0; i < fifo_entries; i += st->fifo_set_size) + for (i = 0; i < fifo_entries; i += st->fifo_set_size) { + /* filter peak detection data */ + if (st->peak_fifo_mode_en) + adxl372_arrange_axis_data(st, &st->fifo_buf[i]); iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + } } err: iio_trigger_notify_done(indio_dev->trig); @@ -723,6 +837,129 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, } } +static int adxl372_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, + enum iio_event_type type, enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct adxl372_state *st = iio_priv(indio_dev); + unsigned int addr; + u16 raw_value; + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + addr = ADXL372_X_THRESH_ACT_H + 2 * chan->scan_index; + ret = adxl372_read_threshold_value(indio_dev, addr, &raw_value); + if (ret < 0) + return ret; + *val = raw_value * ADXL372_USCALE; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_DIR_FALLING: + addr = ADXL372_X_THRESH_INACT_H + 2 * chan->scan_index; + ret = adxl372_read_threshold_value(indio_dev, addr, &raw_value); + if (ret < 0) + return ret; + *val = raw_value * ADXL372_USCALE; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = st->act_time_ms; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_DIR_FALLING: + *val = st->inact_time_ms; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl372_write_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, + enum iio_event_type type, enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct adxl372_state *st = iio_priv(indio_dev); + unsigned int val_ms; + unsigned int addr; + u16 raw_val; + + switch (info) { + case IIO_EV_INFO_VALUE: + raw_val = DIV_ROUND_UP(val * 1000000, ADXL372_USCALE); + switch (dir) { + case IIO_EV_DIR_RISING: + addr = ADXL372_X_THRESH_ACT_H + 2 * chan->scan_index; + return adxl372_write_threshold_value(indio_dev, addr, raw_val); + case IIO_EV_DIR_FALLING: + addr = ADXL372_X_THRESH_INACT_H + 2 * chan->scan_index; + return adxl372_write_threshold_value(indio_dev, addr, raw_val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + val_ms = val * 1000 + DIV_ROUND_UP(val2, 1000); + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl372_set_activity_time_ms(st, val_ms); + case IIO_EV_DIR_FALLING: + return adxl372_set_inactivity_time_ms(st, val_ms); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl372_read_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, + enum iio_event_type type, enum iio_event_direction dir) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + switch (dir) { + case IIO_EV_DIR_RISING: + return FIELD_GET(ADXL372_INT1_MAP_ACT_MSK, st->int1_bitmask); + case IIO_EV_DIR_FALLING: + return FIELD_GET(ADXL372_INT1_MAP_INACT_MSK, st->int1_bitmask); + default: + return -EINVAL; + } +} + +static int adxl372_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, + enum iio_event_type type, enum iio_event_direction dir, + int state) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + switch (dir) { + case IIO_EV_DIR_RISING: + set_mask_bits(&st->int1_bitmask, ADXL372_INT1_MAP_ACT_MSK, + ADXL372_INT1_MAP_ACT_MODE(state)); + break; + case IIO_EV_DIR_FALLING: + set_mask_bits(&st->int1_bitmask, ADXL372_INT1_MAP_INACT_MSK, + ADXL372_INT1_MAP_INACT_MODE(state)); + break; + default: + return -EINVAL; + } + + return adxl372_set_interrupts(st, st->int1_bitmask, 0); +} + static ssize_t adxl372_show_filter_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) @@ -761,17 +998,30 @@ static ssize_t adxl372_get_fifo_watermark(struct device *dev, return sprintf(buf, "%d\n", st->watermark); } -static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); -static IIO_CONST_ATTR(hwfifo_watermark_max, - __stringify(ADXL372_FIFO_SIZE)); +static ssize_t hwfifo_watermark_min_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", "1"); +} + +static ssize_t hwfifo_watermark_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", __stringify(ADXL372_FIFO_SIZE)); +} + +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0); static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, adxl372_get_fifo_watermark, NULL, 0); static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, adxl372_get_fifo_enabled, NULL, 0); static const struct attribute *adxl372_fifo_attributes[] = { - &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, - &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark_max.dev_attr.attr, &iio_dev_attr_hwfifo_watermark.dev_attr.attr, &iio_dev_attr_hwfifo_enabled.dev_attr.attr, NULL, @@ -795,14 +1045,11 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev) unsigned int mask; int i, ret; - ret = iio_triggered_buffer_postenable(indio_dev); + st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK; + ret = adxl372_set_interrupts(st, st->int1_bitmask, 0); if (ret < 0) return ret; - ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0); - if (ret < 0) - goto err; - mask = *indio_dev->active_scan_mask; for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) { @@ -810,19 +1057,26 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev) break; } - if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) { - ret = -EINVAL; - goto err; - } + if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) + return -EINVAL; st->fifo_format = adxl372_axis_lookup_table[i].fifo_format; + st->fifo_axis_mask = adxl372_axis_lookup_table[i].bits; st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, indio_dev->masklength); + + /* Configure the FIFO to store sets of impact event peak. */ + if (st->peak_fifo_mode_en) { + st->fifo_set_size = 3; + st->fifo_format = ADXL372_XYZ_PEAK_FIFO; + } + /* * The 512 FIFO samples can be allotted in several ways, such as: * 170 sample sets of concurrent 3-axis data * 256 sample sets of concurrent 2-axis data (user selectable) * 512 sample sets of single-axis data + * 170 sets of impact event peak (x, y, z) */ if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE) st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size); @@ -832,26 +1086,24 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev) ret = adxl372_configure_fifo(st); if (ret < 0) { st->fifo_mode = ADXL372_FIFO_BYPASSED; - adxl372_set_interrupts(st, 0, 0); - goto err; + st->int1_bitmask &= ~ADXL372_INT1_MAP_FIFO_FULL_MSK; + adxl372_set_interrupts(st, st->int1_bitmask, 0); + return ret; } return 0; - -err: - iio_triggered_buffer_predisable(indio_dev); - return ret; } static int adxl372_buffer_predisable(struct iio_dev *indio_dev) { struct adxl372_state *st = iio_priv(indio_dev); - adxl372_set_interrupts(st, 0, 0); + st->int1_bitmask &= ~ADXL372_INT1_MAP_FIFO_FULL_MSK; + adxl372_set_interrupts(st, st->int1_bitmask, 0); st->fifo_mode = ADXL372_FIFO_BYPASSED; adxl372_configure_fifo(st); - return iio_triggered_buffer_predisable(indio_dev); + return 0; } static const struct iio_buffer_setup_ops adxl372_buffer_ops = { @@ -864,12 +1116,11 @@ static int adxl372_dready_trig_set_state(struct iio_trigger *trig, { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct adxl372_state *st = iio_priv(indio_dev); - unsigned long int mask = 0; if (state) - mask = ADXL372_INT1_MAP_FIFO_FULL_MSK; + st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK; - return adxl372_set_interrupts(st, mask, 0); + return adxl372_set_interrupts(st, st->int1_bitmask, 0); } static int adxl372_validate_trigger(struct iio_dev *indio_dev, @@ -877,7 +1128,7 @@ static int adxl372_validate_trigger(struct iio_dev *indio_dev, { struct adxl372_state *st = iio_priv(indio_dev); - if (st->dready_trig != trig) + if (st->dready_trig != trig && st->peak_datardy_trig != trig) return -EINVAL; return 0; @@ -888,6 +1139,25 @@ static const struct iio_trigger_ops adxl372_trigger_ops = { .set_trigger_state = adxl372_dready_trig_set_state, }; +static int adxl372_peak_dready_trig_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct adxl372_state *st = iio_priv(indio_dev); + + if (state) + st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK; + + st->peak_fifo_mode_en = state; + + return adxl372_set_interrupts(st, st->int1_bitmask, 0); +} + +static const struct iio_trigger_ops adxl372_peak_data_trigger_ops = { + .validate_device = &iio_trigger_validate_own_device, + .set_trigger_state = adxl372_peak_dready_trig_set_state, +}; + static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available, 0444, adxl372_show_filter_freq_avail, NULL, 0); @@ -907,6 +1177,10 @@ static const struct iio_info adxl372_info = { .attrs = &adxl372_attrs_group, .read_raw = adxl372_read_raw, .write_raw = adxl372_write_raw, + .read_event_config = adxl372_read_event_config, + .write_event_config = adxl372_write_event_config, + .read_event_value = adxl372_read_event_value, + .write_event_value = adxl372_write_event_value, .debugfs_reg_access = &adxl372_reg_access, .hwfifo_set_watermark = adxl372_set_watermark, }; @@ -915,7 +1189,7 @@ bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) { return (reg == ADXL372_FIFO_DATA); } -EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg); +EXPORT_SYMBOL_NS_GPL(adxl372_readable_noinc_reg, IIO_ADXL372); int adxl372_probe(struct device *dev, struct regmap *regmap, int irq, const char *name) @@ -935,10 +1209,11 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->regmap = regmap; st->irq = irq; + mutex_init(&st->threshold_m); + indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); indio_dev->available_scan_masks = adxl372_channel_masks; - indio_dev->dev.parent = dev; indio_dev->name = name; indio_dev->info = &adxl372_info; indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; @@ -949,30 +1224,42 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, return ret; } - ret = devm_iio_triggered_buffer_setup(dev, - indio_dev, NULL, - adxl372_trigger_handler, - &adxl372_buffer_ops); + ret = devm_iio_triggered_buffer_setup_ext(dev, + indio_dev, NULL, + adxl372_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &adxl372_buffer_ops, + adxl372_fifo_attributes); if (ret < 0) return ret; - iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes); - if (st->irq) { st->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (st->dready_trig == NULL) return -ENOMEM; + st->peak_datardy_trig = devm_iio_trigger_alloc(dev, + "%s-dev%d-peak", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->peak_datardy_trig) + return -ENOMEM; + st->dready_trig->ops = &adxl372_trigger_ops; - st->dready_trig->dev.parent = dev; + st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; iio_trigger_set_drvdata(st->dready_trig, indio_dev); + iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); ret = devm_iio_trigger_register(dev, st->dready_trig); if (ret < 0) return ret; + ret = devm_iio_trigger_register(dev, st->peak_datardy_trig); + if (ret < 0) + return ret; + indio_dev->trig = iio_trigger_get(st->dready_trig); ret = devm_request_threaded_irq(dev, st->irq, @@ -986,7 +1273,7 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_GPL(adxl372_probe); +EXPORT_SYMBOL_NS_GPL(adxl372_probe, IIO_ADXL372); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c index e1affe480c77..4efb70a5fe40 100644 --- a/drivers/iio/accel/adxl372_i2c.c +++ b/drivers/iio/accel/adxl372_i2c.c @@ -6,6 +6,7 @@ */ #include <linux/i2c.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> @@ -46,9 +47,16 @@ static const struct i2c_device_id adxl372_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); +static const struct of_device_id adxl372_of_match[] = { + { .compatible = "adi,adxl372" }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl372_of_match); + static struct i2c_driver adxl372_i2c_driver = { .driver = { .name = "adxl372_i2c", + .of_match_table = adxl372_of_match, }, .probe = adxl372_i2c_probe, .id_table = adxl372_i2c_id, @@ -59,3 +67,4 @@ module_i2c_driver(adxl372_i2c_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADXL372); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 3ef7e3a4804e..2bd267a22f29 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -40,8 +40,8 @@ static const struct spi_device_id adxl372_spi_id[] = { MODULE_DEVICE_TABLE(spi, adxl372_spi_id); static const struct of_device_id adxl372_of_match[] = { - { .compatible = "adi,adxl372" }, - { }, + { .compatible = "adi,adxl372" }, + { } }; MODULE_DEVICE_TABLE(of, adxl372_of_match); @@ -59,3 +59,4 @@ module_spi_driver(adxl372_spi_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADXL372); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index fcd91d5f05fd..d03fc3400f94 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -7,9 +7,9 @@ * Support for BMA250 (c) Peter Meerwald <pmeerw@pmeerw.net> * * SPI is not supported by driver + * BMA023/BMA150/SMB380: 7-bit I2C slave address 0x38 * 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> @@ -33,9 +33,10 @@ #define BMA180_IRQ_NAME "bma180_event" enum chip_ids { + BMA023, + BMA150, BMA180, BMA250, - BMA254, }; struct bma180_data; @@ -48,22 +49,33 @@ struct bma180_part_info { unsigned int num_scales; const int *bw_table; unsigned int num_bw; - int center_temp; + int temp_offset; u8 int_reset_reg, int_reset_mask; u8 sleep_reg, sleep_mask; - u8 bw_reg, bw_mask; + u8 bw_reg, bw_mask, bw_offset; 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; + u8 softreset_reg, softreset_val; int (*chip_config)(struct bma180_data *data); void (*chip_disable)(struct bma180_data *data); }; /* Register set */ +#define BMA023_CTRL_REG0 0x0a +#define BMA023_CTRL_REG1 0x0b +#define BMA023_CTRL_REG2 0x14 +#define BMA023_CTRL_REG3 0x15 + +#define BMA023_RANGE_MASK GENMASK(4, 3) /* Range of accel values */ +#define BMA023_BW_MASK GENMASK(2, 0) /* Accel bandwidth */ +#define BMA023_SLEEP BIT(0) +#define BMA023_INT_RESET_MASK BIT(6) +#define BMA023_NEW_DATA_INT BIT(5) /* Intr every new accel data is ready */ +#define BMA023_RESET_VAL BIT(1) + #define BMA180_CHIP_ID 0x00 /* Need to distinguish BMA180 from other */ #define BMA180_ACC_X_LSB 0x02 /* First of 6 registers of accel data */ #define BMA180_TEMP 0x08 @@ -94,9 +106,9 @@ struct bma180_part_info { /* We have to write this value in reset register to do soft reset */ #define BMA180_RESET_VAL 0xb6 +#define BMA023_ID_REG_VAL 0x02 #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 @@ -111,29 +123,13 @@ struct bma180_part_info { #define BMA250_RANGE_MASK GENMASK(3, 0) /* Range of accel values */ #define BMA250_BW_MASK GENMASK(4, 0) /* Accel bandwidth */ +#define BMA250_BW_OFFSET 8 #define BMA250_SUSPEND_MASK BIT(7) /* chip will sleep */ #define BMA250_LOWPOWER_MASK BIT(6) #define BMA250_DATA_INTEN_MASK BIT(4) #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; @@ -146,7 +142,11 @@ struct bma180_data { int scale; int bw; bool pmode; - u8 buff[16]; /* 3x 16-bit + 8-bit + padding + timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + s16 chan[4]; + s64 timestamp __aligned(8); + } scan; }; enum bma180_chan { @@ -156,11 +156,14 @@ enum bma180_chan { TEMP }; +static int bma023_bw_table[] = { 25, 50, 100, 190, 375, 750, 1500 }; /* Hz */ +static int bma023_scale_table[] = { 2452, 4903, 9709, }; + 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 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, +static int bma250_bw_table[] = { 8, 16, 31, 63, 125, 250, 500, 1000 }; /* Hz */ +static int bma250_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) @@ -264,7 +267,8 @@ static int bma180_set_bw(struct bma180_data *data, int val) for (i = 0; i < data->part_info->num_bw; ++i) { if (data->part_info->bw_table[i] == val) { ret = bma180_set_bits(data, data->part_info->bw_reg, - data->part_info->bw_mask, i); + data->part_info->bw_mask, + i + data->part_info->bw_offset); if (ret) { dev_err(&data->client->dev, "failed to set bandwidth\n"); @@ -319,7 +323,8 @@ static int bma180_set_pmode(struct bma180_data *data, bool mode) static int bma180_soft_reset(struct bma180_data *data) { int ret = i2c_smbus_write_byte_data(data->client, - data->part_info->softreset_reg, BMA180_RESET_VAL); + data->part_info->softreset_reg, + data->part_info->softreset_val); if (ret) dev_err(&data->client->dev, "failed to reset the chip\n"); @@ -349,11 +354,28 @@ static int bma180_chip_init(struct bma180_data *data) */ msleep(20); - ret = bma180_set_new_data_intr_state(data, false); + return bma180_set_new_data_intr_state(data, false); +} + +static int bma023_chip_config(struct bma180_data *data) +{ + int ret = bma180_chip_init(data); + if (ret) - return ret; + goto err; + + ret = bma180_set_bw(data, 50); /* 50 Hz */ + if (ret) + goto err; + ret = bma180_set_scale(data, 2452); /* 2 G */ + if (ret) + goto err; + + return 0; - return bma180_set_pmode(data, false); +err: + dev_err(&data->client->dev, "failed to config the chip\n"); + return ret; } static int bma180_chip_config(struct bma180_data *data) @@ -362,6 +384,9 @@ static int bma180_chip_config(struct bma180_data *data) if (ret) goto err; + ret = bma180_set_pmode(data, false); + if (ret) + goto err; ret = bma180_set_bits(data, BMA180_CTRL_REG0, BMA180_DIS_WAKE_UP, 1); if (ret) goto err; @@ -385,12 +410,15 @@ err: return ret; } -static int bma25x_chip_config(struct bma180_data *data) +static int bma250_chip_config(struct bma180_data *data) { int ret = bma180_chip_init(data); if (ret) goto err; + ret = bma180_set_pmode(data, false); + if (ret) + goto err; ret = bma180_set_bw(data, 16); /* 16 Hz */ if (ret) goto err; @@ -401,8 +429,7 @@ static int bma25x_chip_config(struct bma180_data *data) * 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); + ret = bma180_set_bits(data, BMA250_INT_MAP_REG, BMA250_INT1_DATA_MASK, 1); if (ret) goto err; @@ -413,6 +440,17 @@ err: return ret; } +static void bma023_chip_disable(struct bma180_data *data) +{ + if (bma180_set_sleep_state(data, true)) + goto err; + + return; + +err: + dev_err(&data->client->dev, "failed to disable the chip\n"); +} + static void bma180_chip_disable(struct bma180_data *data) { if (bma180_set_new_data_intr_state(data, false)) @@ -428,7 +466,7 @@ err: dev_err(&data->client->dev, "failed to disable the chip\n"); } -static void bma25x_chip_disable(struct bma180_data *data) +static void bma250_chip_disable(struct bma180_data *data) { if (bma180_set_new_data_intr_state(data, false)) goto err; @@ -512,8 +550,12 @@ static int bma180_read_raw(struct iio_dev *indio_dev, iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; - *val = sign_extend32(ret >> chan->scan_type.shift, - chan->scan_type.realbits - 1); + if (chan->scan_type.sign == 's') { + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + } else { + *val = ret; + } return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: *val = data->bw; @@ -531,7 +573,7 @@ static int bma180_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - *val = data->part_info->center_temp; + *val = data->part_info->temp_offset; return IIO_VAL_INT; default: return -EINVAL; @@ -609,13 +651,47 @@ static const struct iio_enum bma180_power_mode_enum = { .set = bma180_set_power_mode, }; +static const struct iio_chan_spec_ext_info bma023_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma180_accel_get_mount_matrix), + { } +}; + static const struct iio_chan_spec_ext_info bma180_ext_info[] = { - IIO_ENUM("power_mode", true, &bma180_power_mode_enum), - IIO_ENUM_AVAILABLE("power_mode", &bma180_power_mode_enum), + IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE, &bma180_power_mode_enum), + IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE, &bma180_power_mode_enum), IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma180_accel_get_mount_matrix), { } }; +#define BMA023_ACC_CHANNEL(_axis, _bits) { \ + .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_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = AXIS_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = _bits, \ + .storagebits = 16, \ + .shift = 16 - _bits, \ + }, \ + .ext_info = bma023_ext_info, \ +} + +#define BMA150_TEMP_CHANNEL { \ + .type = IIO_TEMP, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = TEMP, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 16, \ + }, \ +} + #define BMA180_ACC_CHANNEL(_axis, _bits) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -645,6 +721,21 @@ static const struct iio_chan_spec_ext_info bma180_ext_info[] = { }, \ } +static const struct iio_chan_spec bma023_channels[] = { + BMA023_ACC_CHANNEL(X, 10), + BMA023_ACC_CHANNEL(Y, 10), + BMA023_ACC_CHANNEL(Z, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec bma150_channels[] = { + BMA023_ACC_CHANNEL(X, 10), + BMA023_ACC_CHANNEL(Y, 10), + BMA023_ACC_CHANNEL(Z, 10), + BMA150_TEMP_CHANNEL, + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + static const struct iio_chan_spec bma180_channels[] = { BMA180_ACC_CHANNEL(X, 14), BMA180_ACC_CHANNEL(Y, 14), @@ -661,15 +752,64 @@ 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[] = { + [BMA023] = { + .chip_id = BMA023_ID_REG_VAL, + .channels = bma023_channels, + .num_channels = ARRAY_SIZE(bma023_channels), + .scale_table = bma023_scale_table, + .num_scales = ARRAY_SIZE(bma023_scale_table), + .bw_table = bma023_bw_table, + .num_bw = ARRAY_SIZE(bma023_bw_table), + /* No temperature channel */ + .temp_offset = 0, + .int_reset_reg = BMA023_CTRL_REG0, + .int_reset_mask = BMA023_INT_RESET_MASK, + .sleep_reg = BMA023_CTRL_REG0, + .sleep_mask = BMA023_SLEEP, + .bw_reg = BMA023_CTRL_REG2, + .bw_mask = BMA023_BW_MASK, + .scale_reg = BMA023_CTRL_REG2, + .scale_mask = BMA023_RANGE_MASK, + /* No power mode on bma023 */ + .power_reg = 0, + .power_mask = 0, + .lowpower_val = 0, + .int_enable_reg = BMA023_CTRL_REG3, + .int_enable_mask = BMA023_NEW_DATA_INT, + .softreset_reg = BMA023_CTRL_REG0, + .softreset_val = BMA023_RESET_VAL, + .chip_config = bma023_chip_config, + .chip_disable = bma023_chip_disable, + }, + [BMA150] = { + .chip_id = BMA023_ID_REG_VAL, + .channels = bma150_channels, + .num_channels = ARRAY_SIZE(bma150_channels), + .scale_table = bma023_scale_table, + .num_scales = ARRAY_SIZE(bma023_scale_table), + .bw_table = bma023_bw_table, + .num_bw = ARRAY_SIZE(bma023_bw_table), + .temp_offset = -60, /* 0 LSB @ -30 degree C */ + .int_reset_reg = BMA023_CTRL_REG0, + .int_reset_mask = BMA023_INT_RESET_MASK, + .sleep_reg = BMA023_CTRL_REG0, + .sleep_mask = BMA023_SLEEP, + .bw_reg = BMA023_CTRL_REG2, + .bw_mask = BMA023_BW_MASK, + .scale_reg = BMA023_CTRL_REG2, + .scale_mask = BMA023_RANGE_MASK, + /* No power mode on bma150 */ + .power_reg = 0, + .power_mask = 0, + .lowpower_val = 0, + .int_enable_reg = BMA023_CTRL_REG3, + .int_enable_mask = BMA023_NEW_DATA_INT, + .softreset_reg = BMA023_CTRL_REG0, + .softreset_val = BMA023_RESET_VAL, + .chip_config = bma023_chip_config, + .chip_disable = bma023_chip_disable, + }, [BMA180] = { .chip_id = BMA180_ID_REG_VAL, .channels = bma180_channels, @@ -678,7 +818,7 @@ static const struct bma180_part_info bma180_part_info[] = { .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 */ + .temp_offset = 48, /* 0 LSB @ 24 degree C */ .int_reset_reg = BMA180_CTRL_REG0, .int_reset_mask = BMA180_RESET_INT, .sleep_reg = BMA180_CTRL_REG0, @@ -693,6 +833,7 @@ static const struct bma180_part_info bma180_part_info[] = { .int_enable_reg = BMA180_CTRL_REG3, .int_enable_mask = BMA180_NEW_DATA_INT, .softreset_reg = BMA180_RESET, + .softreset_val = BMA180_RESET_VAL, .chip_config = bma180_chip_config, .chip_disable = bma180_chip_disable, }, @@ -700,17 +841,18 @@ static const struct bma180_part_info bma180_part_info[] = { .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 */ + .scale_table = bma250_scale_table, + .num_scales = ARRAY_SIZE(bma250_scale_table), + .bw_table = bma250_bw_table, + .num_bw = ARRAY_SIZE(bma250_bw_table), + .temp_offset = 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, + .bw_offset = BMA250_BW_OFFSET, .scale_reg = BMA250_RANGE_REG, .scale_mask = BMA250_RANGE_MASK, .power_reg = BMA250_POWER_REG, @@ -718,39 +860,10 @@ static const struct bma180_part_info bma180_part_info[] = { .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, + .softreset_val = BMA180_RESET_VAL, + .chip_config = bma250_chip_config, + .chip_disable = bma250_chip_disable, }, }; @@ -771,12 +884,12 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) mutex_unlock(&data->mutex); goto err; } - ((s16 *)data->buff)[i++] = ret; + data->scan.chan[i++] = ret; } mutex_unlock(&data->mutex); - iio_push_to_buffers_with_timestamp(indio_dev, data->buff, time_ns); + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, time_ns); err: iio_trigger_notify_done(indio_dev->trig); @@ -792,17 +905,20 @@ static int bma180_data_rdy_trigger_set_state(struct iio_trigger *trig, return bma180_set_new_data_intr_state(data, state); } -static int bma180_trig_try_reen(struct iio_trigger *trig) +static void bma180_trig_reen(struct iio_trigger *trig) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct bma180_data *data = iio_priv(indio_dev); + int ret; - return bma180_reset_intr(data); + ret = bma180_reset_intr(data); + if (ret) + dev_err(&data->client->dev, "failed to reset interrupt\n"); } static const struct iio_trigger_ops bma180_trigger_ops = { .set_trigger_state = bma180_data_rdy_trigger_set_state, - .try_reenable = bma180_trig_try_reen, + .reenable = bma180_trig_reen, }; static int bma180_probe(struct i2c_client *client, @@ -822,30 +938,25 @@ 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(dev); + chip = (uintptr_t)of_device_get_match_data(dev); else chip = id->driver_data; data->part_info = &bma180_part_info[chip]; - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &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); - } + if (IS_ERR(data->vdd_supply)) + return dev_err_probe(dev, PTR_ERR(data->vdd_supply), + "Failed to get vdd regulator\n"); + 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); - } + if (IS_ERR(data->vddio_supply)) + return dev_err_probe(dev, PTR_ERR(data->vddio_supply), + "Failed to get vddio regulator\n"); + /* Typical voltage 2.4V these are min and max */ ret = regulator_set_voltage(data->vdd_supply, 1620000, 3600000); if (ret) @@ -871,7 +982,6 @@ static int bma180_probe(struct i2c_client *client, goto err_chip_disable; mutex_init(&data->mutex); - 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; @@ -879,8 +989,8 @@ static int bma180_probe(struct i2c_client *client, indio_dev->info = &bma180_info; if (client->irq > 0) { - data->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, - indio_dev->id); + data->trig = iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); if (!data->trig) { ret = -ENOMEM; goto err_chip_disable; @@ -894,14 +1004,14 @@ static int bma180_probe(struct i2c_client *client, goto err_trigger_free; } - 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); ret = iio_trigger_register(data->trig); if (ret) goto err_trigger_free; + + indio_dev->trig = iio_trigger_get(data->trig); } ret = iio_triggered_buffer_setup(indio_dev, NULL, @@ -935,7 +1045,7 @@ err_disable_vdd: return ret; } -static int bma180_remove(struct i2c_client *client) +static void bma180_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct bma180_data *data = iio_priv(indio_dev); @@ -952,11 +1062,8 @@ static int bma180_remove(struct i2c_client *client) mutex_unlock(&data->mutex); regulator_disable(data->vddio_supply); regulator_disable(data->vdd_supply); - - return 0; } -#ifdef CONFIG_PM_SLEEP static int bma180_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -983,16 +1090,14 @@ static int bma180_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); -#define BMA180_PM_OPS (&bma180_pm_ops) -#else -#define BMA180_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); static const struct i2c_device_id bma180_ids[] = { + { "bma023", BMA023 }, + { "bma150", BMA150 }, { "bma180", BMA180 }, { "bma250", BMA250 }, - { "bma254", BMA254 }, + { "smb380", BMA150 }, { } }; @@ -1000,6 +1105,14 @@ MODULE_DEVICE_TABLE(i2c, bma180_ids); static const struct of_device_id bma180_of_match[] = { { + .compatible = "bosch,bma023", + .data = (void *)BMA023 + }, + { + .compatible = "bosch,bma150", + .data = (void *)BMA150 + }, + { .compatible = "bosch,bma180", .data = (void *)BMA180 }, @@ -1008,8 +1121,8 @@ static const struct of_device_id bma180_of_match[] = { .data = (void *)BMA250 }, { - .compatible = "bosch,bma254", - .data = (void *)BMA254 + .compatible = "bosch,smb380", + .data = (void *)BMA150 }, { } }; @@ -1018,7 +1131,7 @@ MODULE_DEVICE_TABLE(of, bma180_of_match); static struct i2c_driver bma180_driver = { .driver = { .name = "bma180", - .pm = BMA180_PM_OPS, + .pm = pm_sleep_ptr(&bma180_pm_ops), .of_match_table = bma180_of_match, }, .probe = bma180_probe, @@ -1030,5 +1143,5 @@ module_i2c_driver(bma180_driver); MODULE_AUTHOR("Kravchenko Oleksandr <x0199363@ti.com>"); MODULE_AUTHOR("Texas Instruments, Inc."); -MODULE_DESCRIPTION("Bosch BMA180/BMA25x triaxial acceleration sensor"); +MODULE_DESCRIPTION("Bosch BMA023/BMA1x0/BMA250 triaxial acceleration sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index cae905039cb6..fcbd695e4654 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -1,17 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * BMA220 Digital triaxial acceleration sensor driver * - * Copyright (c) 2016, Intel Corporation. + * Copyright (c) 2016,2020 Intel Corporation. */ -#include <linux/acpi.h> +#include <linux/bits.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/spi/spi.h> + #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <linux/spi/spi.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> @@ -23,14 +25,12 @@ #define BMA220_REG_SUSPEND 0x18 #define BMA220_CHIP_ID 0xDD -#define BMA220_READ_MASK 0x80 -#define BMA220_RANGE_MASK 0x03 -#define BMA220_DATA_SHIFT 2 +#define BMA220_READ_MASK BIT(7) +#define BMA220_RANGE_MASK GENMASK(1, 0) #define BMA220_SUSPEND_SLEEP 0xFF #define BMA220_SUSPEND_WAKE 0x00 #define BMA220_DEVICE_NAME "bma220" -#define BMA220_SCALE_AVAILABLE "0.623 1.248 2.491 4.983" #define BMA220_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ @@ -44,7 +44,7 @@ .sign = 's', \ .realbits = 6, \ .storagebits = 8, \ - .shift = BMA220_DATA_SHIFT, \ + .shift = 2, \ .endianness = IIO_CPU, \ }, \ } @@ -55,26 +55,19 @@ enum bma220_axis { AXIS_Z, }; -static IIO_CONST_ATTR(in_accel_scale_available, BMA220_SCALE_AVAILABLE); - -static struct attribute *bma220_attributes[] = { - &iio_const_attr_in_accel_scale_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group bma220_attribute_group = { - .attrs = bma220_attributes, -}; - -static const int bma220_scale_table[][4] = { - {0, 623000}, {1, 248000}, {2, 491000}, {4, 983000} +static const int bma220_scale_table[][2] = { + {0, 623000}, {1, 248000}, {2, 491000}, {4, 983000}, }; struct bma220_data { struct spi_device *spi_device; struct mutex lock; - s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 8x8 timestamp */ - u8 tx_buf[2] ____cacheline_aligned; + struct { + s8 chans[3]; + /* Ensure timestamp is naturally aligned. */ + s64 timestamp __aligned(8); + } scan; + u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN); }; static const struct iio_chan_spec bma220_channels[] = { @@ -104,12 +97,12 @@ static irqreturn_t bma220_trigger_handler(int irq, void *p) mutex_lock(&data->lock); data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK; - ret = spi_write_then_read(spi, data->tx_buf, 1, data->buffer, + ret = spi_write_then_read(spi, data->tx_buf, 1, &data->scan.chans, ARRAY_SIZE(bma220_channels) - 1); if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: mutex_unlock(&data->lock); @@ -131,7 +124,8 @@ static int bma220_read_raw(struct iio_dev *indio_dev, ret = bma220_read_reg(data->spi_device, chan->address); if (ret < 0) return -EINVAL; - *val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE); @@ -182,10 +176,26 @@ static int bma220_write_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int bma220_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: + *vals = (int *)bma220_scale_table; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(bma220_scale_table) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static const struct iio_info bma220_info = { .read_raw = bma220_read_raw, .write_raw = bma220_write_raw, - .attrs = &bma220_attribute_group, + .read_avail = bma220_read_avail, }; static int bma220_init(struct spi_device *spi) @@ -198,26 +208,43 @@ static int bma220_init(struct spi_device *spi) /* Make sure the chip is powered on */ ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (ret == BMA220_SUSPEND_WAKE) + ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); if (ret < 0) return ret; - else if (ret == BMA220_SUSPEND_WAKE) - return bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (ret == BMA220_SUSPEND_WAKE) + return -EBUSY; return 0; } -static int bma220_deinit(struct spi_device *spi) +static int bma220_power(struct spi_device *spi, bool up) { - int ret; + int i, ret; + + /** + * The chip can be suspended/woken up by a simple register read. + * So, we need up to 2 register reads of the suspend register + * to make sure that the device is in the desired state. + */ + for (i = 0; i < 2; i++) { + ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (ret < 0) + return ret; - /* Make sure the chip is powered off */ - ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); - if (ret < 0) - return ret; - else if (ret == BMA220_SUSPEND_SLEEP) - return bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (up && ret == BMA220_SUSPEND_SLEEP) + return 0; - return 0; + if (!up && ret == BMA220_SUSPEND_WAKE) + return 0; + } + + return -EBUSY; +} + +static void bma220_deinit(void *spi) +{ + bma220_power(spi, false); } static int bma220_probe(struct spi_device *spi) @@ -234,10 +261,8 @@ static int bma220_probe(struct spi_device *spi) data = iio_priv(indio_dev); data->spi_device = spi; - spi_set_drvdata(spi, indio_dev); mutex_init(&data->lock); - indio_dev->dev.parent = &spi->dev; indio_dev->info = &bma220_info; indio_dev->name = BMA220_DEVICE_NAME; indio_dev->modes = INDIO_DIRECT_MODE; @@ -246,63 +271,38 @@ static int bma220_probe(struct spi_device *spi) indio_dev->available_scan_masks = bma220_accel_scan_masks; ret = bma220_init(data->spi_device); - if (ret < 0) + if (ret) return ret; - ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, - bma220_trigger_handler, NULL); - if (ret < 0) { - dev_err(&spi->dev, "iio triggered buffer setup failed\n"); - goto err_suspend; - } + ret = devm_add_action_or_reset(&spi->dev, bma220_deinit, spi); + if (ret) + return ret; - ret = iio_device_register(indio_dev); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + iio_pollfunc_store_time, + bma220_trigger_handler, NULL); if (ret < 0) { - dev_err(&spi->dev, "iio_device_register failed\n"); - iio_triggered_buffer_cleanup(indio_dev); - goto err_suspend; + dev_err(&spi->dev, "iio triggered buffer setup failed\n"); + return ret; } - return 0; - -err_suspend: - return bma220_deinit(spi); + return devm_iio_device_register(&spi->dev, indio_dev); } -static int bma220_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - - return bma220_deinit(spi); -} - -#ifdef CONFIG_PM_SLEEP static int bma220_suspend(struct device *dev) { - struct bma220_data *data = - iio_priv(spi_get_drvdata(to_spi_device(dev))); + struct spi_device *spi = to_spi_device(dev); - /* The chip can be suspended/woken up by a simple register read. */ - return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND); + return bma220_power(spi, false); } static int bma220_resume(struct device *dev) { - struct bma220_data *data = - iio_priv(spi_get_drvdata(to_spi_device(dev))); + struct spi_device *spi = to_spi_device(dev); - return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND); + return bma220_power(spi, true); } - -static SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume); - -#define BMA220_PM_OPS (&bma220_pm_ops) -#else -#define BMA220_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume); static const struct spi_device_id bma220_spi_id[] = { {"bma220", 0}, @@ -313,20 +313,17 @@ static const struct acpi_device_id bma220_acpi_id[] = { {"BMA0220", 0}, {} }; - MODULE_DEVICE_TABLE(spi, bma220_spi_id); static struct spi_driver bma220_driver = { .driver = { .name = "bma220_spi", - .pm = BMA220_PM_OPS, - .acpi_match_table = ACPI_PTR(bma220_acpi_id), + .pm = pm_sleep_ptr(&bma220_pm_ops), + .acpi_match_table = bma220_acpi_id, }, .probe = bma220_probe, - .remove = bma220_remove, .id_table = bma220_spi_id, }; - module_spi_driver(bma220_driver); MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>"); diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h index 5ad10db9819f..36edbaff4f7f 100644 --- a/drivers/iio/accel/bma400.h +++ b/drivers/iio/accel/bma400.h @@ -39,6 +39,8 @@ #define BMA400_INT_STAT0_REG 0x0e #define BMA400_INT_STAT1_REG 0x0f #define BMA400_INT_STAT2_REG 0x10 +#define BMA400_INT12_MAP_REG 0x23 +#define BMA400_INT_ENG_OVRUN_MSK BIT(4) /* Temperature register */ #define BMA400_TEMP_DATA_REG 0x11 @@ -53,6 +55,9 @@ #define BMA400_STEP_CNT1_REG 0x16 #define BMA400_STEP_CNT3_REG 0x17 #define BMA400_STEP_STAT_REG 0x18 +#define BMA400_STEP_INT_MSK BIT(0) +#define BMA400_STEP_RAW_LEN 0x03 +#define BMA400_STEP_STAT_MASK GENMASK(9, 8) /* * Read-write configuration registers @@ -62,6 +67,13 @@ #define BMA400_ACC_CONFIG2_REG 0x1b #define BMA400_CMD_REG 0x7e +/* Interrupt registers */ +#define BMA400_INT_CONFIG0_REG 0x1f +#define BMA400_INT_CONFIG1_REG 0x20 +#define BMA400_INT1_MAP_REG 0x21 +#define BMA400_INT_IO_CTRL_REG 0x24 +#define BMA400_INT_DRDY_MSK BIT(7) + /* Chip ID of BMA 400 devices found in the chip ID register. */ #define BMA400_ID_REG_VAL 0x90 @@ -83,8 +95,51 @@ #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 +/* Generic interrupts register */ +#define BMA400_GEN1INT_CONFIG0 0x3f +#define BMA400_GEN2INT_CONFIG0 0x4A +#define BMA400_GEN_CONFIG1_OFF 0x01 +#define BMA400_GEN_CONFIG2_OFF 0x02 +#define BMA400_GEN_CONFIG3_OFF 0x03 +#define BMA400_GEN_CONFIG31_OFF 0x04 +#define BMA400_INT_GEN1_MSK BIT(2) +#define BMA400_INT_GEN2_MSK BIT(3) +#define BMA400_GEN_HYST_MSK GENMASK(1, 0) + +/* TAP config registers */ +#define BMA400_TAP_CONFIG 0x57 +#define BMA400_TAP_CONFIG1 0x58 +#define BMA400_S_TAP_MSK BIT(2) +#define BMA400_D_TAP_MSK BIT(3) +#define BMA400_INT_S_TAP_MSK BIT(10) +#define BMA400_INT_D_TAP_MSK BIT(11) +#define BMA400_TAP_SEN_MSK GENMASK(2, 0) +#define BMA400_TAP_TICSTH_MSK GENMASK(1, 0) +#define BMA400_TAP_QUIET_MSK GENMASK(3, 2) +#define BMA400_TAP_QUIETDT_MSK GENMASK(5, 4) +#define BMA400_TAP_TIM_LIST_LEN 4 + +/* + * BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before + * converting to micro values for +-2g range. + * + * For +-2g - 1 LSB = 0.976562 milli g = 0.009576 m/s^2 + * For +-4g - 1 LSB = 1.953125 milli g = 0.019153 m/s^2 + * For +-16g - 1 LSB = 7.8125 milli g = 0.076614 m/s^2 + * + * The raw value which is used to select the different ranges is determined + * by the first bit set position from the scale value, so BMA400_SCALE_MIN + * should be odd. + * + * Scale values for +-2g, +-4g, +-8g and +-16g are populated into bma400_scales + * array by left shifting BMA400_SCALE_MIN. + * e.g.: + * To select +-2g = 9577 << 0 = raw value to write is 0. + * To select +-8g = 9577 << 2 = raw value to write is 2. + * To select +-16g = 9577 << 3 = raw value to write is 3. + */ +#define BMA400_SCALE_MIN 9577 +#define BMA400_SCALE_MAX 76617 #define BMA400_NUM_REGULATORS 2 #define BMA400_VDD_REGULATOR 0 @@ -92,8 +147,7 @@ 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); +int bma400_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name); #endif diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index cc77f89c048b..ad8fce3e08cd 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -11,15 +11,25 @@ * - Create channel for sensor time */ +#include <linux/bitfield.h> #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 <linux/slab.h> + +#include <asm/unaligned.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #include "bma400.h" @@ -38,6 +48,27 @@ static int bma400_sample_freqs[14]; static const int bma400_osr_range[] = { 0, 1, 3 }; +static int tap_reset_timeout[BMA400_TAP_TIM_LIST_LEN] = { + 300000, + 400000, + 500000, + 600000 +}; + +static int tap_max2min_time[BMA400_TAP_TIM_LIST_LEN] = { + 30000, + 45000, + 60000, + 90000 +}; + +static int double_tap2_min_delay[BMA400_TAP_TIM_LIST_LEN] = { + 20000, + 40000, + 60000, + 80000 +}; + /* See the ACC_CONFIG0 section of the datasheet */ enum bma400_power_mode { POWER_MODE_SLEEP = 0x00, @@ -46,11 +77,24 @@ enum bma400_power_mode { POWER_MODE_INVALID = 0x03, }; +enum bma400_scan { + BMA400_ACCL_X, + BMA400_ACCL_Y, + BMA400_ACCL_Z, + BMA400_TEMP, +}; + struct bma400_sample_freq { int hz; int uhz; }; +enum bma400_activity { + BMA400_STILL, + BMA400_WALKING, + BMA400_RUNNING, +}; + struct bma400_data { struct device *dev; struct regmap *regmap; @@ -61,6 +105,20 @@ struct bma400_data { struct bma400_sample_freq sample_freq; int oversampling_ratio; int scale; + struct iio_trigger *trig; + int steps_enabled; + bool step_event_en; + bool activity_event_en; + unsigned int generic_event_en; + unsigned int tap_event_en_bitmask; + /* Correct time stamp alignment */ + struct { + __le16 buff[3]; + u8 temperature; + s64 ts __aligned(8); + } buffer __aligned(IIO_DMA_MINALIGN); + __le16 status; + __be16 duration; }; static bool bma400_is_writable_reg(struct device *dev, unsigned int reg) @@ -136,7 +194,7 @@ const struct regmap_config bma400_regmap_config = { .writeable_reg = bma400_is_writable_reg, .volatile_reg = bma400_is_volatile_reg, }; -EXPORT_SYMBOL(bma400_regmap_config); +EXPORT_SYMBOL_NS(bma400_regmap_config, IIO_BMA400); static const struct iio_mount_matrix * bma400_accel_get_mount_matrix(const struct iio_dev *indio_dev, @@ -152,7 +210,147 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = { { } }; -#define BMA400_ACC_CHANNEL(_axis) { \ +static const struct iio_event_spec bma400_step_detect_event = { + .type = IIO_EV_TYPE_CHANGE, + .dir = IIO_EV_DIR_NONE, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), +}; + +static const struct iio_event_spec bma400_activity_event = { + .type = IIO_EV_TYPE_CHANGE, + .dir = IIO_EV_DIR_NONE, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE), +}; + +static const struct iio_event_spec bma400_accel_event[] = { + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_SINGLETAP, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT), + }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_DOUBLETAP, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT) | + BIT(IIO_EV_INFO_TAP2_MIN_DELAY), + }, +}; + +static int usec_to_tapreg_raw(int usec, const int *time_list) +{ + int index; + + for (index = 0; index < BMA400_TAP_TIM_LIST_LEN; index++) { + if (usec == time_list[index]) + return index; + } + return -EINVAL; +} + +static ssize_t in_accel_gesture_tap_maxtomin_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bma400_data *data = iio_priv(indio_dev); + int ret, reg_val, raw, vals[2]; + + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, ®_val); + if (ret) + return ret; + + raw = FIELD_GET(BMA400_TAP_TICSTH_MSK, reg_val); + vals[0] = 0; + vals[1] = tap_max2min_time[raw]; + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals); +} + +static ssize_t in_accel_gesture_tap_maxtomin_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bma400_data *data = iio_priv(indio_dev); + int ret, val_int, val_fract, raw; + + ret = iio_str_to_fixpoint(buf, 100000, &val_int, &val_fract); + if (ret) + return ret; + + raw = usec_to_tapreg_raw(val_fract, tap_max2min_time); + if (raw < 0) + return -EINVAL; + + ret = regmap_update_bits(data->regmap, BMA400_TAP_CONFIG1, + BMA400_TAP_TICSTH_MSK, + FIELD_PREP(BMA400_TAP_TICSTH_MSK, raw)); + if (ret) + return ret; + + return len; +} + +static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_maxtomin_time, 0); + +/* + * Tap interrupts works with 200 Hz input data rate and the time based tap + * controls are in the terms of data samples so the below calculation is + * used to convert the configuration values into seconds. + * e.g.: + * 60 data samples * 0.005 ms = 0.3 seconds. + * 80 data samples * 0.005 ms = 0.4 seconds. + */ + +/* quiet configuration values in seconds */ +static IIO_CONST_ATTR(in_accel_gesture_tap_reset_timeout_available, + "0.3 0.4 0.5 0.6"); + +/* tics_th configuration values in seconds */ +static IIO_CONST_ATTR(in_accel_gesture_tap_maxtomin_time_available, + "0.03 0.045 0.06 0.09"); + +/* quiet_dt configuration values in seconds */ +static IIO_CONST_ATTR(in_accel_gesture_doubletap_tap2_min_delay_available, + "0.02 0.04 0.06 0.08"); + +/* List of sensitivity values available to configure tap interrupts */ +static IIO_CONST_ATTR(in_accel_gesture_tap_value_available, "0 1 2 3 4 5 6 7"); + +static struct attribute *bma400_event_attributes[] = { + &iio_const_attr_in_accel_gesture_tap_value_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_tap_reset_timeout_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_tap_maxtomin_time_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_doubletap_tap2_min_delay_available.dev_attr.attr, + &iio_dev_attr_in_accel_gesture_tap_maxtomin_time.dev_attr.attr, + NULL +}; + +static const struct attribute_group bma400_event_attribute_group = { + .attrs = bma400_event_attributes, +}; + +#define BMA400_ACC_CHANNEL(_index, _axis) { \ .type = IIO_ACCEL, \ .modified = 1, \ .channel2 = IIO_MOD_##_axis, \ @@ -164,17 +362,55 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = { BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .ext_info = bma400_ext_info, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .event_spec = bma400_accel_event, \ + .num_event_specs = ARRAY_SIZE(bma400_accel_event) \ +} + +#define BMA400_ACTIVITY_CHANNEL(_chan2) { \ + .type = IIO_ACTIVITY, \ + .modified = 1, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = -1, /* No buffer support */ \ + .event_spec = &bma400_activity_event, \ + .num_event_specs = 1, \ } static const struct iio_chan_spec bma400_channels[] = { - BMA400_ACC_CHANNEL(X), - BMA400_ACC_CHANNEL(Y), - BMA400_ACC_CHANNEL(Z), + BMA400_ACC_CHANNEL(0, X), + BMA400_ACC_CHANNEL(1, Y), + BMA400_ACC_CHANNEL(2, Z), { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = 3, + .scan_type = { + .sign = 's', + .realbits = 8, + .storagebits = 8, + .endianness = IIO_LE, + }, + }, + { + .type = IIO_STEPS, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_ENABLE), + .scan_index = -1, /* No buffer support */ + .event_spec = &bma400_step_detect_event, + .num_event_specs = 1, }, + BMA400_ACTIVITY_CHANNEL(IIO_MOD_STILL), + BMA400_ACTIVITY_CHANNEL(IIO_MOD_WALKING), + BMA400_ACTIVITY_CHANNEL(IIO_MOD_RUNNING), + IIO_CHAN_SOFT_TIMESTAMP(4), }; static int bma400_get_temp_reg(struct bma400_data *data, int *val, int *val2) @@ -542,6 +778,40 @@ static int bma400_set_power_mode(struct bma400_data *data, return 0; } +static int bma400_enable_steps(struct bma400_data *data, int val) +{ + int ret; + + if (data->steps_enabled == val) + return 0; + + ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG, + BMA400_STEP_INT_MSK, + FIELD_PREP(BMA400_STEP_INT_MSK, val ? 1 : 0)); + if (ret) + return ret; + data->steps_enabled = val; + return ret; +} + +static int bma400_get_steps_reg(struct bma400_data *data, int *val) +{ + u8 *steps_raw; + int ret; + + steps_raw = kmalloc(BMA400_STEP_RAW_LEN, GFP_KERNEL); + if (!steps_raw) + return -ENOMEM; + + ret = regmap_bulk_read(data->regmap, BMA400_STEP_CNT0_REG, + steps_raw, BMA400_STEP_RAW_LEN); + if (ret) + return ret; + *val = get_unaligned_le24(steps_raw); + kfree(steps_raw); + return IIO_VAL_INT; +} + static void bma400_init_tables(void) { int raw; @@ -560,6 +830,40 @@ static void bma400_init_tables(void) } } +static void bma400_regulators_disable(void *data_ptr) +{ + struct bma400_data *data = data_ptr; + + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); +} + +static void bma400_power_disable(void *data_ptr) +{ + struct bma400_data *data = data_ptr; + int ret; + + mutex_lock(&data->mutex); + ret = bma400_set_power_mode(data, POWER_MODE_SLEEP); + mutex_unlock(&data->mutex); + if (ret) + dev_warn(data->dev, "Failed to put device into sleep mode (%pe)\n", + ERR_PTR(ret)); +} + +static enum iio_modifier bma400_act_to_mod(enum bma400_activity activity) +{ + switch (activity) { + case BMA400_STILL: + return IIO_MOD_STILL; + case BMA400_WALKING: + return IIO_MOD_WALKING; + case BMA400_RUNNING: + return IIO_MOD_RUNNING; + default: + return IIO_NO_MOD; + } +} + static int bma400_init(struct bma400_data *data) { unsigned int val; @@ -569,13 +873,12 @@ static int bma400_init(struct bma400_data *data) 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; + return ret; } if (val != BMA400_ID_REG_VAL) { dev_err(data->dev, "Chip ID mismatch\n"); - ret = -ENODEV; - goto out; + return -ENODEV; } data->regulators[BMA400_VDD_REGULATOR].supply = "vdd"; @@ -589,27 +892,31 @@ static int bma400_init(struct bma400_data *data) "Failed to get regulators: %d\n", ret); - goto out; + return ret; } 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; + return ret; } + ret = devm_add_action_or_reset(data->dev, bma400_regulators_disable, data); + if (ret) + return ret; + ret = bma400_get_power_mode(data); if (ret) { dev_err(data->dev, "Failed to get the initial power-mode\n"); - goto err_reg_disable; + return ret; } 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; + return ret; } /* * TODO: The datasheet waits 1500us here in the example, but @@ -618,20 +925,28 @@ static int bma400_init(struct bma400_data *data) usleep_range(1500, 2000); } + ret = devm_add_action_or_reset(data->dev, bma400_power_disable, data); + if (ret) + return ret; + bma400_init_tables(); ret = bma400_get_accel_output_data_rate(data); if (ret) - goto err_reg_disable; + return ret; ret = bma400_get_accel_oversampling_ratio(data); if (ret) - goto err_reg_disable; + return ret; ret = bma400_get_accel_scale(data); if (ret) - goto err_reg_disable; + return ret; + /* Configure INT1 pin to open drain */ + ret = regmap_write(data->regmap, BMA400_INT_IO_CTRL_REG, 0x06); + if (ret) + return ret; /* * Once the interrupt engine is supported we might use the * data_src_reg, but for now ensure this is set to the @@ -639,12 +954,6 @@ static int bma400_init(struct bma400_data *data) * 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, @@ -652,14 +961,37 @@ static int bma400_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct bma400_data *data = iio_priv(indio_dev); + unsigned int activity; 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; + switch (chan->type) { + case IIO_TEMP: + mutex_lock(&data->mutex); + ret = bma400_get_temp_reg(data, val, val2); + mutex_unlock(&data->mutex); + return ret; + case IIO_STEPS: + return bma400_get_steps_reg(data, val); + case IIO_ACTIVITY: + ret = regmap_read(data->regmap, BMA400_STEP_STAT_REG, + &activity); + if (ret) + return ret; + /* + * The device does not support confidence value levels, + * so we will always have 100% for current activity and + * 0% for the others. + */ + if (chan->channel2 == bma400_act_to_mod(activity)) + *val = 100; + else + *val = 0; + return IIO_VAL_INT; + default: + return -EINVAL; + } case IIO_CHAN_INFO_RAW: mutex_lock(&data->mutex); ret = bma400_get_accel_reg(data, chan, val); @@ -700,6 +1032,9 @@ static int bma400_read_raw(struct iio_dev *indio_dev, *val = data->oversampling_ratio; return IIO_VAL_INT; + case IIO_CHAN_INFO_ENABLE: + *val = data->steps_enabled; + return IIO_VAL_INT; default: return -EINVAL; } @@ -765,6 +1100,11 @@ static int bma400_write_raw(struct iio_dev *indio_dev, ret = bma400_set_accel_oversampling_ratio(data, val); mutex_unlock(&data->mutex); return ret; + case IIO_CHAN_INFO_ENABLE: + mutex_lock(&data->mutex); + ret = bma400_enable_steps(data, val); + mutex_unlock(&data->mutex); + return ret; default: return -EINVAL; } @@ -781,19 +1121,610 @@ static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: return IIO_VAL_INT; + case IIO_CHAN_INFO_ENABLE: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int bma400_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct bma400_data *data = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_ACCEL: + switch (dir) { + case IIO_EV_DIR_RISING: + return FIELD_GET(BMA400_INT_GEN1_MSK, + data->generic_event_en); + case IIO_EV_DIR_FALLING: + return FIELD_GET(BMA400_INT_GEN2_MSK, + data->generic_event_en); + case IIO_EV_DIR_SINGLETAP: + return FIELD_GET(BMA400_S_TAP_MSK, + data->tap_event_en_bitmask); + case IIO_EV_DIR_DOUBLETAP: + return FIELD_GET(BMA400_D_TAP_MSK, + data->tap_event_en_bitmask); + default: + return -EINVAL; + } + case IIO_STEPS: + return data->step_event_en; + case IIO_ACTIVITY: + return data->activity_event_en; default: return -EINVAL; } } +static int bma400_steps_event_enable(struct bma400_data *data, int state) +{ + int ret; + + ret = bma400_enable_steps(data, 1); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, BMA400_INT12_MAP_REG, + BMA400_STEP_INT_MSK, + FIELD_PREP(BMA400_STEP_INT_MSK, + state)); + if (ret) + return ret; + data->step_event_en = state; + return 0; +} + +static int bma400_activity_event_en(struct bma400_data *data, + enum iio_event_direction dir, + int state) +{ + int ret, reg, msk, value; + int field_value = 0; + + switch (dir) { + case IIO_EV_DIR_RISING: + reg = BMA400_GEN1INT_CONFIG0; + msk = BMA400_INT_GEN1_MSK; + value = 2; + set_mask_bits(&field_value, BMA400_INT_GEN1_MSK, + FIELD_PREP(BMA400_INT_GEN1_MSK, state)); + break; + case IIO_EV_DIR_FALLING: + reg = BMA400_GEN2INT_CONFIG0; + msk = BMA400_INT_GEN2_MSK; + value = 0; + set_mask_bits(&field_value, BMA400_INT_GEN2_MSK, + FIELD_PREP(BMA400_INT_GEN2_MSK, state)); + break; + default: + return -EINVAL; + } + + /* Enabling all axis for interrupt evaluation */ + ret = regmap_write(data->regmap, reg, 0xF8); + if (ret) + return ret; + + /* OR combination of all axis for interrupt evaluation */ + ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG1_OFF, value); + if (ret) + return ret; + + /* Initial value to avoid interrupts while enabling*/ + ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG2_OFF, 0x0A); + if (ret) + return ret; + + /* Initial duration value to avoid interrupts while enabling*/ + ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG31_OFF, 0x0F); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG, msk, + field_value); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG, msk, + field_value); + if (ret) + return ret; + + set_mask_bits(&data->generic_event_en, msk, field_value); + return 0; +} + +static int bma400_tap_event_en(struct bma400_data *data, + enum iio_event_direction dir, int state) +{ + unsigned int mask, field_value; + int ret; + + /* + * Tap interrupts can be configured only in normal mode. + * See table in section 4.3 "Power modes - performance modes" of + * datasheet v1.2. + */ + if (data->power_mode != POWER_MODE_NORMAL) + return -EINVAL; + + /* + * Tap interrupts are operating with a data rate of 200Hz. + * See section 4.7 "Tap sensing interrupt" in datasheet v1.2. + */ + if (data->sample_freq.hz != 200 && state) { + dev_err(data->dev, "Invalid data rate for tap interrupts.\n"); + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, BMA400_INT12_MAP_REG, + BMA400_S_TAP_MSK, + FIELD_PREP(BMA400_S_TAP_MSK, state)); + if (ret) + return ret; + + switch (dir) { + case IIO_EV_DIR_SINGLETAP: + mask = BMA400_S_TAP_MSK; + set_mask_bits(&field_value, BMA400_S_TAP_MSK, + FIELD_PREP(BMA400_S_TAP_MSK, state)); + break; + case IIO_EV_DIR_DOUBLETAP: + mask = BMA400_D_TAP_MSK; + set_mask_bits(&field_value, BMA400_D_TAP_MSK, + FIELD_PREP(BMA400_D_TAP_MSK, state)); + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG, mask, + field_value); + if (ret) + return ret; + + set_mask_bits(&data->tap_event_en_bitmask, mask, field_value); + + return 0; +} + +static int bma400_disable_adv_interrupt(struct bma400_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, BMA400_INT_CONFIG0_REG, 0); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMA400_INT_CONFIG1_REG, 0); + if (ret) + return ret; + + data->tap_event_en_bitmask = 0; + data->generic_event_en = 0; + data->step_event_en = false; + data->activity_event_en = false; + + return 0; +} + +static int bma400_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + switch (chan->type) { + case IIO_ACCEL: + switch (type) { + case IIO_EV_TYPE_MAG: + mutex_lock(&data->mutex); + ret = bma400_activity_event_en(data, dir, state); + mutex_unlock(&data->mutex); + return ret; + case IIO_EV_TYPE_GESTURE: + mutex_lock(&data->mutex); + ret = bma400_tap_event_en(data, dir, state); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } + case IIO_STEPS: + mutex_lock(&data->mutex); + ret = bma400_steps_event_enable(data, state); + mutex_unlock(&data->mutex); + return ret; + case IIO_ACTIVITY: + mutex_lock(&data->mutex); + if (!data->step_event_en) { + ret = bma400_steps_event_enable(data, true); + if (ret) { + mutex_unlock(&data->mutex); + return ret; + } + } + data->activity_event_en = state; + mutex_unlock(&data->mutex); + return 0; + default: + return -EINVAL; + } +} + +static int get_gen_config_reg(enum iio_event_direction dir) +{ + switch (dir) { + case IIO_EV_DIR_FALLING: + return BMA400_GEN2INT_CONFIG0; + case IIO_EV_DIR_RISING: + return BMA400_GEN1INT_CONFIG0; + default: + return -EINVAL; + } +} + +static int bma400_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct bma400_data *data = iio_priv(indio_dev); + int ret, reg, reg_val, raw; + + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (type) { + case IIO_EV_TYPE_MAG: + reg = get_gen_config_reg(dir); + if (reg < 0) + return -EINVAL; + + *val2 = 0; + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(data->regmap, + reg + BMA400_GEN_CONFIG2_OFF, + val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_EV_INFO_PERIOD: + mutex_lock(&data->mutex); + ret = regmap_bulk_read(data->regmap, + reg + BMA400_GEN_CONFIG3_OFF, + &data->duration, + sizeof(data->duration)); + if (ret) { + mutex_unlock(&data->mutex); + return ret; + } + *val = be16_to_cpu(data->duration); + mutex_unlock(&data->mutex); + return IIO_VAL_INT; + case IIO_EV_INFO_HYSTERESIS: + ret = regmap_read(data->regmap, reg, val); + if (ret) + return ret; + *val = FIELD_GET(BMA400_GEN_HYST_MSK, *val); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG, + ®_val); + if (ret) + return ret; + + *val = FIELD_GET(BMA400_TAP_SEN_MSK, reg_val); + return IIO_VAL_INT; + case IIO_EV_INFO_RESET_TIMEOUT: + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, + ®_val); + if (ret) + return ret; + + raw = FIELD_GET(BMA400_TAP_QUIET_MSK, reg_val); + *val = 0; + *val2 = tap_reset_timeout[raw]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_EV_INFO_TAP2_MIN_DELAY: + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, + ®_val); + if (ret) + return ret; + + raw = FIELD_GET(BMA400_TAP_QUIETDT_MSK, reg_val); + *val = 0; + *val2 = double_tap2_min_delay[raw]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int bma400_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct bma400_data *data = iio_priv(indio_dev); + int reg, ret, raw; + + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (type) { + case IIO_EV_TYPE_MAG: + reg = get_gen_config_reg(dir); + if (reg < 0) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 1 || val > 255) + return -EINVAL; + + return regmap_write(data->regmap, + reg + BMA400_GEN_CONFIG2_OFF, + val); + case IIO_EV_INFO_PERIOD: + if (val < 1 || val > 65535) + return -EINVAL; + + mutex_lock(&data->mutex); + put_unaligned_be16(val, &data->duration); + ret = regmap_bulk_write(data->regmap, + reg + BMA400_GEN_CONFIG3_OFF, + &data->duration, + sizeof(data->duration)); + mutex_unlock(&data->mutex); + return ret; + case IIO_EV_INFO_HYSTERESIS: + if (val < 0 || val > 3) + return -EINVAL; + + return regmap_update_bits(data->regmap, reg, + BMA400_GEN_HYST_MSK, + FIELD_PREP(BMA400_GEN_HYST_MSK, + val)); + default: + return -EINVAL; + } + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0 || val > 7) + return -EINVAL; + + return regmap_update_bits(data->regmap, + BMA400_TAP_CONFIG, + BMA400_TAP_SEN_MSK, + FIELD_PREP(BMA400_TAP_SEN_MSK, + val)); + case IIO_EV_INFO_RESET_TIMEOUT: + raw = usec_to_tapreg_raw(val2, tap_reset_timeout); + if (raw < 0) + return -EINVAL; + + return regmap_update_bits(data->regmap, + BMA400_TAP_CONFIG1, + BMA400_TAP_QUIET_MSK, + FIELD_PREP(BMA400_TAP_QUIET_MSK, + raw)); + case IIO_EV_INFO_TAP2_MIN_DELAY: + raw = usec_to_tapreg_raw(val2, double_tap2_min_delay); + if (raw < 0) + return -EINVAL; + + return regmap_update_bits(data->regmap, + BMA400_TAP_CONFIG1, + BMA400_TAP_QUIETDT_MSK, + FIELD_PREP(BMA400_TAP_QUIETDT_MSK, + raw)); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int bma400_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct bma400_data *data = iio_priv(indio_dev); + int ret; + + ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG, + BMA400_INT_DRDY_MSK, + FIELD_PREP(BMA400_INT_DRDY_MSK, state)); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG, + BMA400_INT_DRDY_MSK, + FIELD_PREP(BMA400_INT_DRDY_MSK, state)); +} + +static const unsigned long bma400_avail_scan_masks[] = { + BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z), + BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z) + | BIT(BMA400_TEMP), + 0 +}; + 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, + .read_event_config = bma400_read_event_config, + .write_event_config = bma400_write_event_config, + .write_event_value = bma400_write_event_value, + .read_event_value = bma400_read_event_value, + .event_attrs = &bma400_event_attribute_group, +}; + +static const struct iio_trigger_ops bma400_trigger_ops = { + .set_trigger_state = &bma400_data_rdy_trigger_set_state, + .validate_device = &iio_trigger_validate_own_device, }; -int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) +static irqreturn_t bma400_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bma400_data *data = iio_priv(indio_dev); + int ret, temp; + + /* Lock to protect the data->buffer */ + mutex_lock(&data->mutex); + + /* bulk read six registers, with the base being the LSB register */ + ret = regmap_bulk_read(data->regmap, BMA400_X_AXIS_LSB_REG, + &data->buffer.buff, sizeof(data->buffer.buff)); + if (ret) + goto unlock_err; + + if (test_bit(BMA400_TEMP, indio_dev->active_scan_mask)) { + ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &temp); + if (ret) + goto unlock_err; + + data->buffer.temperature = temp; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + iio_get_time_ns(indio_dev)); + + mutex_unlock(&data->mutex); + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; + +unlock_err: + mutex_unlock(&data->mutex); + return IRQ_NONE; +} + +static irqreturn_t bma400_interrupt(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bma400_data *data = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + unsigned int act, ev_dir = IIO_EV_DIR_NONE; + int ret; + + /* Lock to protect the data->status */ + mutex_lock(&data->mutex); + ret = regmap_bulk_read(data->regmap, BMA400_INT_STAT0_REG, + &data->status, + sizeof(data->status)); + /* + * if none of the bit is set in the status register then it is + * spurious interrupt. + */ + if (ret || !data->status) + goto unlock_err; + + /* + * Disable all advance interrupts if interrupt engine overrun occurs. + * See section 4.7 "Interrupt engine overrun" in datasheet v1.2. + */ + if (FIELD_GET(BMA400_INT_ENG_OVRUN_MSK, le16_to_cpu(data->status))) { + bma400_disable_adv_interrupt(data); + dev_err(data->dev, "Interrupt engine overrun\n"); + goto unlock_err; + } + + if (FIELD_GET(BMA400_INT_S_TAP_MSK, le16_to_cpu(data->status))) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_SINGLETAP), + timestamp); + + if (FIELD_GET(BMA400_INT_D_TAP_MSK, le16_to_cpu(data->status))) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_DOUBLETAP), + timestamp); + + if (FIELD_GET(BMA400_INT_GEN1_MSK, le16_to_cpu(data->status))) + ev_dir = IIO_EV_DIR_RISING; + + if (FIELD_GET(BMA400_INT_GEN2_MSK, le16_to_cpu(data->status))) + ev_dir = IIO_EV_DIR_FALLING; + + if (ev_dir != IIO_EV_DIR_NONE) { + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_MAG, ev_dir), + timestamp); + } + + if (FIELD_GET(BMA400_STEP_STAT_MASK, le16_to_cpu(data->status))) { + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, + IIO_EV_TYPE_CHANGE, + IIO_EV_DIR_NONE), + timestamp); + + if (data->activity_event_en) { + ret = regmap_read(data->regmap, BMA400_STEP_STAT_REG, + &act); + if (ret) + goto unlock_err; + + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACTIVITY, 0, + bma400_act_to_mod(act), + IIO_EV_TYPE_CHANGE, + IIO_EV_DIR_NONE), + timestamp); + } + } + + if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) { + mutex_unlock(&data->mutex); + iio_trigger_poll_chained(data->trig); + return IRQ_HANDLED; + } + + mutex_unlock(&data->mutex); + return IRQ_HANDLED; + +unlock_err: + mutex_unlock(&data->mutex); + return IRQ_NONE; +} + +int bma400_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name) { struct iio_dev *indio_dev; struct bma400_data *data; @@ -811,43 +1742,54 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) if (ret) return ret; - ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation); + ret = iio_read_mount_matrix(dev, &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->available_scan_masks = bma400_avail_scan_masks; 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; + if (irq > 0) { + data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->trig) + return -ENOMEM; - mutex_lock(&data->mutex); - ret = bma400_set_power_mode(data, POWER_MODE_SLEEP); - mutex_unlock(&data->mutex); + data->trig->ops = &bma400_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); - regulator_bulk_disable(ARRAY_SIZE(data->regulators), - data->regulators); + ret = devm_iio_trigger_register(data->dev, data->trig); + if (ret) + return dev_err_probe(data->dev, ret, + "iio trigger register fail\n"); + + indio_dev->trig = iio_trigger_get(data->trig); + ret = devm_request_threaded_irq(dev, irq, NULL, + &bma400_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return dev_err_probe(data->dev, ret, + "request irq %d failed\n", irq); + } - iio_device_unregister(indio_dev); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &bma400_trigger_handler, NULL); + if (ret) + return dev_err_probe(data->dev, ret, + "iio triggered buffer setup failed\n"); - return ret; + return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL(bma400_remove); +EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400); MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); +MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.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 index 9dcb7cc9996e..1ba2a982ea73 100644 --- a/drivers/iio/accel/bma400_i2c.c +++ b/drivers/iio/accel/bma400_i2c.c @@ -24,12 +24,7 @@ static int bma400_i2c_probe(struct i2c_client *client, 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); + return bma400_probe(&client->dev, regmap, client->irq, id->name); } static const struct i2c_device_id bma400_i2c_ids[] = { @@ -50,7 +45,6 @@ static struct i2c_driver bma400_i2c_driver = { .of_match_table = bma400_of_i2c_match, }, .probe = bma400_i2c_probe, - .remove = bma400_i2c_remove, .id_table = bma400_i2c_ids, }; @@ -59,3 +53,4 @@ module_i2c_driver(bma400_i2c_driver); MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (I2C)"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMA400); diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c new file mode 100644 index 000000000000..ec13c044b304 --- /dev/null +++ b/drivers/iio/accel/bma400_spi.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPI IIO driver for Bosch BMA400 triaxial acceleration sensor. + * + * Copyright 2020 Dan Robertson <dan@dlrobertson.com> + * + */ +#include <linux/bits.h> +#include <linux/init.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "bma400.h" + +#define BMA400_MAX_SPI_READ 2 +#define BMA400_SPI_READ_BUFFER_SIZE (BMA400_MAX_SPI_READ + 1) + +static int bma400_regmap_spi_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + u8 result[BMA400_SPI_READ_BUFFER_SIZE]; + ssize_t status; + + if (val_size > BMA400_MAX_SPI_READ) + return -EINVAL; + + status = spi_write_then_read(spi, reg, 1, result, val_size + 1); + if (status) + return status; + + /* + * From the BMA400 datasheet: + * + * > For a basic read operation two bytes have to be read and the first + * > has to be dropped and the second byte must be interpreted. + */ + memcpy(val, result + 1, val_size); + + return 0; +} + +static int bma400_regmap_spi_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + return spi_write(spi, data, count); +} + +static struct regmap_bus bma400_regmap_bus = { + .read = bma400_regmap_spi_read, + .write = bma400_regmap_spi_write, + .read_flag_mask = BIT(7), + .max_raw_read = BMA400_MAX_SPI_READ, +}; + +static int bma400_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + unsigned int val; + int ret; + + regmap = devm_regmap_init(&spi->dev, &bma400_regmap_bus, + &spi->dev, &bma400_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "failed to create regmap\n"); + return PTR_ERR(regmap); + } + + /* + * Per the bma400 datasheet, the first SPI read may + * return garbage. As the datasheet recommends, the + * chip ID register will be read here and checked + * again in the following probe. + */ + ret = regmap_read(regmap, BMA400_CHIP_ID_REG, &val); + if (ret) + dev_err(&spi->dev, "Failed to read chip id register\n"); + + return bma400_probe(&spi->dev, regmap, spi->irq, id->name); +} + +static const struct spi_device_id bma400_spi_ids[] = { + { "bma400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, bma400_spi_ids); + +static const struct of_device_id bma400_of_spi_match[] = { + { .compatible = "bosch,bma400" }, + { } +}; +MODULE_DEVICE_TABLE(of, bma400_of_spi_match); + +static struct spi_driver bma400_spi_driver = { + .driver = { + .name = "bma400", + .of_match_table = bma400_of_spi_match, + }, + .probe = bma400_spi_probe, + .id_table = bma400_spi_ids, +}; + +module_spi_driver(bma400_spi_driver); +MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>"); +MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (SPI)"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMA400); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 121b4e89f038..92f8b139acce 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1,13 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: - * - BMC150 - * - BMI055 - * - BMA255 - * - BMA250E - * - BMA222E - * - BMA280 - * + * 3-axis accelerometer driver supporting many Bosch-Sensortec chips * Copyright (c) 2014, Intel Corporation. */ @@ -17,6 +10,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/acpi.h> +#include <linux/of_irq.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/iio/iio.h> @@ -27,6 +21,7 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include "bmc150-accel.h" @@ -63,12 +58,18 @@ #define BMC150_ACCEL_RESET_VAL 0xB6 #define BMC150_ACCEL_REG_INT_MAP_0 0x19 -#define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2) +#define BMC150_ACCEL_INT_MAP_0_BIT_INT1_SLOPE BIT(2) #define BMC150_ACCEL_REG_INT_MAP_1 0x1A -#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0) -#define BMC150_ACCEL_INT_MAP_1_BIT_FWM BIT(1) -#define BMC150_ACCEL_INT_MAP_1_BIT_FFULL BIT(2) +#define BMC150_ACCEL_INT_MAP_1_BIT_INT1_DATA BIT(0) +#define BMC150_ACCEL_INT_MAP_1_BIT_INT1_FWM BIT(1) +#define BMC150_ACCEL_INT_MAP_1_BIT_INT1_FFULL BIT(2) +#define BMC150_ACCEL_INT_MAP_1_BIT_INT2_FFULL BIT(5) +#define BMC150_ACCEL_INT_MAP_1_BIT_INT2_FWM BIT(6) +#define BMC150_ACCEL_INT_MAP_1_BIT_INT2_DATA BIT(7) + +#define BMC150_ACCEL_REG_INT_MAP_2 0x1B +#define BMC150_ACCEL_INT_MAP_2_BIT_INT2_SLOPE BIT(2) #define BMC150_ACCEL_REG_INT_RST_LATCH 0x21 #define BMC150_ACCEL_INT_MODE_LATCH_RESET 0x80 @@ -87,6 +88,7 @@ #define BMC150_ACCEL_REG_INT_OUT_CTRL 0x20 #define BMC150_ACCEL_INT_OUT_CTRL_INT1_LVL BIT(0) +#define BMC150_ACCEL_INT_OUT_CTRL_INT2_LVL BIT(2) #define BMC150_ACCEL_REG_INT_5 0x27 #define BMC150_ACCEL_SLOPE_DUR_MASK 0x03 @@ -155,50 +157,6 @@ struct bmc150_accel_chip_info { const struct bmc150_scale_info scale_table[4]; }; -struct bmc150_accel_interrupt { - const struct bmc150_accel_interrupt_info *info; - atomic_t users; -}; - -struct bmc150_accel_trigger { - struct bmc150_accel_data *data; - struct iio_trigger *indio_trig; - int (*setup)(struct bmc150_accel_trigger *t, bool state); - int intr; - bool enabled; -}; - -enum bmc150_accel_interrupt_id { - BMC150_ACCEL_INT_DATA_READY, - BMC150_ACCEL_INT_ANY_MOTION, - BMC150_ACCEL_INT_WATERMARK, - BMC150_ACCEL_INTERRUPTS, -}; - -enum bmc150_accel_trigger_id { - BMC150_ACCEL_TRIGGER_DATA_READY, - BMC150_ACCEL_TRIGGER_ANY_MOTION, - BMC150_ACCEL_TRIGGERS, -}; - -struct bmc150_accel_data { - struct regmap *regmap; - int irq; - struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS]; - struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; - struct mutex mutex; - u8 fifo_mode, watermark; - s16 buffer[8]; - u8 bw_bits; - u32 slope_dur; - u32 slope_thres; - u32 range; - int ev_enable_state; - int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ - const struct bmc150_accel_chip_info *chip_info; - struct iio_mount_matrix orientation; -}; - static const struct { int val; int val2; @@ -212,7 +170,7 @@ static const struct { {1000, 0, 0x0E}, {2000, 0, 0x0F} }; -static const struct { +static __maybe_unused const struct { int bw_bits; int msec; } bmc150_accel_sample_upd_time[] = { {0x08, 64}, @@ -245,7 +203,7 @@ const struct regmap_config bmc150_regmap_conf = { .val_bits = 8, .max_register = 0x3f, }; -EXPORT_SYMBOL_GPL(bmc150_regmap_conf); +EXPORT_SYMBOL_NS_GPL(bmc150_regmap_conf, IIO_BMC150); static int bmc150_accel_set_mode(struct bmc150_accel_data *data, enum bmc150_power_modes mode, @@ -378,7 +336,7 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -387,9 +345,6 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) if (ret < 0) { dev_err(dev, "Failed: %s for %d\n", __func__, on); - if (on) - pm_runtime_put_noidle(dev); - return ret; } @@ -402,21 +357,175 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) } #endif -static const struct bmc150_accel_interrupt_info { +#ifdef CONFIG_ACPI +/* + * Support for getting accelerometer information from BOSC0200 ACPI nodes. + * + * There are 2 variants of the BOSC0200 ACPI node. Some 2-in-1s with 360 degree + * hinges declare 2 I2C ACPI-resources for 2 accelerometers, 1 in the display + * and 1 in the base of the 2-in-1. On these 2-in-1s the ROMS ACPI object + * contains the mount-matrix for the sensor in the display and ROMK contains + * the mount-matrix for the sensor in the base. On devices using a single + * sensor there is a ROTM ACPI object which contains the mount-matrix. + * + * Here is an incomplete list of devices known to use 1 of these setups: + * + * Yoga devices with 2 accelerometers using ROMS + ROMK for the mount-matrices: + * Lenovo Thinkpad Yoga 11e 3th gen + * Lenovo Thinkpad Yoga 11e 4th gen + * + * Tablets using a single accelerometer using ROTM for the mount-matrix: + * Chuwi Hi8 Pro (CWI513) + * Chuwi Vi8 Plus (CWI519) + * Chuwi Hi13 + * Irbis TW90 + * Jumper EZpad mini 3 + * Onda V80 plus + * Predia Basic Tablet + */ +static bool bmc150_apply_bosc0200_acpi_orientation(struct device *dev, + struct iio_mount_matrix *orientation) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct acpi_device *adev = ACPI_COMPANION(dev); + char *name, *alt_name, *label, *str; + union acpi_object *obj, *elements; + acpi_status status; + int i, j, val[3]; + + if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) { + alt_name = "ROMK"; + label = "accel-base"; + } else { + alt_name = "ROMS"; + label = "accel-display"; + } + + if (acpi_has_method(adev->handle, "ROTM")) { + name = "ROTM"; + } else if (acpi_has_method(adev->handle, alt_name)) { + name = alt_name; + indio_dev->label = label; + } else { + return false; + } + + status = acpi_evaluate_object(adev->handle, name, NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_warn(dev, "Failed to get ACPI mount matrix: %d\n", status); + return false; + } + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) + goto unknown_format; + + elements = obj->package.elements; + for (i = 0; i < 3; i++) { + if (elements[i].type != ACPI_TYPE_STRING) + goto unknown_format; + + str = elements[i].string.pointer; + if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) + goto unknown_format; + + for (j = 0; j < 3; j++) { + switch (val[j]) { + case -1: str = "-1"; break; + case 0: str = "0"; break; + case 1: str = "1"; break; + default: goto unknown_format; + } + orientation->rotation[i * 3 + j] = str; + } + } + + kfree(buffer.pointer); + return true; + +unknown_format: + dev_warn(dev, "Unknown ACPI mount matrix format, ignoring\n"); + kfree(buffer.pointer); + return false; +} + +static bool bmc150_apply_dual250e_acpi_orientation(struct device *dev, + struct iio_mount_matrix *orientation) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + if (strcmp(dev_name(dev), "i2c-DUAL250E:base") == 0) + indio_dev->label = "accel-base"; + else + indio_dev->label = "accel-display"; + + return false; /* DUAL250E fwnodes have no mount matrix info */ +} + +static bool bmc150_apply_acpi_orientation(struct device *dev, + struct iio_mount_matrix *orientation) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + + if (adev && acpi_dev_hid_uid_match(adev, "BOSC0200", NULL)) + return bmc150_apply_bosc0200_acpi_orientation(dev, orientation); + + if (adev && acpi_dev_hid_uid_match(adev, "DUAL250E", NULL)) + return bmc150_apply_dual250e_acpi_orientation(dev, orientation); + + return false; +} +#else +static bool bmc150_apply_acpi_orientation(struct device *dev, + struct iio_mount_matrix *orientation) +{ + return false; +} +#endif + +struct bmc150_accel_interrupt_info { u8 map_reg; u8 map_bitmask; u8 en_reg; u8 en_bitmask; -} bmc150_accel_interrupts[BMC150_ACCEL_INTERRUPTS] = { +}; + +static const struct bmc150_accel_interrupt_info +bmc150_accel_interrupts_int1[BMC150_ACCEL_INTERRUPTS] = { { /* data ready interrupt */ .map_reg = BMC150_ACCEL_REG_INT_MAP_1, - .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_DATA, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_INT1_DATA, .en_reg = BMC150_ACCEL_REG_INT_EN_1, .en_bitmask = BMC150_ACCEL_INT_EN_BIT_DATA_EN, }, { /* motion interrupt */ .map_reg = BMC150_ACCEL_REG_INT_MAP_0, - .map_bitmask = BMC150_ACCEL_INT_MAP_0_BIT_SLOPE, + .map_bitmask = BMC150_ACCEL_INT_MAP_0_BIT_INT1_SLOPE, + .en_reg = BMC150_ACCEL_REG_INT_EN_0, + .en_bitmask = BMC150_ACCEL_INT_EN_BIT_SLP_X | + BMC150_ACCEL_INT_EN_BIT_SLP_Y | + BMC150_ACCEL_INT_EN_BIT_SLP_Z + }, + { /* fifo watermark interrupt */ + .map_reg = BMC150_ACCEL_REG_INT_MAP_1, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_INT1_FWM, + .en_reg = BMC150_ACCEL_REG_INT_EN_1, + .en_bitmask = BMC150_ACCEL_INT_EN_BIT_FWM_EN, + }, +}; + +static const struct bmc150_accel_interrupt_info +bmc150_accel_interrupts_int2[BMC150_ACCEL_INTERRUPTS] = { + { /* data ready interrupt */ + .map_reg = BMC150_ACCEL_REG_INT_MAP_1, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_INT2_DATA, + .en_reg = BMC150_ACCEL_REG_INT_EN_1, + .en_bitmask = BMC150_ACCEL_INT_EN_BIT_DATA_EN, + }, + { /* motion interrupt */ + .map_reg = BMC150_ACCEL_REG_INT_MAP_2, + .map_bitmask = BMC150_ACCEL_INT_MAP_2_BIT_INT2_SLOPE, .en_reg = BMC150_ACCEL_REG_INT_EN_0, .en_bitmask = BMC150_ACCEL_INT_EN_BIT_SLP_X | BMC150_ACCEL_INT_EN_BIT_SLP_Y | @@ -424,19 +533,32 @@ static const struct bmc150_accel_interrupt_info { }, { /* fifo watermark interrupt */ .map_reg = BMC150_ACCEL_REG_INT_MAP_1, - .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_FWM, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_INT2_FWM, .en_reg = BMC150_ACCEL_REG_INT_EN_1, .en_bitmask = BMC150_ACCEL_INT_EN_BIT_FWM_EN, }, }; static void bmc150_accel_interrupts_setup(struct iio_dev *indio_dev, - struct bmc150_accel_data *data) + struct bmc150_accel_data *data, int irq) { + const struct bmc150_accel_interrupt_info *irq_info = NULL; + struct device *dev = regmap_get_device(data->regmap); int i; + /* + * For now we map all interrupts to the same output pin. + * However, some boards may have just INT2 (and not INT1) connected, + * so we try to detect which IRQ it is based on the interrupt-names. + * Without interrupt-names, we assume the irq belongs to INT1. + */ + irq_info = bmc150_accel_interrupts_int1; + if (data->type == BOSCH_BMC156 || + irq == of_irq_get_byname(dev->of_node, "INT2")) + irq_info = bmc150_accel_interrupts_int2; + for (i = 0; i < BMC150_ACCEL_INTERRUPTS; i++) - data->interrupts[i].info = &bmc150_accel_interrupts[i]; + data->interrupts[i].info = &irq_info[i]; } static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i, @@ -803,17 +925,30 @@ static const struct iio_chan_spec_ext_info bmc150_accel_ext_info[] = { { } }; -static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); -static IIO_CONST_ATTR(hwfifo_watermark_max, - __stringify(BMC150_ACCEL_FIFO_LENGTH)); +static ssize_t hwfifo_watermark_min_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", "1"); +} + +static ssize_t hwfifo_watermark_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", __stringify(BMC150_ACCEL_FIFO_LENGTH)); +} + +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0); static IIO_DEVICE_ATTR(hwfifo_enabled, S_IRUGO, bmc150_accel_get_fifo_state, NULL, 0); static IIO_DEVICE_ATTR(hwfifo_watermark, S_IRUGO, bmc150_accel_get_fifo_watermark, NULL, 0); static const struct attribute *bmc150_accel_fifo_attributes[] = { - &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, - &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark_max.dev_attr.attr, &iio_dev_attr_hwfifo_watermark.dev_attr.attr, &iio_dev_attr_hwfifo_enabled.dev_attr.attr, NULL, @@ -922,15 +1057,16 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, * now. */ for (i = 0; i < count; i++) { - u16 sample[8]; int j, bit; j = 0; for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) - memcpy(&sample[j++], &buffer[i * 3 + bit], 2); + memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit], + sizeof(data->scan.channels[0])); - iio_push_to_buffers_with_timestamp(indio_dev, sample, tstamp); + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + tstamp); tstamp += sample_period; } @@ -1013,66 +1149,63 @@ static const struct iio_chan_spec bmc150_accel_channels[] = static const struct iio_chan_spec bma280_accel_channels[] = BMC150_ACCEL_CHANNELS(14); +/* + * The range for the Bosch sensors is typically +-2g/4g/8g/16g, distributed + * over the amount of bits (see above). The scale table can be calculated using + * (range / 2^bits) * g = (range / 2^bits) * 9.80665 m/s^2 + * e.g. for +-2g and 12 bits: (4 / 2^12) * 9.80665 m/s^2 = 0.0095768... m/s^2 + * Multiply 10^6 and round to get the values listed below. + */ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = { - [bmc150] = { - .name = "BMC150A", - .chip_id = 0xFA, - .channels = bmc150_accel_channels, - .num_channels = ARRAY_SIZE(bmc150_accel_channels), - .scale_table = { {9610, BMC150_ACCEL_DEF_RANGE_2G}, - {19122, BMC150_ACCEL_DEF_RANGE_4G}, - {38344, BMC150_ACCEL_DEF_RANGE_8G}, - {76590, BMC150_ACCEL_DEF_RANGE_16G} }, - }, - [bmi055] = { - .name = "BMI055A", - .chip_id = 0xFA, - .channels = bmc150_accel_channels, - .num_channels = ARRAY_SIZE(bmc150_accel_channels), - .scale_table = { {9610, BMC150_ACCEL_DEF_RANGE_2G}, - {19122, BMC150_ACCEL_DEF_RANGE_4G}, - {38344, BMC150_ACCEL_DEF_RANGE_8G}, - {76590, BMC150_ACCEL_DEF_RANGE_16G} }, + { + .name = "BMA222", + .chip_id = 0x03, + .channels = bma222e_accel_channels, + .num_channels = ARRAY_SIZE(bma222e_accel_channels), + .scale_table = { {153229, BMC150_ACCEL_DEF_RANGE_2G}, + {306458, BMC150_ACCEL_DEF_RANGE_4G}, + {612916, BMC150_ACCEL_DEF_RANGE_8G}, + {1225831, BMC150_ACCEL_DEF_RANGE_16G} }, }, - [bma255] = { - .name = "BMA0255", - .chip_id = 0xFA, - .channels = bmc150_accel_channels, - .num_channels = ARRAY_SIZE(bmc150_accel_channels), - .scale_table = { {9610, BMC150_ACCEL_DEF_RANGE_2G}, - {19122, BMC150_ACCEL_DEF_RANGE_4G}, - {38344, BMC150_ACCEL_DEF_RANGE_8G}, - {76590, BMC150_ACCEL_DEF_RANGE_16G} }, + { + .name = "BMA222E", + .chip_id = 0xF8, + .channels = bma222e_accel_channels, + .num_channels = ARRAY_SIZE(bma222e_accel_channels), + .scale_table = { {153229, BMC150_ACCEL_DEF_RANGE_2G}, + {306458, BMC150_ACCEL_DEF_RANGE_4G}, + {612916, BMC150_ACCEL_DEF_RANGE_8G}, + {1225831, BMC150_ACCEL_DEF_RANGE_16G} }, }, - [bma250e] = { + { .name = "BMA250E", .chip_id = 0xF9, .channels = bma250e_accel_channels, .num_channels = ARRAY_SIZE(bma250e_accel_channels), - .scale_table = { {38344, BMC150_ACCEL_DEF_RANGE_2G}, - {76590, BMC150_ACCEL_DEF_RANGE_4G}, - {153277, BMC150_ACCEL_DEF_RANGE_8G}, - {306457, BMC150_ACCEL_DEF_RANGE_16G} }, + .scale_table = { {38307, BMC150_ACCEL_DEF_RANGE_2G}, + {76614, BMC150_ACCEL_DEF_RANGE_4G}, + {153229, BMC150_ACCEL_DEF_RANGE_8G}, + {306458, BMC150_ACCEL_DEF_RANGE_16G} }, }, - [bma222e] = { - .name = "BMA222E", - .chip_id = 0xF8, - .channels = bma222e_accel_channels, - .num_channels = ARRAY_SIZE(bma222e_accel_channels), - .scale_table = { {153277, BMC150_ACCEL_DEF_RANGE_2G}, - {306457, BMC150_ACCEL_DEF_RANGE_4G}, - {612915, BMC150_ACCEL_DEF_RANGE_8G}, - {1225831, BMC150_ACCEL_DEF_RANGE_16G} }, + { + .name = "BMA253/BMA254/BMA255/BMC150/BMC156/BMI055", + .chip_id = 0xFA, + .channels = bmc150_accel_channels, + .num_channels = ARRAY_SIZE(bmc150_accel_channels), + .scale_table = { {9577, BMC150_ACCEL_DEF_RANGE_2G}, + {19154, BMC150_ACCEL_DEF_RANGE_4G}, + {38307, BMC150_ACCEL_DEF_RANGE_8G}, + {76614, BMC150_ACCEL_DEF_RANGE_16G} }, }, - [bma280] = { - .name = "BMA0280", + { + .name = "BMA280", .chip_id = 0xFB, .channels = bma280_accel_channels, .num_channels = ARRAY_SIZE(bma280_accel_channels), - .scale_table = { {2392, BMC150_ACCEL_DEF_RANGE_2G}, - {4785, BMC150_ACCEL_DEF_RANGE_4G}, - {9581, BMC150_ACCEL_DEF_RANGE_8G}, - {19152, BMC150_ACCEL_DEF_RANGE_16G} }, + .scale_table = { {2394, BMC150_ACCEL_DEF_RANGE_2G}, + {4788, BMC150_ACCEL_DEF_RANGE_4G}, + {9577, BMC150_ACCEL_DEF_RANGE_8G}, + {19154, BMC150_ACCEL_DEF_RANGE_16G} }, }, }; @@ -1125,7 +1258,7 @@ err_read: return IRQ_HANDLED; } -static int bmc150_accel_trig_try_reen(struct iio_trigger *trig) +static void bmc150_accel_trig_reen(struct iio_trigger *trig) { struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig); struct bmc150_accel_data *data = t->data; @@ -1134,7 +1267,7 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig) /* new data interrupts don't need ack */ if (t == &t->data->triggers[BMC150_ACCEL_TRIGGER_DATA_READY]) - return 0; + return; mutex_lock(&data->mutex); /* clear any latched interrupt */ @@ -1142,12 +1275,8 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig) BMC150_ACCEL_INT_MODE_LATCH_INT | BMC150_ACCEL_INT_MODE_LATCH_RESET); mutex_unlock(&data->mutex); - if (ret < 0) { + if (ret < 0) dev_err(dev, "Error writing reg_int_rst_latch\n"); - return ret; - } - - return 0; } static int bmc150_accel_trigger_set_state(struct iio_trigger *trig, @@ -1187,7 +1316,7 @@ static int bmc150_accel_trigger_set_state(struct iio_trigger *trig, static const struct iio_trigger_ops bmc150_accel_trigger_ops = { .set_trigger_state = bmc150_accel_trigger_set_state, - .try_reenable = bmc150_accel_trig_try_reen, + .reenable = bmc150_accel_trig_reen, }; static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev) @@ -1345,15 +1474,14 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev, struct bmc150_accel_trigger *t = &data->triggers[i]; t->indio_trig = devm_iio_trigger_alloc(dev, - bmc150_accel_triggers[i].name, + bmc150_accel_triggers[i].name, indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!t->indio_trig) { ret = -ENOMEM; break; } - t->indio_trig->dev.parent = dev; t->indio_trig->ops = &bmc150_accel_trigger_ops; t->intr = bmc150_accel_triggers[i].intr; t->data = data; @@ -1410,8 +1538,8 @@ static int bmc150_accel_buffer_postenable(struct iio_dev *indio_dev) struct bmc150_accel_data *data = iio_priv(indio_dev); int ret = 0; - if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) - return iio_triggered_buffer_postenable(indio_dev); + if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED) + return 0; mutex_lock(&data->mutex); @@ -1442,8 +1570,8 @@ static int bmc150_accel_buffer_predisable(struct iio_dev *indio_dev) { struct bmc150_accel_data *data = iio_priv(indio_dev); - if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) - return iio_triggered_buffer_predisable(indio_dev); + if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED) + return 0; mutex_lock(&data->mutex); @@ -1547,8 +1675,10 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data) } int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, - const char *name, bool block_supported) + enum bmc150_type type, const char *name, + bool block_supported) { + const struct attribute **fifo_attrs; struct bmc150_accel_data *data; struct iio_dev *indio_dev; int ret; @@ -1559,22 +1689,46 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, data = iio_priv(indio_dev); dev_set_drvdata(dev, indio_dev); - data->irq = irq; data->regmap = regmap; + data->type = type; + + if (!bmc150_apply_acpi_orientation(dev, &data->orientation)) { + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return ret; + } - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + /* + * VDD is the analog and digital domain voltage supply + * VDDIO is the digital I/O voltage supply + */ + data->regulators[0].supply = "vdd"; + data->regulators[1].supply = "vddio"; + ret = devm_regulator_bulk_get(dev, + ARRAY_SIZE(data->regulators), + data->regulators); if (ret) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) { + dev_err(dev, "failed to enable regulators: %d\n", ret); return ret; + } + /* + * 2ms or 3ms power-on time according to datasheets, let's better + * be safe than sorry and set this delay to 5ms. + */ + msleep(5); ret = bmc150_accel_chip_init(data); if (ret < 0) - return ret; + goto err_disable_regulators; mutex_init(&data->mutex); - indio_dev->dev.parent = dev; indio_dev->channels = data->chip_info->channels; indio_dev->num_channels = data->chip_info->num_channels; indio_dev->name = name ? name : data->chip_info->name; @@ -1582,18 +1736,27 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &bmc150_accel_info; - ret = iio_triggered_buffer_setup(indio_dev, - &iio_pollfunc_store_time, - bmc150_accel_trigger_handler, - &bmc150_accel_buffer_ops); + if (block_supported) { + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + indio_dev->info = &bmc150_accel_info_fifo; + fifo_attrs = bmc150_accel_fifo_attributes; + } else { + fifo_attrs = NULL; + } + + ret = iio_triggered_buffer_setup_ext(indio_dev, + &iio_pollfunc_store_time, + bmc150_accel_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &bmc150_accel_buffer_ops, + fifo_attrs); if (ret < 0) { dev_err(dev, "Failed: iio triggered buffer setup\n"); - return ret; + goto err_disable_regulators; } - if (data->irq > 0) { - ret = devm_request_threaded_irq( - dev, data->irq, + if (irq > 0) { + ret = devm_request_threaded_irq(dev, irq, bmc150_accel_irq_handler, bmc150_accel_irq_thread_handler, IRQF_TRIGGER_RISING, @@ -1615,18 +1778,11 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, goto err_buffer_cleanup; } - bmc150_accel_interrupts_setup(indio_dev, data); + bmc150_accel_interrupts_setup(indio_dev, data, irq); ret = bmc150_accel_triggers_setup(indio_dev, data); if (ret) goto err_buffer_cleanup; - - if (block_supported) { - indio_dev->modes |= INDIO_BUFFER_SOFTWARE; - indio_dev->info = &bmc150_accel_info_fifo; - iio_buffer_set_attrs(indio_dev->buffer, - bmc150_accel_fifo_attributes); - } } ret = pm_runtime_set_active(dev); @@ -1640,21 +1796,27 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(dev, "Unable to register iio device\n"); - goto err_trigger_unregister; + goto err_pm_cleanup; } return 0; +err_pm_cleanup: + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); err_trigger_unregister: bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(data->regulators), + data->regulators); return ret; } -EXPORT_SYMBOL_GPL(bmc150_accel_core_probe); +EXPORT_SYMBOL_NS_GPL(bmc150_accel_core_probe, IIO_BMC150); -int bmc150_accel_core_remove(struct device *dev) +void bmc150_accel_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_accel_data *data = iio_priv(indio_dev); @@ -1663,7 +1825,6 @@ int bmc150_accel_core_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); @@ -1673,9 +1834,10 @@ int bmc150_accel_core_remove(struct device *dev) bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_DEEP_SUSPEND, 0); mutex_unlock(&data->mutex); - return 0; + regulator_bulk_disable(ARRAY_SIZE(data->regulators), + data->regulators); } -EXPORT_SYMBOL_GPL(bmc150_accel_core_remove); +EXPORT_SYMBOL_NS_GPL(bmc150_accel_core_remove, IIO_BMC150); #ifdef CONFIG_PM_SLEEP static int bmc150_accel_suspend(struct device *dev) @@ -1700,6 +1862,9 @@ static int bmc150_accel_resume(struct device *dev) bmc150_accel_fifo_set_mode(data); mutex_unlock(&data->mutex); + if (data->resume_callback) + data->resume_callback(dev); + return 0; } #endif @@ -1747,7 +1912,7 @@ const struct dev_pm_ops bmc150_accel_pm_ops = { SET_RUNTIME_PM_OPS(bmc150_accel_runtime_suspend, bmc150_accel_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(bmc150_accel_pm_ops); +EXPORT_SYMBOL_NS_GPL(bmc150_accel_pm_ops, IIO_BMC150); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index 06021c8685a7..be8cc598b88e 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -1,13 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * 3-axis accelerometer driver supporting following I2C Bosch-Sensortec chips: - * - BMC150 - * - BMI055 - * - BMA255 - * - BMA250E - * - BMA222E - * - BMA280 - * + * 3-axis accelerometer driver supporting many I2C Bosch-Sensortec chips * Copyright (c) 2014, Intel Corporation. */ @@ -20,15 +13,175 @@ #include "bmc150-accel.h" +#ifdef CONFIG_ACPI +static const struct acpi_device_id bmc150_acpi_dual_accel_ids[] = { + {"BOSC0200"}, + {"DUAL250E"}, + { } +}; + +/* + * The DUAL250E ACPI device for 360° hinges type 2-in-1s with 1 accelerometer + * in the display and 1 in the hinge has an ACPI-method (DSM) to tell the + * ACPI code about the angle between the 2 halves. This will make the ACPI + * code enable/disable the keyboard and touchpad. We need to call this to avoid + * the keyboard being disabled when the 2-in-1 is turned-on or resumed while + * fully folded into tablet mode (which gets detected with a HALL-sensor). + * If we don't call this then the keyboard won't work even when the 2-in-1 is + * changed to be used in laptop mode after the power-on / resume. + * + * This DSM takes 2 angles, selected by setting aux0 to 0 or 1, these presumably + * define the angle between the gravity vector measured by the accelerometer in + * the display (aux0=0) resp. the base (aux0=1) and some reference vector. + * The 2 angles get subtracted from each other so the reference vector does + * not matter and we can simply leave the second angle at 0. + */ + +#define BMC150_DSM_GUID "7681541e-8827-4239-8d9d-36be7fe12542" +#define DUAL250E_SET_ANGLE_FN_INDEX 3 + +struct dual250e_set_angle_args { + u32 aux0; + u32 ang0; + u32 rawx; + u32 rawy; + u32 rawz; +} __packed; + +static bool bmc150_acpi_set_angle_dsm(struct i2c_client *client, u32 aux0, u32 ang0) +{ + struct acpi_device *adev = ACPI_COMPANION(&client->dev); + struct dual250e_set_angle_args args = { + .aux0 = aux0, + .ang0 = ang0, + }; + union acpi_object args_obj, *obj; + guid_t guid; + + if (!acpi_dev_hid_uid_match(adev, "DUAL250E", NULL)) + return false; + + guid_parse(BMC150_DSM_GUID, &guid); + + if (!acpi_check_dsm(adev->handle, &guid, 0, BIT(DUAL250E_SET_ANGLE_FN_INDEX))) + return false; + + /* + * Note this triggers the following warning: + * "ACPI Warning: \_SB.PCI0.I2C2.ACC1._DSM: Argument #4 type mismatch - + * Found [Buffer], ACPI requires [Package]" + * This is unavoidable since the _DSM implementation expects a "naked" + * buffer, so wrapping it in a package will _not_ work. + */ + args_obj.type = ACPI_TYPE_BUFFER; + args_obj.buffer.length = sizeof(args); + args_obj.buffer.pointer = (u8 *)&args; + + obj = acpi_evaluate_dsm(adev->handle, &guid, 0, DUAL250E_SET_ANGLE_FN_INDEX, &args_obj); + if (!obj) { + dev_err(&client->dev, "Failed to call DSM to enable keyboard and touchpad\n"); + return false; + } + + ACPI_FREE(obj); + return true; +} + +static bool bmc150_acpi_enable_keyboard(struct i2c_client *client) +{ + /* + * The EC must see a change for it to re-enable the kbd, so first + * set the angle to 270° (tent/stand mode) and then change it to + * 90° (laptop mode). + */ + if (!bmc150_acpi_set_angle_dsm(client, 0, 270)) + return false; + + /* The EC needs some time to notice the angle being changed */ + msleep(100); + + return bmc150_acpi_set_angle_dsm(client, 0, 90); +} + +static void bmc150_acpi_resume_work(struct work_struct *work) +{ + struct bmc150_accel_data *data = + container_of(work, struct bmc150_accel_data, resume_work.work); + + bmc150_acpi_enable_keyboard(data->second_device); +} + +static void bmc150_acpi_resume_handler(struct device *dev) +{ + struct bmc150_accel_data *data = iio_priv(dev_get_drvdata(dev)); + + /* + * Delay the bmc150_acpi_enable_keyboard() call till after the system + * resume has completed, otherwise it will not work. + */ + schedule_delayed_work(&data->resume_work, msecs_to_jiffies(1000)); +} + +/* + * Some acpi_devices describe 2 accelerometers in a single ACPI device, + * try instantiating a second i2c_client for an I2cSerialBusV2 ACPI resource + * with index 1. + */ +static void bmc150_acpi_dual_accel_probe(struct i2c_client *client) +{ + struct bmc150_accel_data *data = iio_priv(i2c_get_clientdata(client)); + struct acpi_device *adev = ACPI_COMPANION(&client->dev); + char dev_name[16]; + struct i2c_board_info board_info = { + .type = "bmc150_accel", + .dev_name = dev_name, + .fwnode = client->dev.fwnode, + }; + + if (acpi_match_device_ids(adev, bmc150_acpi_dual_accel_ids)) + return; + + /* + * The 2nd accel sits in the base of 2-in-1s. The suffix is static, as + * there should never be more then 1 ACPI node with 2 accelerometers. + */ + snprintf(dev_name, sizeof(dev_name), "%s:base", acpi_device_hid(adev)); + + board_info.irq = acpi_dev_gpio_irq_get(adev, 1); + + data->second_device = i2c_acpi_new_device(&client->dev, 1, &board_info); + + if (!IS_ERR(data->second_device) && bmc150_acpi_enable_keyboard(data->second_device)) { + INIT_DELAYED_WORK(&data->resume_work, bmc150_acpi_resume_work); + data->resume_callback = bmc150_acpi_resume_handler; + } +} + +static void bmc150_acpi_dual_accel_remove(struct i2c_client *client) +{ + struct bmc150_accel_data *data = iio_priv(i2c_get_clientdata(client)); + + if (data->resume_callback) + cancel_delayed_work_sync(&data->resume_work); + + i2c_unregister_device(data->second_device); +} +#else +static void bmc150_acpi_dual_accel_probe(struct i2c_client *client) {} +static void bmc150_acpi_dual_accel_remove(struct i2c_client *client) {} +#endif + static int bmc150_accel_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct regmap *regmap; const char *name = NULL; + enum bmc150_type type = BOSCH_UNKNOWN; bool block_supported = i2c_check_functionality(client->adapter, I2C_FUNC_I2C) || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK); + int ret; regmap = devm_regmap_init_i2c(client, &bmc150_regmap_conf); if (IS_ERR(regmap)) { @@ -36,50 +189,75 @@ static int bmc150_accel_probe(struct i2c_client *client, return PTR_ERR(regmap); } - if (id) + if (id) { name = id->name; + type = id->driver_data; + } - return bmc150_accel_core_probe(&client->dev, regmap, client->irq, name, - block_supported); + ret = bmc150_accel_core_probe(&client->dev, regmap, client->irq, + type, name, block_supported); + if (ret) + return ret; + + /* + * The !id check avoids recursion when probe() gets called + * for the second client. + */ + if (!id && has_acpi_companion(&client->dev)) + bmc150_acpi_dual_accel_probe(client); + + return 0; } -static int bmc150_accel_remove(struct i2c_client *client) +static void bmc150_accel_remove(struct i2c_client *client) { - return bmc150_accel_core_remove(&client->dev); + bmc150_acpi_dual_accel_remove(client); + + bmc150_accel_core_remove(&client->dev); } static const struct acpi_device_id bmc150_accel_acpi_match[] = { - {"BSBA0150", bmc150}, - {"BMC150A", bmc150}, - {"BMI055A", bmi055}, - {"BMA0255", bma255}, - {"BMA250E", bma250e}, - {"BMA222E", bma222e}, - {"BMA0280", bma280}, + {"BMA0255"}, + {"BMA0280"}, + {"BMA222"}, + {"BMA222E"}, + {"BMA250E"}, + {"BMC150A"}, + {"BMI055A"}, {"BOSC0200"}, + {"BSBA0150"}, + {"DUAL250E"}, { }, }; MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct i2c_device_id bmc150_accel_id[] = { - {"bmc150_accel", bmc150}, - {"bmi055_accel", bmi055}, - {"bma255", bma255}, - {"bma250e", bma250e}, - {"bma222e", bma222e}, - {"bma280", bma280}, + {"bma222"}, + {"bma222e"}, + {"bma250e"}, + {"bma253"}, + {"bma254"}, + {"bma255"}, + {"bma280"}, + {"bmc150_accel"}, + {"bmc156_accel", BOSCH_BMC156}, + {"bmi055_accel"}, {} }; MODULE_DEVICE_TABLE(i2c, bmc150_accel_id); static const struct of_device_id bmc150_accel_of_match[] = { - { .compatible = "bosch,bmc150_accel" }, - { .compatible = "bosch,bmi055_accel" }, - { .compatible = "bosch,bma255" }, - { .compatible = "bosch,bma250e" }, + { .compatible = "bosch,bma222" }, { .compatible = "bosch,bma222e" }, + { .compatible = "bosch,bma250e" }, + { .compatible = "bosch,bma253" }, + { .compatible = "bosch,bma254" }, + { .compatible = "bosch,bma255" }, { .compatible = "bosch,bma280" }, + { .compatible = "bosch,bmc150_accel" }, + { .compatible = "bosch,bmc156_accel" }, + { .compatible = "bosch,bmi055_accel" }, { }, }; MODULE_DEVICE_TABLE(of, bmc150_accel_of_match); @@ -100,3 +278,4 @@ module_i2c_driver(bmc150_accel_driver); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("BMC150 I2C accelerometer driver"); +MODULE_IMPORT_NS(IIO_BMC150); diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 2a8c311d6f5a..921fb46be0b8 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -16,6 +16,8 @@ static int bmc150_accel_probe(struct spi_device *spi) { struct regmap *regmap; + const char *name = NULL; + enum bmc150_type type = BOSCH_UNKNOWN; const struct spi_device_id *id = spi_get_device_id(spi); regmap = devm_regmap_init_spi(spi, &bmc150_regmap_conf); @@ -24,34 +26,43 @@ static int bmc150_accel_probe(struct spi_device *spi) return PTR_ERR(regmap); } - return bmc150_accel_core_probe(&spi->dev, regmap, spi->irq, id->name, + if (id) { + name = id->name; + type = id->driver_data; + } + + return bmc150_accel_core_probe(&spi->dev, regmap, spi->irq, type, name, true); } -static int bmc150_accel_remove(struct spi_device *spi) +static void bmc150_accel_remove(struct spi_device *spi) { - return bmc150_accel_core_remove(&spi->dev); + bmc150_accel_core_remove(&spi->dev); } static const struct acpi_device_id bmc150_accel_acpi_match[] = { - {"BSBA0150", bmc150}, - {"BMC150A", bmc150}, - {"BMI055A", bmi055}, - {"BMA0255", bma255}, - {"BMA250E", bma250e}, - {"BMA222E", bma222e}, - {"BMA0280", bma280}, + {"BMA0255"}, + {"BMA0280"}, + {"BMA222"}, + {"BMA222E"}, + {"BMA250E"}, + {"BMC150A"}, + {"BMI055A"}, + {"BSBA0150"}, { }, }; MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct spi_device_id bmc150_accel_id[] = { - {"bmc150_accel", bmc150}, - {"bmi055_accel", bmi055}, - {"bma255", bma255}, - {"bma250e", bma250e}, - {"bma222e", bma222e}, - {"bma280", bma280}, + {"bma222"}, + {"bma222e"}, + {"bma250e"}, + {"bma253"}, + {"bma255"}, + {"bma280"}, + {"bmc150_accel"}, + {"bmc156_accel", BOSCH_BMC156}, + {"bmi055_accel"}, {} }; MODULE_DEVICE_TABLE(spi, bmc150_accel_id); @@ -71,3 +82,4 @@ module_spi_driver(bmc150_accel_driver); MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("BMC150 SPI accelerometer driver"); +MODULE_IMPORT_NS(IIO_BMC150); diff --git a/drivers/iio/accel/bmc150-accel.h b/drivers/iio/accel/bmc150-accel.h index ae6118ae11b1..7775c5edaeef 100644 --- a/drivers/iio/accel/bmc150-accel.h +++ b/drivers/iio/accel/bmc150-accel.h @@ -2,20 +2,93 @@ #ifndef _BMC150_ACCEL_H_ #define _BMC150_ACCEL_H_ +#include <linux/atomic.h> +#include <linux/iio/iio.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/workqueue.h> + struct regmap; +struct i2c_client; +struct bmc150_accel_chip_info; +struct bmc150_accel_interrupt_info; + +/* + * We can often guess better than "UNKNOWN" based on the device IDs + * but unfortunately this information is not always accurate. There are some + * devices where ACPI firmware specifies an ID like "BMA250E" when the device + * actually has a BMA222E. The driver attempts to detect those by reading the + * chip ID from the registers but this information is not always enough either. + * + * Therefore, this enum should be only used when the chip ID detection is not + * enough and we can be reasonably sure that the device IDs are reliable + * in practice (e.g. for device tree platforms). + */ +enum bmc150_type { + BOSCH_UNKNOWN, + BOSCH_BMC156, +}; + +struct bmc150_accel_interrupt { + const struct bmc150_accel_interrupt_info *info; + atomic_t users; +}; + +struct bmc150_accel_trigger { + struct bmc150_accel_data *data; + struct iio_trigger *indio_trig; + int (*setup)(struct bmc150_accel_trigger *t, bool state); + int intr; + bool enabled; +}; + +enum bmc150_accel_interrupt_id { + BMC150_ACCEL_INT_DATA_READY, + BMC150_ACCEL_INT_ANY_MOTION, + BMC150_ACCEL_INT_WATERMARK, + BMC150_ACCEL_INTERRUPTS, +}; + +enum bmc150_accel_trigger_id { + BMC150_ACCEL_TRIGGER_DATA_READY, + BMC150_ACCEL_TRIGGER_ANY_MOTION, + BMC150_ACCEL_TRIGGERS, +}; -enum { - bmc150, - bmi055, - bma255, - bma250e, - bma222e, - bma280, +struct bmc150_accel_data { + struct regmap *regmap; + struct regulator_bulk_data regulators[2]; + struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS]; + struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; + struct mutex mutex; + u8 fifo_mode, watermark; + s16 buffer[8]; + /* + * Ensure there is sufficient space and correct alignment for + * the timestamp if enabled + */ + struct { + __le16 channels[3]; + s64 ts __aligned(8); + } scan; + u8 bw_bits; + u32 slope_dur; + u32 slope_thres; + u32 range; + int ev_enable_state; + int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ + const struct bmc150_accel_chip_info *chip_info; + enum bmc150_type type; + struct i2c_client *second_device; + void (*resume_callback)(struct device *dev); + struct delayed_work resume_work; + struct iio_mount_matrix orientation; }; int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, - const char *name, bool block_supported); -int bmc150_accel_core_remove(struct device *dev); + enum bmc150_type type, const char *name, + bool block_supported); +void bmc150_accel_core_remove(struct device *dev); extern const struct dev_pm_ops bmc150_accel_pm_ops; extern const struct regmap_config bmc150_regmap_conf; diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c new file mode 100644 index 000000000000..84edcc78d796 --- /dev/null +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: + * - BMI088 + * + * Copyright (c) 2018-2021, Topic Embedded Products + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <asm/unaligned.h> + +#include "bmi088-accel.h" + +#define BMI088_ACCEL_REG_CHIP_ID 0x00 +#define BMI088_ACCEL_REG_ERROR 0x02 + +#define BMI088_ACCEL_REG_INT_STATUS 0x1D +#define BMI088_ACCEL_INT_STATUS_BIT_DRDY BIT(7) + +#define BMI088_ACCEL_REG_RESET 0x7E +#define BMI088_ACCEL_RESET_VAL 0xB6 + +#define BMI088_ACCEL_REG_PWR_CTRL 0x7D +#define BMI088_ACCEL_REG_PWR_CONF 0x7C + +#define BMI088_ACCEL_REG_INT_MAP_DATA 0x58 +#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT1_DRDY BIT(2) +#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT2_FWM BIT(5) + +#define BMI088_ACCEL_REG_INT1_IO_CONF 0x53 +#define BMI088_ACCEL_INT1_IO_CONF_BIT_ENABLE_OUT BIT(3) +#define BMI088_ACCEL_INT1_IO_CONF_BIT_LVL BIT(1) + +#define BMI088_ACCEL_REG_INT2_IO_CONF 0x54 +#define BMI088_ACCEL_INT2_IO_CONF_BIT_ENABLE_OUT BIT(3) +#define BMI088_ACCEL_INT2_IO_CONF_BIT_LVL BIT(1) + +#define BMI088_ACCEL_REG_ACC_CONF 0x40 +#define BMI088_ACCEL_MODE_ODR_MASK 0x0f + +#define BMI088_ACCEL_REG_ACC_RANGE 0x41 +#define BMI088_ACCEL_RANGE_3G 0x00 +#define BMI088_ACCEL_RANGE_6G 0x01 +#define BMI088_ACCEL_RANGE_12G 0x02 +#define BMI088_ACCEL_RANGE_24G 0x03 + +#define BMI088_ACCEL_REG_TEMP 0x22 +#define BMI088_ACCEL_REG_TEMP_SHIFT 5 +#define BMI088_ACCEL_TEMP_UNIT 125 +#define BMI088_ACCEL_TEMP_OFFSET 23000 + +#define BMI088_ACCEL_REG_XOUT_L 0x12 +#define BMI088_ACCEL_AXIS_TO_REG(axis) \ + (BMI088_ACCEL_REG_XOUT_L + (axis * 2)) + +#define BMI088_ACCEL_MAX_STARTUP_TIME_US 1000 +#define BMI088_AUTO_SUSPEND_DELAY_MS 2000 + +#define BMI088_ACCEL_REG_FIFO_STATUS 0x0E +#define BMI088_ACCEL_REG_FIFO_CONFIG0 0x48 +#define BMI088_ACCEL_REG_FIFO_CONFIG1 0x49 +#define BMI088_ACCEL_REG_FIFO_DATA 0x3F +#define BMI088_ACCEL_FIFO_LENGTH 100 + +#define BMI088_ACCEL_FIFO_MODE_FIFO 0x40 +#define BMI088_ACCEL_FIFO_MODE_STREAM 0x80 + +#define BMIO088_ACCEL_ACC_RANGE_MSK GENMASK(1, 0) + +enum bmi088_accel_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +static const int bmi088_sample_freqs[] = { + 12, 500000, + 25, 0, + 50, 0, + 100, 0, + 200, 0, + 400, 0, + 800, 0, + 1600, 0, +}; + +/* Available OSR (over sampling rate) sets the 3dB cut-off frequency */ +enum bmi088_osr_modes { + BMI088_ACCEL_MODE_OSR_NORMAL = 0xA, + BMI088_ACCEL_MODE_OSR_2 = 0x9, + BMI088_ACCEL_MODE_OSR_4 = 0x8, +}; + +/* Available ODR (output data rates) in Hz */ +enum bmi088_odr_modes { + BMI088_ACCEL_MODE_ODR_12_5 = 0x5, + BMI088_ACCEL_MODE_ODR_25 = 0x6, + BMI088_ACCEL_MODE_ODR_50 = 0x7, + BMI088_ACCEL_MODE_ODR_100 = 0x8, + BMI088_ACCEL_MODE_ODR_200 = 0x9, + BMI088_ACCEL_MODE_ODR_400 = 0xa, + BMI088_ACCEL_MODE_ODR_800 = 0xb, + BMI088_ACCEL_MODE_ODR_1600 = 0xc, +}; + +struct bmi088_scale_info { + int scale; + u8 reg_range; +}; + +struct bmi088_accel_chip_info { + const char *name; + u8 chip_id; + const struct iio_chan_spec *channels; + int num_channels; + const int scale_table[4][2]; +}; + +struct bmi088_accel_data { + struct regmap *regmap; + const struct bmi088_accel_chip_info *chip_info; + u8 buffer[2] __aligned(IIO_DMA_MINALIGN); /* shared DMA safe buffer */ +}; + +static const struct regmap_range bmi088_volatile_ranges[] = { + /* All registers below 0x40 are volatile, except the CHIP ID. */ + regmap_reg_range(BMI088_ACCEL_REG_ERROR, 0x3f), + /* Mark the RESET as volatile too, it is self-clearing */ + regmap_reg_range(BMI088_ACCEL_REG_RESET, BMI088_ACCEL_REG_RESET), +}; + +static const struct regmap_access_table bmi088_volatile_table = { + .yes_ranges = bmi088_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(bmi088_volatile_ranges), +}; + +const struct regmap_config bmi088_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7E, + .volatile_table = &bmi088_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, IIO_BMI088); + +static int bmi088_accel_power_up(struct bmi088_accel_data *data) +{ + int ret; + + /* Enable accelerometer and temperature sensor */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x4); + if (ret) + return ret; + + /* Datasheet recommends to wait at least 5ms before communication */ + usleep_range(5000, 6000); + + /* Disable suspend mode */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x0); + if (ret) + return ret; + + /* Recommended at least 1ms before further communication */ + usleep_range(1000, 1200); + + return 0; +} + +static int bmi088_accel_power_down(struct bmi088_accel_data *data) +{ + int ret; + + /* Enable suspend mode */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x3); + if (ret) + return ret; + + /* Recommended at least 1ms before further communication */ + usleep_range(1000, 1200); + + /* Disable accelerometer and temperature sensor */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x0); + if (ret) + return ret; + + /* Datasheet recommends to wait at least 5ms before communication */ + usleep_range(5000, 6000); + + return 0; +} + +static int bmi088_accel_get_sample_freq(struct bmi088_accel_data *data, + int *val, int *val2) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_CONF, + &value); + if (ret) + return ret; + + value &= BMI088_ACCEL_MODE_ODR_MASK; + value -= BMI088_ACCEL_MODE_ODR_12_5; + value <<= 1; + + if (value >= ARRAY_SIZE(bmi088_sample_freqs) - 1) + return -EINVAL; + + *val = bmi088_sample_freqs[value]; + *val2 = bmi088_sample_freqs[value + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val) +{ + unsigned int regval; + int index = 0; + + while (index < ARRAY_SIZE(bmi088_sample_freqs) && + bmi088_sample_freqs[index] != val) + index += 2; + + if (index >= ARRAY_SIZE(bmi088_sample_freqs)) + return -EINVAL; + + regval = (index >> 1) + BMI088_ACCEL_MODE_ODR_12_5; + + return regmap_update_bits(data->regmap, BMI088_ACCEL_REG_ACC_CONF, + BMI088_ACCEL_MODE_ODR_MASK, regval); +} + +static int bmi088_accel_set_scale(struct bmi088_accel_data *data, int val, int val2) +{ + unsigned int i; + + for (i = 0; i < 4; i++) + if (val == data->chip_info->scale_table[i][0] && + val2 == data->chip_info->scale_table[i][1]) + break; + + if (i == 4) + return -EINVAL; + + return regmap_write(data->regmap, BMI088_ACCEL_REG_ACC_RANGE, i); +} + +static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val) +{ + int ret; + s16 temp; + + ret = regmap_bulk_read(data->regmap, BMI088_ACCEL_REG_TEMP, + &data->buffer, sizeof(__be16)); + if (ret) + return ret; + + /* data->buffer is cacheline aligned */ + temp = be16_to_cpu(*(__be16 *)data->buffer); + + *val = temp >> BMI088_ACCEL_REG_TEMP_SHIFT; + + return IIO_VAL_INT; +} + +static int bmi088_accel_get_axis(struct bmi088_accel_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + int ret; + s16 raw_val; + + ret = regmap_bulk_read(data->regmap, + BMI088_ACCEL_AXIS_TO_REG(chan->scan_index), + data->buffer, sizeof(__le16)); + if (ret) + return ret; + + raw_val = le16_to_cpu(*(__le16 *)data->buffer); + *val = raw_val; + + return IIO_VAL_INT; +} + +static int bmi088_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bmi088_accel_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + int reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = bmi088_accel_get_temp(data, val); + goto out_read_raw_pm_put; + case IIO_ACCEL: + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + goto out_read_raw_pm_put; + + ret = bmi088_accel_get_axis(data, chan, val); + iio_device_release_direct_mode(indio_dev); + if (!ret) + ret = IIO_VAL_INT; + + goto out_read_raw_pm_put; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + /* Offset applies before scale */ + *val = BMI088_ACCEL_TEMP_OFFSET/BMI088_ACCEL_TEMP_UNIT; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* 0.125 degrees per LSB */ + *val = BMI088_ACCEL_TEMP_UNIT; + return IIO_VAL_INT; + case IIO_ACCEL: + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = regmap_read(data->regmap, + BMI088_ACCEL_REG_ACC_RANGE, ®); + if (ret) + goto out_read_raw_pm_put; + + reg = FIELD_GET(BMIO088_ACCEL_ACC_RANGE_MSK, reg); + *val = data->chip_info->scale_table[reg][0]; + *val2 = data->chip_info->scale_table[reg][1]; + ret = IIO_VAL_INT_PLUS_MICRO; + + goto out_read_raw_pm_put; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = bmi088_accel_get_sample_freq(data, val, val2); + goto out_read_raw_pm_put; + default: + break; + } + + return -EINVAL; + +out_read_raw_pm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int bmi088_accel_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct bmi088_accel_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)data->chip_info->scale_table; + *length = 8; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = bmi088_sample_freqs; + *length = ARRAY_SIZE(bmi088_sample_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int bmi088_accel_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bmi088_accel_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = bmi088_accel_set_scale(data, val, val2); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = bmi088_accel_set_sample_freq(data, val); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; + default: + return -EINVAL; + } +} + +#define BMI088_ACCEL_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_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = AXIS_##_axis, \ +} + +static const struct iio_chan_spec bmi088_accel_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = -1, + }, + BMI088_ACCEL_CHANNEL(X), + BMI088_ACCEL_CHANNEL(Y), + BMI088_ACCEL_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = { + [BOSCH_BMI085] = { + .name = "bmi085-accel", + .chip_id = 0x1F, + .channels = bmi088_accel_channels, + .num_channels = ARRAY_SIZE(bmi088_accel_channels), + .scale_table = {{0, 598}, {0, 1196}, {0, 2393}, {0, 4785}}, + }, + [BOSCH_BMI088] = { + .name = "bmi088-accel", + .chip_id = 0x1E, + .channels = bmi088_accel_channels, + .num_channels = ARRAY_SIZE(bmi088_accel_channels), + .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}}, + }, + [BOSCH_BMI090L] = { + .name = "bmi090l-accel", + .chip_id = 0x1A, + .channels = bmi088_accel_channels, + .num_channels = ARRAY_SIZE(bmi088_accel_channels), + .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}}, + }, +}; + +static const struct iio_info bmi088_accel_info = { + .read_raw = bmi088_accel_read_raw, + .write_raw = bmi088_accel_write_raw, + .read_avail = bmi088_accel_read_avail, +}; + +static const unsigned long bmi088_accel_scan_masks[] = { + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; + +static int bmi088_accel_chip_init(struct bmi088_accel_data *data, enum bmi_device_type type) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret, i; + unsigned int val; + + if (type >= BOSCH_UNKNOWN) + return -ENODEV; + + /* Do a dummy read to enable SPI interface, won't harm I2C */ + regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); + + /* + * Reset chip to get it in a known good state. A delay of 1ms after + * reset is required according to the data sheet + */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_RESET, + BMI088_ACCEL_RESET_VAL); + if (ret) + return ret; + + usleep_range(1000, 2000); + + /* Do a dummy read again after a reset to enable the SPI interface */ + regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); + + /* Read chip ID */ + ret = regmap_read(data->regmap, BMI088_ACCEL_REG_CHIP_ID, &val); + if (ret) { + dev_err(dev, "Error: Reading chip id\n"); + return ret; + } + + /* Validate chip ID */ + for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++) + if (bmi088_accel_chip_info_tbl[i].chip_id == val) + break; + + if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl)) + data->chip_info = &bmi088_accel_chip_info_tbl[type]; + else + data->chip_info = &bmi088_accel_chip_info_tbl[i]; + + if (i != type) + dev_warn(dev, "unexpected chip id 0x%X\n", val); + + return 0; +} + +int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, + int irq, enum bmi_device_type type) +{ + struct bmi088_accel_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + data->regmap = regmap; + + ret = bmi088_accel_chip_init(data, type); + if (ret) + return ret; + + indio_dev->channels = data->chip_info->channels; + indio_dev->num_channels = data->chip_info->num_channels; + indio_dev->name = data->chip_info->name; + indio_dev->available_scan_masks = bmi088_accel_scan_masks; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &bmi088_accel_info; + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + /* We need ~6ms to startup, so set the delay to 6 seconds */ + pm_runtime_set_autosuspend_delay(dev, 6000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + ret = iio_device_register(indio_dev); + if (ret) + dev_err(dev, "Unable to register iio device\n"); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_probe, IIO_BMI088); + + +void bmi088_accel_core_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + bmi088_accel_power_down(data); +} +EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088); + +static int bmi088_accel_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + return bmi088_accel_power_down(data); +} + +static int bmi088_accel_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + return bmi088_accel_power_up(data); +} + +EXPORT_NS_GPL_RUNTIME_DEV_PM_OPS(bmi088_accel_pm_ops, + bmi088_accel_runtime_suspend, + bmi088_accel_runtime_resume, NULL, + IIO_BMI088); + +MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMI088 accelerometer driver (core)"); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c new file mode 100644 index 000000000000..ee540edd8412 --- /dev/null +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: + * - BMI088 + * + * Copyright (c) 2018-2020, Topic Embedded Products + */ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include "bmi088-accel.h" + +static int bmi088_regmap_spi_write(void *context, const void *data, size_t count) +{ + struct spi_device *spi = context; + + /* Write register is same as generic SPI */ + return spi_write(spi, data, count); +} + +static int bmi088_regmap_spi_read(void *context, const void *reg, + size_t reg_size, void *val, size_t val_size) +{ + struct spi_device *spi = context; + u8 addr[2]; + + addr[0] = *(u8 *)reg; + addr[0] |= BIT(7); /* Set RW = '1' */ + addr[1] = 0; /* Read requires a dummy byte transfer */ + + return spi_write_then_read(spi, addr, sizeof(addr), val, val_size); +} + +static struct regmap_bus bmi088_regmap_bus = { + .write = bmi088_regmap_spi_write, + .read = bmi088_regmap_spi_read, +}; + +static int bmi088_accel_probe(struct spi_device *spi) +{ + struct regmap *regmap; + const struct spi_device_id *id = spi_get_device_id(spi); + + regmap = devm_regmap_init(&spi->dev, &bmi088_regmap_bus, + spi, &bmi088_regmap_conf); + + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to initialize spi regmap\n"); + return PTR_ERR(regmap); + } + + return bmi088_accel_core_probe(&spi->dev, regmap, spi->irq, + id->driver_data); +} + +static void bmi088_accel_remove(struct spi_device *spi) +{ + bmi088_accel_core_remove(&spi->dev); +} + +static const struct of_device_id bmi088_of_match[] = { + { .compatible = "bosch,bmi085-accel" }, + { .compatible = "bosch,bmi088-accel" }, + { .compatible = "bosch,bmi090l-accel" }, + {} +}; +MODULE_DEVICE_TABLE(of, bmi088_of_match); + +static const struct spi_device_id bmi088_accel_id[] = { + {"bmi085-accel", BOSCH_BMI085}, + {"bmi088-accel", BOSCH_BMI088}, + {"bmi090l-accel", BOSCH_BMI090L}, + {} +}; +MODULE_DEVICE_TABLE(spi, bmi088_accel_id); + +static struct spi_driver bmi088_accel_driver = { + .driver = { + .name = "bmi088_accel_spi", + .pm = pm_ptr(&bmi088_accel_pm_ops), + .of_match_table = bmi088_of_match, + }, + .probe = bmi088_accel_probe, + .remove = bmi088_accel_remove, + .id_table = bmi088_accel_id, +}; +module_spi_driver(bmi088_accel_driver); + +MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMI088 accelerometer driver (SPI)"); +MODULE_IMPORT_NS(IIO_BMI088); diff --git a/drivers/iio/accel/bmi088-accel.h b/drivers/iio/accel/bmi088-accel.h new file mode 100644 index 000000000000..80cd396a3141 --- /dev/null +++ b/drivers/iio/accel/bmi088-accel.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef BMI088_ACCEL_H +#define BMI088_ACCEL_H + +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/types.h> + +struct device; + +enum bmi_device_type { + BOSCH_BMI085, + BOSCH_BMI088, + BOSCH_BMI090L, + BOSCH_UNKNOWN, +}; + +extern const struct regmap_config bmi088_regmap_conf; +extern const struct dev_pm_ops bmi088_accel_pm_ops; + +int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, + enum bmi_device_type type); +void bmi088_accel_core_remove(struct device *dev); + +#endif /* BMI088_ACCEL_H */ diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c index 68e847c6255e..0f403342b1fc 100644 --- a/drivers/iio/accel/cros_ec_accel_legacy.c +++ b/drivers/iio/accel/cros_ec_accel_legacy.c @@ -33,6 +33,11 @@ */ #define ACCEL_LEGACY_NSCALE 9586168 +/* + * Sensor frequency is hard-coded to 10Hz. + */ +static const int cros_ec_legacy_sample_freq[] = { 10, 0 }; + static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask, s16 *data) { @@ -96,6 +101,11 @@ static int cros_ec_accel_legacy_read(struct iio_dev *indio_dev, *val = 0; ret = IIO_VAL_INT; break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = cros_ec_legacy_sample_freq[0]; + *val2 = cros_ec_legacy_sample_freq[1]; + ret = IIO_VAL_INT_PLUS_MICRO; + break; default: ret = cros_ec_sensors_core_read(st, chan, val, val2, mask); @@ -120,9 +130,39 @@ static int cros_ec_accel_legacy_write(struct iio_dev *indio_dev, return -EINVAL; } +/** + * cros_ec_accel_legacy_read_avail() - get available values + * @indio_dev: pointer to state information for device + * @chan: channel specification structure table + * @vals: list of available values + * @type: type of data returned + * @length: number of data returned in the array + * @mask: specifies which values to be requested + * + * Return: an error code or IIO_AVAIL_LIST + */ +static int cros_ec_accel_legacy_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_SAMP_FREQ: + *length = ARRAY_SIZE(cros_ec_legacy_sample_freq); + *vals = cros_ec_legacy_sample_freq; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + static const struct iio_info cros_ec_accel_legacy_info = { .read_raw = &cros_ec_accel_legacy_read, .write_raw = &cros_ec_accel_legacy_write, + .read_avail = &cros_ec_accel_legacy_read_avail, }; /* @@ -142,7 +182,11 @@ static const struct iio_info cros_ec_accel_legacy_info = { .info_mask_separate = \ BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .ext_info = cros_ec_sensors_ext_info, \ .scan_type = { \ .sign = 's', \ @@ -170,7 +214,8 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev) if (!indio_dev) return -ENOMEM; - ret = cros_ec_sensors_core_init(pdev, indio_dev, true); + ret = cros_ec_sensors_core_init(pdev, indio_dev, true, + cros_ec_sensors_capture); if (ret) return ret; @@ -185,17 +230,12 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev) indio_dev->channels = cros_ec_accel_legacy_channels; indio_dev->num_channels = ARRAY_SIZE(cros_ec_accel_legacy_channels); /* The lid sensor needs to be presented inverted. */ - if (state->loc == MOTIONSENSE_LOC_LID) { + if (!strcmp(indio_dev->label, "accel-display")) { state->sign[CROS_EC_SENSOR_X] = -1; state->sign[CROS_EC_SENSOR_Z] = -1; } - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, - cros_ec_sensors_capture, NULL); - if (ret) - return ret; - - return devm_iio_device_register(dev, indio_dev); + return cros_ec_sensors_core_register(dev, indio_dev, NULL); } static struct platform_driver cros_ec_accel_platform_driver = { diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index 227bea2d738b..04e9c5678964 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the MiraMEMS DA280 3-axis accelerometer and * IIO driver for the MiraMEMS DA226 2-axis accelerometer * @@ -100,6 +100,11 @@ static enum da280_chipset da280_match_acpi_device(struct device *dev) return (enum da280_chipset) id->driver_data; } +static void da280_disable(void *client) +{ + da280_enable(client, false); +} + static int da280_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -118,9 +123,7 @@ static int da280_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; - i2c_set_clientdata(client, indio_dev); - indio_dev->dev.parent = &client->dev; indio_dev->info = &da280_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = da280_channels; @@ -143,25 +146,13 @@ static int da280_probe(struct i2c_client *client, if (ret < 0) return ret; - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "device_register failed\n"); - da280_enable(client, false); - } - - return ret; -} - -static int da280_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - iio_device_unregister(indio_dev); + ret = devm_add_action_or_reset(&client->dev, da280_disable, client); + if (ret) + return ret; - return da280_enable(client, false); + return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int da280_suspend(struct device *dev) { return da280_enable(to_i2c_client(dev), false); @@ -171,9 +162,8 @@ static int da280_resume(struct device *dev) { return da280_enable(to_i2c_client(dev), true); } -#endif -static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); static const struct acpi_device_id da280_acpi_match[] = { {"MIRAACC", da280}, @@ -192,10 +182,9 @@ static struct i2c_driver da280_driver = { .driver = { .name = "da280", .acpi_match_table = ACPI_PTR(da280_acpi_match), - .pm = &da280_pm_ops, + .pm = pm_sleep_ptr(&da280_pm_ops), }, .probe = da280_probe, - .remove = da280_remove, .id_table = da280_i2c_id, }; diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c index c20979249a48..ec4e29d260f7 100644 --- a/drivers/iio/accel/da311.c +++ b/drivers/iio/accel/da311.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the MiraMEMS DA311 3-axis accelerometer * * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> @@ -212,6 +212,11 @@ static const struct iio_info da311_info = { .read_raw = da311_read_raw, }; +static void da311_disable(void *client) +{ + da311_enable(client, false); +} + static int da311_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -229,9 +234,7 @@ static int da311_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; - i2c_set_clientdata(client, indio_dev); - indio_dev->dev.parent = &client->dev; indio_dev->info = &da311_info; indio_dev->name = "da311"; indio_dev->modes = INDIO_DIRECT_MODE; @@ -246,25 +249,13 @@ static int da311_probe(struct i2c_client *client, if (ret < 0) return ret; - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "device_register failed\n"); - da311_enable(client, false); - } - - return ret; -} - -static int da311_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - iio_device_unregister(indio_dev); + ret = devm_add_action_or_reset(&client->dev, da311_disable, client); + if (ret) + return ret; - return da311_enable(client, false); + return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int da311_suspend(struct device *dev) { return da311_enable(to_i2c_client(dev), false); @@ -274,9 +265,8 @@ static int da311_resume(struct device *dev) { return da311_enable(to_i2c_client(dev), true); } -#endif -static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume); static const struct i2c_device_id da311_i2c_id[] = { {"da311", 0}, @@ -287,10 +277,9 @@ MODULE_DEVICE_TABLE(i2c, da311_i2c_id); static struct i2c_driver da311_driver = { .driver = { .name = "da311", - .pm = &da311_pm_ops, + .pm = pm_sleep_ptr(&da311_pm_ops), }, .probe = da311_probe, - .remove = da311_remove, .id_table = da311_i2c_id, }; diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c index 2bf210fa4ba6..4b69c8530f5e 100644 --- a/drivers/iio/accel/dmard06.c +++ b/drivers/iio/accel/dmard06.c @@ -6,6 +6,7 @@ */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/i2c.h> #include <linux/iio/iio.h> @@ -160,7 +161,6 @@ static int dmard06_probe(struct i2c_client *client, dmard06->chip_id = ret; i2c_set_clientdata(client, indio_dev); - indio_dev->dev.parent = &client->dev; indio_dev->name = DMARD06_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = dmard06_channels; @@ -170,7 +170,6 @@ static int dmard06_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int dmard06_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -199,11 +198,8 @@ static int dmard06_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume); -#define DMARD06_PM_OPS (&dmard06_pm_ops) -#else -#define DMARD06_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, + dmard06_resume); static const struct i2c_device_id dmard06_id[] = { { "dmard05", 0 }, @@ -226,8 +222,8 @@ static struct i2c_driver dmard06_driver = { .id_table = dmard06_id, .driver = { .name = DMARD06_DRV_NAME, - .of_match_table = of_match_ptr(dmard06_of_match), - .pm = DMARD06_PM_OPS, + .of_match_table = dmard06_of_match, + .pm = pm_sleep_ptr(&dmard06_pm_ops), }, }; module_i2c_driver(dmard06_driver); diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c index 2d666cd69b29..cb0246ca72f3 100644 --- a/drivers/iio/accel/dmard09.c +++ b/drivers/iio/accel/dmard09.c @@ -24,7 +24,7 @@ #define DMARD09_AXIS_Y 1 #define DMARD09_AXIS_Z 2 #define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2) -#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2) +#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1) * 2) #define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2) struct dmard09_data { @@ -116,7 +116,6 @@ static int dmard09_probe(struct i2c_client *client, } i2c_set_clientdata(client, indio_dev); - indio_dev->dev.parent = &client->dev; indio_dev->name = DMARD09_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = dmard09_channels; @@ -127,7 +126,7 @@ static int dmard09_probe(struct i2c_client *client, } static const struct i2c_device_id dmard09_id[] = { - { "dmard09", 0}, + { "dmard09", 0 }, { }, }; diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c index 71c852b8bb3e..8ac62ec0a04a 100644 --- a/drivers/iio/accel/dmard10.c +++ b/drivers/iio/accel/dmard10.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the 3-axis accelerometer Domintech ARD10. * * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> @@ -170,6 +170,11 @@ static const struct iio_info dmard10_info = { .read_raw = dmard10_read_raw, }; +static void dmard10_shutdown_cleanup(void *client) +{ + dmard10_shutdown(client); +} + static int dmard10_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -194,9 +199,7 @@ static int dmard10_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; - i2c_set_clientdata(client, indio_dev); - indio_dev->dev.parent = &client->dev; indio_dev->info = &dmard10_info; indio_dev->name = "dmard10"; indio_dev->modes = INDIO_DIRECT_MODE; @@ -207,25 +210,14 @@ static int dmard10_probe(struct i2c_client *client, if (ret < 0) return ret; - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "device_register failed\n"); - dmard10_shutdown(client); - } - - return ret; -} - -static int dmard10_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - iio_device_unregister(indio_dev); + ret = devm_add_action_or_reset(&client->dev, dmard10_shutdown_cleanup, + client); + if (ret) + return ret; - return dmard10_shutdown(client); + return devm_iio_device_register(&client->dev, indio_dev); } -#ifdef CONFIG_PM_SLEEP static int dmard10_suspend(struct device *dev) { return dmard10_shutdown(to_i2c_client(dev)); @@ -235,9 +227,9 @@ static int dmard10_resume(struct device *dev) { return dmard10_reset(to_i2c_client(dev)); } -#endif -static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, + dmard10_resume); static const struct i2c_device_id dmard10_i2c_id[] = { {"dmard10", 0}, @@ -248,10 +240,9 @@ MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id); static struct i2c_driver dmard10_driver = { .driver = { .name = "dmard10", - .pm = &dmard10_pm_ops, + .pm = pm_sleep_ptr(&dmard10_pm_ops), }, .probe = dmard10_probe, - .remove = dmard10_remove, .id_table = dmard10_i2c_id, }; diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c new file mode 100644 index 000000000000..8874d6d61725 --- /dev/null +++ b/drivers/iio/accel/fxls8962af-core.c @@ -0,0 +1,1312 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NXP FXLS8962AF/FXLS8964AF Accelerometer Core Driver + * + * Copyright 2021 Connected Cars A/S + * + * Datasheet: + * https://www.nxp.com/docs/en/data-sheet/FXLS8962AF.pdf + * https://www.nxp.com/docs/en/data-sheet/FXLS8964AF.pdf + * + * Errata: + * https://www.nxp.com/docs/en/errata/ES_FXLS8962AF.pdf + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> + +#include "fxls8962af.h" + +#define FXLS8962AF_INT_STATUS 0x00 +#define FXLS8962AF_INT_STATUS_SRC_BOOT BIT(0) +#define FXLS8962AF_INT_STATUS_SRC_SDCD_OT BIT(4) +#define FXLS8962AF_INT_STATUS_SRC_BUF BIT(5) +#define FXLS8962AF_INT_STATUS_SRC_DRDY BIT(7) +#define FXLS8962AF_TEMP_OUT 0x01 +#define FXLS8962AF_VECM_LSB 0x02 +#define FXLS8962AF_OUT_X_LSB 0x04 +#define FXLS8962AF_OUT_Y_LSB 0x06 +#define FXLS8962AF_OUT_Z_LSB 0x08 +#define FXLS8962AF_BUF_STATUS 0x0b +#define FXLS8962AF_BUF_STATUS_BUF_CNT GENMASK(5, 0) +#define FXLS8962AF_BUF_STATUS_BUF_OVF BIT(6) +#define FXLS8962AF_BUF_STATUS_BUF_WMRK BIT(7) +#define FXLS8962AF_BUF_X_LSB 0x0c +#define FXLS8962AF_BUF_Y_LSB 0x0e +#define FXLS8962AF_BUF_Z_LSB 0x10 + +#define FXLS8962AF_PROD_REV 0x12 +#define FXLS8962AF_WHO_AM_I 0x13 + +#define FXLS8962AF_SYS_MODE 0x14 +#define FXLS8962AF_SENS_CONFIG1 0x15 +#define FXLS8962AF_SENS_CONFIG1_ACTIVE BIT(0) +#define FXLS8962AF_SENS_CONFIG1_RST BIT(7) +#define FXLS8962AF_SC1_FSR_MASK GENMASK(2, 1) +#define FXLS8962AF_SC1_FSR_PREP(x) FIELD_PREP(FXLS8962AF_SC1_FSR_MASK, (x)) +#define FXLS8962AF_SC1_FSR_GET(x) FIELD_GET(FXLS8962AF_SC1_FSR_MASK, (x)) + +#define FXLS8962AF_SENS_CONFIG2 0x16 +#define FXLS8962AF_SENS_CONFIG3 0x17 +#define FXLS8962AF_SC3_WAKE_ODR_MASK GENMASK(7, 4) +#define FXLS8962AF_SC3_WAKE_ODR_PREP(x) FIELD_PREP(FXLS8962AF_SC3_WAKE_ODR_MASK, (x)) +#define FXLS8962AF_SC3_WAKE_ODR_GET(x) FIELD_GET(FXLS8962AF_SC3_WAKE_ODR_MASK, (x)) +#define FXLS8962AF_SENS_CONFIG4 0x18 +#define FXLS8962AF_SC4_INT_PP_OD_MASK BIT(1) +#define FXLS8962AF_SC4_INT_PP_OD_PREP(x) FIELD_PREP(FXLS8962AF_SC4_INT_PP_OD_MASK, (x)) +#define FXLS8962AF_SC4_INT_POL_MASK BIT(0) +#define FXLS8962AF_SC4_INT_POL_PREP(x) FIELD_PREP(FXLS8962AF_SC4_INT_POL_MASK, (x)) +#define FXLS8962AF_SENS_CONFIG5 0x19 + +#define FXLS8962AF_WAKE_IDLE_LSB 0x1b +#define FXLS8962AF_SLEEP_IDLE_LSB 0x1c +#define FXLS8962AF_ASLP_COUNT_LSB 0x1e + +#define FXLS8962AF_INT_EN 0x20 +#define FXLS8962AF_INT_EN_SDCD_OT_EN BIT(5) +#define FXLS8962AF_INT_EN_BUF_EN BIT(6) +#define FXLS8962AF_INT_PIN_SEL 0x21 +#define FXLS8962AF_INT_PIN_SEL_MASK GENMASK(7, 0) +#define FXLS8962AF_INT_PIN_SEL_INT1 0x00 +#define FXLS8962AF_INT_PIN_SEL_INT2 GENMASK(7, 0) + +#define FXLS8962AF_OFF_X 0x22 +#define FXLS8962AF_OFF_Y 0x23 +#define FXLS8962AF_OFF_Z 0x24 + +#define FXLS8962AF_BUF_CONFIG1 0x26 +#define FXLS8962AF_BC1_BUF_MODE_MASK GENMASK(6, 5) +#define FXLS8962AF_BC1_BUF_MODE_PREP(x) FIELD_PREP(FXLS8962AF_BC1_BUF_MODE_MASK, (x)) +#define FXLS8962AF_BUF_CONFIG2 0x27 +#define FXLS8962AF_BUF_CONFIG2_BUF_WMRK GENMASK(5, 0) + +#define FXLS8962AF_ORIENT_STATUS 0x28 +#define FXLS8962AF_ORIENT_CONFIG 0x29 +#define FXLS8962AF_ORIENT_DBCOUNT 0x2a +#define FXLS8962AF_ORIENT_BF_ZCOMP 0x2b +#define FXLS8962AF_ORIENT_THS_REG 0x2c + +#define FXLS8962AF_SDCD_INT_SRC1 0x2d +#define FXLS8962AF_SDCD_INT_SRC1_X_OT BIT(5) +#define FXLS8962AF_SDCD_INT_SRC1_X_POL BIT(4) +#define FXLS8962AF_SDCD_INT_SRC1_Y_OT BIT(3) +#define FXLS8962AF_SDCD_INT_SRC1_Y_POL BIT(2) +#define FXLS8962AF_SDCD_INT_SRC1_Z_OT BIT(1) +#define FXLS8962AF_SDCD_INT_SRC1_Z_POL BIT(0) +#define FXLS8962AF_SDCD_INT_SRC2 0x2e +#define FXLS8962AF_SDCD_CONFIG1 0x2f +#define FXLS8962AF_SDCD_CONFIG1_Z_OT_EN BIT(3) +#define FXLS8962AF_SDCD_CONFIG1_Y_OT_EN BIT(4) +#define FXLS8962AF_SDCD_CONFIG1_X_OT_EN BIT(5) +#define FXLS8962AF_SDCD_CONFIG1_OT_ELE BIT(7) +#define FXLS8962AF_SDCD_CONFIG2 0x30 +#define FXLS8962AF_SDCD_CONFIG2_SDCD_EN BIT(7) +#define FXLS8962AF_SC2_REF_UPDM_AC GENMASK(6, 5) +#define FXLS8962AF_SDCD_OT_DBCNT 0x31 +#define FXLS8962AF_SDCD_WT_DBCNT 0x32 +#define FXLS8962AF_SDCD_LTHS_LSB 0x33 +#define FXLS8962AF_SDCD_UTHS_LSB 0x35 + +#define FXLS8962AF_SELF_TEST_CONFIG1 0x37 +#define FXLS8962AF_SELF_TEST_CONFIG2 0x38 + +#define FXLS8962AF_MAX_REG 0x38 + +#define FXLS8962AF_DEVICE_ID 0x62 +#define FXLS8964AF_DEVICE_ID 0x84 + +/* Raw temp channel offset */ +#define FXLS8962AF_TEMP_CENTER_VAL 25 + +#define FXLS8962AF_AUTO_SUSPEND_DELAY_MS 2000 + +#define FXLS8962AF_FIFO_LENGTH 32 +#define FXLS8962AF_SCALE_TABLE_LEN 4 +#define FXLS8962AF_SAMP_FREQ_TABLE_LEN 13 + +static const int fxls8962af_scale_table[FXLS8962AF_SCALE_TABLE_LEN][2] = { + {0, IIO_G_TO_M_S_2(980000)}, + {0, IIO_G_TO_M_S_2(1950000)}, + {0, IIO_G_TO_M_S_2(3910000)}, + {0, IIO_G_TO_M_S_2(7810000)}, +}; + +static const int fxls8962af_samp_freq_table[FXLS8962AF_SAMP_FREQ_TABLE_LEN][2] = { + {3200, 0}, {1600, 0}, {800, 0}, {400, 0}, {200, 0}, {100, 0}, + {50, 0}, {25, 0}, {12, 500000}, {6, 250000}, {3, 125000}, + {1, 563000}, {0, 781000}, +}; + +struct fxls8962af_chip_info { + const char *name; + const struct iio_chan_spec *channels; + int num_channels; + u8 chip_id; +}; + +struct fxls8962af_data { + struct regmap *regmap; + const struct fxls8962af_chip_info *chip_info; + struct regulator *vdd_reg; + struct { + __le16 channels[3]; + s64 ts __aligned(8); + } scan; + int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ + struct iio_mount_matrix orientation; + int irq; + u8 watermark; + u8 enable_event; + u16 lower_thres; + u16 upper_thres; +}; + +const struct regmap_config fxls8962af_i2c_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = FXLS8962AF_MAX_REG, +}; +EXPORT_SYMBOL_NS_GPL(fxls8962af_i2c_regmap_conf, IIO_FXLS8962AF); + +const struct regmap_config fxls8962af_spi_regmap_conf = { + .reg_bits = 8, + .pad_bits = 8, + .val_bits = 8, + .max_register = FXLS8962AF_MAX_REG, +}; +EXPORT_SYMBOL_NS_GPL(fxls8962af_spi_regmap_conf, IIO_FXLS8962AF); + +enum { + fxls8962af_idx_x, + fxls8962af_idx_y, + fxls8962af_idx_z, + fxls8962af_idx_ts, +}; + +enum fxls8962af_int_pin { + FXLS8962AF_PIN_INT1, + FXLS8962AF_PIN_INT2, +}; + +static int fxls8962af_power_on(struct fxls8962af_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + dev_err(dev, "failed to power on\n"); + + return ret; +} + +static int fxls8962af_power_off(struct fxls8962af_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + if (ret) + dev_err(dev, "failed to power off\n"); + + return ret; +} + +static int fxls8962af_standby(struct fxls8962af_data *data) +{ + return regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SENS_CONFIG1_ACTIVE, 0); +} + +static int fxls8962af_active(struct fxls8962af_data *data) +{ + return regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SENS_CONFIG1_ACTIVE, 1); +} + +static int fxls8962af_is_active(struct fxls8962af_data *data) +{ + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG1, ®); + if (ret) + return ret; + + return reg & FXLS8962AF_SENS_CONFIG1_ACTIVE; +} + +static int fxls8962af_get_out(struct fxls8962af_data *data, + struct iio_chan_spec const *chan, int *val) +{ + struct device *dev = regmap_get_device(data->regmap); + __le16 raw_val; + int is_active; + int ret; + + is_active = fxls8962af_is_active(data); + if (!is_active) { + ret = fxls8962af_power_on(data); + if (ret) + return ret; + } + + ret = regmap_bulk_read(data->regmap, chan->address, + &raw_val, sizeof(data->lower_thres)); + + if (!is_active) + fxls8962af_power_off(data); + + if (ret) { + dev_err(dev, "failed to get out reg 0x%lx\n", chan->address); + return ret; + } + + *val = sign_extend32(le16_to_cpu(raw_val), + chan->scan_type.realbits - 1); + + return IIO_VAL_INT; +} + +static int fxls8962af_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_NANO; + *vals = (int *)fxls8962af_scale_table; + *length = ARRAY_SIZE(fxls8962af_scale_table) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = (int *)fxls8962af_samp_freq_table; + *length = ARRAY_SIZE(fxls8962af_samp_freq_table) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int fxls8962af_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return IIO_VAL_INT_PLUS_NANO; + } +} + +static int fxls8962af_update_config(struct fxls8962af_data *data, u8 reg, + u8 mask, u8 val) +{ + int ret; + int is_active; + + is_active = fxls8962af_is_active(data); + if (is_active) { + ret = fxls8962af_standby(data); + if (ret) + return ret; + } + + ret = regmap_update_bits(data->regmap, reg, mask, val); + if (ret) + return ret; + + if (is_active) { + ret = fxls8962af_active(data); + if (ret) + return ret; + } + + return 0; +} + +static int fxls8962af_set_full_scale(struct fxls8962af_data *data, u32 scale) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fxls8962af_scale_table); i++) + if (scale == fxls8962af_scale_table[i][1]) + break; + + if (i == ARRAY_SIZE(fxls8962af_scale_table)) + return -EINVAL; + + return fxls8962af_update_config(data, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SC1_FSR_MASK, + FXLS8962AF_SC1_FSR_PREP(i)); +} + +static unsigned int fxls8962af_read_full_scale(struct fxls8962af_data *data, + int *val) +{ + int ret; + unsigned int reg; + u8 range_idx; + + ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG1, ®); + if (ret) + return ret; + + range_idx = FXLS8962AF_SC1_FSR_GET(reg); + + *val = fxls8962af_scale_table[range_idx][1]; + + return IIO_VAL_INT_PLUS_NANO; +} + +static int fxls8962af_set_samp_freq(struct fxls8962af_data *data, u32 val, + u32 val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fxls8962af_samp_freq_table); i++) + if (val == fxls8962af_samp_freq_table[i][0] && + val2 == fxls8962af_samp_freq_table[i][1]) + break; + + if (i == ARRAY_SIZE(fxls8962af_samp_freq_table)) + return -EINVAL; + + return fxls8962af_update_config(data, FXLS8962AF_SENS_CONFIG3, + FXLS8962AF_SC3_WAKE_ODR_MASK, + FXLS8962AF_SC3_WAKE_ODR_PREP(i)); +} + +static unsigned int fxls8962af_read_samp_freq(struct fxls8962af_data *data, + int *val, int *val2) +{ + int ret; + unsigned int reg; + u8 range_idx; + + ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG3, ®); + if (ret) + return ret; + + range_idx = FXLS8962AF_SC3_WAKE_ODR_GET(reg); + + *val = fxls8962af_samp_freq_table[range_idx][0]; + *val2 = fxls8962af_samp_freq_table[range_idx][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int fxls8962af_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + case IIO_ACCEL: + return fxls8962af_get_out(data, chan, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + if (chan->type != IIO_TEMP) + return -EINVAL; + + *val = FXLS8962AF_TEMP_CENTER_VAL; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + return fxls8962af_read_full_scale(data, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return fxls8962af_read_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static int fxls8962af_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val != 0) + return -EINVAL; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = fxls8962af_set_full_scale(data, val2); + + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = fxls8962af_set_samp_freq(data, val, val2); + + iio_device_release_direct_mode(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int fxls8962af_event_setup(struct fxls8962af_data *data, int state) +{ + /* Enable wakeup interrupt */ + int mask = FXLS8962AF_INT_EN_SDCD_OT_EN; + int value = state ? mask : 0; + + return regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, mask, value); +} + +static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (val > FXLS8962AF_FIFO_LENGTH) + val = FXLS8962AF_FIFO_LENGTH; + + data->watermark = val; + + return 0; +} + +static int __fxls8962af_set_thresholds(struct fxls8962af_data *data, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, + int val) +{ + switch (dir) { + case IIO_EV_DIR_FALLING: + data->lower_thres = val; + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, + &data->lower_thres, sizeof(data->lower_thres)); + case IIO_EV_DIR_RISING: + data->upper_thres = val; + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, + &data->upper_thres, sizeof(data->upper_thres)); + default: + return -EINVAL; + } +} + +static int fxls8962af_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (dir) { + case IIO_EV_DIR_FALLING: + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, + &data->lower_thres, sizeof(data->lower_thres)); + if (ret) + return ret; + + *val = sign_extend32(data->lower_thres, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_EV_DIR_RISING: + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, + &data->upper_thres, sizeof(data->upper_thres)); + if (ret) + return ret; + + *val = sign_extend32(data->upper_thres, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int fxls8962af_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret, val_masked; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (val < -2048 || val > 2047) + return -EINVAL; + + if (data->enable_event) + return -EBUSY; + + val_masked = val & GENMASK(11, 0); + if (fxls8962af_is_active(data)) { + ret = fxls8962af_standby(data); + if (ret) + return ret; + + ret = __fxls8962af_set_thresholds(data, chan, dir, val_masked); + if (ret) + return ret; + + return fxls8962af_active(data); + } else { + return __fxls8962af_set_thresholds(data, chan, dir, val_masked); + } +} + +static int +fxls8962af_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + return !!(FXLS8962AF_SDCD_CONFIG1_X_OT_EN & data->enable_event); + case IIO_MOD_Y: + return !!(FXLS8962AF_SDCD_CONFIG1_Y_OT_EN & data->enable_event); + case IIO_MOD_Z: + return !!(FXLS8962AF_SDCD_CONFIG1_Z_OT_EN & data->enable_event); + default: + return -EINVAL; + } +} + +static int +fxls8962af_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + u8 enable_event, enable_bits; + int ret, value; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + enable_bits = FXLS8962AF_SDCD_CONFIG1_X_OT_EN; + break; + case IIO_MOD_Y: + enable_bits = FXLS8962AF_SDCD_CONFIG1_Y_OT_EN; + break; + case IIO_MOD_Z: + enable_bits = FXLS8962AF_SDCD_CONFIG1_Z_OT_EN; + break; + default: + return -EINVAL; + } + + if (state) + enable_event = data->enable_event | enable_bits; + else + enable_event = data->enable_event & ~enable_bits; + + if (data->enable_event == enable_event) + return 0; + + ret = fxls8962af_standby(data); + if (ret) + return ret; + + /* Enable events */ + value = enable_event | FXLS8962AF_SDCD_CONFIG1_OT_ELE; + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG1, value); + if (ret) + return ret; + + /* + * Enable update of SDCD_REF_X/Y/Z values with the current decimated and + * trimmed X/Y/Z acceleration input data. This allows for acceleration + * slope detection with Data(n) to Data(n–1) always used as the input + * to the window comparator. + */ + value = enable_event ? + FXLS8962AF_SDCD_CONFIG2_SDCD_EN | FXLS8962AF_SC2_REF_UPDM_AC : + 0x00; + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG2, value); + if (ret) + return ret; + + ret = fxls8962af_event_setup(data, state); + if (ret) + return ret; + + data->enable_event = enable_event; + + if (data->enable_event) { + fxls8962af_active(data); + ret = fxls8962af_power_on(data); + } else { + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + /* Not in buffered mode so disable power */ + ret = fxls8962af_power_off(data); + + iio_device_release_direct_mode(indio_dev); + } + + return ret; +} + +static const struct iio_event_spec fxls8962af_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + +#define FXLS8962AF_CHANNEL(axis, reg, idx) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_BE, \ + }, \ + .event_spec = fxls8962af_event, \ + .num_event_specs = ARRAY_SIZE(fxls8962af_event), \ +} + +#define FXLS8962AF_TEMP_CHANNEL { \ + .type = IIO_TEMP, \ + .address = FXLS8962AF_TEMP_OUT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET),\ + .scan_index = -1, \ + .scan_type = { \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ +} + +static const struct iio_chan_spec fxls8962af_channels[] = { + FXLS8962AF_CHANNEL(X, FXLS8962AF_OUT_X_LSB, fxls8962af_idx_x), + FXLS8962AF_CHANNEL(Y, FXLS8962AF_OUT_Y_LSB, fxls8962af_idx_y), + FXLS8962AF_CHANNEL(Z, FXLS8962AF_OUT_Z_LSB, fxls8962af_idx_z), + IIO_CHAN_SOFT_TIMESTAMP(fxls8962af_idx_ts), + FXLS8962AF_TEMP_CHANNEL, +}; + +static const struct fxls8962af_chip_info fxls_chip_info_table[] = { + [fxls8962af] = { + .chip_id = FXLS8962AF_DEVICE_ID, + .name = "fxls8962af", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, + [fxls8964af] = { + .chip_id = FXLS8964AF_DEVICE_ID, + .name = "fxls8964af", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, +}; + +static const struct iio_info fxls8962af_info = { + .read_raw = &fxls8962af_read_raw, + .write_raw = &fxls8962af_write_raw, + .write_raw_get_fmt = fxls8962af_write_raw_get_fmt, + .read_event_value = fxls8962af_read_event, + .write_event_value = fxls8962af_write_event, + .read_event_config = fxls8962af_read_event_config, + .write_event_config = fxls8962af_write_event_config, + .read_avail = fxls8962af_read_avail, + .hwfifo_set_watermark = fxls8962af_set_watermark, +}; + +static int fxls8962af_reset(struct fxls8962af_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int reg; + int ret; + + ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SENS_CONFIG1_RST, + FXLS8962AF_SENS_CONFIG1_RST); + if (ret) + return ret; + + /* TBOOT1, TBOOT2, specifies we have to wait between 1 - 17.7ms */ + ret = regmap_read_poll_timeout(data->regmap, FXLS8962AF_INT_STATUS, reg, + (reg & FXLS8962AF_INT_STATUS_SRC_BOOT), + 1000, 18000); + if (ret == -ETIMEDOUT) + dev_err(dev, "reset timeout, int_status = 0x%x\n", reg); + + return ret; +} + +static int __fxls8962af_fifo_set_mode(struct fxls8962af_data *data, bool onoff) +{ + int ret; + + /* Enable watermark at max fifo size */ + ret = regmap_update_bits(data->regmap, FXLS8962AF_BUF_CONFIG2, + FXLS8962AF_BUF_CONFIG2_BUF_WMRK, + data->watermark); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, FXLS8962AF_BUF_CONFIG1, + FXLS8962AF_BC1_BUF_MODE_MASK, + FXLS8962AF_BC1_BUF_MODE_PREP(onoff)); +} + +static int fxls8962af_buffer_preenable(struct iio_dev *indio_dev) +{ + return fxls8962af_power_on(iio_priv(indio_dev)); +} + +static int fxls8962af_buffer_postenable(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + fxls8962af_standby(data); + + /* Enable buffer interrupt */ + ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, + FXLS8962AF_INT_EN_BUF_EN, + FXLS8962AF_INT_EN_BUF_EN); + if (ret) + return ret; + + ret = __fxls8962af_fifo_set_mode(data, true); + + fxls8962af_active(data); + + return ret; +} + +static int fxls8962af_buffer_predisable(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + fxls8962af_standby(data); + + /* Disable buffer interrupt */ + ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, + FXLS8962AF_INT_EN_BUF_EN, 0); + if (ret) + return ret; + + ret = __fxls8962af_fifo_set_mode(data, false); + + if (data->enable_event) + fxls8962af_active(data); + + return ret; +} + +static int fxls8962af_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (!data->enable_event) + fxls8962af_power_off(data); + + return 0; +} + +static const struct iio_buffer_setup_ops fxls8962af_buffer_ops = { + .preenable = fxls8962af_buffer_preenable, + .postenable = fxls8962af_buffer_postenable, + .predisable = fxls8962af_buffer_predisable, + .postdisable = fxls8962af_buffer_postdisable, +}; + +static int fxls8962af_i2c_raw_read_errata3(struct fxls8962af_data *data, + u16 *buffer, int samples, + int sample_length) +{ + int i, ret; + + for (i = 0; i < samples; i++) { + ret = regmap_raw_read(data->regmap, FXLS8962AF_BUF_X_LSB, + &buffer[i * 3], sample_length); + if (ret) + return ret; + } + + return 0; +} + +static int fxls8962af_fifo_transfer(struct fxls8962af_data *data, + u16 *buffer, int samples) +{ + struct device *dev = regmap_get_device(data->regmap); + int sample_length = 3 * sizeof(*buffer); + int total_length = samples * sample_length; + int ret; + + if (i2c_verify_client(dev)) + /* + * Due to errata bug: + * E3: FIFO burst read operation error using I2C interface + * We have to avoid burst reads on I2C.. + */ + ret = fxls8962af_i2c_raw_read_errata3(data, buffer, samples, + sample_length); + else + ret = regmap_raw_read(data->regmap, FXLS8962AF_BUF_X_LSB, buffer, + total_length); + + if (ret) + dev_err(dev, "Error transferring data from fifo: %d\n", ret); + + return ret; +} + +static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + u16 buffer[FXLS8962AF_FIFO_LENGTH * 3]; + uint64_t sample_period; + unsigned int reg; + int64_t tstamp; + int ret, i; + u8 count; + + ret = regmap_read(data->regmap, FXLS8962AF_BUF_STATUS, ®); + if (ret) + return ret; + + if (reg & FXLS8962AF_BUF_STATUS_BUF_OVF) { + dev_err(dev, "Buffer overflow"); + return -EOVERFLOW; + } + + count = reg & FXLS8962AF_BUF_STATUS_BUF_CNT; + if (!count) + return 0; + + data->old_timestamp = data->timestamp; + data->timestamp = iio_get_time_ns(indio_dev); + + /* + * Approximate timestamps for each of the sample based on the sampling, + * frequency, timestamp for last sample and number of samples. + */ + sample_period = (data->timestamp - data->old_timestamp); + do_div(sample_period, count); + tstamp = data->timestamp - (count - 1) * sample_period; + + ret = fxls8962af_fifo_transfer(data, buffer, count); + if (ret) + return ret; + + /* Demux hw FIFO into kfifo. */ + for (i = 0; i < count; i++) { + int j, bit; + + j = 0; + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit], + sizeof(data->scan.channels[0])); + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + tstamp); + + tstamp += sample_period; + } + + return count; +} + +static int fxls8962af_event_interrupt(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + s64 ts = iio_get_time_ns(indio_dev); + unsigned int reg; + u64 ev_code; + int ret; + + ret = regmap_read(data->regmap, FXLS8962AF_SDCD_INT_SRC1, ®); + if (ret) + return ret; + + if (reg & FXLS8962AF_SDCD_INT_SRC1_X_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_X_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + if (reg & FXLS8962AF_SDCD_INT_SRC1_Y_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Y_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + if (reg & FXLS8962AF_SDCD_INT_SRC1_Z_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Z_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + return 0; +} + +static irqreturn_t fxls8962af_interrupt(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct fxls8962af_data *data = iio_priv(indio_dev); + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, FXLS8962AF_INT_STATUS, ®); + if (ret) + return IRQ_NONE; + + if (reg & FXLS8962AF_INT_STATUS_SRC_BUF) { + ret = fxls8962af_fifo_flush(indio_dev); + if (ret < 0) + return IRQ_NONE; + + return IRQ_HANDLED; + } + + if (reg & FXLS8962AF_INT_STATUS_SRC_SDCD_OT) { + ret = fxls8962af_event_interrupt(indio_dev); + if (ret < 0) + return IRQ_NONE; + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void fxls8962af_regulator_disable(void *data_ptr) +{ + struct fxls8962af_data *data = data_ptr; + + regulator_disable(data->vdd_reg); +} + +static void fxls8962af_pm_disable(void *dev_ptr) +{ + struct device *dev = dev_ptr; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + + fxls8962af_standby(iio_priv(indio_dev)); +} + +static void fxls8962af_get_irq(struct device_node *of_node, + enum fxls8962af_int_pin *pin) +{ + int irq; + + irq = of_irq_get_byname(of_node, "INT2"); + if (irq > 0) { + *pin = FXLS8962AF_PIN_INT2; + return; + } + + *pin = FXLS8962AF_PIN_INT1; +} + +static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned long irq_type; + bool irq_active_high; + enum fxls8962af_int_pin int_pin; + u8 int_pin_sel; + int ret; + + fxls8962af_get_irq(dev->of_node, &int_pin); + switch (int_pin) { + case FXLS8962AF_PIN_INT1: + int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT1; + break; + case FXLS8962AF_PIN_INT2: + int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT2; + break; + default: + dev_err(dev, "unsupported int pin selected\n"); + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_PIN_SEL, + FXLS8962AF_INT_PIN_SEL_MASK, int_pin_sel); + if (ret) + return ret; + + irq_type = irqd_get_trigger_type(irq_get_irq_data(irq)); + + switch (irq_type) { + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + irq_active_high = true; + break; + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_FALLING: + irq_active_high = false; + break; + default: + dev_info(dev, "mode %lx unsupported\n", irq_type); + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG4, + FXLS8962AF_SC4_INT_POL_MASK, + FXLS8962AF_SC4_INT_POL_PREP(irq_active_high)); + if (ret) + return ret; + + if (device_property_read_bool(dev, "drive-open-drain")) { + ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG4, + FXLS8962AF_SC4_INT_PP_OD_MASK, + FXLS8962AF_SC4_INT_PP_OD_PREP(1)); + if (ret) + return ret; + + irq_type |= IRQF_SHARED; + } + + return devm_request_threaded_irq(dev, + irq, + NULL, fxls8962af_interrupt, + irq_type | IRQF_ONESHOT, + indio_dev->name, indio_dev); +} + +int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) +{ + struct fxls8962af_data *data; + struct iio_dev *indio_dev; + unsigned int reg; + int ret, i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + data->regmap = regmap; + data->irq = irq; + + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return ret; + + data->vdd_reg = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd_reg)) + return dev_err_probe(dev, PTR_ERR(data->vdd_reg), + "Failed to get vdd regulator\n"); + + ret = regulator_enable(data->vdd_reg); + if (ret) { + dev_err(dev, "Failed to enable vdd regulator: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, fxls8962af_regulator_disable, data); + if (ret) + return ret; + + ret = regmap_read(data->regmap, FXLS8962AF_WHO_AM_I, ®); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(fxls_chip_info_table); i++) { + if (fxls_chip_info_table[i].chip_id == reg) { + data->chip_info = &fxls_chip_info_table[i]; + break; + } + } + if (i == ARRAY_SIZE(fxls_chip_info_table)) { + dev_err(dev, "failed to match device in table\n"); + return -ENXIO; + } + + indio_dev->channels = data->chip_info->channels; + indio_dev->num_channels = data->chip_info->num_channels; + indio_dev->name = data->chip_info->name; + indio_dev->info = &fxls8962af_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = fxls8962af_reset(data); + if (ret) + return ret; + + if (irq) { + ret = fxls8962af_irq_setup(indio_dev, irq); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + &fxls8962af_buffer_ops); + if (ret) + return ret; + } + + ret = pm_runtime_set_active(dev); + if (ret) + return ret; + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, FXLS8962AF_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + ret = devm_add_action_or_reset(dev, fxls8962af_pm_disable, dev); + if (ret) + return ret; + + if (device_property_read_bool(dev, "wakeup-source")) + device_init_wakeup(dev, true); + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(fxls8962af_core_probe, IIO_FXLS8962AF); + +static int __maybe_unused fxls8962af_runtime_suspend(struct device *dev) +{ + struct fxls8962af_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = fxls8962af_standby(data); + if (ret) { + dev_err(dev, "powering off device failed\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused fxls8962af_runtime_resume(struct device *dev) +{ + struct fxls8962af_data *data = iio_priv(dev_get_drvdata(dev)); + + return fxls8962af_active(data); +} + +static int __maybe_unused fxls8962af_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (device_may_wakeup(dev) && data->enable_event) { + enable_irq_wake(data->irq); + + /* + * Disable buffer, as the buffer is so small the device will wake + * almost immediately. + */ + if (iio_buffer_enabled(indio_dev)) + fxls8962af_buffer_predisable(indio_dev); + } else { + fxls8962af_runtime_suspend(dev); + } + + return 0; +} + +static int __maybe_unused fxls8962af_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (device_may_wakeup(dev) && data->enable_event) { + disable_irq_wake(data->irq); + + if (iio_buffer_enabled(indio_dev)) + fxls8962af_buffer_postenable(indio_dev); + } else { + fxls8962af_runtime_resume(dev); + } + + return 0; +} + +const struct dev_pm_ops fxls8962af_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fxls8962af_suspend, fxls8962af_resume) + SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend, + fxls8962af_runtime_resume, NULL) +}; +EXPORT_SYMBOL_NS_GPL(fxls8962af_pm_ops, IIO_FXLS8962AF); + +MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); +MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c new file mode 100644 index 000000000000..8fbadfea1620 --- /dev/null +++ b/drivers/iio/accel/fxls8962af-i2c.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NXP FXLS8962AF/FXLS8964AF Accelerometer I2C Driver + * + * Copyright 2021 Connected Cars A/S + */ + +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "fxls8962af.h" + +static int fxls8962af_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &fxls8962af_i2c_regmap_conf); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to initialize i2c regmap\n"); + return PTR_ERR(regmap); + } + + return fxls8962af_core_probe(&client->dev, regmap, client->irq); +} + +static const struct i2c_device_id fxls8962af_id[] = { + { "fxls8962af", fxls8962af }, + { "fxls8964af", fxls8964af }, + {} +}; +MODULE_DEVICE_TABLE(i2c, fxls8962af_id); + +static const struct of_device_id fxls8962af_of_match[] = { + { .compatible = "nxp,fxls8962af" }, + { .compatible = "nxp,fxls8964af" }, + {} +}; +MODULE_DEVICE_TABLE(of, fxls8962af_of_match); + +static struct i2c_driver fxls8962af_driver = { + .driver = { + .name = "fxls8962af_i2c", + .of_match_table = fxls8962af_of_match, + .pm = &fxls8962af_pm_ops, + }, + .probe_new = fxls8962af_probe, + .id_table = fxls8962af_id, +}; +module_i2c_driver(fxls8962af_driver); + +MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); +MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer i2c driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_FXLS8962AF); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c new file mode 100644 index 000000000000..885b3ab7fcb5 --- /dev/null +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NXP FXLS8962AF/FXLS8964AF Accelerometer SPI Driver + * + * Copyright 2021 Connected Cars A/S + */ + +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> + +#include "fxls8962af.h" + +static int fxls8962af_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &fxls8962af_spi_regmap_conf); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to initialize spi regmap\n"); + return PTR_ERR(regmap); + } + + return fxls8962af_core_probe(&spi->dev, regmap, spi->irq); +} + +static const struct of_device_id fxls8962af_spi_of_match[] = { + { .compatible = "nxp,fxls8962af" }, + { .compatible = "nxp,fxls8964af" }, + {} +}; +MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match); + +static const struct spi_device_id fxls8962af_spi_id_table[] = { + { "fxls8962af", fxls8962af }, + { "fxls8964af", fxls8964af }, + {} +}; +MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table); + +static struct spi_driver fxls8962af_driver = { + .driver = { + .name = "fxls8962af_spi", + .pm = &fxls8962af_pm_ops, + .of_match_table = fxls8962af_spi_of_match, + }, + .probe = fxls8962af_probe, + .id_table = fxls8962af_spi_id_table, +}; +module_spi_driver(fxls8962af_driver); + +MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); +MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer spi driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_FXLS8962AF); diff --git a/drivers/iio/accel/fxls8962af.h b/drivers/iio/accel/fxls8962af.h new file mode 100644 index 000000000000..9cbe98c3ba9a --- /dev/null +++ b/drivers/iio/accel/fxls8962af.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2021 Connected Cars A/S + */ +#ifndef _FXLS8962AF_H_ +#define _FXLS8962AF_H_ + +struct regmap; +struct device; + +enum { + fxls8962af, + fxls8964af, +}; + +int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq); +int fxls8962af_core_remove(struct device *dev); + +extern const struct dev_pm_ops fxls8962af_pm_ops; +extern const struct regmap_config fxls8962af_i2c_regmap_conf; +extern const struct regmap_config fxls8962af_spi_regmap_conf; + +#endif /* _FXLS8962AF_H_ */ diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 0d9e2def2b25..a2def6f9380a 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -6,16 +6,11 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> -#include <linux/iio/trigger_consumer.h> -#include <linux/iio/triggered_buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" enum accel_3d_channel { @@ -25,12 +20,16 @@ enum accel_3d_channel { ACCEL_3D_CHANNEL_MAX, }; +#define CHANNEL_SCAN_INDEX_TIMESTAMP ACCEL_3D_CHANNEL_MAX struct accel_3d_state { struct hid_sensor_hub_callbacks callbacks; struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; - /* Reserve for 3 channels + padding + timestamp */ - u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3]; + /* Ensure timestamp is naturally aligned */ + struct { + u32 accel_val[3]; + s64 timestamp __aligned(8); + } scan; int scale_pre_decml; int scale_post_decml; int scale_precision; @@ -44,6 +43,10 @@ static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ACCEL_Z_AXIS }; +static const u32 accel_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ACCELERATION, +}; + /* Channel definitions */ static const struct iio_chan_spec accel_3d_channels[] = { { @@ -77,7 +80,7 @@ static const struct iio_chan_spec accel_3d_channels[] = { BIT(IIO_CHAN_INFO_HYSTERESIS), .scan_index = CHANNEL_SCAN_INDEX_Z, }, - IIO_CHAN_SOFT_TIMESTAMP(3) + IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) }; /* Channel definitions */ @@ -112,7 +115,8 @@ static const struct iio_chan_spec gravity_channels[] = { BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_HYSTERESIS), .scan_index = CHANNEL_SCAN_INDEX_Z, - } + }, + IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP), }; /* Adjust channel real bits based on report descriptor */ @@ -241,8 +245,8 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, accel_state->timestamp = iio_get_time_ns(indio_dev); hid_sensor_push_data(indio_dev, - accel_state->accel_val, - sizeof(accel_state->accel_val), + &accel_state->scan, + sizeof(accel_state->scan), accel_state->timestamp); accel_state->timestamp = 0; @@ -267,7 +271,7 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, case HID_USAGE_SENSOR_ACCEL_Y_AXIS: case HID_USAGE_SENSOR_ACCEL_Z_AXIS: offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS; - accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] = + accel_state->scan.accel_val[CHANNEL_SCAN_INDEX_X + offset] = *(u32 *)raw_data; ret = 0; break; @@ -317,18 +321,6 @@ static int accel_3d_parse_report(struct platform_device *pdev, &st->accel[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ACCELERATION, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - return ret; } @@ -366,13 +358,17 @@ static int hid_accel_3d_probe(struct platform_device *pdev) channel_size = sizeof(gravity_channels); indio_dev->num_channels = ARRAY_SIZE(gravity_channels); } - ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, - &accel_state->common_attributes); + ret = hid_sensor_parse_common_attributes(hsdev, + hsdev->usage, + &accel_state->common_attributes, + accel_3d_sensitivity_addresses, + ARRAY_SIZE(accel_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; } - indio_dev->channels = kmemdup(channel_spec, channel_size, GFP_KERNEL); + indio_dev->channels = devm_kmemdup(&pdev->dev, channel_spec, + channel_size, GFP_KERNEL); if (!indio_dev->channels) { dev_err(&pdev->dev, "failed to duplicate channels\n"); @@ -383,26 +379,20 @@ static int hid_accel_3d_probe(struct platform_device *pdev) hsdev->usage, accel_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); - goto error_free_dev_mem; + return ret; } - indio_dev->dev.parent = &pdev->dev; indio_dev->info = &accel_3d_info; indio_dev->name = name; indio_dev->modes = INDIO_DIRECT_MODE; - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - NULL, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); - goto error_free_dev_mem; - } atomic_set(&accel_state->common_attributes.data_ready, 0); + ret = hid_sensor_setup_trigger(indio_dev, name, &accel_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); - goto error_unreg_buffer_funcs; + return ret; } ret = iio_device_register(indio_dev); @@ -426,11 +416,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) error_iio_unreg: iio_device_unregister(indio_dev); error_remove_trigger: - hid_sensor_remove_trigger(&accel_state->common_attributes); -error_unreg_buffer_funcs: - iio_triggered_buffer_cleanup(indio_dev); -error_free_dev_mem: - kfree(indio_dev->channels); + hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); return ret; } @@ -443,9 +429,7 @@ static int hid_accel_3d_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); - hid_sensor_remove_trigger(&accel_state->common_attributes); - iio_triggered_buffer_cleanup(indio_dev); - kfree(indio_dev->channels); + hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); return 0; } @@ -476,3 +460,4 @@ module_platform_driver(hid_accel_3d_platform_driver); MODULE_DESCRIPTION("HID Sensor Accel 3D"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index c9924a65c32a..adc66b3615c0 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -14,6 +14,7 @@ #include <linux/acpi.h> #include <linux/pm.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> @@ -50,13 +51,15 @@ #define KXTF9_REG_TILT_POS_CUR 0x10 #define KXTF9_REG_TILT_POS_PREV 0x11 #define KXTF9_REG_INT_SRC1 0x15 -#define KXCJK1013_REG_INT_SRC1 0x16 /* compatible, but called INT_SRC2 in KXTF9 ds */ +#define KXTF9_REG_INT_SRC2 0x16 +#define KXCJK1013_REG_INT_SRC1 0x16 #define KXCJK1013_REG_INT_SRC2 0x17 #define KXCJK1013_REG_STATUS_REG 0x18 #define KXCJK1013_REG_INT_REL 0x1A #define KXCJK1013_REG_CTRL1 0x1B #define KXTF9_REG_CTRL2 0x1C -#define KXCJK1013_REG_CTRL2 0x1D /* mostly compatible, CTRL_REG3 in KTXF9 ds */ +#define KXTF9_REG_CTRL3 0x1D +#define KXCJK1013_REG_CTRL2 0x1D #define KXCJK1013_REG_INT_CTRL1 0x1E #define KXCJK1013_REG_INT_CTRL2 0x1F #define KXTF9_REG_INT_CTRL3 0x20 @@ -76,6 +79,45 @@ #define KXTF9_REG_HYST_SET 0x5F #define KXCJK1013_REG_WAKE_THRES 0x6A +/* Everything up to 0x11 is equal to KXCJK1013/KXTF9 above */ +#define KX023_REG_INS1 0x12 +#define KX023_REG_INS2 0x13 +#define KX023_REG_INS3 0x14 +#define KX023_REG_STAT 0x15 +#define KX023_REG_INT_REL 0x17 +#define KX023_REG_CNTL1 0x18 +#define KX023_REG_CNTL2 0x19 +#define KX023_REG_CNTL3 0x1A +#define KX023_REG_ODCNTL 0x1B +#define KX023_REG_INC1 0x1C +#define KX023_REG_INC2 0x1D +#define KX023_REG_INC3 0x1E +#define KX023_REG_INC4 0x1F +#define KX023_REG_INC5 0x20 +#define KX023_REG_INC6 0x21 +#define KX023_REG_TILT_TIMER 0x22 +#define KX023_REG_WUFC 0x23 +#define KX023_REG_TDTRC 0x24 +#define KX023_REG_TDTC 0x25 +#define KX023_REG_TTH 0x26 +#define KX023_REG_TTL 0x27 +#define KX023_REG_FTD 0x28 +#define KX023_REG_STD 0x29 +#define KX023_REG_TLT 0x2A +#define KX023_REG_TWS 0x2B +#define KX023_REG_ATH 0x30 +#define KX023_REG_TILT_ANGLE_LL 0x32 +#define KX023_REG_TILT_ANGLE_HL 0x33 +#define KX023_REG_HYST_SET 0x34 +#define KX023_REG_LP_CNTL 0x35 +#define KX023_REG_BUF_CNTL1 0x3A +#define KX023_REG_BUF_CNTL2 0x3B +#define KX023_REG_BUF_STATUS_1 0x3C +#define KX023_REG_BUF_STATUS_2 0x3D +#define KX023_REG_BUF_CLEAR 0x3E +#define KX023_REG_BUF_READ 0x3F +#define KX023_REG_SELF_TEST 0x60 + #define KXCJK1013_REG_CTRL1_BIT_PC1 BIT(7) #define KXCJK1013_REG_CTRL1_BIT_RES BIT(6) #define KXCJK1013_REG_CTRL1_BIT_DRDY BIT(5) @@ -116,6 +158,14 @@ #define KXCJK1013_REG_INT_SRC2_BIT_XP BIT(4) #define KXCJK1013_REG_INT_SRC2_BIT_XN BIT(5) +/* KX023 interrupt routing to INT1. INT2 can be configured with INC6 */ +#define KX023_REG_INC4_BFI1 BIT(6) +#define KX023_REG_INC4_WMI1 BIT(5) +#define KX023_REG_INC4_DRDY1 BIT(4) +#define KX023_REG_INC4_TDTI1 BIT(2) +#define KX023_REG_INC4_WUFI1 BIT(1) +#define KX023_REG_INC4_TPI1 BIT(0) + #define KXCJK1013_DEFAULT_WAKE_THRES 1 enum kx_chipset { @@ -123,16 +173,85 @@ enum kx_chipset { KXCJ91008, KXTJ21009, KXTF9, + KX0231025, KX_MAX_CHIPS /* this must be last */ }; +enum kx_acpi_type { + ACPI_GENERIC, + ACPI_SMO8500, + ACPI_KIOX010A, +}; + +struct kx_chipset_regs { + u8 int_src1; + u8 int_src2; + u8 int_rel; + u8 ctrl1; + u8 wuf_ctrl; + u8 int_ctrl1; + u8 data_ctrl; + u8 wake_timer; + u8 wake_thres; +}; + +static const struct kx_chipset_regs kxcjk1013_regs = { + .int_src1 = KXCJK1013_REG_INT_SRC1, + .int_src2 = KXCJK1013_REG_INT_SRC2, + .int_rel = KXCJK1013_REG_INT_REL, + .ctrl1 = KXCJK1013_REG_CTRL1, + .wuf_ctrl = KXCJK1013_REG_CTRL2, + .int_ctrl1 = KXCJK1013_REG_INT_CTRL1, + .data_ctrl = KXCJK1013_REG_DATA_CTRL, + .wake_timer = KXCJK1013_REG_WAKE_TIMER, + .wake_thres = KXCJK1013_REG_WAKE_THRES, +}; + +static const struct kx_chipset_regs kxtf9_regs = { + /* .int_src1 was moved to INT_SRC2 on KXTF9 */ + .int_src1 = KXTF9_REG_INT_SRC2, + /* .int_src2 is not available */ + .int_rel = KXCJK1013_REG_INT_REL, + .ctrl1 = KXCJK1013_REG_CTRL1, + .wuf_ctrl = KXTF9_REG_CTRL3, + .int_ctrl1 = KXCJK1013_REG_INT_CTRL1, + .data_ctrl = KXCJK1013_REG_DATA_CTRL, + .wake_timer = KXCJK1013_REG_WAKE_TIMER, + .wake_thres = KXTF9_REG_WAKE_THRESH, +}; + +/* The registers have totally different names but the bits are compatible */ +static const struct kx_chipset_regs kx0231025_regs = { + .int_src1 = KX023_REG_INS2, + .int_src2 = KX023_REG_INS3, + .int_rel = KX023_REG_INT_REL, + .ctrl1 = KX023_REG_CNTL1, + .wuf_ctrl = KX023_REG_CNTL3, + .int_ctrl1 = KX023_REG_INC1, + .data_ctrl = KX023_REG_ODCNTL, + .wake_timer = KX023_REG_WUFC, + .wake_thres = KX023_REG_ATH, +}; + +enum kxcjk1013_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + AXIS_MAX +}; + struct kxcjk1013_data { + struct regulator_bulk_data regulators[2]; 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]; + /* Ensure timestamp naturally aligned */ + struct { + s16 chans[AXIS_MAX]; + s64 timestamp __aligned(8); + } scan; u8 odr_bits; u8 range; int wake_thres; @@ -143,14 +262,8 @@ struct kxcjk1013_data { bool motion_trigger_on; int64_t timestamp; enum kx_chipset chipset; - bool is_smo8500_device; -}; - -enum kxcjk1013_axis { - AXIS_X, - AXIS_Y, - AXIS_Z, - AXIS_MAX, + enum kx_acpi_type acpi_type; + const struct kx_chipset_regs *regs; }; enum kxcjk1013_mode { @@ -202,7 +315,7 @@ static const char *const kxtf9_samp_freq_avail = "25 50 100 200 400 800"; /* Refer to section 4 of the specification */ -static const struct { +static __maybe_unused const struct { int odr_bits; int usec; } odr_start_up_times[KX_MAX_CHIPS][12] = { @@ -260,6 +373,22 @@ static const struct { {0x05, 5100}, {0x06, 2700}, }, + /* KX023-1025 */ + { + /* First 4 are not in datasheet, taken from KXCTJ2-1009 */ + {0x08, 1240000}, + {0x09, 621000}, + {0x0A, 309000}, + {0x0B, 151000}, + {0, 81000}, + {0x01, 40000}, + {0x02, 22000}, + {0x03, 12000}, + {0x04, 7000}, + {0x05, 4400}, + {0x06, 3000}, + {0x07, 3000}, + }, }; static const struct { @@ -270,12 +399,38 @@ static const struct { {19163, 1, 0}, {38326, 0, 1} }; +#ifdef CONFIG_ACPI +enum kiox010a_fn_index { + KIOX010A_SET_LAPTOP_MODE = 1, + KIOX010A_SET_TABLET_MODE = 2, +}; + +static int kiox010a_dsm(struct device *dev, int fn_index) +{ + acpi_handle handle = ACPI_HANDLE(dev); + guid_t kiox010a_dsm_guid; + union acpi_object *obj; + + if (!handle) + return -ENODEV; + + guid_parse("1f339696-d475-4e26-8cad-2e9f8e6d7a91", &kiox010a_dsm_guid); + + obj = acpi_evaluate_dsm(handle, &kiox010a_dsm_guid, 1, fn_index, NULL); + if (!obj) + return -EIO; + + ACPI_FREE(obj); + return 0; +} +#endif + static int kxcjk1013_set_mode(struct kxcjk1013_data *data, enum kxcjk1013_mode mode) { int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -286,8 +441,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data, else ret |= KXCJK1013_REG_CTRL1_BIT_PC1; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -301,7 +455,7 @@ static int kxcjk1013_get_mode(struct kxcjk1013_data *data, { int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -319,7 +473,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) { int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -330,9 +484,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3); ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4); - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -347,6 +499,13 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) { int ret; +#ifdef CONFIG_ACPI + if (data->acpi_type == ACPI_KIOX010A) { + /* Make sure the kbd and touchpad on 2-in-1s using 2 KXCJ91008-s work */ + kiox010a_dsm(&data->client->dev, KIOX010A_SET_LAPTOP_MODE); + } +#endif + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_WHO_AM_I); if (ret < 0) { dev_err(&data->client->dev, "Error reading who_am_i\n"); @@ -359,7 +518,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -368,8 +527,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) /* Set 12 bit mode */ ret |= KXCJK1013_REG_CTRL1_BIT_RES; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl\n"); return ret; @@ -380,7 +538,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_DATA_CTRL); + ret = i2c_smbus_read_byte_data(data->client, data->regs->data_ctrl); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_data_ctrl\n"); return ret; @@ -389,7 +547,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) data->odr_bits = ret; /* Set up INT polarity */ - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -400,13 +558,23 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEA; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } + /* On KX023, route all used interrupts to INT1 for now */ + if (data->chipset == KX0231025 && data->client->irq > 0) { + ret = i2c_smbus_write_byte_data(data->client, KX023_REG_INC4, + KX023_REG_INC4_DRDY1 | + KX023_REG_INC4_WUFI1); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_inc4\n"); + return ret; + } + } + ret = kxcjk1013_set_mode(data, OPERATION); if (ret < 0) return ret; @@ -437,7 +605,7 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) int ret; if (on) - ret = pm_runtime_get_sync(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); else { pm_runtime_mark_last_busy(&data->client->dev); ret = pm_runtime_put_autosuspend(&data->client->dev); @@ -445,8 +613,6 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: %s for %d\n", __func__, on); - if (on) - pm_runtime_put_noidle(&data->client->dev); return ret; } #endif @@ -456,10 +622,9 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) { - int waketh_reg, ret; + int ret; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_WAKE_TIMER, + ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_timer, data->wake_dur); if (ret < 0) { dev_err(&data->client->dev, @@ -467,9 +632,7 @@ static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) return ret; } - waketh_reg = data->chipset == KXTF9 ? - KXTF9_REG_WAKE_THRESH : KXCJK1013_REG_WAKE_THRES; - ret = i2c_smbus_write_byte_data(data->client, waketh_reg, + ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_thres, data->wake_thres); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_wake_thres\n"); @@ -498,7 +661,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -509,14 +672,13 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -527,8 +689,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -558,7 +719,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -569,14 +730,13 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -587,8 +747,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -660,7 +819,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) if (ret < 0) return ret; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_DATA_CTRL, + ret = i2c_smbus_write_byte_data(data->client, data->regs->data_ctrl, odr_setting->odr_bits); if (ret < 0) { dev_err(&data->client->dev, "Error writing data_ctrl\n"); @@ -669,7 +828,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) data->odr_bits = odr_setting->odr_bits; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL2, + ret = i2c_smbus_write_byte_data(data->client, data->regs->wuf_ctrl, odr_setting->wuf_bits); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl2\n"); @@ -768,7 +927,8 @@ static int kxcjk1013_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->mutex); return ret; } - *val = sign_extend32(ret >> 4, 11); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); ret = kxcjk1013_set_power_state(data, false); } mutex_unlock(&data->mutex); @@ -904,7 +1064,7 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev, /* * We will expect the enable and disable to do operation in - * in reverse order. This will happen here anyway as our + * reverse order. This will happen here anyway as our * resume operation uses sync mode runtime pm calls, the * suspend operation will be delayed by autosuspend delay * So the disable operation will still happen in reverse of @@ -1027,9 +1187,7 @@ static const struct iio_chan_spec kxcjk1013_channels[] = { static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = { .preenable = kxcjk1013_buffer_preenable, - .postenable = iio_triggered_buffer_postenable, .postdisable = kxcjk1013_buffer_postdisable, - .predisable = iio_triggered_buffer_predisable, }; static const struct iio_info kxcjk1013_info = { @@ -1055,12 +1213,12 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p) ret = i2c_smbus_read_i2c_block_data_or_emulated(data->client, KXCJK1013_REG_XOUT_L, AXIS_MAX * 2, - (u8 *)data->buffer); + (u8 *)data->scan.chans); mutex_unlock(&data->mutex); if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, data->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -1068,19 +1226,15 @@ err: return IRQ_HANDLED; } -static int kxcjk1013_trig_try_reen(struct iio_trigger *trig) +static void kxcjk1013_trig_reen(struct iio_trigger *trig) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct kxcjk1013_data *data = iio_priv(indio_dev); int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL); - if (ret < 0) { + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel); + if (ret < 0) dev_err(&data->client->dev, "Error reading reg_int_rel\n"); - return ret; - } - - return 0; } static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig, @@ -1124,15 +1278,14 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig, static const struct iio_trigger_ops kxcjk1013_trigger_ops = { .set_trigger_state = kxcjk1013_data_rdy_trigger_set_state, - .try_reenable = kxcjk1013_trig_try_reen, + .reenable = kxcjk1013_trig_reen, }; static void kxcjk1013_report_motion_event(struct iio_dev *indio_dev) { struct kxcjk1013_data *data = iio_priv(indio_dev); - int ret = i2c_smbus_read_byte_data(data->client, - KXCJK1013_REG_INT_SRC2); + int ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src2); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_src2\n"); return; @@ -1199,7 +1352,7 @@ static irqreturn_t kxcjk1013_event_handler(int irq, void *private) struct kxcjk1013_data *data = iio_priv(indio_dev); int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_SRC1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_src1\n"); goto ack_intr; @@ -1222,7 +1375,7 @@ ack_intr: if (data->dready_trigger_on) return IRQ_HANDLED; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel); if (ret < 0) dev_err(&data->client->dev, "Error reading reg_int_rel\n"); @@ -1249,7 +1402,8 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private) static const char *kxcjk1013_match_acpi_device(struct device *dev, enum kx_chipset *chipset, - bool *is_smo8500_device) + enum kx_acpi_type *acpi_type, + const char **label) { const struct acpi_device_id *id; @@ -1257,14 +1411,27 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev, if (!id) return NULL; - if (strcmp(id->id, "SMO8500") == 0) - *is_smo8500_device = true; + if (strcmp(id->id, "SMO8500") == 0) { + *acpi_type = ACPI_SMO8500; + } else if (strcmp(id->id, "KIOX010A") == 0) { + *acpi_type = ACPI_KIOX010A; + *label = "accel-display"; + } else if (strcmp(id->id, "KIOX020A") == 0) { + *label = "accel-base"; + } *chipset = (enum kx_chipset)id->driver_data; return dev_name(dev); } +static void kxcjk1013_disable_regulators(void *d) +{ + struct kxcjk1013_data *data = d; + + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); +} + static int kxcjk1013_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1289,29 +1456,67 @@ static int kxcjk1013_probe(struct i2c_client *client, } else { data->active_high_intr = true; /* default polarity */ - ret = iio_read_mount_matrix(&client->dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(&client->dev, &data->orientation); if (ret) return ret; } + data->regulators[0].supply = "vdd"; + data->regulators[1].supply = "vddio"; + ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return dev_err_probe(&client->dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, kxcjk1013_disable_regulators, data); + if (ret) + return ret; + + /* + * A typical delay of 10ms is required for powering up + * according to the data sheets of supported chips. + * Hence double that to play safe. + */ + msleep(20); + if (id) { data->chipset = (enum kx_chipset)(id->driver_data); name = id->name; } else if (ACPI_HANDLE(&client->dev)) { name = kxcjk1013_match_acpi_device(&client->dev, &data->chipset, - &data->is_smo8500_device); + &data->acpi_type, + &indio_dev->label); } else return -ENODEV; + switch (data->chipset) { + case KXCJK1013: + case KXCJ91008: + case KXTJ21009: + data->regs = &kxcjk1013_regs; + break; + case KXTF9: + data->regs = &kxtf9_regs; + break; + case KX0231025: + data->regs = &kx0231025_regs; + break; + default: + return -EINVAL; + } + ret = kxcjk1013_chip_init(data); if (ret < 0) return ret; mutex_init(&data->mutex); - indio_dev->dev.parent = &client->dev; indio_dev->channels = kxcjk1013_channels; indio_dev->num_channels = ARRAY_SIZE(kxcjk1013_channels); indio_dev->available_scan_masks = kxcjk1013_scan_masks; @@ -1319,7 +1524,7 @@ static int kxcjk1013_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &kxcjk1013_info; - if (client->irq > 0 && !data->is_smo8500_device) { + if (client->irq > 0 && data->acpi_type != ACPI_SMO8500) { ret = devm_request_threaded_irq(&client->dev, client->irq, kxcjk1013_data_rdy_trig_poll, kxcjk1013_event_handler, @@ -1332,7 +1537,7 @@ static int kxcjk1013_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; goto err_poweroff; @@ -1341,22 +1546,20 @@ static int kxcjk1013_probe(struct i2c_client *client, data->motion_trig = devm_iio_trigger_alloc(&client->dev, "%s-any-motion-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->motion_trig) { ret = -ENOMEM; goto err_poweroff; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &kxcjk1013_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); - indio_dev->trig = data->dready_trig; - iio_trigger_get(indio_dev->trig); ret = iio_trigger_register(data->dready_trig); if (ret) goto err_poweroff; - data->motion_trig->dev.parent = &client->dev; + indio_dev->trig = iio_trigger_get(data->dready_trig); + data->motion_trig->ops = &kxcjk1013_trigger_ops; iio_trigger_set_drvdata(data->motion_trig, indio_dev); ret = iio_trigger_register(data->motion_trig); @@ -1387,14 +1590,16 @@ static int kxcjk1013_probe(struct i2c_client *client, ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, "unable to register iio device\n"); - goto err_buffer_cleanup; + goto err_pm_cleanup; } return 0; +err_pm_cleanup: + pm_runtime_dont_use_autosuspend(&client->dev); + pm_runtime_disable(&client->dev); err_buffer_cleanup: - if (data->dready_trig) - iio_triggered_buffer_cleanup(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); err_trigger_unregister: if (data->dready_trig) iio_trigger_unregister(data->dready_trig); @@ -1406,7 +1611,7 @@ err_poweroff: return ret; } -static int kxcjk1013_remove(struct i2c_client *client) +static void kxcjk1013_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct kxcjk1013_data *data = iio_priv(indio_dev); @@ -1415,10 +1620,9 @@ static int kxcjk1013_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); + iio_triggered_buffer_cleanup(indio_dev); if (data->dready_trig) { - iio_triggered_buffer_cleanup(indio_dev); iio_trigger_unregister(data->dready_trig); iio_trigger_unregister(data->motion_trig); } @@ -1426,8 +1630,6 @@ static int kxcjk1013_remove(struct i2c_client *client) mutex_lock(&data->mutex); kxcjk1013_set_mode(data, STANDBY); mutex_unlock(&data->mutex); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1523,6 +1725,7 @@ static const struct i2c_device_id kxcjk1013_id[] = { {"kxcj91008", KXCJ91008}, {"kxtj21009", KXTJ21009}, {"kxtf9", KXTF9}, + {"kx023-1025", KX0231025}, {"SMO8500", KXCJ91008}, {} }; @@ -1534,6 +1737,7 @@ static const struct of_device_id kxcjk1013_of_match[] = { { .compatible = "kionix,kxcj91008", }, { .compatible = "kionix,kxtj21009", }, { .compatible = "kionix,kxtf9", }, + { .compatible = "kionix,kx023-1025", }, { } }; MODULE_DEVICE_TABLE(of, kxcjk1013_of_match); diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index 38411e1c155b..61346ea8ef19 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -2,6 +2,7 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/delay.h> @@ -21,8 +22,8 @@ static int kxsd9_i2c_probe(struct i2c_client *i2c, regmap = devm_regmap_init_i2c(i2c, &config); if (IS_ERR(regmap)) { - dev_err(&i2c->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&i2c->dev, "Failed to register i2c regmap: %pe\n", + regmap); return PTR_ERR(regmap); } @@ -31,20 +32,16 @@ static int kxsd9_i2c_probe(struct i2c_client *i2c, i2c->name); } -static int kxsd9_i2c_remove(struct i2c_client *client) +static void kxsd9_i2c_remove(struct i2c_client *client) { - return kxsd9_common_remove(&client->dev); + kxsd9_common_remove(&client->dev); } -#ifdef CONFIG_OF static const struct of_device_id kxsd9_of_match[] = { { .compatible = "kionix,kxsd9", }, { }, }; MODULE_DEVICE_TABLE(of, kxsd9_of_match); -#else -#define kxsd9_of_match NULL -#endif static const struct i2c_device_id kxsd9_i2c_id[] = { {"kxsd9", 0}, @@ -55,8 +52,8 @@ MODULE_DEVICE_TABLE(i2c, kxsd9_i2c_id); static struct i2c_driver kxsd9_i2c_driver = { .driver = { .name = "kxsd9", - .of_match_table = of_match_ptr(kxsd9_of_match), - .pm = &kxsd9_dev_pm_ops, + .of_match_table = kxsd9_of_match, + .pm = pm_ptr(&kxsd9_dev_pm_ops), }, .probe = kxsd9_i2c_probe, .remove = kxsd9_i2c_remove, @@ -66,3 +63,4 @@ module_i2c_driver(kxsd9_i2c_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("KXSD9 accelerometer I2C interface"); +MODULE_IMPORT_NS(IIO_KXSD9); diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 7971ec1eeb7e..07f14a9f22c7 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -32,9 +32,9 @@ static int kxsd9_spi_probe(struct spi_device *spi) spi_get_device_id(spi)->name); } -static int kxsd9_spi_remove(struct spi_device *spi) +static void kxsd9_spi_remove(struct spi_device *spi) { - return kxsd9_common_remove(&spi->dev); + kxsd9_common_remove(&spi->dev); } static const struct spi_device_id kxsd9_spi_id[] = { @@ -44,15 +44,15 @@ static const struct spi_device_id kxsd9_spi_id[] = { MODULE_DEVICE_TABLE(spi, kxsd9_spi_id); static const struct of_device_id kxsd9_of_match[] = { - { .compatible = "kionix,kxsd9" }, - { }, + { .compatible = "kionix,kxsd9" }, + { } }; MODULE_DEVICE_TABLE(of, kxsd9_of_match); static struct spi_driver kxsd9_spi_driver = { .driver = { .name = "kxsd9", - .pm = &kxsd9_dev_pm_ops, + .pm = pm_ptr(&kxsd9_dev_pm_ops), .of_match_table = kxsd9_of_match, }, .probe = kxsd9_spi_probe, @@ -64,3 +64,4 @@ module_spi_driver(kxsd9_spi_driver); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("Kionix KXSD9 SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_KXSD9); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 0b876b2dc5bd..ba99649fe195 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -209,23 +209,29 @@ static irqreturn_t kxsd9_trigger_handler(int irq, void *p) const struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct kxsd9_state *st = iio_priv(indio_dev); + /* + * Ensure correct positioning and alignment of timestamp. + * No need to zero initialize as all elements written. + */ + struct { + __be16 chan[4]; + s64 ts __aligned(8); + } hw_values; int ret; - /* 4 * 16bit values AND timestamp */ - __be16 hw_values[8]; ret = regmap_bulk_read(st->map, KXSD9_REG_X, - &hw_values, - 8); + hw_values.chan, + sizeof(hw_values.chan)); if (ret) { - dev_err(st->dev, - "error reading data\n"); - return ret; + dev_err(st->dev, "error reading data: %d\n", ret); + goto out; } iio_push_to_buffers_with_timestamp(indio_dev, - hw_values, + &hw_values, iio_get_time_ns(indio_dev)); +out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -252,8 +258,6 @@ static int kxsd9_buffer_postdisable(struct iio_dev *indio_dev) static const struct iio_buffer_setup_ops kxsd9_buffer_setup_ops = { .preenable = kxsd9_buffer_preenable, - .postenable = iio_triggered_buffer_postenable, - .predisable = iio_triggered_buffer_predisable, .postdisable = kxsd9_buffer_postdisable, }; @@ -411,13 +415,12 @@ int kxsd9_common_probe(struct device *dev, indio_dev->channels = kxsd9_channels; indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels); indio_dev->name = name; - indio_dev->dev.parent = dev; indio_dev->info = &kxsd9_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->available_scan_masks = kxsd9_scan_masks; /* Read the mounting matrix, if present */ - ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation); + ret = iio_read_mount_matrix(dev, &st->orientation); if (ret) return ret; @@ -473,9 +476,9 @@ err_power_down: return ret; } -EXPORT_SYMBOL(kxsd9_common_probe); +EXPORT_SYMBOL_NS(kxsd9_common_probe, IIO_KXSD9); -int kxsd9_common_remove(struct device *dev) +void kxsd9_common_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct kxsd9_state *st = iio_priv(indio_dev); @@ -486,12 +489,9 @@ int kxsd9_common_remove(struct device *dev) pm_runtime_put_noidle(dev); pm_runtime_disable(dev); kxsd9_power_down(st); - - return 0; } -EXPORT_SYMBOL(kxsd9_common_remove); +EXPORT_SYMBOL_NS(kxsd9_common_remove, IIO_KXSD9); -#ifdef CONFIG_PM static int kxsd9_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -507,15 +507,9 @@ static int kxsd9_runtime_resume(struct device *dev) return kxsd9_power_up(st); } -#endif /* CONFIG_PM */ -const struct dev_pm_ops kxsd9_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend, - kxsd9_runtime_resume, NULL) -}; -EXPORT_SYMBOL(kxsd9_dev_pm_ops); +EXPORT_NS_RUNTIME_DEV_PM_OPS(kxsd9_dev_pm_ops, kxsd9_runtime_suspend, + kxsd9_runtime_resume, NULL, IIO_KXSD9); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("Kionix KXSD9 driver"); diff --git a/drivers/iio/accel/kxsd9.h b/drivers/iio/accel/kxsd9.h index 5e3ca212f5be..c04dbfa4e0d0 100644 --- a/drivers/iio/accel/kxsd9.h +++ b/drivers/iio/accel/kxsd9.h @@ -8,6 +8,6 @@ int kxsd9_common_probe(struct device *dev, struct regmap *map, const char *name); -int kxsd9_common_remove(struct device *dev); +void kxsd9_common_remove(struct device *dev); extern const struct dev_pm_ops kxsd9_dev_pm_ops; diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c index 02b3d25b3d96..2462000e0519 100644 --- a/drivers/iio/accel/mc3230.c +++ b/drivers/iio/accel/mc3230.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/** +/* * mCube MC3230 3-Axis Accelerometer * * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> @@ -132,7 +132,6 @@ static int mc3230_probe(struct i2c_client *client, data->client = client; i2c_set_clientdata(client, indio_dev); - indio_dev->dev.parent = &client->dev; indio_dev->info = &mc3230_info; indio_dev->name = "mc3230"; indio_dev->modes = INDIO_DIRECT_MODE; @@ -152,16 +151,15 @@ static int mc3230_probe(struct i2c_client *client, return ret; } -static int mc3230_remove(struct i2c_client *client) +static void mc3230_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); iio_device_unregister(indio_dev); - return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY); + mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY); } -#ifdef CONFIG_PM_SLEEP static int mc3230_suspend(struct device *dev) { struct mc3230_data *data; @@ -179,9 +177,8 @@ static int mc3230_resume(struct device *dev) return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE); } -#endif -static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); static const struct i2c_device_id mc3230_i2c_id[] = { {"mc3230", 0}, @@ -192,7 +189,7 @@ MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id); static struct i2c_driver mc3230_driver = { .driver = { .name = "mc3230", - .pm = &mc3230_pm_ops, + .pm = pm_sleep_ptr(&mc3230_pm_ops), }, .probe = mc3230_probe, .remove = mc3230_remove, diff --git a/drivers/iio/accel/mma7455.h b/drivers/iio/accel/mma7455.h index 4e3fa988f690..1fcc4b64b3af 100644 --- a/drivers/iio/accel/mma7455.h +++ b/drivers/iio/accel/mma7455.h @@ -11,6 +11,6 @@ extern const struct regmap_config mma7455_core_regmap; int mma7455_core_probe(struct device *dev, struct regmap *regmap, const char *name); -int mma7455_core_remove(struct device *dev); +void mma7455_core_remove(struct device *dev); #endif diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index 8b5a6aff9bf4..a34195b3215d 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -52,6 +52,14 @@ struct mma7455_data { struct regmap *regmap; + /* + * Used to reorganize data. Will ensure correct alignment of + * the timestamp if present + */ + struct { + __le16 channels[3]; + s64 ts __aligned(8); + } scan; }; static int mma7455_drdy(struct mma7455_data *mma7455) @@ -82,19 +90,19 @@ static irqreturn_t mma7455_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct mma7455_data *mma7455 = iio_priv(indio_dev); - u8 buf[16]; /* 3 x 16-bit channels + padding + ts */ int ret; ret = mma7455_drdy(mma7455); if (ret) goto done; - ret = regmap_bulk_read(mma7455->regmap, MMA7455_REG_XOUTL, buf, - sizeof(__le16) * 3); + ret = regmap_bulk_read(mma7455->regmap, MMA7455_REG_XOUTL, + mma7455->scan.channels, + sizeof(mma7455->scan.channels)); if (ret) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_push_to_buffers_with_timestamp(indio_dev, &mma7455->scan, iio_get_time_ns(indio_dev)); done: @@ -126,7 +134,8 @@ static int mma7455_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - *val = sign_extend32(le16_to_cpu(data), 9); + *val = sign_extend32(le16_to_cpu(data), + chan->scan_type.realbits - 1); return IIO_VAL_INT; @@ -229,7 +238,7 @@ const struct regmap_config mma7455_core_regmap = { .val_bits = 8, .max_register = MMA7455_REG_TW, }; -EXPORT_SYMBOL_GPL(mma7455_core_regmap); +EXPORT_SYMBOL_NS_GPL(mma7455_core_regmap, IIO_MMA7455); int mma7455_core_probe(struct device *dev, struct regmap *regmap, const char *name) @@ -260,7 +269,6 @@ int mma7455_core_probe(struct device *dev, struct regmap *regmap, indio_dev->info = &mma7455_info; indio_dev->name = name; - indio_dev->dev.parent = dev; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = mma7455_channels; indio_dev->num_channels = ARRAY_SIZE(mma7455_channels); @@ -285,9 +293,9 @@ int mma7455_core_probe(struct device *dev, struct regmap *regmap, return 0; } -EXPORT_SYMBOL_GPL(mma7455_core_probe); +EXPORT_SYMBOL_NS_GPL(mma7455_core_probe, IIO_MMA7455); -int mma7455_core_remove(struct device *dev) +void mma7455_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct mma7455_data *mma7455 = iio_priv(indio_dev); @@ -297,10 +305,8 @@ int mma7455_core_remove(struct device *dev) regmap_write(mma7455->regmap, MMA7455_REG_MCTL, MMA7455_MCTL_MODE_STANDBY); - - return 0; } -EXPORT_SYMBOL_GPL(mma7455_core_remove); +EXPORT_SYMBOL_NS_GPL(mma7455_core_remove, IIO_MMA7455); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver"); diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c index cddeaa9e230a..c63b321b01cd 100644 --- a/drivers/iio/accel/mma7455_i2c.c +++ b/drivers/iio/accel/mma7455_i2c.c @@ -26,9 +26,9 @@ static int mma7455_i2c_probe(struct i2c_client *i2c, return mma7455_core_probe(&i2c->dev, regmap, name); } -static int mma7455_i2c_remove(struct i2c_client *i2c) +static void mma7455_i2c_remove(struct i2c_client *i2c) { - return mma7455_core_remove(&i2c->dev); + mma7455_core_remove(&i2c->dev); } static const struct i2c_device_id mma7455_i2c_ids[] = { @@ -59,3 +59,4 @@ module_i2c_driver(mma7455_i2c_driver); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); MODULE_DESCRIPTION("Freescale MMA7455L I2C accelerometer driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MMA7455); diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c index eb82cdfa8abc..fcdde2e8a84b 100644 --- a/drivers/iio/accel/mma7455_spi.c +++ b/drivers/iio/accel/mma7455_spi.c @@ -22,9 +22,9 @@ static int mma7455_spi_probe(struct spi_device *spi) return mma7455_core_probe(&spi->dev, regmap, id->name); } -static int mma7455_spi_remove(struct spi_device *spi) +static void mma7455_spi_remove(struct spi_device *spi) { - return mma7455_core_remove(&spi->dev); + mma7455_core_remove(&spi->dev); } static const struct spi_device_id mma7455_spi_ids[] = { @@ -47,3 +47,4 @@ module_spi_driver(mma7455_spi_driver); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); MODULE_DESCRIPTION("Freescale MMA7455L SPI accelerometer driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_MMA7455); diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index 7faf6d8657ae..85829990bbad 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Freescale MMA7660FC 3-Axis Accelerometer * * Copyright (c) 2016, Intel Corporation. @@ -7,8 +7,8 @@ * IIO driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c. */ -#include <linux/acpi.h> #include <linux/i2c.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -188,7 +188,6 @@ static int mma7660_probe(struct i2c_client *client, mutex_init(&data->lock); data->mode = MMA7660_MODE_STANDBY; - indio_dev->dev.parent = &client->dev; indio_dev->info = &mma7660_info; indio_dev->name = MMA7660_DRIVER_NAME; indio_dev->modes = INDIO_DIRECT_MODE; @@ -208,16 +207,19 @@ static int mma7660_probe(struct i2c_client *client, return ret; } -static int mma7660_remove(struct i2c_client *client) +static void mma7660_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + int ret; iio_device_unregister(indio_dev); - return mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); + ret = mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); + if (ret) + dev_warn(&client->dev, "Failed to put device in stand-by mode (%pe), ignoring\n", + ERR_PTR(ret)); } -#ifdef CONFIG_PM_SLEEP static int mma7660_suspend(struct device *dev) { struct mma7660_data *data; @@ -236,12 +238,8 @@ static int mma7660_resume(struct device *dev) return mma7660_set_mode(data, MMA7660_MODE_ACTIVE); } -static SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume); - -#define MMA7660_PM_OPS (&mma7660_pm_ops) -#else -#define MMA7660_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, + mma7660_resume); static const struct i2c_device_id mma7660_i2c_id[] = { {"mma7660", 0}, @@ -265,9 +263,9 @@ MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id); static struct i2c_driver mma7660_driver = { .driver = { .name = "mma7660", - .pm = MMA7660_PM_OPS, + .pm = pm_sleep_ptr(&mma7660_pm_ops), .of_match_table = mma7660_of_match, - .acpi_match_table = ACPI_PTR(mma7660_acpi_id), + .acpi_match_table = mma7660_acpi_id, }, .probe = mma7660_probe, .remove = mma7660_remove, diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 00e100fc845a..3ba28c2ff68a 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -58,7 +58,7 @@ #define MMA8452_FF_MT_THS 0x17 #define MMA8452_FF_MT_THS_MASK 0x7f #define MMA8452_FF_MT_COUNT 0x18 -#define MMA8452_FF_MT_CHAN_SHIFT 3 +#define MMA8452_FF_MT_CHAN_SHIFT 3 #define MMA8452_TRANSIENT_CFG 0x1d #define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) #define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0) @@ -70,7 +70,7 @@ #define MMA8452_TRANSIENT_THS 0x1f #define MMA8452_TRANSIENT_THS_MASK GENMASK(6, 0) #define MMA8452_TRANSIENT_COUNT 0x20 -#define MMA8452_TRANSIENT_CHAN_SHIFT 1 +#define MMA8452_TRANSIENT_CHAN_SHIFT 1 #define MMA8452_CTRL_REG1 0x2a #define MMA8452_CTRL_ACTIVE BIT(0) #define MMA8452_CTRL_DR_MASK GENMASK(5, 3) @@ -104,12 +104,19 @@ struct mma8452_data { struct i2c_client *client; struct mutex lock; + struct iio_mount_matrix orientation; u8 ctrl_reg1; u8 data_cfg; const struct mma_chip_info *chip_info; int sleep_val; struct regulator *vdd_reg; struct regulator *vddio_reg; + + /* Ensure correct alignment of time stamp when present */ + struct { + __be16 channels[3]; + s64 ts __aligned(8); + } buffer; }; /** @@ -128,37 +135,38 @@ struct mma8452_data { * used for different chips and the relevant registers are included here. */ struct mma8452_event_regs { - u8 ev_cfg; - u8 ev_cfg_ele; - u8 ev_cfg_chan_shift; - u8 ev_src; - u8 ev_ths; - u8 ev_ths_mask; - u8 ev_count; + u8 ev_cfg; + u8 ev_cfg_ele; + u8 ev_cfg_chan_shift; + u8 ev_src; + u8 ev_ths; + u8 ev_ths_mask; + u8 ev_count; }; static const struct mma8452_event_regs ff_mt_ev_regs = { - .ev_cfg = MMA8452_FF_MT_CFG, - .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, - .ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT, - .ev_src = MMA8452_FF_MT_SRC, - .ev_ths = MMA8452_FF_MT_THS, - .ev_ths_mask = MMA8452_FF_MT_THS_MASK, - .ev_count = MMA8452_FF_MT_COUNT + .ev_cfg = MMA8452_FF_MT_CFG, + .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, + .ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT, + .ev_src = MMA8452_FF_MT_SRC, + .ev_ths = MMA8452_FF_MT_THS, + .ev_ths_mask = MMA8452_FF_MT_THS_MASK, + .ev_count = MMA8452_FF_MT_COUNT }; static const struct mma8452_event_regs trans_ev_regs = { - .ev_cfg = MMA8452_TRANSIENT_CFG, - .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, - .ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT, - .ev_src = MMA8452_TRANSIENT_SRC, - .ev_ths = MMA8452_TRANSIENT_THS, - .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, - .ev_count = MMA8452_TRANSIENT_COUNT, + .ev_cfg = MMA8452_TRANSIENT_CFG, + .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, + .ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT, + .ev_src = MMA8452_TRANSIENT_SRC, + .ev_ths = MMA8452_TRANSIENT_THS, + .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, + .ev_count = MMA8452_TRANSIENT_COUNT, }; /** * struct mma_chip_info - chip specific data + * @name: part number of device reported via 'name' attr * @chip_id: WHO_AM_I register's value * @channels: struct iio_chan_spec matching the device's * capabilities @@ -170,6 +178,7 @@ static const struct mma8452_event_regs trans_ev_regs = { * @enabled_events: event flags enabled and handled by this driver */ struct mma_chip_info { + const char *name; u8 chip_id; const struct iio_chan_spec *channels; int num_channels; @@ -215,7 +224,7 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(&client->dev); + ret = pm_runtime_resume_and_get(&client->dev); } else { pm_runtime_mark_last_busy(&client->dev); ret = pm_runtime_put_autosuspend(&client->dev); @@ -224,8 +233,6 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on) if (ret < 0) { dev_err(&client->dev, "failed to change power state to %d\n", on); - if (on) - pm_runtime_put_noidle(&client->dev); return ret; } @@ -375,8 +382,8 @@ static ssize_t mma8452_show_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) { - struct mma8452_data *data = iio_priv(i2c_get_clientdata( - to_i2c_client(dev))); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mma8452_data *data = iio_priv(indio_dev); return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales, ARRAY_SIZE(data->chip_info->mma_scales)); @@ -1049,7 +1056,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) { struct iio_dev *indio_dev = p; struct mma8452_data *data = iio_priv(indio_dev); - int ret = IRQ_NONE; + irqreturn_t ret = IRQ_NONE; int src; src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC); @@ -1091,14 +1098,13 @@ static irqreturn_t mma8452_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct mma8452_data *data = iio_priv(indio_dev); - u8 buffer[16]; /* 3 16-bit channels + padding + ts */ int ret; - ret = mma8452_read(data, (__be16 *)buffer); + ret = mma8452_read(data, data->buffer.channels); if (ret < 0) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, iio_get_time_ns(indio_dev)); done: @@ -1182,10 +1188,24 @@ static struct attribute *mma8452_event_attributes[] = { NULL, }; -static struct attribute_group mma8452_event_attribute_group = { +static const struct attribute_group mma8452_event_attribute_group = { .attrs = mma8452_event_attributes, }; +static const struct iio_mount_matrix * +mma8452_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mma8452_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info mma8452_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mma8452_get_mount_matrix), + { } +}; + #define MMA8452_FREEFALL_CHANNEL(modifier) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -1224,6 +1244,7 @@ static struct attribute_group mma8452_event_attribute_group = { }, \ .event_spec = mma8452_transient_event, \ .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \ + .ext_info = mma8452_ext_info, \ } #define MMA8652_CHANNEL(axis, idx, bits) { \ @@ -1245,6 +1266,7 @@ static struct attribute_group mma8452_event_attribute_group = { }, \ .event_spec = mma8452_motion_event, \ .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \ + .ext_info = mma8452_ext_info, \ } static const struct iio_chan_spec mma8451_channels[] = { @@ -1298,6 +1320,7 @@ enum { static const struct mma_chip_info mma_chip_info_table[] = { [mma8451] = { + .name = "mma8451", .chip_id = MMA8451_DEVICE_ID, .channels = mma8451_channels, .num_channels = ARRAY_SIZE(mma8451_channels), @@ -1322,6 +1345,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { MMA8452_INT_FF_MT, }, [mma8452] = { + .name = "mma8452", .chip_id = MMA8452_DEVICE_ID, .channels = mma8452_channels, .num_channels = ARRAY_SIZE(mma8452_channels), @@ -1338,6 +1362,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { MMA8452_INT_FF_MT, }, [mma8453] = { + .name = "mma8453", .chip_id = MMA8453_DEVICE_ID, .channels = mma8453_channels, .num_channels = ARRAY_SIZE(mma8453_channels), @@ -1354,6 +1379,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { MMA8452_INT_FF_MT, }, [mma8652] = { + .name = "mma8652", .chip_id = MMA8652_DEVICE_ID, .channels = mma8652_channels, .num_channels = ARRAY_SIZE(mma8652_channels), @@ -1363,6 +1389,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { .enabled_events = MMA8452_INT_FF_MT, }, [mma8653] = { + .name = "mma8653", .chip_id = MMA8653_DEVICE_ID, .channels = mma8653_channels, .num_channels = ARRAY_SIZE(mma8653_channels), @@ -1377,6 +1404,7 @@ static const struct mma_chip_info mma_chip_info_table[] = { .enabled_events = MMA8452_INT_FF_MT, }, [fxls8471] = { + .name = "fxls8471", .chip_id = FXLS8471_DEVICE_ID, .channels = mma8451_channels, .num_channels = ARRAY_SIZE(mma8451_channels), @@ -1456,11 +1484,10 @@ static int mma8452_trigger_setup(struct iio_dev *indio_dev) trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!trig) return -ENOMEM; - trig->dev.parent = &data->client->dev; trig->ops = &mma8452_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); @@ -1468,7 +1495,7 @@ static int mma8452_trigger_setup(struct iio_dev *indio_dev) if (ret) return ret; - indio_dev->trig = trig; + indio_dev->trig = iio_trigger_get(trig); return 0; } @@ -1484,10 +1511,14 @@ static int mma8452_reset(struct i2c_client *client) int i; int ret; - ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2, + /* + * Find on fxls8471, after config reset bit, it reset immediately, + * and will not give ACK, so here do not check the return value. + * The following code will read the reset register, and check whether + * this reset works. + */ + i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2, MMA8452_CTRL_REG2_RST); - if (ret < 0) - return ret; for (i = 0; i < 10; i++) { usleep_range(100, 200); @@ -1520,13 +1551,6 @@ static int mma8452_probe(struct i2c_client *client, struct mma8452_data *data; struct iio_dev *indio_dev; int ret; - const struct of_device_id *match; - - match = of_match_device(mma8452_dt_ids, &client->dev); - if (!match) { - dev_err(&client->dev, "unknown device model\n"); - return -ENODEV; - } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -1535,25 +1559,30 @@ static int mma8452_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; mutex_init(&data->lock); - data->chip_info = match->data; - data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(data->vdd_reg)) { - if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - dev_err(&client->dev, "failed to get VDD regulator!\n"); - return PTR_ERR(data->vdd_reg); + data->chip_info = device_get_match_data(&client->dev); + if (!data->chip_info) { + if (id) { + data->chip_info = &mma_chip_info_table[id->driver_data]; + } else { + dev_err(&client->dev, "unknown device model\n"); + return -ENODEV; + } } - data->vddio_reg = devm_regulator_get(&client->dev, "vddio"); - if (IS_ERR(data->vddio_reg)) { - if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER) - return -EPROBE_DEFER; + ret = iio_read_mount_matrix(&client->dev, &data->orientation); + if (ret) + return ret; - dev_err(&client->dev, "failed to get VDDIO regulator!\n"); - return PTR_ERR(data->vddio_reg); - } + data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_reg)) + return dev_err_probe(&client->dev, PTR_ERR(data->vdd_reg), + "failed to get VDD regulator!\n"); + + data->vddio_reg = devm_regulator_get(&client->dev, "vddio"); + if (IS_ERR(data->vddio_reg)) + return dev_err_probe(&client->dev, PTR_ERR(data->vddio_reg), + "failed to get VDDIO regulator!\n"); ret = regulator_enable(data->vdd_reg); if (ret) { @@ -1580,19 +1609,18 @@ static int mma8452_probe(struct i2c_client *client, case FXLS8471_DEVICE_ID: if (ret == data->chip_info->chip_id) break; - /* fall through */ + fallthrough; default: ret = -ENODEV; goto disable_regulators; } dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n", - match->compatible, data->chip_info->chip_id); + data->chip_info->name, data->chip_info->chip_id); i2c_set_clientdata(client, indio_dev); indio_dev->info = &mma8452_info; - indio_dev->name = id->name; - indio_dev->dev.parent = &client->dev; + indio_dev->name = data->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = data->chip_info->channels; indio_dev->num_channels = data->chip_info->num_channels; @@ -1685,10 +1713,13 @@ static int mma8452_probe(struct i2c_client *client, ret = mma8452_set_freefall_mode(data, false); if (ret < 0) - goto buffer_cleanup; + goto unregister_device; return 0; +unregister_device: + iio_device_unregister(indio_dev); + buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); @@ -1704,7 +1735,7 @@ disable_regulator_vdd: return ret; } -static int mma8452_remove(struct i2c_client *client) +static void mma8452_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct mma8452_data *data = iio_priv(indio_dev); @@ -1713,7 +1744,6 @@ static int mma8452_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); iio_triggered_buffer_cleanup(indio_dev); mma8452_trigger_cleanup(indio_dev); @@ -1721,8 +1751,6 @@ static int mma8452_remove(struct i2c_client *client) regulator_disable(data->vddio_reg); regulator_disable(data->vdd_reg); - - return 0; } #ifdef CONFIG_PM @@ -1815,7 +1843,7 @@ MODULE_DEVICE_TABLE(i2c, mma8452_id); static struct i2c_driver mma8452_driver = { .driver = { .name = "mma8452", - .of_match_table = of_match_ptr(mma8452_dt_ids), + .of_match_table = mma8452_dt_ids, .pm = &mma8452_pm_ops, }, .probe = mma8452_probe, diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 99e4a21ca942..f7a793f4a8e3 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -473,7 +473,6 @@ static int mma9551_probe(struct i2c_client *client, mutex_init(&data->mutex); - indio_dev->dev.parent = &client->dev; indio_dev->channels = mma9551_channels; indio_dev->num_channels = ARRAY_SIZE(mma9551_channels); indio_dev->name = name; @@ -496,18 +495,21 @@ static int mma9551_probe(struct i2c_client *client, ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, "unable to register iio device\n"); - goto out_poweroff; + goto err_pm_cleanup; } return 0; +err_pm_cleanup: + pm_runtime_dont_use_autosuspend(&client->dev); + pm_runtime_disable(&client->dev); out_poweroff: mma9551_set_device_state(client, false); return ret; } -static int mma9551_remove(struct i2c_client *client) +static void mma9551_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct mma9551_data *data = iio_priv(indio_dev); @@ -516,16 +518,12 @@ static int mma9551_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); mutex_lock(&data->mutex); mma9551_set_device_state(data->client, false); mutex_unlock(&data->mutex); - - return 0; } -#ifdef CONFIG_PM static int mma9551_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -557,9 +555,7 @@ static int mma9551_runtime_resume(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int mma9551_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -585,12 +581,10 @@ static int mma9551_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops mma9551_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) - SET_RUNTIME_PM_OPS(mma9551_runtime_suspend, - mma9551_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) + RUNTIME_PM_OPS(mma9551_runtime_suspend, mma9551_runtime_resume, NULL) }; static const struct acpi_device_id mma9551_acpi_match[] = { @@ -611,7 +605,7 @@ static struct i2c_driver mma9551_driver = { .driver = { .name = MMA9551_DRV_NAME, .acpi_match_table = ACPI_PTR(mma9551_acpi_match), - .pm = &mma9551_pm_ops, + .pm = pm_ptr(&mma9551_pm_ops), }, .probe = mma9551_probe, .remove = mma9551_remove, @@ -624,3 +618,4 @@ MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MMA9551L motion-sensing platform driver"); +MODULE_IMPORT_NS(IIO_MMA9551); diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c index 666e7a04a7d7..64ca7d7a9673 100644 --- a/drivers/iio/accel/mma9551_core.c +++ b/drivers/iio/accel/mma9551_core.c @@ -219,7 +219,7 @@ int mma9551_read_config_byte(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG, reg, NULL, 0, val, 1); } -EXPORT_SYMBOL(mma9551_read_config_byte); +EXPORT_SYMBOL_NS(mma9551_read_config_byte, IIO_MMA9551); /** * mma9551_write_config_byte() - write 1 configuration byte @@ -244,7 +244,7 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg, &val, 1, NULL, 0); } -EXPORT_SYMBOL(mma9551_write_config_byte); +EXPORT_SYMBOL_NS(mma9551_write_config_byte, IIO_MMA9551); /** * mma9551_read_status_byte() - read 1 status byte @@ -269,7 +269,7 @@ int mma9551_read_status_byte(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS, reg, NULL, 0, val, 1); } -EXPORT_SYMBOL(mma9551_read_status_byte); +EXPORT_SYMBOL_NS(mma9551_read_status_byte, IIO_MMA9551); /** * mma9551_read_config_word() - read 1 config word @@ -300,7 +300,7 @@ int mma9551_read_config_word(struct i2c_client *client, u8 app_id, return ret; } -EXPORT_SYMBOL(mma9551_read_config_word); +EXPORT_SYMBOL_NS(mma9551_read_config_word, IIO_MMA9551); /** * mma9551_write_config_word() - write 1 config word @@ -327,7 +327,7 @@ int mma9551_write_config_word(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg, (u8 *)&v, 2, NULL, 0); } -EXPORT_SYMBOL(mma9551_write_config_word); +EXPORT_SYMBOL_NS(mma9551_write_config_word, IIO_MMA9551); /** * mma9551_read_status_word() - read 1 status word @@ -358,7 +358,7 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id, return ret; } -EXPORT_SYMBOL(mma9551_read_status_word); +EXPORT_SYMBOL_NS(mma9551_read_status_word, IIO_MMA9551); /** * mma9551_read_config_words() - read multiple config words @@ -397,7 +397,7 @@ int mma9551_read_config_words(struct i2c_client *client, u8 app_id, return 0; } -EXPORT_SYMBOL(mma9551_read_config_words); +EXPORT_SYMBOL_NS(mma9551_read_config_words, IIO_MMA9551); /** * mma9551_read_status_words() - read multiple status words @@ -436,7 +436,7 @@ int mma9551_read_status_words(struct i2c_client *client, u8 app_id, return 0; } -EXPORT_SYMBOL(mma9551_read_status_words); +EXPORT_SYMBOL_NS(mma9551_read_status_words, IIO_MMA9551); /** * mma9551_write_config_words() - write multiple config words @@ -471,7 +471,7 @@ int mma9551_write_config_words(struct i2c_client *client, u8 app_id, return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg, (u8 *)be_buf, len * sizeof(u16), NULL, 0); } -EXPORT_SYMBOL(mma9551_write_config_words); +EXPORT_SYMBOL_NS(mma9551_write_config_words, IIO_MMA9551); /** * mma9551_update_config_bits() - update bits in register @@ -507,7 +507,7 @@ int mma9551_update_config_bits(struct i2c_client *client, u8 app_id, return mma9551_write_config_byte(client, app_id, reg, tmp); } -EXPORT_SYMBOL(mma9551_update_config_bits); +EXPORT_SYMBOL_NS(mma9551_update_config_bits, IIO_MMA9551); /** * mma9551_gpio_config() - configure gpio @@ -586,7 +586,7 @@ int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin, return ret; } -EXPORT_SYMBOL(mma9551_gpio_config); +EXPORT_SYMBOL_NS(mma9551_gpio_config, IIO_MMA9551); /** * mma9551_read_version() - read device version information @@ -616,7 +616,7 @@ int mma9551_read_version(struct i2c_client *client) return 0; } -EXPORT_SYMBOL(mma9551_read_version); +EXPORT_SYMBOL_NS(mma9551_read_version, IIO_MMA9551); /** * mma9551_set_device_state() - sets HW power mode @@ -646,7 +646,7 @@ int mma9551_set_device_state(struct i2c_client *client, bool enable) MMA9551_SLEEP_CFG_FLEEN : MMA9551_SLEEP_CFG_SNCEN); } -EXPORT_SYMBOL(mma9551_set_device_state); +EXPORT_SYMBOL_NS(mma9551_set_device_state, IIO_MMA9551); /** * mma9551_set_power_state() - sets runtime PM state @@ -664,7 +664,7 @@ int mma9551_set_power_state(struct i2c_client *client, bool on) int ret; if (on) - ret = pm_runtime_get_sync(&client->dev); + ret = pm_runtime_resume_and_get(&client->dev); else { pm_runtime_mark_last_busy(&client->dev); ret = pm_runtime_put_autosuspend(&client->dev); @@ -673,8 +673,6 @@ int mma9551_set_power_state(struct i2c_client *client, bool on) if (ret < 0) { dev_err(&client->dev, "failed to change power state to %d\n", on); - if (on) - pm_runtime_put_noidle(&client->dev); return ret; } @@ -682,7 +680,7 @@ int mma9551_set_power_state(struct i2c_client *client, bool on) return 0; } -EXPORT_SYMBOL(mma9551_set_power_state); +EXPORT_SYMBOL_NS(mma9551_set_power_state, IIO_MMA9551); /** * mma9551_sleep() - sleep @@ -701,7 +699,7 @@ void mma9551_sleep(int freq) else msleep_interruptible(sleep_val); } -EXPORT_SYMBOL(mma9551_sleep); +EXPORT_SYMBOL_NS(mma9551_sleep, IIO_MMA9551); /** * mma9551_read_accel_chan() - read accelerometer channel @@ -757,7 +755,7 @@ out_poweroff: mma9551_set_power_state(client, false); return ret; } -EXPORT_SYMBOL(mma9551_read_accel_chan); +EXPORT_SYMBOL_NS(mma9551_read_accel_chan, IIO_MMA9551); /** * mma9551_read_accel_scale() - read accelerometer scale @@ -775,7 +773,7 @@ int mma9551_read_accel_scale(int *val, int *val2) return IIO_VAL_INT_PLUS_MICRO; } -EXPORT_SYMBOL(mma9551_read_accel_scale); +EXPORT_SYMBOL_NS(mma9551_read_accel_scale, IIO_MMA9551); /** * mma9551_app_reset() - reset application @@ -794,7 +792,7 @@ int mma9551_app_reset(struct i2c_client *client, u32 app_mask) MMA9551_RSC_OFFSET(app_mask), MMA9551_RSC_VAL(app_mask)); } -EXPORT_SYMBOL(mma9551_app_reset); +EXPORT_SYMBOL_NS(mma9551_app_reset, IIO_MMA9551); MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>"); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index 312070dcf035..2da0e005b13e 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -917,7 +917,7 @@ static const struct iio_enum mma9553_calibgender_enum = { static const struct iio_chan_spec_ext_info mma9553_ext_info[] = { IIO_ENUM("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), - IIO_ENUM_AVAILABLE("calibgender", &mma9553_calibgender_enum), + IIO_ENUM_AVAILABLE("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), {}, }; @@ -1103,7 +1103,6 @@ static int mma9553_probe(struct i2c_client *client, if (ret < 0) return ret; - indio_dev->dev.parent = &client->dev; indio_dev->channels = mma9553_channels; indio_dev->num_channels = ARRAY_SIZE(mma9553_channels); indio_dev->name = name; @@ -1135,18 +1134,21 @@ static int mma9553_probe(struct i2c_client *client, ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, "unable to register iio device\n"); - goto out_poweroff; + goto err_pm_cleanup; } dev_dbg(&indio_dev->dev, "Registered device %s\n", name); return 0; +err_pm_cleanup: + pm_runtime_dont_use_autosuspend(&client->dev); + pm_runtime_disable(&client->dev); out_poweroff: mma9551_set_device_state(client, false); return ret; } -static int mma9553_remove(struct i2c_client *client) +static void mma9553_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct mma9553_data *data = iio_priv(indio_dev); @@ -1155,16 +1157,12 @@ static int mma9553_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); mutex_lock(&data->mutex); mma9551_set_device_state(data->client, false); mutex_unlock(&data->mutex); - - return 0; } -#ifdef CONFIG_PM static int mma9553_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1196,9 +1194,7 @@ static int mma9553_runtime_resume(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int mma9553_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1224,12 +1220,10 @@ static int mma9553_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops mma9553_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume) - SET_RUNTIME_PM_OPS(mma9553_runtime_suspend, - mma9553_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume) + RUNTIME_PM_OPS(mma9553_runtime_suspend, mma9553_runtime_resume, NULL) }; static const struct acpi_device_id mma9553_acpi_match[] = { @@ -1250,7 +1244,7 @@ static struct i2c_driver mma9553_driver = { .driver = { .name = MMA9553_DRV_NAME, .acpi_match_table = ACPI_PTR(mma9553_acpi_match), - .pm = &mma9553_pm_ops, + .pm = pm_ptr(&mma9553_pm_ops), }, .probe = mma9553_probe, .remove = mma9553_remove, @@ -1262,3 +1256,4 @@ module_i2c_driver(mma9553_driver); MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MMA9553L pedometer platform driver"); +MODULE_IMPORT_NS(IIO_MMA9551); diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c new file mode 100644 index 000000000000..2fded3759171 --- /dev/null +++ b/drivers/iio/accel/msa311.c @@ -0,0 +1,1321 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MEMSensing digital 3-Axis accelerometer + * + * MSA311 is a tri-axial, low-g accelerometer with I2C digital output for + * sensitivity consumer applications. It has dynamic user-selectable full + * scales range of +-2g/+-4g/+-8g/+-16g and allows acceleration measurements + * with output data rates from 1Hz to 1000Hz. + * + * MSA311 is available in an ultra small (2mm x 2mm, height 0.95mm) LGA package + * and is guaranteed to operate over -40C to +85C. + * + * This driver supports following MSA311 features: + * - IIO interface + * - Different power modes: NORMAL, SUSPEND + * - ODR (Output Data Rate) selection + * - Scale selection + * - IIO triggered buffer + * - NEW_DATA interrupt + trigger + * + * Below features to be done: + * - Motion Events: ACTIVE, TAP, ORIENT, FREEFALL + * - Low Power mode + * + * Copyright (c) 2022, SberDevices. All Rights Reserved. + * + * Author: Dmitry Rokosov <ddrokosov@sberdevices.ru> + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/string_helpers.h> +#include <linux/units.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define MSA311_SOFT_RESET_REG 0x00 +#define MSA311_PARTID_REG 0x01 +#define MSA311_ACC_X_REG 0x02 +#define MSA311_ACC_Y_REG 0x04 +#define MSA311_ACC_Z_REG 0x06 +#define MSA311_MOTION_INT_REG 0x09 +#define MSA311_DATA_INT_REG 0x0A +#define MSA311_TAP_ACTIVE_STS_REG 0x0B +#define MSA311_ORIENT_STS_REG 0x0C +#define MSA311_RANGE_REG 0x0F +#define MSA311_ODR_REG 0x10 +#define MSA311_PWR_MODE_REG 0x11 +#define MSA311_SWAP_POLARITY_REG 0x12 +#define MSA311_INT_SET_0_REG 0x16 +#define MSA311_INT_SET_1_REG 0x17 +#define MSA311_INT_MAP_0_REG 0x19 +#define MSA311_INT_MAP_1_REG 0x1A +#define MSA311_INT_CONFIG_REG 0x20 +#define MSA311_INT_LATCH_REG 0x21 +#define MSA311_FREEFALL_DUR_REG 0x22 +#define MSA311_FREEFALL_TH_REG 0x23 +#define MSA311_FREEFALL_HY_REG 0x24 +#define MSA311_ACTIVE_DUR_REG 0x27 +#define MSA311_ACTIVE_TH_REG 0x28 +#define MSA311_TAP_DUR_REG 0x2A +#define MSA311_TAP_TH_REG 0x2B +#define MSA311_ORIENT_HY_REG 0x2C +#define MSA311_Z_BLOCK_REG 0x2D +#define MSA311_OFFSET_X_REG 0x38 +#define MSA311_OFFSET_Y_REG 0x39 +#define MSA311_OFFSET_Z_REG 0x3A + +enum msa311_fields { + /* Soft_Reset */ + F_SOFT_RESET_I2C, F_SOFT_RESET_SPI, + /* Motion_Interrupt */ + F_ORIENT_INT, F_S_TAP_INT, F_D_TAP_INT, F_ACTIVE_INT, F_FREEFALL_INT, + /* Data_Interrupt */ + F_NEW_DATA_INT, + /* Tap_Active_Status */ + F_TAP_SIGN, F_TAP_FIRST_X, F_TAP_FIRST_Y, F_TAP_FIRST_Z, F_ACTV_SIGN, + F_ACTV_FIRST_X, F_ACTV_FIRST_Y, F_ACTV_FIRST_Z, + /* Orientation_Status */ + F_ORIENT_Z, F_ORIENT_X_Y, + /* Range */ + F_FS, + /* ODR */ + F_X_AXIS_DIS, F_Y_AXIS_DIS, F_Z_AXIS_DIS, F_ODR, + /* Power Mode/Bandwidth */ + F_PWR_MODE, F_LOW_POWER_BW, + /* Swap_Polarity */ + F_X_POLARITY, F_Y_POLARITY, F_Z_POLARITY, F_X_Y_SWAP, + /* Int_Set_0 */ + F_ORIENT_INT_EN, F_S_TAP_INT_EN, F_D_TAP_INT_EN, F_ACTIVE_INT_EN_Z, + F_ACTIVE_INT_EN_Y, F_ACTIVE_INT_EN_X, + /* Int_Set_1 */ + F_NEW_DATA_INT_EN, F_FREEFALL_INT_EN, + /* Int_Map_0 */ + F_INT1_ORIENT, F_INT1_S_TAP, F_INT1_D_TAP, F_INT1_ACTIVE, + F_INT1_FREEFALL, + /* Int_Map_1 */ + F_INT1_NEW_DATA, + /* Int_Config */ + F_INT1_OD, F_INT1_LVL, + /* Int_Latch */ + F_RESET_INT, F_LATCH_INT, + /* Freefall_Hy */ + F_FREEFALL_MODE, F_FREEFALL_HY, + /* Active_Dur */ + F_ACTIVE_DUR, + /* Tap_Dur */ + F_TAP_QUIET, F_TAP_SHOCK, F_TAP_DUR, + /* Tap_Th */ + F_TAP_TH, + /* Orient_Hy */ + F_ORIENT_HYST, F_ORIENT_BLOCKING, F_ORIENT_MODE, + /* Z_Block */ + F_Z_BLOCKING, + /* End of register map */ + F_MAX_FIELDS, +}; + +static const struct reg_field msa311_reg_fields[] = { + /* Soft_Reset */ + [F_SOFT_RESET_I2C] = REG_FIELD(MSA311_SOFT_RESET_REG, 2, 2), + [F_SOFT_RESET_SPI] = REG_FIELD(MSA311_SOFT_RESET_REG, 5, 5), + /* Motion_Interrupt */ + [F_ORIENT_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 6, 6), + [F_S_TAP_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 5, 5), + [F_D_TAP_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 4, 4), + [F_ACTIVE_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 2, 2), + [F_FREEFALL_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 0, 0), + /* Data_Interrupt */ + [F_NEW_DATA_INT] = REG_FIELD(MSA311_DATA_INT_REG, 0, 0), + /* Tap_Active_Status */ + [F_TAP_SIGN] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 7, 7), + [F_TAP_FIRST_X] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 6, 6), + [F_TAP_FIRST_Y] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 5, 5), + [F_TAP_FIRST_Z] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 4, 4), + [F_ACTV_SIGN] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 3, 3), + [F_ACTV_FIRST_X] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 2, 2), + [F_ACTV_FIRST_Y] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 1, 1), + [F_ACTV_FIRST_Z] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 0, 0), + /* Orientation_Status */ + [F_ORIENT_Z] = REG_FIELD(MSA311_ORIENT_STS_REG, 6, 6), + [F_ORIENT_X_Y] = REG_FIELD(MSA311_ORIENT_STS_REG, 4, 5), + /* Range */ + [F_FS] = REG_FIELD(MSA311_RANGE_REG, 0, 1), + /* ODR */ + [F_X_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 7, 7), + [F_Y_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 6, 6), + [F_Z_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 5, 5), + [F_ODR] = REG_FIELD(MSA311_ODR_REG, 0, 3), + /* Power Mode/Bandwidth */ + [F_PWR_MODE] = REG_FIELD(MSA311_PWR_MODE_REG, 6, 7), + [F_LOW_POWER_BW] = REG_FIELD(MSA311_PWR_MODE_REG, 1, 4), + /* Swap_Polarity */ + [F_X_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 3, 3), + [F_Y_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 2, 2), + [F_Z_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 1, 1), + [F_X_Y_SWAP] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 0, 0), + /* Int_Set_0 */ + [F_ORIENT_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 6, 6), + [F_S_TAP_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 5, 5), + [F_D_TAP_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 4, 4), + [F_ACTIVE_INT_EN_Z] = REG_FIELD(MSA311_INT_SET_0_REG, 2, 2), + [F_ACTIVE_INT_EN_Y] = REG_FIELD(MSA311_INT_SET_0_REG, 1, 1), + [F_ACTIVE_INT_EN_X] = REG_FIELD(MSA311_INT_SET_0_REG, 0, 0), + /* Int_Set_1 */ + [F_NEW_DATA_INT_EN] = REG_FIELD(MSA311_INT_SET_1_REG, 4, 4), + [F_FREEFALL_INT_EN] = REG_FIELD(MSA311_INT_SET_1_REG, 3, 3), + /* Int_Map_0 */ + [F_INT1_ORIENT] = REG_FIELD(MSA311_INT_MAP_0_REG, 6, 6), + [F_INT1_S_TAP] = REG_FIELD(MSA311_INT_MAP_0_REG, 5, 5), + [F_INT1_D_TAP] = REG_FIELD(MSA311_INT_MAP_0_REG, 4, 4), + [F_INT1_ACTIVE] = REG_FIELD(MSA311_INT_MAP_0_REG, 2, 2), + [F_INT1_FREEFALL] = REG_FIELD(MSA311_INT_MAP_0_REG, 0, 0), + /* Int_Map_1 */ + [F_INT1_NEW_DATA] = REG_FIELD(MSA311_INT_MAP_1_REG, 0, 0), + /* Int_Config */ + [F_INT1_OD] = REG_FIELD(MSA311_INT_CONFIG_REG, 1, 1), + [F_INT1_LVL] = REG_FIELD(MSA311_INT_CONFIG_REG, 0, 0), + /* Int_Latch */ + [F_RESET_INT] = REG_FIELD(MSA311_INT_LATCH_REG, 7, 7), + [F_LATCH_INT] = REG_FIELD(MSA311_INT_LATCH_REG, 0, 3), + /* Freefall_Hy */ + [F_FREEFALL_MODE] = REG_FIELD(MSA311_FREEFALL_HY_REG, 2, 2), + [F_FREEFALL_HY] = REG_FIELD(MSA311_FREEFALL_HY_REG, 0, 1), + /* Active_Dur */ + [F_ACTIVE_DUR] = REG_FIELD(MSA311_ACTIVE_DUR_REG, 0, 1), + /* Tap_Dur */ + [F_TAP_QUIET] = REG_FIELD(MSA311_TAP_DUR_REG, 7, 7), + [F_TAP_SHOCK] = REG_FIELD(MSA311_TAP_DUR_REG, 6, 6), + [F_TAP_DUR] = REG_FIELD(MSA311_TAP_DUR_REG, 0, 2), + /* Tap_Th */ + [F_TAP_TH] = REG_FIELD(MSA311_TAP_TH_REG, 0, 4), + /* Orient_Hy */ + [F_ORIENT_HYST] = REG_FIELD(MSA311_ORIENT_HY_REG, 4, 6), + [F_ORIENT_BLOCKING] = REG_FIELD(MSA311_ORIENT_HY_REG, 2, 3), + [F_ORIENT_MODE] = REG_FIELD(MSA311_ORIENT_HY_REG, 0, 1), + /* Z_Block */ + [F_Z_BLOCKING] = REG_FIELD(MSA311_Z_BLOCK_REG, 0, 3), +}; + +#define MSA311_WHO_AM_I 0x13 + +/* + * Possible Full Scale ranges + * + * Axis data is 12-bit signed value, so + * + * fs0 = (2 + 2) * 9.81 / (2^11) = 0.009580 + * fs1 = (4 + 4) * 9.81 / (2^11) = 0.019160 + * fs2 = (8 + 8) * 9.81 / (2^11) = 0.038320 + * fs3 = (16 + 16) * 9.81 / (2^11) = 0.076641 + */ +enum { + MSA311_FS_2G, + MSA311_FS_4G, + MSA311_FS_8G, + MSA311_FS_16G, +}; + +struct iio_decimal_fract { + int integral; + int microfract; +}; + +static const struct iio_decimal_fract msa311_fs_table[] = { + {0, 9580}, {0, 19160}, {0, 38320}, {0, 76641}, +}; + +/* Possible Output Data Rate values */ +enum { + MSA311_ODR_1_HZ, + MSA311_ODR_1_95_HZ, + MSA311_ODR_3_9_HZ, + MSA311_ODR_7_81_HZ, + MSA311_ODR_15_63_HZ, + MSA311_ODR_31_25_HZ, + MSA311_ODR_62_5_HZ, + MSA311_ODR_125_HZ, + MSA311_ODR_250_HZ, + MSA311_ODR_500_HZ, + MSA311_ODR_1000_HZ, +}; + +static const struct iio_decimal_fract msa311_odr_table[] = { + {1, 0}, {1, 950000}, {3, 900000}, {7, 810000}, {15, 630000}, + {31, 250000}, {62, 500000}, {125, 0}, {250, 0}, {500, 0}, {1000, 0}, +}; + +/* All supported power modes */ +#define MSA311_PWR_MODE_NORMAL 0b00 +#define MSA311_PWR_MODE_LOW 0b01 +#define MSA311_PWR_MODE_UNKNOWN 0b10 +#define MSA311_PWR_MODE_SUSPEND 0b11 +static const char * const msa311_pwr_modes[] = { + [MSA311_PWR_MODE_NORMAL] = "normal", + [MSA311_PWR_MODE_LOW] = "low", + [MSA311_PWR_MODE_UNKNOWN] = "unknown", + [MSA311_PWR_MODE_SUSPEND] = "suspend", +}; + +/* Autosuspend delay */ +#define MSA311_PWR_SLEEP_DELAY_MS 2000 + +/* Possible INT1 types and levels */ +enum { + MSA311_INT1_OD_PUSH_PULL, + MSA311_INT1_OD_OPEN_DRAIN, +}; + +enum { + MSA311_INT1_LVL_LOW, + MSA311_INT1_LVL_HIGH, +}; + +/* Latch INT modes */ +#define MSA311_LATCH_INT_NOT_LATCHED 0b0000 +#define MSA311_LATCH_INT_250MS 0b0001 +#define MSA311_LATCH_INT_500MS 0b0010 +#define MSA311_LATCH_INT_1S 0b0011 +#define MSA311_LATCH_INT_2S 0b0100 +#define MSA311_LATCH_INT_4S 0b0101 +#define MSA311_LATCH_INT_8S 0b0110 +#define MSA311_LATCH_INT_1MS 0b1010 +#define MSA311_LATCH_INT_2MS 0b1011 +#define MSA311_LATCH_INT_25MS 0b1100 +#define MSA311_LATCH_INT_50MS 0b1101 +#define MSA311_LATCH_INT_100MS 0b1110 +#define MSA311_LATCH_INT_LATCHED 0b0111 + +static const struct regmap_range msa311_readonly_registers[] = { + regmap_reg_range(MSA311_PARTID_REG, MSA311_ORIENT_STS_REG), +}; + +static const struct regmap_access_table msa311_writeable_table = { + .no_ranges = msa311_readonly_registers, + .n_no_ranges = ARRAY_SIZE(msa311_readonly_registers), +}; + +static const struct regmap_range msa311_writeonly_registers[] = { + regmap_reg_range(MSA311_SOFT_RESET_REG, MSA311_SOFT_RESET_REG), +}; + +static const struct regmap_access_table msa311_readable_table = { + .no_ranges = msa311_writeonly_registers, + .n_no_ranges = ARRAY_SIZE(msa311_writeonly_registers), +}; + +static const struct regmap_range msa311_volatile_registers[] = { + regmap_reg_range(MSA311_ACC_X_REG, MSA311_ORIENT_STS_REG), +}; + +static const struct regmap_access_table msa311_volatile_table = { + .yes_ranges = msa311_volatile_registers, + .n_yes_ranges = ARRAY_SIZE(msa311_volatile_registers), +}; + +static const struct regmap_config msa311_regmap_config = { + .name = "msa311", + .reg_bits = 8, + .val_bits = 8, + .max_register = MSA311_OFFSET_Z_REG, + .wr_table = &msa311_writeable_table, + .rd_table = &msa311_readable_table, + .volatile_table = &msa311_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +#define MSA311_GENMASK(field) ({ \ + typeof(&(msa311_reg_fields)[0]) _field; \ + _field = &msa311_reg_fields[(field)]; \ + GENMASK(_field->msb, _field->lsb); \ +}) + +/** + * struct msa311_priv - MSA311 internal private state + * @regs: Underlying I2C bus adapter used to abstract slave + * register accesses + * @fields: Abstract objects for each registers fields access + * @dev: Device handler associated with appropriate bus client + * @lock: Protects msa311 device state between setup and data access routines + * (power transitions, samp_freq/scale tune, retrieving axes data, etc) + * @chip_name: Chip name in the format "msa311-%02x" % partid + * @new_data_trig: Optional NEW_DATA interrupt driven trigger used + * to notify external consumers a new sample is ready + * @vdd: Optional external voltage regulator for the device power supply + */ +struct msa311_priv { + struct regmap *regs; + struct regmap_field *fields[F_MAX_FIELDS]; + + struct device *dev; + struct mutex lock; + char *chip_name; + + struct iio_trigger *new_data_trig; + struct regulator *vdd; +}; + +enum msa311_si { + MSA311_SI_X, + MSA311_SI_Y, + MSA311_SI_Z, + MSA311_SI_TIMESTAMP, +}; + +#define MSA311_ACCEL_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_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = MSA311_SI_##axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_LE, \ + }, \ + .datasheet_name = "ACC_"#axis, \ +} + +static const struct iio_chan_spec msa311_channels[] = { + MSA311_ACCEL_CHANNEL(X), + MSA311_ACCEL_CHANNEL(Y), + MSA311_ACCEL_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(MSA311_SI_TIMESTAMP), +}; + +/** + * msa311_get_odr() - Read Output Data Rate (ODR) value from MSA311 accel + * @msa311: MSA311 internal private state + * @odr: output ODR value + * + * This function should be called under msa311->lock. + * + * Return: 0 on success, -ERRNO in other failures + */ +static int msa311_get_odr(struct msa311_priv *msa311, unsigned int *odr) +{ + int err; + + err = regmap_field_read(msa311->fields[F_ODR], odr); + if (err) + return err; + + /* + * Filter the same 1000Hz ODR register values based on datasheet info. + * ODR can be equal to 1010-1111 for 1000Hz, but function returns 1010 + * all the time. + */ + if (*odr > MSA311_ODR_1000_HZ) + *odr = MSA311_ODR_1000_HZ; + + return 0; +} + +/** + * msa311_set_odr() - Setup Output Data Rate (ODR) value for MSA311 accel + * @msa311: MSA311 internal private state + * @odr: requested ODR value + * + * This function should be called under msa311->lock. Possible ODR values: + * - 1Hz (not available in normal mode) + * - 1.95Hz (not available in normal mode) + * - 3.9Hz + * - 7.81Hz + * - 15.63Hz + * - 31.25Hz + * - 62.5Hz + * - 125Hz + * - 250Hz + * - 500Hz + * - 1000Hz + * + * Return: 0 on success, -EINVAL for bad ODR value in the certain power mode, + * -ERRNO in other failures + */ +static int msa311_set_odr(struct msa311_priv *msa311, unsigned int odr) +{ + struct device *dev = msa311->dev; + unsigned int pwr_mode; + bool good_odr; + int err; + + err = regmap_field_read(msa311->fields[F_PWR_MODE], &pwr_mode); + if (err) + return err; + + /* Filter bad ODR values */ + if (pwr_mode == MSA311_PWR_MODE_NORMAL) + good_odr = (odr > MSA311_ODR_1_95_HZ); + else + good_odr = false; + + if (!good_odr) { + dev_err(dev, + "can't set odr %u.%06uHz, not available in %s mode\n", + msa311_odr_table[odr].integral, + msa311_odr_table[odr].microfract, + msa311_pwr_modes[pwr_mode]); + return -EINVAL; + } + + return regmap_field_write(msa311->fields[F_ODR], odr); +} + +/** + * msa311_wait_for_next_data() - Wait next accel data available after resume + * @msa311: MSA311 internal private state + * + * Return: 0 on success, -EINTR if msleep() was interrupted, + * -ERRNO in other failures + */ +static int msa311_wait_for_next_data(struct msa311_priv *msa311) +{ + static const unsigned int unintr_thresh_ms = 20; + struct device *dev = msa311->dev; + unsigned long freq_uhz; + unsigned long wait_ms; + unsigned int odr; + int err; + + err = msa311_get_odr(msa311, &odr); + if (err) { + dev_err(dev, "can't get actual frequency (%pe)\n", + ERR_PTR(err)); + return err; + } + + /* + * After msa311 resuming is done, we need to wait for data + * to be refreshed by accel logic. + * A certain timeout is calculated based on the current ODR value. + * If requested timeout isn't so long (let's assume 20ms), + * we can wait for next data in uninterruptible sleep. + */ + freq_uhz = msa311_odr_table[odr].integral * MICROHZ_PER_HZ + + msa311_odr_table[odr].microfract; + wait_ms = (MICROHZ_PER_HZ / freq_uhz) * MSEC_PER_SEC; + + if (wait_ms < unintr_thresh_ms) + usleep_range(wait_ms * USEC_PER_MSEC, + unintr_thresh_ms * USEC_PER_MSEC); + else if (msleep_interruptible(wait_ms)) + return -EINTR; + + return 0; +} + +/** + * msa311_set_pwr_mode() - Install certain MSA311 power mode + * @msa311: MSA311 internal private state + * @mode: Power mode can be equal to NORMAL or SUSPEND + * + * This function should be called under msa311->lock. + * + * Return: 0 on success, -ERRNO on failure + */ +static int msa311_set_pwr_mode(struct msa311_priv *msa311, unsigned int mode) +{ + struct device *dev = msa311->dev; + unsigned int prev_mode; + int err; + + if (mode >= ARRAY_SIZE(msa311_pwr_modes)) + return -EINVAL; + + dev_dbg(dev, "transition to %s mode\n", msa311_pwr_modes[mode]); + + err = regmap_field_read(msa311->fields[F_PWR_MODE], &prev_mode); + if (err) + return err; + + err = regmap_field_write(msa311->fields[F_PWR_MODE], mode); + if (err) + return err; + + /* Wait actual data if we wake up */ + if (prev_mode == MSA311_PWR_MODE_SUSPEND && + mode == MSA311_PWR_MODE_NORMAL) + return msa311_wait_for_next_data(msa311); + + return 0; +} + +/** + * msa311_get_axis() - Read MSA311 accel data for certain IIO channel axis spec + * @msa311: MSA311 internal private state + * @chan: IIO channel specification + * @axis: Output accel axis data for requested IIO channel spec + * + * This function should be called under msa311->lock. + * + * Return: 0 on success, -EINVAL for unknown IIO channel specification, + * -ERRNO in other failures + */ +static int msa311_get_axis(struct msa311_priv *msa311, + const struct iio_chan_spec * const chan, + __le16 *axis) +{ + struct device *dev = msa311->dev; + unsigned int axis_reg; + + if (chan->scan_index < MSA311_SI_X || chan->scan_index > MSA311_SI_Z) { + dev_err(dev, "invalid scan_index value [%d]\n", + chan->scan_index); + return -EINVAL; + } + + /* Axes data layout has 2 byte gap for each axis starting from X axis */ + axis_reg = MSA311_ACC_X_REG + (chan->scan_index << 1); + + return regmap_bulk_read(msa311->regs, axis_reg, axis, sizeof(*axis)); +} + +static int msa311_read_raw_data(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + __le16 axis; + int err; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + err = iio_device_claim_direct_mode(indio_dev); + if (err) + return err; + + mutex_lock(&msa311->lock); + err = msa311_get_axis(msa311, chan, &axis); + mutex_unlock(&msa311->lock); + + iio_device_release_direct_mode(indio_dev); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) { + dev_err(dev, "can't get axis %s (%pe)\n", + chan->datasheet_name, ERR_PTR(err)); + return err; + } + + /* + * Axis data format is: + * ACC_X = (ACC_X_MSB[7:0] << 4) | ACC_X_LSB[7:4] + */ + *val = sign_extend32(le16_to_cpu(axis) >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + + return IIO_VAL_INT; +} + +static int msa311_read_scale(struct iio_dev *indio_dev, int *val, int *val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int fs; + int err; + + mutex_lock(&msa311->lock); + err = regmap_field_read(msa311->fields[F_FS], &fs); + mutex_unlock(&msa311->lock); + if (err) { + dev_err(dev, "can't get actual scale (%pe)\n", ERR_PTR(err)); + return err; + } + + *val = msa311_fs_table[fs].integral; + *val2 = msa311_fs_table[fs].microfract; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int msa311_read_samp_freq(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int odr; + int err; + + mutex_lock(&msa311->lock); + err = msa311_get_odr(msa311, &odr); + mutex_unlock(&msa311->lock); + if (err) { + dev_err(dev, "can't get actual frequency (%pe)\n", + ERR_PTR(err)); + return err; + } + + *val = msa311_odr_table[odr].integral; + *val2 = msa311_odr_table[odr].microfract; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int msa311_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_RAW: + return msa311_read_raw_data(indio_dev, chan, val, val2); + + case IIO_CHAN_INFO_SCALE: + return msa311_read_scale(indio_dev, val, val2); + + case IIO_CHAN_INFO_SAMP_FREQ: + return msa311_read_samp_freq(indio_dev, val, val2); + + default: + return -EINVAL; + } +} + +static int msa311_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_SAMP_FREQ: + *vals = (int *)msa311_odr_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* ODR value has 2 ints (integer and fractional parts) */ + *length = ARRAY_SIZE(msa311_odr_table) * 2; + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SCALE: + *vals = (int *)msa311_fs_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* FS value has 2 ints (integer and fractional parts) */ + *length = ARRAY_SIZE(msa311_fs_table) * 2; + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static int msa311_write_scale(struct iio_dev *indio_dev, int val, int val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int fs; + int err; + + /* We do not have fs >= 1, so skip such values */ + if (val) + return 0; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + err = -EINVAL; + for (fs = 0; fs < ARRAY_SIZE(msa311_fs_table); fs++) + /* Do not check msa311_fs_table[fs].integral, it's always 0 */ + if (val2 == msa311_fs_table[fs].microfract) { + mutex_lock(&msa311->lock); + err = regmap_field_write(msa311->fields[F_FS], fs); + mutex_unlock(&msa311->lock); + break; + } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) + dev_err(dev, "can't update scale (%pe)\n", ERR_PTR(err)); + + return err; +} + +static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int odr; + int err; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + /* + * Sampling frequency changing is prohibited when buffer mode is + * enabled, because sometimes MSA311 chip returns outliers during + * frequency values growing up in the read operation moment. + */ + err = iio_device_claim_direct_mode(indio_dev); + if (err) + return err; + + err = -EINVAL; + for (odr = 0; odr < ARRAY_SIZE(msa311_odr_table); odr++) + if (val == msa311_odr_table[odr].integral && + val2 == msa311_odr_table[odr].microfract) { + mutex_lock(&msa311->lock); + err = msa311_set_odr(msa311, odr); + mutex_unlock(&msa311->lock); + break; + } + + iio_device_release_direct_mode(indio_dev); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) + dev_err(dev, "can't update frequency (%pe)\n", ERR_PTR(err)); + + return err; +} + +static int msa311_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return msa311_write_scale(indio_dev, val, val2); + + case IIO_CHAN_INFO_SAMP_FREQ: + return msa311_write_samp_freq(indio_dev, val, val2); + + default: + return -EINVAL; + } +} + +static int msa311_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + int err; + + if (reg > regmap_get_max_register(msa311->regs)) + return -EINVAL; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + mutex_lock(&msa311->lock); + + if (readval) + err = regmap_read(msa311->regs, reg, readval); + else + err = regmap_write(msa311->regs, reg, writeval); + + mutex_unlock(&msa311->lock); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) + dev_err(dev, "can't %s register %u from debugfs (%pe)\n", + str_read_write(readval), reg, ERR_PTR(err)); + + return err; +} + +static int msa311_buffer_preenable(struct iio_dev *indio_dev) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + + return pm_runtime_resume_and_get(dev); +} + +static int msa311_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int msa311_set_new_data_trig_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + int err; + + mutex_lock(&msa311->lock); + err = regmap_field_write(msa311->fields[F_NEW_DATA_INT_EN], state); + mutex_unlock(&msa311->lock); + if (err) + dev_err(dev, + "can't %s buffer due to new_data_int failure (%pe)\n", + str_enable_disable(state), ERR_PTR(err)); + + return err; +} + +static int msa311_validate_device(struct iio_trigger *trig, + struct iio_dev *indio_dev) +{ + return iio_trigger_get_drvdata(trig) == indio_dev ? 0 : -EINVAL; +} + +static irqreturn_t msa311_buffer_thread(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct msa311_priv *msa311 = iio_priv(pf->indio_dev); + struct iio_dev *indio_dev = pf->indio_dev; + const struct iio_chan_spec *chan; + struct device *dev = msa311->dev; + int bit, err, i = 0; + __le16 axis; + struct { + __le16 channels[MSA311_SI_Z + 1]; + s64 ts __aligned(8); + } buf; + + memset(&buf, 0, sizeof(buf)); + + mutex_lock(&msa311->lock); + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + chan = &msa311_channels[bit]; + + err = msa311_get_axis(msa311, chan, &axis); + if (err) { + mutex_unlock(&msa311->lock); + dev_err(dev, "can't get axis %s (%pe)\n", + chan->datasheet_name, ERR_PTR(err)); + goto notify_done; + } + + buf.channels[i++] = axis; + } + + mutex_unlock(&msa311->lock); + + iio_push_to_buffers_with_timestamp(indio_dev, &buf, + iio_get_time_ns(indio_dev)); + +notify_done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t msa311_irq_thread(int irq, void *p) +{ + struct msa311_priv *msa311 = iio_priv(p); + unsigned int new_data_int_enabled; + struct device *dev = msa311->dev; + int err; + + mutex_lock(&msa311->lock); + + /* + * We do not check NEW_DATA int status, because based on the + * specification it's cleared automatically after a fixed time. + * So just check that is enabled by driver logic. + */ + err = regmap_field_read(msa311->fields[F_NEW_DATA_INT_EN], + &new_data_int_enabled); + + mutex_unlock(&msa311->lock); + if (err) { + dev_err(dev, "can't read new_data interrupt state (%pe)\n", + ERR_PTR(err)); + return IRQ_NONE; + } + + if (new_data_int_enabled) + iio_trigger_poll_chained(msa311->new_data_trig); + + return IRQ_HANDLED; +} + +static const struct iio_info msa311_info = { + .read_raw = msa311_read_raw, + .read_avail = msa311_read_avail, + .write_raw = msa311_write_raw, + .debugfs_reg_access = msa311_debugfs_reg_access, +}; + +static const struct iio_buffer_setup_ops msa311_buffer_setup_ops = { + .preenable = msa311_buffer_preenable, + .postdisable = msa311_buffer_postdisable, +}; + +static const struct iio_trigger_ops msa311_new_data_trig_ops = { + .set_trigger_state = msa311_set_new_data_trig_state, + .validate_device = msa311_validate_device, +}; + +static int msa311_check_partid(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + unsigned int partid; + int err; + + err = regmap_read(msa311->regs, MSA311_PARTID_REG, &partid); + if (err) + return dev_err_probe(dev, err, "failed to read partid\n"); + + if (partid != MSA311_WHO_AM_I) + dev_warn(dev, "invalid partid (%#x), expected (%#x)\n", + partid, MSA311_WHO_AM_I); + + msa311->chip_name = devm_kasprintf(dev, GFP_KERNEL, + "msa311-%02x", partid); + if (!msa311->chip_name) + return dev_err_probe(dev, -ENOMEM, "can't alloc chip name\n"); + + return 0; +} + +static int msa311_soft_reset(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + int err; + + err = regmap_write(msa311->regs, MSA311_SOFT_RESET_REG, + MSA311_GENMASK(F_SOFT_RESET_I2C) | + MSA311_GENMASK(F_SOFT_RESET_SPI)); + if (err) + return dev_err_probe(dev, err, "can't soft reset all logic\n"); + + return 0; +} + +static int msa311_chip_init(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + const char zero_bulk[2] = { }; + int err; + + err = regmap_write(msa311->regs, MSA311_RANGE_REG, MSA311_FS_16G); + if (err) + return dev_err_probe(dev, err, "failed to setup accel range\n"); + + /* Disable all interrupts by default */ + err = regmap_bulk_write(msa311->regs, MSA311_INT_SET_0_REG, + zero_bulk, sizeof(zero_bulk)); + if (err) + return dev_err_probe(dev, err, + "can't disable set0/set1 interrupts\n"); + + /* Unmap all INT1 interrupts by default */ + err = regmap_bulk_write(msa311->regs, MSA311_INT_MAP_0_REG, + zero_bulk, sizeof(zero_bulk)); + if (err) + return dev_err_probe(dev, err, + "failed to unmap map0/map1 interrupts\n"); + + /* Disable all axes by default */ + err = regmap_update_bits(msa311->regs, MSA311_ODR_REG, + MSA311_GENMASK(F_X_AXIS_DIS) | + MSA311_GENMASK(F_Y_AXIS_DIS) | + MSA311_GENMASK(F_Z_AXIS_DIS), 0); + if (err) + return dev_err_probe(dev, err, "can't enable all axes\n"); + + err = msa311_set_odr(msa311, MSA311_ODR_125_HZ); + if (err) + return dev_err_probe(dev, err, + "failed to set accel frequency\n"); + + return 0; +} + +static int msa311_setup_interrupts(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + struct i2c_client *i2c = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct iio_trigger *trig; + int err; + + /* Keep going without interrupts if no initialized I2C IRQ */ + if (i2c->irq <= 0) + return 0; + + err = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + msa311_irq_thread, IRQF_ONESHOT, + msa311->chip_name, indio_dev); + if (err) + return dev_err_probe(dev, err, "failed to request IRQ\n"); + + trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name); + if (!trig) + return dev_err_probe(dev, -ENOMEM, + "can't allocate newdata trigger\n"); + + msa311->new_data_trig = trig; + msa311->new_data_trig->ops = &msa311_new_data_trig_ops; + iio_trigger_set_drvdata(msa311->new_data_trig, indio_dev); + + err = devm_iio_trigger_register(dev, msa311->new_data_trig); + if (err) + return dev_err_probe(dev, err, + "can't register newdata trigger\n"); + + err = regmap_field_write(msa311->fields[F_INT1_OD], + MSA311_INT1_OD_PUSH_PULL); + if (err) + return dev_err_probe(dev, err, + "can't enable push-pull interrupt\n"); + + err = regmap_field_write(msa311->fields[F_INT1_LVL], + MSA311_INT1_LVL_HIGH); + if (err) + return dev_err_probe(dev, err, + "can't set active interrupt level\n"); + + err = regmap_field_write(msa311->fields[F_LATCH_INT], + MSA311_LATCH_INT_LATCHED); + if (err) + return dev_err_probe(dev, err, + "can't latch interrupt\n"); + + err = regmap_field_write(msa311->fields[F_RESET_INT], 1); + if (err) + return dev_err_probe(dev, err, + "can't reset interrupt\n"); + + err = regmap_field_write(msa311->fields[F_INT1_NEW_DATA], 1); + if (err) + return dev_err_probe(dev, err, + "can't map new data interrupt\n"); + + return 0; +} + +static int msa311_regmap_init(struct msa311_priv *msa311) +{ + struct regmap_field **fields = msa311->fields; + struct device *dev = msa311->dev; + struct i2c_client *i2c = to_i2c_client(dev); + struct regmap *regmap; + int i; + + regmap = devm_regmap_init_i2c(i2c, &msa311_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to register i2c regmap\n"); + + msa311->regs = regmap; + + for (i = 0; i < F_MAX_FIELDS; i++) { + fields[i] = devm_regmap_field_alloc(dev, + msa311->regs, + msa311_reg_fields[i]); + if (IS_ERR(msa311->fields[i])) + return dev_err_probe(dev, PTR_ERR(msa311->fields[i]), + "can't alloc field[%d]\n", i); + } + + return 0; +} + +static void msa311_powerdown(void *msa311) +{ + msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_SUSPEND); +} + +static void msa311_vdd_disable(void *vdd) +{ + regulator_disable(vdd); +} + +static int msa311_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct msa311_priv *msa311; + struct iio_dev *indio_dev; + int err; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*msa311)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "IIO device allocation failed\n"); + + msa311 = iio_priv(indio_dev); + msa311->dev = dev; + i2c_set_clientdata(i2c, indio_dev); + + err = msa311_regmap_init(msa311); + if (err) + return err; + + mutex_init(&msa311->lock); + + msa311->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(msa311->vdd)) + return dev_err_probe(dev, PTR_ERR(msa311->vdd), + "can't get vdd supply\n"); + + err = regulator_enable(msa311->vdd); + if (err) + return dev_err_probe(dev, err, "can't enable vdd supply\n"); + + err = devm_add_action_or_reset(dev, msa311_vdd_disable, msa311->vdd); + if (err) + return dev_err_probe(dev, err, + "can't add vdd disable action\n"); + + err = msa311_check_partid(msa311); + if (err) + return err; + + err = msa311_soft_reset(msa311); + if (err) + return err; + + err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_NORMAL); + if (err) + return dev_err_probe(dev, err, "failed to power on device\n"); + + /* + * Register powerdown deferred callback which suspends the chip + * after module unloaded. + * + * MSA311 should be in SUSPEND mode in the two cases: + * 1) When driver is loaded, but we do not have any data or + * configuration requests to it (we are solving it using + * autosuspend feature). + * 2) When driver is unloaded and device is not used (devm action is + * used in this case). + */ + err = devm_add_action_or_reset(dev, msa311_powerdown, msa311); + if (err) + return dev_err_probe(dev, err, "can't add powerdown action\n"); + + err = pm_runtime_set_active(dev); + if (err) + return err; + + err = devm_pm_runtime_enable(dev); + if (err) + return err; + + pm_runtime_get_noresume(dev); + pm_runtime_set_autosuspend_delay(dev, MSA311_PWR_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + err = msa311_chip_init(msa311); + if (err) + return err; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = msa311_channels; + indio_dev->num_channels = ARRAY_SIZE(msa311_channels); + indio_dev->name = msa311->chip_name; + indio_dev->info = &msa311_info; + + err = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + msa311_buffer_thread, + &msa311_buffer_setup_ops); + if (err) + return dev_err_probe(dev, err, + "can't setup IIO trigger buffer\n"); + + err = msa311_setup_interrupts(msa311); + if (err) + return err; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + err = devm_iio_device_register(dev, indio_dev); + if (err) + return dev_err_probe(dev, err, "IIO device register failed\n"); + + return 0; +} + +static int msa311_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct msa311_priv *msa311 = iio_priv(indio_dev); + int err; + + mutex_lock(&msa311->lock); + err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_SUSPEND); + mutex_unlock(&msa311->lock); + if (err) + dev_err(dev, "failed to power off device (%pe)\n", + ERR_PTR(err)); + + return err; +} + +static int msa311_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct msa311_priv *msa311 = iio_priv(indio_dev); + int err; + + mutex_lock(&msa311->lock); + err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_NORMAL); + mutex_unlock(&msa311->lock); + if (err) + dev_err(dev, "failed to power on device (%pe)\n", + ERR_PTR(err)); + + return err; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(msa311_pm_ops, msa311_runtime_suspend, + msa311_runtime_resume, NULL); + +static const struct i2c_device_id msa311_i2c_id[] = { + { .name = "msa311" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, msa311_i2c_id); + +static const struct of_device_id msa311_of_match[] = { + { .compatible = "memsensing,msa311" }, + { } +}; +MODULE_DEVICE_TABLE(of, msa311_of_match); + +static struct i2c_driver msa311_driver = { + .driver = { + .name = "msa311", + .of_match_table = msa311_of_match, + .pm = pm_ptr(&msa311_pm_ops), + }, + .probe_new = msa311_probe, + .id_table = msa311_i2c_id, +}; +module_i2c_driver(msa311_driver); + +MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>"); +MODULE_DESCRIPTION("MEMSensing MSA311 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 3d5bea651923..df600d2917c0 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -56,7 +56,11 @@ struct mxc4005_data { struct mutex mutex; struct regmap *regmap; struct iio_trigger *dready_trig; - __be16 buffer[8]; + /* Ensure timestamp is naturally aligned */ + struct { + __be16 chans[3]; + s64 timestamp __aligned(8); + } scan; bool trigger_enabled; }; @@ -135,7 +139,7 @@ static int mxc4005_read_xyz(struct mxc4005_data *data) int ret; ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER, - (u8 *) data->buffer, sizeof(data->buffer)); + data->scan.chans, sizeof(data->scan.chans)); if (ret < 0) { dev_err(data->dev, "failed to read axes\n"); return ret; @@ -150,7 +154,7 @@ static int mxc4005_read_axis(struct mxc4005_data *data, __be16 reg; int ret; - ret = regmap_bulk_read(data->regmap, addr, (u8 *) ®, sizeof(reg)); + ret = regmap_bulk_read(data->regmap, addr, ®, sizeof(reg)); if (ret < 0) { dev_err(data->dev, "failed to read reg %02x\n", addr); return ret; @@ -301,7 +305,7 @@ static irqreturn_t mxc4005_trigger_handler(int irq, void *private) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: @@ -310,19 +314,15 @@ err: return IRQ_HANDLED; } -static int mxc4005_clr_intr(struct mxc4005_data *data) +static void mxc4005_clr_intr(struct mxc4005_data *data) { int ret; /* clear interrupt */ ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1, MXC4005_REG_INT_CLR1_BIT_DRDYC); - if (ret < 0) { + if (ret < 0) dev_err(data->dev, "failed to write to reg_int_clr1\n"); - return ret; - } - - return 0; } static int mxc4005_set_trigger_state(struct iio_trigger *trig, @@ -353,20 +353,20 @@ static int mxc4005_set_trigger_state(struct iio_trigger *trig, return 0; } -static int mxc4005_trigger_try_reen(struct iio_trigger *trig) +static void mxc4005_trigger_reen(struct iio_trigger *trig) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct mxc4005_data *data = iio_priv(indio_dev); if (!data->dready_trig) - return 0; + return; - return mxc4005_clr_intr(data); + mxc4005_clr_intr(data); } static const struct iio_trigger_ops mxc4005_trigger_ops = { .set_trigger_state = mxc4005_set_trigger_state, - .try_reenable = mxc4005_trigger_try_reen, + .reenable = mxc4005_trigger_reen, }; static int mxc4005_chip_init(struct mxc4005_data *data) @@ -416,7 +416,6 @@ static int mxc4005_probe(struct i2c_client *client, mutex_init(&data->mutex); - indio_dev->dev.parent = &client->dev; indio_dev->channels = mxc4005_channels; indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels); indio_dev->available_scan_masks = mxc4005_scan_masks; @@ -438,7 +437,7 @@ static int mxc4005_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) return -ENOMEM; @@ -455,11 +454,8 @@ static int mxc4005_probe(struct i2c_client *client, return ret; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &mxc4005_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); - indio_dev->trig = data->dready_trig; - iio_trigger_get(indio_dev->trig); ret = devm_iio_trigger_register(&client->dev, data->dready_trig); if (ret) { @@ -467,6 +463,8 @@ static int mxc4005_probe(struct i2c_client *client, "failed to register trigger\n"); return ret; } + + indio_dev->trig = iio_trigger_get(data->dready_trig); } return devm_iio_device_register(&client->dev, indio_dev); @@ -474,12 +472,14 @@ static int mxc4005_probe(struct i2c_client *client, static const struct acpi_device_id mxc4005_acpi_match[] = { {"MXC4005", 0}, + {"MXC6655", 0}, { }, }; MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match); static const struct i2c_device_id mxc4005_id[] = { {"mxc4005", 0}, + {"mxc6655", 0}, { }, }; MODULE_DEVICE_TABLE(i2c, mxc4005_id); diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c index f532f8643aa4..9aeeadc420d3 100644 --- a/drivers/iio/accel/mxc6255.c +++ b/drivers/iio/accel/mxc6255.c @@ -138,7 +138,6 @@ static int mxc6255_probe(struct i2c_client *client, data->regmap = regmap; indio_dev->name = MXC6255_DRV_NAME; - indio_dev->dev.parent = &client->dev; indio_dev->channels = mxc6255_channels; indio_dev->num_channels = ARRAY_SIZE(mxc6255_channels); indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 66d768d971e1..87c54e41f6cc 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -167,8 +167,8 @@ struct sca3000_state { int mo_det_use_count; struct mutex lock; /* Can these share a cacheline ? */ - u8 rx[384] ____cacheline_aligned; - u8 tx[6] ____cacheline_aligned; + u8 rx[384] __aligned(IIO_DMA_MINALIGN); + u8 tx[6] __aligned(IIO_DMA_MINALIGN); }; /** @@ -186,9 +186,9 @@ struct sca3000_state { * @option_mode_2_freq: option mode 2 sampling frequency * @option_mode_2_3db_freq: 3db cutoff frequency of the low pass filter for * the second option mode. - * @mod_det_mult_xz: Bit wise multipliers to calculate the threshold + * @mot_det_mult_xz: Bit wise multipliers to calculate the threshold * for motion detection in the x and z axis. - * @mod_det_mult_y: Bit wise multipliers to calculate the threshold + * @mot_det_mult_y: Bit wise multipliers to calculate the threshold * for motion detection in the y axis. * * This structure is used to hold information about the functionality of a given @@ -351,7 +351,7 @@ static int __sca3000_unlock_reg_lock(struct sca3000_state *st) } /** - * sca3000_write_ctrl_reg() write to a lock protect ctrl register + * sca3000_write_ctrl_reg() - write to a lock protect ctrl register * @st: Driver specific device instance data. * @sel: selects which registers we wish to write to * @val: the value to be written @@ -389,7 +389,7 @@ error_ret: } /** - * sca3000_read_ctrl_reg() read from lock protected control register. + * sca3000_read_ctrl_reg() - read from lock protected control register. * @st: Driver specific device instance data. * @ctrl_reg: Which ctrl register do we want to read. * @@ -421,10 +421,10 @@ error_ret: } /** - * sca3000_show_rev() - sysfs interface to read the chip revision number + * sca3000_print_rev() - sysfs interface to read the chip revision number * @indio_dev: Device instance specific generic IIO data. * Driver specific device instance data can be obtained via - * via iio_priv(indio_dev) + * iio_priv(indio_dev) */ static int sca3000_print_rev(struct iio_dev *indio_dev) { @@ -534,6 +534,13 @@ static const struct iio_chan_spec sca3000_channels_with_temp[] = { BIT(IIO_CHAN_INFO_OFFSET), /* No buffer support */ .scan_index = -1, + .scan_type = { + .sign = 'u', + .realbits = 9, + .storagebits = 16, + .shift = 5, + .endianness = IIO_BE, + }, }, { .type = IIO_ACCEL, @@ -730,9 +737,9 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, mutex_unlock(&st->lock); return ret; } - *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF; - *val = ((*val) << (sizeof(*val) * 8 - 13)) >> - (sizeof(*val) * 8 - 13); + *val = sign_extend32(be16_to_cpup((__be16 *)st->rx) >> + chan->scan_type.shift, + chan->scan_type.realbits - 1); } else { /* get the temperature when available */ ret = sca3000_read_data_short(st, @@ -742,8 +749,9 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, mutex_unlock(&st->lock); return ret; } - *val = ((st->rx[0] & 0x3F) << 3) | - ((st->rx[1] & 0xE0) >> 5); + *val = (be16_to_cpup((__be16 *)st->rx) >> + chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); } mutex_unlock(&st->lock); return IIO_VAL_INT; @@ -859,9 +867,9 @@ error_ret: */ static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq); -/** +/* * sca3000_read_event_value() - query of a threshold or period - **/ + */ static int sca3000_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, @@ -902,7 +910,7 @@ static int sca3000_read_event_value(struct iio_dev *indio_dev, } /** - * sca3000_write_value() - control of threshold and period + * sca3000_write_event_value() - control of threshold and period * @indio_dev: Device instance specific IIO information. * @chan: Description of the channel for which the event is being * configured. @@ -980,7 +988,7 @@ static int sca3000_read_data(struct sca3000_state *st, st->tx[0] = SCA3000_READ_REG(reg_address_high); ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer)); if (ret) { - dev_err(get_device(&st->us->dev), "problem reading register"); + dev_err(&st->us->dev, "problem reading register\n"); return ret; } @@ -1100,9 +1108,9 @@ done: return IRQ_HANDLED; } -/** +/* * sca3000_read_event_config() what events are enabled - **/ + */ static int sca3000_read_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, @@ -1272,20 +1280,6 @@ static int sca3000_write_event_config(struct iio_dev *indio_dev, return ret; } -static int sca3000_configure_ring(struct iio_dev *indio_dev) -{ - struct iio_buffer *buffer; - - buffer = devm_iio_kfifo_allocate(&indio_dev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->modes |= INDIO_BUFFER_SOFTWARE; - - return 0; -} - static inline int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state) { @@ -1467,7 +1461,6 @@ static int sca3000_probe(struct spi_device *spi) st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi) ->driver_data]; - indio_dev->dev.parent = &spi->dev; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &sca3000_info; if (st->info->temp_output) { @@ -1480,7 +1473,8 @@ static int sca3000_probe(struct spi_device *spi) } indio_dev->modes = INDIO_DIRECT_MODE; - ret = sca3000_configure_ring(indio_dev); + ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev, + &sca3000_ring_setup_ops); if (ret) return ret; @@ -1494,7 +1488,6 @@ static int sca3000_probe(struct spi_device *spi) if (ret) return ret; } - indio_dev->setup_ops = &sca3000_ring_setup_ops; ret = sca3000_clean_setup(st); if (ret) goto error_free_irq; @@ -1530,7 +1523,7 @@ error_ret: return ret; } -static int sca3000_remove(struct spi_device *spi) +static void sca3000_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct sca3000_state *st = iio_priv(indio_dev); @@ -1541,8 +1534,6 @@ static int sca3000_remove(struct spi_device *spi) sca3000_stop_all_interrupts(st); if (spi->irq) free_irq(spi->irq, indio_dev); - - return 0; } static const struct spi_device_id sca3000_id[] = { diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c new file mode 100644 index 000000000000..eaa0c9cfda44 --- /dev/null +++ b/drivers/iio/accel/sca3300.c @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Murata SCA3300 3-axis industrial accelerometer + * + * Copyright (c) 2021 Vaisala Oyj. All rights reserved. + */ + +#include <linux/bitops.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include <asm/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define SCA3300_ALIAS "sca3300" + +#define SCA3300_CRC8_POLYNOMIAL 0x1d + +/* Device mode register */ +#define SCA3300_REG_MODE 0xd +#define SCA3300_MODE_SW_RESET 0x20 + +/* Last register in map */ +#define SCA3300_REG_SELBANK 0x1f + +/* Device status and mask */ +#define SCA3300_REG_STATUS 0x6 +#define SCA3300_STATUS_MASK GENMASK(8, 0) + +/* Device ID */ +#define SCA3300_REG_WHOAMI 0x10 +#define SCA3300_WHOAMI_ID 0x51 +#define SCL3300_WHOAMI_ID 0xC1 + +/* Device return status and mask */ +#define SCA3300_VALUE_RS_ERROR 0x3 +#define SCA3300_MASK_RS_STATUS GENMASK(1, 0) + +#define SCL3300_REG_ANG_CTRL 0x0C +#define SCL3300_ANG_ENABLE 0x1F + +enum sca3300_scan_indexes { + SCA3300_ACC_X = 0, + SCA3300_ACC_Y, + SCA3300_ACC_Z, + SCA3300_TEMP, + SCA3300_INCLI_X, + SCA3300_INCLI_Y, + SCA3300_INCLI_Z, + SCA3300_SCAN_MAX +}; + +/* + * Buffer size max case: + * Three accel channels, two bytes per channel. + * Temperature channel, two bytes. + * Three incli channels, two bytes per channel. + * Timestamp channel, eight bytes. + */ +#define SCA3300_MAX_BUFFER_SIZE (ALIGN(sizeof(s16) * SCA3300_SCAN_MAX, sizeof(s64)) + sizeof(s64)) + +#define SCA3300_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define SCA3300_INCLI_CHANNEL(index, reg, axis) { \ + .type = IIO_INCLI, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define SCA3300_TEMP_CHANNEL(index, reg) { \ + .type = IIO_TEMP, \ + .address = reg, \ + .scan_index = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec sca3300_channels[] = { + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_X, 0x1, X), + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Y, 0x2, Y), + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Z, 0x3, Z), + SCA3300_TEMP_CHANNEL(SCA3300_TEMP, 0x05), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const int sca3300_lp_freq[] = {70, 10}; +static const int sca3300_lp_freq_map[] = {0, 0, 0, 1}; + +static const int scl3300_lp_freq[] = {40, 70, 10}; +static const int scl3300_lp_freq_map[] = {0, 1, 2}; + +static const int sca3300_accel_scale[][2] = {{0, 370}, {0, 741}, {0, 185}}; +static const int sca3300_accel_scale_map[] = {0, 1, 2, 2}; + +static const int scl3300_accel_scale[][2] = {{0, 167}, {0, 333}, {0, 83}}; +static const int scl3300_accel_scale_map[] = {0, 1, 2}; + +static const int scl3300_incli_scale[][2] = {{0, 5495}}; +static const int scl3300_incli_scale_map[] = {0, 0, 0}; + +static const int sca3300_avail_modes_map[] = {0, 1, 2, 3}; +static const int scl3300_avail_modes_map[] = {0, 1, 3}; + +static const struct iio_chan_spec scl3300_channels[] = { + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_X, 0x1, X), + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Y, 0x2, Y), + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Z, 0x3, Z), + SCA3300_TEMP_CHANNEL(SCA3300_TEMP, 0x05), + SCA3300_INCLI_CHANNEL(SCA3300_INCLI_X, 0x09, X), + SCA3300_INCLI_CHANNEL(SCA3300_INCLI_Y, 0x0A, Y), + SCA3300_INCLI_CHANNEL(SCA3300_INCLI_Z, 0x0B, Z), + IIO_CHAN_SOFT_TIMESTAMP(7), +}; + +static const unsigned long sca3300_scan_masks[] = { + BIT(SCA3300_ACC_X) | BIT(SCA3300_ACC_Y) | BIT(SCA3300_ACC_Z) | + BIT(SCA3300_TEMP), + 0 +}; + +static const unsigned long scl3300_scan_masks[] = { + BIT(SCA3300_ACC_X) | BIT(SCA3300_ACC_Y) | BIT(SCA3300_ACC_Z) | + BIT(SCA3300_TEMP) | + BIT(SCA3300_INCLI_X) | BIT(SCA3300_INCLI_Y) | BIT(SCA3300_INCLI_Z), + 0 +}; + +struct sca3300_chip_info { + const char *name; + const unsigned long *scan_masks; + const struct iio_chan_spec *channels; + u8 num_channels; + u8 num_accel_scales; + const int (*accel_scale)[2]; + const int *accel_scale_map; + const int (*incli_scale)[2]; + const int *incli_scale_map; + u8 num_incli_scales; + u8 num_freqs; + const int *freq_table; + const int *freq_map; + const int *avail_modes_table; + u8 num_avail_modes; + u8 chip_id; + bool angle_supported; +}; + +/** + * struct sca3300_data - device data + * @spi: SPI device structure + * @lock: Data buffer lock + * @chip: Sensor chip specific information + * @buffer: Triggered buffer: + * -SCA3300: 4 channel 16-bit data + 64-bit timestamp + * -SCL3300: 7 channel 16-bit data + 64-bit timestamp + * @txbuf: Transmit buffer + * @rxbuf: Receive buffer + */ +struct sca3300_data { + struct spi_device *spi; + struct mutex lock; + const struct sca3300_chip_info *chip; + u8 buffer[SCA3300_MAX_BUFFER_SIZE] __aligned(sizeof(s64)); + u8 txbuf[4] __aligned(IIO_DMA_MINALIGN); + u8 rxbuf[4]; +}; + +static const struct sca3300_chip_info sca3300_chip_tbl[] = { + { + .name = "sca3300", + .scan_masks = sca3300_scan_masks, + .channels = sca3300_channels, + .num_channels = ARRAY_SIZE(sca3300_channels), + .num_accel_scales = ARRAY_SIZE(sca3300_accel_scale)*2, + .accel_scale = sca3300_accel_scale, + .accel_scale_map = sca3300_accel_scale_map, + .num_freqs = ARRAY_SIZE(sca3300_lp_freq), + .freq_table = sca3300_lp_freq, + .freq_map = sca3300_lp_freq_map, + .avail_modes_table = sca3300_avail_modes_map, + .num_avail_modes = 4, + .chip_id = SCA3300_WHOAMI_ID, + .angle_supported = false, + }, + { + .name = "scl3300", + .scan_masks = scl3300_scan_masks, + .channels = scl3300_channels, + .num_channels = ARRAY_SIZE(scl3300_channels), + .num_accel_scales = ARRAY_SIZE(scl3300_accel_scale)*2, + .accel_scale = scl3300_accel_scale, + .accel_scale_map = scl3300_accel_scale_map, + .incli_scale = scl3300_incli_scale, + .incli_scale_map = scl3300_incli_scale_map, + .num_incli_scales = ARRAY_SIZE(scl3300_incli_scale)*2, + .num_freqs = ARRAY_SIZE(scl3300_lp_freq), + .freq_table = scl3300_lp_freq, + .freq_map = scl3300_lp_freq_map, + .avail_modes_table = scl3300_avail_modes_map, + .num_avail_modes = 3, + .chip_id = SCL3300_WHOAMI_ID, + .angle_supported = true, + }, +}; + +DECLARE_CRC8_TABLE(sca3300_crc_table); + +static int sca3300_transfer(struct sca3300_data *sca_data, int *val) +{ + /* Consecutive requests min. 10 us delay (Datasheet section 5.1.2) */ + struct spi_delay delay = { .value = 10, .unit = SPI_DELAY_UNIT_USECS }; + int32_t ret; + int rs; + u8 crc; + struct spi_transfer xfers[2] = { + { + .tx_buf = sca_data->txbuf, + .len = ARRAY_SIZE(sca_data->txbuf), + .delay = delay, + .cs_change = 1, + }, + { + .rx_buf = sca_data->rxbuf, + .len = ARRAY_SIZE(sca_data->rxbuf), + .delay = delay, + } + }; + + /* inverted crc value as described in device data sheet */ + crc = ~crc8(sca3300_crc_table, &sca_data->txbuf[0], 3, CRC8_INIT_VALUE); + sca_data->txbuf[3] = crc; + + ret = spi_sync_transfer(sca_data->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) { + dev_err(&sca_data->spi->dev, + "transfer error, error: %d\n", ret); + return -EIO; + } + + crc = ~crc8(sca3300_crc_table, &sca_data->rxbuf[0], 3, CRC8_INIT_VALUE); + if (sca_data->rxbuf[3] != crc) { + dev_err(&sca_data->spi->dev, "CRC checksum mismatch"); + return -EIO; + } + + /* get return status */ + rs = sca_data->rxbuf[0] & SCA3300_MASK_RS_STATUS; + if (rs == SCA3300_VALUE_RS_ERROR) + ret = -EINVAL; + + *val = sign_extend32(get_unaligned_be16(&sca_data->rxbuf[1]), 15); + + return ret; +} + +static int sca3300_error_handler(struct sca3300_data *sca_data) +{ + int ret; + int val; + + mutex_lock(&sca_data->lock); + sca_data->txbuf[0] = SCA3300_REG_STATUS << 2; + ret = sca3300_transfer(sca_data, &val); + mutex_unlock(&sca_data->lock); + /* + * Return status error is cleared after reading status register once, + * expect EINVAL here. + */ + if (ret != -EINVAL) { + dev_err(&sca_data->spi->dev, + "error reading device status: %d\n", ret); + return ret; + } + + dev_err(&sca_data->spi->dev, "device status: 0x%lx\n", + val & SCA3300_STATUS_MASK); + + return 0; +} + +static int sca3300_read_reg(struct sca3300_data *sca_data, u8 reg, int *val) +{ + int ret; + + mutex_lock(&sca_data->lock); + sca_data->txbuf[0] = reg << 2; + ret = sca3300_transfer(sca_data, val); + mutex_unlock(&sca_data->lock); + if (ret != -EINVAL) + return ret; + + return sca3300_error_handler(sca_data); +} + +static int sca3300_write_reg(struct sca3300_data *sca_data, u8 reg, int val) +{ + int reg_val = 0; + int ret; + + mutex_lock(&sca_data->lock); + /* BIT(7) for write operation */ + sca_data->txbuf[0] = BIT(7) | (reg << 2); + put_unaligned_be16(val, &sca_data->txbuf[1]); + ret = sca3300_transfer(sca_data, ®_val); + mutex_unlock(&sca_data->lock); + if (ret != -EINVAL) + return ret; + + return sca3300_error_handler(sca_data); +} + +static int sca3300_set_op_mode(struct sca3300_data *sca_data, int index) +{ + if ((index < 0) || (index >= sca_data->chip->num_avail_modes)) + return -EINVAL; + + return sca3300_write_reg(sca_data, SCA3300_REG_MODE, + sca_data->chip->avail_modes_table[index]); +} + +static int sca3300_get_op_mode(struct sca3300_data *sca_data, int *index) +{ + int reg_val; + int ret; + int i; + + ret = sca3300_read_reg(sca_data, SCA3300_REG_MODE, ®_val); + if (ret) + return ret; + + for (i = 0; i < sca_data->chip->num_avail_modes; i++) { + if (sca_data->chip->avail_modes_table[i] == reg_val) + break; + } + if (i == sca_data->chip->num_avail_modes) + return -EINVAL; + + *index = i; + return 0; +} + +static int sca3300_set_frequency(struct sca3300_data *data, int val) +{ + const struct sca3300_chip_info *chip = data->chip; + unsigned int index; + int *opmode_scale; + int *new_scale; + unsigned int i; + + if (sca3300_get_op_mode(data, &index)) + return -EINVAL; + + /* + * Find a mode in which the requested sampling frequency is available + * and the scaling currently set is retained. + */ + opmode_scale = (int *)chip->accel_scale[chip->accel_scale_map[index]]; + for (i = 0; i < chip->num_avail_modes; i++) { + new_scale = (int *)chip->accel_scale[chip->accel_scale_map[i]]; + if ((val == chip->freq_table[chip->freq_map[i]]) && + (opmode_scale[1] == new_scale[1]) && + (opmode_scale[0] == new_scale[0])) + break; + } + if (i == chip->num_avail_modes) + return -EINVAL; + + return sca3300_set_op_mode(data, i); +} + +static int sca3300_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sca3300_data *data = iio_priv(indio_dev); + int index; + int i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + /* + * Letting scale take priority over sampling frequency. + * That makes sense given we can only ever end up increasing + * the sampling frequency which is unlikely to be a problem. + */ + for (i = 0; i < data->chip->num_avail_modes; i++) { + index = data->chip->accel_scale_map[i]; + if ((val == data->chip->accel_scale[index][0]) && + (val2 == data->chip->accel_scale[index][1])) + return sca3300_set_op_mode(data, i); + } + return -EINVAL; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return sca3300_set_frequency(data, val); + default: + return -EINVAL; + } +} + +static int sca3300_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sca3300_data *data = iio_priv(indio_dev); + int index; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = sca3300_read_reg(data, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = sca3300_get_op_mode(data, &index); + if (ret) + return ret; + switch (chan->type) { + case IIO_INCLI: + index = data->chip->incli_scale_map[index]; + *val = data->chip->incli_scale[index][0]; + *val2 = data->chip->incli_scale[index][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + index = data->chip->accel_scale_map[index]; + *val = data->chip->accel_scale[index][0]; + *val2 = data->chip->accel_scale[index][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = sca3300_get_op_mode(data, &index); + if (ret) + return ret; + index = data->chip->freq_map[index]; + *val = data->chip->freq_table[index]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static irqreturn_t sca3300_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct sca3300_data *data = iio_priv(indio_dev); + int bit, ret, val, i = 0; + s16 *channels = (s16 *)data->buffer; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = sca3300_read_reg(data, indio_dev->channels[bit].address, &val); + if (ret) { + dev_err_ratelimited(&data->spi->dev, + "failed to read register, error: %d\n", ret); + /* handled, but bailing out due to errors */ + goto out; + } + channels[i++] = val; + } + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/* + * sca3300_init - Device init sequence. See datasheet rev 2 section + * 4.2 Start-Up Sequence for details. + */ +static int sca3300_init(struct sca3300_data *sca_data, + struct iio_dev *indio_dev) +{ + int value = 0; + int ret; + int i; + + ret = sca3300_write_reg(sca_data, SCA3300_REG_MODE, + SCA3300_MODE_SW_RESET); + if (ret) + return ret; + + /* + * Wait 1ms after SW-reset command. + * Wait for the settling of signal paths, + * 15ms for SCA3300 and 25ms for SCL3300, + */ + usleep_range(26e3, 50e3); + + ret = sca3300_read_reg(sca_data, SCA3300_REG_WHOAMI, &value); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(sca3300_chip_tbl); i++) { + if (sca3300_chip_tbl[i].chip_id == value) + break; + } + if (i == ARRAY_SIZE(sca3300_chip_tbl)) { + dev_err(&sca_data->spi->dev, "unknown chip id %x\n", value); + return -ENODEV; + } + + sca_data->chip = &sca3300_chip_tbl[i]; + + if (sca_data->chip->angle_supported) { + ret = sca3300_write_reg(sca_data, SCL3300_REG_ANG_CTRL, + SCL3300_ANG_ENABLE); + if (ret) + return ret; + } + + return 0; +} + +static int sca3300_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct sca3300_data *data = iio_priv(indio_dev); + int value; + int ret; + + if (reg > SCA3300_REG_SELBANK) + return -EINVAL; + + if (!readval) + return sca3300_write_reg(data, reg, writeval); + + ret = sca3300_read_reg(data, reg, &value); + if (ret) + return ret; + + *readval = value; + + return 0; +} + +static int sca3300_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct sca3300_data *data = iio_priv(indio_dev); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_INCLI: + *vals = (const int *)data->chip->incli_scale; + *length = data->chip->num_incli_scales; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_ACCEL: + *vals = (const int *)data->chip->accel_scale; + *length = data->chip->num_accel_scales; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = (const int *)data->chip->freq_table; + *length = data->chip->num_freqs; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info sca3300_info = { + .read_raw = sca3300_read_raw, + .write_raw = sca3300_write_raw, + .debugfs_reg_access = &sca3300_debugfs_reg_access, + .read_avail = sca3300_read_avail, +}; + +static int sca3300_probe(struct spi_device *spi) +{ + struct sca3300_data *sca_data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*sca_data)); + if (!indio_dev) + return -ENOMEM; + + sca_data = iio_priv(indio_dev); + mutex_init(&sca_data->lock); + sca_data->spi = spi; + + crc8_populate_msb(sca3300_crc_table, SCA3300_CRC8_POLYNOMIAL); + + indio_dev->info = &sca3300_info; + + ret = sca3300_init(sca_data, indio_dev); + if (ret) { + dev_err(&spi->dev, "failed to init device, error: %d\n", ret); + return ret; + } + + indio_dev->name = sca_data->chip->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = sca_data->chip->channels; + indio_dev->num_channels = sca_data->chip->num_channels; + indio_dev->available_scan_masks = sca_data->chip->scan_masks; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + iio_pollfunc_store_time, + sca3300_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, + "iio triggered buffer setup failed, error: %d\n", ret); + return ret; + } + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) { + dev_err(&spi->dev, "iio device register failed, error: %d\n", + ret); + } + + return ret; +} + +static const struct of_device_id sca3300_dt_ids[] = { + { .compatible = "murata,sca3300"}, + { .compatible = "murata,scl3300"}, + {} +}; +MODULE_DEVICE_TABLE(of, sca3300_dt_ids); + +static struct spi_driver sca3300_driver = { + .driver = { + .name = SCA3300_ALIAS, + .of_match_table = sca3300_dt_ids, + }, + .probe = sca3300_probe, +}; +module_spi_driver(sca3300_driver); + +MODULE_AUTHOR("Tomas Melin <tomas.melin@vaisala.com>"); +MODULE_DESCRIPTION("Murata SCA3300 SPI Accelerometer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c index c32647abce20..7ca9d0d543e0 100644 --- a/drivers/iio/accel/ssp_accel_sensor.c +++ b/drivers/iio/accel/ssp_accel_sensor.c @@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev) int ret; struct iio_dev *indio_dev; struct ssp_sensor_data *spd; - struct iio_buffer *buffer; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd)); if (!indio_dev) @@ -108,21 +107,15 @@ static int ssp_accel_probe(struct platform_device *pdev) spd->type = SSP_ACCELEROMETER_SENSOR; indio_dev->name = ssp_accel_device_name; - indio_dev->dev.parent = &pdev->dev; - indio_dev->dev.of_node = pdev->dev.of_node; indio_dev->info = &ssp_accel_iio_info; - indio_dev->modes = INDIO_BUFFER_SOFTWARE; indio_dev->channels = ssp_acc_channels; indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels); indio_dev->available_scan_masks = ssp_accel_scan_mask; - buffer = devm_iio_kfifo_allocate(&pdev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - indio_dev->setup_ops = &ssp_accel_buffer_ops; + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev, + &ssp_accel_buffer_ops); + if (ret) + return ret; platform_set_drvdata(pdev, indio_dev); @@ -148,3 +141,4 @@ module_platform_driver(ssp_accel_driver); MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>"); MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_SSP_SENSORS); diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 5b13e293cade..5b0f54e33d9e 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -14,30 +14,6 @@ #include <linux/types.h> #include <linux/iio/common/st_sensors.h> -enum st_accel_type { - LSM303DLH, - LSM303DLHC, - LIS3DH, - LSM330D, - LSM330DL, - LSM330DLC, - LIS331DLH, - LSM303DL, - LSM303DLM, - LSM330, - LSM303AGR, - LIS2DH12, - LIS3L02DQ, - LNG2DM, - H3LIS331DL, - LIS331DL, - LIS3LV02DL, - LIS2DW12, - LIS3DHH, - LIS2DE12, - ST_ACCEL_MAX, -}; - #define H3LIS331DL_ACCEL_DEV_NAME "h3lis331dl_accel" #define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel" #define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" @@ -59,22 +35,13 @@ enum st_accel_type { #define LIS3DHH_ACCEL_DEV_NAME "lis3dhh" #define LIS3DE_ACCEL_DEV_NAME "lis3de" #define LIS2DE12_ACCEL_DEV_NAME "lis2de12" +#define LIS2HH12_ACCEL_DEV_NAME "lis2hh12" +#define LIS302DL_ACCEL_DEV_NAME "lis302dl" +#define SC7A20_ACCEL_DEV_NAME "sc7a20" -/** -* struct st_sensors_platform_data - default accel platform data -* @drdy_int_pin: default accel DRDY is available on INT1 pin. -*/ -static __maybe_unused const struct st_sensors_platform_data default_accel_pdata = { - .drdy_int_pin = 1, -}; - -const struct st_sensor_settings *st_accel_get_settings(const char *name); -int st_accel_common_probe(struct iio_dev *indio_dev); -void st_accel_common_remove(struct iio_dev *indio_dev); #ifdef CONFIG_IIO_BUFFER int st_accel_allocate_ring(struct iio_dev *indio_dev); -void st_accel_deallocate_ring(struct iio_dev *indio_dev); int st_accel_trig_set_state(struct iio_trigger *trig, bool state); #define ST_ACCEL_TRIGGER_SET_STATE (&st_accel_trig_set_state) #else /* CONFIG_IIO_BUFFER */ @@ -82,9 +49,6 @@ static inline int st_accel_allocate_ring(struct iio_dev *indio_dev) { return 0; } -static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev) -{ -} #define ST_ACCEL_TRIGGER_SET_STATE NULL #endif /* CONFIG_IIO_BUFFER */ diff --git a/drivers/iio/accel/st_accel_buffer.c b/drivers/iio/accel/st_accel_buffer.c index 9f2b40474b8e..b2977ae19b69 100644 --- a/drivers/iio/accel/st_accel_buffer.c +++ b/drivers/iio/accel/st_accel_buffer.c @@ -7,16 +7,10 @@ * Denis Ciocca <denis.ciocca@st.com> */ -#include <linux/module.h> #include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/stat.h> -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/delay.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> -#include <linux/iio/trigger_consumer.h> +#include <linux/iio/trigger.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/common/st_sensors.h> @@ -33,15 +27,10 @@ static int st_accel_buffer_postenable(struct iio_dev *indio_dev) { int err; - err = iio_triggered_buffer_postenable(indio_dev); + err = st_sensors_set_axis_enable(indio_dev, indio_dev->active_scan_mask[0]); if (err < 0) return err; - err = st_sensors_set_axis_enable(indio_dev, - (u8)indio_dev->active_scan_mask[0]); - if (err < 0) - goto st_accel_buffer_predisable; - err = st_sensors_set_enable(indio_dev, true); if (err < 0) goto st_accel_buffer_enable_all_axis; @@ -50,27 +39,19 @@ static int st_accel_buffer_postenable(struct iio_dev *indio_dev) st_accel_buffer_enable_all_axis: st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); -st_accel_buffer_predisable: - iio_triggered_buffer_predisable(indio_dev); return err; } static int st_accel_buffer_predisable(struct iio_dev *indio_dev) { - int err, err2; + int err; err = st_sensors_set_enable(indio_dev, false); if (err < 0) - goto st_accel_buffer_predisable; - - err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); - -st_accel_buffer_predisable: - err2 = iio_triggered_buffer_predisable(indio_dev); - if (!err) - err = err2; + return err; - return err; + return st_sensors_set_axis_enable(indio_dev, + ST_SENSORS_ENABLE_ALL_AXIS); } static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = { @@ -80,15 +61,6 @@ static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = { int st_accel_allocate_ring(struct iio_dev *indio_dev) { - return iio_triggered_buffer_setup(indio_dev, NULL, - &st_sensors_trigger_handler, &st_accel_buffer_setup_ops); + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + NULL, &st_sensors_trigger_handler, &st_accel_buffer_setup_ops); } - -void st_accel_deallocate_ring(struct iio_dev *indio_dev) -{ - iio_triggered_buffer_cleanup(indio_dev); -} - -MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics accelerometers buffer"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 7320275c7e56..c8c8eb15c34e 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -9,17 +9,13 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> #include <linux/slab.h> #include <linux/acpi.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/irq.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/trigger.h> -#include <linux/iio/buffer.h> #include <linux/iio/common/st_sensors.h> #include "st_accel.h" @@ -41,51 +37,74 @@ #define ST_ACCEL_FS_AVL_200G 200 #define ST_ACCEL_FS_AVL_400G 400 +static const struct iio_mount_matrix * +st_accel_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct st_sensor_data *adata = iio_priv(indio_dev); + + return &adata->mount_matrix; +} + +static const struct iio_chan_spec_ext_info st_accel_mount_matrix_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_accel_get_mount_matrix), + { } +}; + static const struct iio_chan_spec st_accel_8bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8, - ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8, - ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8, - ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1), + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1, + st_accel_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_accel_12bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16, - ST_ACCEL_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16, - ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 12, 16, - ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR, + st_accel_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_accel_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_ACCEL_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR, + st_accel_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -425,6 +444,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LIS331DL_ACCEL_DEV_NAME, + [1] = LIS302DL_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_8bit_channels, .odr = { @@ -904,6 +924,258 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = true, .bootime = 2, }, + { + .wai = 0x41, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LIS2HH12_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr = 0x20, + .mask = 0x70, + .odr_avl = { + { .hz = 10, .value = 0x01, }, + { .hz = 50, .value = 0x02, }, + { .hz = 100, .value = 0x03, }, + { .hz = 200, .value = 0x04, }, + { .hz = 400, .value = 0x05, }, + { .hz = 800, .value = 0x06, }, + }, + }, + .pw = { + .addr = 0x20, + .mask = 0x70, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = 0x23, + .mask = 0x30, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = 0x00, + .gain = IIO_G_TO_M_S_2(61), + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = 0x02, + .gain = IIO_G_TO_M_S_2(122), + }, + [2] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = 0x03, + .gain = IIO_G_TO_M_S_2(244), + }, + }, + }, + .bdu = { + .addr = 0x20, + .mask = 0x08, + }, + .drdy_irq = { + .int1 = { + .addr = 0x22, + .mask = 0x01, + }, + .int2 = { + .addr = 0x25, + .mask = 0x01, + }, + .addr_ihl = 0x24, + .mask_ihl = 0x02, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, + }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, + { + .wai = 0x49, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LSM9DS0_IMU_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr = 0x20, + .mask = GENMASK(7, 4), + .odr_avl = { + { 3, 0x01, }, + { 6, 0x02, }, + { 12, 0x03, }, + { 25, 0x04, }, + { 50, 0x05, }, + { 100, 0x06, }, + { 200, 0x07, }, + { 400, 0x08, }, + { 800, 0x09, }, + { 1600, 0x0a, }, + }, + }, + .pw = { + .addr = 0x20, + .mask = GENMASK(7, 4), + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = 0x21, + .mask = GENMASK(5, 3), + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = 0x00, + .gain = IIO_G_TO_M_S_2(61), + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = 0x01, + .gain = IIO_G_TO_M_S_2(122), + }, + [2] = { + .num = ST_ACCEL_FS_AVL_6G, + .value = 0x02, + .gain = IIO_G_TO_M_S_2(183), + }, + [3] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = 0x03, + .gain = IIO_G_TO_M_S_2(244), + }, + [4] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = 0x04, + .gain = IIO_G_TO_M_S_2(732), + }, + }, + }, + .bdu = { + .addr = 0x20, + .mask = BIT(3), + }, + .drdy_irq = { + .int1 = { + .addr = 0x22, + .mask = BIT(2), + }, + .int2 = { + .addr = 0x23, + .mask = BIT(3), + }, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = GENMASK(2, 0), + }, + }, + .sim = { + .addr = 0x21, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, + { + /* + * Not an ST part. Register-compatible with the LIS2DH, even + * though the WAI value is different. + */ + .wai = 0x11, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = SC7A20_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = 0x20, + .mask = 0xf0, + .odr_avl = { + { .hz = 1, .value = 0x01, }, + { .hz = 10, .value = 0x02, }, + { .hz = 25, .value = 0x03, }, + { .hz = 50, .value = 0x04, }, + { .hz = 100, .value = 0x05, }, + { .hz = 200, .value = 0x06, }, + { .hz = 400, .value = 0x07, }, + { .hz = 1600, .value = 0x08, }, + }, + }, + .pw = { + .addr = 0x20, + .mask = 0xf0, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = 0x23, + .mask = 0x30, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = 0x00, + .gain = IIO_G_TO_M_S_2(1000), + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = 0x01, + .gain = IIO_G_TO_M_S_2(2000), + }, + [2] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = 0x02, + .gain = IIO_G_TO_M_S_2(4000), + }, + [3] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = 0x03, + .gain = IIO_G_TO_M_S_2(12000), + }, + }, + }, + .bdu = { + .addr = 0x23, + .mask = 0x80, + }, + .drdy_irq = { + .int1 = { + .addr = 0x22, + .mask = 0x10, + }, + .addr_ihl = 0x25, + .mask_ihl = 0x02, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, + }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, +}; + +/* Default accel DRDY is available on INT1 pin */ +static const struct st_sensors_platform_data default_accel_pdata = { + .drdy_int_pin = 1, }; static int st_accel_read_raw(struct iio_dev *indio_dev, @@ -938,28 +1210,21 @@ read_error: static int st_accel_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - int err; - switch (mask) { case IIO_CHAN_INFO_SCALE: { int gain; gain = val * 1000000 + val2; - err = st_sensors_set_fullscale_by_gain(indio_dev, gain); - break; + return st_sensors_set_fullscale_by_gain(indio_dev, gain); } case IIO_CHAN_INFO_SAMP_FREQ: if (val2) return -EINVAL; - mutex_lock(&indio_dev->mlock); - err = st_sensors_set_odr(indio_dev, val); - mutex_unlock(&indio_dev->mlock); - return err; + + return st_sensors_set_odr(indio_dev, val); default: return -EINVAL; } - - return err; } static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); @@ -993,25 +1258,10 @@ static const struct iio_trigger_ops st_accel_trigger_ops = { #endif #ifdef CONFIG_ACPI -static const struct iio_mount_matrix * -get_mount_matrix(const struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct st_sensor_data *adata = iio_priv(indio_dev); - - return adata->mount_matrix; -} - -static const struct iio_chan_spec_ext_info mount_matrix_ext_info[] = { - IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, get_mount_matrix), - { }, -}; - /* Read ST-specific _ONT orientation data from ACPI and generate an * appropriate mount matrix. */ -static int apply_acpi_orientation(struct iio_dev *indio_dev, - struct iio_chan_spec *channels) +static int apply_acpi_orientation(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -1037,7 +1287,7 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev, }; - adev = ACPI_COMPANION(adata->dev); + adev = ACPI_COMPANION(indio_dev->dev.parent); if (!adev) return 0; @@ -1100,14 +1350,6 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev, } /* Convert our integer matrix to a string-based iio_mount_matrix */ - adata->mount_matrix = devm_kmalloc(&indio_dev->dev, - sizeof(*adata->mount_matrix), - GFP_KERNEL); - if (!adata->mount_matrix) { - ret = -ENOMEM; - goto out; - } - for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { int matrix_val = final_ont[i][j]; @@ -1126,26 +1368,25 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev, default: goto out; } - adata->mount_matrix->rotation[i * 3 + j] = str_value; + adata->mount_matrix.rotation[i * 3 + j] = str_value; } } - /* Expose the mount matrix via ext_info */ - for (i = 0; i < indio_dev->num_channels; i++) - channels[i].ext_info = mount_matrix_ext_info; - ret = 0; dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n"); out: kfree(buffer.pointer); + if (ret) + dev_dbg(&indio_dev->dev, + "failed to apply ACPI orientation data: %d\n", ret); + return ret; } #else /* !CONFIG_ACPI */ -static int apply_acpi_orientation(struct iio_dev *indio_dev, - struct iio_chan_spec *channels) +static int apply_acpi_orientation(struct iio_dev *indio_dev) { - return 0; + return -EINVAL; } #endif @@ -1165,47 +1406,38 @@ const struct st_sensor_settings *st_accel_get_settings(const char *name) return &st_accel_sensors_settings[index]; } -EXPORT_SYMBOL(st_accel_get_settings); +EXPORT_SYMBOL_NS(st_accel_get_settings, IIO_ST_SENSORS); int st_accel_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); - struct st_sensors_platform_data *pdata = - (struct st_sensors_platform_data *)adata->dev->platform_data; - struct iio_chan_spec *channels; - size_t channels_size; + struct device *parent = indio_dev->dev.parent; + struct st_sensors_platform_data *pdata = dev_get_platdata(parent); int err; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &accel_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_accel_power_off; + return err; adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS; + indio_dev->channels = adata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; - channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec); - channels = devm_kmemdup(&indio_dev->dev, - adata->sensor_settings->ch, - channels_size, GFP_KERNEL); - if (!channels) { - err = -ENOMEM; - goto st_accel_power_off; + /* + * First try specific ACPI methods to retrieve orientation then try the + * generic function. + */ + err = apply_acpi_orientation(indio_dev); + if (err) { + err = iio_read_mount_matrix(parent, &adata->mount_matrix); + if (err) + return err; } - if (apply_acpi_orientation(indio_dev, channels)) - dev_warn(&indio_dev->dev, - "failed to apply ACPI orientation data: %d\n", err); - - indio_dev->channels = channels; - adata->current_fullscale = (struct st_sensor_fullscale_avl *) - &adata->sensor_settings->fs.fs_avl[0]; + adata->current_fullscale = &adata->sensor_settings->fs.fs_avl[0]; adata->odr = adata->sensor_settings->odr.odr_avl[0].hz; if (!pdata) @@ -1213,54 +1445,24 @@ int st_accel_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_accel_power_off; + return err; err = st_accel_allocate_ring(indio_dev); if (err < 0) - goto st_accel_power_off; + return err; if (adata->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, ST_ACCEL_TRIGGER_OPS); if (err < 0) - goto st_accel_probe_trigger_error; + return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_accel_device_register_error; - - dev_info(&indio_dev->dev, "registered accelerometer %s\n", - indio_dev->name); - - return 0; - -st_accel_device_register_error: - if (adata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); -st_accel_probe_trigger_error: - st_accel_deallocate_ring(indio_dev); -st_accel_power_off: - st_sensors_power_disable(indio_dev); - - return err; -} -EXPORT_SYMBOL(st_accel_common_probe); - -void st_accel_common_remove(struct iio_dev *indio_dev) -{ - struct st_sensor_data *adata = iio_priv(indio_dev); - - st_sensors_power_disable(indio_dev); - - iio_device_unregister(indio_dev); - if (adata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - - st_accel_deallocate_ring(indio_dev); + return devm_iio_device_register(parent, indio_dev); } -EXPORT_SYMBOL(st_accel_common_remove); +EXPORT_SYMBOL_NS(st_accel_common_probe, IIO_ST_SENSORS); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 849cf74153c4..45ee0ddc133c 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -9,11 +9,10 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/slab.h> +#include <linux/mod_devicetable.h> #include <linux/acpi.h> #include <linux/i2c.h> #include <linux/iio/iio.h> -#include <linux/property.h> #include <linux/iio/common/st_sensors_i2c.h> #include "st_accel.h" @@ -104,6 +103,18 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis2de12", .data = LIS2DE12_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis2hh12", + .data = LIS2HH12_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lis302dl", + .data = LIS302DL_ACCEL_DEV_NAME, + }, + { + .compatible = "silan,sc7a20", + .data = SC7A20_ACCEL_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -138,6 +149,9 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS2DW12_ACCEL_DEV_NAME }, { LIS3DE_ACCEL_DEV_NAME }, { LIS2DE12_ACCEL_DEV_NAME }, + { LIS2HH12_ACCEL_DEV_NAME }, + { LIS302DL_ACCEL_DEV_NAME }, + { SC7A20_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); @@ -147,12 +161,9 @@ static int st_accel_i2c_probe(struct i2c_client *client) const struct st_sensor_settings *settings; struct st_sensor_data *adata; struct iio_dev *indio_dev; - const char *match; int ret; - match = device_get_match_data(&client->dev); - if (match) - strlcpy(client->name, match, sizeof(client->name)); + st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name)); settings = st_accel_get_settings(client->name); if (!settings) { @@ -172,18 +183,11 @@ static int st_accel_i2c_probe(struct i2c_client *client) if (ret < 0) return ret; - ret = st_accel_common_probe(indio_dev); - if (ret < 0) + ret = st_sensors_power_enable(indio_dev); + if (ret) return ret; - return 0; -} - -static int st_accel_i2c_remove(struct i2c_client *client) -{ - st_accel_common_remove(i2c_get_clientdata(client)); - - return 0; + return st_accel_common_probe(indio_dev); } static struct i2c_driver st_accel_driver = { @@ -193,7 +197,6 @@ static struct i2c_driver st_accel_driver = { .acpi_match_table = ACPI_PTR(st_accel_acpi_match), }, .probe_new = st_accel_i2c_probe, - .remove = st_accel_i2c_remove, .id_table = st_accel_id_table, }; module_i2c_driver(st_accel_driver); @@ -201,3 +204,4 @@ module_i2c_driver(st_accel_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 568ff1bae0ee..6c0917750288 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/slab.h> +#include <linux/mod_devicetable.h> #include <linux/spi/spi.h> #include <linux/iio/iio.h> @@ -92,6 +92,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis3de", .data = LIS3DE_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis302dl", + .data = LIS302DL_ACCEL_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -123,18 +127,11 @@ static int st_accel_spi_probe(struct spi_device *spi) if (err < 0) return err; - err = st_accel_common_probe(indio_dev); - if (err < 0) + err = st_sensors_power_enable(indio_dev); + if (err) return err; - return 0; -} - -static int st_accel_spi_remove(struct spi_device *spi) -{ - st_accel_common_remove(spi_get_drvdata(spi)); - - return 0; + return st_accel_common_probe(indio_dev); } static const struct spi_device_id st_accel_id_table[] = { @@ -154,6 +151,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS2DW12_ACCEL_DEV_NAME }, { LIS3DHH_ACCEL_DEV_NAME }, { LIS3DE_ACCEL_DEV_NAME }, + { LIS302DL_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); @@ -164,7 +162,6 @@ static struct spi_driver st_accel_driver = { .of_match_table = st_accel_of_match, }, .probe = st_accel_spi_probe, - .remove = st_accel_spi_remove, .id_table = st_accel_id_table, }; module_spi_driver(st_accel_driver); @@ -172,3 +169,4 @@ module_spi_driver(st_accel_driver); MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ST_SENSORS); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 58c160ccdee7..7b1d6fb692b3 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK8312 3-Axis Accelerometer * * Copyright (c) 2015, Intel Corporation. @@ -7,7 +7,6 @@ * IIO driver for STK8312; 7-bit I2C address: 0x3D. */ -#include <linux/acpi.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -103,7 +102,11 @@ struct stk8312_data { u8 mode; struct iio_trigger *dready_trig; bool dready_trigger_on; - s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 64-bit timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + s8 chans[3]; + s64 timestamp __aligned(8); + } scan; }; static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL); @@ -352,7 +355,7 @@ static int stk8312_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return ret; } - *val = sign_extend32(ret, 7); + *val = sign_extend32(ret, chan->scan_type.realbits - 1); ret = stk8312_set_mode(data, data->mode & (~STK8312_MODE_ACTIVE)); mutex_unlock(&data->lock); @@ -438,7 +441,7 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p) ret = i2c_smbus_read_i2c_block_data(data->client, STK8312_REG_XOUT, STK8312_ALL_CHANNEL_SIZE, - data->buffer); + data->scan.chans); if (ret < STK8312_ALL_CHANNEL_SIZE) { dev_err(&data->client->dev, "register read failed\n"); mutex_unlock(&data->lock); @@ -452,12 +455,12 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p) mutex_unlock(&data->lock); goto err; } - data->buffer[i++] = ret; + data->scan.chans[i++] = ret; } } mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -492,8 +495,6 @@ static int stk8312_buffer_postdisable(struct iio_dev *indio_dev) static const struct iio_buffer_setup_ops stk8312_buffer_setup_ops = { .preenable = stk8312_buffer_preenable, - .postenable = iio_triggered_buffer_postenable, - .predisable = iio_triggered_buffer_predisable, .postdisable = stk8312_buffer_postdisable, }; @@ -515,7 +516,6 @@ static int stk8312_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); mutex_init(&data->lock); - indio_dev->dev.parent = &client->dev; indio_dev->info = &stk8312_info; indio_dev->name = STK8312_DRIVER_NAME; indio_dev->modes = INDIO_DIRECT_MODE; @@ -555,13 +555,12 @@ static int stk8312_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; goto err_power_off; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &stk8312_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); @@ -598,7 +597,7 @@ err_power_off: return ret; } -static int stk8312_remove(struct i2c_client *client) +static void stk8312_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct stk8312_data *data = iio_priv(indio_dev); @@ -609,10 +608,9 @@ static int stk8312_remove(struct i2c_client *client) if (data->dready_trig) iio_trigger_unregister(data->dready_trig); - return stk8312_set_mode(data, STK8312_MODE_STANDBY); + stk8312_set_mode(data, STK8312_MODE_STANDBY); } -#ifdef CONFIG_PM_SLEEP static int stk8312_suspend(struct device *dev) { struct stk8312_data *data; @@ -631,31 +629,21 @@ static int stk8312_resume(struct device *dev) return stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE); } -static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume); - -#define STK8312_PM_OPS (&stk8312_pm_ops) -#else -#define STK8312_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, + stk8312_resume); static const struct i2c_device_id stk8312_i2c_id[] = { - {"STK8312", 0}, + /* Deprecated in favour of lowercase form */ + { "STK8312", 0 }, + { "stk8312", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id); -static const struct acpi_device_id stk8312_acpi_id[] = { - {"STK8312", 0}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id); - static struct i2c_driver stk8312_driver = { .driver = { .name = STK8312_DRIVER_NAME, - .pm = STK8312_PM_OPS, - .acpi_match_table = ACPI_PTR(stk8312_acpi_id), + .pm = pm_sleep_ptr(&stk8312_pm_ops), }, .probe = stk8312_probe, .remove = stk8312_remove, diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index c70ddec29eb4..2f5e4ab2a6e7 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK8BA50 3-Axis Accelerometer * * Copyright (c) 2015, Intel Corporation. @@ -91,12 +91,11 @@ struct stk8ba50_data { u8 sample_rate_idx; struct iio_trigger *dready_trig; bool dready_trigger_on; - /* - * 3 x 16-bit channels (10-bit data, 6-bit padding) + - * 1 x 16 padding + - * 4 x 16 64-bit timestamp - */ - s16 buffer[8]; + /* Ensure timestamp is naturally aligned */ + struct { + s16 chans[3]; + s64 timetamp __aligned(8); + } scan; }; #define STK8BA50_ACCEL_CHANNEL(index, reg, axis) { \ @@ -228,7 +227,8 @@ static int stk8ba50_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return -EINVAL; } - *val = sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND); mutex_unlock(&data->lock); return IIO_VAL_INT; @@ -324,7 +324,7 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p) ret = i2c_smbus_read_i2c_block_data(data->client, STK8BA50_REG_XOUT, STK8BA50_ALL_CHANNEL_SIZE, - (u8 *)data->buffer); + (u8 *)data->scan.chans); if (ret < STK8BA50_ALL_CHANNEL_SIZE) { dev_err(&data->client->dev, "register read failed\n"); goto err; @@ -337,10 +337,10 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p) if (ret < 0) goto err; - data->buffer[i++] = ret; + data->scan.chans[i++] = ret; } } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: mutex_unlock(&data->lock); @@ -376,8 +376,6 @@ static int stk8ba50_buffer_postdisable(struct iio_dev *indio_dev) static const struct iio_buffer_setup_ops stk8ba50_buffer_setup_ops = { .preenable = stk8ba50_buffer_preenable, - .postenable = iio_triggered_buffer_postenable, - .predisable = iio_triggered_buffer_predisable, .postdisable = stk8ba50_buffer_postdisable, }; @@ -399,7 +397,6 @@ static int stk8ba50_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); mutex_init(&data->lock); - indio_dev->dev.parent = &client->dev; indio_dev->info = &stk8ba50_info; indio_dev->name = STK8BA50_DRIVER_NAME; indio_dev->modes = INDIO_DIRECT_MODE; @@ -451,13 +448,12 @@ static int stk8ba50_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; goto err_power_off; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &stk8ba50_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); @@ -494,7 +490,7 @@ err_power_off: return ret; } -static int stk8ba50_remove(struct i2c_client *client) +static void stk8ba50_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct stk8ba50_data *data = iio_priv(indio_dev); @@ -505,10 +501,9 @@ static int stk8ba50_remove(struct i2c_client *client) if (data->dready_trig) iio_trigger_unregister(data->dready_trig); - return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND); + stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND); } -#ifdef CONFIG_PM_SLEEP static int stk8ba50_suspend(struct device *dev) { struct stk8ba50_data *data; @@ -527,12 +522,8 @@ static int stk8ba50_resume(struct device *dev) return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL); } -static SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume); - -#define STK8BA50_PM_OPS (&stk8ba50_pm_ops) -#else -#define STK8BA50_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, + stk8ba50_resume); static const struct i2c_device_id stk8ba50_i2c_id[] = { {"stk8ba50", 0}, @@ -550,7 +541,7 @@ MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id); static struct i2c_driver stk8ba50_driver = { .driver = { .name = "stk8ba50", - .pm = STK8BA50_PM_OPS, + .pm = pm_sleep_ptr(&stk8ba50_pm_ops), .acpi_match_table = ACPI_PTR(stk8ba50_acpi_id), }, .probe = stk8ba50_probe, |