Second set of IIO new device support, features and cleanup for the 6.1 cycle.

Normal mixed bag of new device support with continuing trend that most new
 devices are supported by extending existing drivers - a positive sign perhaps
 that device manufacturers have somewhat stabilized their interfaces across
 product generations. The BNO055 driver was however a substantial addition
 including several additions to the IIO core.
 
 There are a number of significant patch sets under review, so if the 6.0
 cycle runs long I may send a 3rd pull request.
 
 New device support
 * adi,adxl313
   - Support for the ADXL312 and ADXL314 accelerometers.
 * bosch,bmp280
   - Support for the BMP380 family of pressures sensors.
     Included considerable refactoring and modernization of the bmp280
     driver.
 * bosch,bno055
   - New driver for this i2c/serial attached complex IMU.
 * lltc,ltc2497
   - Support for the LTC2499 16 channel, 24bit ADC.
 * st,pressure
   - Support for the LPS22DF pressure sensor
 * st,lsm6dsx
   - Support for the LSM6DSTX (Mainly adding the ID and WAI)
 
 Features
 * core - to support the bosch,bno055 requirements
   - Support for linear acceleration channel type (effect  of gravity removed)
   - Pitch, yaw and roll modifiers for angle channels.
   - Standard serialnumber attribute documentation.
   - Binary attributes - to allow for calibration save and restore.
 * adi,ad7923
   - Support extended range (wider supported input voltage range).
 * bosch,bmp280
   - Add filter controls for some supported parts.
 * microchip,mcp3911
   - Buffered capture support for this ADC.
   - Data ready interrupt support, including hiz control for line.
   - Oversampling ratio support.
 * st,stm32-adc
   - Support ID registers on parts where they are present, providing
     discoverability of some features.
 
 Fixes - late breaking fixes that I judged could wait for the merge window.
 * adi,ad5593r
   - Add a missing STOP condition between address write and data read.
   - Check for related i2c functionality.
 * adi,ad7923
   - Fix shift reporting for some variants supported by the driver.
 * infinion,dps310
   - Work around a hardware issue where a chip can hang by adding a
     timeout and reset path.
 
 Cleanups
 * Continuing work to switch to new pm macros.
 * MAINTAINERS
   - Drop duplication of wild card covered entry in ADI block and
     add missing entries to cover ltc294x binding files.
 * bosch,bma400
   - Fix trivial smatch warning.
 * bosch,bmp280
   - Fix broken links to datasheets
 * lltc,ltc2497
   - Fix missing entry for ltc2499
 * mexelis,mlx90614
   - Switch to get_avail() callback for _available attributes.
 * microchip,mcp3911
   - Move to devm_ resource management for all elements of probe()
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmMvCnIRHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0FojJvw//eryvTKL0V0O5Sy0urt1xYV0La/u6z6NB
 ZGIj0yLftIDw2XdU1nJ/uWun6PlqzW9Dbo31jwBrir5vA9m1KWfZ8MXQrYLay1LM
 PLPcIWcfbt7X6inFB6a5Ibe4AiF1XlRp+GmCwm6+IheLj1OphnYsjc74P10Tv40S
 svzqAJ7anZBxym1zIwXOWx8q4xUrzLt/dHsu4z04EbZpdBL8gRjRboKZYJGCHCQJ
 KJtR6zF6ckhTJI6dgFBOAdIeKAeYQMXhIyGbICmZ60vMGxTxXEB8hc2zlCMMBGHj
 ZwrR8wTUU1gPERLAQkSdRHw/3HaxgrAInYfukUZEkJFuxrrVfyNGF901844qBH71
 j6bzbg6pmI/7jijADB6WCAYuSMvpuHei0Ze1ZHG5c+ihHJQFx2vBC3/tVwagT49c
 aGGET0aP4l4D5AzUlLGaDTeS6ySOl+lP8X+jqIWKUiD7ywoKkOvB77BlVilkhpbQ
 IdCt81kxObTSJjDdUounYF0QEnKWxh5DayX72Pj0rBmy4oMEUbQjI6M22guPd00t
 gFb4qD+Sq/3OvNKVBLbP7Qk/92HqZZQZXF85NGRnNW2Xf7ajXnwMCuNlseAGjEtz
 oAryC/LuSvmOMfFQtl842H/9cnlZ13f5GpnukKEFPyxqGvThMd7obUejm7XGDE7P
 /IVEH8eJOjI=
 =q/TD
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-6.1b' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next

