diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 66e81c48ee21..6ba34c0d9789 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -260,6 +260,15 @@ Description: Has all of the equivalent parameters as per voltageY. Units after application of scale and offset are m/s^2. +What: /sys/bus/iio/devices/iio:deviceX/in_accel_linear_x_raw +What: /sys/bus/iio/devices/iio:deviceX/in_accel_linear_y_raw +What: /sys/bus/iio/devices/iio:deviceX/in_accel_linear_z_raw +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + As per in_accel_X_raw attributes, but minus the + acceleration due to gravity. + What: /sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw What: /sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw What: /sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw @@ -2137,3 +2146,19 @@ Contact: linux-iio@vger.kernel.org Description: Lists all available time values between upper peak to lower peak. Units in seconds. + +What: /sys/bus/iio/devices/iio:deviceX/in_rot_yaw_raw +What: /sys/bus/iio/devices/iio:deviceX/in_rot_pitch_raw +What: /sys/bus/iio/devices/iio:deviceX/in_rot_roll_raw +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) euler angles readings. Units after + application of scale are deg. + +What: /sys/bus/iio/devices/iio:deviceX/serialnumber +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + An example format is 16-bytes, 2-digits-per-byte, HEX-string + representing the sensor unique ID number. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-bno055 b/Documentation/ABI/testing/sysfs-bus-iio-bno055 new file mode 100644 index 000000000000..f32b1644e986 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-bno055 @@ -0,0 +1,81 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_accel_raw_range +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) range for acceleration readings. Unit after + application of scale is m/s^2. Note that this doesn't affects + the scale (which should be used when changing the maximum and + minimum readable value affects also the reading scaling factor). + +What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Range for angular velocity readings in radians per second. Note + that this does not affects the scale (which should be used when + changing the maximum and minimum readable value affects also the + reading scaling factor). + +What: /sys/bus/iio/devices/iio:deviceX/in_accel_raw_range_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + List of allowed values for in_accel_raw_range attribute + +What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + List of allowed values for in_anglvel_raw_range attribute + +What: /sys/bus/iio/devices/iio:deviceX/in_magn_calibration_fast_enable +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Can be 1 or 0. Enables/disables the "Fast Magnetometer + Calibration" HW function. + +What: /sys/bus/iio/devices/iio:deviceX/fusion_enable +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a. + NDOF) HW function. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_data +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the binary calibration data blob for the IMU sensors. + +What: /sys/bus/iio/devices/iio:deviceX/in_accel_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the autocalibration status for the accelerometer sensor. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. + +What: /sys/bus/iio/devices/iio:deviceX/in_gyro_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the autocalibration status for the gyroscope sensor. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. + +What: /sys/bus/iio/devices/iio:deviceX/in_magn_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the autocalibration status for the magnetometer sensor. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. + +What: /sys/bus/iio/devices/iio:deviceX/sys_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the status for the IMU overall autocalibration. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml index 05fa7af409cc..185b68ffb536 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml @@ -4,20 +4,24 @@ $id: http://devicetree.org/schemas/iio/accel/adi,adxl313.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices ADXL313 3-Axis Digital Accelerometer +title: Analog Devices ADXL312, ADXL313, and ADXL314 3-Axis Digital Accelerometers maintainers: - Lucas Stankus description: | - Analog Devices ADXL313 3-Axis Digital Accelerometer that supports - both I2C & SPI interfaces. + Analog Devices ADXL312, ADXL313, and ADXL314 3-Axis Digital Accelerometer that + support both I2C & SPI interfaces. + https://www.analog.com/en/products/adxl312.html https://www.analog.com/en/products/adxl313.html + https://www.analog.com/en/products/adxl314.html properties: compatible: enum: + - adi,adxl312 - adi,adxl313 + - adi,adxl314 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml index 40b0a887db57..07f9d1c09c7d 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml @@ -36,6 +36,10 @@ properties: description: | The regulator supply for ADC reference voltage. + adi,range-double: + description: Sets the analog input range from 0 to 2xVREF. + type: boolean + '#address-cells': const: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml index c1772b568cd1..875f394576c2 100644 --- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml +++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml @@ -13,10 +13,14 @@ description: | 16bit ADC supporting up to 16 single ended or 8 differential inputs. I2C interface. + https://www.analog.com/media/en/technical-documentation/data-sheets/2497fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/2499fe.pdf + properties: compatible: - const: - lltc,ltc2497 + enum: + - lltc,ltc2497 + - lltc,ltc2499 reg: true vref-supply: true diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml index 067a7bbadab8..2c93fb41f172 100644 --- a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml +++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml @@ -36,6 +36,13 @@ properties: description: IRQ line of the ADC maxItems: 1 + microchip,data-ready-hiz: + description: + Data Ready Pin Inactive State Control + true = The DR pin state is high-impedance + false = The DR pin state is logic high + type: boolean + microchip,device-addr: description: Device address when multiple MCP3911 chips are present on the same SPI bus. $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml new file mode 100644 index 000000000000..e0d06db161a9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/imu/bosch,bno055.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bosch BNO055 + +maintainers: + - Andrea Merello + +description: | + Inertial Measurement Unit with Accelerometer, Gyroscope, Magnetometer and + internal MCU for sensor fusion + https://www.bosch-sensortec.com/products/smart-sensors/bno055/ + +properties: + compatible: + enum: + - bosch,bno055 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + +additionalProperties: false + +examples: + - | + #include + serial { + imu { + compatible = "bosch,bno055"; + reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>; + clocks = <&imu_clk>; + }; + }; + + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + imu@28 { + compatible = "bosch,bno055"; + reg = <0x28>; + reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>; + clocks = <&imu_clk>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml index 0ceb29fb01b7..fe1e02e5d7b3 100644 --- a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml +++ b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml @@ -35,6 +35,9 @@ properties: - items: - const: st,asm330lhhx - const: st,lsm6dsr + - items: + - const: st,lsm6dstx + - const: st,lsm6dst reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml index 49257f9251e8..72cd2c2d3f17 100644 --- a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/pressure/bmp085.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: BMP085/BMP180/BMP280/BME280 pressure iio sensors +title: BMP085/BMP180/BMP280/BME280/BMP380 pressure iio sensors maintainers: - Andreas Klinger @@ -16,6 +16,7 @@ description: | https://www.bosch-sensortec.com/bst/products/all_products/bmp180 https://www.bosch-sensortec.com/bst/products/all_products/bmp280 https://www.bosch-sensortec.com/bst/products/all_products/bme280 + https://www.bosch-sensortec.com/bst/products/all_products/bmp380 properties: compatible: @@ -24,6 +25,7 @@ properties: - bosch,bmp180 - bosch,bmp280 - bosch,bme280 + - bosch,bmp380 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml index fcb2902683c7..250439b13152 100644 --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml @@ -73,6 +73,7 @@ properties: - description: STMicroelectronics Pressure Sensors enum: - st,lps001wp-press + - st,lps22df - st,lps22hb-press - st,lps22hh - st,lps25h-press @@ -141,6 +142,7 @@ allOf: - st,lis2mdl - st,lis3l02dq - st,lis3lv02dl-accel + - st,lps22df - st,lps22hb-press - st,lps22hh - st,lps25h-press diff --git a/Documentation/iio/bno055.rst b/Documentation/iio/bno055.rst new file mode 100644 index 000000000000..9a489a79d8f5 --- /dev/null +++ b/Documentation/iio/bno055.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================== +BNO055 driver +============================== + +1. Overview +=========== + +This driver supports Bosch BNO055 IMUs (on both serial and I2C busses). + +Accelerometer, magnetometer and gyroscope measures are always provided. +When "fusion_enable" sysfs attribute is set to 1, orientation (both Euler +angles and quaternion), linear velocity and gravity vector are also +provided, but some sensor settings (e.g. low pass filtering and range) +became locked (the IMU firmware controls them). + +This driver supports also IIO buffers. + +2. Calibration +============== + +The IMU continuously performs an autocalibration procedure if (and only if) +operating in fusion mode. The magnetometer autocalibration can however be +disabled writing 0 in the sysfs in_magn_calibration_fast_enable attribute. + +The driver provides access to autocalibration flags (i.e. you can known if +the IMU has successfully autocalibrated) and to the calibration data blob. + +The user can save this blob in a firmware file (i.e. in /lib/firmware) that +the driver looks for at probe time. If found, then the IMU is initialized +with this calibration data. This saves the user from performing the +calibration procedure every time (which consist of moving the IMU in +various way). + +The driver looks for calibration data file using two different names: first +a file whose name is suffixed with the IMU unique ID (exposed in sysfs as +serial_number) is searched for; this is useful when there is more than one +IMU instance. If this file is not found, then a "generic" calibration file +is searched for (which can be used when only one IMU is present, without +struggling with fancy names, that change on each device). + +Valid calibration file names would be e.g. + bno055-caldata-0e7c26a33541515120204a35342b04ff.dat + bno055-caldata.dat + +In non-fusion mode the IIO 'offset' attributes provide access to the +offsets from calibration data (if any), so that the user can apply them to +the accel, angvel and magn IIO attributes. In fusion mode they are not +needed (the IMU firmware internally applies those corrections) and they +read as zero. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 58b7a4ebac51..1b7292c58cd0 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -10,3 +10,5 @@ Industrial I/O iio_configfs ep93xx_adc + + bno055 diff --git a/MAINTAINERS b/MAINTAINERS index ad28606cec5b..db50c8234da6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1319,7 +1319,8 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 F: Documentation/devicetree/bindings/iio/*/adi,* -F: Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml +F: Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml +F: Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml F: drivers/iio/*/ad* F: drivers/iio/adc/ltc249* F: drivers/iio/amplifiers/hmc425a.c diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h index 4415f2fc07e1..72f624af4686 100644 --- a/drivers/iio/accel/adxl313.h +++ b/drivers/iio/accel/adxl313.h @@ -8,6 +8,8 @@ #ifndef _ADXL313_H_ #define _ADXL313_H_ +#include + /* ADXL313 register definitions */ #define ADXL313_REG_DEVID0 0x00 #define ADXL313_REG_DEVID1 0x01 @@ -26,6 +28,7 @@ #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 @@ -37,18 +40,46 @@ #define ADXL313_MEASUREMENT_MODE BIT(3) #define ADXL313_RANGE_MSK GENMASK(1, 0) -#define ADXL313_RANGE_4G 3 +#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 char *name, + 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 index afeef779e1d0..4de0a41bd679 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -8,12 +8,18 @@ */ #include -#include #include #include #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), @@ -22,12 +28,109 @@ static const struct regmap_range adxl313_readable_reg_range[] = { 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)), @@ -37,17 +140,23 @@ static const struct regmap_range adxl313_writable_reg_range[] = { 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); -struct adxl313_data { - struct regmap *regmap; - struct mutex lock; /* lock to protect transf_buf */ - __le16 transf_buf __aligned(IIO_DMA_MINALIGN); +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 }, @@ -156,12 +265,10 @@ static int adxl313_read_raw(struct iio_dev *indio_dev, *val = sign_extend32(ret, chan->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - /* - * Scale for any g range is given in datasheet as - * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2 - */ *val = 0; - *val2 = 9576806; + + *val2 = data->chip_info->scale_factor; + return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_CALIBBIAS: ret = regmap_read(data->regmap, @@ -170,7 +277,7 @@ static int adxl313_read_raw(struct iio_dev *indio_dev, return ret; /* - * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * 8-bit resolution at minimum range, that is 4x accel data scale * factor at full resolution */ *val = sign_extend32(regval, 7) * 4; @@ -198,7 +305,7 @@ static int adxl313_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: /* - * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * 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) @@ -223,14 +330,18 @@ static const struct iio_info adxl313_info = { static int adxl313_setup(struct device *dev, struct adxl313_data *data, int (*setup)(struct device *, struct regmap *)) { - unsigned int regval; int ret; - /* Ensures the device is in a consistent state after start up */ - ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET, - ADXL313_SOFT_RESET); - if (ret) - return 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); @@ -238,46 +349,25 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data, return ret; } - ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + ret = data->chip_info->check_id(dev, data); if (ret) return ret; - if (regval != ADXL313_DEVID0) { - dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval); - return -ENODEV; + /* 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; } - ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, ®val); - if (ret) - return ret; - - if (regval != ADXL313_DEVID1) { - dev_err(dev, "Invalid mems ID: 0x%02x\n", regval); - return -ENODEV; - } - - ret = regmap_read(data->regmap, ADXL313_REG_PARTID, ®val); - if (ret) - return ret; - - if (regval != ADXL313_PARTID) { - dev_err(dev, "Invalid device ID: 0x%02x\n", regval); - return -ENODEV; - } - - /* Sets the range to +/- 4g */ - ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, - ADXL313_RANGE_MSK, - FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_4G)); - 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, @@ -288,7 +378,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data, * adxl313_core_probe() - probe and setup for adxl313 accelerometer * @dev: Driver model representation of the device * @regmap: Register map of the device - * @name: Device name buffer reference + * @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 * @@ -296,7 +386,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data, */ int adxl313_core_probe(struct device *dev, struct regmap *regmap, - const char *name, + const struct adxl313_chip_info *chip_info, int (*setup)(struct device *, struct regmap *)) { struct adxl313_data *data; @@ -309,9 +399,11 @@ int adxl313_core_probe(struct device *dev, data = iio_priv(indio_dev); data->regmap = regmap; + data->chip_info = chip_info; + mutex_init(&data->lock); - indio_dev->name = name; + indio_dev->name = chip_info->name; indio_dev->info = &adxl313_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl313_channels; diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c index c329765dbf60..99cc7fc29488 100644 --- a/drivers/iio/accel/adxl313_i2c.c +++ b/drivers/iio/accel/adxl313_i2c.c @@ -14,42 +14,72 @@ #include "adxl313.h" -static const struct regmap_config adxl313_i2c_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .rd_table = &adxl313_readable_regs_table, - .wr_table = &adxl313_writable_regs_table, - .max_register = 0x39, +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 int adxl313_i2c_probe(struct i2c_client *client) -{ - struct regmap *regmap; - - regmap = devm_regmap_init_i2c(client, &adxl313_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 adxl313_core_probe(&client->dev, regmap, client->name, NULL); -} - static const struct i2c_device_id adxl313_i2c_id[] = { - { "adxl313" }, + { .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,adxl313" }, + { .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", diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c index a3c6d553462d..b7cc15678a2b 100644 --- a/drivers/iio/accel/adxl313_spi.c +++ b/drivers/iio/accel/adxl313_spi.c @@ -11,17 +11,38 @@ #include #include #include +#include #include "adxl313.h" -static const struct regmap_config adxl313_spi_regmap_config = { - .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), +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) @@ -42,7 +63,7 @@ static int adxl313_spi_setup(struct device *dev, struct regmap *regmap) static int adxl313_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); + const struct adxl313_chip_info *chip_data; struct regmap *regmap; int ret; @@ -51,26 +72,40 @@ static int adxl313_spi_probe(struct spi_device *spi) if (ret) return ret; - regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config); + /* + * 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, id->name, - &adxl313_spi_setup); + return adxl313_core_probe(&spi->dev, regmap, + chip_data, &adxl313_spi_setup); } static const struct spi_device_id adxl313_spi_id[] = { - { "adxl313" }, + { .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,adxl313" }, + { .compatible = "adi,adxl312", .data = &adxl31x_chip_info[ADXL312] }, + { .compatible = "adi,adxl313", .data = &adxl31x_chip_info[ADXL313] }, + { .compatible = "adi,adxl314", .data = &adxl31x_chip_info[ADXL314] }, { } }; diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index eceb1f8d338d..ad8fce3e08cd 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -1184,7 +1184,8 @@ static int bma400_activity_event_en(struct bma400_data *data, enum iio_event_direction dir, int state) { - int ret, reg, msk, value, field_value; + int ret, reg, msk, value; + int field_value = 0; switch (dir) { case IIO_EV_DIR_RISING: diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index bca4cf98bf4d..84edcc78d796 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -606,7 +606,7 @@ void bmi088_accel_core_remove(struct device *dev) } EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088); -static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) +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); @@ -614,7 +614,7 @@ static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) return bmi088_accel_power_down(data); } -static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev) +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); @@ -622,13 +622,10 @@ static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev) return bmi088_accel_power_up(data); } -const struct dev_pm_ops bmi088_accel_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend, - bmi088_accel_runtime_resume, NULL) -}; -EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088); +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 "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index 9e2ed3bd5661..ee540edd8412 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -80,7 +80,7 @@ MODULE_DEVICE_TABLE(spi, bmi088_accel_id); static struct spi_driver bmi088_accel_driver = { .driver = { .name = "bmi088_accel_spi", - .pm = &bmi088_accel_pm_ops, + .pm = pm_ptr(&bmi088_accel_pm_ops), .of_match_table = bmi088_of_match, }, .probe = bmi088_accel_probe, diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 56e3ca3b4a5d..791612ca6012 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -732,6 +732,8 @@ config MCP3422 config MCP3911 tristate "Microchip Technology MCP3911 driver" depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for Microchip Technology's MCP3911 analog to digital converter. diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index edad1f30121d..9d6bf6d0927a 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -93,6 +94,7 @@ enum ad7923_id { .sign = 'u', \ .realbits = (bits), \ .storagebits = 16, \ + .shift = 12 - (bits), \ .endianness = IIO_BE, \ }, \ } @@ -268,7 +270,8 @@ static int ad7923_read_raw(struct iio_dev *indio_dev, return ret; if (chan->address == EXTRACT(ret, 12, 4)) - *val = EXTRACT(ret, 0, 12); + *val = EXTRACT(ret, chan->scan_type.shift, + chan->scan_type.realbits); else return -EIO; @@ -298,6 +301,7 @@ static void ad7923_regulator_disable(void *data) static int ad7923_probe(struct spi_device *spi) { + u32 ad7923_range = AD7923_RANGE; struct ad7923_state *st; struct iio_dev *indio_dev; const struct ad7923_chip_info *info; @@ -309,8 +313,11 @@ static int ad7923_probe(struct spi_device *spi) st = iio_priv(indio_dev); + if (device_property_read_bool(&spi->dev, "adi,range-double")) + ad7923_range = 0; + st->spi = spi; - st->settings = AD7923_CODING | AD7923_RANGE | + st->settings = AD7923_CODING | ad7923_range | AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS); info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data]; diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c index dfb3bb5997e5..2593fa4322eb 100644 --- a/drivers/iio/adc/ltc2496.c +++ b/drivers/iio/adc/ltc2496.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "ltc2497.h" @@ -74,6 +75,7 @@ static int ltc2496_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); st->spi = spi; st->common_ddata.result_and_measure = ltc2496_result_and_measure; + st->common_ddata.chip_info = device_get_match_data(dev); return ltc2497core_probe(dev, indio_dev); } @@ -85,8 +87,13 @@ static void ltc2496_remove(struct spi_device *spi) ltc2497core_remove(indio_dev); } +static const struct ltc2497_chip_info ltc2496_info = { + .resolution = 16, + .name = NULL, +}; + static const struct of_device_id ltc2496_of_match[] = { - { .compatible = "lltc,ltc2496", }, + { .compatible = "lltc,ltc2496", .data = <c2496_info, }, {}, }; MODULE_DEVICE_TABLE(of, ltc2496_of_match); diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c index 2a485c8a1940..f52d37af4d1f 100644 --- a/drivers/iio/adc/ltc2497-core.c +++ b/drivers/iio/adc/ltc2497-core.c @@ -95,7 +95,7 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev, return ret; *val = ret / 1000; - *val2 = 17; + *val2 = ddata->chip_info->resolution + 1; return IIO_VAL_FRACTIONAL_LOG2; @@ -169,7 +169,15 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); int ret; - indio_dev->name = dev_name(dev); + /* + * Keep using dev_name() for the iio_dev's name on some of the parts, + * since updating it would result in a ABI breakage. + */ + if (ddata->chip_info->name) + indio_dev->name = ddata->chip_info->name; + else + indio_dev->name = dev_name(dev); + indio_dev->info = <c2497core_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ltc2497core_channel; diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index f7c786f37ceb..4206f17e284c 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -12,18 +12,31 @@ #include #include #include +#include + +#include #include "ltc2497.h" +enum ltc2497_chip_type { + TYPE_LTC2497, + TYPE_LTC2499, +}; + struct ltc2497_driverdata { /* this must be the first member */ struct ltc2497core_driverdata common_ddata; struct i2c_client *client; + u32 recv_size; + u32 sub_lsb; /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. */ - __be32 buf __aligned(IIO_DMA_MINALIGN); + union { + __be32 d32; + u8 d8[3]; + } data __aligned(IIO_DMA_MINALIGN); }; static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, @@ -34,13 +47,43 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, int ret; if (val) { - ret = i2c_master_recv(st->client, (char *)&st->buf, 3); + if (st->recv_size == 3) + ret = i2c_master_recv(st->client, (char *)&st->data.d8, + st->recv_size); + else + ret = i2c_master_recv(st->client, (char *)&st->data.d32, + st->recv_size); if (ret < 0) { dev_err(&st->client->dev, "i2c_master_recv failed\n"); return ret; } - *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); + /* + * The data format is 16/24 bit 2s complement, but with an upper sign bit on the + * resolution + 1 position, which is set for positive values only. Given this + * bit's value, subtracting BIT(resolution + 1) from the ADC's result is + * equivalent to a sign extension. + */ + if (st->recv_size == 3) { + *val = (get_unaligned_be24(st->data.d8) >> st->sub_lsb) + - BIT(ddata->chip_info->resolution + 1); + } else { + *val = (be32_to_cpu(st->data.d32) >> st->sub_lsb) + - BIT(ddata->chip_info->resolution + 1); + } + + /* + * The part started a new conversion at the end of the above i2c + * transfer, so if the address didn't change since the last call + * everything is fine and we can return early. + * If not (which should only happen when some sort of bulk + * conversion is implemented) we have to program the new + * address. Note that this probably fails as the conversion that + * was triggered above is like not complete yet and the two + * operations have to be done in a single transfer. + */ + if (ddata->addr_prev == address) + return 0; } ret = i2c_smbus_write_byte(st->client, @@ -54,9 +97,11 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, static int ltc2497_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const struct ltc2497_chip_info *chip_info; struct iio_dev *indio_dev; struct ltc2497_driverdata *st; struct device *dev = &client->dev; + u32 resolution; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) @@ -71,6 +116,15 @@ static int ltc2497_probe(struct i2c_client *client, st->client = client; st->common_ddata.result_and_measure = ltc2497_result_and_measure; + chip_info = device_get_match_data(dev); + if (!chip_info) + chip_info = (const struct ltc2497_chip_info *)id->driver_data; + st->common_ddata.chip_info = chip_info; + + resolution = chip_info->resolution; + st->sub_lsb = 31 - (resolution + 1); + st->recv_size = BITS_TO_BYTES(resolution) + 1; + return ltc2497core_probe(dev, indio_dev); } @@ -83,14 +137,27 @@ static int ltc2497_remove(struct i2c_client *client) return 0; } +static const struct ltc2497_chip_info ltc2497_info[] = { + [TYPE_LTC2497] = { + .resolution = 16, + .name = NULL, + }, + [TYPE_LTC2499] = { + .resolution = 24, + .name = "ltc2499", + }, +}; + static const struct i2c_device_id ltc2497_id[] = { - { "ltc2497", 0 }, + { "ltc2497", (kernel_ulong_t)<c2497_info[TYPE_LTC2497] }, + { "ltc2499", (kernel_ulong_t)<c2497_info[TYPE_LTC2499] }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2497_id); static const struct of_device_id ltc2497_of_match[] = { - { .compatible = "lltc,ltc2497", }, + { .compatible = "lltc,ltc2497", .data = <c2497_info[TYPE_LTC2497] }, + { .compatible = "lltc,ltc2499", .data = <c2497_info[TYPE_LTC2499] }, {}, }; MODULE_DEVICE_TABLE(of, ltc2497_of_match); diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h index d0b42dd6b8ad..e023de0d88c4 100644 --- a/drivers/iio/adc/ltc2497.h +++ b/drivers/iio/adc/ltc2497.h @@ -4,9 +4,15 @@ #define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE #define LTC2497_CONVERSION_TIME_MS 150ULL +struct ltc2497_chip_info { + u32 resolution; + const char *name; +}; + struct ltc2497core_driverdata { struct regulator *ref; ktime_t time_prev; + const struct ltc2497_chip_info *chip_info; u8 addr_prev; int (*result_and_measure)(struct ltc2497core_driverdata *ddata, u8 address, int *val); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 890af7dca62d..b35fd2c9c3c0 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -5,16 +5,25 @@ * Copyright (C) 2018 Marcus Folkesson * Copyright (C) 2018 Kent Gustavsson */ +#include +#include #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include +#include + +#include + #define MCP3911_REG_CHANNEL0 0x00 #define MCP3911_REG_CHANNEL1 0x03 #define MCP3911_REG_MOD 0x06 @@ -22,6 +31,8 @@ #define MCP3911_REG_GAIN 0x09 #define MCP3911_REG_STATUSCOM 0x0a +#define MCP3911_STATUSCOM_DRHIZ BIT(12) +#define MCP3911_STATUSCOM_READ GENMASK(7, 6) #define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) #define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) #define MCP3911_STATUSCOM_EN_OFFCAL BIT(2) @@ -30,6 +41,7 @@ #define MCP3911_REG_CONFIG 0x0c #define MCP3911_CONFIG_CLKEXT BIT(1) #define MCP3911_CONFIG_VREFEXT BIT(2) +#define MCP3911_CONFIG_OSR GENMASK(13, 11) #define MCP3911_REG_OFFCAL_CH0 0x0e #define MCP3911_REG_GAINCAL_CH0 0x11 @@ -48,12 +60,22 @@ #define MCP3911_NUM_CHANNELS 2 +static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 }; + struct mcp3911 { struct spi_device *spi; struct mutex lock; struct regulator *vref; struct clk *clki; u32 dev_addr; + struct iio_trigger *trig; + struct { + u32 channels[MCP3911_NUM_CHANNELS]; + s64 ts __aligned(8); + } scan; + + u8 tx_buf __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[MCP3911_NUM_CHANNELS * 3]; }; static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len) @@ -98,6 +120,36 @@ static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, return mcp3911_write(adc, reg, val, len); } +static int mcp3911_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_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_NANO; + } +} + +static int mcp3911_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_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = mcp3911_osr_table; + *length = ARRAY_SIZE(mcp3911_osr_table); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static int mcp3911_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel, int *val, int *val2, long mask) @@ -126,6 +178,15 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev, ret = IIO_VAL_INT; break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, val, 2); + if (ret) + goto out; + + *val = FIELD_GET(MCP3911_CONFIG_OSR, *val); + *val = 32 << *val; + ret = IIO_VAL_INT; + break; case IIO_CHAN_INFO_SCALE: if (adc->vref) { @@ -185,6 +246,17 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, MCP3911_STATUSCOM_EN_OFFCAL, MCP3911_STATUSCOM_EN_OFFCAL, 2); break; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + for (int i = 0; i < sizeof(mcp3911_osr_table); i++) { + if (val == mcp3911_osr_table[i]) { + val = FIELD_PREP(MCP3911_CONFIG_OSR, i); + ret = mcp3911_update(adc, MCP3911_REG_CONFIG, MCP3911_CONFIG_OSR, + val, 2); + break; + } + } + break; } out: @@ -196,25 +268,80 @@ out: .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = idx, \ + .scan_index = idx, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ } static const struct iio_chan_spec mcp3911_channels[] = { MCP3911_CHAN(0), MCP3911_CHAN(1), + IIO_CHAN_SOFT_TIMESTAMP(2), }; +static irqreturn_t mcp3911_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct mcp3911 *adc = iio_priv(indio_dev); + struct spi_transfer xfer[] = { + { + .tx_buf = &adc->tx_buf, + .len = 1, + }, { + .rx_buf = adc->rx_buf, + .len = sizeof(adc->rx_buf), + }, + }; + int scan_index; + int i = 0; + int ret; + + mutex_lock(&adc->lock); + adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr); + ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer)); + if (ret < 0) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) { + const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; + + adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]); + i++; + } + iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const struct iio_info mcp3911_info = { .read_raw = mcp3911_read_raw, .write_raw = mcp3911_write_raw, + .read_avail = mcp3911_read_avail, + .write_raw_get_fmt = mcp3911_write_raw_get_fmt, }; static int mcp3911_config(struct mcp3911 *adc) { struct device *dev = &adc->spi->dev; - u32 configreg; + u32 regval; int ret; ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr); @@ -233,31 +360,67 @@ static int mcp3911_config(struct mcp3911 *adc) } dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); - ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2); + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, ®val, 2); if (ret) return ret; + regval &= ~MCP3911_CONFIG_VREFEXT; if (adc->vref) { dev_dbg(&adc->spi->dev, "use external voltage reference\n"); - configreg |= MCP3911_CONFIG_VREFEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1); } else { dev_dbg(&adc->spi->dev, "use internal voltage reference (1.2V)\n"); - configreg &= ~MCP3911_CONFIG_VREFEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 0); } + regval &= ~MCP3911_CONFIG_CLKEXT; if (adc->clki) { dev_dbg(&adc->spi->dev, "use external clock as clocksource\n"); - configreg |= MCP3911_CONFIG_CLKEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 1); } else { dev_dbg(&adc->spi->dev, "use crystal oscillator as clocksource\n"); - configreg &= ~MCP3911_CONFIG_CLKEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 0); } - return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2); + ret = mcp3911_write(adc, MCP3911_REG_CONFIG, regval, 2); + if (ret) + return ret; + + ret = mcp3911_read(adc, MCP3911_REG_STATUSCOM, ®val, 2); + if (ret) + return ret; + + /* Address counter incremented, cycle through register types */ + regval &= ~MCP3911_STATUSCOM_READ; + regval |= FIELD_PREP(MCP3911_STATUSCOM_READ, 0x02); + + return mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2); } +static void mcp3911_cleanup_regulator(void *vref) +{ + regulator_disable(vref); +} + +static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct mcp3911 *adc = iio_trigger_get_drvdata(trig); + + if (enable) + enable_irq(adc->spi->irq); + else + disable_irq(adc->spi->irq); + + return 0; +} + +static const struct iio_trigger_ops mcp3911_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = mcp3911_set_trigger_state, +}; + static int mcp3911_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -286,9 +449,14 @@ static int mcp3911_probe(struct spi_device *spi) ret = regulator_enable(adc->vref); if (ret) return ret; + + ret = devm_add_action_or_reset(&spi->dev, + mcp3911_cleanup_regulator, adc->vref); + if (ret) + return ret; } - adc->clki = devm_clk_get(&adc->spi->dev, NULL); + adc->clki = devm_clk_get_enabled(&adc->spi->dev, NULL); if (IS_ERR(adc->clki)) { if (PTR_ERR(adc->clki) == -ENOENT) { adc->clki = NULL; @@ -296,21 +464,22 @@ static int mcp3911_probe(struct spi_device *spi) dev_err(&adc->spi->dev, "failed to get adc clk (%ld)\n", PTR_ERR(adc->clki)); - ret = PTR_ERR(adc->clki); - goto reg_disable; - } - } else { - ret = clk_prepare_enable(adc->clki); - if (ret < 0) { - dev_err(&adc->spi->dev, - "Failed to enable clki: %d\n", ret); - goto reg_disable; + return PTR_ERR(adc->clki); } } ret = mcp3911_config(adc); if (ret) - goto clk_disable; + return ret; + + if (device_property_read_bool(&adc->spi->dev, "microchip,data-ready-hiz")) + ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, + 0, 2); + else + ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, + MCP3911_STATUSCOM_DRHIZ, 2); + if (ret) + return ret; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; @@ -322,31 +491,38 @@ static int mcp3911_probe(struct spi_device *spi) mutex_init(&adc->lock); - ret = iio_device_register(indio_dev); + if (spi->irq > 0) { + adc->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!adc->trig) + return PTR_ERR(adc->trig); + + adc->trig->ops = &mcp3911_trigger_ops; + iio_trigger_set_drvdata(adc->trig, adc); + ret = devm_iio_trigger_register(&spi->dev, adc->trig); + if (ret) + return ret; + + /* + * The device generates interrupts as long as it is powered up. + * Some platforms might not allow the option to power it down so + * don't enable the interrupt to avoid extra load on the system. + */ + ret = devm_request_irq(&spi->dev, spi->irq, + &iio_trigger_generic_data_rdy_poll, IRQF_NO_AUTOEN | IRQF_ONESHOT, + indio_dev->name, adc->trig); + if (ret) + return ret; + } + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + NULL, + mcp3911_trigger_handler, NULL); if (ret) - goto clk_disable; + return ret; - return ret; - -clk_disable: - clk_disable_unprepare(adc->clki); -reg_disable: - if (adc->vref) - regulator_disable(adc->vref); - - return ret; -} - -static void mcp3911_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct mcp3911 *adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - clk_disable_unprepare(adc->clki); - if (adc->vref) - regulator_disable(adc->vref); + return devm_iio_device_register(&adc->spi->dev, indio_dev); } static const struct of_device_id mcp3911_dt_ids[] = { @@ -367,7 +543,6 @@ static struct spi_driver mcp3911_driver = { .of_match_table = mcp3911_dt_ids, }, .probe = mcp3911_probe, - .remove = mcp3911_remove, .id_table = mcp3911_id, }; module_spi_driver(mcp3911_driver); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 1ce52af3fe8b..81d5db91c67b 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -9,6 +9,7 @@ * */ +#include #include #include #include @@ -62,6 +63,7 @@ struct stm32_adc_priv; * @regs: common registers for all instances * @clk_sel: clock selection routine * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) + * @ipid: adc identification number * @has_syscfg: SYSCFG capability flags * @num_irqs: number of interrupt lines * @num_adcs: maximum number of ADC instances in the common registers @@ -70,6 +72,7 @@ struct stm32_adc_priv_cfg { const struct stm32_adc_common_regs *regs; int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); u32 max_clk_rate_hz; + u32 ipid; unsigned int has_syscfg; unsigned int num_irqs; unsigned int num_adcs; @@ -78,6 +81,7 @@ struct stm32_adc_priv_cfg { /** * struct stm32_adc_priv - stm32 ADC core private data * @irq: irq(s) for ADC block + * @nb_adc_max: actual maximum number of instance per ADC block * @domain: irq domain reference * @aclk: clock reference for the analog circuitry * @bclk: bus clock common for all ADCs, depends on part used @@ -95,6 +99,7 @@ struct stm32_adc_priv_cfg { */ struct stm32_adc_priv { int irq[STM32_ADC_MAX_ADCS]; + unsigned int nb_adc_max; struct irq_domain *domain; struct clk *aclk; struct clk *bclk; @@ -354,7 +359,7 @@ static void stm32_adc_irq_handler(struct irq_desc *desc) * before invoking the interrupt handler (e.g. call ISR only for * IRQ-enabled ADCs). */ - for (i = 0; i < priv->cfg->num_adcs; i++) { + for (i = 0; i < priv->nb_adc_max; i++) { if ((status & priv->cfg->regs->eoc_msk[i] && stm32_adc_eoc_enabled(priv, i)) || (status & priv->cfg->regs->ovr_msk[i])) @@ -424,7 +429,7 @@ static void stm32_adc_irq_remove(struct platform_device *pdev, int hwirq; unsigned int i; - for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++) + for (hwirq = 0; hwirq < priv->nb_adc_max; hwirq++) irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); irq_domain_remove(priv->domain); @@ -642,6 +647,49 @@ static int stm32_adc_core_switches_probe(struct device *dev, return 0; } +static int stm32_adc_probe_identification(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + const char *compat; + int ret, count = 0; + u32 id, val; + + if (!priv->cfg->ipid) + return 0; + + id = FIELD_GET(STM32MP1_IPIDR_MASK, + readl_relaxed(priv->common.base + STM32MP1_ADC_IPDR)); + if (id != priv->cfg->ipid) { + dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id); + return -EINVAL; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_string(child, "compatible", &compat); + if (ret) + continue; + /* Count child nodes with stm32 adc compatible */ + if (strstr(compat, "st,stm32") && strstr(compat, "adc")) + count++; + } + + val = readl_relaxed(priv->common.base + STM32MP1_ADC_HWCFGR0); + priv->nb_adc_max = FIELD_GET(STM32MP1_ADCNUM_MASK, val); + if (count > priv->nb_adc_max) { + dev_err(&pdev->dev, "Unexpected child number: %d", count); + return -EINVAL; + } + + val = readl_relaxed(priv->common.base + STM32MP1_ADC_VERR); + dev_dbg(&pdev->dev, "ADC version: %lu.%lu\n", + FIELD_GET(STM32MP1_MAJREV_MASK, val), + FIELD_GET(STM32MP1_MINREV_MASK, val)); + + return 0; +} + static int stm32_adc_probe(struct platform_device *pdev) { struct stm32_adc_priv *priv; @@ -661,6 +709,7 @@ static int stm32_adc_probe(struct platform_device *pdev) priv->cfg = (const struct stm32_adc_priv_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; + priv->nb_adc_max = priv->cfg->num_adcs; spin_lock_init(&priv->common.lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -703,6 +752,10 @@ static int stm32_adc_probe(struct platform_device *pdev) if (ret) goto err_pm_stop; + ret = stm32_adc_probe_identification(pdev, priv); + if (ret < 0) + goto err_hw_stop; + ret = regulator_get_voltage(priv->vref); if (ret < 0) { dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret); @@ -811,8 +864,8 @@ static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { .clk_sel = stm32h7_adc_clk_sel, .max_clk_rate_hz = 36000000, .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD, + .ipid = STM32MP15_IPIDR_NUMBER, .num_irqs = 2, - .num_adcs = 2, }; static const struct of_device_id stm32_adc_of_match[] = { diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index faedf7a49555..2118ef63843d 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -24,6 +24,7 @@ * | 0x300 | Master & Slave common regs | * -------------------------------------------------------- */ +/* Maximum ADC instances number per ADC block for all supported SoCs */ #define STM32_ADC_MAX_ADCS 3 #define STM32_ADC_OFFSET 0x100 #define STM32_ADCX_COMN_OFFSET 0x300 @@ -105,6 +106,12 @@ /* STM32MP1 - ADC2 instance option register */ #define STM32MP1_ADC2_OR 0xD0 +/* STM32MP1 - Identification registers */ +#define STM32MP1_ADC_HWCFGR0 0x3F0 +#define STM32MP1_ADC_VERR 0x3F4 +#define STM32MP1_ADC_IPDR 0x3F8 +#define STM32MP1_ADC_SIDR 0x3FC + /* STM32H7 - common registers for all ADC instances */ #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) @@ -181,6 +188,30 @@ enum stm32h7_adc_dmngt { /* STM32MP1_ADC2_OR - bit fields */ #define STM32MP1_VDDCOREEN BIT(0) +/* STM32MP1_ADC_HWCFGR0 - bit fields */ +#define STM32MP1_ADCNUM_SHIFT 0 +#define STM32MP1_ADCNUM_MASK GENMASK(3, 0) +#define STM32MP1_MULPIPE_SHIFT 4 +#define STM32MP1_MULPIPE_MASK GENMASK(7, 4) +#define STM32MP1_OPBITS_SHIFT 8 +#define STM32MP1_OPBITS_MASK GENMASK(11, 8) +#define STM32MP1_IDLEVALUE_SHIFT 12 +#define STM32MP1_IDLEVALUE_MASK GENMASK(15, 12) + +/* STM32MP1_ADC_VERR - bit fields */ +#define STM32MP1_MINREV_SHIFT 0 +#define STM32MP1_MINREV_MASK GENMASK(3, 0) +#define STM32MP1_MAJREV_SHIFT 4 +#define STM32MP1_MAJREV_MASK GENMASK(7, 4) + +/* STM32MP1_ADC_IPDR - bit fields */ +#define STM32MP1_IPIDR_MASK GENMASK(31, 0) + +/* STM32MP1_ADC_SIDR - bit fields */ +#define STM32MP1_SIDR_MASK GENMASK(31, 0) + +#define STM32MP15_IPIDR_NUMBER 0x00110005 + /** * struct stm32_adc_common - stm32 ADC driver common data (for all instances) * @base: control registers base cpu addr diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index 34e1319a9712..420981e7c5c3 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -13,6 +13,8 @@ #include #include +#include + #define AD5593R_MODE_CONF (0 << 4) #define AD5593R_MODE_DAC_WRITE (1 << 4) #define AD5593R_MODE_ADC_READBACK (4 << 4) @@ -20,6 +22,24 @@ #define AD5593R_MODE_GPIO_READBACK (6 << 4) #define AD5593R_MODE_REG_READBACK (7 << 4) +static int ad5593r_read_word(struct i2c_client *i2c, u8 reg, u16 *value) +{ + int ret; + u8 buf[2]; + + ret = i2c_smbus_write_byte(i2c, reg); + if (ret < 0) + return ret; + + ret = i2c_master_recv(i2c, buf, sizeof(buf)); + if (ret < 0) + return ret; + + *value = get_unaligned_be16(buf); + + return 0; +} + static int ad5593r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value) { struct i2c_client *i2c = to_i2c_client(st->dev); @@ -38,13 +58,7 @@ static int ad5593r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value) if (val < 0) return (int) val; - val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_ADC_READBACK); - if (val < 0) - return (int) val; - - *value = (u16) val; - - return 0; + return ad5593r_read_word(i2c, AD5593R_MODE_ADC_READBACK, value); } static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value) @@ -58,25 +72,19 @@ static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value) static int ad5593r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value) { struct i2c_client *i2c = to_i2c_client(st->dev); - s32 val; - val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_REG_READBACK | reg); - if (val < 0) - return (int) val; - - *value = (u16) val; - - return 0; + return ad5593r_read_word(i2c, AD5593R_MODE_REG_READBACK | reg, value); } static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value) { struct i2c_client *i2c = to_i2c_client(st->dev); - s32 val; + u16 val; + int ret; - val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_GPIO_READBACK); - if (val < 0) - return (int) val; + ret = ad5593r_read_word(i2c, AD5593R_MODE_GPIO_READBACK, &val); + if (ret) + return ret; *value = (u8) val; @@ -94,6 +102,10 @@ static const struct ad5592r_rw_ops ad5593r_rw_ops = { static int ad5593r_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + if (!i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -EOPNOTSUPP; + return ad5592r_probe(&i2c->dev, id->name, &ad5593r_rw_ops); } diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 001ca2c3ff95..f1d7d4b5e222 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -52,6 +52,7 @@ config ADIS16480 ADIS16485, ADIS16488 inertial sensors. source "drivers/iio/imu/bmi160/Kconfig" +source "drivers/iio/imu/bno055/Kconfig" config FXOS8700 tristate diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index c82748096c77..6eb612034722 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o obj-y += bmi160/ +obj-y += bno055/ obj-$(CONFIG_FXOS8700) += fxos8700_core.o obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig new file mode 100644 index 000000000000..fa79b1ac4f85 --- /dev/null +++ b/drivers/iio/imu/bno055/Kconfig @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0 + +config BOSCH_BNO055 + tristate + +config BOSCH_BNO055_SERIAL + tristate "Bosch BNO055 attached via UART" + depends on SERIAL_DEV_BUS + select BOSCH_BNO055 + help + Enable this to support Bosch BNO055 IMUs attached via UART. + + This driver can also be built as a module. If so, the module will be + called bno055_sl. + +config BOSCH_BNO055_I2C + tristate "Bosch BNO055 attached via I2C bus" + depends on I2C + select REGMAP_I2C + select BOSCH_BNO055 + help + Enable this to support Bosch BNO055 IMUs attached via I2C bus. + + This driver can also be built as a module. If so, the module will be + called bno055_i2c. diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile new file mode 100644 index 000000000000..98c624730dae --- /dev/null +++ b/drivers/iio/imu/bno055/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_BOSCH_BNO055) += bno055.o +obj-$(CONFIG_BOSCH_BNO055_SERIAL) += bno055_ser.o +bno055_ser-y := bno055_ser_core.o +# define_trace.h needs to know how to find our header +CFLAGS_bno055_ser_trace.o := -I$(src) +bno055_ser-$(CONFIG_TRACING) += bno055_ser_trace.o + +obj-$(CONFIG_BOSCH_BNO055_I2C) += bno055_i2c.o diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c new file mode 100644 index 000000000000..307557a609e3 --- /dev/null +++ b/drivers/iio/imu/bno055/bno055.c @@ -0,0 +1,1685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO driver for Bosch BNO055 IMU + * + * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * Written by Andrea Merello + * + * Portions of this driver are taken from the BNO055 driver patch + * from Vlad Dogaru which is Copyright (c) 2016, Intel Corporation. + * + * This driver is also based on BMI160 driver, which is: + * Copyright (c) 2016, Intel Corporation. + * Copyright (c) 2019, Martin Kelly. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bno055.h" + +#define BNO055_FW_UID_FMT "bno055-caldata-%*phN.dat" +#define BNO055_FW_GENERIC_NAME "bno055-caldata.dat" + +/* common registers */ +#define BNO055_PAGESEL_REG 0x7 + +/* page 0 registers */ +#define BNO055_CHIP_ID_REG 0x0 +#define BNO055_CHIP_ID_MAGIC 0xA0 +#define BNO055_SW_REV_LSB_REG 0x4 +#define BNO055_SW_REV_MSB_REG 0x5 +#define BNO055_ACC_DATA_X_LSB_REG 0x8 +#define BNO055_ACC_DATA_Y_LSB_REG 0xA +#define BNO055_ACC_DATA_Z_LSB_REG 0xC +#define BNO055_MAG_DATA_X_LSB_REG 0xE +#define BNO055_MAG_DATA_Y_LSB_REG 0x10 +#define BNO055_MAG_DATA_Z_LSB_REG 0x12 +#define BNO055_GYR_DATA_X_LSB_REG 0x14 +#define BNO055_GYR_DATA_Y_LSB_REG 0x16 +#define BNO055_GYR_DATA_Z_LSB_REG 0x18 +#define BNO055_EUL_DATA_X_LSB_REG 0x1A +#define BNO055_EUL_DATA_Y_LSB_REG 0x1C +#define BNO055_EUL_DATA_Z_LSB_REG 0x1E +#define BNO055_QUAT_DATA_W_LSB_REG 0x20 +#define BNO055_LIA_DATA_X_LSB_REG 0x28 +#define BNO055_LIA_DATA_Y_LSB_REG 0x2A +#define BNO055_LIA_DATA_Z_LSB_REG 0x2C +#define BNO055_GRAVITY_DATA_X_LSB_REG 0x2E +#define BNO055_GRAVITY_DATA_Y_LSB_REG 0x30 +#define BNO055_GRAVITY_DATA_Z_LSB_REG 0x32 +#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2) +#define BNO055_TEMP_REG 0x34 +#define BNO055_CALIB_STAT_REG 0x35 +#define BNO055_CALIB_STAT_MAGN_SHIFT 0 +#define BNO055_CALIB_STAT_ACCEL_SHIFT 2 +#define BNO055_CALIB_STAT_GYRO_SHIFT 4 +#define BNO055_CALIB_STAT_SYS_SHIFT 6 +#define BNO055_SYS_ERR_REG 0x3A +#define BNO055_POWER_MODE_REG 0x3E +#define BNO055_POWER_MODE_NORMAL 0 +#define BNO055_SYS_TRIGGER_REG 0x3F +#define BNO055_SYS_TRIGGER_RST_SYS BIT(5) +#define BNO055_SYS_TRIGGER_CLK_SEL BIT(7) +#define BNO055_OPR_MODE_REG 0x3D +#define BNO055_OPR_MODE_CONFIG 0x0 +#define BNO055_OPR_MODE_AMG 0x7 +#define BNO055_OPR_MODE_FUSION_FMC_OFF 0xB +#define BNO055_OPR_MODE_FUSION 0xC +#define BNO055_UNIT_SEL_REG 0x3B +/* Android orientation mode means: pitch value decreases turning clockwise */ +#define BNO055_UNIT_SEL_ANDROID BIT(7) +#define BNO055_UNIT_SEL_GYR_RPS BIT(1) +#define BNO055_CALDATA_START 0x55 +#define BNO055_CALDATA_END 0x6A +#define BNO055_CALDATA_LEN 22 + +/* + * The difference in address between the register that contains the + * value and the register that contains the offset. This applies for + * accel, gyro and magn channels. + */ +#define BNO055_REG_OFFSET_ADDR 0x4D + +/* page 1 registers */ +#define BNO055_PG1(x) ((x) | 0x80) +#define BNO055_ACC_CONFIG_REG BNO055_PG1(0x8) +#define BNO055_ACC_CONFIG_LPF_MASK GENMASK(4, 2) +#define BNO055_ACC_CONFIG_RANGE_MASK GENMASK(1, 0) +#define BNO055_MAG_CONFIG_REG BNO055_PG1(0x9) +#define BNO055_MAG_CONFIG_HIGHACCURACY 0x18 +#define BNO055_MAG_CONFIG_ODR_MASK GENMASK(2, 0) +#define BNO055_GYR_CONFIG_REG BNO055_PG1(0xA) +#define BNO055_GYR_CONFIG_RANGE_MASK GENMASK(2, 0) +#define BNO055_GYR_CONFIG_LPF_MASK GENMASK(5, 3) +#define BNO055_GYR_AM_SET_REG BNO055_PG1(0x1F) +#define BNO055_UID_LOWER_REG BNO055_PG1(0x50) +#define BNO055_UID_HIGHER_REG BNO055_PG1(0x5F) +#define BNO055_UID_LEN 16 + +struct bno055_sysfs_attr { + int *vals; + int len; + int *fusion_vals; + int *hw_xlate; + int type; +}; + +static int bno055_acc_lpf_vals[] = { + 7, 810000, 15, 630000, 31, 250000, 62, 500000, + 125, 0, 250, 0, 500, 0, 1000, 0, +}; + +static struct bno055_sysfs_attr bno055_acc_lpf = { + .vals = bno055_acc_lpf_vals, + .len = ARRAY_SIZE(bno055_acc_lpf_vals), + .fusion_vals = (int[]){62, 500000}, + .type = IIO_VAL_INT_PLUS_MICRO, +}; + +static int bno055_acc_range_vals[] = { + /* G: 2, 4, 8, 16 */ + 1962, 3924, 7848, 15696 +}; + +static struct bno055_sysfs_attr bno055_acc_range = { + .vals = bno055_acc_range_vals, + .len = ARRAY_SIZE(bno055_acc_range_vals), + .fusion_vals = (int[]){3924}, /* 4G */ + .type = IIO_VAL_INT, +}; + +/* + * Theoretically the IMU should return data in a given (i.e. fixed) unit + * regardless of the range setting. This happens for the accelerometer, but not + * for the gyroscope; the gyroscope range setting affects the scale. + * This is probably due to this[0] bug. + * For this reason we map the internal range setting onto the standard IIO scale + * attribute for gyro. + * Since the bug[0] may be fixed in future, we check for the IMU FW version and + * eventually warn the user. + * Currently we just don't care about "range" attributes for gyro. + * + * [0] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266 + */ + +/* + * dps = hwval * (dps_range/2^15) + * rps = hwval * (rps_range/2^15) + * = hwval * (dps_range/(2^15 * k)) + * where k is rad-to-deg factor + */ +static int bno055_gyr_scale_vals[] = { + 125, 1877467, 250, 1877467, 500, 1877467, + 1000, 1877467, 2000, 1877467, +}; + +static struct bno055_sysfs_attr bno055_gyr_scale = { + .vals = bno055_gyr_scale_vals, + .len = ARRAY_SIZE(bno055_gyr_scale_vals), + .fusion_vals = (int[]){1, 900}, + .hw_xlate = (int[]){4, 3, 2, 1, 0}, + .type = IIO_VAL_FRACTIONAL, +}; + +static int bno055_gyr_lpf_vals[] = {12, 23, 32, 47, 64, 116, 230, 523}; +static struct bno055_sysfs_attr bno055_gyr_lpf = { + .vals = bno055_gyr_lpf_vals, + .len = ARRAY_SIZE(bno055_gyr_lpf_vals), + .fusion_vals = (int[]){32}, + .hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0}, + .type = IIO_VAL_INT, +}; + +static int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30}; +static struct bno055_sysfs_attr bno055_mag_odr = { + .vals = bno055_mag_odr_vals, + .len = ARRAY_SIZE(bno055_mag_odr_vals), + .fusion_vals = (int[]){20}, + .type = IIO_VAL_INT, +}; + +struct bno055_priv { + struct regmap *regmap; + struct device *dev; + struct clk *clk; + int operation_mode; + int xfer_burst_break_thr; + struct mutex lock; + u8 uid[BNO055_UID_LEN]; + struct gpio_desc *reset_gpio; + bool sw_reset; + struct { + __le16 chans[BNO055_SCAN_CH_COUNT]; + s64 timestamp __aligned(8); + } buf; + struct dentry *debugfs; +}; + +static bool bno055_regmap_volatile(struct device *dev, unsigned int reg) +{ + /* data and status registers */ + if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG) + return true; + + /* when in fusion mode, config is updated by chip */ + if (reg == BNO055_MAG_CONFIG_REG || + reg == BNO055_ACC_CONFIG_REG || + reg == BNO055_GYR_CONFIG_REG) + return true; + + /* calibration data may be updated by the IMU */ + if (reg >= BNO055_CALDATA_START && reg <= BNO055_CALDATA_END) + return true; + + return false; +} + +static bool bno055_regmap_readable(struct device *dev, unsigned int reg) +{ + /* unnamed PG0 reserved areas */ + if ((reg < BNO055_PG1(0) && reg > BNO055_CALDATA_END) || + reg == 0x3C) + return false; + + /* unnamed PG1 reserved areas */ + if (reg > BNO055_PG1(BNO055_UID_HIGHER_REG) || + (reg < BNO055_PG1(BNO055_UID_LOWER_REG) && reg > BNO055_PG1(BNO055_GYR_AM_SET_REG)) || + reg == BNO055_PG1(0xE) || + (reg < BNO055_PG1(BNO055_PAGESEL_REG) && reg >= BNO055_PG1(0x0))) + return false; + return true; +} + +static bool bno055_regmap_writeable(struct device *dev, unsigned int reg) +{ + /* + * Unreadable registers are indeed reserved; there are no WO regs + * (except for a single bit in SYS_TRIGGER register) + */ + if (!bno055_regmap_readable(dev, reg)) + return false; + + /* data and status registers */ + if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG) + return false; + + /* ID areas */ + if (reg < BNO055_PAGESEL_REG || + (reg <= BNO055_UID_HIGHER_REG && reg >= BNO055_UID_LOWER_REG)) + return false; + + return true; +} + +static const struct regmap_range_cfg bno055_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 0x7f * 2, + .selector_reg = BNO055_PAGESEL_REG, + .selector_mask = GENMASK(7, 0), + .selector_shift = 0, + .window_start = 0, + .window_len = 0x80, + }, +}; + +const struct regmap_config bno055_regmap_config = { + .name = "bno055", + .reg_bits = 8, + .val_bits = 8, + .ranges = bno055_regmap_ranges, + .num_ranges = 1, + .volatile_reg = bno055_regmap_volatile, + .max_register = 0x80 * 2, + .writeable_reg = bno055_regmap_writeable, + .readable_reg = bno055_regmap_readable, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(bno055_regmap_config, IIO_BNO055); + +/* must be called in configuration mode */ +static int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len) +{ + if (len != BNO055_CALDATA_LEN) { + dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)", + len, BNO055_CALDATA_LEN); + return -EINVAL; + } + + dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, data); + return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START, + data, BNO055_CALDATA_LEN); +} + +static int bno055_operation_mode_do_set(struct bno055_priv *priv, + int operation_mode) +{ + int ret; + + ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG, + operation_mode); + if (ret) + return ret; + + /* Following datasheet specifications: sensor takes 7mS up to 19 mS to switch mode */ + msleep(20); + + return 0; +} + +static int bno055_system_reset(struct bno055_priv *priv) +{ + int ret; + + if (priv->reset_gpio) { + gpiod_set_value_cansleep(priv->reset_gpio, 0); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(priv->reset_gpio, 1); + } else if (priv->sw_reset) { + ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG, + BNO055_SYS_TRIGGER_RST_SYS); + if (ret) + return ret; + } else { + return 0; + } + + regcache_drop_region(priv->regmap, 0x0, 0xff); + usleep_range(650000, 700000); + + return 0; +} + +static int bno055_init(struct bno055_priv *priv, const u8 *caldata, int len) +{ + int ret; + + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, BNO055_POWER_MODE_REG, + BNO055_POWER_MODE_NORMAL); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG, + priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0); + if (ret) + return ret; + + /* use standard SI units */ + ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG, + BNO055_UNIT_SEL_ANDROID | BNO055_UNIT_SEL_GYR_RPS); + if (ret) + return ret; + + if (caldata) { + ret = bno055_calibration_load(priv, caldata, len); + if (ret) + dev_warn(priv->dev, "failed to load calibration data with error %d\n", + ret); + } + + return 0; +} + +static ssize_t bno055_operation_mode_set(struct bno055_priv *priv, + int operation_mode) +{ + u8 caldata[BNO055_CALDATA_LEN]; + int ret; + + mutex_lock(&priv->lock); + + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + goto exit_unlock; + + if (operation_mode == BNO055_OPR_MODE_FUSION || + operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF) { + /* for entering fusion mode, reset the chip to clear the algo state */ + ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, caldata, + BNO055_CALDATA_LEN); + if (ret) + goto exit_unlock; + + ret = bno055_system_reset(priv); + if (ret) + goto exit_unlock; + + ret = bno055_init(priv, caldata, BNO055_CALDATA_LEN); + if (ret) + goto exit_unlock; + } + + ret = bno055_operation_mode_do_set(priv, operation_mode); + if (ret) + goto exit_unlock; + + priv->operation_mode = operation_mode; + +exit_unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static void bno055_uninit(void *arg) +{ + struct bno055_priv *priv = arg; + + /* stop the IMU */ + bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); +} + +#define BNO055_CHANNEL(_type, _axis, _index, _address, _sep, _sh, _avail) { \ + .address = _address, \ + .type = _type, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | (_sep), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_sh), \ + .info_mask_shared_by_type_available = _avail, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + .repeat = IIO_MOD_##_axis == IIO_MOD_QUATERNION ? 4 : 0, \ + }, \ +} + +/* scan indexes follow DATA register order */ +enum bno055_scan_axis { + BNO055_SCAN_ACCEL_X, + BNO055_SCAN_ACCEL_Y, + BNO055_SCAN_ACCEL_Z, + BNO055_SCAN_MAGN_X, + BNO055_SCAN_MAGN_Y, + BNO055_SCAN_MAGN_Z, + BNO055_SCAN_GYRO_X, + BNO055_SCAN_GYRO_Y, + BNO055_SCAN_GYRO_Z, + BNO055_SCAN_YAW, + BNO055_SCAN_ROLL, + BNO055_SCAN_PITCH, + BNO055_SCAN_QUATERNION, + BNO055_SCAN_LIA_X, + BNO055_SCAN_LIA_Y, + BNO055_SCAN_LIA_Z, + BNO055_SCAN_GRAVITY_X, + BNO055_SCAN_GRAVITY_Y, + BNO055_SCAN_GRAVITY_Z, + BNO055_SCAN_TIMESTAMP, + _BNO055_SCAN_MAX +}; + +static const struct iio_chan_spec bno055_channels[] = { + /* accelerometer */ + BNO055_CHANNEL(IIO_ACCEL, X, BNO055_SCAN_ACCEL_X, + BNO055_ACC_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), + BNO055_CHANNEL(IIO_ACCEL, Y, BNO055_SCAN_ACCEL_Y, + BNO055_ACC_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), + BNO055_CHANNEL(IIO_ACCEL, Z, BNO055_SCAN_ACCEL_Z, + BNO055_ACC_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), + /* gyroscope */ + BNO055_CHANNEL(IIO_ANGL_VEL, X, BNO055_SCAN_GYRO_X, + BNO055_GYR_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_SCALE)), + BNO055_CHANNEL(IIO_ANGL_VEL, Y, BNO055_SCAN_GYRO_Y, + BNO055_GYR_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_SCALE)), + BNO055_CHANNEL(IIO_ANGL_VEL, Z, BNO055_SCAN_GYRO_Z, + BNO055_GYR_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_SCALE)), + /* magnetometer */ + BNO055_CHANNEL(IIO_MAGN, X, BNO055_SCAN_MAGN_X, + BNO055_MAG_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), + BNO055_CHANNEL(IIO_MAGN, Y, BNO055_SCAN_MAGN_Y, + BNO055_MAG_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), + BNO055_CHANNEL(IIO_MAGN, Z, BNO055_SCAN_MAGN_Z, + BNO055_MAG_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), + /* euler angle */ + BNO055_CHANNEL(IIO_ROT, YAW, BNO055_SCAN_YAW, + BNO055_EUL_DATA_X_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ROT, ROLL, BNO055_SCAN_ROLL, + BNO055_EUL_DATA_Y_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ROT, PITCH, BNO055_SCAN_PITCH, + BNO055_EUL_DATA_Z_LSB_REG, 0, 0, 0), + /* quaternion */ + BNO055_CHANNEL(IIO_ROT, QUATERNION, BNO055_SCAN_QUATERNION, + BNO055_QUAT_DATA_W_LSB_REG, 0, 0, 0), + + /* linear acceleration */ + BNO055_CHANNEL(IIO_ACCEL, LINEAR_X, BNO055_SCAN_LIA_X, + BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ACCEL, LINEAR_Y, BNO055_SCAN_LIA_Y, + BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ACCEL, LINEAR_Z, BNO055_SCAN_LIA_Z, + BNO055_LIA_DATA_Z_LSB_REG, 0, 0, 0), + + /* gravity vector */ + BNO055_CHANNEL(IIO_GRAVITY, X, BNO055_SCAN_GRAVITY_X, + BNO055_GRAVITY_DATA_X_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_GRAVITY, Y, BNO055_SCAN_GRAVITY_Y, + BNO055_GRAVITY_DATA_Y_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_GRAVITY, Z, BNO055_SCAN_GRAVITY_Z, + BNO055_GRAVITY_DATA_Z_LSB_REG, 0, 0, 0), + + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, + }, + IIO_CHAN_SOFT_TIMESTAMP(BNO055_SCAN_TIMESTAMP), +}; + +static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2, + int reg, int mask, struct bno055_sysfs_attr *attr) +{ + const int shift = __ffs(mask); + int hwval, idx; + int ret; + int i; + + ret = regmap_read(priv->regmap, reg, &hwval); + if (ret) + return ret; + + idx = (hwval & mask) >> shift; + if (attr->hw_xlate) + for (i = 0; i < attr->len; i++) + if (attr->hw_xlate[i] == idx) { + idx = i; + break; + } + if (attr->type == IIO_VAL_INT) { + *val = attr->vals[idx]; + } else { /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL */ + *val = attr->vals[idx * 2]; + *val2 = attr->vals[idx * 2 + 1]; + } + + return attr->type; +} + +static int bno055_set_regmask(struct bno055_priv *priv, int val, int val2, + int reg, int mask, struct bno055_sysfs_attr *attr) +{ + const int shift = __ffs(mask); + int best_delta; + int req_val; + int tbl_val; + bool first; + int delta; + int hwval; + int ret; + int len; + int i; + + /* + * The closest value the HW supports is only one in fusion mode, + * and it is autoselected, so don't do anything, just return OK, + * as the closest possible value has been (virtually) selected + */ + if (priv->operation_mode != BNO055_OPR_MODE_AMG) + return 0; + + len = attr->len; + + /* + * We always get a request in INT_PLUS_MICRO, but we + * take care of the micro part only when we really have + * non-integer tables. This prevents 32-bit overflow with + * larger integers contained in integer tables. + */ + req_val = val; + if (attr->type != IIO_VAL_INT) { + len /= 2; + req_val = min(val, 2147) * 1000000 + val2; + } + + first = true; + for (i = 0; i < len; i++) { + switch (attr->type) { + case IIO_VAL_INT: + tbl_val = attr->vals[i]; + break; + case IIO_VAL_INT_PLUS_MICRO: + WARN_ON(attr->vals[i * 2] > 2147); + tbl_val = attr->vals[i * 2] * 1000000 + + attr->vals[i * 2 + 1]; + break; + case IIO_VAL_FRACTIONAL: + WARN_ON(attr->vals[i * 2] > 4294); + tbl_val = attr->vals[i * 2] * 1000000 / + attr->vals[i * 2 + 1]; + break; + default: + return -EINVAL; + } + delta = abs(tbl_val - req_val); + if (delta < best_delta || first) { + best_delta = delta; + hwval = i; + first = false; + } + } + + if (attr->hw_xlate) + hwval = attr->hw_xlate[hwval]; + + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, reg, mask, hwval << shift); + if (ret) + return ret; + + return bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_AMG); +} + +static int bno055_read_simple_chan(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + __le16 raw_val; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_bulk_read(priv->regmap, chan->address, + &raw_val, sizeof(raw_val)); + if (ret < 0) + return ret; + *val = sign_extend32(le16_to_cpu(raw_val), 15); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + if (priv->operation_mode != BNO055_OPR_MODE_AMG) { + *val = 0; + } else { + ret = regmap_bulk_read(priv->regmap, + chan->address + + BNO055_REG_OFFSET_ADDR, + &raw_val, sizeof(raw_val)); + if (ret < 0) + return ret; + /* + * IMU reports sensor offsets; IIO wants correction + * offsets, thus we need the 'minus' here. + */ + *val = -sign_extend32(le16_to_cpu(raw_val), 15); + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 1; + switch (chan->type) { + case IIO_GRAVITY: + /* Table 3-35: 1 m/s^2 = 100 LSB */ + case IIO_ACCEL: + /* Table 3-17: 1 m/s^2 = 100 LSB */ + *val2 = 100; + break; + case IIO_MAGN: + /* + * Table 3-19: 1 uT = 16 LSB. But we need + * Gauss: 1G = 0.1 uT. + */ + *val2 = 160; + break; + case IIO_ANGL_VEL: + /* + * Table 3-22: 1 Rps = 900 LSB + * .. but this is not exactly true. See comment at the + * beginning of this file. + */ + if (priv->operation_mode != BNO055_OPR_MODE_AMG) { + *val = bno055_gyr_scale.fusion_vals[0]; + *val2 = bno055_gyr_scale.fusion_vals[1]; + return IIO_VAL_FRACTIONAL; + } + + return bno055_get_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_RANGE_MASK, + &bno055_gyr_scale); + break; + case IIO_ROT: + /* Table 3-28: 1 degree = 16 LSB */ + *val2 = 16; + break; + default: + return -EINVAL; + } + return IIO_VAL_FRACTIONAL; + + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->type != IIO_MAGN) + return -EINVAL; + + return bno055_get_regmask(priv, val, val2, + BNO055_MAG_CONFIG_REG, + BNO055_MAG_CONFIG_ODR_MASK, + &bno055_mag_odr); + + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + switch (chan->type) { + case IIO_ANGL_VEL: + return bno055_get_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_LPF_MASK, + &bno055_gyr_lpf); + case IIO_ACCEL: + return bno055_get_regmask(priv, val, val2, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_LPF_MASK, + &bno055_acc_lpf); + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr, + const int **vals, int *length) +{ + if (priv->operation_mode != BNO055_OPR_MODE_AMG) { + /* locked when fusion enabled */ + *vals = attr->fusion_vals; + if (attr->type == IIO_VAL_INT) + *length = 1; + else + *length = 2; /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL*/ + } else { + *vals = attr->vals; + *length = attr->len; + } + + return attr->type; +} + +static int bno055_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: + *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_scale, + vals, length); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + switch (chan->type) { + case IIO_ANGL_VEL: + *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_lpf, + vals, length); + return IIO_AVAIL_LIST; + case IIO_ACCEL: + *type = bno055_sysfs_attr_avail(priv, &bno055_acc_lpf, + vals, length); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + + break; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_MAGN: + *type = bno055_sysfs_attr_avail(priv, &bno055_mag_odr, + vals, length); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int bno055_read_temp_chan(struct iio_dev *indio_dev, int *val) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + unsigned int raw_val; + int ret; + + ret = regmap_read(priv->regmap, BNO055_TEMP_REG, &raw_val); + if (ret < 0) + return ret; + + /* + * Tables 3-36 and 3-37: one byte of priv, signed, 1 LSB = 1C. + * ABI wants milliC. + */ + *val = raw_val * 1000; + + return IIO_VAL_INT; +} + +static int bno055_read_quaternion(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + __le16 raw_vals[4]; + int i, ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (size < 4) + return -EINVAL; + ret = regmap_bulk_read(priv->regmap, + BNO055_QUAT_DATA_W_LSB_REG, + raw_vals, sizeof(raw_vals)); + if (ret < 0) + return ret; + for (i = 0; i < 4; i++) + vals[i] = sign_extend32(le16_to_cpu(raw_vals[i]), 15); + *val_len = 4; + return IIO_VAL_INT_MULTIPLE; + case IIO_CHAN_INFO_SCALE: + /* Table 3-31: 1 quaternion = 2^14 LSB */ + if (size < 2) + return -EINVAL; + vals[0] = 1; + vals[1] = 14; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static bool bno055_is_chan_readable(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + + if (priv->operation_mode != BNO055_OPR_MODE_AMG) + return true; + + switch (chan->type) { + case IIO_GRAVITY: + case IIO_ROT: + return false; + case IIO_ACCEL: + if (chan->channel2 == IIO_MOD_LINEAR_X || + chan->channel2 == IIO_MOD_LINEAR_Y || + chan->channel2 == IIO_MOD_LINEAR_Z) + return false; + return true; + default: + return true; + } +} + +static int _bno055_read_raw_multi(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + if (!bno055_is_chan_readable(indio_dev, chan)) + return -EBUSY; + + switch (chan->type) { + case IIO_MAGN: + case IIO_ACCEL: + case IIO_ANGL_VEL: + case IIO_GRAVITY: + if (size < 2) + return -EINVAL; + *val_len = 2; + return bno055_read_simple_chan(indio_dev, chan, + &vals[0], &vals[1], + mask); + case IIO_TEMP: + *val_len = 1; + return bno055_read_temp_chan(indio_dev, &vals[0]); + case IIO_ROT: + /* + * Rotation is exposed as either a quaternion or three + * Euler angles. + */ + if (chan->channel2 == IIO_MOD_QUATERNION) + return bno055_read_quaternion(indio_dev, chan, + size, vals, + val_len, mask); + if (size < 2) + return -EINVAL; + *val_len = 2; + return bno055_read_simple_chan(indio_dev, chan, + &vals[0], &vals[1], + mask); + default: + return -EINVAL; + } +} + +static int bno055_read_raw_multi(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + int ret; + + mutex_lock(&priv->lock); + ret = _bno055_read_raw_multi(indio_dev, chan, size, + vals, val_len, mask); + mutex_unlock(&priv->lock); + return ret; +} + +static int _bno055_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + + switch (chan->type) { + case IIO_MAGN: + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return bno055_set_regmask(priv, val, val2, + BNO055_MAG_CONFIG_REG, + BNO055_MAG_CONFIG_ODR_MASK, + &bno055_mag_odr); + default: + return -EINVAL; + } + case IIO_ACCEL: + switch (mask) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return bno055_set_regmask(priv, val, val2, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_LPF_MASK, + &bno055_acc_lpf); + + default: + return -EINVAL; + } + case IIO_ANGL_VEL: + switch (mask) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return bno055_set_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_LPF_MASK, + &bno055_gyr_lpf); + case IIO_CHAN_INFO_SCALE: + return bno055_set_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_RANGE_MASK, + &bno055_gyr_scale); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int bno055_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + int ret; + + mutex_lock(&priv->lock); + ret = _bno055_write_raw(iio_dev, chan, val, val2, mask); + mutex_unlock(&priv->lock); + + return ret; +} + +static ssize_t in_accel_range_raw_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int len = 0; + int i; + + if (priv->operation_mode != BNO055_OPR_MODE_AMG) + return sysfs_emit(buf, "%d\n", bno055_acc_range.fusion_vals[0]); + + for (i = 0; i < bno055_acc_range.len; i++) + len += sysfs_emit_at(buf, len, "%d ", bno055_acc_range.vals[i]); + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t fusion_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%d\n", + priv->operation_mode != BNO055_OPR_MODE_AMG); +} + +static ssize_t fusion_enable_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 bno055_priv *priv = iio_priv(indio_dev); + bool en; + int ret; + + if (indio_dev->active_scan_mask && + !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX)) + return -EBUSY; + + ret = kstrtobool(buf, &en); + if (ret) + return -EINVAL; + + if (!en) + return bno055_operation_mode_set(priv, BNO055_OPR_MODE_AMG) ?: len; + + /* + * Coming from AMG means the FMC was off, just switch to fusion but + * don't change anything that doesn't belong to us (i.e let FMC stay off). + * Coming from any other fusion mode means we don't need to do anything. + */ + if (priv->operation_mode == BNO055_OPR_MODE_AMG) + return bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF) ?: len; + + return len; +} + +static ssize_t in_magn_calibration_fast_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%d\n", + priv->operation_mode == BNO055_OPR_MODE_FUSION); +} + +static ssize_t in_magn_calibration_fast_enable_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 bno055_priv *priv = iio_priv(indio_dev); + int ret; + + if (indio_dev->active_scan_mask && + !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX)) + return -EBUSY; + + if (sysfs_streq(buf, "0")) { + if (priv->operation_mode == BNO055_OPR_MODE_FUSION) { + ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF); + if (ret) + return ret; + } + } else { + if (priv->operation_mode == BNO055_OPR_MODE_AMG) + return -EINVAL; + + if (priv->operation_mode != BNO055_OPR_MODE_FUSION) { + ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION); + if (ret) + return ret; + } + } + + return len; +} + +static ssize_t in_accel_range_raw_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int val; + int ret; + + ret = bno055_get_regmask(priv, &val, NULL, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_RANGE_MASK, + &bno055_acc_range); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t in_accel_range_raw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&priv->lock); + ret = bno055_set_regmask(priv, val, 0, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_RANGE_MASK, + &bno055_acc_range); + mutex_unlock(&priv->lock); + + return ret ?: len; +} + +static ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int calib; + int ret; + int val; + + if (priv->operation_mode == BNO055_OPR_MODE_AMG || + (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF && + which == BNO055_CALIB_STAT_MAGN_SHIFT)) { + calib = 0; + } else { + mutex_lock(&priv->lock); + ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val); + mutex_unlock(&priv->lock); + + if (ret) + return -EIO; + + calib = ((val >> which) & GENMASK(1, 0)) + 1; + } + + return sysfs_emit(buf, "%d\n", calib); +} + +static ssize_t serialnumber_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%*ph\n", BNO055_UID_LEN, priv->uid); +} + +static ssize_t calibration_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(kobj_to_dev(kobj))); + u8 data[BNO055_CALDATA_LEN]; + int ret; + + /* + * Calibration data is volatile; reading it in chunks will possibly + * results in inconsistent data. We require the user to read the whole + * blob in a single chunk + */ + if (count < BNO055_CALDATA_LEN || pos) + return -EINVAL; + + mutex_lock(&priv->lock); + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + goto exit_unlock; + + ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, data, + BNO055_CALDATA_LEN); + if (ret) + goto exit_unlock; + + ret = bno055_operation_mode_do_set(priv, priv->operation_mode); + if (ret) + goto exit_unlock; + + memcpy(buf, data, BNO055_CALDATA_LEN); + + ret = BNO055_CALDATA_LEN; +exit_unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static ssize_t sys_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_SYS_SHIFT); +} + +static ssize_t in_accel_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_ACCEL_SHIFT); +} + +static ssize_t in_gyro_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_GYRO_SHIFT); +} + +static ssize_t in_magn_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_MAGN_SHIFT); +} + +static int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + + if (readval) + return regmap_read(priv->regmap, reg, readval); + else + return regmap_write(priv->regmap, reg, writeval); +} + +static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct bno055_priv *priv = file->private_data; + int rev, ver; + char *buf; + int ret; + + ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver); + if (ret) + return ret; + + buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev); + if (!buf) + return -ENOMEM; + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); + kfree(buf); + + return ret; +} + +static const struct file_operations bno055_fw_version_ops = { + .open = simple_open, + .read = bno055_show_fw_version, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void bno055_debugfs_remove(void *_priv) +{ + struct bno055_priv *priv = _priv; + + debugfs_remove(priv->debugfs); + priv->debugfs = NULL; +} + +static void bno055_debugfs_init(struct iio_dev *iio_dev) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + + priv->debugfs = debugfs_create_file("firmware_version", 0400, + iio_get_debugfs_dentry(iio_dev), + priv, &bno055_fw_version_ops); + if (!IS_ERR(priv->debugfs)) + devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, + priv); + if (IS_ERR_OR_NULL(priv->debugfs)) + dev_warn(priv->dev, "failed to setup debugfs"); +} + +static IIO_DEVICE_ATTR_RW(fusion_enable, 0); +static IIO_DEVICE_ATTR_RW(in_magn_calibration_fast_enable, 0); +static IIO_DEVICE_ATTR_RW(in_accel_range_raw, 0); + +static IIO_DEVICE_ATTR_RO(in_accel_range_raw_available, 0); +static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(serialnumber, 0); + +static struct attribute *bno055_attrs[] = { + &iio_dev_attr_in_accel_range_raw_available.dev_attr.attr, + &iio_dev_attr_in_accel_range_raw.dev_attr.attr, + &iio_dev_attr_fusion_enable.dev_attr.attr, + &iio_dev_attr_in_magn_calibration_fast_enable.dev_attr.attr, + &iio_dev_attr_sys_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_in_accel_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_in_gyro_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_in_magn_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_serialnumber.dev_attr.attr, + NULL +}; + +static BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN); + +static struct bin_attribute *bno055_bin_attrs[] = { + &bin_attr_calibration_data, + NULL +}; + +static const struct attribute_group bno055_attrs_group = { + .attrs = bno055_attrs, + .bin_attrs = bno055_bin_attrs, +}; + +static const struct iio_info bno055_info = { + .read_raw_multi = bno055_read_raw_multi, + .read_avail = bno055_read_avail, + .write_raw = bno055_write_raw, + .attrs = &bno055_attrs_group, + .debugfs_reg_access = bno055_debugfs_reg_access, +}; + +/* + * Reads len samples from the HW, stores them in buf starting from buf_idx, + * and applies mask to cull (skip) unneeded samples. + * Updates buf_idx incrementing with the number of stored samples. + * Samples from HW are transferred into buf, then in-place copy on buf is + * performed in order to cull samples that need to be skipped. + * This avoids copies of the first samples until we hit the 1st sample to skip, + * and also avoids having an extra bounce buffer. + * buf must be able to contain len elements in spite of how many samples we are + * going to cull. + */ +static int bno055_scan_xfer(struct bno055_priv *priv, + int start_ch, int len, unsigned long mask, + __le16 *buf, int *buf_idx) +{ + const int base = BNO055_ACC_DATA_X_LSB_REG; + bool quat_in_read = false; + int buf_base = *buf_idx; + __le16 *dst, *src; + int offs_fixup = 0; + int xfer_len = len; + int ret; + int i, n; + + if (!mask) + return 0; + + /* + * All channels are made up 1 16-bit sample, except for quaternion that + * is made up 4 16-bit values. + * For us the quaternion CH is just like 4 regular CHs. + * If our read starts past the quaternion make sure to adjust the + * starting offset; if the quaternion is contained in our scan then make + * sure to adjust the read len. + */ + if (start_ch > BNO055_SCAN_QUATERNION) { + start_ch += 3; + } else if ((start_ch <= BNO055_SCAN_QUATERNION) && + ((start_ch + len) > BNO055_SCAN_QUATERNION)) { + quat_in_read = true; + xfer_len += 3; + } + + ret = regmap_bulk_read(priv->regmap, + base + start_ch * sizeof(__le16), + buf + buf_base, + xfer_len * sizeof(__le16)); + if (ret) + return ret; + + for_each_set_bit(i, &mask, len) { + if (quat_in_read && ((start_ch + i) > BNO055_SCAN_QUATERNION)) + offs_fixup = 3; + + dst = buf + *buf_idx; + src = buf + buf_base + offs_fixup + i; + + n = (start_ch + i == BNO055_SCAN_QUATERNION) ? 4 : 1; + + if (dst != src) + memcpy(dst, src, n * sizeof(__le16)); + + *buf_idx += n; + } + return 0; +} + +static irqreturn_t bno055_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio_dev = pf->indio_dev; + struct bno055_priv *priv = iio_priv(iio_dev); + int xfer_start, start, end, prev_end; + unsigned long mask; + int quat_extra_len; + bool first = true; + int buf_idx = 0; + bool thr_hit; + int ret; + + mutex_lock(&priv->lock); + + /* + * Walk the bitmap and eventually perform several transfers. + * Bitmap ones-fields that are separated by gaps <= xfer_burst_break_thr + * will be included in same transfer. + * Every time the bitmap contains a gap wider than xfer_burst_break_thr + * then we split the transfer, skipping the gap. + */ + for_each_set_bitrange(start, end, iio_dev->active_scan_mask, + iio_dev->masklength) { + /* + * First transfer will start from the beginning of the first + * ones-field in the bitmap + */ + if (first) { + xfer_start = start; + } else { + /* + * We found the next ones-field; check whether to + * include it in * the current transfer or not (i.e. + * let's perform the current * transfer and prepare for + * another one). + */ + + /* + * In case the zeros-gap contains the quaternion bit, + * then its length is actually 4 words instead of 1 + * (i.e. +3 wrt other channels). + */ + quat_extra_len = ((start > BNO055_SCAN_QUATERNION) && + (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0; + + /* If the gap is wider than xfer_burst_break_thr then.. */ + thr_hit = (start - prev_end + quat_extra_len) > + priv->xfer_burst_break_thr; + + /* + * .. transfer all the data up to the gap. Then set the + * next transfer start index at right after the gap + * (i.e. at the start of this ones-field). + */ + if (thr_hit) { + mask = *iio_dev->active_scan_mask >> xfer_start; + ret = bno055_scan_xfer(priv, xfer_start, + prev_end - xfer_start, + mask, priv->buf.chans, &buf_idx); + if (ret) + goto done; + xfer_start = start; + } + } + first = false; + prev_end = end; + } + + /* + * We finished walking the bitmap; no more gaps to check for. Just + * perform the current transfer. + */ + mask = *iio_dev->active_scan_mask >> xfer_start; + ret = bno055_scan_xfer(priv, xfer_start, + prev_end - xfer_start, + mask, priv->buf.chans, &buf_idx); + + if (!ret) + iio_push_to_buffers_with_timestamp(iio_dev, + &priv->buf, pf->timestamp); +done: + mutex_unlock(&priv->lock); + iio_trigger_notify_done(iio_dev->trig); + return IRQ_HANDLED; +} + +static int bno055_buffer_preenable(struct iio_dev *indio_dev) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + const unsigned long fusion_mask = + BIT(BNO055_SCAN_YAW) | + BIT(BNO055_SCAN_ROLL) | + BIT(BNO055_SCAN_PITCH) | + BIT(BNO055_SCAN_QUATERNION) | + BIT(BNO055_SCAN_LIA_X) | + BIT(BNO055_SCAN_LIA_Y) | + BIT(BNO055_SCAN_LIA_Z) | + BIT(BNO055_SCAN_GRAVITY_X) | + BIT(BNO055_SCAN_GRAVITY_Y) | + BIT(BNO055_SCAN_GRAVITY_Z); + + if (priv->operation_mode == BNO055_OPR_MODE_AMG && + bitmap_intersects(indio_dev->active_scan_mask, &fusion_mask, + _BNO055_SCAN_MAX)) + return -EBUSY; + return 0; +} + +static const struct iio_buffer_setup_ops bno055_buffer_setup_ops = { + .preenable = bno055_buffer_preenable, +}; + +int bno055_probe(struct device *dev, struct regmap *regmap, + int xfer_burst_break_thr, bool sw_reset) +{ + const struct firmware *caldata = NULL; + struct bno055_priv *priv; + struct iio_dev *iio_dev; + char *fw_name_buf; + unsigned int val; + int rev, ver; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!iio_dev) + return -ENOMEM; + + iio_dev->name = "bno055"; + priv = iio_priv(iio_dev); + mutex_init(&priv->lock); + priv->regmap = regmap; + priv->dev = dev; + priv->xfer_burst_break_thr = xfer_burst_break_thr; + priv->sw_reset = sw_reset; + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); + + priv->clk = devm_clk_get_optional_enabled(dev, "clk"); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK\n"); + + if (priv->reset_gpio) { + usleep_range(5000, 10000); + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(650000, 750000); + } else if (!sw_reset) { + dev_warn(dev, "No usable reset method; IMU may be unreliable\n"); + } + + ret = regmap_read(priv->regmap, BNO055_CHIP_ID_REG, &val); + if (ret) + return ret; + + if (val != BNO055_CHIP_ID_MAGIC) + dev_warn(dev, "Unrecognized chip ID 0x%x\n", val); + + /* + * In case we haven't a HW reset pin, we can still reset the chip via + * register write. This is probably nonsense in case we can't even + * communicate with the chip or the chip isn't the one we expect (i.e. + * we don't write to unknown chips), so we perform SW reset only after + * chip magic ID check + */ + if (!priv->reset_gpio) { + ret = bno055_system_reset(priv); + if (ret) + return ret; + } + + ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver); + if (ret) + return ret; + + /* + * The stock FW version contains a bug (see comment at the beginning of + * this file) that causes the anglvel scale to be changed depending on + * the chip range setting. We workaround this, but we don't know what + * other FW versions might do. + */ + if (ver != 0x3 || rev != 0x11) + dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected\n"); + + ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG, + priv->uid, BNO055_UID_LEN); + if (ret) + return ret; + + /* Sensor calibration data */ + fw_name_buf = kasprintf(GFP_KERNEL, BNO055_FW_UID_FMT, + BNO055_UID_LEN, priv->uid); + if (!fw_name_buf) + return -ENOMEM; + + ret = request_firmware(&caldata, fw_name_buf, dev); + kfree(fw_name_buf); + if (ret) + ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev); + if (ret) { + dev_notice(dev, "Calibration file load failed. See instruction in kernel Documentation/iio/bno055.rst\n"); + ret = bno055_init(priv, NULL, 0); + } else { + ret = bno055_init(priv, caldata->data, caldata->size); + release_firmware(caldata); + } + if (ret) + return ret; + + priv->operation_mode = BNO055_OPR_MODE_FUSION; + ret = bno055_operation_mode_do_set(priv, priv->operation_mode); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, bno055_uninit, priv); + if (ret) + return ret; + + iio_dev->channels = bno055_channels; + iio_dev->num_channels = ARRAY_SIZE(bno055_channels); + iio_dev->info = &bno055_info; + iio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_triggered_buffer_setup(dev, iio_dev, + iio_pollfunc_store_time, + bno055_trigger_handler, + &bno055_buffer_setup_ops); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, iio_dev); + if (ret) + return ret; + + bno055_debugfs_init(iio_dev); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(bno055_probe, IIO_BNO055); + +MODULE_AUTHOR("Andrea Merello "); +MODULE_DESCRIPTION("Bosch BNO055 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bno055/bno055.h b/drivers/iio/imu/bno055/bno055.h new file mode 100644 index 000000000000..64f9fc95cebc --- /dev/null +++ b/drivers/iio/imu/bno055/bno055.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __BNO055_H__ +#define __BNO055_H__ + +#include +#include + +struct device; +int bno055_probe(struct device *dev, struct regmap *regmap, + int xfer_burst_break_thr, bool sw_reset); +extern const struct regmap_config bno055_regmap_config; + +#endif diff --git a/drivers/iio/imu/bno055/bno055_i2c.c b/drivers/iio/imu/bno055/bno055_i2c.c new file mode 100644 index 000000000000..c1bbc0fe34f9 --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_i2c.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for I2C-interfaced Bosch BNO055 IMU. + * + * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * Written by Andrea Merello + */ + +#include +#include +#include +#include + +#include "bno055.h" + +#define BNO055_I2C_XFER_BURST_BREAK_THRESHOLD 3 + +static int bno055_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &bno055_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "Unable to init register map"); + + return bno055_probe(&client->dev, regmap, + BNO055_I2C_XFER_BURST_BREAK_THRESHOLD, true); +} + +static const struct i2c_device_id bno055_i2c_id[] = { + {"bno055", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, bno055_i2c_id); + +static const struct of_device_id __maybe_unused bno055_i2c_of_match[] = { + { .compatible = "bosch,bno055" }, + { } +}; +MODULE_DEVICE_TABLE(of, bno055_i2c_of_match); + +static struct i2c_driver bno055_driver = { + .driver = { + .name = "bno055-i2c", + .of_match_table = bno055_i2c_of_match, + }, + .probe_new = bno055_i2c_probe, + .id_table = bno055_i2c_id, +}; +module_i2c_driver(bno055_driver); + +MODULE_AUTHOR("Andrea Merello"); +MODULE_DESCRIPTION("Bosch BNO055 I2C interface"); +MODULE_IMPORT_NS(IIO_BNO055); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c new file mode 100644 index 000000000000..57728a568471 --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_ser_core.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Serial line interface for Bosh BNO055 IMU (via serdev). + * This file implements serial communication up to the register read/write + * level. + * + * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * Written by Andrea Merello + * + * This driver is based on + * Plantower PMS7003 particulate matter sensor driver + * Which is + * Copyright (c) Tomasz Duszynski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bno055_ser_trace.h" +#include "bno055.h" + +/* + * Register writes cmd have the following format + * +------+------+-----+-----+----- ... ----+ + * | 0xAA | 0xOO | REG | LEN | payload[LEN] | + * +------+------+-----+-----+----- ... ----+ + * + * Register write responses have the following format + * +------+----------+ + * | 0xEE | ERROCODE | + * +------+----------+ + * + * .. except when writing the SYS_RST bit (i.e. triggering a system reset); in + * case the IMU accepts the command, then it resets without responding. We don't + * handle this (yet) here (so we inform the common bno055 code not to perform + * sw resets - bno055 on serial bus basically requires the hw reset pin). + * + * Register read have the following format + * +------+------+-----+-----+ + * | 0xAA | 0xO1 | REG | LEN | + * +------+------+-----+-----+ + * + * Successful register read response have the following format + * +------+-----+----- ... ----+ + * | 0xBB | LEN | payload[LEN] | + * +------+-----+----- ... ----+ + * + * Failed register read response have the following format + * +------+--------+ + * | 0xEE | ERRCODE| (ERRCODE always > 1) + * +------+--------+ + * + * Error codes are + * 01: OK + * 02: read/write FAIL + * 04: invalid address + * 05: write on RO + * 06: wrong start byte + * 07: bus overrun + * 08: len too high + * 09: len too low + * 10: bus RX byte timeout (timeout is 30mS) + * + * + * **WORKAROUND ALERT** + * + * Serial communication seems very fragile: the BNO055 buffer seems to overflow + * very easy; BNO055 seems able to sink few bytes, then it needs a brief pause. + * On the other hand, it is also picky on timeout: if there is a pause > 30mS in + * between two bytes then the transaction fails (IMU internal RX FSM resets). + * + * BNO055 has been seen also failing to process commands in case we send them + * too close each other (or if it is somehow busy?) + * + * In particular I saw these scenarios: + * 1) If we send 2 bytes per time, then the IMU never(?) overflows. + * 2) If we send 4 bytes per time (i.e. the full header), then the IMU could + * overflow, but it seem to sink all 4 bytes, then it returns error. + * 3) If we send more than 4 bytes, the IMU could overflow, and I saw it sending + * error after 4 bytes are sent; we have troubles in synchronizing again, + * because we are still sending data, and the IMU interprets it as the 1st + * byte of a new command. + * + * While we must avoid case 3, we could send 4 bytes per time and eventually + * retry in case of failure; this seemed convenient for reads (which requires + * TXing exactly 4 bytes), however it has been seen that, depending by the IMU + * settings (e.g. LPF), failures became less or more frequent; in certain IMU + * configurations they are very rare, but in certain others we keeps failing + * even after like 30 retries. + * + * So, we just split TXes in [2-bytes + delay] steps, and still keep an eye on + * the IMU response; in case it overflows (which is now unlikely), we retry. + */ + +/* + * Read operation overhead: + * 4 bytes req + 2byte resp hdr. + * 6 bytes = 60 bit (considering 1start + 1stop bits). + * 60/115200 = ~520uS + about 2500mS delay -> ~3mS + * In 3mS we could read back about 34 bytes that means 17 samples, this means + * that in case of scattered reads in which the gap is 17 samples or less it is + * still convenient to go for a burst. + * We have to take into account also IMU response time - IMU seems to be often + * reasonably quick to respond, but sometimes it seems to be in some "critical + * section" in which it delays handling of serial protocol. Because of this we + * round-up to 22, which is the max number of samples, always bursting indeed. + */ +#define BNO055_SER_XFER_BURST_BREAK_THRESHOLD 22 + +struct bno055_ser_priv { + enum { + CMD_NONE, + CMD_READ, + CMD_WRITE, + } expect_response; + int expected_data_len; + u8 *response_buf; + + /** + * enum cmd_status - represent the status of a command sent to the HW. + * @STATUS_CRIT: The command failed: the serial communication failed. + * @STATUS_OK: The command executed successfully. + * @STATUS_FAIL: The command failed: HW responded with an error. + */ + enum { + STATUS_CRIT = -1, + STATUS_OK = 0, + STATUS_FAIL = 1, + } cmd_status; + + /* + * Protects all the above fields, which are accessed in behalf of both + * the serdev RX callback and the regmap side + */ + struct mutex lock; + + /* Only accessed in serdev RX callback context*/ + struct { + enum { + RX_IDLE, + RX_START, + RX_DATA, + } state; + int databuf_count; + int expected_len; + int type; + } rx; + + /* Never accessed in behalf of serdev RX callback context */ + bool cmd_stale; + + struct completion cmd_complete; + struct serdev_device *serdev; +}; + +static int bno055_ser_send_chunk(struct bno055_ser_priv *priv, const u8 *data, int len) +{ + int ret; + + trace_send_chunk(len, data); + ret = serdev_device_write(priv->serdev, data, len, msecs_to_jiffies(25)); + if (ret < 0) + return ret; + + if (ret < len) + return -EIO; + + return 0; +} + +/* + * Send a read or write command. + * 'data' can be NULL (used in read case). 'len' parameter is always valid; in + * case 'data' is non-NULL then it must match 'data' size. + */ +static int bno055_ser_do_send_cmd(struct bno055_ser_priv *priv, + bool read, int addr, int len, const u8 *data) +{ + u8 hdr[] = {0xAA, read, addr, len}; + int chunk_len; + int ret; + + ret = bno055_ser_send_chunk(priv, hdr, 2); + if (ret) + goto fail; + usleep_range(2000, 3000); + ret = bno055_ser_send_chunk(priv, hdr + 2, 2); + if (ret) + goto fail; + + if (read) + return 0; + + while (len) { + chunk_len = min(len, 2); + usleep_range(2000, 3000); + ret = bno055_ser_send_chunk(priv, data, chunk_len); + if (ret) + goto fail; + data += chunk_len; + len -= chunk_len; + } + + return 0; +fail: + /* waiting more than 30mS should clear the BNO055 internal state */ + usleep_range(40000, 50000); + return ret; +} + +static int bno055_ser_send_cmd(struct bno055_ser_priv *priv, + bool read, int addr, int len, const u8 *data) +{ + const int retry_max = 5; + int retry = retry_max; + int ret = 0; + + /* + * In case previous command was interrupted we still need to wait it to + * complete before we can issue new commands + */ + if (priv->cmd_stale) { + ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete, + msecs_to_jiffies(100)); + if (ret == -ERESTARTSYS) + return -ERESTARTSYS; + + priv->cmd_stale = false; + /* if serial protocol broke, bail out */ + if (priv->cmd_status == STATUS_CRIT) + return -EIO; + } + + /* + * Try to convince the IMU to cooperate.. as explained in the comments + * at the top of this file, the IMU could also refuse the command (i.e. + * it is not ready yet); retry in this case. + */ + do { + mutex_lock(&priv->lock); + priv->expect_response = read ? CMD_READ : CMD_WRITE; + reinit_completion(&priv->cmd_complete); + mutex_unlock(&priv->lock); + + if (retry != retry_max) + trace_cmd_retry(read, addr, retry_max - retry); + ret = bno055_ser_do_send_cmd(priv, read, addr, len, data); + if (ret) + continue; + + ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete, + msecs_to_jiffies(100)); + if (ret == -ERESTARTSYS) { + priv->cmd_stale = true; + return -ERESTARTSYS; + } + + if (!ret) + return -ETIMEDOUT; + + if (priv->cmd_status == STATUS_OK) + return 0; + if (priv->cmd_status == STATUS_CRIT) + return -EIO; + + /* loop in case priv->cmd_status == STATUS_FAIL */ + } while (--retry); + + if (ret < 0) + return ret; + if (priv->cmd_status == STATUS_FAIL) + return -EINVAL; + return 0; +} + +static int bno055_ser_write_reg(void *context, const void *_data, size_t count) +{ + const u8 *data = _data; + struct bno055_ser_priv *priv = context; + + if (count < 2) { + dev_err(&priv->serdev->dev, "Invalid write count %zu", count); + return -EINVAL; + } + + trace_write_reg(data[0], data[1]); + return bno055_ser_send_cmd(priv, 0, data[0], count - 1, data + 1); +} + +static int bno055_ser_read_reg(void *context, + const void *_reg, size_t reg_size, + void *val, size_t val_size) +{ + int ret; + int reg_addr; + const u8 *reg = _reg; + struct bno055_ser_priv *priv = context; + + if (val_size > 128) { + dev_err(&priv->serdev->dev, "Invalid read valsize %zu", val_size); + return -EINVAL; + } + + reg_addr = *reg; + trace_read_reg(reg_addr, val_size); + mutex_lock(&priv->lock); + priv->expected_data_len = val_size; + priv->response_buf = val; + mutex_unlock(&priv->lock); + + ret = bno055_ser_send_cmd(priv, 1, reg_addr, val_size, NULL); + + mutex_lock(&priv->lock); + priv->response_buf = NULL; + mutex_unlock(&priv->lock); + + return ret; +} + +/* + * Handler for received data; this is called from the receiver callback whenever + * it got some packet from the serial bus. The status tells us whether the + * packet is valid (i.e. header ok && received payload len consistent wrt the + * header). It's now our responsibility to check whether this is what we + * expected, of whether we got some unexpected, yet valid, packet. + */ +static void bno055_ser_handle_rx(struct bno055_ser_priv *priv, int status) +{ + mutex_lock(&priv->lock); + switch (priv->expect_response) { + case CMD_NONE: + dev_warn(&priv->serdev->dev, "received unexpected, yet valid, data from sensor"); + mutex_unlock(&priv->lock); + return; + + case CMD_READ: + priv->cmd_status = status; + if (status == STATUS_OK && + priv->rx.databuf_count != priv->expected_data_len) { + /* + * If we got here, then the lower layer serial protocol + * seems consistent with itself; if we got an unexpected + * amount of data then signal it as a non critical error + */ + priv->cmd_status = STATUS_FAIL; + dev_warn(&priv->serdev->dev, + "received an unexpected amount of, yet valid, data from sensor"); + } + break; + + case CMD_WRITE: + priv->cmd_status = status; + break; + } + + priv->expect_response = CMD_NONE; + mutex_unlock(&priv->lock); + complete(&priv->cmd_complete); +} + +/* + * Serdev receiver FSM. This tracks the serial communication and parse the + * header. It pushes packets to bno055_ser_handle_rx(), eventually communicating + * failures (i.e. malformed packets). + * Ideally it doesn't know anything about upper layer (i.e. if this is the + * packet we were really expecting), but since we copies the payload into the + * receiver buffer (that is not valid when i.e. we don't expect data), we + * snoop a bit in the upper layer.. + * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything + * unless we require to AND we don't queue more than one request per time). + */ +static int bno055_ser_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t size) +{ + int status; + struct bno055_ser_priv *priv = serdev_device_get_drvdata(serdev); + int remaining = size; + + if (size == 0) + return 0; + + trace_recv(size, buf); + switch (priv->rx.state) { + case RX_IDLE: + /* + * New packet. + * Check for its 1st byte that identifies the pkt type. + */ + if (buf[0] != 0xEE && buf[0] != 0xBB) { + dev_err(&priv->serdev->dev, + "Invalid packet start %x", buf[0]); + bno055_ser_handle_rx(priv, STATUS_CRIT); + break; + } + priv->rx.type = buf[0]; + priv->rx.state = RX_START; + remaining--; + buf++; + priv->rx.databuf_count = 0; + fallthrough; + + case RX_START: + /* + * Packet RX in progress, we expect either 1-byte len or 1-byte + * status depending by the packet type. + */ + if (remaining == 0) + break; + + if (priv->rx.type == 0xEE) { + if (remaining > 1) { + dev_err(&priv->serdev->dev, "EE pkt. Extra data received"); + status = STATUS_CRIT; + } else { + status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL; + } + bno055_ser_handle_rx(priv, status); + priv->rx.state = RX_IDLE; + break; + + } else { + /*priv->rx.type == 0xBB */ + priv->rx.state = RX_DATA; + priv->rx.expected_len = buf[0]; + remaining--; + buf++; + } + fallthrough; + + case RX_DATA: + /* Header parsed; now receiving packet data payload */ + if (remaining == 0) + break; + + if (priv->rx.databuf_count + remaining > priv->rx.expected_len) { + /* + * This is an inconsistency in serial protocol, we lost + * sync and we don't know how to handle further data + */ + dev_err(&priv->serdev->dev, "BB pkt. Extra data received"); + bno055_ser_handle_rx(priv, STATUS_CRIT); + priv->rx.state = RX_IDLE; + break; + } + + mutex_lock(&priv->lock); + /* + * NULL e.g. when read cmd is stale or when no read cmd is + * actually pending. + */ + if (priv->response_buf && + /* + * Snoop on the upper layer protocol stuff to make sure not + * to write to an invalid memory. Apart for this, let's the + * upper layer manage any inconsistency wrt expected data + * len (as long as the serial protocol is consistent wrt + * itself (i.e. response header is consistent with received + * response len. + */ + (priv->rx.databuf_count + remaining <= priv->expected_data_len)) + memcpy(priv->response_buf + priv->rx.databuf_count, + buf, remaining); + mutex_unlock(&priv->lock); + + priv->rx.databuf_count += remaining; + + /* + * Reached expected len advertised by the IMU for the current + * packet. Pass it to the upper layer (for us it is just valid). + */ + if (priv->rx.databuf_count == priv->rx.expected_len) { + bno055_ser_handle_rx(priv, STATUS_OK); + priv->rx.state = RX_IDLE; + } + break; + } + + return size; +} + +static const struct serdev_device_ops bno055_ser_serdev_ops = { + .receive_buf = bno055_ser_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static struct regmap_bus bno055_ser_regmap_bus = { + .write = bno055_ser_write_reg, + .read = bno055_ser_read_reg, +}; + +static int bno055_ser_probe(struct serdev_device *serdev) +{ + struct bno055_ser_priv *priv; + struct regmap *regmap; + int ret; + + priv = devm_kzalloc(&serdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + serdev_device_set_drvdata(serdev, priv); + priv->serdev = serdev; + mutex_init(&priv->lock); + init_completion(&priv->cmd_complete); + + serdev_device_set_client_ops(serdev, &bno055_ser_serdev_ops); + ret = devm_serdev_device_open(&serdev->dev, serdev); + if (ret) + return ret; + + if (serdev_device_set_baudrate(serdev, 115200) != 115200) { + dev_err(&serdev->dev, "Cannot set required baud rate"); + return -EIO; + } + + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) { + dev_err(&serdev->dev, "Cannot set required parity setting"); + return ret; + } + serdev_device_set_flow_control(serdev, false); + + regmap = devm_regmap_init(&serdev->dev, &bno055_ser_regmap_bus, + priv, &bno055_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&serdev->dev, PTR_ERR(regmap), + "Unable to init register map"); + + return bno055_probe(&serdev->dev, regmap, + BNO055_SER_XFER_BURST_BREAK_THRESHOLD, false); +} + +static const struct of_device_id bno055_ser_of_match[] = { + { .compatible = "bosch,bno055" }, + { } +}; +MODULE_DEVICE_TABLE(of, bno055_ser_of_match); + +static struct serdev_device_driver bno055_ser_driver = { + .driver = { + .name = "bno055-ser", + .of_match_table = bno055_ser_of_match, + }, + .probe = bno055_ser_probe, +}; +module_serdev_device_driver(bno055_ser_driver); + +MODULE_AUTHOR("Andrea Merello "); +MODULE_DESCRIPTION("Bosch BNO055 serdev interface"); +MODULE_IMPORT_NS(IIO_BNO055); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.c b/drivers/iio/imu/bno055/bno055_ser_trace.c new file mode 100644 index 000000000000..48397b66daef --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_ser_trace.c @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: GPL-2.0 + +/* + * bno055_ser Trace Support + * Copyright (C) 2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * + * Based on: + * Device core Trace Support + * Copyright (C) 2021, Intel Corporation + */ + +#define CREATE_TRACE_POINTS +#include "bno055_ser_trace.h" diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.h b/drivers/iio/imu/bno055/bno055_ser_trace.h new file mode 100644 index 000000000000..7d9eae166eec --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_ser_trace.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#if !defined(__BNO055_SERDEV_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __BNO055_SERDEV_TRACE_H__ + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM bno055_ser + +TRACE_EVENT(send_chunk, + TP_PROTO(int len, const u8 *data), + TP_ARGS(len, data), + TP_STRUCT__entry( + __field(int, len) + __dynamic_array(u8, chunk, len) + ), + TP_fast_assign( + __entry->len = len; + memcpy(__get_dynamic_array(chunk), + data, __entry->len); + ), + TP_printk("len: %d, data: = %*ph", + __entry->len, __entry->len, __get_dynamic_array(chunk) + ) +); + +TRACE_EVENT(cmd_retry, + TP_PROTO(bool read, int addr, int retry), + TP_ARGS(read, addr, retry), + TP_STRUCT__entry( + __field(bool, read) + __field(int, addr) + __field(int, retry) + ), + TP_fast_assign( + __entry->read = read; + __entry->addr = addr; + __entry->retry = retry; + ), + TP_printk("%s addr 0x%x retry #%d", + __entry->read ? "read" : "write", + __entry->addr, __entry->retry + ) +); + +TRACE_EVENT(write_reg, + TP_PROTO(u8 addr, u8 value), + TP_ARGS(addr, value), + TP_STRUCT__entry( + __field(u8, addr) + __field(u8, value) + ), + TP_fast_assign( + __entry->addr = addr; + __entry->value = value; + ), + TP_printk("reg 0x%x = 0x%x", + __entry->addr, __entry->value + ) +); + +TRACE_EVENT(read_reg, + TP_PROTO(int addr, size_t len), + TP_ARGS(addr, len), + TP_STRUCT__entry( + __field(int, addr) + __field(size_t, len) + ), + TP_fast_assign( + __entry->addr = addr; + __entry->len = len; + ), + TP_printk("reg 0x%x (len %zu)", + __entry->addr, __entry->len + ) +); + +TRACE_EVENT(recv, + TP_PROTO(size_t len, const unsigned char *buf), + TP_ARGS(len, buf), + TP_STRUCT__entry( + __field(size_t, len) + __dynamic_array(unsigned char, buf, len) + ), + TP_fast_assign( + __entry->len = len; + memcpy(__get_dynamic_array(buf), + buf, __entry->len); + ), + TP_printk("len: %zu, data: = %*ph", + __entry->len, (int)__entry->len, __get_dynamic_array(buf) + ) +); + +#endif /* __BNO055_SERDEV_TRACE_H__ || TRACE_HEADER_MULTI_READ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE bno055_ser_trace + +/* This part must be outside protection */ +#include diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index fefd0b939100..2ed2b3f40c0b 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -12,7 +12,7 @@ config IIO_ST_LSM6DSX Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, ism330dlc, lsm6dso, lsm6dsox, asm330lhh, asm330lhhx, lsm6dsr, - lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, + lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, lsm6dstx, the accelerometer/gyroscope of lsm9ds1 and lsm6dst. To compile this driver as a module, choose M here: the module diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index a86dd29a4738..6b57d47be69e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -32,6 +32,7 @@ #define ST_LSM6DST_DEV_NAME "lsm6dst" #define ST_LSM6DSOP_DEV_NAME "lsm6dsop" #define ST_ASM330LHHX_DEV_NAME "asm330lhhx" +#define ST_LSM6DSTX_DEV_NAME "lsm6dstx" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, @@ -51,6 +52,7 @@ enum st_lsm6dsx_hw_id { ST_LSM6DST_ID, ST_LSM6DSOP_ID, ST_ASM330LHHX_ID, + ST_LSM6DSTX_ID, ST_LSM6DSX_MAX_ID, }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index c7d3730ab1c5..e49f2d120ed3 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -15,7 +15,7 @@ * value of the decimation factor and ODR set for each FIFO data set. * * LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/LSM6DSRX/ISM330DHCX/ - * LSM6DST/LSM6DSOP: + * LSM6DST/LSM6DSOP/LSM6DSTX: * The FIFO buffer can be configured to store data from gyroscope and * accelerometer. Each sample is queued with a tag (1B) indicating data * source (gyroscope, accelerometer, hw timer). diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index b5e4a4113652..f8bbb005718e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -26,7 +26,8 @@ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 4KB * - * - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP: + * - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP/ + * LSM6DSTX: * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416, * 833 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 @@ -791,6 +792,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .hw_id = ST_ASM330LHHX_ID, .name = ST_ASM330LHHX_DEV_NAME, .wai = 0x6b, + }, { + .hw_id = ST_LSM6DSTX_ID, + .name = ST_LSM6DSTX_DEV_NAME, + .wai = 0x6d, }, }, .channels = { diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index 2ea34c0d3a8c..307c8c436862 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -105,6 +105,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,asm330lhhx", .data = (void *)ST_ASM330LHHX_ID, }, + { + .compatible = "st,lsm6dstx", + .data = (void *)ST_LSM6DSTX_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -127,6 +131,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, + { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 6a8883f022a8..6a4eecf4bb05 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -105,6 +105,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,asm330lhhx", .data = (void *)ST_ASM330LHHX_ID, }, + { + .compatible = "st,lsm6dstx", + .data = (void *)ST_LSM6DSTX_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -127,6 +131,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, + { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 583e0e5205a0..151ff3993354 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -134,6 +134,12 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_ETHANOL] = "ethanol", [IIO_MOD_H2] = "h2", [IIO_MOD_O2] = "o2", + [IIO_MOD_LINEAR_X] = "linear_x", + [IIO_MOD_LINEAR_Y] = "linear_y", + [IIO_MOD_LINEAR_Z] = "linear_z", + [IIO_MOD_PITCH] = "pitch", + [IIO_MOD_YAW] = "yaw", + [IIO_MOD_ROLL] = "roll", }; /* relies on pairs of these shared then separate */ @@ -1570,7 +1576,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) ret = -ENOMEM; goto error_clear_attrs; } - /* Copy across original attributes */ + /* Copy across original attributes, and point to original binary attributes */ if (indio_dev->info->attrs) { memcpy(iio_dev_opaque->chan_attr_group.attrs, indio_dev->info->attrs->attrs, @@ -1578,6 +1584,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) *attrcount_orig); iio_dev_opaque->chan_attr_group.is_visible = indio_dev->info->attrs->is_visible; + iio_dev_opaque->chan_attr_group.bin_attrs = + indio_dev->info->attrs->bin_attrs; } attrn = attrcount_orig; /* Add all elements from the list. */ diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index 3d4cc1180b6a..c737d3e193ae 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -325,7 +325,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap) } EXPORT_SYMBOL_NS(st_uvis25_probe, IIO_UVIS25); -static int __maybe_unused st_uvis25_suspend(struct device *dev) +static int st_uvis25_suspend(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct st_uvis25_hw *hw = iio_priv(iio_dev); @@ -334,7 +334,7 @@ static int __maybe_unused st_uvis25_suspend(struct device *dev) ST_UVIS25_REG_ODR_MASK, 0); } -static int __maybe_unused st_uvis25_resume(struct device *dev) +static int st_uvis25_resume(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct st_uvis25_hw *hw = iio_priv(iio_dev); @@ -346,10 +346,7 @@ static int __maybe_unused st_uvis25_resume(struct device *dev) return 0; } -const struct dev_pm_ops st_uvis25_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume) -}; -EXPORT_SYMBOL_NS(st_uvis25_pm_ops, IIO_UVIS25); +EXPORT_NS_SIMPLE_DEV_PM_OPS(st_uvis25_pm_ops, st_uvis25_suspend, st_uvis25_resume, IIO_UVIS25); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver"); diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c index b06d09af28a3..c982b0b255cf 100644 --- a/drivers/iio/light/st_uvis25_i2c.c +++ b/drivers/iio/light/st_uvis25_i2c.c @@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table); static struct i2c_driver st_uvis25_driver = { .driver = { .name = "st_uvis25_i2c", - .pm = &st_uvis25_pm_ops, + .pm = pm_sleep_ptr(&st_uvis25_pm_ops), .of_match_table = st_uvis25_i2c_of_match, }, .probe = st_uvis25_i2c_probe, diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c index 3a4dc6d7180c..86a232320d7d 100644 --- a/drivers/iio/light/st_uvis25_spi.c +++ b/drivers/iio/light/st_uvis25_spi.c @@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table); static struct spi_driver st_uvis25_driver = { .driver = { .name = "st_uvis25_spi", - .pm = &st_uvis25_pm_ops, + .pm = pm_sleep_ptr(&st_uvis25_pm_ops), .of_match_table = st_uvis25_spi_of_match, }, .probe = st_uvis25_spi_probe, diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 0ff756cea63a..c9453389e4f7 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -17,14 +17,14 @@ config ABP060MG will be called abp060mg. config BMP280 - tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver" + tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor I2C driver" depends on (I2C || SPI_MASTER) select REGMAP select BMP280_I2C if (I2C) select BMP280_SPI if (SPI_MASTER) help - Say yes here to build support for Bosch Sensortec BMP180 and BMP280 - pressure and temperature sensors. Also supports the BME280 with + Say yes here to build support for Bosch Sensortec BMP180, BMP280 and + BMP380 pressure and temperature sensors. Also supports the BME280 with an additional humidity sensor channel. To compile this driver as a module, choose M here: the core module diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index fe7aa81e7cc9..c0aff78489b4 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -9,13 +9,22 @@ * Driver for Bosch Sensortec BMP180 and BMP280 digital pressure sensor. * * Datasheet: - * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf - * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf - * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf + * https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf + * + * Notice: + * The link to the bmp180 datasheet points to an outdated version missing these changes: + * - Changed document referral from ANP015 to BST-MPS-AN004-00 on page 26 + * - Updated equation for B3 param on section 3.5 to ((((long)AC1 * 4 + X3) << oss) + 2) / 4 + * - Updated RoHS directive to 2011/65/EU effective 8 June 2011 on page 26 */ #define pr_fmt(fmt) "bmp280: " fmt +#include +#include #include #include #include @@ -30,6 +39,8 @@ #include #include +#include + #include "bmp280.h" /* @@ -74,12 +85,51 @@ struct bmp280_calib { s8 H6; }; +/* See datasheet Section 3.11.1. */ +struct bmp380_calib { + u16 T1; + u16 T2; + s8 T3; + s16 P1; + s16 P2; + s8 P3; + s8 P4; + u16 P5; + u16 P6; + s8 P7; + s8 P8; + s16 P9; + s8 P10; + s8 P11; +}; + static const char *const bmp280_supply_names[] = { "vddd", "vdda" }; #define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) +enum bmp380_odr { + BMP380_ODR_200HZ, + BMP380_ODR_100HZ, + BMP380_ODR_50HZ, + BMP380_ODR_25HZ, + BMP380_ODR_12_5HZ, + BMP380_ODR_6_25HZ, + BMP380_ODR_3_125HZ, + BMP380_ODR_1_5625HZ, + BMP380_ODR_0_78HZ, + BMP380_ODR_0_39HZ, + BMP380_ODR_0_2HZ, + BMP380_ODR_0_1HZ, + BMP380_ODR_0_05HZ, + BMP380_ODR_0_02HZ, + BMP380_ODR_0_01HZ, + BMP380_ODR_0_006HZ, + BMP380_ODR_0_003HZ, + BMP380_ODR_0_0015HZ, +}; + struct bmp280_data { struct device *dev; struct mutex lock; @@ -90,6 +140,7 @@ struct bmp280_data { union { struct bmp180_calib bmp180; struct bmp280_calib bmp280; + struct bmp380_calib bmp380; } calib; struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; unsigned int start_up_time; /* in microseconds */ @@ -98,36 +149,99 @@ struct bmp280_data { u8 oversampling_press; u8 oversampling_temp; u8 oversampling_humid; + u8 iir_filter_coeff; + + /* + * BMP380 devices introduce sampling frequency configuration. See + * datasheet sections 3.3.3. and 4.3.19 for more details. + * + * BMx280 devices allowed indirect configuration of sampling frequency + * changing the t_standby duration between measurements, as detailed on + * section 3.6.3 of the datasheet. + */ + int sampling_freq; /* * Carryover value from temperature conversion, used in pressure * calculation. */ s32 t_fine; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + union { + /* Sensor data buffer */ + u8 buf[3]; + /* Calibration data buffers */ + __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; + __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; + /* Miscellaneous, endianess-aware data buffers */ + __le16 le16; + __be16 be16; + } __aligned(IIO_DMA_MINALIGN); }; struct bmp280_chip_info { + unsigned int id_reg; + + const struct iio_chan_spec *channels; + int num_channels; + unsigned int start_up_time; + const int *oversampling_temp_avail; int num_oversampling_temp_avail; + int oversampling_temp_default; const int *oversampling_press_avail; int num_oversampling_press_avail; + int oversampling_press_default; const int *oversampling_humid_avail; int num_oversampling_humid_avail; + int oversampling_humid_default; + + const int *iir_filter_coeffs_avail; + int num_iir_filter_coeffs_avail; + int iir_filter_coeff_default; + + const int (*sampling_freq_avail)[2]; + int num_sampling_freq_avail; + int sampling_freq_default; int (*chip_config)(struct bmp280_data *); int (*read_temp)(struct bmp280_data *, int *); int (*read_press)(struct bmp280_data *, int *, int *); int (*read_humid)(struct bmp280_data *, int *, int *); + int (*read_calib)(struct bmp280_data *); }; /* * These enums are used for indexing into the array of compensation * parameters for BMP280. */ -enum { T1, T2, T3 }; -enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 }; +enum { T1, T2, T3, P1, P2, P3, P4, P5, P6, P7, P8, P9 }; + +enum { + /* Temperature calib indexes */ + BMP380_T1 = 0, + BMP380_T2 = 2, + BMP380_T3 = 4, + /* Pressure calib indexes */ + BMP380_P1 = 5, + BMP380_P2 = 7, + BMP380_P3 = 9, + BMP380_P4 = 10, + BMP380_P5 = 11, + BMP380_P6 = 13, + BMP380_P7 = 15, + BMP380_P8 = 16, + BMP380_P9 = 17, + BMP380_P10 = 19, + BMP380_P11 = 20, +}; static const struct iio_chan_spec bmp280_channels[] = { { @@ -147,56 +261,81 @@ static const struct iio_chan_spec bmp280_channels[] = { }, }; -static int bmp280_read_calib(struct bmp280_data *data, - struct bmp280_calib *calib, - unsigned int chip) +static const struct iio_chan_spec bmp380_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + }, +}; + +static int bmp280_read_calib(struct bmp280_data *data) { + struct bmp280_calib *calib = &data->calib.bmp280; int ret; - unsigned int tmp; - __le16 l16; - __be16 b16; - struct device *dev = data->dev; - __le16 t_buf[BMP280_COMP_TEMP_REG_COUNT / 2]; - __le16 p_buf[BMP280_COMP_PRESS_REG_COUNT / 2]; - /* Read temperature calibration values. */ + + /* Read temperature and pressure calibration values. */ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START, - t_buf, BMP280_COMP_TEMP_REG_COUNT); + data->bmp280_cal_buf, sizeof(data->bmp280_cal_buf)); if (ret < 0) { dev_err(data->dev, - "failed to read temperature calibration parameters\n"); + "failed to read temperature and pressure calibration parameters\n"); return ret; } - /* Toss the temperature calibration data into the entropy pool */ - add_device_randomness(t_buf, sizeof(t_buf)); + /* Toss the temperature and pressure calibration data into the entropy pool */ + add_device_randomness(data->bmp280_cal_buf, sizeof(data->bmp280_cal_buf)); - calib->T1 = le16_to_cpu(t_buf[T1]); - calib->T2 = le16_to_cpu(t_buf[T2]); - calib->T3 = le16_to_cpu(t_buf[T3]); + /* Parse temperature calibration values. */ + calib->T1 = le16_to_cpu(data->bmp280_cal_buf[T1]); + calib->T2 = le16_to_cpu(data->bmp280_cal_buf[T2]); + calib->T3 = le16_to_cpu(data->bmp280_cal_buf[T3]); - /* Read pressure calibration values. */ - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START, - p_buf, BMP280_COMP_PRESS_REG_COUNT); - if (ret < 0) { - dev_err(data->dev, - "failed to read pressure calibration parameters\n"); + /* Parse pressure calibration values. */ + calib->P1 = le16_to_cpu(data->bmp280_cal_buf[P1]); + calib->P2 = le16_to_cpu(data->bmp280_cal_buf[P2]); + calib->P3 = le16_to_cpu(data->bmp280_cal_buf[P3]); + calib->P4 = le16_to_cpu(data->bmp280_cal_buf[P4]); + calib->P5 = le16_to_cpu(data->bmp280_cal_buf[P5]); + calib->P6 = le16_to_cpu(data->bmp280_cal_buf[P6]); + calib->P7 = le16_to_cpu(data->bmp280_cal_buf[P7]); + calib->P8 = le16_to_cpu(data->bmp280_cal_buf[P8]); + calib->P9 = le16_to_cpu(data->bmp280_cal_buf[P9]); + + return 0; +} + +static int bme280_read_calib(struct bmp280_data *data) +{ + struct bmp280_calib *calib = &data->calib.bmp280; + struct device *dev = data->dev; + unsigned int tmp; + int ret; + + /* Load shared calibration params with bmp280 first */ + ret = bmp280_read_calib(data); + if (ret < 0) { + dev_err(dev, "failed to read common bmp280 calibration parameters\n"); return ret; } - /* Toss the pressure calibration data into the entropy pool */ - add_device_randomness(p_buf, sizeof(p_buf)); - - calib->P1 = le16_to_cpu(p_buf[P1]); - calib->P2 = le16_to_cpu(p_buf[P2]); - calib->P3 = le16_to_cpu(p_buf[P3]); - calib->P4 = le16_to_cpu(p_buf[P4]); - calib->P5 = le16_to_cpu(p_buf[P5]); - calib->P6 = le16_to_cpu(p_buf[P6]); - calib->P7 = le16_to_cpu(p_buf[P7]); - calib->P8 = le16_to_cpu(p_buf[P8]); - calib->P9 = le16_to_cpu(p_buf[P9]); - /* * Read humidity calibration values. * Due to some odd register addressing we cannot just @@ -204,8 +343,6 @@ static int bmp280_read_calib(struct bmp280_data *data, * value separately and sometimes do some bit shifting... * Humidity data is only available on BME280. */ - if (chip != BME280_CHIP_ID) - return 0; ret = regmap_read(data->regmap, BMP280_REG_COMP_H1, &tmp); if (ret < 0) { @@ -214,12 +351,13 @@ static int bmp280_read_calib(struct bmp280_data *data, } calib->H1 = tmp; - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &l16, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, + &data->le16, sizeof(data->le16)); if (ret < 0) { dev_err(dev, "failed to read H2 comp value\n"); return ret; } - calib->H2 = sign_extend32(le16_to_cpu(l16), 15); + calib->H2 = sign_extend32(le16_to_cpu(data->le16), 15); ret = regmap_read(data->regmap, BMP280_REG_COMP_H3, &tmp); if (ret < 0) { @@ -228,20 +366,22 @@ static int bmp280_read_calib(struct bmp280_data *data, } calib->H3 = tmp; - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &b16, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, + &data->be16, sizeof(data->be16)); if (ret < 0) { dev_err(dev, "failed to read H4 comp value\n"); return ret; } - calib->H4 = sign_extend32(((be16_to_cpu(b16) >> 4) & 0xff0) | - (be16_to_cpu(b16) & 0xf), 11); + calib->H4 = sign_extend32(((be16_to_cpu(data->be16) >> 4) & 0xff0) | + (be16_to_cpu(data->be16) & 0xf), 11); - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &l16, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, + &data->le16, sizeof(data->le16)); if (ret < 0) { dev_err(dev, "failed to read H5 comp value\n"); return ret; } - calib->H5 = sign_extend32(((le16_to_cpu(l16) >> 4) & 0xfff), 11); + calib->H5 = sign_extend32(FIELD_GET(BMP280_COMP_H5_MASK, le16_to_cpu(data->le16)), 11); ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp); if (ret < 0) { @@ -261,8 +401,8 @@ static int bmp280_read_calib(struct bmp280_data *data, static u32 bmp280_compensate_humidity(struct bmp280_data *data, s32 adc_humidity) { - s32 var; struct bmp280_calib *calib = &data->calib.bmp280; + s32 var; var = ((s32)data->t_fine) - (s32)76800; var = ((((adc_humidity << 14) - (calib->H4 << 20) - (calib->H5 * var)) @@ -286,8 +426,8 @@ static u32 bmp280_compensate_humidity(struct bmp280_data *data, static s32 bmp280_compensate_temp(struct bmp280_data *data, s32 adc_temp) { - s32 var1, var2; struct bmp280_calib *calib = &data->calib.bmp280; + s32 var1, var2; var1 = (((adc_temp >> 3) - ((s32)calib->T1 << 1)) * ((s32)calib->T2)) >> 11; @@ -309,8 +449,8 @@ static s32 bmp280_compensate_temp(struct bmp280_data *data, static u32 bmp280_compensate_press(struct bmp280_data *data, s32 adc_press) { - s64 var1, var2, p; struct bmp280_calib *calib = &data->calib.bmp280; + s64 var1, var2, p; var1 = ((s64)data->t_fine) - 128000; var2 = var1 * var1 * (s64)calib->P6; @@ -335,17 +475,17 @@ static u32 bmp280_compensate_press(struct bmp280_data *data, static int bmp280_read_temp(struct bmp280_data *data, int *val) { - int ret; - __be32 tmp = 0; s32 adc_temp, comp_temp; + int ret; - ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, &tmp, 3); + ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, + data->buf, sizeof(data->buf)); if (ret < 0) { dev_err(data->dev, "failed to read temperature\n"); return ret; } - adc_temp = be32_to_cpu(tmp) >> 12; + adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf)); if (adc_temp == BMP280_TEMP_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading temperature skipped\n"); @@ -368,23 +508,23 @@ static int bmp280_read_temp(struct bmp280_data *data, static int bmp280_read_press(struct bmp280_data *data, int *val, int *val2) { - int ret; - __be32 tmp = 0; - s32 adc_press; u32 comp_press; + s32 adc_press; + int ret; /* Read and compensate temperature so we get a reading of t_fine. */ ret = bmp280_read_temp(data, NULL); if (ret < 0) return ret; - ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, &tmp, 3); + ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, + data->buf, sizeof(data->buf)); if (ret < 0) { dev_err(data->dev, "failed to read pressure\n"); return ret; } - adc_press = be32_to_cpu(tmp) >> 12; + adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf)); if (adc_press == BMP280_PRESS_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading pressure skipped\n"); @@ -400,23 +540,23 @@ static int bmp280_read_press(struct bmp280_data *data, static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) { - __be16 tmp; - int ret; - s32 adc_humidity; u32 comp_humidity; + s32 adc_humidity; + int ret; /* Read and compensate temperature so we get a reading of t_fine. */ ret = bmp280_read_temp(data, NULL); if (ret < 0) return ret; - ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB, &tmp, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB, + &data->be16, sizeof(data->be16)); if (ret < 0) { dev_err(data->dev, "failed to read humidity\n"); return ret; } - adc_humidity = be16_to_cpu(tmp); + adc_humidity = be16_to_cpu(data->be16); if (adc_humidity == BMP280_HUMIDITY_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading humidity skipped\n"); @@ -433,8 +573,8 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - int ret; struct bmp280_data *data = iio_priv(indio_dev); + int ret; pm_runtime_get_sync(data->dev); mutex_lock(&data->lock); @@ -475,6 +615,25 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, break; } break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!data->chip_info->sampling_freq_avail) { + ret = -EINVAL; + break; + } + + *val = data->chip_info->sampling_freq_avail[data->sampling_freq][0]; + *val2 = data->chip_info->sampling_freq_avail[data->sampling_freq][1]; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + if (!data->chip_info->iir_filter_coeffs_avail) { + ret = -EINVAL; + break; + } + + *val = (1 << data->iir_filter_coeff) - 1; + ret = IIO_VAL_INT; + break; default: ret = -EINVAL; break; @@ -490,15 +649,23 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data, int val) { - int i; const int *avail = data->chip_info->oversampling_humid_avail; const int n = data->chip_info->num_oversampling_humid_avail; + int ret, prev; + int i; for (i = 0; i < n; i++) { if (avail[i] == val) { + prev = data->oversampling_humid; data->oversampling_humid = ilog2(val); - return data->chip_info->chip_config(data); + ret = data->chip_info->chip_config(data); + if (ret) { + data->oversampling_humid = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; } } return -EINVAL; @@ -507,15 +674,23 @@ static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data, static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data, int val) { - int i; const int *avail = data->chip_info->oversampling_temp_avail; const int n = data->chip_info->num_oversampling_temp_avail; + int ret, prev; + int i; for (i = 0; i < n; i++) { if (avail[i] == val) { + prev = data->oversampling_temp; data->oversampling_temp = ilog2(val); - return data->chip_info->chip_config(data); + ret = data->chip_info->chip_config(data); + if (ret) { + data->oversampling_temp = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; } } return -EINVAL; @@ -524,15 +699,73 @@ static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data, static int bmp280_write_oversampling_ratio_press(struct bmp280_data *data, int val) { - int i; const int *avail = data->chip_info->oversampling_press_avail; const int n = data->chip_info->num_oversampling_press_avail; + int ret, prev; + int i; for (i = 0; i < n; i++) { if (avail[i] == val) { + prev = data->oversampling_press; data->oversampling_press = ilog2(val); - return data->chip_info->chip_config(data); + ret = data->chip_info->chip_config(data); + if (ret) { + data->oversampling_press = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; + } + } + return -EINVAL; +} + +static int bmp280_write_sampling_frequency(struct bmp280_data *data, + int val, int val2) +{ + const int (*avail)[2] = data->chip_info->sampling_freq_avail; + const int n = data->chip_info->num_sampling_freq_avail; + int ret, prev; + int i; + + for (i = 0; i < n; i++) { + if (avail[i][0] == val && avail[i][1] == val2) { + prev = data->sampling_freq; + data->sampling_freq = i; + + ret = data->chip_info->chip_config(data); + if (ret) { + data->sampling_freq = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; + } + } + return -EINVAL; +} + +static int bmp280_write_iir_filter_coeffs(struct bmp280_data *data, int val) +{ + const int *avail = data->chip_info->iir_filter_coeffs_avail; + const int n = data->chip_info->num_iir_filter_coeffs_avail; + int ret, prev; + int i; + + for (i = 0; i < n; i++) { + if (avail[i] - 1 == val) { + prev = data->iir_filter_coeff; + data->iir_filter_coeff = i; + + ret = data->chip_info->chip_config(data); + if (ret) { + data->iir_filter_coeff = prev; + data->chip_info->chip_config(data); + return ret; + + } + return 0; } } return -EINVAL; @@ -542,9 +775,15 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - int ret = 0; struct bmp280_data *data = iio_priv(indio_dev); + int ret = 0; + /* + * Helper functions to update sensor running configuration. + * If an error happens applying new settings, will try restore + * previous parameters to ensure the sensor is left in a known + * working configuration. + */ switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: pm_runtime_get_sync(data->dev); @@ -567,6 +806,22 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, pm_runtime_mark_last_busy(data->dev); pm_runtime_put_autosuspend(data->dev); break; + case IIO_CHAN_INFO_SAMP_FREQ: + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + ret = bmp280_write_sampling_frequency(data, val, val2); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + break; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + ret = bmp280_write_iir_filter_coeffs(data, val); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + break; default: return -EINVAL; } @@ -597,6 +852,17 @@ static int bmp280_read_avail(struct iio_dev *indio_dev, } *type = IIO_VAL_INT; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)data->chip_info->sampling_freq_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = data->chip_info->num_sampling_freq_avail; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = data->chip_info->iir_filter_coeffs_avail; + *type = IIO_VAL_INT; + *length = data->chip_info->num_iir_filter_coeffs_avail; + return IIO_AVAIL_LIST; default: return -EINVAL; } @@ -610,9 +876,9 @@ static const struct iio_info bmp280_info = { static int bmp280_chip_config(struct bmp280_data *data) { + u8 osrs = FIELD_PREP(BMP280_OSRS_TEMP_MASK, data->oversampling_temp + 1) | + FIELD_PREP(BMP280_OSRS_PRESS_MASK, data->oversampling_press + 1); int ret; - u8 osrs = BMP280_OSRS_TEMP_X(data->oversampling_temp + 1) | - BMP280_OSRS_PRESS_X(data->oversampling_press + 1); ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS, BMP280_OSRS_TEMP_MASK | @@ -640,21 +906,39 @@ static int bmp280_chip_config(struct bmp280_data *data) static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; static const struct bmp280_chip_info bmp280_chip_info = { + .id_reg = BMP280_REG_ID, + .start_up_time = 2000, + .channels = bmp280_channels, + .num_channels = 2, + .oversampling_temp_avail = bmp280_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), + /* + * Oversampling config values on BMx280 have one additional setting + * that other generations of the family don't: + * The value 0 means the measurement is bypassed instead of + * oversampling set to x1. + * + * To account for this difference, and preserve the same common + * config logic, this is handled later on chip_config callback + * incrementing one unit the oversampling setting. + */ + .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1, .oversampling_press_avail = bmp280_oversampling_avail, .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1, .chip_config = bmp280_chip_config, .read_temp = bmp280_read_temp, .read_press = bmp280_read_press, + .read_calib = bmp280_read_calib, }; static int bme280_chip_config(struct bmp280_data *data) { + u8 osrs = FIELD_PREP(BMP280_OSRS_HUMIDITY_MASK, data->oversampling_humid + 1); int ret; - u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1); /* * Oversampling of humidity must be set before oversampling of @@ -670,27 +954,405 @@ static int bme280_chip_config(struct bmp280_data *data) } static const struct bmp280_chip_info bme280_chip_info = { + .id_reg = BMP280_REG_ID, + .start_up_time = 2000, + .channels = bmp280_channels, + .num_channels = 3, + .oversampling_temp_avail = bmp280_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1, .oversampling_press_avail = bmp280_oversampling_avail, .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1, .oversampling_humid_avail = bmp280_oversampling_avail, .num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_humid_default = BMP280_OSRS_HUMIDITY_16X - 1, .chip_config = bme280_chip_config, .read_temp = bmp280_read_temp, .read_press = bmp280_read_press, .read_humid = bmp280_read_humid, + .read_calib = bme280_read_calib, +}; + +/* + * Helper function to send a command to BMP3XX sensors. + * + * Sensor processes commands written to the CMD register and signals + * execution result through "cmd_rdy" and "cmd_error" flags available on + * STATUS and ERROR registers. + */ +static int bmp380_cmd(struct bmp280_data *data, u8 cmd) +{ + unsigned int reg; + int ret; + + /* Check if device is ready to process a command */ + ret = regmap_read(data->regmap, BMP380_REG_STATUS, ®); + if (ret) { + dev_err(data->dev, "failed to read error register\n"); + return ret; + } + if (!(reg & BMP380_STATUS_CMD_RDY_MASK)) { + dev_err(data->dev, "device is not ready to accept commands\n"); + return -EBUSY; + } + + /* Send command to process */ + ret = regmap_write(data->regmap, BMP380_REG_CMD, cmd); + if (ret) { + dev_err(data->dev, "failed to send command to device\n"); + return ret; + } + /* Wait for 2ms for command to be processed */ + usleep_range(data->start_up_time, data->start_up_time + 100); + /* Check for command processing error */ + ret = regmap_read(data->regmap, BMP380_REG_ERROR, ®); + if (ret) { + dev_err(data->dev, "error reading ERROR reg\n"); + return ret; + } + if (reg & BMP380_ERR_CMD_MASK) { + dev_err(data->dev, "error processing command 0x%X\n", cmd); + return -EINVAL; + } + + return 0; +} + +/* + * Returns temperature in Celsius dregrees, resolution is 0.01º C. Output value of + * "5123" equals 51.2º C. t_fine carries fine temperature as global value. + * + * Taken from datasheet, Section Appendix 9, "Compensation formula" and repo + * https://github.com/BoschSensortec/BMP3-Sensor-API. + */ +static s32 bmp380_compensate_temp(struct bmp280_data *data, u32 adc_temp) +{ + s64 var1, var2, var3, var4, var5, var6, comp_temp; + struct bmp380_calib *calib = &data->calib.bmp380; + + var1 = ((s64) adc_temp) - (((s64) calib->T1) << 8); + var2 = var1 * ((s64) calib->T2); + var3 = var1 * var1; + var4 = var3 * ((s64) calib->T3); + var5 = (var2 << 18) + var4; + var6 = var5 >> 32; + data->t_fine = (s32) var6; + comp_temp = (var6 * 25) >> 14; + + comp_temp = clamp_val(comp_temp, BMP380_MIN_TEMP, BMP380_MAX_TEMP); + return (s32) comp_temp; +} + +/* + * Returns pressure in Pa as an unsigned 32 bit integer in fractional Pascal. + * Output value of "9528709" represents 9528709/100 = 95287.09 Pa = 952.8709 hPa. + * + * Taken from datasheet, Section 9.3. "Pressure compensation" and repository + * https://github.com/BoschSensortec/BMP3-Sensor-API. + */ +static u32 bmp380_compensate_press(struct bmp280_data *data, u32 adc_press) +{ + s64 var1, var2, var3, var4, var5, var6, offset, sensitivity; + struct bmp380_calib *calib = &data->calib.bmp380; + u32 comp_press; + + var1 = (s64)data->t_fine * (s64)data->t_fine; + var2 = var1 >> 6; + var3 = (var2 * ((s64) data->t_fine)) >> 8; + var4 = ((s64)calib->P8 * var3) >> 5; + var5 = ((s64)calib->P7 * var1) << 4; + var6 = ((s64)calib->P6 * (s64)data->t_fine) << 22; + offset = ((s64)calib->P5 << 47) + var4 + var5 + var6; + var2 = ((s64)calib->P4 * var3) >> 5; + var4 = ((s64)calib->P3 * var1) << 2; + var5 = ((s64)calib->P2 - ((s64)1 << 14)) * + ((s64)data->t_fine << 21); + sensitivity = (((s64) calib->P1 - ((s64) 1 << 14)) << 46) + + var2 + var4 + var5; + var1 = (sensitivity >> 24) * (s64)adc_press; + var2 = (s64)calib->P10 * (s64)data->t_fine; + var3 = var2 + ((s64)calib->P9 << 16); + var4 = (var3 * (s64)adc_press) >> 13; + + /* + * Dividing by 10 followed by multiplying by 10 to avoid + * possible overflow caused by (uncomp_data->pressure * partial_data4). + */ + var5 = ((s64)adc_press * div_s64(var4, 10)) >> 9; + var5 *= 10; + var6 = (s64)adc_press * (s64)adc_press; + var2 = ((s64)calib->P11 * var6) >> 16; + var3 = (var2 * (s64)adc_press) >> 7; + var4 = (offset >> 2) + var1 + var5 + var3; + comp_press = ((u64)var4 * 25) >> 40; + + comp_press = clamp_val(comp_press, BMP380_MIN_PRES, BMP380_MAX_PRES); + return comp_press; +} + +static int bmp380_read_temp(struct bmp280_data *data, int *val) +{ + s32 comp_temp; + u32 adc_temp; + int ret; + + ret = regmap_bulk_read(data->regmap, BMP380_REG_TEMP_XLSB, + data->buf, sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read temperature\n"); + return ret; + } + + adc_temp = get_unaligned_le24(data->buf); + if (adc_temp == BMP380_TEMP_SKIPPED) { + dev_err(data->dev, "reading temperature skipped\n"); + return -EIO; + } + comp_temp = bmp380_compensate_temp(data, adc_temp); + + /* + * Val might be NULL if we're called by the read_press routine, + * who only cares about the carry over t_fine value. + */ + if (val) { + /* IIO reports temperatures in milli Celsius */ + *val = comp_temp * 10; + return IIO_VAL_INT; + } + + return 0; +} + +static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2) +{ + s32 comp_press; + u32 adc_press; + int ret; + + /* Read and compensate for temperature so we get a reading of t_fine */ + ret = bmp380_read_temp(data, NULL); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, BMP380_REG_PRESS_XLSB, + data->buf, sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read pressure\n"); + return ret; + } + + adc_press = get_unaligned_le24(data->buf); + if (adc_press == BMP380_PRESS_SKIPPED) { + dev_err(data->dev, "reading pressure skipped\n"); + return -EIO; + } + comp_press = bmp380_compensate_press(data, adc_press); + + *val = comp_press; + /* Compensated pressure is in cPa (centipascals) */ + *val2 = 100000; + + return IIO_VAL_FRACTIONAL; +} + +static int bmp380_read_calib(struct bmp280_data *data) +{ + struct bmp380_calib *calib = &data->calib.bmp380; + int ret; + + /* Read temperature and pressure calibration data */ + ret = regmap_bulk_read(data->regmap, BMP380_REG_CALIB_TEMP_START, + data->bmp380_cal_buf, sizeof(data->bmp380_cal_buf)); + if (ret) { + dev_err(data->dev, + "failed to read temperature calibration parameters\n"); + return ret; + } + + /* Toss the temperature calibration data into the entropy pool */ + add_device_randomness(data->bmp380_cal_buf, sizeof(data->bmp380_cal_buf)); + + /* Parse calibration values */ + calib->T1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T1]); + calib->T2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T2]); + calib->T3 = data->bmp380_cal_buf[BMP380_T3]; + calib->P1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P1]); + calib->P2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P2]); + calib->P3 = data->bmp380_cal_buf[BMP380_P3]; + calib->P4 = data->bmp380_cal_buf[BMP380_P4]; + calib->P5 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P5]); + calib->P6 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P6]); + calib->P7 = data->bmp380_cal_buf[BMP380_P7]; + calib->P8 = data->bmp380_cal_buf[BMP380_P8]; + calib->P9 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P9]); + calib->P10 = data->bmp380_cal_buf[BMP380_P10]; + calib->P11 = data->bmp380_cal_buf[BMP380_P11]; + + return 0; +} + +static const int bmp380_odr_table[][2] = { + [BMP380_ODR_200HZ] = {200, 0}, + [BMP380_ODR_100HZ] = {100, 0}, + [BMP380_ODR_50HZ] = {50, 0}, + [BMP380_ODR_25HZ] = {25, 0}, + [BMP380_ODR_12_5HZ] = {12, 500000}, + [BMP380_ODR_6_25HZ] = {6, 250000}, + [BMP380_ODR_3_125HZ] = {3, 125000}, + [BMP380_ODR_1_5625HZ] = {1, 562500}, + [BMP380_ODR_0_78HZ] = {0, 781250}, + [BMP380_ODR_0_39HZ] = {0, 390625}, + [BMP380_ODR_0_2HZ] = {0, 195313}, + [BMP380_ODR_0_1HZ] = {0, 97656}, + [BMP380_ODR_0_05HZ] = {0, 48828}, + [BMP380_ODR_0_02HZ] = {0, 24414}, + [BMP380_ODR_0_01HZ] = {0, 12207}, + [BMP380_ODR_0_006HZ] = {0, 6104}, + [BMP380_ODR_0_003HZ] = {0, 3052}, + [BMP380_ODR_0_0015HZ] = {0, 1526}, +}; + +static int bmp380_chip_config(struct bmp280_data *data) +{ + bool change = false, aux; + unsigned int tmp; + u8 osrs; + int ret; + + /* Configure power control register */ + ret = regmap_update_bits(data->regmap, BMP380_REG_POWER_CONTROL, + BMP380_CTRL_SENSORS_MASK, + BMP380_CTRL_SENSORS_PRESS_EN | + BMP380_CTRL_SENSORS_TEMP_EN); + if (ret) { + dev_err(data->dev, + "failed to write operation control register\n"); + return ret; + } + + /* Configure oversampling */ + osrs = FIELD_PREP(BMP380_OSRS_TEMP_MASK, data->oversampling_temp) | + FIELD_PREP(BMP380_OSRS_PRESS_MASK, data->oversampling_press); + + ret = regmap_update_bits_check(data->regmap, BMP380_REG_OSR, + BMP380_OSRS_TEMP_MASK | + BMP380_OSRS_PRESS_MASK, + osrs, &aux); + if (ret) { + dev_err(data->dev, "failed to write oversampling register\n"); + return ret; + } + change = change || aux; + + /* Configure output data rate */ + ret = regmap_update_bits_check(data->regmap, BMP380_REG_ODR, + BMP380_ODRS_MASK, data->sampling_freq, &aux); + if (ret) { + dev_err(data->dev, "failed to write ODR selection register\n"); + return ret; + } + change = change || aux; + + /* Set filter data */ + ret = regmap_update_bits_check(data->regmap, BMP380_REG_CONFIG, BMP380_FILTER_MASK, + FIELD_PREP(BMP380_FILTER_MASK, data->iir_filter_coeff), + &aux); + if (ret) { + dev_err(data->dev, "failed to write config register\n"); + return ret; + } + change = change || aux; + + if (change) { + /* + * The configurations errors are detected on the fly during a measurement + * cycle. If the sampling frequency is too low, it's faster to reset + * the measurement loop than wait until the next measurement is due. + * + * Resets sensor measurement loop toggling between sleep and normal + * operating modes. + */ + ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL, + BMP380_MODE_MASK, + FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to set sleep mode\n"); + return ret; + } + usleep_range(2000, 2500); + ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL, + BMP380_MODE_MASK, + FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_NORMAL)); + if (ret) { + dev_err(data->dev, "failed to set normal mode\n"); + return ret; + } + /* + * Waits for measurement before checking configuration error flag. + * Selected longest measure time indicated in section 3.9.1 + * in the datasheet. + */ + msleep(80); + + /* Check config error flag */ + ret = regmap_read(data->regmap, BMP380_REG_ERROR, &tmp); + if (ret) { + dev_err(data->dev, + "failed to read error register\n"); + return ret; + } + if (tmp & BMP380_ERR_CONF_MASK) { + dev_warn(data->dev, + "sensor flagged configuration as incompatible\n"); + return -EINVAL; + } + } + + return 0; +} + +static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; +static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128}; + +static const struct bmp280_chip_info bmp380_chip_info = { + .id_reg = BMP380_REG_ID, + .start_up_time = 2000, + .channels = bmp380_channels, + .num_channels = 2, + + .oversampling_temp_avail = bmp380_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp380_oversampling_avail), + .oversampling_temp_default = ilog2(1), + + .oversampling_press_avail = bmp380_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp380_oversampling_avail), + .oversampling_press_default = ilog2(4), + + .sampling_freq_avail = bmp380_odr_table, + .num_sampling_freq_avail = ARRAY_SIZE(bmp380_odr_table) * 2, + .sampling_freq_default = BMP380_ODR_50HZ, + + .iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail, + .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail), + .iir_filter_coeff_default = 2, + + .chip_config = bmp380_chip_config, + .read_temp = bmp380_read_temp, + .read_press = bmp380_read_press, + .read_calib = bmp380_read_calib, }; static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) { - int ret; const int conversion_time_max[] = { 4500, 7500, 13500, 25500 }; unsigned int delay_us; unsigned int ctrl; + int ret; if (data->use_eoc) reinit_completion(&data->done); @@ -710,7 +1372,7 @@ static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) if (!ret) dev_err(data->dev, "timeout waiting for completion\n"); } else { - if (ctrl_meas == BMP180_MEAS_TEMP) + if (FIELD_GET(BMP180_MEAS_CTRL_MASK, ctrl_meas) == BMP180_MEAS_TEMP) delay_us = 4500; else delay_us = @@ -732,55 +1394,57 @@ static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) { - __be16 tmp; int ret; - ret = bmp180_measure(data, BMP180_MEAS_TEMP); + ret = bmp180_measure(data, + FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_TEMP) | + BMP180_MEAS_SCO); if (ret) return ret; - ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, &tmp, 2); + ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, + &data->be16, sizeof(data->be16)); if (ret) return ret; - *val = be16_to_cpu(tmp); + *val = be16_to_cpu(data->be16); return 0; } -static int bmp180_read_calib(struct bmp280_data *data, - struct bmp180_calib *calib) +static int bmp180_read_calib(struct bmp280_data *data) { + struct bmp180_calib *calib = &data->calib.bmp180; int ret; int i; - __be16 buf[BMP180_REG_CALIB_COUNT / 2]; - ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START, buf, - sizeof(buf)); + ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START, + data->bmp180_cal_buf, sizeof(data->bmp180_cal_buf)); if (ret < 0) return ret; /* None of the words has the value 0 or 0xFFFF */ - for (i = 0; i < ARRAY_SIZE(buf); i++) { - if (buf[i] == cpu_to_be16(0) || buf[i] == cpu_to_be16(0xffff)) + for (i = 0; i < ARRAY_SIZE(data->bmp180_cal_buf); i++) { + if (data->bmp180_cal_buf[i] == cpu_to_be16(0) || + data->bmp180_cal_buf[i] == cpu_to_be16(0xffff)) return -EIO; } /* Toss the calibration data into the entropy pool */ - add_device_randomness(buf, sizeof(buf)); + add_device_randomness(data->bmp180_cal_buf, sizeof(data->bmp180_cal_buf)); - calib->AC1 = be16_to_cpu(buf[AC1]); - calib->AC2 = be16_to_cpu(buf[AC2]); - calib->AC3 = be16_to_cpu(buf[AC3]); - calib->AC4 = be16_to_cpu(buf[AC4]); - calib->AC5 = be16_to_cpu(buf[AC5]); - calib->AC6 = be16_to_cpu(buf[AC6]); - calib->B1 = be16_to_cpu(buf[B1]); - calib->B2 = be16_to_cpu(buf[B2]); - calib->MB = be16_to_cpu(buf[MB]); - calib->MC = be16_to_cpu(buf[MC]); - calib->MD = be16_to_cpu(buf[MD]); + calib->AC1 = be16_to_cpu(data->bmp180_cal_buf[AC1]); + calib->AC2 = be16_to_cpu(data->bmp180_cal_buf[AC2]); + calib->AC3 = be16_to_cpu(data->bmp180_cal_buf[AC3]); + calib->AC4 = be16_to_cpu(data->bmp180_cal_buf[AC4]); + calib->AC5 = be16_to_cpu(data->bmp180_cal_buf[AC5]); + calib->AC6 = be16_to_cpu(data->bmp180_cal_buf[AC6]); + calib->B1 = be16_to_cpu(data->bmp180_cal_buf[B1]); + calib->B2 = be16_to_cpu(data->bmp180_cal_buf[B2]); + calib->MB = be16_to_cpu(data->bmp180_cal_buf[MB]); + calib->MC = be16_to_cpu(data->bmp180_cal_buf[MC]); + calib->MD = be16_to_cpu(data->bmp180_cal_buf[MD]); return 0; } @@ -793,8 +1457,8 @@ static int bmp180_read_calib(struct bmp280_data *data, */ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp) { - s32 x1, x2; struct bmp180_calib *calib = &data->calib.bmp180; + s32 x1, x2; x1 = ((adc_temp - calib->AC6) * calib->AC5) >> 15; x2 = (calib->MC << 11) / (x1 + calib->MD); @@ -805,8 +1469,8 @@ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp) static int bmp180_read_temp(struct bmp280_data *data, int *val) { - int ret; s32 adc_temp, comp_temp; + int ret; ret = bmp180_read_adc_temp(data, &adc_temp); if (ret) @@ -828,19 +1492,22 @@ static int bmp180_read_temp(struct bmp280_data *data, int *val) static int bmp180_read_adc_press(struct bmp280_data *data, int *val) { - int ret; - __be32 tmp = 0; u8 oss = data->oversampling_press; + int ret; - ret = bmp180_measure(data, BMP180_MEAS_PRESS_X(oss)); + ret = bmp180_measure(data, + FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_PRESS) | + FIELD_PREP(BMP180_OSRS_PRESS_MASK, oss) | + BMP180_MEAS_SCO); if (ret) return ret; - ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, &tmp, 3); + ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, + data->buf, sizeof(data->buf)); if (ret) return ret; - *val = (be32_to_cpu(tmp) >> 8) >> (8 - oss); + *val = get_unaligned_be24(data->buf) >> (8 - oss); return 0; } @@ -852,11 +1519,11 @@ static int bmp180_read_adc_press(struct bmp280_data *data, int *val) */ static u32 bmp180_compensate_press(struct bmp280_data *data, s32 adc_press) { + struct bmp180_calib *calib = &data->calib.bmp180; + s32 oss = data->oversampling_press; s32 x1, x2, x3, p; s32 b3, b6; u32 b4, b7; - s32 oss = data->oversampling_press; - struct bmp180_calib *calib = &data->calib.bmp180; b6 = data->t_fine - 4000; x1 = (calib->B2 * (b6 * b6 >> 12)) >> 11; @@ -883,9 +1550,9 @@ static u32 bmp180_compensate_press(struct bmp280_data *data, s32 adc_press) static int bmp180_read_press(struct bmp280_data *data, int *val, int *val2) { - int ret; - s32 adc_press; u32 comp_press; + s32 adc_press; + int ret; /* Read and compensate temperature so we get a reading of t_fine. */ ret = bmp180_read_temp(data, NULL); @@ -913,17 +1580,25 @@ static const int bmp180_oversampling_temp_avail[] = { 1 }; static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; static const struct bmp280_chip_info bmp180_chip_info = { + .id_reg = BMP280_REG_ID, + .start_up_time = 2000, + .channels = bmp280_channels, + .num_channels = 2, + .oversampling_temp_avail = bmp180_oversampling_temp_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp180_oversampling_temp_avail), + .oversampling_temp_default = 0, .oversampling_press_avail = bmp180_oversampling_press_avail, .num_oversampling_press_avail = ARRAY_SIZE(bmp180_oversampling_press_avail), + .oversampling_press_default = BMP180_MEAS_PRESS_8X, .chip_config = bmp180_chip_config, .read_temp = bmp180_read_temp, .read_press = bmp180_read_press, + .read_calib = bmp180_read_calib, }; static irqreturn_t bmp085_eoc_irq(int irq, void *d) @@ -990,11 +1665,12 @@ int bmp280_common_probe(struct device *dev, const char *name, int irq) { - int ret; + const struct bmp280_chip_info *chip_info; struct iio_dev *indio_dev; struct bmp280_data *data; - unsigned int chip_id; struct gpio_desc *gpiod; + unsigned int chip_id; + int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -1005,36 +1681,36 @@ int bmp280_common_probe(struct device *dev, data->dev = dev; indio_dev->name = name; - indio_dev->channels = bmp280_channels; indio_dev->info = &bmp280_info; indio_dev->modes = INDIO_DIRECT_MODE; switch (chip) { case BMP180_CHIP_ID: - indio_dev->num_channels = 2; - data->chip_info = &bmp180_chip_info; - data->oversampling_press = ilog2(8); - data->oversampling_temp = ilog2(1); - data->start_up_time = 10000; + chip_info = &bmp180_chip_info; break; case BMP280_CHIP_ID: - indio_dev->num_channels = 2; - data->chip_info = &bmp280_chip_info; - data->oversampling_press = ilog2(16); - data->oversampling_temp = ilog2(2); - data->start_up_time = 2000; + chip_info = &bmp280_chip_info; break; case BME280_CHIP_ID: - indio_dev->num_channels = 3; - data->chip_info = &bme280_chip_info; - data->oversampling_press = ilog2(16); - data->oversampling_humid = ilog2(16); - data->oversampling_temp = ilog2(2); - data->start_up_time = 2000; + chip_info = &bme280_chip_info; + break; + case BMP380_CHIP_ID: + chip_info = &bmp380_chip_info; break; default: return -EINVAL; } + data->chip_info = chip_info; + + /* Apply initial values from chip info structure */ + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + data->oversampling_press = chip_info->oversampling_press_default; + data->oversampling_humid = chip_info->oversampling_humid_default; + data->oversampling_temp = chip_info->oversampling_temp_default; + data->iir_filter_coeff = chip_info->iir_filter_coeff_default; + data->sampling_freq = chip_info->sampling_freq_default; + data->start_up_time = chip_info->start_up_time; /* Bring up regulators */ regulator_bulk_set_supply_names(data->supplies, @@ -1071,7 +1747,8 @@ int bmp280_common_probe(struct device *dev, } data->regmap = regmap; - ret = regmap_read(regmap, BMP280_REG_ID, &chip_id); + + ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id); if (ret < 0) return ret; if (chip_id != chip) { @@ -1080,6 +1757,13 @@ int bmp280_common_probe(struct device *dev, return -EINVAL; } + /* BMP3xx requires soft-reset as part of initialization */ + if (chip_id == BMP380_CHIP_ID) { + ret = bmp380_cmd(data, BMP380_CMD_SOFT_RESET); + if (ret < 0) + return ret; + } + ret = data->chip_info->chip_config(data); if (ret < 0) return ret; @@ -1091,21 +1775,11 @@ int bmp280_common_probe(struct device *dev, * non-volatile memory during production". Let's read them out at probe * time once. They will not change. */ - if (chip_id == BMP180_CHIP_ID) { - ret = bmp180_read_calib(data, &data->calib.bmp180); - if (ret < 0) { - dev_err(data->dev, - "failed to read calibration coefficients\n"); - return ret; - } - } else if (chip_id == BMP280_CHIP_ID || chip_id == BME280_CHIP_ID) { - ret = bmp280_read_calib(data, &data->calib.bmp280, chip_id); - if (ret < 0) { - dev_err(data->dev, - "failed to read calibration coefficients\n"); - return ret; - } - } + + ret = data->chip_info->read_calib(data); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "failed to read calibration coefficients\n"); /* * Attempt to grab an optional EOC IRQ - only the BMP085 has this diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index bf4a7a617537..0c27211f3ea0 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -19,6 +19,9 @@ static int bmp280_i2c_probe(struct i2c_client *client, case BME280_CHIP_ID: regmap_config = &bmp280_regmap_config; break; + case BMP380_CHIP_ID: + regmap_config = &bmp380_regmap_config; + break; default: return -EINVAL; } @@ -37,19 +40,21 @@ static int bmp280_i2c_probe(struct i2c_client *client, } static const struct of_device_id bmp280_of_i2c_match[] = { - { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, - { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, - { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, { .compatible = "bosch,bmp085", .data = (void *)BMP180_CHIP_ID }, + { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, + { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, + { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, + { .compatible = "bosch,bmp380", .data = (void *)BMP380_CHIP_ID }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); static const struct i2c_device_id bmp280_i2c_id[] = { - {"bmp280", BMP280_CHIP_ID }, - {"bmp180", BMP180_CHIP_ID }, {"bmp085", BMP180_CHIP_ID }, + {"bmp180", BMP180_CHIP_ID }, + {"bmp280", BMP280_CHIP_ID }, {"bme280", BME280_CHIP_ID }, + {"bmp380", BMP380_CHIP_ID }, { }, }; MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); diff --git a/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c index 969698518984..c98c67970265 100644 --- a/drivers/iio/pressure/bmp280-regmap.c +++ b/drivers/iio/pressure/bmp280-regmap.c @@ -72,6 +72,49 @@ static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg) } } +static bool bmp380_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP380_REG_CMD: + case BMP380_REG_CONFIG: + case BMP380_REG_FIFO_CONFIG_1: + case BMP380_REG_FIFO_CONFIG_2: + case BMP380_REG_FIFO_WATERMARK_LSB: + case BMP380_REG_FIFO_WATERMARK_MSB: + case BMP380_REG_POWER_CONTROL: + case BMP380_REG_INT_CONTROL: + case BMP380_REG_IF_CONFIG: + case BMP380_REG_ODR: + case BMP380_REG_OSR: + return true; + default: + return false; + } +} + +static bool bmp380_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP380_REG_TEMP_XLSB: + case BMP380_REG_TEMP_LSB: + case BMP380_REG_TEMP_MSB: + case BMP380_REG_PRESS_XLSB: + case BMP380_REG_PRESS_LSB: + case BMP380_REG_PRESS_MSB: + case BMP380_REG_SENSOR_TIME_XLSB: + case BMP380_REG_SENSOR_TIME_LSB: + case BMP380_REG_SENSOR_TIME_MSB: + case BMP380_REG_INT_STATUS: + case BMP380_REG_FIFO_DATA: + case BMP380_REG_STATUS: + case BMP380_REG_ERROR: + case BMP380_REG_EVENT: + return true; + default: + return false; + } +} + const struct regmap_config bmp280_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -83,3 +126,15 @@ const struct regmap_config bmp280_regmap_config = { .volatile_reg = bmp280_is_volatile_reg, }; EXPORT_SYMBOL_NS(bmp280_regmap_config, IIO_BMP280); + +const struct regmap_config bmp380_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BMP380_REG_CMD, + .cache_type = REGCACHE_RBTREE, + + .writeable_reg = bmp380_is_writeable_reg, + .volatile_reg = bmp380_is_volatile_reg, +}; +EXPORT_SYMBOL_NS(bmp380_regmap_config, IIO_BMP280); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 4cfaf3e869b8..011c68e07ebf 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -66,6 +66,9 @@ static int bmp280_spi_probe(struct spi_device *spi) case BME280_CHIP_ID: regmap_config = &bmp280_regmap_config; break; + case BMP380_CHIP_ID: + regmap_config = &bmp380_regmap_config; + break; default: return -EINVAL; } @@ -92,6 +95,7 @@ static const struct of_device_id bmp280_of_spi_match[] = { { .compatible = "bosch,bmp181", }, { .compatible = "bosch,bmp280", }, { .compatible = "bosch,bme280", }, + { .compatible = "bosch,bmp380", }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); @@ -101,6 +105,7 @@ static const struct spi_device_id bmp280_spi_id[] = { { "bmp181", BMP180_CHIP_ID }, { "bmp280", BMP280_CHIP_ID }, { "bme280", BME280_CHIP_ID }, + { "bmp380", BMP380_CHIP_ID }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 57ba0e85db91..c791325c7416 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -3,6 +3,87 @@ #include #include +/* BMP380 specific registers */ +#define BMP380_REG_CMD 0x7E +#define BMP380_REG_CONFIG 0x1F +#define BMP380_REG_ODR 0x1D +#define BMP380_REG_OSR 0x1C +#define BMP380_REG_POWER_CONTROL 0x1B +#define BMP380_REG_IF_CONFIG 0x1A +#define BMP380_REG_INT_CONTROL 0x19 +#define BMP380_REG_INT_STATUS 0x11 +#define BMP380_REG_EVENT 0x10 +#define BMP380_REG_STATUS 0x03 +#define BMP380_REG_ERROR 0x02 +#define BMP380_REG_ID 0x00 + +#define BMP380_REG_FIFO_CONFIG_1 0x18 +#define BMP380_REG_FIFO_CONFIG_2 0x17 +#define BMP380_REG_FIFO_WATERMARK_MSB 0x16 +#define BMP380_REG_FIFO_WATERMARK_LSB 0x15 +#define BMP380_REG_FIFO_DATA 0x14 +#define BMP380_REG_FIFO_LENGTH_MSB 0x13 +#define BMP380_REG_FIFO_LENGTH_LSB 0x12 + +#define BMP380_REG_SENSOR_TIME_MSB 0x0E +#define BMP380_REG_SENSOR_TIME_LSB 0x0D +#define BMP380_REG_SENSOR_TIME_XLSB 0x0C + +#define BMP380_REG_TEMP_MSB 0x09 +#define BMP380_REG_TEMP_LSB 0x08 +#define BMP380_REG_TEMP_XLSB 0x07 + +#define BMP380_REG_PRESS_MSB 0x06 +#define BMP380_REG_PRESS_LSB 0x05 +#define BMP380_REG_PRESS_XLSB 0x04 + +#define BMP380_REG_CALIB_TEMP_START 0x31 +#define BMP380_CALIB_REG_COUNT 21 + +#define BMP380_FILTER_MASK GENMASK(3, 1) +#define BMP380_FILTER_OFF 0 +#define BMP380_FILTER_1X 1 +#define BMP380_FILTER_3X 2 +#define BMP380_FILTER_7X 3 +#define BMP380_FILTER_15X 4 +#define BMP380_FILTER_31X 5 +#define BMP380_FILTER_63X 6 +#define BMP380_FILTER_127X 7 + +#define BMP380_OSRS_TEMP_MASK GENMASK(5, 3) +#define BMP380_OSRS_PRESS_MASK GENMASK(2, 0) + +#define BMP380_ODRS_MASK GENMASK(4, 0) + +#define BMP380_CTRL_SENSORS_MASK GENMASK(1, 0) +#define BMP380_CTRL_SENSORS_PRESS_EN BIT(0) +#define BMP380_CTRL_SENSORS_TEMP_EN BIT(1) +#define BMP380_MODE_MASK GENMASK(5, 4) +#define BMP380_MODE_SLEEP 0 +#define BMP380_MODE_FORCED 1 +#define BMP380_MODE_NORMAL 3 + +#define BMP380_MIN_TEMP -4000 +#define BMP380_MAX_TEMP 8500 +#define BMP380_MIN_PRES 3000000 +#define BMP380_MAX_PRES 12500000 + +#define BMP380_CMD_NOOP 0x00 +#define BMP380_CMD_EXTMODE_EN_MID 0x34 +#define BMP380_CMD_FIFO_FLUSH 0xB0 +#define BMP380_CMD_SOFT_RESET 0xB6 + +#define BMP380_STATUS_CMD_RDY_MASK BIT(4) +#define BMP380_STATUS_DRDY_PRESS_MASK BIT(5) +#define BMP380_STATUS_DRDY_TEMP_MASK BIT(6) + +#define BMP380_ERR_FATAL_MASK BIT(0) +#define BMP380_ERR_CMD_MASK BIT(1) +#define BMP380_ERR_CONF_MASK BIT(2) + +#define BMP380_TEMP_SKIPPED 0x800000 +#define BMP380_PRESS_SKIPPED 0x800000 + /* BMP280 specific registers */ #define BMP280_REG_HUMIDITY_LSB 0xFE #define BMP280_REG_HUMIDITY_MSB 0xFD @@ -13,6 +94,9 @@ #define BMP280_REG_PRESS_LSB 0xF8 #define BMP280_REG_PRESS_MSB 0xF7 +/* Helper mask to truncate excess 4 bits on pressure and temp readings */ +#define BMP280_MEAS_TRIM_MASK GENMASK(24, 4) + #define BMP280_REG_CONFIG 0xF5 #define BMP280_REG_CTRL_MEAS 0xF4 #define BMP280_REG_STATUS 0xF3 @@ -32,44 +116,46 @@ #define BMP280_REG_COMP_PRESS_START 0x8E #define BMP280_COMP_PRESS_REG_COUNT 18 -#define BMP280_FILTER_MASK (BIT(4) | BIT(3) | BIT(2)) +#define BMP280_COMP_H5_MASK GENMASK(15, 4) + +#define BMP280_CONTIGUOUS_CALIB_REGS (BMP280_COMP_TEMP_REG_COUNT + \ + BMP280_COMP_PRESS_REG_COUNT) + +#define BMP280_FILTER_MASK GENMASK(4, 2) #define BMP280_FILTER_OFF 0 -#define BMP280_FILTER_2X BIT(2) -#define BMP280_FILTER_4X BIT(3) -#define BMP280_FILTER_8X (BIT(3) | BIT(2)) -#define BMP280_FILTER_16X BIT(4) +#define BMP280_FILTER_2X 1 +#define BMP280_FILTER_4X 2 +#define BMP280_FILTER_8X 3 +#define BMP280_FILTER_16X 4 -#define BMP280_OSRS_HUMIDITY_MASK (BIT(2) | BIT(1) | BIT(0)) -#define BMP280_OSRS_HUMIDITIY_X(osrs_h) ((osrs_h) << 0) +#define BMP280_OSRS_HUMIDITY_MASK GENMASK(2, 0) #define BMP280_OSRS_HUMIDITY_SKIP 0 -#define BMP280_OSRS_HUMIDITY_1X BMP280_OSRS_HUMIDITIY_X(1) -#define BMP280_OSRS_HUMIDITY_2X BMP280_OSRS_HUMIDITIY_X(2) -#define BMP280_OSRS_HUMIDITY_4X BMP280_OSRS_HUMIDITIY_X(3) -#define BMP280_OSRS_HUMIDITY_8X BMP280_OSRS_HUMIDITIY_X(4) -#define BMP280_OSRS_HUMIDITY_16X BMP280_OSRS_HUMIDITIY_X(5) +#define BMP280_OSRS_HUMIDITY_1X 1 +#define BMP280_OSRS_HUMIDITY_2X 2 +#define BMP280_OSRS_HUMIDITY_4X 3 +#define BMP280_OSRS_HUMIDITY_8X 4 +#define BMP280_OSRS_HUMIDITY_16X 5 -#define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5)) +#define BMP280_OSRS_TEMP_MASK GENMASK(7, 5) #define BMP280_OSRS_TEMP_SKIP 0 -#define BMP280_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5) -#define BMP280_OSRS_TEMP_1X BMP280_OSRS_TEMP_X(1) -#define BMP280_OSRS_TEMP_2X BMP280_OSRS_TEMP_X(2) -#define BMP280_OSRS_TEMP_4X BMP280_OSRS_TEMP_X(3) -#define BMP280_OSRS_TEMP_8X BMP280_OSRS_TEMP_X(4) -#define BMP280_OSRS_TEMP_16X BMP280_OSRS_TEMP_X(5) +#define BMP280_OSRS_TEMP_1X 1 +#define BMP280_OSRS_TEMP_2X 2 +#define BMP280_OSRS_TEMP_4X 3 +#define BMP280_OSRS_TEMP_8X 4 +#define BMP280_OSRS_TEMP_16X 5 -#define BMP280_OSRS_PRESS_MASK (BIT(4) | BIT(3) | BIT(2)) +#define BMP280_OSRS_PRESS_MASK GENMASK(4, 2) #define BMP280_OSRS_PRESS_SKIP 0 -#define BMP280_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2) -#define BMP280_OSRS_PRESS_1X BMP280_OSRS_PRESS_X(1) -#define BMP280_OSRS_PRESS_2X BMP280_OSRS_PRESS_X(2) -#define BMP280_OSRS_PRESS_4X BMP280_OSRS_PRESS_X(3) -#define BMP280_OSRS_PRESS_8X BMP280_OSRS_PRESS_X(4) -#define BMP280_OSRS_PRESS_16X BMP280_OSRS_PRESS_X(5) +#define BMP280_OSRS_PRESS_1X 1 +#define BMP280_OSRS_PRESS_2X 2 +#define BMP280_OSRS_PRESS_4X 3 +#define BMP280_OSRS_PRESS_8X 4 +#define BMP280_OSRS_PRESS_16X 5 -#define BMP280_MODE_MASK (BIT(1) | BIT(0)) +#define BMP280_MODE_MASK GENMASK(1, 0) #define BMP280_MODE_SLEEP 0 -#define BMP280_MODE_FORCED BIT(0) -#define BMP280_MODE_NORMAL (BIT(1) | BIT(0)) +#define BMP280_MODE_FORCED 1 +#define BMP280_MODE_NORMAL 3 /* BMP180 specific registers */ #define BMP180_REG_OUT_XLSB 0xF8 @@ -79,19 +165,22 @@ #define BMP180_REG_CALIB_START 0xAA #define BMP180_REG_CALIB_COUNT 22 +#define BMP180_MEAS_CTRL_MASK GENMASK(4, 0) +#define BMP180_MEAS_TEMP 0x0E +#define BMP180_MEAS_PRESS 0x14 #define BMP180_MEAS_SCO BIT(5) -#define BMP180_MEAS_TEMP (0x0E | BMP180_MEAS_SCO) -#define BMP180_MEAS_PRESS_X(oss) ((oss) << 6 | 0x14 | BMP180_MEAS_SCO) -#define BMP180_MEAS_PRESS_1X BMP180_MEAS_PRESS_X(0) -#define BMP180_MEAS_PRESS_2X BMP180_MEAS_PRESS_X(1) -#define BMP180_MEAS_PRESS_4X BMP180_MEAS_PRESS_X(2) -#define BMP180_MEAS_PRESS_8X BMP180_MEAS_PRESS_X(3) +#define BMP180_OSRS_PRESS_MASK GENMASK(7, 6) +#define BMP180_MEAS_PRESS_1X 0 +#define BMP180_MEAS_PRESS_2X 1 +#define BMP180_MEAS_PRESS_4X 2 +#define BMP180_MEAS_PRESS_8X 3 /* BMP180 and BMP280 common registers */ #define BMP280_REG_CTRL_MEAS 0xF4 #define BMP280_REG_RESET 0xE0 #define BMP280_REG_ID 0xD0 +#define BMP380_CHIP_ID 0x50 #define BMP180_CHIP_ID 0x55 #define BMP280_CHIP_ID 0x58 #define BME280_CHIP_ID 0x60 @@ -105,6 +194,7 @@ /* Regmap configurations */ extern const struct regmap_config bmp180_regmap_config; extern const struct regmap_config bmp280_regmap_config; +extern const struct regmap_config bmp380_regmap_config; /* Probe called from different transports */ int bmp280_common_probe(struct device *dev, diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index 36fb7ae0d0a9..984a3f511a1a 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -89,6 +89,7 @@ struct dps310_data { s32 c00, c10, c20, c30, c01, c11, c21; s32 pressure_raw; s32 temp_raw; + bool timeout_recovery_failed; }; static const struct iio_chan_spec dps310_channels[] = { @@ -159,6 +160,102 @@ static int dps310_get_coefs(struct dps310_data *data) return 0; } +/* + * Some versions of the chip will read temperatures in the ~60C range when + * it's actually ~20C. This is the manufacturer recommended workaround + * to correct the issue. The registers used below are undocumented. + */ +static int dps310_temp_workaround(struct dps310_data *data) +{ + int rc; + int reg; + + rc = regmap_read(data->regmap, 0x32, ®); + if (rc) + return rc; + + /* + * If bit 1 is set then the device is okay, and the workaround does not + * need to be applied + */ + if (reg & BIT(1)) + return 0; + + rc = regmap_write(data->regmap, 0x0e, 0xA5); + if (rc) + return rc; + + rc = regmap_write(data->regmap, 0x0f, 0x96); + if (rc) + return rc; + + rc = regmap_write(data->regmap, 0x62, 0x02); + if (rc) + return rc; + + rc = regmap_write(data->regmap, 0x0e, 0x00); + if (rc) + return rc; + + return regmap_write(data->regmap, 0x0f, 0x00); +} + +static int dps310_startup(struct dps310_data *data) +{ + int rc; + int ready; + + /* + * Set up pressure sensor in single sample, one measurement per second + * mode + */ + rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0); + if (rc) + return rc; + + /* + * Set up external (MEMS) temperature sensor in single sample, one + * measurement per second mode + */ + rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT); + if (rc) + return rc; + + /* Temp and pressure shifts are disabled when PRC <= 8 */ + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0); + if (rc) + return rc; + + /* MEAS_CFG doesn't update correctly unless first written with 0 */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, 0); + if (rc) + return rc; + + /* Turn on temperature and pressure measurement in the background */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN | + DPS310_TEMP_EN | DPS310_BACKGROUND); + if (rc) + return rc; + + /* + * Calibration coefficients required for reporting temperature. + * They are available 40ms after the device has started + */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_COEF_RDY, 10000, 40000); + if (rc) + return rc; + + rc = dps310_get_coefs(data); + if (rc) + return rc; + + return dps310_temp_workaround(data); +} + static int dps310_get_pres_precision(struct dps310_data *data) { int rc; @@ -297,11 +394,69 @@ static int dps310_get_temp_k(struct dps310_data *data) return scale_factors[ilog2(rc)]; } +static int dps310_reset_wait(struct dps310_data *data) +{ + int rc; + + rc = regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); + if (rc) + return rc; + + /* Wait for device chip access: 2.5ms in specification */ + usleep_range(2500, 12000); + return 0; +} + +static int dps310_reset_reinit(struct dps310_data *data) +{ + int rc; + + rc = dps310_reset_wait(data); + if (rc) + return rc; + + return dps310_startup(data); +} + +static int dps310_ready_status(struct dps310_data *data, int ready_bit, int timeout) +{ + int sleep = DPS310_POLL_SLEEP_US(timeout); + int ready; + + return regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, ready & ready_bit, + sleep, timeout); +} + +static int dps310_ready(struct dps310_data *data, int ready_bit, int timeout) +{ + int rc; + + rc = dps310_ready_status(data, ready_bit, timeout); + if (rc) { + if (rc == -ETIMEDOUT && !data->timeout_recovery_failed) { + /* Reset and reinitialize the chip. */ + if (dps310_reset_reinit(data)) { + data->timeout_recovery_failed = true; + } else { + /* Try again to get sensor ready status. */ + if (dps310_ready_status(data, ready_bit, timeout)) + data->timeout_recovery_failed = true; + else + return 0; + } + } + + return rc; + } + + data->timeout_recovery_failed = false; + return 0; +} + static int dps310_read_pres_raw(struct dps310_data *data) { int rc; int rate; - int ready; int timeout; s32 raw; u8 val[3]; @@ -313,9 +468,7 @@ static int dps310_read_pres_raw(struct dps310_data *data) timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, - ready & DPS310_PRS_RDY, - DPS310_POLL_SLEEP_US(timeout), timeout); + rc = dps310_ready(data, DPS310_PRS_RDY, timeout); if (rc) goto done; @@ -352,7 +505,6 @@ static int dps310_read_temp_raw(struct dps310_data *data) { int rc; int rate; - int ready; int timeout; if (mutex_lock_interruptible(&data->lock)) @@ -362,10 +514,8 @@ static int dps310_read_temp_raw(struct dps310_data *data) timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, - ready & DPS310_TMP_RDY, - DPS310_POLL_SLEEP_US(timeout), timeout); - if (rc < 0) + rc = dps310_ready(data, DPS310_TMP_RDY, timeout); + if (rc) goto done; rc = dps310_read_temp_ready(data); @@ -660,7 +810,7 @@ static void dps310_reset(void *action_data) { struct dps310_data *data = action_data; - regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); + dps310_reset_wait(data); } static const struct regmap_config dps310_regmap_config = { @@ -677,52 +827,12 @@ static const struct iio_info dps310_info = { .write_raw = dps310_write_raw, }; -/* - * Some verions of chip will read temperatures in the ~60C range when - * its actually ~20C. This is the manufacturer recommended workaround - * to correct the issue. The registers used below are undocumented. - */ -static int dps310_temp_workaround(struct dps310_data *data) -{ - int rc; - int reg; - - rc = regmap_read(data->regmap, 0x32, ®); - if (rc < 0) - return rc; - - /* - * If bit 1 is set then the device is okay, and the workaround does not - * need to be applied - */ - if (reg & BIT(1)) - return 0; - - rc = regmap_write(data->regmap, 0x0e, 0xA5); - if (rc < 0) - return rc; - - rc = regmap_write(data->regmap, 0x0f, 0x96); - if (rc < 0) - return rc; - - rc = regmap_write(data->regmap, 0x62, 0x02); - if (rc < 0) - return rc; - - rc = regmap_write(data->regmap, 0x0e, 0x00); - if (rc < 0) - return rc; - - return regmap_write(data->regmap, 0x0f, 0x00); -} - static int dps310_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct dps310_data *data; struct iio_dev *iio; - int rc, ready; + int rc; iio = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!iio) @@ -747,54 +857,8 @@ static int dps310_probe(struct i2c_client *client, if (rc) return rc; - /* - * Set up pressure sensor in single sample, one measurement per second - * mode - */ - rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0); - - /* - * Set up external (MEMS) temperature sensor in single sample, one - * measurement per second mode - */ - rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT); - if (rc < 0) - return rc; - - /* Temp and pressure shifts are disabled when PRC <= 8 */ - rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, - DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0); - if (rc < 0) - return rc; - - /* MEAS_CFG doesn't update correctly unless first written with 0 */ - rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, - DPS310_MEAS_CTRL_BITS, 0); - if (rc < 0) - return rc; - - /* Turn on temperature and pressure measurement in the background */ - rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, - DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN | - DPS310_TEMP_EN | DPS310_BACKGROUND); - if (rc < 0) - return rc; - - /* - * Calibration coefficients required for reporting temperature. - * They are available 40ms after the device has started - */ - rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, - ready & DPS310_COEF_RDY, 10000, 40000); - if (rc < 0) - return rc; - - rc = dps310_get_coefs(data); - if (rc < 0) - return rc; - - rc = dps310_temp_workaround(data); - if (rc < 0) + rc = dps310_startup(data); + if (rc) return rc; rc = devm_iio_device_register(&client->dev, iio); diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index 156e6a72dc5c..6e11bea784fa 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -22,6 +22,7 @@ enum st_press_type { LPS33HW, LPS35HW, LPS22HH, + LPS22DF, ST_PRESS_MAX, }; @@ -32,6 +33,7 @@ enum st_press_type { #define LPS33HW_PRESS_DEV_NAME "lps33hw" #define LPS35HW_PRESS_DEV_NAME "lps35hw" #define LPS22HH_PRESS_DEV_NAME "lps22hh" +#define LPS22DF_PRESS_DEV_NAME "lps22df" /** * struct st_sensors_platform_data - default press platform data diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 76913a2028d2..80176e3083af 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -552,6 +552,76 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .multi_read_bit = false, .bootime = 2, }, + { + /* + * CUSTOM VALUES FOR LPS22DF SENSOR + * See LPS22DF datasheet: + * http://www.st.com/resource/en/datasheet/lps22df.pdf + */ + .wai = 0xb4, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LPS22DF_PRESS_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_press_lps22hb_channels, + .num_ch = ARRAY_SIZE(st_press_lps22hb_channels), + .odr = { + .addr = 0x10, + .mask = 0x78, + .odr_avl = { + { .hz = 1, .value = 0x01 }, + { .hz = 4, .value = 0x02 }, + { .hz = 10, .value = 0x03 }, + { .hz = 25, .value = 0x04 }, + { .hz = 50, .value = 0x05 }, + { .hz = 75, .value = 0x06 }, + { .hz = 100, .value = 0x07 }, + { .hz = 200, .value = 0x08 }, + }, + }, + .pw = { + .addr = 0x10, + .mask = 0x78, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .fs_avl = { + /* + * Pressure and temperature sensitivity values + * as defined in table 2 of LPS22DF datasheet. + */ + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .gain = ST_PRESS_KPASCAL_NANO_SCALE, + .gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS, + }, + }, + }, + .bdu = { + .addr = 0x11, + .mask = BIT(3), + }, + .drdy_irq = { + .int1 = { + .addr = 0x13, + .mask = BIT(5), + .addr_od = 0x12, + .mask_od = BIT(1), + }, + .addr_ihl = 0x12, + .mask_ihl = BIT(3), + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x03, + }, + }, + .sim = { + .addr = 0x0E, + .value = BIT(5), + }, + .multi_read_bit = false, + .bootime = 2, + }, }; static int st_press_write_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 7035777fd988..58fede861891 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -47,6 +47,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps22hh", .data = LPS22HH_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22df", + .data = LPS22DF_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); @@ -67,6 +71,7 @@ static const struct i2c_device_id st_press_id_table[] = { { LPS33HW_PRESS_DEV_NAME, LPS33HW }, { LPS35HW_PRESS_DEV_NAME, LPS35HW }, { LPS22HH_PRESS_DEV_NAME, LPS22HH }, + { LPS22DF_PRESS_DEV_NAME, LPS22DF }, {}, }; MODULE_DEVICE_TABLE(i2c, st_press_id_table); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index bfab8e7fb061..25cca5ad7c55 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -51,6 +51,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps22hh", .data = LPS22HH_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22df", + .data = LPS22DF_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); @@ -97,6 +101,7 @@ static const struct spi_device_id st_press_id_table[] = { { LPS33HW_PRESS_DEV_NAME }, { LPS35HW_PRESS_DEV_NAME }, { LPS22HH_PRESS_DEV_NAME }, + { LPS22DF_PRESS_DEV_NAME }, { "lps001wp-press" }, { "lps25h-press", }, { "lps331ap-press" }, diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 05015351a34a..faf2f806ce80 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -359,7 +359,7 @@ static int srf04_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) +static int srf04_pm_runtime_suspend(struct device *dev) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); @@ -371,7 +371,7 @@ static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused srf04_pm_runtime_resume(struct device *dev) +static int srf04_pm_runtime_resume(struct device *dev) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); @@ -385,8 +385,8 @@ static int __maybe_unused srf04_pm_runtime_resume(struct device *dev) } static const struct dev_pm_ops srf04_pm_ops = { - SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend, - srf04_pm_runtime_resume, NULL) + RUNTIME_PM_OPS(srf04_pm_runtime_suspend, + srf04_pm_runtime_resume, NULL) }; static struct platform_driver srf04_driver = { @@ -395,7 +395,7 @@ static struct platform_driver srf04_driver = { .driver = { .name = "srf04-gpio", .of_match_table = of_srf04_match, - .pm = &srf04_pm_ops, + .pm = pm_ptr(&srf04_pm_ops), }, }; diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index ea7318b508ea..0e4747ccd3cf 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -965,7 +965,7 @@ static int sx9310_probe(struct i2c_client *client) return sx_common_probe(client, &sx9310_chip_info, &sx9310_regmap_config); } -static int __maybe_unused sx9310_suspend(struct device *dev) +static int sx9310_suspend(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); u8 ctrl0; @@ -991,7 +991,7 @@ out: return ret; } -static int __maybe_unused sx9310_resume(struct device *dev) +static int sx9310_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; @@ -1013,7 +1013,7 @@ out: return 0; } -static SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume); static const struct acpi_device_id sx9310_acpi_match[] = { { "STH9310", SX9310_WHOAMI_VALUE }, @@ -1041,7 +1041,7 @@ static struct i2c_driver sx9310_driver = { .name = "sx9310", .acpi_match_table = sx9310_acpi_match, .of_match_table = sx9310_of_match, - .pm = &sx9310_pm_ops, + .pm = pm_sleep_ptr(&sx9310_pm_ops), /* * Lots of i2c transfers in probe + over 200 ms waiting in diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index edb5a2ce4e27..977cf17cec52 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -1073,7 +1073,7 @@ static int sx9324_probe(struct i2c_client *client) return sx_common_probe(client, &sx9324_chip_info, &sx9324_regmap_config); } -static int __maybe_unused sx9324_suspend(struct device *dev) +static int sx9324_suspend(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); unsigned int regval; @@ -1098,7 +1098,7 @@ out: return ret; } -static int __maybe_unused sx9324_resume(struct device *dev) +static int sx9324_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; @@ -1114,7 +1114,7 @@ static int __maybe_unused sx9324_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume); static const struct acpi_device_id sx9324_acpi_match[] = { { "STH9324", SX9324_WHOAMI_VALUE }, @@ -1139,7 +1139,7 @@ static struct i2c_driver sx9324_driver = { .name = "sx9324", .acpi_match_table = sx9324_acpi_match, .of_match_table = sx9324_of_match, - .pm = &sx9324_pm_ops, + .pm = pm_sleep_ptr(&sx9324_pm_ops), /* * Lots of i2c transfers in probe + over 200 ms waiting in diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index d9a12e6be6ca..7fa2213d23ba 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -819,7 +819,7 @@ static int sx9360_probe(struct i2c_client *client) return sx_common_probe(client, &sx9360_chip_info, &sx9360_regmap_config); } -static int __maybe_unused sx9360_suspend(struct device *dev) +static int sx9360_suspend(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); unsigned int regval; @@ -844,7 +844,7 @@ out: return ret; } -static int __maybe_unused sx9360_resume(struct device *dev) +static int sx9360_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; @@ -861,7 +861,7 @@ static int __maybe_unused sx9360_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume); static const struct acpi_device_id sx9360_acpi_match[] = { { "STH9360", SX9360_WHOAMI_VALUE }, @@ -886,7 +886,7 @@ static struct i2c_driver sx9360_driver = { .name = "sx9360", .acpi_match_table = sx9360_acpi_match, .of_match_table = sx9360_of_match, - .pm = &sx9360_pm_ops, + .pm = pm_sleep_ptr(&sx9360_pm_ops), /* * Lots of i2c transfers in probe + over 200 ms waiting in diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index c253a5315988..3157416be912 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -79,16 +79,15 @@ struct mlx90614_data { /* Bandwidth values for IIR filtering */ static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86}; -static IIO_CONST_ATTR(in_temp_object_filter_low_pass_3db_frequency_available, - "0.15 0.20 0.31 0.77 0.86 1.10 1.53 7.23"); - -static struct attribute *mlx90614_attributes[] = { - &iio_const_attr_in_temp_object_filter_low_pass_3db_frequency_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group mlx90614_attr_group = { - .attrs = mlx90614_attributes, +static const int mlx90614_freqs[][2] = { + {0, 150000}, + {0, 200000}, + {0, 310000}, + {0, 770000}, + {0, 860000}, + {1, 100000}, + {1, 530000}, + {7, 230000} }; /* @@ -373,6 +372,22 @@ static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev, } } +static int mlx90614_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_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = (int *)mlx90614_freqs; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = 2 * ARRAY_SIZE(mlx90614_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static const struct iio_chan_spec mlx90614_channels[] = { { .type = IIO_TEMP, @@ -389,6 +404,8 @@ static const struct iio_chan_spec mlx90614_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, @@ -401,6 +418,8 @@ static const struct iio_chan_spec mlx90614_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, @@ -410,7 +429,7 @@ static const struct iio_info mlx90614_info = { .read_raw = mlx90614_read_raw, .write_raw = mlx90614_write_raw, .write_raw_get_fmt = mlx90614_write_raw_get_fmt, - .attrs = &mlx90614_attr_group, + .read_avail = mlx90614_read_avail, }; #ifdef CONFIG_PM diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 913864221ac4..c79f2f046a0b 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -95,6 +95,12 @@ enum iio_modifier { IIO_MOD_ETHANOL, IIO_MOD_H2, IIO_MOD_O2, + IIO_MOD_LINEAR_X, + IIO_MOD_LINEAR_Y, + IIO_MOD_LINEAR_Z, + IIO_MOD_PITCH, + IIO_MOD_YAW, + IIO_MOD_ROLL, }; enum iio_event_type { @@ -118,4 +124,3 @@ enum iio_event_direction { }; #endif /* _UAPI_IIO_TYPES_H_ */ - diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index b3b3ea399f67..0a5c2bb60030 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -125,6 +125,12 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_PM4] = "pm4", [IIO_MOD_PM10] = "pm10", [IIO_MOD_O2] = "o2", + [IIO_MOD_LINEAR_X] = "linear_x", + [IIO_MOD_LINEAR_Y] = "linear_y", + [IIO_MOD_LINEAR_Z] = "linear_z", + [IIO_MOD_PITCH] = "pitch", + [IIO_MOD_YAW] = "yaw", + [IIO_MOD_ROLL] = "roll", }; static bool event_is_known(struct iio_event_data *event)