1st set of IIO new device support, features and cleanup for 6.6

New device support
 * adi,ad8366
   - Add support for the HMC792 digital attenuator (mostly chip specific data)
 * alwinner,sun20i-gpadc
   - New driver for the integrated ADC on a number of allwinner SoCs
     including dt-binding documentation.
 * microchip,mcp4728
   - New driver for this quad channel DAC. Includes dt-bindings.
 * miramems, da280
   - Add ID for DA217 accelerometer which is compatible with the da280.
 * murata,irs-d200
   - New driver for this passive infrared sensor typically used for human
     detection. Includes bindings and a few pieces of new ABI to
     cover a case of needing to count a number of repeats of an event
     before reporting it.
 * rohm,bu27008
   - Add initial support for the BU27010 RGB + flickering sensor to this
     driver. Substantial refactoring was needed to enable this.
 
 Features
 * adi,admv8818
   - Add mode that bypasses the input and output filters.
 * amlogic,meson
   - Support control of the MUX on channel 7, exposed as multiple channels.
   - Support channel labels.
 * sensirion,scd4x
   - Add pressure compensation. Controlled via an 'output' pressure channel.
 * ti,lmp92040
   - Add IIO buffered supported (read via chrdev).
 * vishay,vcnl4000
   - Add proximity interrupt support for vcnl4200.
   - Add proximity integration time control for vcnl4200.
   - Add illuminance integration time control for vcnl4040 and vcnl4200.
   - Add calibration bias, proximity and illuminance event period, and
     oversampling ratio control for vcnl4040 and vncl4200.
 
 Cleanup and minor fixes
 * core
   - Tidy up handling of set_trigger_state() callback return values
     to consistently assume no positive return values.
   - Use min() rather than min_t() in a case where types were clearly
     the same.
   - Drop some else statements that follow continue with a loop or
     a returns.
   - White space and comment format cleanup.
   - Use sysfs_match_string() helper to improve readability.
   - Use krealloc_array() to make it explicit a krealloc is for an array
     of structures, not just one.
 * tools
   - Tidy up potential overflow in array index.
 * tree wide
   - Fix up includes for DT related headers.
   - Drop some error prints in places where as similar error message
     is printed by the function being called.
   - Tidy up handling of return value from platform_get_irq() to no longer
     take into account 0 as a value that might be returned. Similar for
     fwnode_irq_get().
 * adi,ad7192
   - Add missing error check and improved debug logging.
   - Use sysfs_emit_at() rather than open coded variant.
 * adi,adis16475
   - Drop unused scan element enum entries.
   - Specify that a few more devices support burst32 mode.
 * adi,admv1013
   - Enable all required regulators and document as required in the
     dt-binding.
 * adi,admv1014
   - Make all regulators required in the dt-binding as the device needs
     them all enabled.
 * adi,adxl313
   - Fix wrong enum values being used in the i2c_device_id table.
   - Use i2c_get_match_data() to reduce open coded handling of the
     various id tables.
 * allwinner,gpadc
   - Make the kconfig text more specific to make space for separate drivers
     for other Allwinner devices.
 * amlogic,meson
   - Drop unused timestamp channels as no buffer support.
   - Various minor reorganizations to enable addition of support channel 7
     MUX.
   - Initialize some default values to account for potential previous user
     since reboot.
 * qcom,spmi-adc5
   - Add ADC5_GPIO2_100K_PU support to driver to line up with bindings.
 * qcom,spmi-adc7
   - Use predefined channel ID definitions rather than values.
 * invensense, common
   - Factor out the timestamp handling to a module used by both mpu6050 and
     icm42600.
 * invensense,mpu6050
   - Read as many FIFO elements as possible in one bus access.
 * men,s188
   - Drop redundant initialization of driver owner field.
 * microchip,mcp4018 and mcp4531
   - Use i2c_get_match_data() instead of open coding. Includes making the
     data format the same for the i2c_device_id and firmware match
     tables.
 * semtech,sx9310
   - dt-bindings: Add reference to IIO schema to provide the label property.
 * semtech,sx9324
   - dt-bindings: Add reference to IIO schema to provide the label property.
 * st,stm32-adc
   - Use devm_platform_get_and_ioremap_resource() instead of open coded
     version.
 * st,stm-lptimer-trigger
   - Drop setting platform drvdata as it wasn't then used.
 * ti,ads1015
   - Fix wrong dt binding description of ti,datarate for some devices.
 * vishay,vcnl4200
   - Move to switch statements for channel type checking to make later
     additions simpler.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmTTydERHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0Foj9sw//QDch/sLMb1bEiJTHDwECuSgm+jWth8gi
 CdG3GQerBU2U+eQkf2jgG73HhfHccanKWGAHfkdOLSblcFWJL6Q+MSJCNu4J3kGY
 2XUkdazXMXRbfqoq3vqnC4u6mEMXGTpq8JZoAz7XXCs8GcW/loyqV2E54cbQJxCJ
 KSqCWIhoWAU7/PQkgOJLBnDirMt+rEwUObifG4PJcOYIPq17qS+XhpMVPydNfYgg
 3PJ+jK4Gg6cZsI3lW7vaBoLvzYRn+Ri2vVc2ZnwyLI/y9rEJPjt4aXt9FZbw10Eg
 S/7twD1Ns5n8X6IS3VFA4A4ITu0VFX3pjOg9WLEpkqRJqLt1n2R9pcnTkD6x4a0r
 6LZvmLij9R6ozPZKlalncyOPZamnEcIxyqyuqcEEFDAF6/Mf+VznwBQM8HDhQ/CO
 3OAGkNprtXIvKAz/xiGx9r8z8d5Gt1lD+vclcDcf6U8YwEkMWrc95EVIumcNOkG2
 qNESgzDvOh92aHpKfwrsHproNKcKbtm9he1BrN5u28G+UBA1jsPg15hmYVkv/Hd3
 v6tgvkxVSn2xS94K2BhqjLzOgXgl4EmbSewg5AfPVNYM1RZrMLJvBky1/81JJmsD
 565dcF/vE3/hguFX5lCOKGTxT88pwFNHth26ypAOXuwhUNz+jxp9NJ0Adn/IR8Kp
 hA/SfjALiDA=
 =hTrR
 -----END PGP SIGNATURE-----

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

Jonathan writes:

1st set of IIO new device support, features and cleanup for 6.6

New device support
* adi,ad8366
  - Add support for the HMC792 digital attenuator (mostly chip specific data)
* alwinner,sun20i-gpadc
  - New driver for the integrated ADC on a number of allwinner SoCs
    including dt-binding documentation.
* microchip,mcp4728
  - New driver for this quad channel DAC. Includes dt-bindings.
* miramems, da280
  - Add ID for DA217 accelerometer which is compatible with the da280.
* murata,irs-d200
  - New driver for this passive infrared sensor typically used for human
    detection. Includes bindings and a few pieces of new ABI to
    cover a case of needing to count a number of repeats of an event
    before reporting it.
* rohm,bu27008
  - Add initial support for the BU27010 RGB + flickering sensor to this
    driver. Substantial refactoring was needed to enable this.

Features
* adi,admv8818
  - Add mode that bypasses the input and output filters.
* amlogic,meson
  - Support control of the MUX on channel 7, exposed as multiple channels.
  - Support channel labels.
* sensirion,scd4x
  - Add pressure compensation. Controlled via an 'output' pressure channel.
* ti,lmp92040
  - Add IIO buffered supported (read via chrdev).
* vishay,vcnl4000
  - Add proximity interrupt support for vcnl4200.
  - Add proximity integration time control for vcnl4200.
  - Add illuminance integration time control for vcnl4040 and vcnl4200.
  - Add calibration bias, proximity and illuminance event period, and
    oversampling ratio control for vcnl4040 and vncl4200.

Cleanup and minor fixes
* core
  - Tidy up handling of set_trigger_state() callback return values
    to consistently assume no positive return values.
  - Use min() rather than min_t() in a case where types were clearly
    the same.
  - Drop some else statements that follow continue with a loop or
    a returns.
  - White space and comment format cleanup.
  - Use sysfs_match_string() helper to improve readability.
  - Use krealloc_array() to make it explicit a krealloc is for an array
    of structures, not just one.
* tools
  - Tidy up potential overflow in array index.
* tree wide
  - Fix up includes for DT related headers.
  - Drop some error prints in places where as similar error message
    is printed by the function being called.
  - Tidy up handling of return value from platform_get_irq() to no longer
    take into account 0 as a value that might be returned. Similar for
    fwnode_irq_get().
* adi,ad7192
  - Add missing error check and improved debug logging.
  - Use sysfs_emit_at() rather than open coded variant.
* adi,adis16475
  - Drop unused scan element enum entries.
  - Specify that a few more devices support burst32 mode.
* adi,admv1013
  - Enable all required regulators and document as required in the
    dt-binding.
* adi,admv1014
  - Make all regulators required in the dt-binding as the device needs
    them all enabled.
* adi,adxl313
  - Fix wrong enum values being used in the i2c_device_id table.
  - Use i2c_get_match_data() to reduce open coded handling of the
    various id tables.
* allwinner,gpadc
  - Make the kconfig text more specific to make space for separate drivers
    for other Allwinner devices.
* amlogic,meson
  - Drop unused timestamp channels as no buffer support.
  - Various minor reorganizations to enable addition of support channel 7
    MUX.
  - Initialize some default values to account for potential previous user
    since reboot.
* qcom,spmi-adc5
  - Add ADC5_GPIO2_100K_PU support to driver to line up with bindings.
* qcom,spmi-adc7
  - Use predefined channel ID definitions rather than values.
* invensense, common
  - Factor out the timestamp handling to a module used by both mpu6050 and
    icm42600.
* invensense,mpu6050
  - Read as many FIFO elements as possible in one bus access.
* men,s188
  - Drop redundant initialization of driver owner field.
* microchip,mcp4018 and mcp4531
  - Use i2c_get_match_data() instead of open coding. Includes making the
    data format the same for the i2c_device_id and firmware match
    tables.
* semtech,sx9310
  - dt-bindings: Add reference to IIO schema to provide the label property.
* semtech,sx9324
  - dt-bindings: Add reference to IIO schema to provide the label property.
* st,stm32-adc
  - Use devm_platform_get_and_ioremap_resource() instead of open coded
    version.
* st,stm-lptimer-trigger
  - Drop setting platform drvdata as it wasn't then used.
* ti,ads1015
  - Fix wrong dt binding description of ti,datarate for some devices.
* vishay,vcnl4200
  - Move to switch statements for channel type checking to make later
    additions simpler.

* tag 'iio-for-6.6a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (73 commits)
  Documentation: ABI: testing: admv8818: add bypass
  drivers: iio: filter: admv8818: add bypass mode
  iio: light: bd27008: Support BD27010 RGB
  iio: light: bu27008: add chip info
  dt-bindings: iio: ROHM BU27010 RGBC + flickering sensor
  iio: add MCP4728 I2C DAC driver
  dt-bindings: iio: dac: add mcp4728.yaml
  drivers: iio: admv1013: add vcc regulators
  dt-bindings: iio: admv1013: add vcc regulators
  iio: trigger: stm32-lptimer-trigger: remove unneeded platform_set_drvdata()
  iio: adc: men_z188_adc: Remove redundant initialization owner in men_z188_driver
  dt-bindings: iio: admv1014: make all regs required
  iio: cdc: ad7150: relax return value check for IRQ get
  iio: mb1232: relax return value check for IRQ get
  iio: adc: fix the return value handle for platform_get_irq()
  tools: iio: iio_generic_buffer: Fix some integer type and calculation
  iio: potentiometer: mcp4531: Use i2c_get_match_data()
  iio: potentiometer: mcp4018: Use i2c_get_match_data()
  iio: core: Fix issues and style of the comments
  iio: core: Switch to krealloc_array()
  ...
This commit is contained in:
Greg Kroah-Hartman 2023-08-11 21:22:33 +02:00
commit 43fbd50649
98 changed files with 4539 additions and 944 deletions

View File

@ -2163,3 +2163,19 @@ 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.
What: /sys/.../events/in_proximity_thresh_either_runningperiod
KernelVersion: 6.6
Contact: linux-iio@vger.kernel.org
Description:
A running period of time (in seconds) for which
in_proximity_thresh_either_runningcount amount of conditions
must occur before an event is generated. If direction is not
specified then this period applies to both directions.
What: /sys/.../events/in_proximity_thresh_either_runningcount
KernelVersion: 6.6
Contact: linux-iio@vger.kernel.org
Description:
Number of conditions that must occur, during a running
period, before an event is generated.

View File

@ -7,6 +7,8 @@ Description:
- auto -> Adjust bandpass filter to track changes in input clock rate.
- manual -> disable/unregister the clock rate notifier / input clock tracking.
- bypass -> bypass low pass filter, high pass filter and disable/unregister
the clock rate notifier
What: /sys/bus/iio/devices/iio:deviceX/filter_mode
KernelVersion:

View File

@ -0,0 +1,91 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/allwinner,sun20i-d1-gpadc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Allwinner D1 General Purpose ADC
maintainers:
- Maksim Kiselev <bigunclemax@gmail.com>
properties:
compatible:
enum:
- allwinner,sun20i-d1-gpadc
"#io-channel-cells":
const: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
clocks:
maxItems: 1
interrupts:
maxItems: 1
reg:
maxItems: 1
resets:
maxItems: 1
patternProperties:
"^channel@[0-9a-f]+$":
$ref: adc.yaml
type: object
description:
Represents the internal channels of the ADC.
properties:
reg:
items:
minimum: 0
maximum: 15
required:
- reg
unevaluatedProperties: false
required:
- "#io-channel-cells"
- clocks
- compatible
- interrupts
- reg
- resets
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/sun20i-d1-ccu.h>
#include <dt-bindings/reset/sun20i-d1-ccu.h>
#include <dt-bindings/interrupt-controller/irq.h>
gpadc: adc@2009000 {
compatible = "allwinner,sun20i-d1-gpadc";
reg = <0x2009000 0x400>;
clocks = <&ccu CLK_BUS_GPADC>;
resets = <&ccu RST_BUS_GPADC>;
interrupts = <73 IRQ_TYPE_LEVEL_HIGH>;
#io-channel-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
};
channel@1 {
reg = <1>;
};
};
...

View File

@ -78,9 +78,9 @@ patternProperties:
ti,datarate:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 6
maximum: 7
description: |
Data acquisition rate in samples per second
Data acquisition rate in samples per second for ADS1015, TLA2024
0: 128
1: 250
2: 490
@ -88,6 +88,17 @@ patternProperties:
4: 1600 (default)
5: 2400
6: 3300
7: 3300
Data acquisition rate in samples per second for ADS1115
0: 8
1: 16
2: 32
3: 64
4: 128 (default)
5: 250
6: 475
7: 860
required:
- reg

View File

@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/microchip,mcp4728.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip MCP4728 DAC
maintainers:
- Andrea Collamati <andrea.collamati@gmail.com>
description: |
MCP4728 is a quad channel, 12-bit voltage output
Digital-to-Analog Converter with non-volatile
memory and I2C compatible Serial Interface.
https://www.microchip.com/en-us/product/mcp4728
properties:
compatible:
const: microchip,mcp4728
reg:
maxItems: 1
vdd-supply:
description: |
Provides both power and acts as the reference supply on the MCP4728
when Internal Vref is not selected.
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
dac@60 {
compatible = "microchip,mcp4728";
reg = <0x60>;
vdd-supply = <&vdac_vdd>;
};
};

View File

@ -39,6 +39,46 @@ properties:
description:
Analog voltage regulator.
vcc-drv-supply:
description:
RF Driver voltage regulator.
vcc2-drv-supply:
description:
RF predriver voltage regulator.
vcc-vva-supply:
description:
VVA Control Circuit voltage regulator.
vcc-amp1-supply:
description:
RF Amplifier 1 voltage regulator.
vcc-amp2-supply:
description:
RF Amplifier 2 voltage regulator.
vcc-env-supply:
description:
Envelope Detector voltage regulator.
vcc-bg-supply:
description:
Mixer Chip Band Gap Circuit voltage regulator.
vcc-bg2-supply:
description:
VGA Chip Band Gap Circuit voltage regulator.
vcc-mixer-supply:
description:
Mixer voltage regulator.
vcc-quad-supply:
description:
Quadruppler voltage regulator.
adi,detector-enable:
description:
Enable the Envelope Detector available at output pins VENV_P and
@ -69,6 +109,16 @@ required:
- clocks
- clock-names
- vcm-supply
- vcc-drv-supply
- vcc2-drv-supply
- vcc-vva-supply
- vcc-amp1-supply
- vcc-amp2-supply
- vcc-env-supply
- vcc-bg-supply
- vcc-bg2-supply
- vcc-mixer-supply
- vcc-quad-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
@ -87,6 +137,16 @@ examples:
clocks = <&admv1013_lo>;
clock-names = "lo_in";
vcm-supply = <&vcm>;
vcc-drv-supply = <&vcc_drv>;
vcc2-drv-supply = <&vcc2_drv>;
vcc-vva-supply = <&vcc_vva>;
vcc-amp1-supply = <&vcc_amp1>;
vcc-amp2-supply = <&vcc_amp2>;
vcc-env-supply = <&vcc_env>;
vcc-bg-supply = <&vcc_bg>;
vcc-bg2-supply = <&vcc_bg2>;
vcc-mixer-supply = <&vcc_mixer>;
vcc-quad-supply = <&vcc_quad>;
adi,quad-se-mode = "diff";
adi,detector-enable;
};

View File

@ -103,6 +103,14 @@ required:
- clocks
- clock-names
- vcm-supply
- vcc-if-bb-supply
- vcc-vga-supply
- vcc-vva-supply
- vcc-lna-3p3-supply
- vcc-lna-1p5-supply
- vcc-bg-supply
- vcc-quad-supply
- vcc-mixer-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#

View File

@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/rohm,bu27010.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BU27010 color sensor
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
The ROHM BU27010 is a sensor with 6 photodiodes (red, green, blue, clear,
IR and flickering detection) with five configurable channels. Red, green
and flickering detection being always available and two out of the rest
three (blue, clear, IR) can be selected to be simultaneously measured.
Typical application is adjusting LCD/OLED backlight of TVs, mobile phones
and tablet PCs.
properties:
compatible:
const: rohm,bu27010
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@38 {
compatible = "rohm,bu27010";
reg = <0x38>;
};
};

View File

@ -0,0 +1,60 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/proximity/murata,irsd200.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Murata IRS-D200 PIR sensor
maintainers:
- Waqar Hameed <waqar.hameed@axis.com>
description:
PIR sensor for human detection.
properties:
compatible:
const: murata,irsd200
reg:
items:
- enum:
- 0x48
- 0x49
description: |
When the AD pin is connected to GND, the slave address is 0x48.
When the AD pin is connected to VDD, the slave address is 0x49.
interrupts:
maxItems: 1
description:
Type should be IRQ_TYPE_EDGE_RISING.
vdd-supply:
description:
3.3 V supply voltage.
required:
- compatible
- reg
- interrupts
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
proximity@48 {
compatible = "murata,irsd200";
reg = <0x48>;
interrupts = <24 IRQ_TYPE_EDGE_RISING>;
vdd-supply = <&regulator_3v3>;
};
};
...

View File

@ -15,6 +15,9 @@ description: |
Specifications about the devices can be found at:
https://www.semtech.com/products/smart-sensing/sar-sensors/sx9310
allOf:
- $ref: /schemas/iio/iio.yaml#
properties:
compatible:
enum:
@ -102,7 +105,7 @@ required:
- reg
- "#io-channel-cells"
additionalProperties: false
unevaluatedProperties: false
examples:
- |

View File

@ -13,6 +13,9 @@ maintainers:
description: |
Semtech's SX9324 proximity sensor.
allOf:
- $ref: /schemas/iio/iio.yaml#
properties:
compatible:
const: semtech,sx9324
@ -167,7 +170,7 @@ required:
- reg
- "#io-channel-cells"
additionalProperties: false
unevaluatedProperties: false
examples:
- |

View File