Jonathan writes:

Second set of IIO new device support, features and cleanup for the 6.1 cycle.

Normal mixed bag of new device support with continuing trend that most new
devices are supported by extending existing drivers - a positive sign perhaps
that device manufacturers have somewhat stabilized their interfaces across
product generations. The BNO055 driver was however a substantial addition
including several additions to the IIO core.

There are a number of significant patch sets under review, so if the 6.0
cycle runs long I may send a 3rd pull request.

New device support
* adi,adxl313
  - Support for the ADXL312 and ADXL314 accelerometers.
* bosch,bmp280
  - Support for the BMP380 family of pressures sensors.
    Included considerable refactoring and modernization of the bmp280
    driver.
* bosch,bno055
  - New driver for this i2c/serial attached complex IMU.
* lltc,ltc2497
  - Support for the LTC2499 16 channel, 24bit ADC.
* st,pressure
  - Support for the LPS22DF pressure sensor
* st,lsm6dsx
  - Support for the LSM6DSTX (Mainly adding the ID and WAI)

Features
* core - to support the bosch,bno055 requirements
  - Support for linear acceleration channel type (effect  of gravity removed)
  - Pitch, yaw and roll modifiers for angle channels.
  - Standard serialnumber attribute documentation.
  - Binary attributes - to allow for calibration save and restore.
* adi,ad7923
  - Support extended range (wider supported input voltage range).
* bosch,bmp280
  - Add filter controls for some supported parts.
* microchip,mcp3911
  - Buffered capture support for this ADC.
  - Data ready interrupt support, including hiz control for line.
  - Oversampling ratio support.
* st,stm32-adc
  - Support ID registers on parts where they are present, providing
    discoverability of some features.

Fixes - late breaking fixes that I judged could wait for the merge window.
* adi,ad5593r
  - Add a missing STOP condition between address write and data read.
  - Check for related i2c functionality.
* adi,ad7923
  - Fix shift reporting for some variants supported by the driver.
* infinion,dps310
  - Work around a hardware issue where a chip can hang by adding a
    timeout and reset path.

Cleanups
* Continuing work to switch to new pm macros.
* MAINTAINERS
  - Drop duplication of wild card covered entry in ADI block and
    add missing entries to cover ltc294x binding files.
* bosch,bma400
  - Fix trivial smatch warning.
* bosch,bmp280
  - Fix broken links to datasheets
* lltc,ltc2497
  - Fix missing entry for ltc2499
* mexelis,mlx90614
  - Switch to get_avail() callback for _available attributes.
* microchip,mcp3911
  - Move to devm_ resource management for all elements of probe()

* tag 'iio-for-6.1b' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (57 commits)
  iio: adc: mcp3911: add support for oversampling ratio
  dt-bindings: iio: adc: mcp3911: add microchip,data-ready-hiz entry
  iio: adc: mcp3911: add support for interrupts
  iio: adc: mcp3911: add support for buffers
  iio: adc: mcp3911: use resource-managed version of iio_device_register
  iio: accel: bma400: Fix smatch warning based on use of unintialized value.
  iio: light: st_uvis25: Use EXPORT_NS_SIMPLE_DEV_PM_OPS()
  iio: accel: bmi088: Use EXPORT_NS_GPL_RUNTIME_DEV_PM_OPS() and pm_ptr()
  iio: proximity: srf04: Use pm_ptr() to remove unused struct dev_pm_ops
  iio: proximity: sx9360: Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  iio: proximity: sx9324: Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  iio: proximity: sx9310: Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  docs: iio: add documentation for BNO055 driver
  iio: imu: add BNO055 I2C driver
  iio: imu: add BNO055 serdev driver
  dt-bindings: iio/imu: Add Bosch BNO055
  iio: document "serialnumber" sysfs attribute
  iio: document bno055 private sysfs attributes
  iio: imu: add Bosch Sensortec BNO055 core driver
  iio: add support for binary attributes
  ...