@ -40,8 +40,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
static const struct i2c_device_id adxl313_i2c_id[] = {
{ .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
{ .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
{ .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
{ .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL313] },
{ .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL314] },
{ }
};
@ -65,9 +65,7 @@ static int adxl313_i2c_probe(struct i2c_client *client)
* 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;
chip_data = i2c_get_match_data(client);
regmap = devm_regmap_init_i2c(client,
&adxl31x_i2c_regmap_config[chip_data->type]);

View File

@ -24,19 +24,10 @@ static int adxl355_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
const struct adxl355_chip_info *chip_data;
const struct i2c_device_id *adxl355;
chip_data = device_get_match_data(&client->dev);
if (!chip_data) {
adxl355 = to_i2c_driver(client->dev.driver)->id_table;
if (!adxl355)
return -EINVAL;
chip_data = (void *)i2c_match_id(adxl355, client)->driver_data;
if (!chip_data)
return -EINVAL;
}
chip_data = i2c_get_match_data(client);
if (!chip_data)
return -ENODEV;
regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config);
if (IS_ERR(regmap)) {

View File

@ -8,7 +8,6 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include "adxl372.h"

View File

@ -16,7 +16,6 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for the MiraMEMS DA280 3-axis accelerometer and
* IIO driver for the MiraMEMS DA217 and DA280 3-axis accelerometer and
* IIO driver for the MiraMEMS DA226 2-axis accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
@ -23,7 +23,7 @@
#define DA280_MODE_ENABLE 0x1e
#define DA280_MODE_DISABLE 0x9e
enum da280_chipset { da226, da280 };
enum da280_chipset { da217, da226, da280 };
/*
* a value of + or -4096 corresponds to + or - 1G
@ -134,7 +134,10 @@ static int da280_probe(struct i2c_client *client)
chip = id->driver_data;
}
if (chip == da226) {
if (chip == da217) {
indio_dev->name = "da217";
indio_dev->num_channels = 3;
} else if (chip == da226) {
indio_dev->name = "da226";
indio_dev->num_channels = 2;
} else {
@ -166,12 +169,14 @@ static int da280_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
static const struct acpi_device_id da280_acpi_match[] = {
{"NSA2513", da217},
{"MIRAACC", da280},
{},
};
MODULE_DEVICE_TABLE(acpi, da280_acpi_match);
static const struct i2c_device_id da280_i2c_id[] = {
{ "da217", da217 },
{ "da226", da226 },
{ "da280", da280 },
{}

View File

@ -2,7 +2,6 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/slab.h>

View File

@ -28,7 +28,7 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/events.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>

View File

@ -1116,7 +1116,7 @@ config STMPE_ADC
built-in ADC block (stmpe811).
config SUN4I_GPADC
tristate "Support for the Allwinner SoCs GPADC"
tristate "Allwinner A10/A13/A31 and similar GPADCs driver"
depends on IIO
depends on MFD_SUN4I_GPADC || MACH_SUN8I
depends on THERMAL || !THERMAL_OF
@ -1134,6 +1134,16 @@ config SUN4I_GPADC
To compile this driver as a module, choose M here: the module will be
called sun4i-gpadc-iio.
config SUN20I_GPADC
tristate "Allwinner D1/T113s/T507/R329 and similar GPADCs driver"
depends on ARCH_SUNXI || COMPILE_TEST
help
Say yes here to build support for Allwinner (D1, T113, T507 and R329)
SoCs GPADC. This ADC provides up to 16 channels.
To compile this driver as a module, choose M here: the module will be
called sun20i-gpadc-iio.
config TI_ADC081C
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
depends on I2C

View File

@ -96,6 +96,7 @@ obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
obj-$(CONFIG_SUN20I_GPADC) += sun20i-gpadc-iio.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o

View File

@ -1099,14 +1099,12 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
if (gpadc->irq_sw < 0)
return dev_err_probe(dev, gpadc->irq_sw,
"failed to get platform sw_conv_end irq\n");
return gpadc->irq_sw;
if (is_ab8500(gpadc->ab8500)) {
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
if (gpadc->irq_hw < 0)
return dev_err_probe(dev, gpadc->irq_hw,
"failed to get platform hw_conv_end irq\n");
return gpadc->irq_hw;
} else {
gpadc->irq_hw = 0;
}

View File

@ -14,7 +14,7 @@
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>

View File

@ -16,7 +16,7 @@
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@ -402,8 +402,8 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
id &= AD7192_ID_MASK;
if (id != st->chip_info->chip_id)
dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
id);
dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n",
id, st->chip_info->chip_id);
st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) |
AD7192_MODE_CLKSRC(st->clock_sel) |
@ -561,9 +561,8 @@ static ssize_t ad7192_show_filter_avail(struct device *dev,
ad7192_get_available_filter_freq(st, freq_avail);
for (i = 0; i < ARRAY_SIZE(freq_avail); i++)
len += scnprintf(buf + len, PAGE_SIZE - len,
"%d.%d ", freq_avail[i] / 1000,
freq_avail[i] % 1000);
len += sysfs_emit_at(buf, len, "%d.%03d ", freq_avail[i] / 1000,
freq_avail[i] % 1000);
buf[len - 1] = '\n';
@ -972,11 +971,6 @@ static void ad7192_reg_disable(void *reg)
regulator_disable(reg);
}
static void ad7192_clk_disable(void *clk)
{
clk_disable_unprepare(clk);
}
static int ad7192_probe(struct spi_device *spi)
{
struct ad7192_state *st;
@ -1036,7 +1030,9 @@ static int ad7192_probe(struct spi_device *spi)
else
indio_dev->info = &ad7192_info;
ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
if (ret)
return ret;
ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
if (ret)
@ -1044,7 +1040,7 @@ static int ad7192_probe(struct spi_device *spi)
st->fclk = AD7192_INT_FREQ_MHZ;
st->mclk = devm_clk_get_optional(&spi->dev, "mclk");
st->mclk = devm_clk_get_optional_enabled(&spi->dev, "mclk");
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
@ -1052,15 +1048,6 @@ static int ad7192_probe(struct spi_device *spi)
if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
st->clock_sel == AD7192_CLK_EXT_MCLK2) {
ret = clk_prepare_enable(st->mclk);
if (ret < 0)
return ret;
ret = devm_add_action_or_reset(&spi->dev, ad7192_clk_disable,
st->mclk);
if (ret)
return ret;
st->fclk = clk_get_rate(st->mclk);
if (!ad7192_valid_external_frequency(st->fclk)) {
dev_err(&spi->dev,

View File

@ -13,7 +13,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/iio/iio.h>

View File

@ -11,8 +11,9 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>

View File

@ -16,7 +16,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>

View File

@ -540,8 +540,8 @@ static int iproc_adc_probe(struct platform_device *pdev)
}
adc_priv->irqno = platform_get_irq(pdev, 0);
if (adc_priv->irqno <= 0)
return -ENODEV;
if (adc_priv->irqno < 0)
return adc_priv->irqno;
ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
IPROC_ADC_AUXIN_SCAN_ENA, 0);

View File

@ -9,7 +9,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>

View File

@ -496,7 +496,7 @@ static int imx7d_adc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return dev_err_probe(dev, irq, "Failed getting irq\n");
return irq;
info->clk = devm_clk_get(dev, "adc");
if (IS_ERR(info->clk))

View File

@ -28,7 +28,7 @@
#include <linux/iio/sysfs.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/sched/task.h>
#include <linux/util_macros.h>

View File

@ -176,8 +176,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0)
return -ENXIO;
if (irq < 0)
return irq;
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
LPC32XXAD_NAME, st);

View File

@ -161,7 +161,6 @@ MODULE_DEVICE_TABLE(mcb, men_z188_ids);
static struct mcb_driver men_z188_driver = {
.driver = {
.name = "z188-adc",
.owner = THIS_MODULE,
},
.probe = men_z188_probe,
.remove = men_z188_remove,

View File

@ -17,7 +17,6 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@ -156,6 +155,10 @@
*/
#define MESON_SAR_ADC_REG11 0x2c
#define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13)
#define MESON_SAR_ADC_REG11_CMV_SEL BIT(6)
#define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5)
#define MESON_SAR_ADC_REG11_EOC BIT(1)
#define MESON_SAR_ADC_REG11_VREF_SEL BIT(0)
#define MESON_SAR_ADC_REG13 0x34
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
@ -163,6 +166,7 @@
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6
#define MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL 7
#define MESON_SAR_ADC_TEMP_OFFSET 27
/* temperature sensor calibration information in eFuse */
@ -202,29 +206,22 @@
.datasheet_name = "TEMP_SENSOR", \
}
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
MESON_SAR_ADC_CHAN(0),
MESON_SAR_ADC_CHAN(1),
MESON_SAR_ADC_CHAN(2),
MESON_SAR_ADC_CHAN(3),
MESON_SAR_ADC_CHAN(4),
MESON_SAR_ADC_CHAN(5),
MESON_SAR_ADC_CHAN(6),
MESON_SAR_ADC_CHAN(7),
IIO_CHAN_SOFT_TIMESTAMP(8),
};
#define MESON_SAR_ADC_MUX(_chan, _sel) { \
.type = IIO_VOLTAGE, \
.channel = _chan, \
.indexed = 1, \
.address = MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
BIT(IIO_CHAN_INFO_CALIBSCALE), \
.datasheet_name = "SAR_ADC_MUX_"#_sel, \
}
static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
MESON_SAR_ADC_CHAN(0),
MESON_SAR_ADC_CHAN(1),
MESON_SAR_ADC_CHAN(2),
MESON_SAR_ADC_CHAN(3),
MESON_SAR_ADC_CHAN(4),
MESON_SAR_ADC_CHAN(5),
MESON_SAR_ADC_CHAN(6),
MESON_SAR_ADC_CHAN(7),
MESON_SAR_ADC_TEMP_CHAN(8),
IIO_CHAN_SOFT_TIMESTAMP(9),
enum meson_sar_adc_vref_sel {
VREF_CALIBATION_VOLTAGE = 0,
VREF_VDDA = 1,
};
enum meson_sar_adc_avg_mode {
@ -249,6 +246,72 @@ enum meson_sar_adc_chan7_mux_sel {
CHAN7_MUX_CH7_INPUT = 0x7,
};
enum meson_sar_adc_channel_index {
NUM_CHAN_0,
NUM_CHAN_1,
NUM_CHAN_2,
NUM_CHAN_3,
NUM_CHAN_4,
NUM_CHAN_5,
NUM_CHAN_6,
NUM_CHAN_7,
NUM_CHAN_TEMP,
NUM_MUX_0_VSS,
NUM_MUX_1_VDD_DIV4,
NUM_MUX_2_VDD_DIV2,
NUM_MUX_3_VDD_MUL3_DIV4,
NUM_MUX_4_VDD,
};
static enum meson_sar_adc_chan7_mux_sel chan7_mux_values[] = {
CHAN7_MUX_VSS,
CHAN7_MUX_VDD_DIV4,
CHAN7_MUX_VDD_DIV2,
CHAN7_MUX_VDD_MUL3_DIV4,
CHAN7_MUX_VDD,
};
static const char * const chan7_mux_names[] = {
[CHAN7_MUX_VSS] = "gnd",
[CHAN7_MUX_VDD_DIV4] = "0.25vdd",
[CHAN7_MUX_VDD_DIV2] = "0.5vdd",
[CHAN7_MUX_VDD_MUL3_DIV4] = "0.75vdd",
[CHAN7_MUX_VDD] = "vdd",
};
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
MESON_SAR_ADC_CHAN(NUM_CHAN_0),
MESON_SAR_ADC_CHAN(NUM_CHAN_1),
MESON_SAR_ADC_CHAN(NUM_CHAN_2),
MESON_SAR_ADC_CHAN(NUM_CHAN_3),
MESON_SAR_ADC_CHAN(NUM_CHAN_4),
MESON_SAR_ADC_CHAN(NUM_CHAN_5),
MESON_SAR_ADC_CHAN(NUM_CHAN_6),
MESON_SAR_ADC_CHAN(NUM_CHAN_7),
MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0),
MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1),
MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2),
MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3),
MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4),
};
static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
MESON_SAR_ADC_CHAN(NUM_CHAN_0),
MESON_SAR_ADC_CHAN(NUM_CHAN_1),
MESON_SAR_ADC_CHAN(NUM_CHAN_2),
MESON_SAR_ADC_CHAN(NUM_CHAN_3),
MESON_SAR_ADC_CHAN(NUM_CHAN_4),
MESON_SAR_ADC_CHAN(NUM_CHAN_5),
MESON_SAR_ADC_CHAN(NUM_CHAN_6),
MESON_SAR_ADC_CHAN(NUM_CHAN_7),
MESON_SAR_ADC_TEMP_CHAN(NUM_CHAN_TEMP),
MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0),
MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1),
MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2),
MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3),
MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4),
};
struct meson_sar_adc_param {
bool has_bl30_integration;
unsigned long clock_rate;
@ -258,6 +321,13 @@ struct meson_sar_adc_param {
u8 temperature_trimming_bits;
unsigned int temperature_multiplier;
unsigned int temperature_divider;
u8 disable_ring_counter;
bool has_reg11;
bool has_vref_select;
u8 vref_select;
u8 cmv_select;
u8 adc_eoc;
enum meson_sar_adc_vref_sel vref_volatge;
};
struct meson_sar_adc_data {
@ -285,6 +355,7 @@ struct meson_sar_adc_priv {
bool temperature_sensor_calibrated;
u8 temperature_sensor_coefficient;
u16 temperature_sensor_adc_val;
enum meson_sar_adc_chan7_mux_sel chan7_mux_sel;
};
static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
@ -301,6 +372,17 @@ static const struct regmap_config meson_sar_adc_regmap_config_meson8 = {
.max_register = MESON_SAR_ADC_DELTA_10,
};
static const struct iio_chan_spec *
find_channel_by_num(struct iio_dev *indio_dev, int num)
{
int i;
for (i = 0; i < indio_dev->num_channels; i++)
if (indio_dev->channels[i].channel == num)
return &indio_dev->channels[i];
return NULL;
}
static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
@ -338,6 +420,21 @@ static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
1, 10000);
}
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
enum meson_sar_adc_chan7_mux_sel sel)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
u32 regval;
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
usleep_range(10, 20);
priv->chan7_mux_sel = sel;
}
static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val)
@ -431,22 +528,18 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
regmap_update_bits(priv->regmap,
MESON_SAR_ADC_DELTA_10,
MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval);
} else if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL) {
enum meson_sar_adc_chan7_mux_sel sel;
if (chan->channel == NUM_CHAN_7)
sel = CHAN7_MUX_CH7_INPUT;
else
sel = chan7_mux_values[chan->channel - NUM_MUX_0_VSS];
if (sel != priv->chan7_mux_sel)
meson_sar_adc_set_chan7_mux(indio_dev, sel);
}
}
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
enum meson_sar_adc_chan7_mux_sel sel)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
u32 regval;
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
usleep_range(10, 20);
}
static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
@ -821,6 +914,22 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK,
regval);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW,
MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW);
/*
* set up the input channel muxes in MESON_SAR_ADC_AUX_SW
* (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable
@ -873,6 +982,35 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
MESON_SAR_ADC_DELTA_10_TS_REVE0, 0);
}
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN,
priv->param->disable_ring_counter);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN,
regval);
if (priv->param->has_reg11) {
regval = FIELD_PREP(MESON_SAR_ADC_REG11_EOC, priv->param->adc_eoc);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
MESON_SAR_ADC_REG11_EOC, regval);
if (priv->param->has_vref_select) {
regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_SEL,
priv->param->vref_select);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
MESON_SAR_ADC_REG11_VREF_SEL, regval);
}
regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_VOLTAGE,
priv->param->vref_volatge);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
MESON_SAR_ADC_REG11_VREF_VOLTAGE, regval);
regval = FIELD_PREP(MESON_SAR_ADC_REG11_CMV_SEL,
priv->param->cmv_select);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
MESON_SAR_ADC_REG11_CMV_SEL, regval);
}
ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
if (ret)
return dev_err_probe(dev, ret, "failed to set adc parent to clkin\n");
@ -1016,7 +1154,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
&indio_dev->channels[7],
find_channel_by_num(indio_dev,
NUM_MUX_1_VDD_DIV4),
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
if (ret < 0)
goto out;
@ -1024,7 +1163,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
&indio_dev->channels[7],
find_channel_by_num(indio_dev,
NUM_MUX_3_VDD_MUL3_DIV4),
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
if (ret < 0)
goto out;
@ -1045,8 +1185,23 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
return ret;
}
static int read_label(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
char *label)
{
if (chan->type == IIO_TEMP)
return sprintf(label, "temp-sensor\n");
if (chan->type == IIO_VOLTAGE && chan->channel >= NUM_MUX_0_VSS)
return sprintf(label, "%s\n",
chan7_mux_names[chan->channel - NUM_MUX_0_VSS]);
if (chan->type == IIO_VOLTAGE)
return sprintf(label, "channel-%d\n", chan->channel);
return 0;
}
static const struct iio_info meson_sar_adc_iio_info = {
.read_raw = meson_sar_adc_iio_info_read_raw,
.read_label = read_label,
};
static const struct meson_sar_adc_param meson_sar_adc_meson8_param = {
@ -1077,6 +1232,9 @@ static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
.bandgap_reg = MESON_SAR_ADC_REG11,
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
.resolution = 10,
.has_reg11 = true,
.vref_volatge = 1,
.cmv_select = 1,
};
static const struct meson_sar_adc_param meson_sar_adc_gxl_param = {
@ -1085,6 +1243,10 @@ static const struct meson_sar_adc_param meson_sar_adc_gxl_param = {
.bandgap_reg = MESON_SAR_ADC_REG11,
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
.resolution = 12,
.disable_ring_counter = 1,
.has_reg11 = true,
.vref_volatge = 1,
.cmv_select = 1,
};
static const struct meson_sar_adc_param meson_sar_adc_g12a_param = {
@ -1093,6 +1255,11 @@ static const struct meson_sar_adc_param meson_sar_adc_g12a_param = {
.bandgap_reg = MESON_SAR_ADC_REG11,
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
.resolution = 12,
.disable_ring_counter = 1,
.has_reg11 = true,
.adc_eoc = 1,
.has_vref_select = true,
.vref_select = VREF_VDDA,
};
static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {

View File

@ -244,8 +244,8 @@ static int npcm_adc_probe(struct platform_device *pdev)
info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
ret = -EINVAL;
if (irq < 0) {
ret = irq;
goto err_disable_clk;
}

View File

@ -18,7 +18,6 @@
#include <linux/mfd/palmas.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
@ -916,8 +915,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
adc->irq_auto_0 = platform_get_irq(pdev, 1);
if (adc->irq_auto_0 < 0)
return dev_err_probe(adc->dev, adc->irq_auto_0,
"get auto0 irq failed\n");
return adc->irq_auto_0;
ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, NULL,
palmas_gpadc_irq_auto, IRQF_ONESHOT,
@ -929,8 +927,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
adc->irq_auto_1 = platform_get_irq(pdev, 2);
if (adc->irq_auto_1 < 0)
return dev_err_probe(adc->dev, adc->irq_auto_1,
"get auto1 irq failed\n");
return adc->irq_auto_1;
ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, NULL,
palmas_gpadc_irq_auto, IRQF_ONESHOT,

View File

@ -555,6 +555,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_PM5_SMB_TEMP)
[ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0,

View File

@ -13,7 +13,6 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>

View File

@ -11,7 +11,6 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@ -467,7 +466,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return dev_err_probe(&pdev->dev, irq, "failed to get irq\n");
return irq;
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
0, dev_name(&pdev->dev), info);

View File

@ -7,7 +7,6 @@
#include <linux/mutex.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>

View File

@ -310,8 +310,8 @@ static int spear_adc_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
ret = -EINVAL;
if (irq < 0) {
ret = irq;
goto errout2;
}

View File

@ -18,6 +18,8 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@ -723,8 +725,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
priv->nb_adc_max = priv->cfg->num_adcs;
spin_lock_init(&priv->common.lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->common.base = devm_ioremap_resource(&pdev->dev, res);
priv->common.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(priv->common.base))
return PTR_ERR(priv->common.base);
priv->common.phys_base = res->start;

View File

@ -19,7 +19,8 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>

View File

@ -12,8 +12,10 @@
#include <linux/iio/sysfs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>

View File

@ -14,7 +14,7 @@
#include <linux/kernel.h>
#include <linux/mfd/stmpe.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/device.h>

View File

@ -0,0 +1,276 @@
// SPDX-License-Identifier: GPL-2.0
/*
* GPADC driver for sunxi platforms (D1, T113-S3 and R329)
* Copyright (c) 2023 Maksim Kiselev <bigunclemax@gmail.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/reset.h>
#include <linux/iio/iio.h>
#define SUN20I_GPADC_DRIVER_NAME "sun20i-gpadc"
/* Register map definition */
#define SUN20I_GPADC_SR 0x00
#define SUN20I_GPADC_CTRL 0x04
#define SUN20I_GPADC_CS_EN 0x08
#define SUN20I_GPADC_FIFO_INTC 0x0c
#define SUN20I_GPADC_FIFO_INTS 0x10
#define SUN20I_GPADC_FIFO_DATA 0X14
#define SUN20I_GPADC_CB_DATA 0X18
#define SUN20I_GPADC_DATAL_INTC 0x20
#define SUN20I_GPADC_DATAH_INTC 0x24
#define SUN20I_GPADC_DATA_INTC 0x28
#define SUN20I_GPADC_DATAL_INTS 0x30
#define SUN20I_GPADC_DATAH_INTS 0x34
#define SUN20I_GPADC_DATA_INTS 0x38
#define SUN20I_GPADC_CH_CMP_DATA(x) (0x40 + (x) * 4)
#define SUN20I_GPADC_CH_DATA(x) (0x80 + (x) * 4)
#define SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK BIT(23)
#define SUN20I_GPADC_CTRL_WORK_MODE_MASK GENMASK(19, 18)
#define SUN20I_GPADC_CTRL_ADC_EN_MASK BIT(16)
#define SUN20I_GPADC_CS_EN_ADC_CH(x) BIT(x)
#define SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(x) BIT(x)
#define SUN20I_GPADC_WORK_MODE_SINGLE 0
struct sun20i_gpadc_iio {
void __iomem *regs;
struct completion completion;
int last_channel;
/*
* Lock to protect the device state during a potential concurrent
* read access from userspace. Reading a raw value requires a sequence
* of register writes, then a wait for a completion callback,
* and finally a register read, during which userspace could issue
* another read request. This lock protects a read access from
* ocurring before another one has finished.
*/
struct mutex lock;
};
static int sun20i_gpadc_adc_read(struct sun20i_gpadc_iio *info,
struct iio_chan_spec const *chan, int *val)
{
u32 ctrl;
int ret = IIO_VAL_INT;
mutex_lock(&info->lock);
reinit_completion(&info->completion);
if (info->last_channel != chan->channel) {
info->last_channel = chan->channel;
/* enable the analog input channel */
writel(SUN20I_GPADC_CS_EN_ADC_CH(chan->channel),
info->regs + SUN20I_GPADC_CS_EN);
/* enable the data irq for input channel */
writel(SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(chan->channel),
info->regs + SUN20I_GPADC_DATA_INTC);
}
/* enable the ADC function */
ctrl = readl(info->regs + SUN20I_GPADC_CTRL);
ctrl |= FIELD_PREP(SUN20I_GPADC_CTRL_ADC_EN_MASK, 1);
writel(ctrl, info->regs + SUN20I_GPADC_CTRL);
/*
* According to the datasheet maximum acquire time(TACQ) can be
* (65535+1)/24Mhz and conversion time(CONV_TIME) is always constant
* and equal to 14/24Mhz, so (TACQ+CONV_TIME) <= 2.73125ms.
* A 10ms delay should be enough to make sure an interrupt occurs in
* normal conditions. If it doesn't occur, then there is a timeout.
*/
if (!wait_for_completion_timeout(&info->completion, msecs_to_jiffies(10))) {
ret = -ETIMEDOUT;
goto err_unlock;
}
/* read the ADC data */
*val = readl(info->regs + SUN20I_GPADC_CH_DATA(chan->channel));
err_unlock:
mutex_unlock(&info->lock);
return ret;
}
static int sun20i_gpadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct sun20i_gpadc_iio *info = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return sun20i_gpadc_adc_read(info, chan, val);
case IIO_CHAN_INFO_SCALE:
/* value in mv = 1800mV / 4096 raw */
*val = 1800;
*val2 = 12;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static irqreturn_t sun20i_gpadc_irq_handler(int irq, void *data)
{
struct sun20i_gpadc_iio *info = data;
/* clear data interrupt status register */
writel(GENMASK(31, 0), info->regs + SUN20I_GPADC_DATA_INTS);
complete(&info->completion);
return IRQ_HANDLED;
}
static const struct iio_info sun20i_gpadc_iio_info = {
.read_raw = sun20i_gpadc_read_raw,
};
static void sun20i_gpadc_reset_assert(void *data)
{
struct reset_control *rst = data;
reset_control_assert(rst);
}
static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev,
struct device *dev)
{
unsigned int channel;
int num_channels, i, ret;
struct iio_chan_spec *channels;
struct fwnode_handle *node;
num_channels = device_get_child_node_count(dev);
if (num_channels == 0)
return dev_err_probe(dev, -ENODEV, "no channel children\n");
channels = devm_kcalloc(dev, num_channels, sizeof(*channels),
GFP_KERNEL);
if (!channels)
return -ENOMEM;
i = 0;
device_for_each_child_node(dev, node) {
ret = fwnode_property_read_u32(node, "reg", &channel);
if (ret) {
fwnode_handle_put(node);
return dev_err_probe(dev, ret, "invalid channel number\n");
}
channels[i].type = IIO_VOLTAGE;
channels[i].indexed = 1;
channels[i].channel = channel;
channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
i++;
}
indio_dev->channels = channels;
indio_dev->num_channels = num_channels;
return 0;
}
static int sun20i_gpadc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct sun20i_gpadc_iio *info;
struct reset_control *rst;
struct clk *clk;
int irq;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->last_channel = -1;
mutex_init(&info->lock);
init_completion(&info->completion);
ret = sun20i_gpadc_alloc_channels(indio_dev, dev);
if (ret)
return ret;
indio_dev->info = &sun20i_gpadc_iio_info;
indio_dev->name = SUN20I_GPADC_DRIVER_NAME;
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock\n");
rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst), "failed to get reset control\n");
ret = reset_control_deassert(rst);
if (ret)
return dev_err_probe(dev, ret, "failed to deassert reset\n");
ret = devm_add_action_or_reset(dev, sun20i_gpadc_reset_assert, rst);
if (ret)
return ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, sun20i_gpadc_irq_handler, 0,
dev_name(dev), info);
if (ret)
return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq);
writel(FIELD_PREP(SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK, 1) |
FIELD_PREP(SUN20I_GPADC_CTRL_WORK_MODE_MASK, SUN20I_GPADC_WORK_MODE_SINGLE),
info->regs + SUN20I_GPADC_CTRL);
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "could not register the device\n");
return 0;
}
static const struct of_device_id sun20i_gpadc_of_id[] = {
{ .compatible = "allwinner,sun20i-d1-gpadc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun20i_gpadc_of_id);
static struct platform_driver sun20i_gpadc_driver = {
.driver = {
.name = SUN20I_GPADC_DRIVER_NAME,
.of_match_table = sun20i_gpadc_of_id,
},
.probe = sun20i_gpadc_probe,
};
module_platform_driver(sun20i_gpadc_driver);
MODULE_DESCRIPTION("ADC driver for sunxi platforms");
MODULE_AUTHOR("Maksim Kiselev <bigunclemax@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -24,7 +24,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>

View File

@ -16,7 +16,10 @@
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/driver.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define TI_LMP92064_REG_CONFIG_A 0x0000
#define TI_LMP92064_REG_CONFIG_B 0x0001
@ -91,6 +94,12 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
.address = TI_LMP92064_CHAN_INC,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.scan_index = TI_LMP92064_CHAN_INC,
.scan_type = {
.sign = 'u',
.realbits = 12,
.storagebits = 16,
},
.datasheet_name = "INC",
},
{
@ -98,8 +107,20 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
.address = TI_LMP92064_CHAN_INV,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.scan_index = TI_LMP92064_CHAN_INV,
.scan_type = {
.sign = 'u',
.realbits = 12,
.storagebits = 16,
},
.datasheet_name = "INV",
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static const unsigned long lmp92064_scan_masks[] = {
BIT(TI_LMP92064_CHAN_INC) | BIT(TI_LMP92064_CHAN_INV),
0
};
static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
@ -171,6 +192,32 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev,
}
}
static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
struct {
u16 values[2];
int64_t timestamp __aligned(8);
} data;
int ret;
memset(&data, 0, sizeof(data));
ret = lmp92064_read_meas(priv, data.values);
if (ret)
goto err;
iio_push_to_buffers_with_timestamp(indio_dev, &data,
iio_get_time_ns(indio_dev));
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int lmp92064_reset(struct lmp92064_adc_priv *priv,
struct gpio_desc *gpio_reset)
{
@ -301,6 +348,12 @@ static int lmp92064_adc_probe(struct spi_device *spi)
indio_dev->channels = lmp92064_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels);
indio_dev->info = &lmp92064_adc_info;
indio_dev->available_scan_masks = lmp92064_scan_masks;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
lmp92064_trigger_handler, NULL);
if (ret)
return dev_err_probe(dev, ret, "Failed to setup buffered read\n");
return devm_iio_device_register(dev, indio_dev);
}

View File

@ -14,7 +14,6 @@
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#include <linux/iopoll.h>

View File

@ -18,6 +18,7 @@ config AD8366
AD8366 Dual-Digital Variable Gain Amplifier (VGA)
ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
ADL5240 Digitally controlled variable gain amplifier (VGA)
HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator
HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
To compile this driver as a module, choose M here: the

View File

@ -5,6 +5,7 @@
* AD8366 Dual-Digital Variable Gain Amplifier (VGA)
* ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
* ADL5240 Digitally controlled variable gain amplifier (VGA)
* HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator
* HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
*
* Copyright 2012-2019 Analog Devices Inc.
@ -28,6 +29,7 @@ enum ad8366_type {
ID_AD8366,
ID_ADA4961,
ID_ADL5240,
ID_HMC792,
ID_HMC1119,
};
@ -64,6 +66,10 @@ static struct ad8366_info ad8366_infos[] = {
.gain_min = -11500,
.gain_max = 20000,
},
[ID_HMC792] = {
.gain_min = -15750,
.gain_max = 0,
},
[ID_HMC1119] = {
.gain_min = -31750,
.gain_max = 0,
@ -90,6 +96,7 @@ static int ad8366_write(struct iio_dev *indio_dev,
case ID_ADL5240:
st->data[0] = (ch_a & 0x3F);
break;
case ID_HMC792:
case ID_HMC1119:
st->data[0] = ch_a;
break;
@ -127,6 +134,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev,
case ID_ADL5240:
gain = 20000 - 31500 + code * 500;
break;
case ID_HMC792:
gain = -1 * code * 500;
break;
case ID_HMC1119:
gain = -1 * code * 250;
break;
@ -176,6 +186,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
case ID_ADL5240:
code = ((gain - 500 - 20000) / 500) & 0x3F;
break;
case ID_HMC792:
code = (abs(gain) / 500) & 0x3F;
break;
case ID_HMC1119:
code = (abs(gain) / 250) & 0x7F;
break;
@ -261,6 +274,7 @@ static int ad8366_probe(struct spi_device *spi)
break;
case ID_ADA4961:
case ID_ADL5240:
case ID_HMC792:
case ID_HMC1119:
st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(st->reset_gpio)) {
@ -314,6 +328,7 @@ static const struct spi_device_id ad8366_id[] = {
{"ad8366", ID_AD8366},
{"ada4961", ID_ADA4961},
{"adl5240", ID_ADL5240},
{"hmc792a", ID_HMC792},
{"hmc1119", ID_HMC1119},
{}
};

View File

@ -541,6 +541,7 @@ static int ad7150_probe(struct i2c_client *client)
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct ad7150_chip_info *chip;
struct iio_dev *indio_dev;
bool use_irq = true;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
@ -561,14 +562,13 @@ static int ad7150_probe(struct i2c_client *client)
chip->interrupts[0] = fwnode_irq_get(dev_fwnode(&client->dev), 0);
if (chip->interrupts[0] < 0)
return chip->interrupts[0];
if (id->driver_data == AD7150) {
use_irq = false;
else if (id->driver_data == AD7150) {
chip->interrupts[1] = fwnode_irq_get(dev_fwnode(&client->dev), 1);
if (chip->interrupts[1] < 0)
return chip->interrupts[1];
use_irq = false;
}
if (chip->interrupts[0] &&
(id->driver_data == AD7151 || chip->interrupts[1])) {
if (use_irq) {
irq_set_status_flags(chip->interrupts[0], IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(&client->dev,
chip->interrupts[0],

View File

@ -36,6 +36,8 @@
#define SCD4X_WRITE_BUF_SIZE 5
#define SCD4X_FRC_MIN_PPM 0
#define SCD4X_FRC_MAX_PPM 2000
#define SCD4X_PRESSURE_COMP_MIN_MBAR 700
#define SCD4X_PRESSURE_COMP_MAX_MBAR 1200
#define SCD4X_READY_MASK 0x01
/*Commands SCD4X*/
@ -45,6 +47,8 @@ enum scd4x_cmd {
CMD_STOP_MEAS = 0x3f86,
CMD_SET_TEMP_OFFSET = 0x241d,
CMD_GET_TEMP_OFFSET = 0x2318,
CMD_SET_AMB_PRESSURE = 0xe000,
CMD_GET_AMB_PRESSURE = 0xe000,
CMD_FRC = 0x362f,
CMD_SET_ASC = 0x2416,
CMD_GET_ASC = 0x2313,
@ -137,7 +141,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd,
* Measurement needs to be stopped before sending commands.
* Except for reading measurement and data ready command.
*/
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) {
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&
(cmd != CMD_GET_AMB_PRESSURE)) {
ret = scd4x_send_command(state, CMD_STOP_MEAS);
if (ret)
return ret;
@ -166,7 +171,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd,
}
/* start measurement */
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) {
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&
(cmd != CMD_GET_AMB_PRESSURE)) {
ret = scd4x_send_command(state, CMD_START_MEAS);
if (ret)
return ret;
@ -188,9 +194,11 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a
buf[4] = crc;
/* measurement needs to be stopped before sending commands */
ret = scd4x_send_command(state, CMD_STOP_MEAS);
if (ret)
return ret;
if (cmd != CMD_SET_AMB_PRESSURE) {
ret = scd4x_send_command(state, CMD_STOP_MEAS);
if (ret)
return ret;
}
/* execution time */
msleep_interruptible(500);
@ -200,7 +208,7 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a
return ret;
/* start measurement, except for forced calibration command */
if (cmd != CMD_FRC) {
if ((cmd != CMD_FRC) && (cmd != CMD_SET_AMB_PRESSURE)) {
ret = scd4x_send_command(state, CMD_START_MEAS);
if (ret)
return ret;
@ -338,6 +346,18 @@ static int scd4x_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->output) {
mutex_lock(&state->lock);
ret = scd4x_read(state, CMD_GET_AMB_PRESSURE, &tmp, sizeof(tmp));
mutex_unlock(&state->lock);
if (ret)
return ret;
*val = be16_to_cpu(tmp);
return IIO_VAL_INT;
}
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
@ -386,6 +406,25 @@ static int scd4x_read_raw(struct iio_dev *indio_dev,
}
}
static const int scd4x_pressure_calibbias_available[] = {
SCD4X_PRESSURE_COMP_MIN_MBAR, 1, SCD4X_PRESSURE_COMP_MAX_MBAR,
};
static int scd4x_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_RAW:
*vals = scd4x_pressure_calibbias_available;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
}
return -EINVAL;
}
static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
@ -399,6 +438,21 @@ static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const
mutex_unlock(&state->lock);
return ret;
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_PRESSURE:
if (val < SCD4X_PRESSURE_COMP_MIN_MBAR ||
val > SCD4X_PRESSURE_COMP_MAX_MBAR)
return -EINVAL;
mutex_lock(&state->lock);
ret = scd4x_write(state, CMD_SET_AMB_PRESSURE, val);
mutex_unlock(&state->lock);
return ret;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
@ -503,9 +557,22 @@ static const struct iio_info scd4x_info = {
.attrs = &scd4x_attr_group,
.read_raw = scd4x_read_raw,
.write_raw = scd4x_write_raw,
.read_avail = scd4x_read_avail,
};
static const struct iio_chan_spec scd4x_channels[] = {
{
/*
* this channel is special in a sense we are pretending that
* sensor is able to change measurement chamber pressure but in
* fact we're just setting pressure compensation value
*/
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
.output = 1,
.scan_index = -1,
},
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_CO2,

View File

@ -5,6 +5,7 @@
source "drivers/iio/common/cros_ec_sensors/Kconfig"
source "drivers/iio/common/hid-sensors/Kconfig"
source "drivers/iio/common/inv_sensors/Kconfig"
source "drivers/iio/common/ms_sensors/Kconfig"
source "drivers/iio/common/scmi_sensors/Kconfig"
source "drivers/iio/common/ssp_sensors/Kconfig"

View File

@ -10,6 +10,7 @@
# When adding new entries keep the list in alphabetical order
obj-y += cros_ec_sensors/
obj-y += hid-sensors/
obj-y += inv_sensors/
obj-y += ms_sensors/
obj-y += scmi_sensors/
obj-y += ssp_sensors/

View File

@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# TDK-InvenSense sensors common library
#
config IIO_INV_SENSORS_TIMESTAMP
tristate

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for TDK-InvenSense sensors module.
#
obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_sensors_timestamp.o

View File

@ -3,25 +3,23 @@
* Copyright (C) 2020 Invensense, Inc.
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/regmap.h>
#include <linux/math64.h>
#include <linux/module.h>
#include "inv_icm42600.h"
#include "inv_icm42600_timestamp.h"
#include <linux/iio/common/inv_sensors_timestamp.h>
/* internal chip period is 32kHz, 31250ns */
#define INV_ICM42600_TIMESTAMP_PERIOD 31250
/* allow a jitter of +/- 2% */
#define INV_ICM42600_TIMESTAMP_JITTER 2
/* compute min and max periods accepted */
#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p) \
(((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100)
#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p) \
(((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100)
/* compute jitter, min and max following jitter in per mille */
#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \
(div_s64((_val) * (_jitter), 1000))
#define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \
(((_val) * (1000 - (_jitter))) / 1000)
#define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \
(((_val) * (1000 + (_jitter))) / 1000)
/* Add a new value inside an accumulator and update the estimate value */
static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val)
{
uint64_t sum = 0;
size_t i;
@ -40,65 +38,57 @@ static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
acc->val = div_u64(sum, i);
}
void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
uint32_t period)
void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
const struct inv_sensors_timestamp_chip *chip)
{
/* initial odr for sensor after reset is 1kHz */
const uint32_t default_period = 1000000;
memset(ts, 0, sizeof(*ts));
/* save chip parameters and compute min and max clock period */
ts->chip = *chip;
ts->min_period = INV_SENSORS_TIMESTAMP_MIN(chip->clock_period, chip->jitter);
ts->max_period = INV_SENSORS_TIMESTAMP_MAX(chip->clock_period, chip->jitter);
/* current multiplier and period values after reset */
ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD;
ts->period = default_period;
/* new set multiplier is the one from chip initialization */
ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
ts->mult = chip->init_period / chip->clock_period;
ts->period = chip->init_period;
/* use theoretical value for chip period */
inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD);
inv_update_acc(&ts->chip_period, chip->clock_period);
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, IIO_INV_SENSORS_TIMESTAMP);
int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
{
unsigned int val;
/* enable timestamp register */
val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
INV_ICM42600_TMST_CONFIG_TMST_EN;
return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
INV_ICM42600_TMST_CONFIG_MASK, val);
}
int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
uint32_t period, bool fifo)
int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
uint32_t period, bool fifo)
{
/* when FIFO is on, prevent odr change if one is already pending */
if (fifo && ts->new_mult != 0)
return -EAGAIN;
ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
ts->new_mult = period / ts->chip.clock_period;
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
static bool inv_validate_period(uint32_t period, uint32_t mult)
static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult)
{
const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD;
uint32_t period_min, period_max;
/* check that period is acceptable */
period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult;
period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult;
period_min = ts->min_period * mult;
period_max = ts->max_period * mult;
if (period > period_min && period < period_max)
return true;
else
return false;
}
static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts,
uint32_t mult, uint32_t period)
static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
uint32_t mult, uint32_t period)
{
uint32_t new_chip_period;
if (!inv_validate_period(period, mult))
if (!inv_validate_period(ts, period, mult))
return false;
/* update chip internal period estimation */
@ -109,7 +99,7 @@ static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts,
return true;
}
static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts)
{
int64_t delta, jitter;
int64_t adjust;
@ -118,7 +108,7 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
delta = ts->it.lo - ts->timestamp;
/* adjust timestamp while respecting jitter */
jitter = div_s64((int64_t)ts->period * INV_ICM42600_TIMESTAMP_JITTER, 100);
jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter);
if (delta > jitter)
adjust = jitter;
else if (delta < -jitter)
@ -129,13 +119,13 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
ts->timestamp += adjust;
}
void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
uint32_t fifo_period, size_t fifo_nb,
size_t sensor_nb, int64_t timestamp)
{
struct inv_icm42600_timestamp_interval *it;
struct inv_sensors_timestamp_interval *it;
int64_t delta, interval;
const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
uint32_t period = ts->period;
bool valid = false;
@ -165,10 +155,11 @@ void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
if (valid)
inv_align_timestamp_it(ts);
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP);
void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
uint32_t fifo_period, size_t fifo_nb,
unsigned int fifo_no)
void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
uint32_t fifo_period, size_t fifo_nb,
unsigned int fifo_no)
{
int64_t interval;
uint32_t fifo_mult;
@ -189,10 +180,15 @@ void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
*/
if (ts->timestamp != 0) {
/* compute measured fifo period */
fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
fifo_mult = fifo_period / ts->chip.clock_period;
fifo_period = fifo_mult * ts->chip_period.val;
/* computes time interval between interrupt and this sample */
interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
ts->timestamp = ts->it.up - interval;
}
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP);
MODULE_AUTHOR("InvenSense, Inc.");
MODULE_DESCRIPTION("InvenSense sensors timestamp module");
MODULE_LICENSE("GPL");

View File

@ -389,6 +389,17 @@ config MCP4725
To compile this driver as a module, choose M here: the module
will be called mcp4725.
config MCP4728
tristate "MCP4728 DAC driver"
depends on I2C
help
Say Y here if you want to build a driver for the Microchip
MCP4728 quad channel, 12-bit digital-to-analog converter (DAC)
with I2C interface.
To compile this driver as a module, choose M here: the module
will be called mcp4728.
config MCP4922
tristate "MCP4902, MCP4912, MCP4922 DAC driver"
depends on SPI

View File

@ -41,6 +41,7 @@ obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
obj-$(CONFIG_STM32_DAC) += stm32-dac.o

618
drivers/iio/dac/mcp4728.c Normal file
View File

@ -0,0 +1,618 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Support for Microchip MCP4728
*
* Copyright (C) 2023 Andrea Collamati <andrea.collamati@gmail.com>
*
* Based on mcp4725 by Peter Meerwald <pmeerw@pmeerw.net>
*
* Driver for the Microchip I2C 12-bit digital-to-analog quad channels
* converter (DAC).
*
* (7-bit I2C slave address 0x60, the three LSBs can be configured in
* hardware)
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#define MCP4728_RESOLUTION 12
#define MCP4728_N_CHANNELS 4
#define MCP4728_CMD_MASK GENMASK(7, 3)
#define MCP4728_CHSEL_MASK GENMASK(2, 1)
#define MCP4728_UDAC_MASK BIT(0)
#define MCP4728_VREF_MASK BIT(7)
#define MCP4728_PDMODE_MASK GENMASK(6, 5)
#define MCP4728_GAIN_MASK BIT(4)
#define MCP4728_DAC_H_MASK GENMASK(3, 0)
#define MCP4728_DAC_L_MASK GENMASK(7, 0)
#define MCP4728_RDY_MASK BIT(7)
#define MCP4728_MW_CMD 0x08 /* Multiwrite Command */
#define MCP4728_SW_CMD 0x0A /* Sequential Write Command with EEPROM */
#define MCP4728_READ_RESPONSE_LEN (MCP4728_N_CHANNELS * 3 * 2)
#define MCP4728_WRITE_EEPROM_LEN (1 + MCP4728_N_CHANNELS * 2)
enum vref_mode {
MCP4728_VREF_EXTERNAL_VDD = 0,
MCP4728_VREF_INTERNAL_2048mV = 1,
};
enum gain_mode {
MCP4728_GAIN_X1 = 0,
MCP4728_GAIN_X2 = 1,
};
enum iio_powerdown_mode {
MCP4728_IIO_1K,
MCP4728_IIO_100K,
MCP4728_IIO_500K,
};
struct mcp4728_channel_data {
enum vref_mode ref_mode;
enum iio_powerdown_mode pd_mode;
enum gain_mode g_mode;
u16 dac_value;
};
/* MCP4728 Full Scale Ranges
* the device available ranges are
* - VREF = VDD FSR = from 0.0V to VDD
* - VREF = Internal Gain = 1 FSR = from 0.0V to VREF
* - VREF = Internal Gain = 2 FSR = from 0.0V to 2*VREF
*/
enum mcp4728_scale {
MCP4728_SCALE_VDD,
MCP4728_SCALE_VINT_NO_GAIN,
MCP4728_SCALE_VINT_GAIN_X2,
MCP4728_N_SCALES
};
struct mcp4728_data {
struct i2c_client *client;
struct regulator *vdd_reg;
bool powerdown;
int scales_avail[MCP4728_N_SCALES * 2];
struct mcp4728_channel_data chdata[MCP4728_N_CHANNELS];
};
#define MCP4728_CHAN(chan) { \
.type = IIO_VOLTAGE, \
.output = 1, \
.indexed = 1, \
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = mcp4728_ext_info, \
}
static int mcp4728_suspend(struct device *dev);
static int mcp4728_resume(struct device *dev);
static ssize_t mcp4728_store_eeprom(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct mcp4728_data *data = iio_priv(indio_dev);
u8 outbuf[MCP4728_WRITE_EEPROM_LEN];
int tries = 20;
u8 inbuf[3];
bool state;
int ret;
unsigned int i;
ret = kstrtobool(buf, &state);
if (ret < 0)
return ret;
if (!state)
return 0;
outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_SW_CMD);
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
struct mcp4728_channel_data *ch = &data->chdata[i];
int offset = 1 + i * 2;
outbuf[offset] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode);
if (data->powerdown) {
u8 mcp4728_pd_mode = ch->pd_mode + 1;
outbuf[offset] |= FIELD_PREP(MCP4728_PDMODE_MASK,
mcp4728_pd_mode);
}
outbuf[offset] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode);
outbuf[offset] |=
FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8);
outbuf[offset + 1] =
FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value);
}
ret = i2c_master_send(data->client, outbuf, MCP4728_WRITE_EEPROM_LEN);
if (ret < 0)
return ret;
else if (ret != MCP4728_WRITE_EEPROM_LEN)
return -EIO;
/* wait RDY signal for write complete, takes up to 50ms */
while (tries--) {
msleep(20);
ret = i2c_master_recv(data->client, inbuf, 3);
if (ret < 0)
return ret;
else if (ret != 3)
return -EIO;
if (FIELD_GET(MCP4728_RDY_MASK, inbuf[0]))
break;
}
if (tries < 0) {
dev_err(&data->client->dev, "%s failed, incomplete\n",
__func__);
return -EIO;
}
return len;
}
static IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, mcp4728_store_eeprom, 0);
static struct attribute *mcp4728_attributes[] = {
&iio_dev_attr_store_eeprom.dev_attr.attr,
NULL,
};
static const struct attribute_group mcp4728_attribute_group = {
.attrs = mcp4728_attributes,
};
static int mcp4728_program_channel_cfg(int channel, struct iio_dev *indio_dev)
{
struct mcp4728_data *data = iio_priv(indio_dev);
struct mcp4728_channel_data *ch = &data->chdata[channel];
u8 outbuf[3];
int ret;
outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_MW_CMD);
outbuf[0] |= FIELD_PREP(MCP4728_CHSEL_MASK, channel);
outbuf[0] |= FIELD_PREP(MCP4728_UDAC_MASK, 0);
outbuf[1] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode);
if (data->powerdown)
outbuf[1] |= FIELD_PREP(MCP4728_PDMODE_MASK, ch->pd_mode + 1);
outbuf[1] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode);
outbuf[1] |= FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8);
outbuf[2] = FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value);
ret = i2c_master_send(data->client, outbuf, 3);
if (ret < 0)
return ret;
else if (ret != 3)
return -EIO;
return 0;
}
static const char *const mcp4728_powerdown_modes[] = { "1kohm_to_gnd",
"100kohm_to_gnd",
"500kohm_to_gnd" };
static int mcp4728_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct mcp4728_data *data = iio_priv(indio_dev);
return data->chdata[chan->channel].pd_mode;
}
static int mcp4728_set_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct mcp4728_data *data = iio_priv(indio_dev);
data->chdata[chan->channel].pd_mode = mode;
return 0;
}
static ssize_t mcp4728_read_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct mcp4728_data *data = iio_priv(indio_dev);
return sysfs_emit(buf, "%d\n", data->powerdown);
}
static ssize_t mcp4728_write_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct mcp4728_data *data = iio_priv(indio_dev);
bool state;
int ret;
ret = kstrtobool(buf, &state);
if (ret)
return ret;
if (state)
ret = mcp4728_suspend(&data->client->dev);
else
ret = mcp4728_resume(&data->client->dev);
if (ret < 0)
return ret;
return len;
}
static const struct iio_enum mcp4728_powerdown_mode_enum = {
.items = mcp4728_powerdown_modes,
.num_items = ARRAY_SIZE(mcp4728_powerdown_modes),
.get = mcp4728_get_powerdown_mode,
.set = mcp4728_set_powerdown_mode,
};
static const struct iio_chan_spec_ext_info mcp4728_ext_info[] = {
{
.name = "powerdown",
.read = mcp4728_read_powerdown,
.write = mcp4728_write_powerdown,
.shared = IIO_SEPARATE,
},
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4728_powerdown_mode_enum),
IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE,
&mcp4728_powerdown_mode_enum),
{},
};
static const struct iio_chan_spec mcp4728_channels[MCP4728_N_CHANNELS] = {
MCP4728_CHAN(0),
MCP4728_CHAN(1),
MCP4728_CHAN(2),
MCP4728_CHAN(3),
};
static void mcp4728_get_scale_avail(enum mcp4728_scale scale,
struct mcp4728_data *data, int *val,
int *val2)
{
*val = data->scales_avail[scale * 2];
*val2 = data->scales_avail[scale * 2 + 1];
}
static void mcp4728_get_scale(int channel, struct mcp4728_data *data, int *val,
int *val2)
{
int ref_mode = data->chdata[channel].ref_mode;
int g_mode = data->chdata[channel].g_mode;
if (ref_mode == MCP4728_VREF_EXTERNAL_VDD) {
mcp4728_get_scale_avail(MCP4728_SCALE_VDD, data, val, val2);
} else {
if (g_mode == MCP4728_GAIN_X1) {
mcp4728_get_scale_avail(MCP4728_SCALE_VINT_NO_GAIN,
data, val, val2);
} else {
mcp4728_get_scale_avail(MCP4728_SCALE_VINT_GAIN_X2,
data, val, val2);
}
}
}
static int mcp4728_find_matching_scale(struct mcp4728_data *data, int val,
int val2)
{
for (int i = 0; i < MCP4728_N_SCALES; i++) {
if (data->scales_avail[i * 2] == val &&
data->scales_avail[i * 2 + 1] == val2)
return i;
}
return -EINVAL;
}
static int mcp4728_set_scale(int channel, struct mcp4728_data *data, int val,
int val2)
{
int scale = mcp4728_find_matching_scale(data, val, val2);
if (scale < 0)
return scale;
switch (scale) {
case MCP4728_SCALE_VDD:
data->chdata[channel].ref_mode = MCP4728_VREF_EXTERNAL_VDD;
return 0;
case MCP4728_SCALE_VINT_NO_GAIN:
data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV;
data->chdata[channel].g_mode = MCP4728_GAIN_X1;
return 0;
case MCP4728_SCALE_VINT_GAIN_X2:
data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV;
data->chdata[channel].g_mode = MCP4728_GAIN_X2;
return 0;
default:
return -EINVAL;
}
}
static int mcp4728_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct mcp4728_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = data->chdata[chan->channel].dac_value;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
mcp4728_get_scale(chan->channel, data, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
static int mcp4728_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct mcp4728_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (val < 0 || val > GENMASK(MCP4728_RESOLUTION - 1, 0))
return -EINVAL;
data->chdata[chan->channel].dac_value = val;
return mcp4728_program_channel_cfg(chan->channel, indio_dev);
case IIO_CHAN_INFO_SCALE:
ret = mcp4728_set_scale(chan->channel, data, val, val2);
if (ret)
return ret;
return mcp4728_program_channel_cfg(chan->channel, indio_dev);
default:
return -EINVAL;
}
}
static void mcp4728_init_scale_avail(enum mcp4728_scale scale, int vref_mv,
struct mcp4728_data *data)
{
s64 tmp;
int value_micro;
int value_int;
tmp = (s64)vref_mv * 1000000LL >> MCP4728_RESOLUTION;
value_int = div_s64_rem(tmp, 1000000LL, &value_micro);
data->scales_avail[scale * 2] = value_int;
data->scales_avail[scale * 2 + 1] = value_micro;
}
static int mcp4728_init_scales_avail(struct mcp4728_data *data)
{
int ret;
ret = regulator_get_voltage(data->vdd_reg);
if (ret < 0)
return ret;
mcp4728_init_scale_avail(MCP4728_SCALE_VDD, ret / 1000, data);
mcp4728_init_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 2048, data);
mcp4728_init_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 4096, data);
return 0;
}
static int mcp4728_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long info)
{
struct mcp4728_data *data = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SCALE:
*type = IIO_VAL_INT_PLUS_MICRO;
switch (chan->type) {
case IIO_VOLTAGE:
*vals = data->scales_avail;
*length = MCP4728_N_SCALES * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static const struct iio_info mcp4728_info = {
.read_raw = mcp4728_read_raw,
.write_raw = mcp4728_write_raw,
.read_avail = &mcp4728_read_avail,
.attrs = &mcp4728_attribute_group,
};
static int mcp4728_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct mcp4728_data *data = iio_priv(indio_dev);
unsigned int i;
data->powerdown = true;
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
int err = mcp4728_program_channel_cfg(i, indio_dev);
if (err)
return err;
}
return 0;
}
static int mcp4728_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct mcp4728_data *data = iio_priv(indio_dev);
int err = 0;
unsigned int i;
data->powerdown = false;
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
int ret = mcp4728_program_channel_cfg(i, indio_dev);
if (ret)
err = ret;
}
return err;
}
static DEFINE_SIMPLE_DEV_PM_OPS(mcp4728_pm_ops, mcp4728_suspend,
mcp4728_resume);
static int mcp4728_init_channels_data(struct mcp4728_data *data)
{
u8 inbuf[MCP4728_READ_RESPONSE_LEN];
int ret;
unsigned int i;
ret = i2c_master_recv(data->client, inbuf, MCP4728_READ_RESPONSE_LEN);
if (ret < 0) {
return dev_err_probe(&data->client->dev, ret,
"failed to read mcp4728 conf.\n");
} else if (ret != MCP4728_READ_RESPONSE_LEN) {
return dev_err_probe(&data->client->dev, -EIO,
"failed to read mcp4728 conf. Wrong Response Len ret=%d\n",
ret);
}
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
struct mcp4728_channel_data *ch = &data->chdata[i];
u8 r2 = inbuf[i * 6 + 1];
u8 r3 = inbuf[i * 6 + 2];
ch->dac_value = FIELD_GET(MCP4728_DAC_H_MASK, r2) << 8 |
FIELD_GET(MCP4728_DAC_L_MASK, r3);
ch->ref_mode = FIELD_GET(MCP4728_VREF_MASK, r2);
ch->pd_mode = FIELD_GET(MCP4728_PDMODE_MASK, r2);
ch->g_mode = FIELD_GET(MCP4728_GAIN_MASK, r2);
}
return 0;
}
static void mcp4728_reg_disable(void *reg)
{
regulator_disable(reg);
}
static int mcp4728_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct mcp4728_data *data;
struct iio_dev *indio_dev;
int err;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg))
return PTR_ERR(data->vdd_reg);
err = regulator_enable(data->vdd_reg);
if (err)
return err;
err = devm_add_action_or_reset(&client->dev, mcp4728_reg_disable,
data->vdd_reg);
if (err)
return err;
/*
* MCP4728 has internal EEPROM that save each channel boot
* configuration. It means that device configuration is unknown to the
* driver at kernel boot. mcp4728_init_channels_data() reads back DAC
* settings and stores them in data structure.
*/
err = mcp4728_init_channels_data(data);
if (err) {
return dev_err_probe(&client->dev, err,
"failed to read mcp4728 current configuration\n");
}
err = mcp4728_init_scales_avail(data);
if (err) {
return dev_err_probe(&client->dev, err,
"failed to init scales\n");
}
indio_dev->name = id->name;
indio_dev->info = &mcp4728_info;
indio_dev->channels = mcp4728_channels;
indio_dev->num_channels = MCP4728_N_CHANNELS;
indio_dev->modes = INDIO_DIRECT_MODE;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id mcp4728_id[] = {
{ "mcp4728", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, mcp4728_id);
static const struct of_device_id mcp4728_of_match[] = {
{ .compatible = "microchip,mcp4728" },
{}
};
MODULE_DEVICE_TABLE(of, mcp4728_of_match);
static struct i2c_driver mcp4728_driver = {
.driver = {
.name = "mcp4728",
.of_match_table = mcp4728_of_match,
.pm = pm_sleep_ptr(&mcp4728_pm_ops),
},
.probe = mcp4728_probe,
.id_table = mcp4728_id,
};
module_i2c_driver(mcp4728_driver);
MODULE_AUTHOR("Andrea Collamati <andrea.collamati@gmail.com>");
MODULE_DESCRIPTION("MCP4728 12-bit DAC");
MODULE_LICENSE("GPL");

View File

@ -78,6 +78,7 @@ enum {
enum {
ADMV8818_AUTO_MODE,
ADMV8818_MANUAL_MODE,
ADMV8818_BYPASS_MODE,
};
struct admv8818_state {
@ -114,7 +115,8 @@ static const struct regmap_config admv8818_regmap_config = {
static const char * const admv8818_modes[] = {
[0] = "auto",
[1] = "manual"
[1] = "manual",
[2] = "bypass"
};
static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq)
@ -394,6 +396,36 @@ static int admv8818_reg_access(struct iio_dev *indio_dev,
return regmap_write(st->regmap, reg, write_val);
}
static int admv8818_filter_bypass(struct admv8818_state *st)
{
int ret;
mutex_lock(&st->lock);
ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW,
ADMV8818_SW_IN_SET_WR0_MSK |
ADMV8818_SW_IN_WR0_MSK |
ADMV8818_SW_OUT_SET_WR0_MSK |
ADMV8818_SW_OUT_WR0_MSK,
FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) |
FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, 0) |
FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) |
FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, 0));
if (ret)
goto exit;
ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER,
ADMV8818_HPF_WR0_MSK |
ADMV8818_LPF_WR0_MSK,
FIELD_PREP(ADMV8818_HPF_WR0_MSK, 0) |
FIELD_PREP(ADMV8818_LPF_WR0_MSK, 0));
exit:
mutex_unlock(&st->lock);
return ret;
}
static int admv8818_get_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@ -411,14 +443,22 @@ static int admv8818_set_mode(struct iio_dev *indio_dev,
if (!st->clkin) {
if (mode == ADMV8818_MANUAL_MODE)
return 0;
goto set_mode;
if (mode == ADMV8818_BYPASS_MODE) {
ret = admv8818_filter_bypass(st);
if (ret)
return ret;
goto set_mode;
}
return -EINVAL;
}
switch (mode) {
case ADMV8818_AUTO_MODE:
if (!st->filter_mode)
if (st->filter_mode == ADMV8818_AUTO_MODE)
return 0;
ret = clk_prepare_enable(st->clkin);
@ -434,20 +474,27 @@ static int admv8818_set_mode(struct iio_dev *indio_dev,
break;
case ADMV8818_MANUAL_MODE:
if (st->filter_mode)
return 0;
case ADMV8818_BYPASS_MODE:
if (st->filter_mode == ADMV8818_AUTO_MODE) {
clk_disable_unprepare(st->clkin);
clk_disable_unprepare(st->clkin);
ret = clk_notifier_unregister(st->clkin, &st->nb);
if (ret)
return ret;
}
ret = clk_notifier_unregister(st->clkin, &st->nb);
if (ret)
return ret;
if (mode == ADMV8818_BYPASS_MODE) {
ret = admv8818_filter_bypass(st);
if (ret)
return ret;
}
break;
default:
return -EINVAL;
}
set_mode:
st->filter_mode = mode;
return ret;

View File

@ -379,6 +379,11 @@ static const struct iio_info admv1013_info = {
.debugfs_reg_access = &admv1013_reg_access,
};
static const char * const admv1013_vcc_regs[] = {
"vcc-drv", "vcc2-drv", "vcc-vva", "vcc-amp1", "vcc-amp2",
"vcc-env", "vcc-bg", "vcc-bg2", "vcc-mixer", "vcc-quad"
};
static int admv1013_freq_change(struct notifier_block *nb, unsigned long action, void *data)
{
struct admv1013_state *st = container_of(nb, struct admv1013_state, nb);
@ -554,6 +559,15 @@ static int admv1013_properties_parse(struct admv1013_state *st)
return dev_err_probe(&spi->dev, PTR_ERR(st->reg),
"failed to get the common-mode voltage\n");
ret = devm_regulator_bulk_get_enable(&st->spi->dev,
ARRAY_SIZE(admv1013_vcc_regs),
admv1013_vcc_regs);
if (ret) {
dev_err_probe(&spi->dev, ret,
"Failed to request VCC regulators\n");
return ret;
}
return 0;
}

View File

@ -115,8 +115,6 @@ enum {
ADIS16475_SCAN_ACCEL_Y,
ADIS16475_SCAN_ACCEL_Z,
ADIS16475_SCAN_TEMP,
ADIS16475_SCAN_DIAG_S_FLAGS,
ADIS16475_SCAN_CRC_FAILURE,
};
static bool low_rate_allow;
@ -728,6 +726,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
.max_dec = 1999,
.sync = adis16475_sync_mode,
.num_sync = ARRAY_SIZE(adis16475_sync_mode),
.has_burst32 = true,
.adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
},
[ADIS16477_2] = {
@ -743,6 +742,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
.max_dec = 1999,
.sync = adis16475_sync_mode,
.num_sync = ARRAY_SIZE(adis16475_sync_mode),
.has_burst32 = true,
.adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
},
[ADIS16477_3] = {
@ -758,6 +758,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
.max_dec = 1999,
.sync = adis16475_sync_mode,
.num_sync = ARRAY_SIZE(adis16475_sync_mode),
.has_burst32 = true,
.adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
},
[ADIS16465_1] = {

View File

@ -3,6 +3,7 @@
config INV_ICM42600
tristate
select IIO_BUFFER
select IIO_INV_SENSORS_TIMESTAMP
config INV_ICM42600_I2C
tristate "InvenSense ICM-426xx I2C driver"

View File

@ -6,7 +6,6 @@ inv-icm42600-y += inv_icm42600_gyro.o
inv-icm42600-y += inv_icm42600_accel.o
inv-icm42600-y += inv_icm42600_temp.o
inv-icm42600-y += inv_icm42600_buffer.o
inv-icm42600-y += inv_icm42600_timestamp.o
obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o
inv-icm42600-i2c-y += inv_icm42600_i2c.o

View File

@ -10,14 +10,15 @@
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/math64.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include "inv_icm42600.h"
#include "inv_icm42600_temp.h"
#include "inv_icm42600_buffer.h"
#include "inv_icm42600_timestamp.h"
#define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \
{ \
@ -98,7 +99,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
unsigned int fifo_en = 0;
unsigned int sleep_temp = 0;
@ -126,7 +127,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
}
/* update data FIFO write */
inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
if (ret)
goto out_unlock;
@ -311,7 +312,7 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev,
int val, int val2)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
struct device *dev = regmap_get_device(st->map);
unsigned int idx;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
@ -330,8 +331,8 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev,
pm_runtime_get_sync(dev);
mutex_lock(&st->lock);
ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
iio_buffer_enabled(indio_dev));
ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
iio_buffer_enabled(indio_dev));
if (ret)
goto out_unlock;
@ -707,7 +708,8 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
{
struct device *dev = regmap_get_device(st->map);
const char *name;
struct inv_icm42600_timestamp *ts;
struct inv_sensors_timestamp_chip ts_chip;
struct inv_sensors_timestamp *ts;
struct iio_dev *indio_dev;
int ret;
@ -719,8 +721,15 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
if (!indio_dev)
return ERR_PTR(-ENOMEM);
/*
* clock period is 32kHz (31250ns)
* jitter is +/- 2% (20 per mille)
*/
ts_chip.clock_period = 31250;
ts_chip.jitter = 20;
ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
ts = iio_priv(indio_dev);
inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
inv_sensors_timestamp_init(ts, &ts_chip);
iio_device_set_drvdata(indio_dev, st);
indio_dev->name = name;
@ -745,7 +754,7 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
ssize_t i, size;
unsigned int no;
const void *accel, *gyro, *timestamp;
@ -768,15 +777,15 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
/* update odr */
if (odr & INV_ICM42600_SENSOR_ACCEL)
inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
st->fifo.nb.total, no);
inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
st->fifo.nb.total, no);
/* buffer is copied to userspace, zeroing it to avoid any data leak */
memset(&buffer, 0, sizeof(buffer));
memcpy(&buffer.accel, accel, sizeof(buffer.accel));
/* convert 8 bits FIFO temperature in high resolution format */
buffer.temp = temp ? (*temp * 64) : 0;
ts_val = inv_icm42600_timestamp_pop(ts);
ts_val = inv_sensors_timestamp_pop(ts);
iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
}

View File