This commit is contained in:
Greg Kroah-Hartman 2022-09-25 09:09:07 +02:00
commit 4314a0b79f
68 changed files with 4815 additions and 519 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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 <lucas.p.stankus@gmail.com>
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <andrea.merello@iit.it>
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 <dt-bindings/gpio/gpio.h>
serial {
imu {
compatible = "bosch,bno055";
reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>;
clocks = <&imu_clk>;
};
};
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
imu@28 {
compatible = "bosch,bno055";
reg = <0x28>;
reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>;
clocks = <&imu_clk>;
};
};

View File

@ -35,6 +35,9 @@ properties:
- items:
- const: st,asm330lhhx
- const: st,lsm6dsr
- items:
- const: st,lsm6dstx
- const: st,lsm6dst
reg:
maxItems: 1

View File

@ -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 <ak@it-klinger.de>
@ -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

View File

@ -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

View File

@ -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.

View File

@ -10,3 +10,5 @@ Industrial I/O
iio_configfs
ep93xx_adc
bno055

View File

@ -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

View File

@ -8,6 +8,8 @@
#ifndef _ADXL313_H_
#define _ADXL313_H_
#include <linux/iio/iio.h>
/* 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_ */

View File

@ -8,12 +8,18 @@
*/
#include <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "adxl313.h"
static const struct regmap_range adxl312_readable_reg_range[] = {
regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
};
static const struct regmap_range adxl313_readable_reg_range[] = {
regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),
regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
@ -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, &regval);
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, &regval);
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, &regval);
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, &regval);
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, &regval);
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, &regval);
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, &regval);
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;

View File

@ -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",

View File

@ -11,17 +11,38 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/property.h>
#include "adxl313.h"
static const struct regmap_config 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] },
{ }
};

View File

@ -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:

View File

@ -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 <niek.van.agt@topicproducts.com>");
MODULE_LICENSE("GPL v2");

View File

@ -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,

View File

@ -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.

View File

@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
@ -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];

View File

@ -15,6 +15,7 @@
#include <linux/iio/driver.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#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 = &ltc2496_info, },
{},
};
MODULE_DEVICE_TABLE(of, ltc2496_of_match);

View File

@ -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 = &ltc2497core_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ltc2497core_channel;

View File

@ -12,18 +12,31 @@
#include <linux/iio/driver.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <asm/unaligned.h>
#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)&ltc2497_info[TYPE_LTC2497] },
{ "ltc2499", (kernel_ulong_t)&ltc2497_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 = &ltc2497_info[TYPE_LTC2497] },
{ .compatible = "lltc,ltc2499", .data = &ltc2497_info[TYPE_LTC2499] },
{},
};
MODULE_DEVICE_TABLE(of, ltc2497_of_match);

View File

@ -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);

View File

@ -5,16 +5,25 @@
* Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
* Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/trigger.h>
#include <asm/unaligned.h>
#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, &regval, 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, &regval, 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);

View File

@ -9,6 +9,7 @@
*
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
@ -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[] = {

View File

@ -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

View File

@ -13,6 +13,8 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <asm/unaligned.h>
#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);
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __BNO055_H__
#define __BNO055_H__
#include <linux/regmap.h>
#include <linux/types.h>
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

View File

@ -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 <andrea.merello@iit.it>
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#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");

View File

@ -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 <andrea.merello@iit.it>
*
* This driver is based on
* Plantower PMS7003 particulate matter sensor driver
* Which is
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/serdev.h>
#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 <andrea.merello@iit.it>");
MODULE_DESCRIPTION("Bosch BNO055 serdev interface");
MODULE_IMPORT_NS(IIO_BNO055);
MODULE_LICENSE("GPL");

View File

@ -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"

View File

@ -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 <linux/tracepoint.h>
#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 <trace/define_trace.h>

View File

@ -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

View File

@ -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,
};

View File

@ -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).

View File

@ -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 = {

View File

@ -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);

View File

@ -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);

View File

@ -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. */

View File

@ -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 <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver");

View File

@ -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,

View File

@ -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,

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -3,6 +3,87 @@
#include <linux/device.h>
#include <linux/regmap.h>
/* 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,

View File

@ -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, &reg);
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, &reg);
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);

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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" },

View File

@ -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),
},
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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)