@ -9,11 +9,12 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/iio.h>
#include "inv_icm42600.h"
#include "inv_icm42600_timestamp.h"
#include "inv_icm42600_buffer.h"
/* FIFO header: 1 byte */
@ -275,12 +276,12 @@ static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct device *dev = regmap_get_device(st->map);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
pm_runtime_get_sync(dev);
mutex_lock(&st->lock);
inv_icm42600_timestamp_reset(ts);
inv_sensors_timestamp_reset(ts);
mutex_unlock(&st->lock);
return 0;
@ -504,7 +505,7 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
{
struct inv_icm42600_timestamp *ts;
struct inv_sensors_timestamp *ts;
int ret;
if (st->fifo.nb.total == 0)
@ -512,8 +513,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
/* handle gyroscope timestamp and FIFO data parsing */
ts = iio_priv(st->indio_gyro);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
st->fifo.nb.gyro, st->timestamp.gyro);
inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
st->fifo.nb.gyro, st->timestamp.gyro);
if (st->fifo.nb.gyro > 0) {
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
@ -522,8 +523,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
/* handle accelerometer timestamp and FIFO data parsing */
ts = iio_priv(st->indio_accel);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
st->fifo.nb.accel, st->timestamp.accel);
inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
st->fifo.nb.accel, st->timestamp.accel);
if (st->fifo.nb.accel > 0) {
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
@ -536,7 +537,7 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
unsigned int count)
{
struct inv_icm42600_timestamp *ts;
struct inv_sensors_timestamp *ts;
int64_t gyro_ts, accel_ts;
int ret;
@ -552,9 +553,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
if (st->fifo.nb.gyro > 0) {
ts = iio_priv(st->indio_gyro);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
st->fifo.nb.total, st->fifo.nb.gyro,
gyro_ts);
inv_sensors_timestamp_interrupt(ts, st->fifo.period,
st->fifo.nb.total, st->fifo.nb.gyro,
gyro_ts);
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
return ret;
@ -562,9 +563,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
if (st->fifo.nb.accel > 0) {
ts = iio_priv(st->indio_accel);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
st->fifo.nb.total, st->fifo.nb.accel,
accel_ts);
inv_sensors_timestamp_interrupt(ts, st->fifo.period,
st->fifo.nb.total, st->fifo.nb.accel,
accel_ts);
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
return ret;

View File

@ -15,11 +15,11 @@
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include "inv_icm42600.h"
#include "inv_icm42600_buffer.h"
#include "inv_icm42600_timestamp.h"
static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
{
@ -516,6 +516,17 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq,
"inv_icm42600", st);
}
static int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
{
unsigned int val;
/* enable timestamp register */
val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
INV_ICM42600_TMST_CONFIG_TMST_EN;
return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
INV_ICM42600_TMST_CONFIG_MASK, val);
}
static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st)
{
int ret;
@ -788,3 +799,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42600_pm_ops, IIO_ICM42600) = {
MODULE_AUTHOR("InvenSense, Inc.");
MODULE_DESCRIPTION("InvenSense ICM-426xx device driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);

View File

@ -10,14 +10,15 @@
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/math64.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include "inv_icm42600.h"
#include "inv_icm42600_temp.h"
#include "inv_icm42600_buffer.h"
#include "inv_icm42600_timestamp.h"
#define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \
{ \
@ -98,7 +99,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
unsigned int fifo_en = 0;
unsigned int sleep_gyro = 0;
@ -126,7 +127,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
}
/* update data FIFO write */
inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
if (ret)
goto out_unlock;
@ -323,7 +324,7 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev,
int val, int val2)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
struct device *dev = regmap_get_device(st->map);
unsigned int idx;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
@ -342,8 +343,8 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev,
pm_runtime_get_sync(dev);
mutex_lock(&st->lock);
ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
iio_buffer_enabled(indio_dev));
ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
iio_buffer_enabled(indio_dev));
if (ret)
goto out_unlock;
@ -718,7 +719,8 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
{
struct device *dev = regmap_get_device(st->map);
const char *name;
struct inv_icm42600_timestamp *ts;
struct inv_sensors_timestamp_chip ts_chip;
struct inv_sensors_timestamp *ts;
struct iio_dev *indio_dev;
int ret;
@ -730,8 +732,15 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
if (!indio_dev)
return ERR_PTR(-ENOMEM);
/*
* clock period is 32kHz (31250ns)
* jitter is +/- 2% (20 per mille)
*/
ts_chip.clock_period = 31250;
ts_chip.jitter = 20;
ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
ts = iio_priv(indio_dev);
inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
inv_sensors_timestamp_init(ts, &ts_chip);
iio_device_set_drvdata(indio_dev, st);
indio_dev->name = name;
@ -757,7 +766,7 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
ssize_t i, size;
unsigned int no;
const void *accel, *gyro, *timestamp;
@ -780,15 +789,15 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
/* update odr */
if (odr & INV_ICM42600_SENSOR_GYRO)
inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
st->fifo.nb.total, no);
inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
st->fifo.nb.total, no);
/* buffer is copied to userspace, zeroing it to avoid any data leak */
memset(&buffer, 0, sizeof(buffer));
memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
/* convert 8 bits FIFO temperature in high resolution format */
buffer.temp = temp ? (*temp * 64) : 0;
ts_val = inv_icm42600_timestamp_pop(ts);
ts_val = inv_sensors_timestamp_pop(ts);
iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
}

View File

@ -1,85 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2020 Invensense, Inc.
*/
#ifndef INV_ICM42600_TIMESTAMP_H_
#define INV_ICM42600_TIMESTAMP_H_
#include <linux/kernel.h>
struct inv_icm42600_state;
/**
* struct inv_icm42600_timestamp_interval - timestamps interval
* @lo: interval lower bound
* @up: interval upper bound
*/
struct inv_icm42600_timestamp_interval {
int64_t lo;
int64_t up;
};
/**
* struct inv_icm42600_timestamp_acc - accumulator for computing an estimation
* @val: current estimation of the value, the mean of all values
* @idx: current index of the next free place in values table
* @values: table of all measured values, use for computing the mean
*/
struct inv_icm42600_timestamp_acc {
uint32_t val;
size_t idx;
uint32_t values[32];
};
/**
* struct inv_icm42600_timestamp - timestamp management states
* @it: interrupts interval timestamps
* @timestamp: store last timestamp for computing next data timestamp
* @mult: current internal period multiplier
* @new_mult: new set internal period multiplier (not yet effective)
* @period: measured current period of the sensor
* @chip_period: accumulator for computing internal chip period
*/
struct inv_icm42600_timestamp {
struct inv_icm42600_timestamp_interval it;
int64_t timestamp;
uint32_t mult;
uint32_t new_mult;
uint32_t period;
struct inv_icm42600_timestamp_acc chip_period;
};
void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
uint32_t period);
int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st);
int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
uint32_t period, bool fifo);
void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
uint32_t fifo_period, size_t fifo_nb,
size_t sensor_nb, int64_t timestamp);
static inline int64_t
inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts)
{
ts->timestamp += ts->period;
return ts->timestamp;
}
void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
uint32_t fifo_period, size_t fifo_nb,
unsigned int fifo_no);
static inline void
inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts)
{
const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL};
ts->it = interval_init;
ts->timestamp = 0;
}
#endif

View File

@ -7,6 +7,7 @@ config INV_MPU6050_IIO
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IIO_INV_SENSORS_TIMESTAMP
config INV_MPU6050_I2C
tristate "Invensense MPU6050 devices (I2C)"

View File

@ -12,12 +12,15 @@
#include <linux/jiffies.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/iio/iio.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/iio.h>
#include "inv_mpu_iio.h"
#include "inv_mpu_magn.h"
@ -521,6 +524,7 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
int result;
u8 d;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
struct inv_sensors_timestamp_chip timestamp;
result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr);
if (result)
@ -544,12 +548,12 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
if (result)
return result;
/*
* Internal chip period is 1ms (1kHz).
* Let's use at the beginning the theorical value before measuring
* with interrupt timestamps.
*/
st->chip_period = NSEC_PER_MSEC;
/* clock jitter is +/- 2% */
timestamp.clock_period = NSEC_PER_SEC / INV_MPU6050_INTERNAL_FREQ_HZ;
timestamp.jitter = 20;
timestamp.init_period =
NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
inv_sensors_timestamp_init(&st->timestamp, &timestamp);
/* magn chip init, noop if not present in the chip */
result = inv_mpu_magn_probe(st);
@ -936,6 +940,8 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int fifo_rate;
u32 fifo_period;
bool fifo_on;
u8 d;
int result;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@ -952,12 +958,21 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
/* compute back the fifo rate to handle truncation cases */
fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d);
fifo_period = NSEC_PER_SEC / fifo_rate;
mutex_lock(&st->lock);
if (d == st->chip_config.divider) {
result = 0;
goto fifo_rate_fail_unlock;
}
fifo_on = st->chip_config.accl_fifo_enable ||
st->chip_config.gyro_fifo_enable ||
st->chip_config.magn_fifo_enable;
result = inv_sensors_timestamp_update_odr(&st->timestamp, fifo_period, fifo_on);
if (result)
goto fifo_rate_fail_unlock;
result = pm_runtime_resume_and_get(pdev);
if (result)
goto fifo_rate_fail_unlock;
@ -1330,6 +1345,9 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
st->reg = hw_info[st->chip_type].reg;
memcpy(&st->chip_config, hw_info[st->chip_type].config,
sizeof(st->chip_config));
st->data = devm_kzalloc(regmap_get_device(st->map), st->hw->fifo_size, GFP_KERNEL);
if (st->data == NULL)
return -ENOMEM;
/* check chip self-identification */
result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, &regval);
@ -1785,3 +1803,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_mpu_pmops, IIO_MPU6050) = {
MODULE_AUTHOR("Invensense Corporation");
MODULE_DESCRIPTION("Invensense device MPU6050 driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);

View File

@ -9,15 +9,17 @@
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/mutex.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/platform_data/invensense_mpu6050.h>
#include <linux/regmap.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/platform_data/invensense_mpu6050.h>
#include <linux/iio/sysfs.h>
/**
* struct inv_mpu6050_reg_map - Notable registers.
@ -170,16 +172,14 @@ struct inv_mpu6050_hw {
* @map regmap pointer.
* @irq interrupt number.
* @irq_mask the int_pin_cfg mask to configure interrupt type.
* @chip_period: chip internal period estimation (~1kHz).
* @it_timestamp: timestamp from previous interrupt.
* @data_timestamp: timestamp for next data sample.
* @timestamp: timestamping module
* @vdd_supply: VDD voltage regulator for the chip.
* @vddio_supply I/O voltage regulator for the chip.
* @magn_disabled: magnetometer disabled for backward compatibility reason.
* @magn_raw_to_gauss: coefficient to convert mag raw value to Gauss.
* @magn_orient: magnetometer sensor chip orientation if available.
* @suspended_sensors: sensors mask of sensors turned off for suspend
* @data: dma safe buffer used for bulk reads.
* @data: read buffer used for bulk reads.
*/
struct inv_mpu6050_state {
struct mutex lock;
@ -196,16 +196,14 @@ struct inv_mpu6050_state {
int irq;
u8 irq_mask;
unsigned skip_samples;
s64 chip_period;
s64 it_timestamp;
s64 data_timestamp;
struct inv_sensors_timestamp timestamp;
struct regulator *vdd_supply;
struct regulator *vddio_supply;
bool magn_disabled;
s32 magn_raw_to_gauss[3];
struct iio_mount_matrix magn_orient;
unsigned int suspended_sensors;
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE] __aligned(IIO_DMA_MINALIGN);
u8 *data;
};
/*register and associated bit definition*/

View File

@ -13,82 +13,11 @@
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/math64.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
#include "inv_mpu_iio.h"
/**
* inv_mpu6050_update_period() - Update chip internal period estimation
*
* @st: driver state
* @timestamp: the interrupt timestamp
* @nb: number of data set in the fifo
*
* This function uses interrupt timestamps to estimate the chip period and
* to choose the data timestamp to come.
*/
static void inv_mpu6050_update_period(struct inv_mpu6050_state *st,
s64 timestamp, size_t nb)
{
/* Period boundaries for accepting timestamp */
const s64 period_min =
(NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100;
const s64 period_max =
(NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100;
const s32 divider = INV_MPU6050_FREQ_DIVIDER(st);
s64 delta, interval;
bool use_it_timestamp = false;
if (st->it_timestamp == 0) {
/* not initialized, forced to use it_timestamp */
use_it_timestamp = true;
} else if (nb == 1) {
/*
* Validate the use of it timestamp by checking if interrupt
* has been delayed.
* nb > 1 means interrupt was delayed for more than 1 sample,
* so it's obviously not good.
* Compute the chip period between 2 interrupts for validating.
*/
delta = div_s64(timestamp - st->it_timestamp, divider);
if (delta > period_min && delta < period_max) {
/* update chip period and use it timestamp */
st->chip_period = (st->chip_period + delta) / 2;
use_it_timestamp = true;
}
}
if (use_it_timestamp) {
/*
* Manage case of multiple samples in the fifo (nb > 1):
* compute timestamp corresponding to the first sample using
* estimated chip period.
*/
interval = (nb - 1) * st->chip_period * divider;
st->data_timestamp = timestamp - interval;
}
/* save it timestamp */
st->it_timestamp = timestamp;
}
/**
* inv_mpu6050_get_timestamp() - Return the current data timestamp
*
* @st: driver state
* @return: current data timestamp
*
* This function returns the current data timestamp and prepares for next one.
*/
static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
{
s64 ts;
/* return current data timestamp and increment */
ts = st->data_timestamp;
st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st);
return ts;
}
static int inv_reset_fifo(struct iio_dev *indio_dev)
{
int result;
@ -121,7 +50,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
size_t bytes_per_datum;
int result;
u16 fifo_count;
u32 fifo_period;
s64 timestamp;
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
int int_status;
size_t i, nb;
@ -175,21 +106,30 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
goto flush_fifo;
}
/* compute and process all complete datum */
/* compute and process only all complete datum */
nb = fifo_count / bytes_per_datum;
inv_mpu6050_update_period(st, pf->timestamp, nb);
fifo_count = nb * bytes_per_datum;
/* Each FIFO data contains all sensors, so same number for FIFO and sensor data */
fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp);
inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0);
/* clear internal data buffer for avoiding kernel data leak */
memset(data, 0, sizeof(data));
/* read all data once and process every samples */
result = regmap_noinc_read(st->map, st->reg->fifo_r_w, st->data, fifo_count);
if (result)
goto flush_fifo;
for (i = 0; i < nb; ++i) {
result = regmap_noinc_read(st->map, st->reg->fifo_r_w,
st->data, bytes_per_datum);
if (result)
goto flush_fifo;
/* skip first samples if needed */
if (st->skip_samples) {
st->skip_samples--;
continue;
}
timestamp = inv_mpu6050_get_timestamp(st);
iio_push_to_buffers_with_timestamp(indio_dev, st->data, timestamp);
memcpy(data, &st->data[i * bytes_per_datum], bytes_per_datum);
timestamp = inv_sensors_timestamp_pop(&st->timestamp);
iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
}
end_session:

View File

@ -4,6 +4,9 @@
*/
#include <linux/pm_runtime.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
#include "inv_mpu_iio.h"
static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev)
@ -106,7 +109,8 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
int ret;
if (enable) {
st->it_timestamp = 0;
/* reset timestamping */
inv_sensors_timestamp_reset(&st->timestamp);
/* reset FIFO */
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
ret = regmap_write(st->map, st->reg->user_ctrl, d);

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* The industrial I/O core
/*
* The industrial I/O core
*
* Copyright (c) 2008 Jonathan Cameron
*
@ -183,7 +184,9 @@ static const char * const iio_chan_info_postfix[] = {
* @indio_dev: Device structure whose ID is being queried
*
* The IIO device ID is a unique index used for example for the naming
* of the character device /dev/iio\:device[ID]
* of the character device /dev/iio\:device[ID].
*
* Returns: Unique ID for the device.
*/
int iio_device_id(struct iio_dev *indio_dev)
{
@ -196,14 +199,16 @@ EXPORT_SYMBOL_GPL(iio_device_id);
/**
* iio_buffer_enabled() - helper function to test if the buffer is enabled
* @indio_dev: IIO device structure for device
*
* Returns: True, if the buffer is enabled.
*/
bool iio_buffer_enabled(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
return iio_dev_opaque->currentmode
& (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE |
INDIO_BUFFER_SOFTWARE);
return iio_dev_opaque->currentmode &
(INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE |
INDIO_BUFFER_TRIGGERED);
}
EXPORT_SYMBOL_GPL(iio_buffer_enabled);
@ -225,6 +230,9 @@ EXPORT_SYMBOL_GPL(iio_get_debugfs_dentry);
* iio_find_channel_from_si() - get channel from its scan index
* @indio_dev: device
* @si: scan index to match
*
* Returns:
* Constant pointer to iio_chan_spec, if scan index matches, NULL on failure.
*/
const struct iio_chan_spec
*iio_find_channel_from_si(struct iio_dev *indio_dev, int si)
@ -249,7 +257,9 @@ EXPORT_SYMBOL(iio_read_const_attr);
/**
* iio_device_set_clock() - Set current timestamping clock for the device
* @indio_dev: IIO device structure containing the device
* @clock_id: timestamping clock posix identifier to set.
* @clock_id: timestamping clock POSIX identifier to set.
*
* Returns: 0 on success, or a negative error code.
*/
int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
{
@ -275,6 +285,8 @@ EXPORT_SYMBOL(iio_device_set_clock);
/**
* iio_device_get_clock() - Retrieve current timestamping clock for the device
* @indio_dev: IIO device structure containing the device
*
* Returns: Clock ID of the current timestamping clock for the device.
*/
clockid_t iio_device_get_clock(const struct iio_dev *indio_dev)
{
@ -287,6 +299,8 @@ EXPORT_SYMBOL(iio_device_get_clock);
/**
* iio_get_time_ns() - utility function to get a time stamp for events etc
* @indio_dev: device
*
* Returns: Timestamp of the event in nanoseconds.
*/
s64 iio_get_time_ns(const struct iio_dev *indio_dev)
{
@ -372,8 +386,8 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf,
}
iio_dev_opaque->read_buf_len = snprintf(iio_dev_opaque->read_buf,
sizeof(iio_dev_opaque->read_buf),
"0x%X\n", val);
sizeof(iio_dev_opaque->read_buf),
"0x%X\n", val);
return simple_read_from_buffer(userbuf, count, ppos,
iio_dev_opaque->read_buf,
@ -389,7 +403,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file,
char buf[80];
int ret;
count = min_t(size_t, count, (sizeof(buf)-1));
count = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, userbuf, count))
return -EFAULT;
@ -476,8 +490,7 @@ static ssize_t iio_read_channel_ext_info(struct device *dev,
static ssize_t iio_write_channel_ext_info(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
@ -524,7 +537,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev,
i = e->get(indio_dev, chan);
if (i < 0)
return i;
else if (i >= e->num_items || !e->items[i])
if (i >= e->num_items || !e->items[i])
return -EINVAL;
return sysfs_emit(buf, "%s\n", e->items[i]);
@ -569,9 +582,9 @@ static int iio_setup_mount_idmatrix(const struct device *dev,
ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv,
const struct iio_chan_spec *chan, char *buf)
{
const struct iio_mount_matrix *mtx = ((iio_get_mount_matrix_t *)
priv)(indio_dev, chan);
const struct iio_mount_matrix *mtx;
mtx = ((iio_get_mount_matrix_t *)priv)(indio_dev, chan);
if (IS_ERR(mtx))
return PTR_ERR(mtx);
@ -594,7 +607,7 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix);
* If device is assigned no mounting matrix property, a default 3x3 identity
* matrix will be filled in.
*
* Return: 0 if success, or a negative error code on failure.
* Returns: 0 if success, or a negative error code on failure.
*/
int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix)
{
@ -692,9 +705,9 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
* @vals: Pointer to the values, exact meaning depends on the
* type parameter.
*
* Return: 0 by default, a negative number on failure or the
* total number of characters written for a type that belongs
* to the IIO_VAL_* constant.
* Returns:
* 0 by default, a negative number on failure or the total number of characters
* written for a type that belongs to the IIO_VAL_* constant.
*/
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
{
@ -847,8 +860,8 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
* @fract: The fractional part of the number
* @scale_db: True if this should parse as dB
*
* Returns 0 on success, or a negative error code if the string could not be
* parsed.
* Returns:
* 0 on success, or a negative error code if the string could not be parsed.
*/
static int __iio_str_to_fixpoint(const char *str, int fract_mult,
int *integer, int *fract, bool scale_db)
@ -917,8 +930,8 @@ static int __iio_str_to_fixpoint(const char *str, int fract_mult,
* @integer: The integer part of the number
* @fract: The fractional part of the number
*
* Returns 0 on success, or a negative error code if the string could not be
* parsed.
* Returns:
* 0 on success, or a negative error code if the string could not be parsed.
*/
int iio_str_to_fixpoint(const char *str, int fract_mult,
int *integer, int *fract)
@ -1009,14 +1022,12 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
if (chan->modified && (shared_by == IIO_SEPARATE)) {
if (chan->extend_name)
full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_modifier_names[chan
->channel2],
iio_modifier_names[chan->channel2],
chan->extend_name,
postfix);
else
full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
iio_modifier_names[chan
->channel2],
iio_modifier_names[chan->channel2],
postfix);
} else {
if (chan->extend_name == NULL || shared_by != IIO_SEPARATE)
@ -1217,7 +1228,7 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
&iio_dev_opaque->channel_attr_list);
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
continue;
else if (ret < 0)
if (ret < 0)
return ret;
attrcount++;
}
@ -1255,7 +1266,7 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
kfree(avail_postfix);
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
continue;
else if (ret < 0)
if (ret < 0)
return ret;
attrcount++;
}
@ -1400,50 +1411,42 @@ static ssize_t label_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RO(label);
static const char * const clock_names[] = {
[CLOCK_REALTIME] = "realtime",
[CLOCK_MONOTONIC] = "monotonic",
[CLOCK_PROCESS_CPUTIME_ID] = "process_cputime_id",
[CLOCK_THREAD_CPUTIME_ID] = "thread_cputime_id",
[CLOCK_MONOTONIC_RAW] = "monotonic_raw",
[CLOCK_REALTIME_COARSE] = "realtime_coarse",
[CLOCK_MONOTONIC_COARSE] = "monotonic_coarse",
[CLOCK_BOOTTIME] = "boottime",
[CLOCK_REALTIME_ALARM] = "realtime_alarm",
[CLOCK_BOOTTIME_ALARM] = "boottime_alarm",
[CLOCK_SGI_CYCLE] = "sgi_cycle",
[CLOCK_TAI] = "tai",
};
static ssize_t current_timestamp_clock_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
const struct iio_dev *indio_dev = dev_to_iio_dev(dev);
const clockid_t clk = iio_device_get_clock(indio_dev);
const char *name;
ssize_t sz;
switch (clk) {
case CLOCK_REALTIME:
name = "realtime\n";
sz = sizeof("realtime\n");
break;
case CLOCK_MONOTONIC:
name = "monotonic\n";
sz = sizeof("monotonic\n");
break;
case CLOCK_MONOTONIC_RAW:
name = "monotonic_raw\n";
sz = sizeof("monotonic_raw\n");
break;
case CLOCK_REALTIME_COARSE:
name = "realtime_coarse\n";
sz = sizeof("realtime_coarse\n");
break;
case CLOCK_MONOTONIC_COARSE:
name = "monotonic_coarse\n";
sz = sizeof("monotonic_coarse\n");
break;
case CLOCK_BOOTTIME:
name = "boottime\n";
sz = sizeof("boottime\n");
break;
case CLOCK_TAI:
name = "tai\n";
sz = sizeof("tai\n");
break;
default:
BUG();
}
memcpy(buf, name, sz);
return sz;
return sysfs_emit(buf, "%s\n", clock_names[clk]);
}
static ssize_t current_timestamp_clock_store(struct device *dev,
@ -1453,22 +1456,23 @@ static ssize_t current_timestamp_clock_store(struct device *dev,
clockid_t clk;
int ret;
if (sysfs_streq(buf, "realtime"))
clk = CLOCK_REALTIME;
else if (sysfs_streq(buf, "monotonic"))
clk = CLOCK_MONOTONIC;
else if (sysfs_streq(buf, "monotonic_raw"))
clk = CLOCK_MONOTONIC_RAW;
else if (sysfs_streq(buf, "realtime_coarse"))
clk = CLOCK_REALTIME_COARSE;
else if (sysfs_streq(buf, "monotonic_coarse"))
clk = CLOCK_MONOTONIC_COARSE;
else if (sysfs_streq(buf, "boottime"))
clk = CLOCK_BOOTTIME;
else if (sysfs_streq(buf, "tai"))
clk = CLOCK_TAI;
else
ret = sysfs_match_string(clock_names, buf);
if (ret < 0)
return ret;
clk = ret;
switch (clk) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_MONOTONIC_RAW:
case CLOCK_REALTIME_COARSE:
case CLOCK_MONOTONIC_COARSE:
case CLOCK_BOOTTIME:
case CLOCK_TAI:
break;
default:
return -EINVAL;
}
ret = iio_device_set_clock(dev_to_iio_dev(dev), clk);
if (ret)
@ -1484,7 +1488,7 @@ int iio_device_register_sysfs_group(struct iio_dev *indio_dev,
const struct attribute_group **new, **old = iio_dev_opaque->groups;
unsigned int cnt = iio_dev_opaque->groupcounter;
new = krealloc(old, sizeof(*new) * (cnt + 2), GFP_KERNEL);
new = krealloc_array(old, cnt + 2, sizeof(*new), GFP_KERNEL);
if (!new)
return -ENOMEM;
@ -1621,7 +1625,10 @@ const struct device_type iio_device_type = {
* iio_device_alloc() - allocate an iio_dev from a driver
* @parent: Parent device.
* @sizeof_priv: Space to allocate for private structure.
**/
*
* Returns:
* Pointer to allocated iio_dev on success, NULL on failure.
*/
struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
{
struct iio_dev_opaque *iio_dev_opaque;
@ -1677,7 +1684,7 @@ EXPORT_SYMBOL(iio_device_alloc);
/**
* iio_device_free() - free an iio_dev from a driver
* @dev: the iio_dev associated with the device
**/
*/
void iio_device_free(struct iio_dev *dev)
{
if (dev)
@ -1698,7 +1705,7 @@ static void devm_iio_device_release(void *iio_dev)
* Managed iio_device_alloc. iio_dev allocated with this function is
* automatically freed on driver detach.
*
* RETURNS:
* Returns:
* Pointer to allocated iio_dev on success, NULL on failure.
*/
struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv)
@ -1725,8 +1732,8 @@ EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
* @filp: File structure for iio device used to keep and later access
* private data
*
* Return: 0 on success or -EBUSY if the device is already opened
**/
* Returns: 0 on success or -EBUSY if the device is already opened
*/
static int iio_chrdev_open(struct inode *inode, struct file *filp)
{
struct iio_dev_opaque *iio_dev_opaque =
@ -1759,7 +1766,7 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
* @inode: Inode structure pointer for the char device
* @filp: File structure pointer for the char device
*
* Return: 0 for successful release
* Returns: 0 for successful release.
*/
static int iio_chrdev_release(struct inode *inode, struct file *filp)
{
@ -1798,7 +1805,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mutex_lock(&iio_dev_opaque->info_exist_lock);
/**
/*
* The NULL check here is required to prevent crashing when a device
* is being removed while userspace would still have open file handles
* to try to access this device.
@ -1975,7 +1982,7 @@ EXPORT_SYMBOL(__iio_device_register);
/**
* iio_device_unregister() - unregister a device from the IIO subsystem
* @indio_dev: Device structure representing the device.
**/
*/
void iio_device_unregister(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
@ -2026,7 +2033,7 @@ EXPORT_SYMBOL_GPL(__devm_iio_device_register);
*
* Use with iio_device_release_direct_mode()
*
* Returns: 0 on success, -EBUSY on failure
* Returns: 0 on success, -EBUSY on failure.
*/
int iio_device_claim_direct_mode(struct iio_dev *indio_dev)
{

View File

@ -252,6 +252,8 @@ static const char * const iio_ev_info_text[] = {
[IIO_EV_INFO_TIMEOUT] = "timeout",
[IIO_EV_INFO_RESET_TIMEOUT] = "reset_timeout",
[IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay",
[IIO_EV_INFO_RUNNING_PERIOD] = "runningperiod",
[IIO_EV_INFO_RUNNING_COUNT] = "runningcount",
};
static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)

View File

@ -313,7 +313,7 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig,
/* Enable trigger in driver */
if (trig->ops && trig->ops->set_trigger_state && notinuse) {
ret = trig->ops->set_trigger_state(trig, true);
if (ret < 0)
if (ret)
goto out_free_irq;
}

View File

@ -227,7 +227,7 @@ static int cm3605_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = dev_err_probe(dev, irq, "failed to get irq\n");
ret = irq;
goto out_disable_aset;
}

View File

@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* BU27008 ROHM Colour Sensor
* ROHM Colour Sensor driver for
* - BU27008 RGBC sensor
* - BU27010 RGBC + Flickering sensor
*
* Copyright (c) 2023, ROHM Semiconductor.
*/
@ -22,6 +24,25 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
/*
* A word about register address and mask definitions.
*
* At a quick glance to the data-sheet register tables, the BU27010 has all the
* registers that the BU27008 has. On top of that the BU27010 adds couple of new
* ones.
*
* So, all definitions BU27008_REG_* are there also for BU27010 but none of the
* BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds
* some features (Flicker FIFO, more power control) on top of the BU27008.
*
* Unfortunately, some of the wheel has been re-invented. Even though the names
* of the registers have stayed the same, pretty much all of the functionality
* provided by the registers has changed place. Contents of all MODE_CONTROL
* registers on BU27008 and BU27010 are different.
*
* Chip-specific mapping from register addresses/bits to functionality is done
* in bu27_chip_data structures.
*/
#define BU27008_REG_SYSTEM_CONTROL 0x40
#define BU27008_MASK_SW_RESET BIT(7)
#define BU27008_MASK_PART_ID GENMASK(5, 0)
@ -52,6 +73,56 @@
#define BU27008_REG_MANUFACTURER_ID 0x92
#define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID
/* BU27010 specific definitions */
#define BU27010_MASK_SW_RESET BIT(7)
#define BU27010_ID 0x1b
#define BU27010_REG_POWER 0x3e
#define BU27010_MASK_POWER BIT(0)
#define BU27010_REG_RESET 0x3f
#define BU27010_MASK_RESET BIT(0)
#define BU27010_RESET_RELEASE BU27010_MASK_RESET
#define BU27010_MASK_MEAS_EN BIT(1)
#define BU27010_MASK_CHAN_SEL GENMASK(7, 6)
#define BU27010_MASK_MEAS_MODE GENMASK(5, 4)
#define BU27010_MASK_RGBC_GAIN GENMASK(3, 0)
#define BU27010_MASK_DATA3_GAIN GENMASK(7, 6)
#define BU27010_MASK_DATA2_GAIN GENMASK(5, 4)
#define BU27010_MASK_DATA1_GAIN GENMASK(3, 2)
#define BU27010_MASK_DATA0_GAIN GENMASK(1, 0)
#define BU27010_MASK_FLC_MODE BIT(7)
#define BU27010_MASK_FLC_GAIN GENMASK(4, 0)
#define BU27010_REG_MODE_CONTROL4 0x44
/* If flicker is ever to be supported the IRQ must be handled as a field */
#define BU27010_IRQ_DIS_ALL GENMASK(1, 0)
#define BU27010_DRDY_EN BIT(0)
#define BU27010_MASK_INT_SEL GENMASK(1, 0)
#define BU27010_REG_MODE_CONTROL5 0x45
#define BU27010_MASK_RGB_VALID BIT(7)
#define BU27010_MASK_FLC_VALID BIT(6)
#define BU27010_MASK_WAIT_EN BIT(3)
#define BU27010_MASK_FIFO_EN BIT(2)
#define BU27010_MASK_RGB_EN BIT(1)
#define BU27010_MASK_FLC_EN BIT(0)
#define BU27010_REG_DATA_FLICKER_LO 0x56
#define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0)
#define BU27010_REG_FLICKER_COUNT 0x5a
#define BU27010_REG_FIFO_LEVEL_LO 0x5b
#define BU27010_MASK_FIFO_LEVEL_HI BIT(0)
#define BU27010_REG_FIFO_DATA_LO 0x5d
#define BU27010_REG_FIFO_DATA_HI 0x5e
#define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0)
#define BU27010_REG_MANUFACTURER_ID 0x92
#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID
/**
* enum bu27008_chan_type - BU27008 channel types
* @BU27008_RED: Red channel. Always via data0.
@ -117,6 +188,17 @@ static const unsigned long bu27008_scan_masks[] = {
*/
#define BU27008_SCALE_1X 16
/*
* On BU27010 available scales with gain 1x - 4096x,
* timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x.
*
* => Max total gain is HWGAIN * gain by integration time (8 * 4096)
*
* Using NANO precision for scale we must use scale 64x corresponding gain 1x
* to avoid precision loss.
*/
#define BU27010_SCALE_1X 64
/* See the data sheet for the "Gain Setting" table */
#define BU27008_GSEL_1X 0x00
#define BU27008_GSEL_4X 0x08
@ -152,10 +234,44 @@ static const struct iio_gain_sel_pair bu27008_gains_ir[] = {
GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X),
};
#define BU27010_GSEL_1X 0x00 /* 000000 */
#define BU27010_GSEL_4X 0x08 /* 001000 */
#define BU27010_GSEL_16X 0x09 /* 001001 */
#define BU27010_GSEL_64X 0x0e /* 001110 */
#define BU27010_GSEL_256X 0x1e /* 011110 */
#define BU27010_GSEL_1024X 0x2e /* 101110 */
#define BU27010_GSEL_4096X 0x3f /* 111111 */
static const struct iio_gain_sel_pair bu27010_gains[] = {
GAIN_SCALE_GAIN(1, BU27010_GSEL_1X),
GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
};
static const struct iio_gain_sel_pair bu27010_gains_ir[] = {
GAIN_SCALE_GAIN(2, BU27010_GSEL_1X),
GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
};
#define BU27008_MEAS_MODE_100MS 0x00
#define BU27008_MEAS_MODE_55MS 0x01
#define BU27008_MEAS_MODE_200MS 0x02
#define BU27008_MEAS_MODE_400MS 0x04
#define BU27010_MEAS_MODE_100MS 0x00
#define BU27010_MEAS_MODE_55MS 0x03
#define BU27010_MEAS_MODE_200MS 0x01
#define BU27010_MEAS_MODE_400MS 0x02
#define BU27008_MEAS_TIME_MAX_MS 400
static const struct iio_itime_sel_mul bu27008_itimes[] = {
@ -165,6 +281,13 @@ static const struct iio_itime_sel_mul bu27008_itimes[] = {
GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1),
};
static const struct iio_itime_sel_mul bu27010_itimes[] = {
GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8),
GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4),
GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2),
GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1),
};
/*
* All the RGBC channels share the same gain.
* IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this
@ -211,7 +334,35 @@ static const struct iio_chan_spec bu27008_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS),
};
struct bu27008_data;
struct bu27_chip_data {
const char *name;
int (*chip_init)(struct bu27008_data *data);
int (*get_gain_sel)(struct bu27008_data *data, int *sel);
int (*write_gain_sel)(struct bu27008_data *data, int sel);
const struct regmap_config *regmap_cfg;
const struct iio_gain_sel_pair *gains;
const struct iio_gain_sel_pair *gains_ir;
const struct iio_itime_sel_mul *itimes;
int num_gains;
int num_gains_ir;
int num_itimes;
int scale1x;
int drdy_en_reg;
int drdy_en_mask;
int meas_en_reg;
int meas_en_mask;
int valid_reg;
int chan_sel_reg;
int chan_sel_mask;
int int_time_mask;
u8 part_id;
};
struct bu27008_data {
const struct bu27_chip_data *cd;
struct regmap *regmap;
struct iio_trigger *trig;
struct device *dev;
@ -240,11 +391,29 @@ static const struct regmap_range bu27008_volatile_ranges[] = {
},
};
static const struct regmap_range bu27010_volatile_ranges[] = {
{
.range_min = BU27010_REG_RESET, /* RSTB */
.range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */
}, {
.range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */
.range_max = BU27010_REG_MODE_CONTROL5,
}, {
.range_min = BU27008_REG_DATA0_LO,
.range_max = BU27010_REG_FIFO_DATA_HI,
},
};
static const struct regmap_access_table bu27008_volatile_regs = {
.yes_ranges = &bu27008_volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges),
};
static const struct regmap_access_table bu27010_volatile_regs = {
.yes_ranges = &bu27010_volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges),
};
static const struct regmap_range bu27008_read_only_ranges[] = {
{
.range_min = BU27008_REG_DATA0_LO,
@ -255,11 +424,26 @@ static const struct regmap_range bu27008_read_only_ranges[] = {
},
};
static const struct regmap_range bu27010_read_only_ranges[] = {
{
.range_min = BU27008_REG_DATA0_LO,
.range_max = BU27010_REG_FIFO_DATA_HI,
}, {
.range_min = BU27010_REG_MANUFACTURER_ID,
.range_max = BU27010_REG_MANUFACTURER_ID,
}
};
static const struct regmap_access_table bu27008_ro_regs = {
.no_ranges = &bu27008_read_only_ranges[0],
.n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges),
};
static const struct regmap_access_table bu27010_ro_regs = {
.no_ranges = &bu27010_read_only_ranges[0],
.n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges),
};
static const struct regmap_config bu27008_regmap = {
.reg_bits = 8,
.val_bits = 8,
@ -282,50 +466,16 @@ static const struct regmap_config bu27008_regmap = {
.disable_locking = true,
};
#define BU27008_MAX_VALID_RESULT_WAIT_US 50000
#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000
static const struct regmap_config bu27010_regmap = {
.reg_bits = 8,
.val_bits = 8,
static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
{
int ret, valid;
__le16 tmp;
ret = regmap_read_poll_timeout(data->regmap, BU27008_REG_MODE_CONTROL3,
valid, (valid & BU27008_MASK_VALID),
BU27008_VALID_RESULT_WAIT_QUANTA_US,
BU27008_MAX_VALID_RESULT_WAIT_US);
if (ret)
return ret;
ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp));
if (ret)
dev_err(data->dev, "Reading channel data failed\n");
*val = le16_to_cpu(tmp);
return ret;
}
static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
{
int ret, sel;
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, &sel);
if (ret)
return ret;
sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, sel);
ret = iio_gts_find_gain_by_sel(gts, sel);
if (ret < 0) {
dev_err(data->dev, "unknown gain value 0x%x\n", sel);
return ret;
}
*gain = ret;
return 0;
}
.max_register = BU27010_REG_MAX,
.cache_type = REGCACHE_RBTREE,
.volatile_table = &bu27010_volatile_regs,
.wr_table = &bu27010_ro_regs,
.disable_locking = true,
};
static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
{
@ -368,6 +518,264 @@ static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
BU27008_MASK_RGBC_GAIN, regval);
}
static int bu27010_write_gain_sel(struct bu27008_data *data, int sel)
{
unsigned int regval;
int ret, chan_selector;
/*
* Gain 'selector' is composed of two registers. Selector is 6bit value,
* 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and
* two low bits being the channel specific gain in MODE_CONTROL2.
*
* Let's take the 4 high bits of whole 6 bit selector, and prepare
* the MODE_CONTROL1 value (RGBC gain part).
*/
regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2));
ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
BU27010_MASK_RGBC_GAIN, regval);
if (ret)
return ret;
/*
* Two low two bits of the selector must be written for all 4
* channels in the MODE_CONTROL2 register. Copy these two bits for
* all channels.
*/
chan_selector = sel & GENMASK(1, 0);
regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector);
regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector);
regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector);
regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector);
return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval);
}
static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel)
{
int ret;
/*
* If we always "lock" the gain selectors for all channels to prevent
* unsupported configs, then it does not matter which channel is used
* we can just return selector from any of them.
*
* This, however is not true if we decide to support only 4X and 16X
* and then individual gains for channels. Currently this is not the
* case.
*
* If we some day decide to support individual gains, then we need to
* have channel information here.
*/
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
if (ret)
return ret;
*sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel);
return 0;
}
static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel)
{
int ret, tmp;
/*
* We always "lock" the gain selectors for all channels to prevent
* unsupported configs. It does not matter which channel is used
* we can just return selector from any of them.
*
* Read the channel0 gain.
*/
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
if (ret)
return ret;
*sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel);
/* Read the shared gain */
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp);
if (ret)
return ret;
/*
* The gain selector is made as a combination of common RGBC gain and
* the channel specific gain. The channel specific gain forms the low
* bits of selector and RGBC gain is appended right after it.
*
* Compose the selector from channel0 gain and shared RGBC gain.
*/
*sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN);
return ret;
}
static int bu27008_chip_init(struct bu27008_data *data)
{
int ret;
ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET);
if (ret)
return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
/*
* The data-sheet does not tell how long performing the IC reset takes.
* However, the data-sheet says the minimum time it takes the IC to be
* able to take inputs after power is applied, is 100 uS. I'd assume
* > 1 mS is enough.
*/
msleep(1);
ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg);
if (ret)
dev_err(data->dev, "Failed to reinit reg cache\n");
return ret;
}
static int bu27010_chip_init(struct bu27008_data *data)
{
int ret;
ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET);
if (ret)
return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
msleep(1);
/* Power ON*/
ret = regmap_write_bits(data->regmap, BU27010_REG_POWER,
BU27010_MASK_POWER, BU27010_MASK_POWER);
if (ret)
return dev_err_probe(data->dev, ret, "Sensor power-on failed\n");
msleep(1);
/* Release blocks from reset */
ret = regmap_write_bits(data->regmap, BU27010_REG_RESET,
BU27010_MASK_RESET, BU27010_RESET_RELEASE);
if (ret)
return dev_err_probe(data->dev, ret, "Sensor powering failed\n");
msleep(1);
/*
* The IRQ enabling on BU27010 is done in a peculiar way. The IRQ
* enabling is not a bit mask where individual IRQs could be enabled but
* a field which values are:
* 00 => IRQs disabled
* 01 => Data-ready (RGBC/IR)
* 10 => Data-ready (flicker)
* 11 => Flicker FIFO
*
* So, only one IRQ can be enabled at a time and enabling for example
* flicker FIFO would automagically disable data-ready IRQ.
*
* Currently the driver does not support the flicker. Hence, we can
* just treat the RGBC data-ready as single bit which can be enabled /
* disabled. This works for as long as the second bit in the field
* stays zero. Here we ensure it gets zeroed.
*/
return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4,
BU27010_IRQ_DIS_ALL);
}
static const struct bu27_chip_data bu27010_chip = {
.name = "bu27010",
.chip_init = bu27010_chip_init,
.get_gain_sel = bu27010_get_gain_sel,
.write_gain_sel = bu27010_write_gain_sel,
.regmap_cfg = &bu27010_regmap,
.gains = &bu27010_gains[0],
.gains_ir = &bu27010_gains_ir[0],
.itimes = &bu27010_itimes[0],
.num_gains = ARRAY_SIZE(bu27010_gains),
.num_gains_ir = ARRAY_SIZE(bu27010_gains_ir),
.num_itimes = ARRAY_SIZE(bu27010_itimes),
.scale1x = BU27010_SCALE_1X,
.drdy_en_reg = BU27010_REG_MODE_CONTROL4,
.drdy_en_mask = BU27010_DRDY_EN,
.meas_en_reg = BU27010_REG_MODE_CONTROL5,
.meas_en_mask = BU27010_MASK_MEAS_EN,
.valid_reg = BU27010_REG_MODE_CONTROL5,
.chan_sel_reg = BU27008_REG_MODE_CONTROL1,
.chan_sel_mask = BU27010_MASK_CHAN_SEL,
.int_time_mask = BU27010_MASK_MEAS_MODE,
.part_id = BU27010_ID,
};
static const struct bu27_chip_data bu27008_chip = {
.name = "bu27008",
.chip_init = bu27008_chip_init,
.get_gain_sel = bu27008_get_gain_sel,
.write_gain_sel = bu27008_write_gain_sel,
.regmap_cfg = &bu27008_regmap,
.gains = &bu27008_gains[0],
.gains_ir = &bu27008_gains_ir[0],
.itimes = &bu27008_itimes[0],
.num_gains = ARRAY_SIZE(bu27008_gains),
.num_gains_ir = ARRAY_SIZE(bu27008_gains_ir),
.num_itimes = ARRAY_SIZE(bu27008_itimes),
.scale1x = BU27008_SCALE_1X,
.drdy_en_reg = BU27008_REG_MODE_CONTROL3,
.drdy_en_mask = BU27008_MASK_INT_EN,
.valid_reg = BU27008_REG_MODE_CONTROL3,
.meas_en_reg = BU27008_REG_MODE_CONTROL3,
.meas_en_mask = BU27008_MASK_MEAS_EN,
.chan_sel_reg = BU27008_REG_MODE_CONTROL3,
.chan_sel_mask = BU27008_MASK_CHAN_SEL,
.int_time_mask = BU27008_MASK_MEAS_MODE,
.part_id = BU27008_ID,
};
#define BU27008_MAX_VALID_RESULT_WAIT_US 50000
#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000
static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
{
int ret, valid;
__le16 tmp;
ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg,
valid, (valid & BU27008_MASK_VALID),
BU27008_VALID_RESULT_WAIT_QUANTA_US,
BU27008_MAX_VALID_RESULT_WAIT_US);
if (ret)
return ret;
ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp));
if (ret)
dev_err(data->dev, "Reading channel data failed\n");
*val = le16_to_cpu(tmp);
return ret;
}
static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
{
int ret, sel;
ret = data->cd->get_gain_sel(data, &sel);
if (ret)
return ret;
ret = iio_gts_find_gain_by_sel(gts, sel);
if (ret < 0) {
dev_err(data->dev, "unknown gain value 0x%x\n", sel);
return ret;
}
*gain = ret;
return 0;
}
static int bu27008_set_gain(struct bu27008_data *data, int gain)
{
int ret;
@ -376,7 +784,7 @@ static int bu27008_set_gain(struct bu27008_data *data, int gain)
if (ret < 0)
return ret;
return bu27008_write_gain_sel(data, ret);
return data->cd->write_gain_sel(data, ret);
}
static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
@ -384,15 +792,23 @@ static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
int ret, val;
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val);
*sel = FIELD_GET(BU27008_MASK_MEAS_MODE, val);
if (ret)
return ret;
return ret;
val &= data->cd->int_time_mask;
val >>= ffs(data->cd->int_time_mask) - 1;
*sel = val;
return 0;
}
static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel)
{
sel <<= ffs(data->cd->int_time_mask) - 1;
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
BU27008_MASK_MEAS_MODE, sel);
data->cd->int_time_mask, sel);
}
static int bu27008_get_int_time_us(struct bu27008_data *data)
@ -448,8 +864,7 @@ static int bu27008_set_int_time(struct bu27008_data *data, int time)
if (ret < 0)
return ret;
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
BU27008_MASK_MEAS_MODE, ret);
return bu27008_set_int_time_sel(data, ret);
}
/* Try to change the time so that the scale is maintained */
@ -527,10 +942,13 @@ static int bu27008_try_set_int_time(struct bu27008_data *data, int int_time_new)
return ret;
}
static int bu27008_meas_set(struct bu27008_data *data, int state)
static int bu27008_meas_set(struct bu27008_data *data, bool enable)
{
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
BU27008_MASK_MEAS_EN, state);
if (enable)
return regmap_set_bits(data->regmap, data->cd->meas_en_reg,
data->cd->meas_en_mask);
return regmap_clear_bits(data->regmap, data->cd->meas_en_reg,
data->cd->meas_en_mask);
}
static int bu27008_chan_cfg(struct bu27008_data *data,
@ -543,9 +961,15 @@ static int bu27008_chan_cfg(struct bu27008_data *data,
else
chan_sel = BU27008_CLEAR2_IR3;
chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel);
/*
* prepare bitfield for channel sel. The FIELD_PREP works only when
* mask is constant. In our case the mask is assigned based on the
* chip type. Hence the open-coded FIELD_PREP here. We don't bother
* zeroing the irrelevant bits though - update_bits takes care of that.
*/
chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
BU27008_MASK_CHAN_SEL, chan_sel);
}
@ -558,7 +982,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
if (ret)
return ret;
ret = bu27008_meas_set(data, BU27008_MEAS_EN);
ret = bu27008_meas_set(data, true);
if (ret)
return ret;
@ -574,7 +998,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
if (!ret)
ret = IIO_VAL_INT;
if (bu27008_meas_set(data, BU27008_MEAS_DIS))
if (bu27008_meas_set(data, false))
dev_warn(data->dev, "measurement disabling failed\n");
return ret;
@ -669,7 +1093,7 @@ static int bu27008_set_scale(struct bu27008_data *data,
goto unlock_out;
}
ret = bu27008_write_gain_sel(data, gain_sel);
ret = data->cd->write_gain_sel(data, gain_sel);
unlock_out:
mutex_unlock(&data->mutex);
@ -747,10 +1171,10 @@ static int bu27008_update_scan_mode(struct iio_dev *idev,
chan_sel = BU27008_CLEAR2_IR3;
}
chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel);
chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
BU27008_MASK_CHAN_SEL, chan_sel);
return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
data->cd->chan_sel_mask, chan_sel);
}
static const struct iio_info bu27008_info = {
@ -761,46 +1185,18 @@ static const struct iio_info bu27008_info = {
.validate_trigger = iio_validate_own_trigger,
};
static int bu27008_chip_init(struct bu27008_data *data)
{
int ret;
ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET);
if (ret)
return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
/*
* The data-sheet does not tell how long performing the IC reset takes.
* However, the data-sheet says the minimum time it takes the IC to be
* able to take inputs after power is applied, is 100 uS. I'd assume
* > 1 mS is enough.
*/
msleep(1);
ret = regmap_reinit_cache(data->regmap, &bu27008_regmap);
if (ret)
dev_err(data->dev, "Failed to reinit reg cache\n");
return ret;
}
static int bu27008_set_drdy_irq(struct bu27008_data *data, int state)
{
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
BU27008_MASK_INT_EN, state);
}
static int bu27008_trigger_set_state(struct iio_trigger *trig,
bool state)
static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state)
{
struct bu27008_data *data = iio_trigger_get_drvdata(trig);
int ret;
if (state)
ret = bu27008_set_drdy_irq(data, BU27008_INT_EN);
ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg,
data->cd->drdy_en_mask);
else
ret = bu27008_set_drdy_irq(data, BU27008_INT_DIS);
ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg,
data->cd->drdy_en_mask);
if (ret)
dev_err(data->dev, "Failed to set trigger state\n");
@ -836,7 +1232,7 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p)
* After some measurements, it seems reading the
* BU27008_REG_MODE_CONTROL3 debounces the IRQ line
*/
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL3, &dummy);
ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy);
if (ret < 0)
goto err_read;
@ -856,14 +1252,14 @@ static int bu27008_buffer_preenable(struct iio_dev *idev)
{
struct bu27008_data *data = iio_priv(idev);
return bu27008_meas_set(data, BU27008_MEAS_EN);
return bu27008_meas_set(data, true);
}
static int bu27008_buffer_postdisable(struct iio_dev *idev)
{
struct bu27008_data *data = iio_priv(idev);
return bu27008_meas_set(data, BU27008_MEAS_DIS);
return bu27008_meas_set(data, false);
}
static const struct iio_buffer_setup_ops bu27008_buffer_ops = {
@ -936,11 +1332,6 @@ static int bu27008_probe(struct i2c_client *i2c)
struct iio_dev *idev;
int ret;
regmap = devm_regmap_init_i2c(i2c, &bu27008_regmap);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to initialize Regmap\n");
idev = devm_iio_device_alloc(dev, sizeof(*data));
if (!idev)
return -ENOMEM;
@ -951,24 +1342,34 @@ static int bu27008_probe(struct i2c_client *i2c)
data = iio_priv(idev);
data->cd = device_get_match_data(&i2c->dev);
if (!data->cd)
return -ENODEV;
regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to initialize Regmap\n");
ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, &reg);
if (ret)
return dev_err_probe(dev, ret, "Failed to access sensor\n");
part_id = FIELD_GET(BU27008_MASK_PART_ID, reg);
if (part_id != BU27008_ID)
if (part_id != data->cd->part_id)
dev_warn(dev, "unknown device 0x%x\n", part_id);
ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains,
ARRAY_SIZE(bu27008_gains), bu27008_itimes,
ARRAY_SIZE(bu27008_itimes), &data->gts);
ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains,
data->cd->num_gains, data->cd->itimes,
data->cd->num_itimes, &data->gts);
if (ret)
return ret;
ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains_ir,
ARRAY_SIZE(bu27008_gains_ir), bu27008_itimes,
ARRAY_SIZE(bu27008_itimes), &data->gts_ir);
ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir,
data->cd->num_gains_ir, data->cd->itimes,
data->cd->num_itimes, &data->gts_ir);
if (ret)
return ret;
@ -979,12 +1380,12 @@ static int bu27008_probe(struct i2c_client *i2c)
idev->channels = bu27008_channels;
idev->num_channels = ARRAY_SIZE(bu27008_channels);
idev->name = "bu27008";
idev->name = data->cd->name;
idev->info = &bu27008_info;
idev->modes = INDIO_DIRECT_MODE;
idev->available_scan_masks = bu27008_scan_masks;
ret = bu27008_chip_init(data);
ret = data->cd->chip_init(data);
if (ret)
return ret;
@ -1005,7 +1406,8 @@ static int bu27008_probe(struct i2c_client *i2c)
}
static const struct of_device_id bu27008_of_match[] = {
{ .compatible = "rohm,bu27008" },
{ .compatible = "rohm,bu27008", .data = &bu27008_chip },
{ .compatible = "rohm,bu27010", .data = &bu27010_chip },
{ }
};
MODULE_DEVICE_TABLE(of, bu27008_of_match);
@ -1020,7 +1422,7 @@ static struct i2c_driver bu27008_i2c_driver = {
};
module_i2c_driver(bu27008_i2c_driver);
MODULE_DESCRIPTION("ROHM BU27008 colour sensor driver");
MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver");
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_GTS_HELPER);

View File

@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
@ -60,11 +61,15 @@
#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
#define VCNL4200_PS_CONF3 0x04 /* Proximity configuration */
#define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */
#define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */
#define VCNL4040_ALS_THDL_LM 0x02 /* Ambient light threshold low */
#define VCNL4040_ALS_THDH_LM 0x01 /* Ambient light threshold high */
#define VCNL4200_PS_DATA 0x08 /* Proximity data */
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
#define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */
#define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
#define VCNL4040_DEV_ID 0x0c /* Device ID and version */
@ -79,11 +84,19 @@
#define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */
#define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0)
#define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */
#define VCNL4040_ALS_CONF_INT_EN BIT(1) /* Ambient light Interrupt enable */
#define VCNL4040_ALS_CONF_PERS GENMASK(3, 2) /* Ambient interrupt persistence setting */
#define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0)
#define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */
#define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */
#define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */
#define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */
#define VCNL4040_PS_MS_LED_I GENMASK(10, 8) /* Proximity current */
#define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */
#define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */
#define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */
#define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold */
/* Bit masks for interrupt registers. */
#define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */
@ -123,6 +136,44 @@ static const int vcnl4040_ps_it_times[][2] = {
{0, 800},
};
static const int vcnl4200_ps_it_times[][2] = {
{0, 96},
{0, 144},
{0, 192},
{0, 384},
{0, 768},
{0, 864},
};
static const int vcnl4040_als_it_times[][2] = {
{0, 80000},
{0, 160000},
{0, 320000},
{0, 640000},
};
static const int vcnl4200_als_it_times[][2] = {
{0, 50000},
{0, 100000},
{0, 200000},
{0, 400000},
};
static const int vcnl4040_ps_calibbias_ua[][2] = {
{0, 50000},
{0, 75000},
{0, 100000},
{0, 120000},
{0, 140000},
{0, 160000},
{0, 180000},
{0, 200000},
};
static const int vcnl4040_als_persistence[] = {1, 2, 4, 8};
static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4};
static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8};
#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */
enum vcnl4000_device_ids {
@ -145,6 +196,7 @@ struct vcnl4000_data {
int rev;
int al_scale;
u8 ps_int; /* proximity interrupt mode */
u8 als_int; /* ambient light interrupt mode*/
const struct vcnl4000_chip_spec *chip_spec;
struct mutex vcnl4000_lock;
struct vcnl4200_channel vcnl4200_al;
@ -164,6 +216,13 @@ struct vcnl4000_chip_spec {
int (*set_power_state)(struct vcnl4000_data *data, bool on);
irqreturn_t (*irq_thread)(int irq, void *priv);
irqreturn_t (*trig_buffer_func)(int irq, void *priv);
u8 int_reg;
const int(*ps_it_times)[][2];
const int num_ps_it_times;
const int(*als_it_times)[][2];
const int num_als_it_times;
const unsigned int ulux_step;
};
static const struct i2c_device_id vcnl4000_id[] = {
@ -263,7 +322,7 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
int ret;
/* Do not power down if interrupts are enabled */
if (!on && data->ps_int)
if (!on && (data->ps_int || data->als_int))
return 0;
ret = vcnl4000_write_als_enable(data, on);
@ -308,6 +367,7 @@ static int vcnl4200_init(struct vcnl4000_data *data)
data->rev = (ret >> 8) & 0xf;
data->ps_int = 0;
data->als_int = 0;
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
@ -317,16 +377,15 @@ static int vcnl4200_init(struct vcnl4000_data *data)
data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
/* Default wait time is 4.8ms, add 20% tolerance. */
data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
data->al_scale = 24000;
break;
case VCNL4040_PROD_ID:
/* Default wait time is 80ms, add 20% tolerance. */
data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
/* Default wait time is 5ms, add 20% tolerance. */
data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
data->al_scale = 120000;
break;
}
data->al_scale = data->chip_spec->ulux_step;
mutex_init(&data->vcnl4200_al.lock);
mutex_init(&data->vcnl4200_ps.lock);
@ -496,6 +555,60 @@ static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
return ret;
}
static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2)
{
int ret;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
if (ret < 0)
return ret;
ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
if (ret >= data->chip_spec->num_als_it_times)
return -EINVAL;
*val = (*data->chip_spec->als_it_times)[ret][0];
*val2 = (*data->chip_spec->als_it_times)[ret][1];
return 0;
}
static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val)
{
unsigned int i;
int ret;
u16 regval;
for (i = 0; i < data->chip_spec->num_als_it_times; i++) {
if (val == (*data->chip_spec->als_it_times)[i][1])
break;
}
if (i == data->chip_spec->num_als_it_times)
return -EINVAL;
data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200);
data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step,
(*data->chip_spec->als_it_times)[0][1]),
val);
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
if (ret < 0)
goto out_unlock;
regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i);
regval |= (ret & ~VCNL4040_ALS_CONF_IT);
ret = i2c_smbus_write_word_data(data->client,
VCNL4200_AL_CONF,
regval);
out_unlock:
mutex_unlock(&data->vcnl4000_lock);
return ret;
}
static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
{
int ret;
@ -506,11 +619,11 @@ static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times))
if (ret >= data->chip_spec->num_ps_it_times)
return -EINVAL;
*val = vcnl4040_ps_it_times[ret][0];
*val2 = vcnl4040_ps_it_times[ret][1];
*val = (*data->chip_spec->ps_it_times)[ret][0];
*val2 = (*data->chip_spec->ps_it_times)[ret][1];
return 0;
}
@ -521,8 +634,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
int ret, index = -1;
u16 regval;
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) {
if (val == vcnl4040_ps_it_times[i][1]) {
for (i = 0; i < data->chip_spec->num_ps_it_times; i++) {
if (val == (*data->chip_spec->ps_it_times)[i][1]) {
index = i;
break;
}
@ -531,6 +644,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
if (index < 0)
return -EINVAL;
data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC);
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
@ -547,6 +662,224 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
return ret;
}
static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2)
{
int ret, ret_pers, it;
int64_t val_c;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
if (ret < 0)
return ret;
ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret);
if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence))
return -EINVAL;
it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
if (it >= data->chip_spec->num_als_it_times)
return -EINVAL;
val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1],
vcnl4040_als_persistence[ret_pers]);
*val = div_u64_rem(val_c, MICRO, val2);
return IIO_VAL_INT_PLUS_MICRO;
}
static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2)
{
unsigned int i;
int ret, it;
u16 regval;
u64 val_n = mul_u32_u32(val, MICRO) + val2;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
if (ret < 0)
return ret;
it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
if (it >= data->chip_spec->num_als_it_times)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) {
if (val_n < mul_u32_u32(vcnl4040_als_persistence[i],
(*data->chip_spec->als_it_times)[it][1]))
break;
}
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
if (ret < 0)
goto out_unlock;
regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i);
regval |= (ret & ~VCNL4040_ALS_CONF_PERS);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
regval);
out_unlock:
mutex_unlock(&data->vcnl4000_lock);
return ret;
}
static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2)
{
int ret, ret_pers, it;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
return ret;
ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret);
if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence))
return -EINVAL;
it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
if (it >= data->chip_spec->num_ps_it_times)
return -EINVAL;
*val = (*data->chip_spec->ps_it_times)[it][0];
*val2 = (*data->chip_spec->ps_it_times)[it][1] *
vcnl4040_ps_persistence[ret_pers];
return IIO_VAL_INT_PLUS_MICRO;
}
static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2)
{
int ret, it, i;
u16 regval;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
return ret;
it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
if (it >= data->chip_spec->num_ps_it_times)
return -EINVAL;
if (val > 0)
i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1;
else {
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) {
if (val2 <= vcnl4040_ps_persistence[i] *
(*data->chip_spec->ps_it_times)[it][1])
break;
}
}
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
goto out_unlock;
regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i);
regval |= (ret & ~VCNL4040_CONF1_PS_PERS);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
regval);
out_unlock:
mutex_unlock(&data->vcnl4000_lock);
return ret;
}
static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val)
{
int ret;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
if (ret < 0)
return ret;
ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret);
if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
return -EINVAL;
*val = vcnl4040_ps_oversampling_ratio[ret];
return ret;
}
static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val)
{
unsigned int i;
int ret;
u16 regval;
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) {
if (val == vcnl4040_ps_oversampling_ratio[i])
break;
}
if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
return -EINVAL;
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
if (ret < 0)
goto out_unlock;
regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i);
regval |= (ret & ~VCNL4040_PS_CONF3_MPS);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
regval);
out_unlock:
mutex_unlock(&data->vcnl4000_lock);
return ret;
}
static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2)
{
int ret;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
if (ret < 0)
return ret;
ret = FIELD_GET(VCNL4040_PS_MS_LED_I, ret);
if (ret >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
return -EINVAL;
*val = vcnl4040_ps_calibbias_ua[ret][0];
*val2 = vcnl4040_ps_calibbias_ua[ret][1];
return ret;
}
static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val)
{
unsigned int i;
int ret;
u16 regval;
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_calibbias_ua); i++) {
if (val == vcnl4040_ps_calibbias_ua[i][1])
break;
}
if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
return -EINVAL;
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
if (ret < 0)
goto out_unlock;
regval = (ret & ~VCNL4040_PS_MS_LED_I);
regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
regval);
out_unlock:
mutex_unlock(&data->vcnl4000_lock);
return ret;
}
static int vcnl4000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@ -584,12 +917,39 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
*val2 = data->al_scale;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type != IIO_PROXIMITY)
switch (chan->type) {
case IIO_LIGHT:
ret = vcnl4040_read_als_it(data, val, val2);
break;
case IIO_PROXIMITY:
ret = vcnl4040_read_ps_it(data, val, val2);
break;
default:
return -EINVAL;
ret = vcnl4040_read_ps_it(data, val, val2);
}
if (ret < 0)
return ret;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_PROXIMITY:
ret = vcnl4040_read_ps_oversampling_ratio(data, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->type) {
case IIO_PROXIMITY:
ret = vcnl4040_read_ps_calibbias(data, val, val2);
if (ret < 0)
return ret;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
@ -605,9 +965,28 @@ static int vcnl4040_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_INT_TIME:
if (val != 0)
return -EINVAL;
if (chan->type != IIO_PROXIMITY)
switch (chan->type) {
case IIO_LIGHT:
return vcnl4040_write_als_it(data, val2);
case IIO_PROXIMITY:
return vcnl4040_write_ps_it(data, val2);
default:
return -EINVAL;
return vcnl4040_write_ps_it(data, val2);
}
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_PROXIMITY:
return vcnl4040_write_ps_oversampling_ratio(data, val);
default:
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->type) {
case IIO_PROXIMITY:
return vcnl4040_write_ps_calibbias(data, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
@ -618,12 +997,44 @@ static int vcnl4040_read_avail(struct iio_dev *indio_dev,
const int **vals, int *type, int *length,
long mask)
{
struct vcnl4000_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
*vals = (int *)vcnl4040_ps_it_times;
switch (chan->type) {
case IIO_LIGHT:
*vals = (int *)(*data->chip_spec->als_it_times);
*length = 2 * data->chip_spec->num_als_it_times;
break;
case IIO_PROXIMITY:
*vals = (int *)(*data->chip_spec->ps_it_times);
*length = 2 * data->chip_spec->num_ps_it_times;
break;
default:
return -EINVAL;
}
*type = IIO_VAL_INT_PLUS_MICRO;
*length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_PROXIMITY:
*vals = (int *)vcnl4040_ps_oversampling_ratio;
*length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio);
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->type) {
case IIO_PROXIMITY:
*vals = (int *)vcnl4040_ps_calibbias_ua;
*length = 2 * ARRAY_SIZE(vcnl4040_ps_calibbias_ua);
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
@ -818,24 +1229,58 @@ static int vcnl4040_read_event(struct iio_dev *indio_dev,
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_PS_THDH_LM);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_PS_THDL_LM);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
switch (chan->type) {
case IIO_LIGHT:
switch (info) {
case IIO_EV_INFO_PERIOD:
return vcnl4040_read_als_period(data, val, val2);
case IIO_EV_INFO_VALUE:
switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_ALS_THDH_LM);
break;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_ALS_THDL_LM);
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
break;
case IIO_PROXIMITY:
switch (info) {
case IIO_EV_INFO_PERIOD:
return vcnl4040_read_ps_period(data, val, val2);
case IIO_EV_INFO_VALUE:
switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_PS_THDH_LM);
break;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_PS_THDL_LM);
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
}
static int vcnl4040_write_event(struct iio_dev *indio_dev,
@ -848,22 +1293,61 @@ static int vcnl4040_write_event(struct iio_dev *indio_dev,
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_PS_THDH_LM, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_PS_THDL_LM, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
switch (chan->type) {
case IIO_LIGHT:
switch (info) {
case IIO_EV_INFO_PERIOD:
return vcnl4040_write_als_period(data, val, val2);
case IIO_EV_INFO_VALUE:
switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_ALS_THDH_LM,
val);
break;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_ALS_THDL_LM,
val);
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
break;
case IIO_PROXIMITY:
switch (info) {
case IIO_EV_INFO_PERIOD:
return vcnl4040_write_ps_period(data, val, val2);
case IIO_EV_INFO_VALUE:
switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_PS_THDH_LM,
val);
break;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_PS_THDL_LM,
val);
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
if (ret < 0)
return ret;
return IIO_VAL_INT;
}
static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
@ -956,15 +1440,28 @@ static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_LIGHT:
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
if (ret < 0)
return ret;
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret);
return (dir == IIO_EV_DIR_RISING) ?
FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
return data->als_int;
case IIO_PROXIMITY:
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
return ret;
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
return (dir == IIO_EV_DIR_RISING) ?
FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
default:
return -EINVAL;
}
}
static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
@ -972,29 +1469,51 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
enum iio_event_type type,
enum iio_event_direction dir, int state)
{
int ret;
int ret = -EINVAL;
u16 val, mask;
struct vcnl4000_data *data = iio_priv(indio_dev);
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
goto out;
switch (chan->type) {
case IIO_LIGHT:
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
if (ret < 0)
goto out;
if (dir == IIO_EV_DIR_RISING)
mask = VCNL4040_PS_IF_AWAY;
else
mask = VCNL4040_PS_IF_CLOSE;
mask = VCNL4040_ALS_CONF_INT_EN;
if (state)
val = (ret | mask);
else
val = (ret & ~mask);
val = state ? (ret | mask) : (ret & ~mask);
data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
val);
break;
case IIO_PROXIMITY:
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
goto out;
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
if (dir == IIO_EV_DIR_RISING)
mask = VCNL4040_PS_IF_AWAY;
else
mask = VCNL4040_PS_IF_CLOSE;
val = state ? (ret | mask) : (ret & ~mask);
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
val);
break;
default:
break;
}
out:
mutex_unlock(&data->vcnl4000_lock);
data->chip_spec->set_power_state(data, data->ps_int != 0);
data->chip_spec->set_power_state(data, data->ps_int || data->als_int);
return ret;
}
@ -1005,7 +1524,7 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
struct vcnl4000_data *data = iio_priv(indio_dev);
int ret;
ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS);
ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg);
if (ret < 0)
return IRQ_HANDLED;
@ -1025,6 +1544,22 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
iio_get_time_ns(indio_dev));
}
if (ret & VCNL4040_ALS_FALLING) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
iio_get_time_ns(indio_dev));
}
if (ret & VCNL4040_ALS_RISING) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
iio_get_time_ns(indio_dev));
}
return IRQ_HANDLED;
}
@ -1191,6 +1726,22 @@ static const struct iio_event_spec vcnl4000_event_spec[] = {
}
};
static const struct iio_event_spec vcnl4040_als_event_spec[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE),
}, {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE),
}, {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD),
},
};
static const struct iio_event_spec vcnl4040_event_spec[] = {
{
.type = IIO_EV_TYPE_THRESH,
@ -1200,6 +1751,10 @@ static const struct iio_event_spec vcnl4040_event_spec[] = {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
}, {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_PERIOD),
},
};
@ -1244,12 +1799,20 @@ static const struct iio_chan_spec vcnl4040_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
.event_spec = vcnl4040_als_event_spec,
.num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec),
}, {
.type = IIO_PROXIMITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_INT_TIME),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_CALIBBIAS),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_CALIBBIAS),
.ext_info = vcnl4000_ext_info,
.event_spec = vcnl4040_event_spec,
.num_event_specs = ARRAY_SIZE(vcnl4040_event_spec),
@ -1314,6 +1877,12 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
.num_channels = ARRAY_SIZE(vcnl4040_channels),
.info = &vcnl4040_info,
.irq_thread = vcnl4040_irq_thread,
.int_reg = VCNL4040_INT_FLAGS,
.ps_it_times = &vcnl4040_ps_it_times,
.num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times),
.als_it_times = &vcnl4040_als_it_times,
.num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times),
.ulux_step = 100000,
},
[VCNL4200] = {
.prod = "VCNL4200",
@ -1321,9 +1890,16 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
.measure_light = vcnl4200_measure_light,
.measure_proximity = vcnl4200_measure_proximity,
.set_power_state = vcnl4200_set_power_state,
.channels = vcnl4000_channels,
.channels = vcnl4040_channels,
.num_channels = ARRAY_SIZE(vcnl4000_channels),
.info = &vcnl4000_info,
.info = &vcnl4040_info,
.irq_thread = vcnl4040_irq_thread,
.int_reg = VCNL4200_INT_FLAGS,
.ps_it_times = &vcnl4200_ps_it_times,
.num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times),
.als_it_times = &vcnl4200_als_it_times,
.num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times),
.ulux_step = 24000,
},
};

View File

@ -99,20 +99,25 @@ static const struct iio_info mcp4018_info = {
.write_raw = mcp4018_write_raw,
};
#define MCP4018_ID_TABLE(_name, cfg) { \
.name = _name, \
.driver_data = (kernel_ulong_t)&mcp4018_cfg[cfg], \
}
static const struct i2c_device_id mcp4018_id[] = {
{ "mcp4017-502", MCP4018_502 },
{ "mcp4017-103", MCP4018_103 },
{ "mcp4017-503", MCP4018_503 },
{ "mcp4017-104", MCP4018_104 },
{ "mcp4018-502", MCP4018_502 },
{ "mcp4018-103", MCP4018_103 },
{ "mcp4018-503", MCP4018_503 },
{ "mcp4018-104", MCP4018_104 },
{ "mcp4019-502", MCP4018_502 },
{ "mcp4019-103", MCP4018_103 },
{ "mcp4019-503", MCP4018_503 },
{ "mcp4019-104", MCP4018_104 },
{}
MCP4018_ID_TABLE("mcp4017-502", MCP4018_502),
MCP4018_ID_TABLE("mcp4017-103", MCP4018_103),
MCP4018_ID_TABLE("mcp4017-503", MCP4018_503),
MCP4018_ID_TABLE("mcp4017-104", MCP4018_104),
MCP4018_ID_TABLE("mcp4018-502", MCP4018_502),
MCP4018_ID_TABLE("mcp4018-103", MCP4018_103),
MCP4018_ID_TABLE("mcp4018-503", MCP4018_503),
MCP4018_ID_TABLE("mcp4018-104", MCP4018_104),
MCP4018_ID_TABLE("mcp4019-502", MCP4018_502),
MCP4018_ID_TABLE("mcp4019-103", MCP4018_103),
MCP4018_ID_TABLE("mcp4019-503", MCP4018_503),
MCP4018_ID_TABLE("mcp4019-104", MCP4018_104),
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, mcp4018_id);
@ -157,9 +162,7 @@ static int mcp4018_probe(struct i2c_client *client)
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->cfg = device_get_match_data(dev);
if (!data->cfg)
data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data];
data->cfg = i2c_get_match_data(client);
indio_dev->info = &mcp4018_info;
indio_dev->channels = &mcp4018_channel;

View File

@ -206,72 +206,77 @@ static const struct iio_info mcp4531_info = {
.write_raw = mcp4531_write_raw,
};
#define MCP4531_ID_TABLE(_name, cfg) { \
.name = _name, \
.driver_data = (kernel_ulong_t)&mcp4531_cfg[cfg], \
}
static const struct i2c_device_id mcp4531_id[] = {
{ "mcp4531-502", MCP453x_502 },
{ "mcp4531-103", MCP453x_103 },
{ "mcp4531-503", MCP453x_503 },
{ "mcp4531-104", MCP453x_104 },
{ "mcp4532-502", MCP453x_502 },
{ "mcp4532-103", MCP453x_103 },
{ "mcp4532-503", MCP453x_503 },
{ "mcp4532-104", MCP453x_104 },
{ "mcp4541-502", MCP454x_502 },
{ "mcp4541-103", MCP454x_103 },
{ "mcp4541-503", MCP454x_503 },
{ "mcp4541-104", MCP454x_104 },
{ "mcp4542-502", MCP454x_502 },
{ "mcp4542-103", MCP454x_103 },
{ "mcp4542-503", MCP454x_503 },
{ "mcp4542-104", MCP454x_104 },
{ "mcp4551-502", MCP455x_502 },
{ "mcp4551-103", MCP455x_103 },
{ "mcp4551-503", MCP455x_503 },
{ "mcp4551-104", MCP455x_104 },
{ "mcp4552-502", MCP455x_502 },
{ "mcp4552-103", MCP455x_103 },
{ "mcp4552-503", MCP455x_503 },
{ "mcp4552-104", MCP455x_104 },
{ "mcp4561-502", MCP456x_502 },
{ "mcp4561-103", MCP456x_103 },
{ "mcp4561-503", MCP456x_503 },
{ "mcp4561-104", MCP456x_104 },
{ "mcp4562-502", MCP456x_502 },
{ "mcp4562-103", MCP456x_103 },
{ "mcp4562-503", MCP456x_503 },
{ "mcp4562-104", MCP456x_104 },
{ "mcp4631-502", MCP463x_502 },
{ "mcp4631-103", MCP463x_103 },
{ "mcp4631-503", MCP463x_503 },
{ "mcp4631-104", MCP463x_104 },
{ "mcp4632-502", MCP463x_502 },
{ "mcp4632-103", MCP463x_103 },
{ "mcp4632-503", MCP463x_503 },
{ "mcp4632-104", MCP463x_104 },
{ "mcp4641-502", MCP464x_502 },
{ "mcp4641-103", MCP464x_103 },
{ "mcp4641-503", MCP464x_503 },
{ "mcp4641-104", MCP464x_104 },
{ "mcp4642-502", MCP464x_502 },
{ "mcp4642-103", MCP464x_103 },
{ "mcp4642-503", MCP464x_503 },
{ "mcp4642-104", MCP464x_104 },
{ "mcp4651-502", MCP465x_502 },
{ "mcp4651-103", MCP465x_103 },
{ "mcp4651-503", MCP465x_503 },
{ "mcp4651-104", MCP465x_104 },
{ "mcp4652-502", MCP465x_502 },
{ "mcp4652-103", MCP465x_103 },
{ "mcp4652-503", MCP465x_503 },
{ "mcp4652-104", MCP465x_104 },
{ "mcp4661-502", MCP466x_502 },
{ "mcp4661-103", MCP466x_103 },
{ "mcp4661-503", MCP466x_503 },
{ "mcp4661-104", MCP466x_104 },
{ "mcp4662-502", MCP466x_502 },
{ "mcp4662-103", MCP466x_103 },
{ "mcp4662-503", MCP466x_503 },
{ "mcp4662-104", MCP466x_104 },
{}
MCP4531_ID_TABLE("mcp4531-502", MCP453x_502),
MCP4531_ID_TABLE("mcp4531-103", MCP453x_103),
MCP4531_ID_TABLE("mcp4531-503", MCP453x_503),
MCP4531_ID_TABLE("mcp4531-104", MCP453x_104),
MCP4531_ID_TABLE("mcp4532-502", MCP453x_502),
MCP4531_ID_TABLE("mcp4532-103", MCP453x_103),
MCP4531_ID_TABLE("mcp4532-503", MCP453x_503),
MCP4531_ID_TABLE("mcp4532-104", MCP453x_104),
MCP4531_ID_TABLE("mcp4541-502", MCP454x_502),
MCP4531_ID_TABLE("mcp4541-103", MCP454x_103),
MCP4531_ID_TABLE("mcp4541-503", MCP454x_503),
MCP4531_ID_TABLE("mcp4541-104", MCP454x_104),
MCP4531_ID_TABLE("mcp4542-502", MCP454x_502),
MCP4531_ID_TABLE("mcp4542-103", MCP454x_103),
MCP4531_ID_TABLE("mcp4542-503", MCP454x_503),
MCP4531_ID_TABLE("mcp4542-104", MCP454x_104),
MCP4531_ID_TABLE("mcp4551-502", MCP455x_502),
MCP4531_ID_TABLE("mcp4551-103", MCP455x_103),
MCP4531_ID_TABLE("mcp4551-503", MCP455x_503),
MCP4531_ID_TABLE("mcp4551-104", MCP455x_104),
MCP4531_ID_TABLE("mcp4552-502", MCP455x_502),
MCP4531_ID_TABLE("mcp4552-103", MCP455x_103),
MCP4531_ID_TABLE("mcp4552-503", MCP455x_503),
MCP4531_ID_TABLE("mcp4552-104", MCP455x_104),
MCP4531_ID_TABLE("mcp4561-502", MCP456x_502),
MCP4531_ID_TABLE("mcp4561-103", MCP456x_103),
MCP4531_ID_TABLE("mcp4561-503", MCP456x_503),
MCP4531_ID_TABLE("mcp4561-104", MCP456x_104),
MCP4531_ID_TABLE("mcp4562-502", MCP456x_502),
MCP4531_ID_TABLE("mcp4562-103", MCP456x_103),
MCP4531_ID_TABLE("mcp4562-503", MCP456x_503),
MCP4531_ID_TABLE("mcp4562-104", MCP456x_104),
MCP4531_ID_TABLE("mcp4631-502", MCP463x_502),
MCP4531_ID_TABLE("mcp4631-103", MCP463x_103),
MCP4531_ID_TABLE("mcp4631-503", MCP463x_503),
MCP4531_ID_TABLE("mcp4631-104", MCP463x_104),
MCP4531_ID_TABLE("mcp4632-502", MCP463x_502),
MCP4531_ID_TABLE("mcp4632-103", MCP463x_103),
MCP4531_ID_TABLE("mcp4632-503", MCP463x_503),
MCP4531_ID_TABLE("mcp4632-104", MCP463x_104),
MCP4531_ID_TABLE("mcp4641-502", MCP464x_502),
MCP4531_ID_TABLE("mcp4641-103", MCP464x_103),
MCP4531_ID_TABLE("mcp4641-503", MCP464x_503),
MCP4531_ID_TABLE("mcp4641-104", MCP464x_104),
MCP4531_ID_TABLE("mcp4642-502", MCP464x_502),
MCP4531_ID_TABLE("mcp4642-103", MCP464x_103),
MCP4531_ID_TABLE("mcp4642-503", MCP464x_503),
MCP4531_ID_TABLE("mcp4642-104", MCP464x_104),
MCP4531_ID_TABLE("mcp4651-502", MCP465x_502),
MCP4531_ID_TABLE("mcp4651-103", MCP465x_103),
MCP4531_ID_TABLE("mcp4651-503", MCP465x_503),
MCP4531_ID_TABLE("mcp4651-104", MCP465x_104),
MCP4531_ID_TABLE("mcp4652-502", MCP465x_502),
MCP4531_ID_TABLE("mcp4652-103", MCP465x_103),
MCP4531_ID_TABLE("mcp4652-503", MCP465x_503),
MCP4531_ID_TABLE("mcp4652-104", MCP465x_104),
MCP4531_ID_TABLE("mcp4661-502", MCP466x_502),
MCP4531_ID_TABLE("mcp4661-103", MCP466x_103),
MCP4531_ID_TABLE("mcp4661-503", MCP466x_503),
MCP4531_ID_TABLE("mcp4661-104", MCP466x_104),
MCP4531_ID_TABLE("mcp4662-502", MCP466x_502),
MCP4531_ID_TABLE("mcp4662-103", MCP466x_103),
MCP4531_ID_TABLE("mcp4662-503", MCP466x_503),
MCP4531_ID_TABLE("mcp4662-104", MCP466x_104),
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, mcp4531_id);
@ -368,9 +373,7 @@ static int mcp4531_probe(struct i2c_client *client)
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->cfg = device_get_match_data(dev);
if (!data->cfg)
data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data];
data->cfg = i2c_get_match_data(client);
indio_dev->info = &mcp4531_info;
indio_dev->channels = mcp4531_channels;

View File

@ -32,6 +32,18 @@ config CROS_EC_MKBP_PROXIMITY
To compile this driver as a module, choose M here: the
module will be called cros_ec_mkbp_proximity.
config IRSD200
tristate "Murata IRS-D200 PIR sensor"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP_I2C
depends on I2C
help
Say Y here to build a driver for the Murata IRS-D200 PIR sensor.
To compile this driver as a module, choose M here: the module will be
called irsd200.
config ISL29501
tristate "Intersil ISL29501 Time Of Flight sensor"
depends on I2C

View File

@ -6,6 +6,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o
obj-$(CONFIG_IRSD200) += irsd200.o
obj-$(CONFIG_ISL29501) += isl29501.o
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
obj-$(CONFIG_MB1232) += mb1232.o

View File

@ -0,0 +1,958 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Murata IRS-D200 PIR sensor.
*
* Copyright (C) 2023 Axis Communications AB
*/
#include <asm/unaligned.h>
#include <linux/bitfield.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/types.h>
#define IRS_DRV_NAME "irsd200"
/* Registers. */
#define IRS_REG_OP 0x00 /* Operation mode. */
#define IRS_REG_DATA_LO 0x02 /* Sensor data LSB. */
#define IRS_REG_DATA_HI 0x03 /* Sensor data MSB. */
#define IRS_REG_STATUS 0x04 /* Interrupt status. */
#define IRS_REG_COUNT 0x05 /* Count of exceeding threshold. */
#define IRS_REG_DATA_RATE 0x06 /* Output data rate. */
#define IRS_REG_FILTER 0x07 /* High-pass and low-pass filter. */
#define IRS_REG_INTR 0x09 /* Interrupt mode. */
#define IRS_REG_NR_COUNT 0x0a /* Number of counts before interrupt. */
#define IRS_REG_THR_HI 0x0b /* Upper threshold. */
#define IRS_REG_THR_LO 0x0c /* Lower threshold. */
#define IRS_REG_TIMER_LO 0x0d /* Timer setting LSB. */
#define IRS_REG_TIMER_HI 0x0e /* Timer setting MSB. */
/* Interrupt status bits. */
#define IRS_INTR_DATA 0 /* Data update. */
#define IRS_INTR_TIMER 1 /* Timer expiration. */
#define IRS_INTR_COUNT_THR_AND 2 /* Count "AND" threshold. */
#define IRS_INTR_COUNT_THR_OR 3 /* Count "OR" threshold. */
/* Operation states. */
#define IRS_OP_ACTIVE 0x00
#define IRS_OP_SLEEP 0x01
/*
* Quantization scale value for threshold. Used for conversion from/to register
* value.
*/
#define IRS_THR_QUANT_SCALE 128
#define IRS_UPPER_COUNT(count) FIELD_GET(GENMASK(7, 4), count)
#define IRS_LOWER_COUNT(count) FIELD_GET(GENMASK(3, 0), count)
/* Index corresponds to the value of IRS_REG_DATA_RATE register. */
static const int irsd200_data_rates[] = {
50,
100,
};
/* Index corresponds to the (field) value of IRS_REG_FILTER register. */
static const unsigned int irsd200_lp_filter_freq[] = {
10,
7,
};
/*
* Index corresponds to the (field) value of IRS_REG_FILTER register. Note that
* this represents a fractional value (e.g the first value corresponds to 3 / 10
* = 0.3 Hz).
*/
static const unsigned int irsd200_hp_filter_freq[][2] = {
{ 3, 10 },
{ 5, 10 },
};
/* Register fields. */
enum irsd200_regfield {
/* Data interrupt. */
IRS_REGF_INTR_DATA,
/* Timer interrupt. */
IRS_REGF_INTR_TIMER,
/* AND count threshold interrupt. */
IRS_REGF_INTR_COUNT_THR_AND,
/* OR count threshold interrupt. */
IRS_REGF_INTR_COUNT_THR_OR,
/* Low-pass filter frequency. */
IRS_REGF_LP_FILTER,
/* High-pass filter frequency. */
IRS_REGF_HP_FILTER,
/* Sentinel value. */
IRS_REGF_MAX
};
static const struct reg_field irsd200_regfields[] = {
[IRS_REGF_INTR_DATA] =
REG_FIELD(IRS_REG_INTR, IRS_INTR_DATA, IRS_INTR_DATA),
[IRS_REGF_INTR_TIMER] =
REG_FIELD(IRS_REG_INTR, IRS_INTR_TIMER, IRS_INTR_TIMER),
[IRS_REGF_INTR_COUNT_THR_AND] = REG_FIELD(
IRS_REG_INTR, IRS_INTR_COUNT_THR_AND, IRS_INTR_COUNT_THR_AND),
[IRS_REGF_INTR_COUNT_THR_OR] = REG_FIELD(
IRS_REG_INTR, IRS_INTR_COUNT_THR_OR, IRS_INTR_COUNT_THR_OR),
[IRS_REGF_LP_FILTER] = REG_FIELD(IRS_REG_FILTER, 1, 1),
[IRS_REGF_HP_FILTER] = REG_FIELD(IRS_REG_FILTER, 0, 0),
};
static const struct regmap_config irsd200_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = IRS_REG_TIMER_HI,
};
struct irsd200_data {
struct regmap *regmap;
struct regmap_field *regfields[IRS_REGF_MAX];
struct device *dev;
};
static int irsd200_setup(struct irsd200_data *data)
{
unsigned int val;
int ret;
/* Disable all interrupt sources. */
ret = regmap_write(data->regmap, IRS_REG_INTR, 0);
if (ret) {
dev_err(data->dev, "Could not set interrupt sources (%d)\n",
ret);
return ret;
}
/* Set operation to active. */
ret = regmap_write(data->regmap, IRS_REG_OP, IRS_OP_ACTIVE);
if (ret) {
dev_err(data->dev, "Could not set operation mode (%d)\n", ret);
return ret;
}
/* Clear threshold count. */
ret = regmap_read(data->regmap, IRS_REG_COUNT, &val);
if (ret) {
dev_err(data->dev, "Could not clear threshold count (%d)\n",
ret);
return ret;
}
/* Clear status. */
ret = regmap_write(data->regmap, IRS_REG_STATUS, 0x0f);
if (ret) {
dev_err(data->dev, "Could not clear status (%d)\n", ret);
return ret;
}
return 0;
}
static int irsd200_read_threshold(struct irsd200_data *data,
enum iio_event_direction dir, int *val)
{
unsigned int regval;
unsigned int reg;
int scale;
int ret;
/* Set quantization scale. */
if (dir == IIO_EV_DIR_RISING) {
scale = IRS_THR_QUANT_SCALE;
reg = IRS_REG_THR_HI;
} else if (dir == IIO_EV_DIR_FALLING) {
scale = -IRS_THR_QUANT_SCALE;
reg = IRS_REG_THR_LO;
} else {
return -EINVAL;
}
ret = regmap_read(data->regmap, reg, &regval);
if (ret) {
dev_err(data->dev, "Could not read threshold (%d)\n", ret);
return ret;
}
*val = ((int)regval) * scale;
return 0;
}
static int irsd200_write_threshold(struct irsd200_data *data,
enum iio_event_direction dir, int val)
{
unsigned int regval;
unsigned int reg;
int scale;
int ret;
/* Set quantization scale. */
if (dir == IIO_EV_DIR_RISING) {
if (val < 0)
return -ERANGE;
scale = IRS_THR_QUANT_SCALE;
reg = IRS_REG_THR_HI;
} else if (dir == IIO_EV_DIR_FALLING) {
if (val > 0)
return -ERANGE;
scale = -IRS_THR_QUANT_SCALE;
reg = IRS_REG_THR_LO;
} else {
return -EINVAL;
}
regval = val / scale;
if (regval >= BIT(8))
return -ERANGE;
ret = regmap_write(data->regmap, reg, regval);
if (ret) {
dev_err(data->dev, "Could not write threshold (%d)\n", ret);
return ret;
}
return 0;
}
static int irsd200_read_data(struct irsd200_data *data, s16 *val)
{
__le16 buf;
int ret;
ret = regmap_bulk_read(data->regmap, IRS_REG_DATA_LO, &buf,
sizeof(buf));
if (ret) {
dev_err(data->dev, "Could not bulk read data (%d)\n", ret);
return ret;
}
*val = le16_to_cpu(buf);
return 0;
}
static int irsd200_read_data_rate(struct irsd200_data *data, int *val)
{
unsigned int regval;
int ret;
ret = regmap_read(data->regmap, IRS_REG_DATA_RATE, &regval);
if (ret) {
dev_err(data->dev, "Could not read data rate (%d)\n", ret);
return ret;
}
if (regval >= ARRAY_SIZE(irsd200_data_rates))
return -ERANGE;
*val = irsd200_data_rates[regval];
return 0;
}
static int irsd200_write_data_rate(struct irsd200_data *data, int val)
{
size_t idx;
int ret;
for (idx = 0; idx < ARRAY_SIZE(irsd200_data_rates); ++idx) {
if (irsd200_data_rates[idx] == val)
break;
}
if (idx == ARRAY_SIZE(irsd200_data_rates))
return -ERANGE;
ret = regmap_write(data->regmap, IRS_REG_DATA_RATE, idx);
if (ret) {
dev_err(data->dev, "Could not write data rate (%d)\n", ret);
return ret;
}
/*
* Data sheet says the device needs 3 seconds of settling time. The
* device operates normally during this period though. This is more of a
* "guarantee" than trying to prevent other user space reads/writes.
*/
ssleep(3);
return 0;
}
static int irsd200_read_timer(struct irsd200_data *data, int *val, int *val2)
{
__le16 buf;
int ret;
ret = regmap_bulk_read(data->regmap, IRS_REG_TIMER_LO, &buf,
sizeof(buf));
if (ret) {
dev_err(data->dev, "Could not bulk read timer (%d)\n", ret);
return ret;
}
ret = irsd200_read_data_rate(data, val2);
if (ret)
return ret;
*val = le16_to_cpu(buf);
return 0;
}
static int irsd200_write_timer(struct irsd200_data *data, int val, int val2)
{
unsigned int regval;
int data_rate;
__le16 buf;
int ret;
if (val < 0 || val2 < 0)
return -ERANGE;
ret = irsd200_read_data_rate(data, &data_rate);
if (ret)
return ret;
/* Quantize from seconds. */
regval = val * data_rate + (val2 * data_rate) / 1000000;
/* Value is 10 bits. */
if (regval >= BIT(10))
return -ERANGE;
buf = cpu_to_le16((u16)regval);
ret = regmap_bulk_write(data->regmap, IRS_REG_TIMER_LO, &buf,
sizeof(buf));
if (ret) {
dev_err(data->dev, "Could not bulk write timer (%d)\n", ret);
return ret;
}
return 0;
}
static int irsd200_read_nr_count(struct irsd200_data *data, int *val)
{
unsigned int regval;
int ret;
ret = regmap_read(data->regmap, IRS_REG_NR_COUNT, &regval);
if (ret) {
dev_err(data->dev, "Could not read nr count (%d)\n", ret);
return ret;
}
*val = regval;
return 0;
}
static int irsd200_write_nr_count(struct irsd200_data *data, int val)
{
unsigned int regval;
int ret;
/* A value of zero means that IRS_REG_STATUS is never set. */
if (val <= 0 || val >= 8)
return -ERANGE;
regval = val;
if (regval >= 2) {
/*
* According to the data sheet, timer must be also set in this
* case (i.e. be non-zero). Check and enforce that.
*/
ret = irsd200_read_timer(data, &val, &val);
if (ret)
return ret;
if (val == 0) {
dev_err(data->dev,
"Timer must be non-zero when nr count is %u\n",
regval);
return -EPERM;
}
}
ret = regmap_write(data->regmap, IRS_REG_NR_COUNT, regval);
if (ret) {
dev_err(data->dev, "Could not write nr count (%d)\n", ret);
return ret;
}
return 0;
}
static int irsd200_read_lp_filter(struct irsd200_data *data, int *val)
{
unsigned int regval;
int ret;
ret = regmap_field_read(data->regfields[IRS_REGF_LP_FILTER], &regval);
if (ret) {
dev_err(data->dev, "Could not read lp filter frequency (%d)\n",
ret);
return ret;
}
*val = irsd200_lp_filter_freq[regval];
return 0;
}
static int irsd200_write_lp_filter(struct irsd200_data *data, int val)
{
size_t idx;
int ret;
for (idx = 0; idx < ARRAY_SIZE(irsd200_lp_filter_freq); ++idx) {
if (irsd200_lp_filter_freq[idx] == val)
break;
}
if (idx == ARRAY_SIZE(irsd200_lp_filter_freq))
return -ERANGE;
ret = regmap_field_write(data->regfields[IRS_REGF_LP_FILTER], idx);
if (ret) {
dev_err(data->dev, "Could not write lp filter frequency (%d)\n",
ret);
return ret;
}
return 0;
}
static int irsd200_read_hp_filter(struct irsd200_data *data, int *val,
int *val2)
{
unsigned int regval;
int ret;
ret = regmap_field_read(data->regfields[IRS_REGF_HP_FILTER], &regval);
if (ret) {
dev_err(data->dev, "Could not read hp filter frequency (%d)\n",
ret);
return ret;
}
*val = irsd200_hp_filter_freq[regval][0];
*val2 = irsd200_hp_filter_freq[regval][1];
return 0;
}
static int irsd200_write_hp_filter(struct irsd200_data *data, int val, int val2)
{
size_t idx;
int ret;
/* Truncate fractional part to one digit. */
val2 /= 100000;
for (idx = 0; idx < ARRAY_SIZE(irsd200_hp_filter_freq); ++idx) {
if (irsd200_hp_filter_freq[idx][0] == val2)
break;
}
if (idx == ARRAY_SIZE(irsd200_hp_filter_freq) || val != 0)
return -ERANGE;
ret = regmap_field_write(data->regfields[IRS_REGF_HP_FILTER], idx);
if (ret) {
dev_err(data->dev, "Could not write hp filter frequency (%d)\n",
ret);
return ret;
}
return 0;
}
static int irsd200_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct irsd200_data *data = iio_priv(indio_dev);
int ret;
s16 buf;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = irsd200_read_data(data, &buf);
if (ret)
return ret;
*val = buf;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = irsd200_read_data_rate(data, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
ret = irsd200_read_lp_filter(data, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
ret = irsd200_read_hp_filter(data, val, val2);
if (ret)
return ret;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
static int irsd200_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = irsd200_data_rates;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(irsd200_data_rates);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = irsd200_lp_filter_freq;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(irsd200_lp_filter_freq);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
*vals = (int *)irsd200_hp_filter_freq;
*type = IIO_VAL_FRACTIONAL;
*length = 2 * ARRAY_SIZE(irsd200_hp_filter_freq);
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int irsd200_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct irsd200_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return irsd200_write_data_rate(data, val);
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
return irsd200_write_lp_filter(data, val);
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
return irsd200_write_hp_filter(data, val, val2);
default:
return -EINVAL;
}
}
static int irsd200_read_event(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info, int *val, int *val2)
{
struct irsd200_data *data = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_EV_INFO_VALUE:
ret = irsd200_read_threshold(data, dir, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_EV_INFO_RUNNING_PERIOD:
ret = irsd200_read_timer(data, val, val2);
if (ret)
return ret;
return IIO_VAL_FRACTIONAL;
case IIO_EV_INFO_RUNNING_COUNT:
ret = irsd200_read_nr_count(data, val);
if (ret)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int irsd200_write_event(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info, int val, int val2)
{
struct irsd200_data *data = iio_priv(indio_dev);
switch (info) {
case IIO_EV_INFO_VALUE:
return irsd200_write_threshold(data, dir, val);
case IIO_EV_INFO_RUNNING_PERIOD:
return irsd200_write_timer(data, val, val2);
case IIO_EV_INFO_RUNNING_COUNT:
return irsd200_write_nr_count(data, val);
default:
return -EINVAL;
}
}
static int irsd200_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct irsd200_data *data = iio_priv(indio_dev);
unsigned int val;
int ret;
switch (type) {
case IIO_EV_TYPE_THRESH:
ret = regmap_field_read(
data->regfields[IRS_REGF_INTR_COUNT_THR_OR], &val);
if (ret)
return ret;
return val;
default:
return -EINVAL;
}
}
static int irsd200_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir, int state)
{
struct irsd200_data *data = iio_priv(indio_dev);
unsigned int tmp;
int ret;
switch (type) {
case IIO_EV_TYPE_THRESH:
/* Clear the count register (by reading from it). */
ret = regmap_read(data->regmap, IRS_REG_COUNT, &tmp);
if (ret)
return ret;
return regmap_field_write(
data->regfields[IRS_REGF_INTR_COUNT_THR_OR], !!state);
default:
return -EINVAL;
}
}
static irqreturn_t irsd200_irq_thread(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct irsd200_data *data = iio_priv(indio_dev);
enum iio_event_direction dir;
unsigned int lower_count;
unsigned int upper_count;
unsigned int status = 0;
unsigned int source = 0;
unsigned int clear = 0;
unsigned int count = 0;
int ret;
ret = regmap_read(data->regmap, IRS_REG_INTR, &source);
if (ret) {
dev_err(data->dev, "Could not read interrupt source (%d)\n",
ret);
return IRQ_HANDLED;
}
ret = regmap_read(data->regmap, IRS_REG_STATUS, &status);
if (ret) {
dev_err(data->dev, "Could not acknowledge interrupt (%d)\n",
ret);
return IRQ_HANDLED;
}
if (status & BIT(IRS_INTR_DATA) && iio_buffer_enabled(indio_dev)) {
iio_trigger_poll_nested(indio_dev->trig);
clear |= BIT(IRS_INTR_DATA);
}
if (status & BIT(IRS_INTR_COUNT_THR_OR) &&
source & BIT(IRS_INTR_COUNT_THR_OR)) {
/*
* The register value resets to zero after reading. We therefore
* need to read once and manually extract the lower and upper
* count register fields.
*/
ret = regmap_read(data->regmap, IRS_REG_COUNT, &count);
if (ret)
dev_err(data->dev, "Could not read count (%d)\n", ret);
upper_count = IRS_UPPER_COUNT(count);
lower_count = IRS_LOWER_COUNT(count);
/*
* We only check the OR mode to be able to push events for
* rising and falling thresholds. AND mode is covered when both
* upper and lower count is non-zero, and is signaled with
* IIO_EV_DIR_EITHER.
*/
if (upper_count && !lower_count)
dir = IIO_EV_DIR_RISING;
else if (!upper_count && lower_count)
dir = IIO_EV_DIR_FALLING;
else
dir = IIO_EV_DIR_EITHER;
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
IIO_EV_TYPE_THRESH, dir),
iio_get_time_ns(indio_dev));
/*
* The OR mode will always trigger when the AND mode does, but
* not vice versa. However, it seems like the AND bit needs to
* be cleared if data capture _and_ threshold count interrupts
* are desirable, even though it hasn't explicitly been selected
* (with IRS_REG_INTR). Either way, it doesn't hurt...
*/
clear |= BIT(IRS_INTR_COUNT_THR_OR) |
BIT(IRS_INTR_COUNT_THR_AND);
}
if (!clear)
return IRQ_NONE;
ret = regmap_write(data->regmap, IRS_REG_STATUS, clear);
if (ret)
dev_err(data->dev,
"Could not clear interrupt status (%d)\n", ret);
return IRQ_HANDLED;
}
static irqreturn_t irsd200_trigger_handler(int irq, void *pollf)
{
struct iio_dev *indio_dev = ((struct iio_poll_func *)pollf)->indio_dev;
struct irsd200_data *data = iio_priv(indio_dev);
s16 buf = 0;
int ret;
ret = irsd200_read_data(data, &buf);
if (ret)
goto end;
iio_push_to_buffers_with_timestamp(indio_dev, &buf,
iio_get_time_ns(indio_dev));
end:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int irsd200_set_trigger_state(struct iio_trigger *trig, bool state)
{
struct irsd200_data *data = iio_trigger_get_drvdata(trig);
int ret;
ret = regmap_field_write(data->regfields[IRS_REGF_INTR_DATA], state);
if (ret) {
dev_err(data->dev, "Could not %s data interrupt source (%d)\n",
state ? "enable" : "disable", ret);
}
return ret;
}
static const struct iio_info irsd200_info = {
.read_raw = irsd200_read_raw,
.read_avail = irsd200_read_avail,
.write_raw = irsd200_write_raw,
.read_event_value = irsd200_read_event,
.write_event_value = irsd200_write_event,
.read_event_config = irsd200_read_event_config,
.write_event_config = irsd200_write_event_config,
};
static const struct iio_trigger_ops irsd200_trigger_ops = {
.set_trigger_state = irsd200_set_trigger_state,
.validate_device = iio_trigger_validate_own_device,
};
static const struct iio_event_spec irsd200_event_spec[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate =
BIT(IIO_EV_INFO_RUNNING_PERIOD) |
BIT(IIO_EV_INFO_RUNNING_COUNT) |
BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_chan_spec irsd200_channels[] = {
{
.type = IIO_PROXIMITY,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY),
.info_mask_separate_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY),
.event_spec = irsd200_event_spec,
.num_event_specs = ARRAY_SIZE(irsd200_event_spec),
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
};
static int irsd200_probe(struct i2c_client *client)
{
struct iio_trigger *trigger;
struct irsd200_data *data;
struct iio_dev *indio_dev;
size_t i;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return dev_err_probe(&client->dev, -ENOMEM,
"Could not allocate iio device\n");
data = iio_priv(indio_dev);
data->dev = &client->dev;
data->regmap = devm_regmap_init_i2c(client, &irsd200_regmap_config);
if (IS_ERR(data->regmap))
return dev_err_probe(data->dev, PTR_ERR(data->regmap),
"Could not initialize regmap\n");
for (i = 0; i < IRS_REGF_MAX; ++i) {
data->regfields[i] = devm_regmap_field_alloc(
data->dev, data->regmap, irsd200_regfields[i]);
if (IS_ERR(data->regfields[i]))
return dev_err_probe(
data->dev, PTR_ERR(data->regfields[i]),
"Could not allocate register field %zu\n", i);
}
ret = devm_regulator_get_enable(data->dev, "vdd");
if (ret)
return dev_err_probe(
data->dev, ret,
"Could not get and enable regulator (%d)\n", ret);
ret = irsd200_setup(data);
if (ret)
return ret;
indio_dev->info = &irsd200_info;
indio_dev->name = IRS_DRV_NAME;
indio_dev->channels = irsd200_channels;
indio_dev->num_channels = ARRAY_SIZE(irsd200_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
if (!client->irq)
return dev_err_probe(data->dev, -ENXIO, "No irq available\n");
ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev, NULL,
irsd200_trigger_handler, NULL);
if (ret)
return dev_err_probe(
data->dev, ret,
"Could not setup iio triggered buffer (%d)\n", ret);
ret = devm_request_threaded_irq(data->dev, client->irq, NULL,
irsd200_irq_thread,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
NULL, indio_dev);
if (ret)
return dev_err_probe(data->dev, ret,
"Could not request irq (%d)\n", ret);
trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name,
iio_device_id(indio_dev));
if (!trigger)
return dev_err_probe(data->dev, -ENOMEM,
"Could not allocate iio trigger\n");
trigger->ops = &irsd200_trigger_ops;
iio_trigger_set_drvdata(trigger, data);
ret = devm_iio_trigger_register(data->dev, trigger);
if (ret)
return dev_err_probe(data->dev, ret,
"Could not register iio trigger (%d)\n",
ret);
ret = devm_iio_device_register(data->dev, indio_dev);
if (ret)
return dev_err_probe(data->dev, ret,
"Could not register iio device (%d)\n",
ret);
return 0;
}
static const struct of_device_id irsd200_of_match[] = {
{
.compatible = "murata,irsd200",
},
{}
};
MODULE_DEVICE_TABLE(of, irsd200_of_match);
static struct i2c_driver irsd200_driver = {
.driver = {
.name = IRS_DRV_NAME,
.of_match_table = irsd200_of_match,
},
.probe = irsd200_probe,
};
module_i2c_driver(irsd200_driver);
MODULE_AUTHOR("Waqar Hameed <waqar.hameed@axis.com>");
MODULE_DESCRIPTION("Murata IRS-D200 PIR sensor driver");
MODULE_LICENSE("GPL");

View File

@ -12,7 +12,7 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/of_device.h>
#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>

View File

@ -76,7 +76,7 @@ static s16 mb1232_read_distance(struct mb1232_data *data)
goto error_unlock;
}
if (data->irqnr >= 0) {
if (data->irqnr > 0) {
/* it cannot take more than 100 ms */
ret = wait_for_completion_killable_timeout(&data->ranging,
HZ/10);
@ -212,10 +212,7 @@ static int mb1232_probe(struct i2c_client *client)
init_completion(&data->ranging);
data->irqnr = fwnode_irq_get(dev_fwnode(&client->dev), 0);
if (data->irqnr <= 0) {
/* usage of interrupt is optional */
data->irqnr = -1;
} else {
if (data->irqnr > 0) {
ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq,
IRQF_TRIGGER_FALLING, id->name, indio_dev);
if (ret < 0) {

View File

@ -27,8 +27,8 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/jiffies.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>

View File

@ -73,7 +73,6 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev)
{
struct stm32_lptim_trigger *priv;
u32 index;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@ -88,13 +87,7 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
priv->trg = stm32_lptim_triggers[index];
ret = stm32_lptim_setup_trig(priv);
if (ret)
return ret;
platform_set_drvdata(pdev, priv);
return 0;
return stm32_lptim_setup_trig(priv);
}
static const struct of_device_id stm32_lptim_trig_of_match[] = {

View File

@ -6,58 +6,60 @@
#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
#define _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
/* ADC channels for PM8350_ADC for PMIC7 */
#define PM8350_ADC7_REF_GND(sid) ((sid) << 8 | 0x0)
#define PM8350_ADC7_1P25VREF(sid) ((sid) << 8 | 0x01)
#define PM8350_ADC7_VREF_VADC(sid) ((sid) << 8 | 0x02)
#define PM8350_ADC7_DIE_TEMP(sid) ((sid) << 8 | 0x03)
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#define PM8350_ADC7_AMUX_THM1(sid) ((sid) << 8 | 0x04)
#define PM8350_ADC7_AMUX_THM2(sid) ((sid) << 8 | 0x05)
#define PM8350_ADC7_AMUX_THM3(sid) ((sid) << 8 | 0x06)
#define PM8350_ADC7_AMUX_THM4(sid) ((sid) << 8 | 0x07)
#define PM8350_ADC7_AMUX_THM5(sid) ((sid) << 8 | 0x08)
#define PM8350_ADC7_GPIO1(sid) ((sid) << 8 | 0x0a)
#define PM8350_ADC7_GPIO2(sid) ((sid) << 8 | 0x0b)
#define PM8350_ADC7_GPIO3(sid) ((sid) << 8 | 0x0c)
#define PM8350_ADC7_GPIO4(sid) ((sid) << 8 | 0x0d)
/* ADC channels for PM8350_ADC for PMIC7 */
#define PM8350_ADC7_REF_GND(sid) ((sid) << 8 | ADC7_REF_GND)
#define PM8350_ADC7_1P25VREF(sid) ((sid) << 8 | ADC7_1P25VREF)
#define PM8350_ADC7_VREF_VADC(sid) ((sid) << 8 | ADC7_VREF_VADC)
#define PM8350_ADC7_DIE_TEMP(sid) ((sid) << 8 | ADC7_DIE_TEMP)
#define PM8350_ADC7_AMUX_THM1(sid) ((sid) << 8 | ADC7_AMUX_THM1)
#define PM8350_ADC7_AMUX_THM2(sid) ((sid) << 8 | ADC7_AMUX_THM2)
#define PM8350_ADC7_AMUX_THM3(sid) ((sid) << 8 | ADC7_AMUX_THM3)
#define PM8350_ADC7_AMUX_THM4(sid) ((sid) << 8 | ADC7_AMUX_THM4)
#define PM8350_ADC7_AMUX_THM5(sid) ((sid) << 8 | ADC7_AMUX_THM5)
#define PM8350_ADC7_GPIO1(sid) ((sid) << 8 | ADC7_GPIO1)
#define PM8350_ADC7_GPIO2(sid) ((sid) << 8 | ADC7_GPIO2)
#define PM8350_ADC7_GPIO3(sid) ((sid) << 8 | ADC7_GPIO3)
#define PM8350_ADC7_GPIO4(sid) ((sid) << 8 | ADC7_GPIO4)
/* 30k pull-up1 */
#define PM8350_ADC7_AMUX_THM1_30K_PU(sid) ((sid) << 8 | 0x24)
#define PM8350_ADC7_AMUX_THM2_30K_PU(sid) ((sid) << 8 | 0x25)
#define PM8350_ADC7_AMUX_THM3_30K_PU(sid) ((sid) << 8 | 0x26)
#define PM8350_ADC7_AMUX_THM4_30K_PU(sid) ((sid) << 8 | 0x27)
#define PM8350_ADC7_AMUX_THM5_30K_PU(sid) ((sid) << 8 | 0x28)
#define PM8350_ADC7_GPIO1_30K_PU(sid) ((sid) << 8 | 0x2a)
#define PM8350_ADC7_GPIO2_30K_PU(sid) ((sid) << 8 | 0x2b)
#define PM8350_ADC7_GPIO3_30K_PU(sid) ((sid) << 8 | 0x2c)
#define PM8350_ADC7_GPIO4_30K_PU(sid) ((sid) << 8 | 0x2d)
#define PM8350_ADC7_AMUX_THM1_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM1_30K_PU)
#define PM8350_ADC7_AMUX_THM2_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM2_30K_PU)
#define PM8350_ADC7_AMUX_THM3_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM3_30K_PU)
#define PM8350_ADC7_AMUX_THM4_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM4_30K_PU)
#define PM8350_ADC7_AMUX_THM5_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM5_30K_PU)
#define PM8350_ADC7_GPIO1_30K_PU(sid) ((sid) << 8 | ADC7_GPIO1_30K_PU)
#define PM8350_ADC7_GPIO2_30K_PU(sid) ((sid) << 8 | ADC7_GPIO2_30K_PU)
#define PM8350_ADC7_GPIO3_30K_PU(sid) ((sid) << 8 | ADC7_GPIO3_30K_PU)
#define PM8350_ADC7_GPIO4_30K_PU(sid) ((sid) << 8 | ADC7_GPIO4_30K_PU)
/* 100k pull-up2 */
#define PM8350_ADC7_AMUX_THM1_100K_PU(sid) ((sid) << 8 | 0x44)
#define PM8350_ADC7_AMUX_THM2_100K_PU(sid) ((sid) << 8 | 0x45)
#define PM8350_ADC7_AMUX_THM3_100K_PU(sid) ((sid) << 8 | 0x46)
#define PM8350_ADC7_AMUX_THM4_100K_PU(sid) ((sid) << 8 | 0x47)
#define PM8350_ADC7_AMUX_THM5_100K_PU(sid) ((sid) << 8 | 0x48)
#define PM8350_ADC7_GPIO1_100K_PU(sid) ((sid) << 8 | 0x4a)
#define PM8350_ADC7_GPIO2_100K_PU(sid) ((sid) << 8 | 0x4b)
#define PM8350_ADC7_GPIO3_100K_PU(sid) ((sid) << 8 | 0x4c)
#define PM8350_ADC7_GPIO4_100K_PU(sid) ((sid) << 8 | 0x4d)
#define PM8350_ADC7_AMUX_THM1_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM1_100K_PU)
#define PM8350_ADC7_AMUX_THM2_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM2_100K_PU)
#define PM8350_ADC7_AMUX_THM3_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM3_100K_PU)
#define PM8350_ADC7_AMUX_THM4_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM4_100K_PU)
#define PM8350_ADC7_AMUX_THM5_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM5_100K_PU)
#define PM8350_ADC7_GPIO1_100K_PU(sid) ((sid) << 8 | ADC7_GPIO1_100K_PU)
#define PM8350_ADC7_GPIO2_100K_PU(sid) ((sid) << 8 | ADC7_GPIO2_100K_PU)
#define PM8350_ADC7_GPIO3_100K_PU(sid) ((sid) << 8 | ADC7_GPIO3_100K_PU)
#define PM8350_ADC7_GPIO4_100K_PU(sid) ((sid) << 8 | ADC7_GPIO4_100K_PU)
/* 400k pull-up3 */
#define PM8350_ADC7_AMUX_THM1_400K_PU(sid) ((sid) << 8 | 0x64)
#define PM8350_ADC7_AMUX_THM2_400K_PU(sid) ((sid) << 8 | 0x65)
#define PM8350_ADC7_AMUX_THM3_400K_PU(sid) ((sid) << 8 | 0x66)
#define PM8350_ADC7_AMUX_THM4_400K_PU(sid) ((sid) << 8 | 0x67)
#define PM8350_ADC7_AMUX_THM5_400K_PU(sid) ((sid) << 8 | 0x68)
#define PM8350_ADC7_GPIO1_400K_PU(sid) ((sid) << 8 | 0x6a)
#define PM8350_ADC7_GPIO2_400K_PU(sid) ((sid) << 8 | 0x6b)
#define PM8350_ADC7_GPIO3_400K_PU(sid) ((sid) << 8 | 0x6c)
#define PM8350_ADC7_GPIO4_400K_PU(sid) ((sid) << 8 | 0x6d)
#define PM8350_ADC7_AMUX_THM1_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM1_400K_PU)
#define PM8350_ADC7_AMUX_THM2_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM2_400K_PU)
#define PM8350_ADC7_AMUX_THM3_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM3_400K_PU)
#define PM8350_ADC7_AMUX_THM4_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM4_400K_PU)
#define PM8350_ADC7_AMUX_THM5_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM5_400K_PU)
#define PM8350_ADC7_GPIO1_400K_PU(sid) ((sid) << 8 | ADC7_GPIO1_400K_PU)
#define PM8350_ADC7_GPIO2_400K_PU(sid) ((sid) << 8 | ADC7_GPIO2_400K_PU)
#define PM8350_ADC7_GPIO3_400K_PU(sid) ((sid) << 8 | ADC7_GPIO3_400K_PU)
#define PM8350_ADC7_GPIO4_400K_PU(sid) ((sid) << 8 | ADC7_GPIO4_400K_PU)
/* 1/3 Divider */
#define PM8350_ADC7_GPIO4_DIV3(sid) ((sid) << 8 | 0x8d)
#define PM8350_ADC7_GPIO4_DIV3(sid) ((sid) << 8 | ADC7_GPIO4_DIV3)
#define PM8350_ADC7_VPH_PWR(sid) ((sid) << 8 | 0x8e)
#define PM8350_ADC7_VPH_PWR(sid) ((sid) << 8 | ADC7_VPH_PWR)
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H */

View File

@ -10,79 +10,81 @@
#define PM8350B_SID 3
#endif
#include <dt-bindings/iio/qcom,spmi-vadc.h>
/* ADC channels for PM8350B_ADC for PMIC7 */
#define PM8350B_ADC7_REF_GND (PM8350B_SID << 8 | 0x0)
#define PM8350B_ADC7_1P25VREF (PM8350B_SID << 8 | 0x01)
#define PM8350B_ADC7_VREF_VADC (PM8350B_SID << 8 | 0x02)
#define PM8350B_ADC7_DIE_TEMP (PM8350B_SID << 8 | 0x03)
#define PM8350B_ADC7_REF_GND (PM8350B_SID << 8 | ADC7_REF_GND)
#define PM8350B_ADC7_1P25VREF (PM8350B_SID << 8 | ADC7_1P25VREF)
#define PM8350B_ADC7_VREF_VADC (PM8350B_SID << 8 | ADC7_VREF_VADC)
#define PM8350B_ADC7_DIE_TEMP (PM8350B_SID << 8 | ADC7_DIE_TEMP)
#define PM8350B_ADC7_AMUX_THM1 (PM8350B_SID << 8 | 0x04)
#define PM8350B_ADC7_AMUX_THM2 (PM8350B_SID << 8 | 0x05)
#define PM8350B_ADC7_AMUX_THM3 (PM8350B_SID << 8 | 0x06)
#define PM8350B_ADC7_AMUX_THM4 (PM8350B_SID << 8 | 0x07)
#define PM8350B_ADC7_AMUX_THM5 (PM8350B_SID << 8 | 0x08)
#define PM8350B_ADC7_AMUX_THM6 (PM8350B_SID << 8 | 0x09)
#define PM8350B_ADC7_GPIO1 (PM8350B_SID << 8 | 0x0a)
#define PM8350B_ADC7_GPIO2 (PM8350B_SID << 8 | 0x0b)
#define PM8350B_ADC7_GPIO3 (PM8350B_SID << 8 | 0x0c)
#define PM8350B_ADC7_GPIO4 (PM8350B_SID << 8 | 0x0d)
#define PM8350B_ADC7_AMUX_THM1 (PM8350B_SID << 8 | ADC7_AMUX_THM1)
#define PM8350B_ADC7_AMUX_THM2 (PM8350B_SID << 8 | ADC7_AMUX_THM2)
#define PM8350B_ADC7_AMUX_THM3 (PM8350B_SID << 8 | ADC7_AMUX_THM3)
#define PM8350B_ADC7_AMUX_THM4 (PM8350B_SID << 8 | ADC7_AMUX_THM4)
#define PM8350B_ADC7_AMUX_THM5 (PM8350B_SID << 8 | ADC7_AMUX_THM5)
#define PM8350B_ADC7_AMUX_THM6 (PM8350B_SID << 8 | ADC7_AMUX_THM6)
#define PM8350B_ADC7_GPIO1 (PM8350B_SID << 8 | ADC7_GPIO1)
#define PM8350B_ADC7_GPIO2 (PM8350B_SID << 8 | ADC7_GPIO2)
#define PM8350B_ADC7_GPIO3 (PM8350B_SID << 8 | ADC7_GPIO3)
#define PM8350B_ADC7_GPIO4 (PM8350B_SID << 8 | ADC7_GPIO4)
#define PM8350B_ADC7_CHG_TEMP (PM8350B_SID << 8 | 0x10)
#define PM8350B_ADC7_USB_IN_V_16 (PM8350B_SID << 8 | 0x11)
#define PM8350B_ADC7_VDC_16 (PM8350B_SID << 8 | 0x12)
#define PM8350B_ADC7_CC1_ID (PM8350B_SID << 8 | 0x13)
#define PM8350B_ADC7_VREF_BAT_THERM (PM8350B_SID << 8 | 0x15)
#define PM8350B_ADC7_IIN_FB (PM8350B_SID << 8 | 0x17)
#define PM8350B_ADC7_CHG_TEMP (PM8350B_SID << 8 | ADC7_CHG_TEMP)
#define PM8350B_ADC7_USB_IN_V_16 (PM8350B_SID << 8 | ADC7_USB_IN_V_16)
#define PM8350B_ADC7_VDC_16 (PM8350B_SID << 8 | ADC7_VDC_16)
#define PM8350B_ADC7_CC1_ID (PM8350B_SID << 8 | ADC7_CC1_ID)
#define PM8350B_ADC7_VREF_BAT_THERM (PM8350B_SID << 8 | ADC7_VREF_BAT_THERM)
#define PM8350B_ADC7_IIN_FB (PM8350B_SID << 8 | ADC7_IIN_FB)
/* 30k pull-up1 */
#define PM8350B_ADC7_AMUX_THM1_30K_PU (PM8350B_SID << 8 | 0x24)
#define PM8350B_ADC7_AMUX_THM2_30K_PU (PM8350B_SID << 8 | 0x25)
#define PM8350B_ADC7_AMUX_THM3_30K_PU (PM8350B_SID << 8 | 0x26)
#define PM8350B_ADC7_AMUX_THM4_30K_PU (PM8350B_SID << 8 | 0x27)
#define PM8350B_ADC7_AMUX_THM5_30K_PU (PM8350B_SID << 8 | 0x28)
#define PM8350B_ADC7_AMUX_THM6_30K_PU (PM8350B_SID << 8 | 0x29)
#define PM8350B_ADC7_GPIO1_30K_PU (PM8350B_SID << 8 | 0x2a)
#define PM8350B_ADC7_GPIO2_30K_PU (PM8350B_SID << 8 | 0x2b)
#define PM8350B_ADC7_GPIO3_30K_PU (PM8350B_SID << 8 | 0x2c)
#define PM8350B_ADC7_GPIO4_30K_PU (PM8350B_SID << 8 | 0x2d)
#define PM8350B_ADC7_CC1_ID_30K_PU (PM8350B_SID << 8 | 0x33)
#define PM8350B_ADC7_AMUX_THM1_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM1_30K_PU)
#define PM8350B_ADC7_AMUX_THM2_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM2_30K_PU)
#define PM8350B_ADC7_AMUX_THM3_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM3_30K_PU)
#define PM8350B_ADC7_AMUX_THM4_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM4_30K_PU)
#define PM8350B_ADC7_AMUX_THM5_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM5_30K_PU)
#define PM8350B_ADC7_AMUX_THM6_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM6_30K_PU)
#define PM8350B_ADC7_GPIO1_30K_PU (PM8350B_SID << 8 | ADC7_GPIO1_30K_PU)
#define PM8350B_ADC7_GPIO2_30K_PU (PM8350B_SID << 8 | ADC7_GPIO2_30K_PU)
#define PM8350B_ADC7_GPIO3_30K_PU (PM8350B_SID << 8 | ADC7_GPIO3_30K_PU)
#define PM8350B_ADC7_GPIO4_30K_PU (PM8350B_SID << 8 | ADC7_GPIO4_30K_PU)
#define PM8350B_ADC7_CC1_ID_30K_PU (PM8350B_SID << 8 | ADC7_CC1_ID_30K_PU)
/* 100k pull-up2 */
#define PM8350B_ADC7_AMUX_THM1_100K_PU (PM8350B_SID << 8 | 0x44)
#define PM8350B_ADC7_AMUX_THM2_100K_PU (PM8350B_SID << 8 | 0x45)
#define PM8350B_ADC7_AMUX_THM3_100K_PU (PM8350B_SID << 8 | 0x46)
#define PM8350B_ADC7_AMUX_THM4_100K_PU (PM8350B_SID << 8 | 0x47)
#define PM8350B_ADC7_AMUX_THM5_100K_PU (PM8350B_SID << 8 | 0x48)
#define PM8350B_ADC7_AMUX_THM6_100K_PU (PM8350B_SID << 8 | 0x49)
#define PM8350B_ADC7_GPIO1_100K_PU (PM8350B_SID << 8 | 0x4a)
#define PM8350B_ADC7_GPIO2_100K_PU (PM8350B_SID << 8 | 0x4b)
#define PM8350B_ADC7_GPIO3_100K_PU (PM8350B_SID << 8 | 0x4c)
#define PM8350B_ADC7_GPIO4_100K_PU (PM8350B_SID << 8 | 0x4d)
#define PM8350B_ADC7_CC1_ID_100K_PU (PM8350B_SID << 8 | 0x53)
#define PM8350B_ADC7_AMUX_THM1_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM1_100K_PU)
#define PM8350B_ADC7_AMUX_THM2_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM2_100K_PU)
#define PM8350B_ADC7_AMUX_THM3_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM3_100K_PU)
#define PM8350B_ADC7_AMUX_THM4_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM4_100K_PU)
#define PM8350B_ADC7_AMUX_THM5_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM5_100K_PU)
#define PM8350B_ADC7_AMUX_THM6_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM6_100K_PU)
#define PM8350B_ADC7_GPIO1_100K_PU (PM8350B_SID << 8 | ADC7_GPIO1_100K_PU)
#define PM8350B_ADC7_GPIO2_100K_PU (PM8350B_SID << 8 | ADC7_GPIO2_100K_PU)
#define PM8350B_ADC7_GPIO3_100K_PU (PM8350B_SID << 8 | ADC7_GPIO3_100K_PU)
#define PM8350B_ADC7_GPIO4_100K_PU (PM8350B_SID << 8 | ADC7_GPIO4_100K_PU)
#define PM8350B_ADC7_CC1_ID_100K_PU (PM8350B_SID << 8 | ADC7_CC1_ID_100K_PU)
/* 400k pull-up3 */
#define PM8350B_ADC7_AMUX_THM1_400K_PU (PM8350B_SID << 8 | 0x64)
#define PM8350B_ADC7_AMUX_THM2_400K_PU (PM8350B_SID << 8 | 0x65)
#define PM8350B_ADC7_AMUX_THM3_400K_PU (PM8350B_SID << 8 | 0x66)
#define PM8350B_ADC7_AMUX_THM4_400K_PU (PM8350B_SID << 8 | 0x67)
#define PM8350B_ADC7_AMUX_THM5_400K_PU (PM8350B_SID << 8 | 0x68)
#define PM8350B_ADC7_AMUX_THM6_400K_PU (PM8350B_SID << 8 | 0x69)
#define PM8350B_ADC7_GPIO1_400K_PU (PM8350B_SID << 8 | 0x6a)
#define PM8350B_ADC7_GPIO2_400K_PU (PM8350B_SID << 8 | 0x6b)
#define PM8350B_ADC7_GPIO3_400K_PU (PM8350B_SID << 8 | 0x6c)
#define PM8350B_ADC7_GPIO4_400K_PU (PM8350B_SID << 8 | 0x6d)
#define PM8350B_ADC7_CC1_ID_400K_PU (PM8350B_SID << 8 | 0x73)
#define PM8350B_ADC7_AMUX_THM1_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM1_400K_PU)
#define PM8350B_ADC7_AMUX_THM2_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM2_400K_PU)
#define PM8350B_ADC7_AMUX_THM3_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM3_400K_PU)
#define PM8350B_ADC7_AMUX_THM4_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM4_400K_PU)
#define PM8350B_ADC7_AMUX_THM5_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM5_400K_PU)
#define PM8350B_ADC7_AMUX_THM6_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM6_400K_PU)
#define PM8350B_ADC7_GPIO1_400K_PU (PM8350B_SID << 8 | ADC7_GPIO1_400K_PU)
#define PM8350B_ADC7_GPIO2_400K_PU (PM8350B_SID << 8 | ADC7_GPIO2_400K_PU)
#define PM8350B_ADC7_GPIO3_400K_PU (PM8350B_SID << 8 | ADC7_GPIO3_400K_PU)
#define PM8350B_ADC7_GPIO4_400K_PU (PM8350B_SID << 8 | ADC7_GPIO4_400K_PU)
#define PM8350B_ADC7_CC1_ID_400K_PU (PM8350B_SID << 8 | ADC7_CC1_ID_400K_PU)
/* 1/3 Divider */
#define PM8350B_ADC7_GPIO1_DIV3 (PM8350B_SID << 8 | 0x8a)
#define PM8350B_ADC7_GPIO2_DIV3 (PM8350B_SID << 8 | 0x8b)
#define PM8350B_ADC7_GPIO3_DIV3 (PM8350B_SID << 8 | 0x8c)
#define PM8350B_ADC7_GPIO4_DIV3 (PM8350B_SID << 8 | 0x8d)
#define PM8350B_ADC7_GPIO1_DIV3 (PM8350B_SID << 8 | ADC7_GPIO1_DIV3)
#define PM8350B_ADC7_GPIO2_DIV3 (PM8350B_SID << 8 | ADC7_GPIO2_DIV3)
#define PM8350B_ADC7_GPIO3_DIV3 (PM8350B_SID << 8 | ADC7_GPIO3_DIV3)
#define PM8350B_ADC7_GPIO4_DIV3 (PM8350B_SID << 8 | ADC7_GPIO4_DIV3)
#define PM8350B_ADC7_VPH_PWR (PM8350B_SID << 8 | 0x8e)
#define PM8350B_ADC7_VBAT_SNS (PM8350B_SID << 8 | 0x8f)
#define PM8350B_ADC7_VPH_PWR (PM8350B_SID << 8 | ADC7_VPH_PWR)
#define PM8350B_ADC7_VBAT_SNS (PM8350B_SID << 8 | ADC7_VBAT_SNS)
#define PM8350B_ADC7_SBUx (PM8350B_SID << 8 | 0x94)
#define PM8350B_ADC7_VBAT_2S_MID (PM8350B_SID << 8 | 0x96)
#define PM8350B_ADC7_SBUx (PM8350B_SID << 8 | ADC7_SBU)
#define PM8350B_ADC7_VBAT_2S_MID (PM8350B_SID << 8 | ADC7_VBAT_2S_MID)
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350B_H */

View File

@ -10,37 +10,39 @@
#define PMK8350_SID 0
#endif
/* ADC channels for PMK8350_ADC for PMIC7 */
#define PMK8350_ADC7_REF_GND (PMK8350_SID << 8 | 0x0)
#define PMK8350_ADC7_1P25VREF (PMK8350_SID << 8 | 0x01)
#define PMK8350_ADC7_VREF_VADC (PMK8350_SID << 8 | 0x02)
#define PMK8350_ADC7_DIE_TEMP (PMK8350_SID << 8 | 0x03)
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#define PMK8350_ADC7_AMUX_THM1 (PMK8350_SID << 8 | 0x04)
#define PMK8350_ADC7_AMUX_THM2 (PMK8350_SID << 8 | 0x05)
#define PMK8350_ADC7_AMUX_THM3 (PMK8350_SID << 8 | 0x06)
#define PMK8350_ADC7_AMUX_THM4 (PMK8350_SID << 8 | 0x07)
#define PMK8350_ADC7_AMUX_THM5 (PMK8350_SID << 8 | 0x08)
/* ADC channels for PMK8350_ADC for PMIC7 */
#define PMK8350_ADC7_REF_GND (PMK8350_SID << 8 | ADC7_REF_GND)
#define PMK8350_ADC7_1P25VREF (PMK8350_SID << 8 | ADC7_1P25VREF)
#define PMK8350_ADC7_VREF_VADC (PMK8350_SID << 8 | ADC7_VREF_VADC)
#define PMK8350_ADC7_DIE_TEMP (PMK8350_SID << 8 | ADC7_DIE_TEMP)
#define PMK8350_ADC7_AMUX_THM1 (PMK8350_SID << 8 | ADC7_AMUX_THM1)
#define PMK8350_ADC7_AMUX_THM2 (PMK8350_SID << 8 | ADC7_AMUX_THM2)
#define PMK8350_ADC7_AMUX_THM3 (PMK8350_SID << 8 | ADC7_AMUX_THM3)
#define PMK8350_ADC7_AMUX_THM4 (PMK8350_SID << 8 | ADC7_AMUX_THM4)
#define PMK8350_ADC7_AMUX_THM5 (PMK8350_SID << 8 | ADC7_AMUX_THM5)
/* 30k pull-up1 */
#define PMK8350_ADC7_AMUX_THM1_30K_PU (PMK8350_SID << 8 | 0x24)
#define PMK8350_ADC7_AMUX_THM2_30K_PU (PMK8350_SID << 8 | 0x25)
#define PMK8350_ADC7_AMUX_THM3_30K_PU (PMK8350_SID << 8 | 0x26)
#define PMK8350_ADC7_AMUX_THM4_30K_PU (PMK8350_SID << 8 | 0x27)
#define PMK8350_ADC7_AMUX_THM5_30K_PU (PMK8350_SID << 8 | 0x28)
#define PMK8350_ADC7_AMUX_THM1_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM1_30K_PU)
#define PMK8350_ADC7_AMUX_THM2_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM2_30K_PU)
#define PMK8350_ADC7_AMUX_THM3_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM3_30K_PU)
#define PMK8350_ADC7_AMUX_THM4_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM4_30K_PU)
#define PMK8350_ADC7_AMUX_THM5_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM5_30K_PU)
/* 100k pull-up2 */
#define PMK8350_ADC7_AMUX_THM1_100K_PU (PMK8350_SID << 8 | 0x44)
#define PMK8350_ADC7_AMUX_THM2_100K_PU (PMK8350_SID << 8 | 0x45)
#define PMK8350_ADC7_AMUX_THM3_100K_PU (PMK8350_SID << 8 | 0x46)
#define PMK8350_ADC7_AMUX_THM4_100K_PU (PMK8350_SID << 8 | 0x47)
#define PMK8350_ADC7_AMUX_THM5_100K_PU (PMK8350_SID << 8 | 0x48)
#define PMK8350_ADC7_AMUX_THM1_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM1_100K_PU)
#define PMK8350_ADC7_AMUX_THM2_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM2_100K_PU)
#define PMK8350_ADC7_AMUX_THM3_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM3_100K_PU)
#define PMK8350_ADC7_AMUX_THM4_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM4_100K_PU)
#define PMK8350_ADC7_AMUX_THM5_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM5_100K_PU)
/* 400k pull-up3 */
#define PMK8350_ADC7_AMUX_THM1_400K_PU (PMK8350_SID << 8 | 0x64)
#define PMK8350_ADC7_AMUX_THM2_400K_PU (PMK8350_SID << 8 | 0x65)
#define PMK8350_ADC7_AMUX_THM3_400K_PU (PMK8350_SID << 8 | 0x66)
#define PMK8350_ADC7_AMUX_THM4_400K_PU (PMK8350_SID << 8 | 0x67)
#define PMK8350_ADC7_AMUX_THM5_400K_PU (PMK8350_SID << 8 | 0x68)
#define PMK8350_ADC7_AMUX_THM1_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM1_400K_PU)
#define PMK8350_ADC7_AMUX_THM2_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM2_400K_PU)
#define PMK8350_ADC7_AMUX_THM3_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM3_400K_PU)
#define PMK8350_ADC7_AMUX_THM4_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM4_400K_PU)
#define PMK8350_ADC7_AMUX_THM5_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM5_400K_PU)
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H */

View File

@ -10,19 +10,21 @@
#define PMR735A_SID 4
#endif
/* ADC channels for PMR735A_ADC for PMIC7 */
#define PMR735A_ADC7_REF_GND (PMR735A_SID << 8 | 0x0)
#define PMR735A_ADC7_1P25VREF (PMR735A_SID << 8 | 0x01)
#define PMR735A_ADC7_VREF_VADC (PMR735A_SID << 8 | 0x02)
#define PMR735A_ADC7_DIE_TEMP (PMR735A_SID << 8 | 0x03)
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#define PMR735A_ADC7_GPIO1 (PMR735A_SID << 8 | 0x0a)
#define PMR735A_ADC7_GPIO2 (PMR735A_SID << 8 | 0x0b)
#define PMR735A_ADC7_GPIO3 (PMR735A_SID << 8 | 0x0c)
/* ADC channels for PMR735A_ADC for PMIC7 */
#define PMR735A_ADC7_REF_GND (PMR735A_SID << 8 | ADC7_REF_GND)
#define PMR735A_ADC7_1P25VREF (PMR735A_SID << 8 | ADC7_1P25VREF)
#define PMR735A_ADC7_VREF_VADC (PMR735A_SID << 8 | ADC7_VREF_VADC)
#define PMR735A_ADC7_DIE_TEMP (PMR735A_SID << 8 | ADC7_DIE_TEMP)
#define PMR735A_ADC7_GPIO1 (PMR735A_SID << 8 | ADC7_GPIO1)
#define PMR735A_ADC7_GPIO2 (PMR735A_SID << 8 | ADC7_GPIO2)
#define PMR735A_ADC7_GPIO3 (PMR735A_SID << 8 | ADC7_GPIO3)
/* 100k pull-up2 */
#define PMR735A_ADC7_GPIO1_100K_PU (PMR735A_SID << 8 | 0x4a)
#define PMR735A_ADC7_GPIO2_100K_PU (PMR735A_SID << 8 | 0x4b)
#define PMR735A_ADC7_GPIO3_100K_PU (PMR735A_SID << 8 | 0x4c)
#define PMR735A_ADC7_GPIO1_100K_PU (PMR735A_SID << 8 | ADC7_GPIO1_100K_PU)
#define PMR735A_ADC7_GPIO2_100K_PU (PMR735A_SID << 8 | ADC7_GPIO2_100K_PU)
#define PMR735A_ADC7_GPIO3_100K_PU (PMR735A_SID << 8 | ADC7_GPIO3_100K_PU)
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735A_H */

View File

@ -10,19 +10,21 @@
#define PMR735B_SID 5
#endif
/* ADC channels for PMR735B_ADC for PMIC7 */
#define PMR735B_ADC7_REF_GND (PMR735B_SID << 8 | 0x0)
#define PMR735B_ADC7_1P25VREF (PMR735B_SID << 8 | 0x01)
#define PMR735B_ADC7_VREF_VADC (PMR735B_SID << 8 | 0x02)
#define PMR735B_ADC7_DIE_TEMP (PMR735B_SID << 8 | 0x03)
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#define PMR735B_ADC7_GPIO1 (PMR735B_SID << 8 | 0x0a)
#define PMR735B_ADC7_GPIO2 (PMR735B_SID << 8 | 0x0b)
#define PMR735B_ADC7_GPIO3 (PMR735B_SID << 8 | 0x0c)
/* ADC channels for PMR735B_ADC for PMIC7 */
#define PMR735B_ADC7_REF_GND (PMR735B_SID << 8 | ADC7_REF_GND)
#define PMR735B_ADC7_1P25VREF (PMR735B_SID << 8 | ADC7_1P25VREF)
#define PMR735B_ADC7_VREF_VADC (PMR735B_SID << 8 | ADC7_VREF_VADC)
#define PMR735B_ADC7_DIE_TEMP (PMR735B_SID << 8 | ADC7_DIE_TEMP)
#define PMR735B_ADC7_GPIO1 (PMR735B_SID << 8 | ADC7_GPIO1)
#define PMR735B_ADC7_GPIO2 (PMR735B_SID << 8 | ADC7_GPIO2)
#define PMR735B_ADC7_GPIO3 (PMR735B_SID << 8 | ADC7_GPIO3)
/* 100k pull-up2 */
#define PMR735B_ADC7_GPIO1_100K_PU (PMR735B_SID << 8 | 0x4a)
#define PMR735B_ADC7_GPIO2_100K_PU (PMR735B_SID << 8 | 0x4b)
#define PMR735B_ADC7_GPIO3_100K_PU (PMR735B_SID << 8 | 0x4c)
#define PMR735B_ADC7_GPIO1_100K_PU (PMR735B_SID << 8 | ADC7_GPIO1_100K_PU)
#define PMR735B_ADC7_GPIO2_100K_PU (PMR735B_SID << 8 | ADC7_GPIO2_100K_PU)
#define PMR735B_ADC7_GPIO3_100K_PU (PMR735B_SID << 8 | ADC7_GPIO3_100K_PU)
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735B_H */

View File

@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2020 Invensense, Inc.
*/
#ifndef INV_SENSORS_TIMESTAMP_H_
#define INV_SENSORS_TIMESTAMP_H_
/**
* struct inv_sensors_timestamp_chip - chip internal properties
* @clock_period: internal clock period in ns
* @jitter: acceptable jitter in per-mille
* @init_period: chip initial period at reset in ns
*/
struct inv_sensors_timestamp_chip {
uint32_t clock_period;
uint32_t jitter;
uint32_t init_period;
};
/**
* struct inv_sensors_timestamp_interval - timestamps interval
* @lo: interval lower bound
* @up: interval upper bound
*/
struct inv_sensors_timestamp_interval {
int64_t lo;
int64_t up;
};
/**
* struct inv_sensors_timestamp_acc - accumulator for computing an estimation
* @val: current estimation of the value, the mean of all values
* @idx: current index of the next free place in values table
* @values: table of all measured values, use for computing the mean
*/
struct inv_sensors_timestamp_acc {
uint32_t val;
size_t idx;
uint32_t values[32];
};
/**
* struct inv_sensors_timestamp - timestamp management states
* @chip: chip internal characteristics
* @min_period: minimal acceptable clock period
* @max_period: maximal acceptable clock period
* @it: interrupts interval timestamps
* @timestamp: store last timestamp for computing next data timestamp
* @mult: current internal period multiplier
* @new_mult: new set internal period multiplier (not yet effective)
* @period: measured current period of the sensor
* @chip_period: accumulator for computing internal chip period
*/
struct inv_sensors_timestamp {
struct inv_sensors_timestamp_chip chip;
uint32_t min_period;
uint32_t max_period;
struct inv_sensors_timestamp_interval it;
int64_t timestamp;
uint32_t mult;
uint32_t new_mult;
uint32_t period;
struct inv_sensors_timestamp_acc chip_period;
};
void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
const struct inv_sensors_timestamp_chip *chip);
int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
uint32_t period, bool fifo);
void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
uint32_t fifo_period, size_t fifo_nb,
size_t sensor_nb, int64_t timestamp);
static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts)
{
ts->timestamp += ts->period;
return ts->timestamp;
}
void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
uint32_t fifo_period, size_t fifo_nb,
unsigned int fifo_no);
static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts)
{
const struct inv_sensors_timestamp_interval interval_init = {0LL, 0LL};
ts->it = interval_init;
ts->timestamp = 0;
}
#endif

View File

@ -19,6 +19,8 @@ enum iio_event_info {
IIO_EV_INFO_TIMEOUT,
IIO_EV_INFO_RESET_TIMEOUT,
IIO_EV_INFO_TAP2_MIN_DELAY,
IIO_EV_INFO_RUNNING_PERIOD,
IIO_EV_INFO_RUNNING_COUNT,
};
#define IIO_VAL_INT 1

View File

@ -51,9 +51,9 @@ enum autochan {
* Has the side effect of filling the channels[i].location values used
* in processing the buffer output.
**/
static int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
static unsigned int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
{
int bytes = 0;
unsigned int bytes = 0;
int i = 0;
while (i < num_channels) {
@ -348,7 +348,7 @@ int main(int argc, char **argv)
ssize_t read_size;
int dev_num = -1, trig_num = -1;
char *buffer_access = NULL;
int scan_size;
unsigned int scan_size;
int noevents = 0;
int notrigger = 0;
char *dummy;
@ -674,7 +674,16 @@ int main(int argc, char **argv)
}
scan_size = size_from_channelarray(channels, num_channels);
data = malloc(scan_size * buf_len);
size_t total_buf_len = scan_size * buf_len;
if (scan_size > 0 && total_buf_len / scan_size != buf_len) {
ret = -EFAULT;
perror("Integer overflow happened when calculate scan_size * buf_len");
goto error;
}
data = malloc(total_buf_len);
if (!data) {
ret = -ENOMEM;
goto error;