Commit e0014ce7 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'iio-for-6.9b' of...

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

Jonathan writes:

IIO: 2nd set of new device support, cleanups and features for 6.9

New device support
=================
adi,hmc425a
- Add support for LTC6373 Instrumentation Amplifier.
microchip,pac1934
- New driver supporting PAC1931, PAC1932, PAC1933 and PAC1934 power monitoring
chips with accumulators.
voltafield,af8133j
- New driver for the AF8133J 3 axis magnetometer.

Docs
====

New general documentation of device buffers, and a specific section on
the adi,adis16475 IMU

Features
========

kionix,kxcjk-1013
 - Add support for ACPI ROTM (Microsoft defined ACPI method) to get rotation
   matrix.
ti,tmp117
- Add missing vcc-supply control and binding.

Cleanups and minor fixes
========================

Tree-wide
- Corrected headers to remove linux/of.h from a bunch of drivers
  that only had it to get to linux/mod_devicetable.h
- dt binding cleanup to drop redundant type from label properties.

adi,hmc425a
- Fix constraints on GPIO array sizes for different devices.
adi,ltc2983
- Use spi_get_device_match_data instead of open coding similar.
- Update naming of fw parsing function to reflect that it is not longer
  dt only.
- Set the chip name explicitly to reduce fragility resulting from different
  entries in the various ID tables.
bosch,bmg160
- Add spi-max-frequency property and limit to dt-binding.
microchip,mcp320x
- Use devm_* to simplify device removal and error handling.
nxp,imx93
- Drop a non existent 4th interrupt from bindings.
qcom,mp8xxx-xoadc
- Drop unused kerneldoc
renesas,isl29501
- Actually use the of_match table.
rockchip,saradc
- Fix channel bitmask
- Fix write masks
- Replace custom handling of optional reset control with how it should be
  done.
ti,ads1298
- Fix error code to not return a successfully obtained regulator.
- Avoid a divide by zero when setting frequency.
ti,hdc2010
- Add missing interrupts dt binding property
vishay,veml6075
- Make vdd-supply required in the dt-binding.

* tag 'iio-for-6.9b' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (42 commits)
  dt-bindings: iio: gyroscope: bosch,bmg160: add spi-max-frequency
  dt-bindings: iio: adc: imx93: drop the 4th interrupt
  iio: proximity: isl29501: make use of of_device_id table
  iio: adc: qcom-pm8xxx-xoadc: drop unused kerneldoc struct pm8xxx_chan_info member
  dt-bindings: iio: adc: drop redundant type from label
  dt-bindings: iio: ti,tmp117: add optional label property
  MAINTAINERS: Add an entry for AF8133J driver
  iio: magnetometer: add a driver for Voltafield AF8133J magnetometer
  dt-bindings: iio: magnetometer: Add Voltafield AF8133J
  dt-bindings: vendor-prefix: Add prefix for Voltafield
  iio: adc: rockchip_saradc: replace custom logic with devm_reset_control_get_optional_exclusive
  iio: adc: rockchip_saradc: use mask for write_enable bitfield
  iio: adc: rockchip_saradc: fix bitmask for channels on SARADCv2
  dt-bindings: iio: light: vishay,veml6075: make vdd-supply required
  iio: adc: adding support for PAC193x
  dt-bindings: iio: adc: adding support for PAC193X
  iio: temperature: ltc2983: explicitly set the name in chip_info
  iio: temperature: ltc2983: rename ltc2983_parse_dt()
  iio: temperature: ltc2983: make use of spi_get_device_match_data()
  iio: adc: ti-ads1298: prevent divide by zero in ads1298_set_samp_freq()
  ...
parents a6a3bf9b 6b61aae3
What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistorY
KernelVersion: 6.7
Contact: linux-iio@vger.kernel.org
Description:
The value of the shunt resistor may be known only at runtime
and set by a client application. This attribute allows to
set its value in micro-ohms. X is the IIO index of the device.
Y is the channel number. The value is used to calculate
current, power and accumulated energy.
......@@ -22,7 +22,6 @@ properties:
maxItems: 1
label:
$ref: /schemas/types.yaml#/definitions/string
description: Unique name to identify which channel this is.
bipolar:
......
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/microchip,pac1934.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip PAC1934 Power Monitors with Accumulator
maintainers:
- Marius Cristea <marius.cristea@microchip.com>
description: |
This device is part of the Microchip family of Power Monitors with
Accumulator.
The datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here:
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf
properties:
compatible:
enum:
- microchip,pac1931
- microchip,pac1932
- microchip,pac1933
- microchip,pac1934
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
interrupts:
maxItems: 1
slow-io-gpios:
description:
A GPIO used to trigger a change is sampling rate (lowering the chip power
consumption). If configured in SLOW mode, if this pin is forced high,
sampling rate is forced to eight samples/second. When it is forced low,
the sampling rate is 1024 samples/second unless a different sample rate
has been programmed.
patternProperties:
"^channel@[1-4]+$":
type: object
$ref: adc.yaml
description:
Represents the external channels which are connected to the ADC.
properties:
reg:
items:
minimum: 1
maximum: 4
shunt-resistor-micro-ohms:
description:
Value in micro Ohms of the shunt resistor connected between
the SENSE+ and SENSE- inputs, across which the current is measured.
Value is needed to compute the scaling of the measured current.
required:
- reg
- shunt-resistor-micro-ohms
unevaluatedProperties: false
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
power-monitor@10 {
compatible = "microchip,pac1934";
reg = <0x10>;
#address-cells = <1>;
#size-cells = <0>;
channel@1 {
reg = <0x1>;
shunt-resistor-micro-ohms = <24900000>;
label = "CPU";
};
channel@2 {
reg = <0x2>;
shunt-resistor-micro-ohms = <49900000>;
label = "GPU";
};
channel@3 {
reg = <0x3>;
shunt-resistor-micro-ohms = <75000000>;
label = "MEM";
bipolar;
};
channel@4 {
reg = <0x4>;
shunt-resistor-micro-ohms = <100000000>;
label = "NET";
bipolar;
};
};
};
...
......@@ -31,7 +31,6 @@ properties:
- description: normal conversion, include EOC (End of Conversion),
ECH (End of Chain), JEOC (End of Injected Conversion) and
JECH (End of injected Chain).
- description: Self-testing Interrupts.
clocks:
maxItems: 1
......@@ -70,8 +69,7 @@ examples:
reg = <0x44530000 0x10000>;
interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
<GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX93_CLK_ADC1_GATE>;
clock-names = "ipg";
vref-supply = <&reg_vref_1v8>;
......
......@@ -75,7 +75,6 @@ patternProperties:
in the PMIC-specific files in include/dt-bindings/iio/.
label:
$ref: /schemas/types.yaml#/definitions/string
description: |
ADC input of the platform as seen in the schematics.
For thermistor inputs connected to generic AMUX or GPIO inputs
......
......@@ -21,6 +21,8 @@ description: |
HMC540S 1 dB LSB Silicon MMIC 4-Bit Digital Positive Control Attenuator, 0.1 - 8 GHz
https://www.analog.com/media/en/technical-documentation/data-sheets/hmc540s.pdf
LTC6373 is a 3-Bit precision instrumentation amplifier with fully differential outputs
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc6373.pdf
properties:
compatible:
......@@ -28,16 +30,55 @@ properties:
- adi,adrf5740
- adi,hmc425a
- adi,hmc540s
- adi,ltc6373
vcc-supply: true
ctrl-gpios:
description:
Must contain an array of 6 GPIO specifiers, referring to the GPIO pins
connected to the control pins V1-V6.
minItems: 6
Must contain an array of GPIO specifiers, referring to the GPIO pins
connected to the control pins.
ADRF5740 - 4 GPIO connected to D2-D5
HMC540S - 4 GPIO connected to V1-V4
HMC425A - 6 GPIO connected to V1-V6
LTC6373 - 3 GPIO connected to A0-A2
minItems: 1
maxItems: 6
allOf:
- if:
properties:
compatible:
contains:
const: adi,hmc425a
then:
properties:
ctrl-gpios:
minItems: 6
maxItems: 6
- if:
properties:
compatible:
contains:
anyOf:
- const: adi,adrf5740
- const: adi,hmc540s
then:
properties:
ctrl-gpios:
minItems: 4
maxItems: 4
- if:
properties:
compatible:
contains:
const: adi,ltc6373
then:
properties:
ctrl-gpios:
minItems: 3
maxItems: 3
required:
- compatible
- ctrl-gpios
......
......@@ -22,6 +22,9 @@ properties:
vdd-supply: true
vddio-supply: true
spi-max-frequency:
maximum: 10000000
interrupts:
minItems: 1
maxItems: 2
......@@ -33,7 +36,10 @@ required:
- compatible
- reg
additionalProperties: false
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
......
......@@ -27,6 +27,9 @@ properties:
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
......
......@@ -21,6 +21,7 @@ properties:
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
......
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/magnetometer/voltafield,af8133j.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Voltafield AF8133J magnetometer sensor
maintainers:
- Ondřej Jirman <megi@xff.cz>
properties:
compatible:
const: voltafield,af8133j
reg:
maxItems: 1
reset-gpios:
description:
A signal for active low reset input of the sensor. (optional; if not
used, software reset over I2C will be used instead)
avdd-supply:
description:
A regulator that provides AVDD power (Working power, usually 3.3V) to
the sensor.
dvdd-supply:
description:
A regulator that provides DVDD power (Digital IO power, 1.8V - AVDD)
to the sensor.
mount-matrix:
description: An optional 3x3 mounting rotation matrix.
required:
- compatible
- reg
- avdd-supply
- dvdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@1c {
compatible = "voltafield,af8133j";
reg = <0x1c>;
avdd-supply = <&reg_dldo1>;
dvdd-supply = <&reg_dldo1>;
reset-gpios = <&pio 1 1 GPIO_ACTIVE_LOW>;
};
};
......@@ -24,9 +24,16 @@ properties:
reg:
maxItems: 1
vcc-supply:
description: provide VCC power to the sensor.
label:
description: Unique name to identify which device this is.
required:
- compatible
- reg
- vcc-supply
additionalProperties: false
......@@ -39,5 +46,6 @@ examples:
tmp117@48 {
compatible = "ti,tmp117";
reg = <0x48>;
vcc-supply = <&pmic_reg_3v3>;
};
};
......@@ -1534,6 +1534,8 @@ patternProperties:
description: VoCore Studio
"^voipac,.*":
description: Voipac Technologies s.r.o.
"^voltafield,.*":
description: Voltafield Technology Corp.
"^vot,.*":
description: Vision Optical Technology Co., Ltd.
"^vxt,.*":
......
.. SPDX-License-Identifier: GPL-2.0
================
ADIS16475 driver
================
This driver supports Analog Device's IMUs on SPI bus.
1. Supported devices
====================
* `ADIS16465 <https://www.analog.com/ADIS16465>`_
* `ADIS16467 <https://www.analog.com/ADIS16467>`_
* `ADIS16470 <https://www.analog.com/ADIS16470>`_
* `ADIS16475 <https://www.analog.com/ADIS16475>`_
* `ADIS16477 <https://www.analog.com/ADIS16477>`_
* `ADIS16500 <https://www.analog.com/ADIS16500>`_
* `ADIS16505 <https://www.analog.com/ADIS16505>`_
* `ADIS16507 <https://www.analog.com/ADIS16507>`_
Each supported device is a precision, miniature microelectromechanical system
(MEMS) inertial measurement unit (IMU) that includes a triaxial gyroscope and a
triaxial accelerometer. Each inertial sensor in the IMU device combines with
signal conditioning that optimizes dynamic performance. The factory calibration
characterizes each sensor for sensitivity, bias, alignment, linear acceleration
(gyroscope bias), and point of percussion (accelerometer location). As a result,
each sensor has dynamic compensation formulas that provide accurate sensor
measurements over a broad set of conditions.
2. Device attributes
====================
Accelerometer, gyroscope measurements are always provided. Furthermore, the
driver offers the capability to retrieve the delta angle and the delta velocity
measurements computed by the device.
The delta angle measurements represent a calculation of angular displacement
between each sample update, while the delta velocity measurements represent a
calculation of linear velocity change between each sample update.
Finally, temperature data are provided which show a coarse measurement of
the temperature inside of the IMU device. This data is most useful for
monitoring relative changes in the thermal environment.
The signal chain of each inertial sensor (accelerometers and gyroscopes)
includes the application of unique correction formulas, which are derived from
extensive characterization of bias, sensitivity, alignment, response to linear
acceleration (gyroscopes), and point of percussion (accelerometer location)
over a temperature range of −40°C to +85°C, for each ADIS device. These
correction formulas are not accessible, but users do have the opportunity to
adjust the bias for each sensor individually through the calibbias attribute.
Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
where X is the IIO index of the device. Under these folders reside a set of
device files, depending on the characteristics and features of the hardware
device in questions. These files are consistently generalized and documented in
the IIO ABI documentation.
The following tables show the adis16475 related device files, found in the
specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+-------------------------------------------+----------------------------------------------------------+
| 3-Axis Accelerometer related device files | Description |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_scale | Scale for the accelerometer channels. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_calibbias_x | x-axis acceleration offset correction |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_calibbias_y | y-axis acceleration offset correction |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_z_raw | Raw Z-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_scale | Scale for delta velocity channels. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_x_raw | Raw X-axis delta velocity channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_y_raw | Raw Y-axis delta velocity channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_z_raw | Raw Z-axis delta velocity channel value. |
+-------------------------------------------+----------------------------------------------------------+
+---------------------------------------+------------------------------------------------------+
| 3-Axis Gyroscope related device files | Description |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_scale | Scale for the gyroscope channels. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_x_calibbias | Calibration offset for the X-axis gyroscope channel. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_calibbias_x | x-axis gyroscope offset correction |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_x_raw | Raw X-axis gyroscope channel value. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_calibbias_y | y-axis gyroscope offset correction |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_y_raw | Raw Y-axis gyroscope channel value. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_z_calibbias | Calibration offset for the Z-axis gyroscope channel. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_z_raw | Raw Z-axis gyroscope channel value. |
+---------------------------------------+------------------------------------------------------+
| in_deltaangl_scale | Scale for delta angle channels. |
+---------------------------------------+------------------------------------------------------+
| in_deltaangl_x_raw | Raw X-axis delta angle channel value. |
+---------------------------------------+------------------------------------------------------+
| in_deltaangl_y_raw | Raw Y-axis delta angle channel value. |
+---------------------------------------+------------------------------------------------------+
| in_deltaangl_z_raw | Raw Z-axis delta angle channel value. |
+---------------------------------------+------------------------------------------------------+
+----------------------------------+-------------------------------------------+
| Temperature sensor related files | Description |
+----------------------------------+-------------------------------------------+
| in_temp0_raw | Raw temperature channel value. |
+----------------------------------+-------------------------------------------+
| in_temp0_scale | Scale for the temperature sensor channel. |
+----------------------------------+-------------------------------------------+
+-------------------------------+---------------------------------------------------------+
| Miscellaneous device files | Description |
+-------------------------------+---------------------------------------------------------+
| name | Name of the IIO device. |
+-------------------------------+---------------------------------------------------------+
| sampling_frequency | Currently selected sample rate. |
+-------------------------------+---------------------------------------------------------+
| filter_low_pass_3db_frequency | Bandwidth for the accelerometer and gyroscope channels. |
+-------------------------------+---------------------------------------------------------+
The following table shows the adis16475 related device debug files, found in the
specific device debug folder path ``/sys/kernel/debug/iio/iio:deviceX``.
+----------------------+-------------------------------------------------------------------------+
| Debugfs device files | Description |
+----------------------+-------------------------------------------------------------------------+
| serial_number | The serial number of the chip in hexadecimal format. |
+----------------------+-------------------------------------------------------------------------+
| product_id | Chip specific product id (e.g. 16475, 16500, 16505, etc.). |
+----------------------+-------------------------------------------------------------------------+
| flash_count | The number of flash writes performed on the device. |
+----------------------+-------------------------------------------------------------------------+
| firmware_revision | String containing the firmware revision in the following format ##.##. |
+----------------------+-------------------------------------------------------------------------+
| firmware_date | String containing the firmware date in the following format mm-dd-yyyy. |
+----------------------+-------------------------------------------------------------------------+
Channels processed values
-------------------------
A channel value can be read from its _raw attribute. The value returned is the
raw value as reported by the devices. To get the processed value of the channel,
apply the following formula:
.. code-block:: bash
processed value = (_raw + _offset) * _scale
Where _offset and _scale are device attributes. If no _offset attribute is
present, simply assume its value is 0.
The adis16475 driver offers data for 5 types of channels, the table below shows
the measurement units for the processed value, which are defined by the IIO
framework:
+-------------------------------------+---------------------------+
| Channel type | Measurement unit |
+-------------------------------------+---------------------------+
| Acceleration on X, Y, and Z axis | Meters per Second squared |
+-------------------------------------+---------------------------+
| Angular velocity on X, Y and Z axis | Radians per second |
+-------------------------------------+---------------------------+
| Delta velocity on X. Y, and Z axis | Meters per Second |
+-------------------------------------+---------------------------+
| Delta angle on X, Y, and Z axis | Radians |
+-------------------------------------+---------------------------+
| Temperature | Millidegrees Celsius |
+-------------------------------------+---------------------------+
Usage examples
--------------
Show device name:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat name
adis16505-2
Show accelerometer channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
-275924
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
-30142222
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
261265769
root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
0.000000037
- X-axis acceleration = in_accel_x_raw * in_accel_scale = −0.010209188 m/s^2
- Y-axis acceleration = in_accel_y_raw * in_accel_scale = −1.115262214 m/s^2
- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 9.666833453 m/s^2
Show gyroscope channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_x_raw
-3324626
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_raw
1336980
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_z_raw
-602983
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_scale
0.000000006
- X-axis angular velocity = in_anglvel_x_raw * in_anglvel_scale = −0.019947756 rad/s
- Y-axis angular velocity = in_anglvel_y_raw * in_anglvel_scale = 0.00802188 rad/s
- Z-axis angular velocity = in_anglvel_z_raw * in_anglvel_scale = −0.003617898 rad/s
Set calibration offset for accelerometer channels:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
0
root:/sys/bus/iio/devices/iio:device0> echo 5000 > in_accel_x_calibbias
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
5000
Set calibration offset for gyroscope channels:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
0
root:/sys/bus/iio/devices/iio:device0> echo -5000 > in_anglvel_y_calibbias
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
-5000
Set sampling frequency:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency
2000.000000
root:/sys/bus/iio/devices/iio:device0> echo 1000 > sampling_frequency
1000.000000
Set bandwidth for accelerometer and gyroscope:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat filter_low_pass_3db_frequency
720
root:/sys/bus/iio/devices/iio:device0> echo 360 > filter_low_pass_3db_frequency
root:/sys/bus/iio/devices/iio:device0> cat filter_low_pass_3db_frequency
360
Show serial number:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat serial_number
0x04f9
Show product id:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat product_id
16505
Show flash count:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat flash_count
150
Show firmware revision:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat firmware_revision
1.6
Show firmware date:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat firmware_date
06-27-2019
3. Device buffers
=================
This driver supports IIO buffers.
All devices support retrieving the raw acceleration, gyroscope and temperature
measurements using buffers.
The following device families also support retrieving the delta velocity, delta
angle and temperature measurements using buffers:
- ADIS16477
- ADIS16500
- ADIS16505
- ADIS16507
However, when retrieving acceleration or gyroscope data using buffers, delta
readings will not be available and vice versa.
Usage examples
--------------
Set device trigger in current_trigger, if not already set:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
root:/sys/bus/iio/devices/iio:device0> echo adis16505-2-dev0 > trigger/current_trigger
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
adis16505-2-dev0
Select channels for buffer read:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_x_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_y_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_z_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_temp0_en
Set the number of samples to be stored in the buffer:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length
Enable buffer readings:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
Obtain buffered data:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0
...
00001680 01 1f 00 00 ff ff fe ef 00 00 47 bf 00 03 35 55 |..........G...5U|
00001690 01 1f 00 00 ff ff ff d9 00 00 46 f1 00 03 35 35 |..........F...55|
000016a0 01 1f 00 00 ff ff fe fc 00 00 46 cb 00 03 35 7b |..........F...5{|
000016b0 01 1f 00 00 ff ff fe 41 00 00 47 0d 00 03 35 8b |.......A..G...5.|
000016c0 01 1f 00 00 ff ff fe 37 00 00 46 b4 00 03 35 90 |.......7..F...5.|
000016d0 01 1d 00 00 ff ff fe 5a 00 00 45 d7 00 03 36 08 |.......Z..E...6.|
000016e0 01 1b 00 00 ff ff fe fb 00 00 45 e7 00 03 36 60 |..........E...6`|
000016f0 01 1a 00 00 ff ff ff 17 00 00 46 bc 00 03 36 de |..........F...6.|
00001700 01 1a 00 00 ff ff fe 59 00 00 46 d7 00 03 37 b8 |.......Y..F...7.|
00001710 01 1a 00 00 ff ff fe ae 00 00 46 95 00 03 37 ba |..........F...7.|
00001720 01 1a 00 00 ff ff fe c5 00 00 46 63 00 03 37 9f |..........Fc..7.|
00001730 01 1a 00 00 ff ff fe 55 00 00 46 89 00 03 37 c1 |.......U..F...7.|
00001740 01 1a 00 00 ff ff fe 31 00 00 46 aa 00 03 37 f7 |.......1..F...7.|
...
See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered
data is structured.
4. IIO Interfacing Tools
========================
Linux Kernel Tools
------------------
Linux Kernel provides some userspace tools that can be used to retrieve data
from IIO sysfs:
* lsiio: example application that provides a list of IIO devices and triggers
* iio_event_monitor: example application that reads events from an IIO device
and prints them
* iio_generic_buffer: example application that reads data from buffer
* iio_utils: set of APIs, typically used to access sysfs files.
LibIIO
------
LibIIO is a C/C++ library that provides generic access to IIO devices. The
library abstracts the low-level details of the hardware, and provides a simple
yet complete programming interface that can be used for advanced projects.
For more information about LibIIO, please see:
https://github.com/analogdevicesinc/libiio
.. SPDX-License-Identifier: GPL-2.0
=============================
Industrial IIO device buffers
=============================
1. Overview
===========
The Industrial I/O core offers a way for continuous data capture based on a
trigger source. Multiple data channels can be read at once from
``/dev/iio:deviceX`` character device node, thus reducing the CPU load.
Devices with buffer support feature an additional sub-directory in the
``/sys/bus/iio/devices/iio:deviceX/`` directory hierarchy, called bufferY, where
Y defaults to 0, for devices with a single buffer.
2. Buffer attributes
====================
An IIO buffer has an associated attributes directory under
``/sys/bus/iio/iio:deviceX/bufferY/``. The attributes are described below.
``length``
----------
Read / Write attribute which states the total number of data samples (capacity)
that can be stored by the buffer.
``enable``
----------
Read / Write attribute which starts / stops the buffer capture. This file should
be written last, after length and selection of scan elements. Writing a non-zero
value may result in an error, such as EINVAL, if, for example, an unsupported
combination of channels is given.
``watermark``
-------------
Read / Write positive integer attribute specifying the maximum number of scan
elements to wait for.
Poll will block until the watermark is reached.
Blocking read will wait until the minimum between the requested read amount or
the low watermark is available.
Non-blocking read will retrieve the available samples from the buffer even if
there are less samples than the watermark level. This allows the application to
block on poll with a timeout and read the available samples after the timeout
expires and thus have a maximum delay guarantee.
Data available
--------------
Read-only attribute indicating the bytes of data available in the buffer. In the
case of an output buffer, this indicates the amount of empty space available to
write data to. In the case of an input buffer, this indicates the amount of data
available for reading.
Scan elements
-------------
The meta information associated with a channel data placed in a buffer is called
a scan element. The scan elements attributes are presented below.
**_en**
Read / Write attribute used for enabling a channel. If and only if its value
is non-zero, then a triggered capture will contain data samples for this
channel.
**_index**
Read-only unsigned integer attribute specifying the position of the channel in
the buffer. Note these are not dependent on what is enabled and may not be
contiguous. Thus for userspace to establish the full layout these must be used
in conjunction with all _en attributes to establish which channels are present,
and the relevant _type attributes to establish the data storage format.
**_type**
Read-only attribute containing the description of the scan element data storage
within the buffer and hence the form in which it is read from userspace. Format
is [be|le]:[s|u]bits/storagebits[Xrepeat][>>shift], where:
- **be** or **le** specifies big or little-endian.
- **s** or **u** specifies if signed (2's complement) or unsigned.
- **bits** is the number of valid data bits.
- **storagebits** is the number of bits (after padding) that it occupies in the
buffer.
- **repeat** specifies the number of bits/storagebits repetitions. When the
repeat element is 0 or 1, then the repeat value is omitted.
- **shift** if specified, is the shift that needs to be applied prior to
masking out unused bits.
For example, a driver for a 3-axis accelerometer with 12-bit resolution where
data is stored in two 8-bit registers is as follows::
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
|D3 |D2 |D1 |D0 | X | X | X | X | (LOW byte, address 0x06)
+---+---+---+---+---+---+---+---+
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
|D11|D10|D9 |D8 |D7 |D6 |D5 |D4 | (HIGH byte, address 0x07)
+---+---+---+---+---+---+---+---+
will have the following scan element type for each axis:
.. code-block:: bash
$ cat /sys/bus/iio/devices/iio:device0/buffer0/in_accel_y_type
le:s12/16>>4
A userspace application will interpret data samples read from the buffer as
two-byte little-endian signed data, that needs a 4 bits right shift before
masking out the 12 valid bits of data.
It is also worth mentioning that the data in the buffer will be naturally
aligned, so the userspace application has to handle the buffers accordingly.
Take for example, a driver with four channels with the following description:
- channel0: index: 0, type: be:u16/16>>0
- channel1: index: 1, type: be:u32/32>>0
- channel2: index: 2, type: be:u32/32>>0
- channel3: index: 3, type: be:u64/64>>0
If all channels are enabled, the data will be aligned in the buffer as follows::
0-1 2 3 4-7 8-11 12 13 14 15 16-23 -> buffer byte number
+-----+---+---+-----+-----+---+---+---+---+-----+
|CHN_0|PAD|PAD|CHN_1|CHN_2|PAD|PAD|PAD|PAD|CHN_3| -> buffer content
+-----+---+---+-----+-----+---+---+---+---+-----+
If only channel0 and channel3 are enabled, the data will be aligned in the
buffer as follows::
0-1 2 3 4 5 6 7 8-15 -> buffer byte number
+-----+---+---+---+---+---+---+-----+
|CHN_0|PAD|PAD|PAD|PAD|PAD|PAD|CHN_3| -> buffer content
+-----+---+---+---+---+---+---+-----+
Typically the buffered data is found in raw format (unscaled with no offset
applied), however there are corner cases in which the buffered data may be found
in a processed form. Please note that these corner cases are not addressed by
this documentation.
Please see ``Documentation/ABI/testing/sysfs-bus-iio`` for a complete
description of the attributes.
......@@ -8,7 +8,14 @@ Industrial I/O
:maxdepth: 1
iio_configfs
iio_devbuf
ep93xx_adc
Industrial I/O Kernel Drivers
=============================
.. toctree::
:maxdepth: 1
adis16475
bno055
ep93xx_adc
......@@ -579,6 +579,12 @@ F: drivers/iio/accel/adxl372.c
F: drivers/iio/accel/adxl372_i2c.c
F: drivers/iio/accel/adxl372_spi.c
AF8133J THREE-AXIS MAGNETOMETER DRIVER
M: Ondřej Jirman <megi@xff.cz>
S: Maintained
F: Documentation/devicetree/bindings/iio/magnetometer/voltafield,af8133j.yaml
F: drivers/iio/magnetometer/af8133j.c
AF9013 MEDIA DRIVER
L: linux-media@vger.kernel.org
S: Orphan
......@@ -14426,6 +14432,13 @@ F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml
F: drivers/nvmem/microchip-otpc.c
F: include/dt-bindings/nvmem/microchip,sama7g5-otpc.h
MICROCHIP PAC1934 POWER/ENERGY MONITOR DRIVER
M: Marius Cristea <marius.cristea@microchip.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml
F: drivers/iio/adc/pac1934.c
MICROCHIP PCI1XXXX GP DRIVER
M: Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com>
M: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
......
......@@ -6,8 +6,8 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#include "adxl372.h"
......
......@@ -13,10 +13,10 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
......
......@@ -636,6 +636,84 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
return 0;
}
#ifdef CONFIG_ACPI
static bool kxj_acpi_orientation(struct device *dev,
struct iio_mount_matrix *orientation)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_device *adev = ACPI_COMPANION(dev);
char *str;
union acpi_object *obj, *elements;
acpi_status status;
int i, j, val[3];
bool ret = false;
if (!acpi_has_method(adev->handle, "ROTM"))
return false;
status = acpi_evaluate_object(adev->handle, "ROTM", NULL, &buffer);
if (ACPI_FAILURE(status)) {
dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status);
return false;
}
obj = buffer.pointer;
if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) {
dev_err(dev, "Unknown ACPI mount matrix package format\n");
goto out_free_buffer;
}
elements = obj->package.elements;
for (i = 0; i < 3; i++) {
if (elements[i].type != ACPI_TYPE_STRING) {
dev_err(dev, "Unknown ACPI mount matrix element format\n");
goto out_free_buffer;
}
str = elements[i].string.pointer;
if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) {
dev_err(dev, "Incorrect ACPI mount matrix string format\n");
goto out_free_buffer;
}
for (j = 0; j < 3; j++) {
switch (val[j]) {
case -1: str = "-1"; break;
case 0: str = "0"; break;
case 1: str = "1"; break;
default:
dev_err(dev, "Invalid value in ACPI mount matrix: %d\n", val[j]);
goto out_free_buffer;
}
orientation->rotation[i * 3 + j] = str;
}
}
ret = true;
out_free_buffer:
kfree(buffer.pointer);
return ret;
}
static bool kxj1009_apply_acpi_orientation(struct device *dev,
struct iio_mount_matrix *orientation)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
if (adev && acpi_dev_hid_uid_match(adev, "KIOX000A", NULL))
return kxj_acpi_orientation(dev, orientation);
return false;
}
#else
static bool kxj1009_apply_acpi_orientation(struct device *dev,
struct iio_mount_matrix *orientation)
{
return false;
}
#endif
static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data)
{
int ret;
......@@ -1466,9 +1544,12 @@ static int kxcjk1013_probe(struct i2c_client *client)
} else {
data->active_high_intr = true; /* default polarity */
ret = iio_read_mount_matrix(&client->dev, &data->orientation);
if (ret)
return ret;
if (!kxj1009_apply_acpi_orientation(&client->dev, &data->orientation)) {
ret = iio_read_mount_matrix(&client->dev, &data->orientation);
if (ret)
return ret;
}
}
ret = devm_regulator_bulk_get_enable(&client->dev,
......
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/regmap.h>
......
......@@ -930,6 +930,17 @@ config NPCM_ADC
This driver can also be built as a module. If so, the module
will be called npcm_adc.
config PAC1934
tristate "Microchip Technology PAC1934 driver"
depends on I2C
help
Say yes here to build support for Microchip Technology's PAC1931,
PAC1932, PAC1933, PAC1934 Single/Multi-Channel Power Monitor with
Accumulator.
This driver can also be built as a module. If so, the module
will be called pac1934.
config PALMAS_GPADC
tristate "TI Palmas General Purpose ADC"
depends on MFD_PALMAS
......
......@@ -86,6 +86,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_PAC1934) += pac1934.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
......
......@@ -371,6 +371,11 @@ static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
},
};
static void mcp320x_regulator_disable(void *reg)
{
regulator_disable(reg);
}
static int mcp320x_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
......@@ -388,7 +393,6 @@ static int mcp320x_probe(struct spi_device *spi)
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mcp320x_info;
spi_set_drvdata(spi, indio_dev);
device_index = spi_get_device_id(spi)->driver_data;
chip_info = &mcp320x_chip_infos[device_index];
......@@ -445,27 +449,13 @@ static int mcp320x_probe(struct spi_device *spi)
if (ret < 0)
return ret;
mutex_init(&adc->lock);
ret = iio_device_register(indio_dev);
ret = devm_add_action_or_reset(&spi->dev, mcp320x_regulator_disable, adc->reg);
if (ret < 0)
goto reg_disable;
return 0;
reg_disable:
regulator_disable(adc->reg);
return ret;
}
return ret;
static void mcp320x_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct mcp320x *adc = iio_priv(indio_dev);
mutex_init(&adc->lock);
iio_device_unregister(indio_dev);
regulator_disable(adc->reg);
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct of_device_id mcp320x_dt_ids[] = {
......@@ -520,7 +510,6 @@ static struct spi_driver mcp320x_driver = {
.of_match_table = mcp320x_dt_ids,
},
.probe = mcp320x_probe,
.remove = mcp320x_remove,
.id_table = mcp320x_id,
};
module_spi_driver(mcp320x_driver);
......
// SPDX-License-Identifier: GPL-2.0+
/*
* IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor
*
* Copyright (C) 2017-2024 Microchip Technology Inc. and its subsidiaries
*
* Author: Bogdan Bolocan <bogdan.bolocan@microchip.com>
* Author: Victor Tudose
* Author: Marius Cristea <marius.cristea@microchip.com>
*
* Datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here:
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf
*/
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <asm/unaligned.h>
/*
* maximum accumulation time should be (17 * 60 * 1000) around 17 minutes@1024 sps
* till PAC1934 accumulation registers starts to saturate
*/
#define PAC1934_MAX_RFSH_LIMIT_MS 60000
/* 50msec is the timeout for validity of the cached registers */
#define PAC1934_MIN_POLLING_TIME_MS 50
/*
* 1000usec is the minimum wait time for normal conversions when sample
* rate doesn't change
*/
#define PAC1934_MIN_UPDATE_WAIT_TIME_US 1000
/* 32000mV */
#define PAC1934_VOLTAGE_MILLIVOLTS_MAX 32000
/* voltage bits resolution when set for unsigned values */
#define PAC1934_VOLTAGE_U_RES 16
/* voltage bits resolution when set for signed values */
#define PAC1934_VOLTAGE_S_RES 15
/*
* max signed value that can be stored on 32 bits and 8 digits fractional value
* (2^31 - 1) * 10^8 + 99999999
*/
#define PAC_193X_MAX_POWER_ACC 214748364799999999LL
/*
* min signed value that can be stored on 32 bits and 8 digits fractional value
* -(2^31) * 10^8 - 99999999
*/
#define PAC_193X_MIN_POWER_ACC -214748364899999999LL
#define PAC1934_MAX_NUM_CHANNELS 4
#define PAC1934_MEAS_REG_LEN 76
#define PAC1934_CTRL_REG_LEN 12
#define PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ 1024
/* I2C address map */
#define PAC1934_REFRESH_REG_ADDR 0x00
#define PAC1934_CTRL_REG_ADDR 0x01
#define PAC1934_ACC_COUNT_REG_ADDR 0x02
#define PAC1934_VPOWER_ACC_1_ADDR 0x03
#define PAC1934_VPOWER_ACC_2_ADDR 0x04
#define PAC1934_VPOWER_ACC_3_ADDR 0x05
#define PAC1934_VPOWER_ACC_4_ADDR 0x06
#define PAC1934_VBUS_1_ADDR 0x07
#define PAC1934_VBUS_2_ADDR 0x08
#define PAC1934_VBUS_3_ADDR 0x09
#define PAC1934_VBUS_4_ADDR 0x0A
#define PAC1934_VSENSE_1_ADDR 0x0B
#define PAC1934_VSENSE_2_ADDR 0x0C
#define PAC1934_VSENSE_3_ADDR 0x0D
#define PAC1934_VSENSE_4_ADDR 0x0E
#define PAC1934_VBUS_AVG_1_ADDR 0x0F
#define PAC1934_VBUS_AVG_2_ADDR 0x10
#define PAC1934_VBUS_AVG_3_ADDR 0x11
#define PAC1934_VBUS_AVG_4_ADDR 0x12
#define PAC1934_VSENSE_AVG_1_ADDR 0x13
#define PAC1934_VSENSE_AVG_2_ADDR 0x14
#define PAC1934_VSENSE_AVG_3_ADDR 0x15
#define PAC1934_VSENSE_AVG_4_ADDR 0x16
#define PAC1934_VPOWER_1_ADDR 0x17
#define PAC1934_VPOWER_2_ADDR 0x18
#define PAC1934_VPOWER_3_ADDR 0x19
#define PAC1934_VPOWER_4_ADDR 0x1A
#define PAC1934_REFRESH_V_REG_ADDR 0x1F
#define PAC1934_CTRL_STAT_REGS_ADDR 0x1C
#define PAC1934_PID_REG_ADDR 0xFD
#define PAC1934_MID_REG_ADDR 0xFE
#define PAC1934_RID_REG_ADDR 0xFF
/* PRODUCT ID REGISTER + MANUFACTURER ID REGISTER + REVISION ID REGISTER */
#define PAC1934_ID_REG_LEN 3
#define PAC1934_PID_IDX 0
#define PAC1934_MID_IDX 1
#define PAC1934_RID_IDX 2
#define PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS 1
#define PAC1934_ACPI_GET_UOHMS_VALS 2
#define PAC1934_ACPI_GET_BIPOLAR_SETTINGS 4
#define PAC1934_ACPI_GET_SAMP 5
#define PAC1934_SAMPLE_RATE_SHIFT 6
#define PAC1934_VBUS_SENSE_REG_LEN 2
#define PAC1934_ACC_REG_LEN 3
#define PAC1934_VPOWER_REG_LEN 4
#define PAC1934_VPOWER_ACC_REG_LEN 6
#define PAC1934_MAX_REGISTER_LENGTH 6
#define PAC1934_CUSTOM_ATTR_FOR_CHANNEL 1
/*
* relative offsets when using multi-byte reads/writes even though these
* bytes are read one after the other, they are not at adjacent memory
* locations within the I2C memory map. The chip can skip some addresses
*/
#define PAC1934_CHANNEL_DIS_REG_OFF 0
#define PAC1934_NEG_PWR_REG_OFF 1
/*
* when reading/writing multiple bytes from offset PAC1934_CHANNEL_DIS_REG_OFF,
* the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets
*/
#define PAC1934_SLOW_REG_OFF 2
#define PAC1934_CTRL_ACT_REG_OFF 3
#define PAC1934_CHANNEL_DIS_ACT_REG_OFF 4
#define PAC1934_NEG_PWR_ACT_REG_OFF 5
#define PAC1934_CTRL_LAT_REG_OFF 6
#define PAC1934_CHANNEL_DIS_LAT_REG_OFF 7
#define PAC1934_NEG_PWR_LAT_REG_OFF 8
#define PAC1934_PID_REG_OFF 9
#define PAC1934_MID_REG_OFF 10
#define PAC1934_REV_REG_OFF 11
#define PAC1934_CTRL_STATUS_INFO_LEN 12
#define PAC1934_MID 0x5D
#define PAC1931_PID 0x58
#define PAC1932_PID 0x59
#define PAC1933_PID 0x5A
#define PAC1934_PID 0x5B
/* Scale constant = (10^3 * 3.2 * 10^9 / 2^28) for mili Watt-second */
#define PAC1934_SCALE_CONSTANT 11921
#define PAC1934_MAX_VPOWER_RSHIFTED_BY_28B 11921
#define PAC1934_MAX_VSENSE_RSHIFTED_BY_16B 1525
#define PAC1934_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
#define PAC1934_CRTL_SAMPLE_RATE_MASK GENMASK(7, 6)
#define PAC1934_CHAN_SLEEP_MASK BIT(5)
#define PAC1934_CHAN_SLEEP_SET BIT(5)
#define PAC1934_CHAN_SINGLE_MASK BIT(4)
#define PAC1934_CHAN_SINGLE_SHOT_SET BIT(4)
#define PAC1934_CHAN_ALERT_MASK BIT(3)
#define PAC1934_CHAN_ALERT_EN BIT(3)
#define PAC1934_CHAN_ALERT_CC_MASK BIT(2)
#define PAC1934_CHAN_ALERT_CC_EN BIT(2)
#define PAC1934_CHAN_OVF_ALERT_MASK BIT(1)
#define PAC1934_CHAN_OVF_ALERT_EN BIT(1)
#define PAC1934_CHAN_OVF_MASK BIT(0)
#define PAC1934_CHAN_DIS_CH1_OFF_MASK BIT(7)
#define PAC1934_CHAN_DIS_CH2_OFF_MASK BIT(6)
#define PAC1934_CHAN_DIS_CH3_OFF_MASK BIT(5)
#define PAC1934_CHAN_DIS_CH4_OFF_MASK BIT(4)
#define PAC1934_SMBUS_TIMEOUT_MASK BIT(3)
#define PAC1934_SMBUS_BYTECOUNT_MASK BIT(2)
#define PAC1934_SMBUS_NO_SKIP_MASK BIT(1)
#define PAC1934_NEG_PWR_CH1_BIDI_MASK BIT(7)
#define PAC1934_NEG_PWR_CH2_BIDI_MASK BIT(6)
#define PAC1934_NEG_PWR_CH3_BIDI_MASK BIT(5)
#define PAC1934_NEG_PWR_CH4_BIDI_MASK BIT(4)
#define PAC1934_NEG_PWR_CH1_BIDV_MASK BIT(3)
#define PAC1934_NEG_PWR_CH2_BIDV_MASK BIT(2)
#define PAC1934_NEG_PWR_CH3_BIDV_MASK BIT(1)
#define PAC1934_NEG_PWR_CH4_BIDV_MASK BIT(0)
/*
* Universal Unique Identifier (UUID),
* 033771E0-1705-47B4-9535-D1BBE14D9A09,
* is reserved to Microchip for the PAC1934.
*/
#define PAC1934_DSM_UUID "033771E0-1705-47B4-9535-D1BBE14D9A09"
enum pac1934_ids {
PAC1931,
PAC1932,
PAC1933,
PAC1934
};
enum pac1934_samps {
PAC1934_SAMP_1024SPS,
PAC1934_SAMP_256SPS,
PAC1934_SAMP_64SPS,
PAC1934_SAMP_8SPS
};
/*
* these indexes are exactly describing the element order within a single
* PAC1934 phys channel IIO channel descriptor; see the static const struct
* iio_chan_spec pac1934_single_channel[] declaration
*/
enum pac1934_ch_idx {
PAC1934_CH_ENERGY,
PAC1934_CH_POWER,
PAC1934_CH_VOLTAGE,
PAC1934_CH_CURRENT,
PAC1934_CH_VOLTAGE_AVERAGE,
PAC1934_CH_CURRENT_AVERAGE
};
/**
* struct pac1934_features - features of a pac1934 instance
* @phys_channels: number of physical channels supported by the chip
* @name: chip's name
*/
struct pac1934_features {
u8 phys_channels;
const char *name;
};
struct samp_rate_mapping {
u16 samp_rate;
u8 shift2value;
};
static const unsigned int samp_rate_map_tbl[] = {
[PAC1934_SAMP_1024SPS] = 1024,
[PAC1934_SAMP_256SPS] = 256,
[PAC1934_SAMP_64SPS] = 64,
[PAC1934_SAMP_8SPS] = 8,
};
static const struct pac1934_features pac1934_chip_config[] = {
[PAC1931] = {
.phys_channels = 1,
.name = "pac1931",
},
[PAC1932] = {
.phys_channels = 2,
.name = "pac1932",
},
[PAC1933] = {
.phys_channels = 3,
.name = "pac1933",
},
[PAC1934] = {
.phys_channels = 4,
.name = "pac1934",
},
};
/**
* struct reg_data - data from the registers
* @meas_regs: snapshot of raw measurements registers
* @ctrl_regs: snapshot of control registers
* @energy_sec_acc: snapshot of energy values
* @vpower_acc: accumulated vpower values
* @vpower: snapshot of vpower registers
* @vbus: snapshot of vbus registers
* @vbus_avg: averages of vbus registers
* @vsense: snapshot of vsense registers
* @vsense_avg: averages of vsense registers
* @num_enabled_channels: count of how many chip channels are currently enabled
*/
struct reg_data {
u8 meas_regs[PAC1934_MEAS_REG_LEN];
u8 ctrl_regs[PAC1934_CTRL_REG_LEN];
s64 energy_sec_acc[PAC1934_MAX_NUM_CHANNELS];
s64 vpower_acc[PAC1934_MAX_NUM_CHANNELS];
s32 vpower[PAC1934_MAX_NUM_CHANNELS];
s32 vbus[PAC1934_MAX_NUM_CHANNELS];
s32 vbus_avg[PAC1934_MAX_NUM_CHANNELS];
s32 vsense[PAC1934_MAX_NUM_CHANNELS];
s32 vsense_avg[PAC1934_MAX_NUM_CHANNELS];
u8 num_enabled_channels;
};
/**
* struct pac1934_chip_info - information about the chip
* @client: the i2c-client attached to the device
* @lock: synchronize access to driver's state members
* @work_chip_rfsh: work queue used for refresh commands
* @phys_channels: phys channels count
* @active_channels: array of values, true means that channel is active
* @enable_energy: array of values, true means that channel energy is measured
* @bi_dir: array of bools, true means that channel is bidirectional
* @chip_variant: chip variant
* @chip_revision: chip revision
* @shunts: shunts
* @chip_reg_data: chip reg data
* @sample_rate_value: sampling frequency
* @labels: table with channels labels
* @iio_info: iio_info
* @tstamp: chip's uptime
*/
struct pac1934_chip_info {
struct i2c_client *client;
struct mutex lock; /* synchronize access to driver's state members */
struct delayed_work work_chip_rfsh;
u8 phys_channels;
bool active_channels[PAC1934_MAX_NUM_CHANNELS];
bool enable_energy[PAC1934_MAX_NUM_CHANNELS];
bool bi_dir[PAC1934_MAX_NUM_CHANNELS];
u8 chip_variant;
u8 chip_revision;
u32 shunts[PAC1934_MAX_NUM_CHANNELS];
struct reg_data chip_reg_data;
s32 sample_rate_value;
char *labels[PAC1934_MAX_NUM_CHANNELS];
struct iio_info iio_info;
unsigned long tstamp;
};
#define TO_PAC1934_CHIP_INFO(d) container_of(d, struct pac1934_chip_info, work_chip_rfsh)
#define PAC1934_VPOWER_ACC_CHANNEL(_index, _si, _address) { \
.type = IIO_ENERGY, \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_ENABLE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (_si), \
.scan_type = { \
.sign = 'u', \
.realbits = 48, \
.storagebits = 64, \
.endianness = IIO_CPU, \
} \
}
#define PAC1934_VBUS_CHANNEL(_index, _si, _address) { \
.type = IIO_VOLTAGE, \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (_si), \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
} \
}
#define PAC1934_VBUS_AVG_CHANNEL(_index, _si, _address) { \
.type = IIO_VOLTAGE, \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (_si), \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
} \
}
#define PAC1934_VSENSE_CHANNEL(_index, _si, _address) { \
.type = IIO_CURRENT, \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (_si), \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
} \
}
#define PAC1934_VSENSE_AVG_CHANNEL(_index, _si, _address) { \
.type = IIO_CURRENT, \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (_si), \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
} \
}
#define PAC1934_VPOWER_CHANNEL(_index, _si, _address) { \
.type = IIO_POWER, \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (_si), \
.scan_type = { \
.sign = 'u', \
.realbits = 28, \
.storagebits = 32, \
.shift = 4, \
.endianness = IIO_CPU, \
} \
}
static const struct iio_chan_spec pac1934_single_channel[] = {
PAC1934_VPOWER_ACC_CHANNEL(0, 0, PAC1934_VPOWER_ACC_1_ADDR),
PAC1934_VPOWER_CHANNEL(0, 0, PAC1934_VPOWER_1_ADDR),
PAC1934_VBUS_CHANNEL(0, 0, PAC1934_VBUS_1_ADDR),
PAC1934_VSENSE_CHANNEL(0, 0, PAC1934_VSENSE_1_ADDR),
PAC1934_VBUS_AVG_CHANNEL(0, 0, PAC1934_VBUS_AVG_1_ADDR),
PAC1934_VSENSE_AVG_CHANNEL(0, 0, PAC1934_VSENSE_AVG_1_ADDR),
};
/* Low-level I2c functions used to transfer up to 76 bytes at once */
static int pac1934_i2c_read(struct i2c_client *client, u8 reg_addr,
void *databuf, u8 len)
{
int ret;
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.len = 1,
.buf = (u8 *)&reg_addr,
},
{
.addr = client->addr,
.len = len,
.buf = databuf,
.flags = I2C_M_RD
}
};
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return ret;
return 0;
}
static int pac1934_get_samp_rate_idx(struct pac1934_chip_info *info,
u32 new_samp_rate)
{
int cnt;
for (cnt = 0; cnt < ARRAY_SIZE(samp_rate_map_tbl); cnt++)
if (new_samp_rate == samp_rate_map_tbl[cnt])
return cnt;
/* not a valid sample rate value */
return -EINVAL;
}
static ssize_t pac1934_shunt_value_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct pac1934_chip_info *info = iio_priv(indio_dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
return sysfs_emit(buf, "%u\n", info->shunts[this_attr->address]);
}
static ssize_t pac1934_shunt_value_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct pac1934_chip_info *info = iio_priv(indio_dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
int sh_val;
if (kstrtouint(buf, 10, &sh_val)) {
dev_err(dev, "Shunt value is not valid\n");
return -EINVAL;
}
scoped_guard(mutex, &info->lock)
info->shunts[this_attr->address] = sh_val;
return count;
}
static int pac1934_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel,
const int **vals, int *type, int *length, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*type = IIO_VAL_INT;
*vals = samp_rate_map_tbl;
*length = ARRAY_SIZE(samp_rate_map_tbl);
return IIO_AVAIL_LIST;
}
return -EINVAL;
}
static int pac1934_send_refresh(struct pac1934_chip_info *info,
u8 refresh_cmd, u32 wait_time)
{
/* this function only sends REFRESH or REFRESH_V */
struct i2c_client *client = info->client;
int ret;
u8 bidir_reg;
bool revision_bug = false;
if (info->chip_revision == 2 || info->chip_revision == 3) {
/*
* chip rev 2 and 3 bug workaround
* see: PAC1934 Family Data Sheet Errata DS80000836A.pdf
*/
revision_bug = true;
bidir_reg =
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) |
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) |
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) |
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) |
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) |
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) |
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) |
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]);
ret = i2c_smbus_write_byte_data(client,
PAC1934_CTRL_STAT_REGS_ADDR +
PAC1934_NEG_PWR_REG_OFF,
bidir_reg);
if (ret)
return ret;
}
ret = i2c_smbus_write_byte(client, refresh_cmd);
if (ret) {
dev_err(&client->dev, "%s - cannot send 0x%02X\n",
__func__, refresh_cmd);
return ret;
}
if (revision_bug) {
/*
* chip rev 2 and 3 bug workaround - write again the same
* register write the updated registers back
*/
ret = i2c_smbus_write_byte_data(client,
PAC1934_CTRL_STAT_REGS_ADDR +
PAC1934_NEG_PWR_REG_OFF, bidir_reg);
if (ret)
return ret;
}
/* register data retrieval timestamp */
info->tstamp = jiffies;
/* wait till the data is available */
usleep_range(wait_time, wait_time + 100);
return ret;
}
static int pac1934_reg_snapshot(struct pac1934_chip_info *info,
bool do_refresh, u8 refresh_cmd, u32 wait_time)
{
int ret;
struct i2c_client *client = info->client;
u8 samp_shift, ctrl_regs_tmp;
u8 *offset_reg_data_p;
u16 tmp_value;
u32 samp_rate, cnt, tmp;
s64 curr_energy, inc;
u64 tmp_energy;
struct reg_data *reg_data;
guard(mutex)(&info->lock);
if (do_refresh) {
ret = pac1934_send_refresh(info, refresh_cmd, wait_time);
if (ret < 0) {
dev_err(&client->dev,
"%s - cannot send refresh\n",
__func__);
return ret;
}
}
ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
PAC1934_CTRL_REG_LEN,
(u8 *)info->chip_reg_data.ctrl_regs);
if (ret < 0) {
dev_err(&client->dev,
"%s - cannot read ctrl/status registers\n",
__func__);
return ret;
}
reg_data = &info->chip_reg_data;
/* read the data registers */
ret = pac1934_i2c_read(client, PAC1934_ACC_COUNT_REG_ADDR,
(u8 *)reg_data->meas_regs, PAC1934_MEAS_REG_LEN);
if (ret) {
dev_err(&client->dev,
"%s - cannot read ACC_COUNT register: %d:%d\n",
__func__, ret, PAC1934_MEAS_REG_LEN);
return ret;
}
/* see how much shift is required by the sample rate */
samp_rate = samp_rate_map_tbl[((reg_data->ctrl_regs[PAC1934_CTRL_LAT_REG_OFF]) >> 6)];
samp_shift = get_count_order(samp_rate);
ctrl_regs_tmp = reg_data->ctrl_regs[PAC1934_CHANNEL_DIS_LAT_REG_OFF];
offset_reg_data_p = &reg_data->meas_regs[PAC1934_ACC_REG_LEN];
/* start with VPOWER_ACC */
for (cnt = 0; cnt < info->phys_channels; cnt++) {
/* check if the channel is active, skip all fields if disabled */
if ((ctrl_regs_tmp << cnt) & 0x80)
continue;
/* skip if the energy accumulation is disabled */
if (info->enable_energy[cnt]) {
curr_energy = info->chip_reg_data.energy_sec_acc[cnt];
tmp_energy = get_unaligned_be48(offset_reg_data_p);
if (info->bi_dir[cnt])
reg_data->vpower_acc[cnt] = sign_extend64(tmp_energy, 47);
else
reg_data->vpower_acc[cnt] = tmp_energy;
/*
* compute the scaled to 1 second accumulated energy value;
* energy accumulator scaled to 1sec = VPOWER_ACC/2^samp_shift
* the chip's sampling rate is 2^samp_shift samples/sec
*/
inc = (reg_data->vpower_acc[cnt] >> samp_shift);
/* add the power_acc field */
curr_energy += inc;
clamp(curr_energy, PAC_193X_MIN_POWER_ACC, PAC_193X_MAX_POWER_ACC);
reg_data->energy_sec_acc[cnt] = curr_energy;
}
offset_reg_data_p += PAC1934_VPOWER_ACC_REG_LEN;
}
/* continue with VBUS */
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if ((ctrl_regs_tmp << cnt) & 0x80)
continue;
tmp_value = get_unaligned_be16(offset_reg_data_p);
if (info->bi_dir[cnt])
reg_data->vbus[cnt] = sign_extend32((u32)(tmp_value), 15);
else
reg_data->vbus[cnt] = tmp_value;
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
}
/* VSENSE */
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if ((ctrl_regs_tmp << cnt) & 0x80)
continue;
tmp_value = get_unaligned_be16(offset_reg_data_p);
if (info->bi_dir[cnt])
reg_data->vsense[cnt] = sign_extend32((u32)(tmp_value), 15);
else
reg_data->vsense[cnt] = tmp_value;
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
}
/* VBUS_AVG */
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if ((ctrl_regs_tmp << cnt) & 0x80)
continue;
tmp_value = get_unaligned_be16(offset_reg_data_p);
if (info->bi_dir[cnt])
reg_data->vbus_avg[cnt] = sign_extend32((u32)(tmp_value), 15);
else
reg_data->vbus_avg[cnt] = tmp_value;
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
}
/* VSENSE_AVG */
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if ((ctrl_regs_tmp << cnt) & 0x80)
continue;
tmp_value = get_unaligned_be16(offset_reg_data_p);
if (info->bi_dir[cnt])
reg_data->vsense_avg[cnt] = sign_extend32((u32)(tmp_value), 15);
else
reg_data->vsense_avg[cnt] = tmp_value;
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
}
/* VPOWER */
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if ((ctrl_regs_tmp << cnt) & 0x80)
continue;
tmp = get_unaligned_be32(offset_reg_data_p) >> 4;
if (info->bi_dir[cnt])
reg_data->vpower[cnt] = sign_extend32(tmp, 27);
else
reg_data->vpower[cnt] = tmp;
offset_reg_data_p += PAC1934_VPOWER_REG_LEN;
}
return 0;
}
static int pac1934_retrieve_data(struct pac1934_chip_info *info,
u32 wait_time)
{
int ret = 0;
/*
* check if the minimal elapsed time has passed and if so,
* re-read the chip, otherwise the cached info is just fine
*/
if (time_after(jiffies, info->tstamp + msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS))) {
ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
wait_time);
/*
* Re-schedule the work for the read registers on timeout
* (to prevent chip registers saturation)
*/
mod_delayed_work(system_wq, &info->work_chip_rfsh,
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
}
return ret;
}
static int pac1934_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct pac1934_chip_info *info = iio_priv(indio_dev);
s64 curr_energy;
int ret, channel = chan->channel - 1;
ret = pac1934_retrieve_data(info, PAC1934_MIN_UPDATE_WAIT_TIME_US);
if (ret < 0)
return ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_VOLTAGE:
*val = info->chip_reg_data.vbus[channel];
return IIO_VAL_INT;
case IIO_CURRENT:
*val = info->chip_reg_data.vsense[channel];
return IIO_VAL_INT;
case IIO_POWER:
*val = info->chip_reg_data.vpower[channel];
return IIO_VAL_INT;
case IIO_ENERGY:
curr_energy = info->chip_reg_data.energy_sec_acc[channel];
*val = (u32)curr_energy;
*val2 = (u32)(curr_energy >> 32);
return IIO_VAL_INT_64;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_AVERAGE_RAW:
switch (chan->type) {
case IIO_VOLTAGE:
*val = info->chip_reg_data.vbus_avg[channel];
return IIO_VAL_INT;
case IIO_CURRENT:
*val = info->chip_reg_data.vsense_avg[channel];
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->address) {
/* Voltages - scale for millivolts */
case PAC1934_VBUS_1_ADDR:
case PAC1934_VBUS_2_ADDR:
case PAC1934_VBUS_3_ADDR:
case PAC1934_VBUS_4_ADDR:
case PAC1934_VBUS_AVG_1_ADDR:
case PAC1934_VBUS_AVG_2_ADDR:
case PAC1934_VBUS_AVG_3_ADDR:
case PAC1934_VBUS_AVG_4_ADDR:
*val = PAC1934_VOLTAGE_MILLIVOLTS_MAX;
if (chan->scan_type.sign == 'u')
*val2 = PAC1934_VOLTAGE_U_RES;
else
*val2 = PAC1934_VOLTAGE_S_RES;
return IIO_VAL_FRACTIONAL_LOG2;
/*
* Currents - scale for mA - depends on the
* channel's shunt value
* (100mV * 1000000) / (2^16 * shunt(uohm))
*/
case PAC1934_VSENSE_1_ADDR:
case PAC1934_VSENSE_2_ADDR:
case PAC1934_VSENSE_3_ADDR:
case PAC1934_VSENSE_4_ADDR:
case PAC1934_VSENSE_AVG_1_ADDR:
case PAC1934_VSENSE_AVG_2_ADDR:
case PAC1934_VSENSE_AVG_3_ADDR:
case PAC1934_VSENSE_AVG_4_ADDR:
*val = PAC1934_MAX_VSENSE_RSHIFTED_BY_16B;
if (chan->scan_type.sign == 'u')
*val2 = info->shunts[channel];
else
*val2 = info->shunts[channel] >> 1;
return IIO_VAL_FRACTIONAL;
/*
* Power - uW - it will use the combined scale
* for current and voltage
* current(mA) * voltage(mV) = power (uW)
*/
case PAC1934_VPOWER_1_ADDR:
case PAC1934_VPOWER_2_ADDR:
case PAC1934_VPOWER_3_ADDR:
case PAC1934_VPOWER_4_ADDR:
*val = PAC1934_MAX_VPOWER_RSHIFTED_BY_28B;
if (chan->scan_type.sign == 'u')
*val2 = info->shunts[channel];
else
*val2 = info->shunts[channel] >> 1;
return IIO_VAL_FRACTIONAL;
case PAC1934_VPOWER_ACC_1_ADDR:
case PAC1934_VPOWER_ACC_2_ADDR:
case PAC1934_VPOWER_ACC_3_ADDR:
case PAC1934_VPOWER_ACC_4_ADDR:
/*
* expresses the 32 bit scale value here compute
* the scale for energy (miliWatt-second or miliJoule)
*/
*val = PAC1934_SCALE_CONSTANT;
if (chan->scan_type.sign == 'u')
*val2 = info->shunts[channel];
else
*val2 = info->shunts[channel] >> 1;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ:
*val = info->sample_rate_value;
return IIO_VAL_INT;
case IIO_CHAN_INFO_ENABLE:
*val = info->enable_energy[channel];
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int pac1934_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct pac1934_chip_info *info = iio_priv(indio_dev);
struct i2c_client *client = info->client;
int ret = -EINVAL;
s32 old_samp_rate;
u8 ctrl_reg;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = pac1934_get_samp_rate_idx(info, val);
if (ret < 0)
return ret;
/* write the new sampling value and trigger a snapshot(incl refresh) */
scoped_guard(mutex, &info->lock) {
ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, ret);
ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg);
if (ret) {
dev_err(&client->dev,
"%s - can't update sample rate\n",
__func__);
return ret;
}
}
old_samp_rate = info->sample_rate_value;
info->sample_rate_value = val;
/*
* now, force a snapshot with refresh - call retrieve
* data in order to update the refresh timer
* alter the timestamp in order to force trigger a
* register snapshot and a timestamp update
*/
info->tstamp -= msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS);
ret = pac1934_retrieve_data(info, (1024 / old_samp_rate) * 1000);
if (ret < 0) {
dev_err(&client->dev,
"%s - cannot snapshot ctrl and measurement regs\n",
__func__);
return ret;
}
return 0;
case IIO_CHAN_INFO_ENABLE:
scoped_guard(mutex, &info->lock) {
info->enable_energy[chan->channel - 1] = val ? true : false;
if (!val)
info->chip_reg_data.energy_sec_acc[chan->channel - 1] = 0;
}
return 0;
default:
return -EINVAL;
}
}
static int pac1934_read_label(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, char *label)
{
struct pac1934_chip_info *info = iio_priv(indio_dev);
switch (chan->address) {
case PAC1934_VBUS_1_ADDR:
case PAC1934_VBUS_2_ADDR:
case PAC1934_VBUS_3_ADDR:
case PAC1934_VBUS_4_ADDR:
return sysfs_emit(label, "%s_VBUS_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1);
case PAC1934_VBUS_AVG_1_ADDR:
case PAC1934_VBUS_AVG_2_ADDR:
case PAC1934_VBUS_AVG_3_ADDR:
case PAC1934_VBUS_AVG_4_ADDR:
return sysfs_emit(label, "%s_VBUS_AVG_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1);
case PAC1934_VSENSE_1_ADDR:
case PAC1934_VSENSE_2_ADDR:
case PAC1934_VSENSE_3_ADDR:
case PAC1934_VSENSE_4_ADDR:
return sysfs_emit(label, "%s_IBUS_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1);
case PAC1934_VSENSE_AVG_1_ADDR:
case PAC1934_VSENSE_AVG_2_ADDR:
case PAC1934_VSENSE_AVG_3_ADDR:
case PAC1934_VSENSE_AVG_4_ADDR:
return sysfs_emit(label, "%s_IBUS_AVG_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1);
case PAC1934_VPOWER_1_ADDR:
case PAC1934_VPOWER_2_ADDR:
case PAC1934_VPOWER_3_ADDR:
case PAC1934_VPOWER_4_ADDR:
return sysfs_emit(label, "%s_POWER_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1);
case PAC1934_VPOWER_ACC_1_ADDR:
case PAC1934_VPOWER_ACC_2_ADDR:
case PAC1934_VPOWER_ACC_3_ADDR:
case PAC1934_VPOWER_ACC_4_ADDR:
return sysfs_emit(label, "%s_ENERGY_%d\n",
info->labels[chan->scan_index],
chan->scan_index + 1);
}
return 0;
}
static void pac1934_work_periodic_rfsh(struct work_struct *work)
{
struct pac1934_chip_info *info = TO_PAC1934_CHIP_INFO((struct delayed_work *)work);
struct device *dev = &info->client->dev;
dev_dbg(dev, "%s - Periodic refresh\n", __func__);
/* do a REFRESH, then read */
pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
PAC1934_MIN_UPDATE_WAIT_TIME_US);
schedule_delayed_work(&info->work_chip_rfsh,
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
}
static int pac1934_read_revision(struct pac1934_chip_info *info, u8 *buf)
{
int ret;
struct i2c_client *client = info->client;
ret = i2c_smbus_read_i2c_block_data(client, PAC1934_PID_REG_ADDR,
PAC1934_ID_REG_LEN,
buf);
if (ret < 0) {
dev_err(&client->dev, "cannot read revision\n");
return ret;
}
return 0;
}
static int pac1934_chip_identify(struct pac1934_chip_info *info)
{
u8 rev_info[PAC1934_ID_REG_LEN];
struct device *dev = &info->client->dev;
int ret = 0;
ret = pac1934_read_revision(info, (u8 *)rev_info);
if (ret)
return ret;
info->chip_variant = rev_info[PAC1934_PID_IDX];
info->chip_revision = rev_info[PAC1934_RID_IDX];
dev_dbg(dev, "Chip variant: 0x%02X\n", info->chip_variant);
dev_dbg(dev, "Chip revision: 0x%02X\n", info->chip_revision);
switch (info->chip_variant) {
case PAC1934_PID:
return PAC1934;
case PAC1933_PID:
return PAC1933;
case PAC1932_PID:
return PAC1932;
case PAC1931_PID:
return PAC1931;
default:
return -EINVAL;
}
}
/*
* documentation related to the ACPI device definition
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
*/
static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
struct pac1934_chip_info *info)
{
acpi_handle handle;
union acpi_object *rez;
struct device *dev = &client->dev;
unsigned short bi_dir_mask;
int idx, i;
guid_t guid;
handle = ACPI_HANDLE(dev);
guid_parse(PAC1934_DSM_UUID, &guid);
rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL);
if (!rez)
return false;
for (i = 0; i < rez->package.count; i += 2) {
idx = i / 2;
info->labels[idx] =
devm_kmemdup(dev, rez->package.elements[i].string.pointer,
(size_t)rez->package.elements[i].string.length + 1,
GFP_KERNEL);
info->labels[idx][rez->package.elements[i].string.length] = '\0';
info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000;
info->active_channels[idx] = (info->shunts[idx] != 0);
}
ACPI_FREE(rez);
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_UOHMS_VALS, NULL);
if (!rez) {
/*
* initializing with default values
* we assume all channels are unidirectional(the mask is zero)
* and assign the default sampling rate
*/
info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ;
return true;
}
for (i = 0; i < rez->package.count; i++) {
idx = i;
info->shunts[idx] = rez->package.elements[i].integer.value;
info->active_channels[idx] = (info->shunts[idx] != 0);
}
ACPI_FREE(rez);
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL);
if (!rez)
return false;
bi_dir_mask = rez->package.elements[0].integer.value;
info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0;
info->bi_dir[1] = ((bi_dir_mask & (1 << 2)) | (bi_dir_mask & (1 << 6))) != 0;
info->bi_dir[2] = ((bi_dir_mask & (1 << 1)) | (bi_dir_mask & (1 << 5))) != 0;
info->bi_dir[3] = ((bi_dir_mask & (1 << 0)) | (bi_dir_mask & (1 << 4))) != 0;
ACPI_FREE(rez);
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL);
if (!rez)
return false;
info->sample_rate_value = rez->package.elements[0].integer.value;
ACPI_FREE(rez);
return true;
}
static bool pac1934_of_parse_channel_config(struct i2c_client *client,
struct pac1934_chip_info *info)
{
struct fwnode_handle *node, *fwnode;
struct device *dev = &client->dev;
unsigned int current_channel;
int idx, ret;
info->sample_rate_value = 1024;
current_channel = 1;
fwnode = dev_fwnode(dev);
fwnode_for_each_available_child_node(fwnode, node) {
ret = fwnode_property_read_u32(node, "reg", &idx);
if (ret) {
dev_err_probe(dev, ret,
"reading invalid channel index\n");
goto err_fwnode;
}
/* adjust idx to match channel index (1 to 4) from the datasheet */
idx--;
if (current_channel >= (info->phys_channels + 1) ||
idx >= info->phys_channels || idx < 0) {
dev_err_probe(dev, -EINVAL,
"%s: invalid channel_index %d value\n",
fwnode_get_name(node), idx);
goto err_fwnode;
}
/* enable channel */
info->active_channels[idx] = true;
ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms",
&info->shunts[idx]);
if (ret) {
dev_err_probe(dev, ret,
"%s: invalid shunt-resistor value: %d\n",
fwnode_get_name(node), info->shunts[idx]);
goto err_fwnode;
}
if (fwnode_property_present(node, "label")) {
ret = fwnode_property_read_string(node, "label",
(const char **)&info->labels[idx]);
if (ret) {
dev_err_probe(dev, ret,
"%s: invalid rail-name value\n",
fwnode_get_name(node));
goto err_fwnode;
}
}
info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar");
current_channel++;
}
return true;
err_fwnode:
fwnode_handle_put(node);
return false;
}
static void pac1934_cancel_delayed_work(void *dwork)
{
cancel_delayed_work_sync(dwork);
}
static int pac1934_chip_configure(struct pac1934_chip_info *info)
{
int cnt, ret;
struct i2c_client *client = info->client;
u8 regs[PAC1934_CTRL_STATUS_INFO_LEN], idx, ctrl_reg;
u32 wait_time;
info->chip_reg_data.num_enabled_channels = 0;
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if (info->active_channels[cnt])
info->chip_reg_data.num_enabled_channels++;
}
/*
* read whatever information was gathered before the driver was loaded
* establish which channels are enabled/disabled and then establish the
* information retrieval mode (using SKIP or no).
* Read the chip ID values
*/
ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
ARRAY_SIZE(regs),
(u8 *)regs);
if (ret < 0) {
dev_err_probe(&client->dev, ret,
"%s - cannot read regs from 0x%02X\n",
__func__, PAC1934_CTRL_STAT_REGS_ADDR);
return ret;
}
/* write the CHANNEL_DIS and the NEG_PWR registers */
regs[PAC1934_CHANNEL_DIS_REG_OFF] =
FIELD_PREP(PAC1934_CHAN_DIS_CH1_OFF_MASK, info->active_channels[0] ? 0 : 1) |
FIELD_PREP(PAC1934_CHAN_DIS_CH2_OFF_MASK, info->active_channels[1] ? 0 : 1) |
FIELD_PREP(PAC1934_CHAN_DIS_CH3_OFF_MASK, info->active_channels[2] ? 0 : 1) |
FIELD_PREP(PAC1934_CHAN_DIS_CH4_OFF_MASK, info->active_channels[3] ? 0 : 1) |
FIELD_PREP(PAC1934_SMBUS_TIMEOUT_MASK, 0) |
FIELD_PREP(PAC1934_SMBUS_BYTECOUNT_MASK, 0) |
FIELD_PREP(PAC1934_SMBUS_NO_SKIP_MASK, 0);
regs[PAC1934_NEG_PWR_REG_OFF] =
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) |
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) |
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) |
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) |
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) |
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) |
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) |
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]);
/* no SLOW triggered REFRESH, clear POR */
regs[PAC1934_SLOW_REG_OFF] = 0;
ret = i2c_smbus_write_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
ARRAY_SIZE(regs), (u8 *)regs);
if (ret)
return ret;
/* Default sampling rate */
ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, PAC1934_SAMP_1024SPS);
ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg);
if (ret)
return ret;
/*
* send a REFRESH to the chip, so the new settings take place
* as well as resetting the accumulators
*/
ret = i2c_smbus_write_byte(client, PAC1934_REFRESH_REG_ADDR);
if (ret) {
dev_err(&client->dev,
"%s - cannot send 0x%02X\n",
__func__, PAC1934_REFRESH_REG_ADDR);
return ret;
}
/*
* get the current(in the chip) sampling speed and compute the
* required timeout based on its value
* the timeout is 1/sampling_speed
*/
idx = regs[PAC1934_CTRL_ACT_REG_OFF] >> PAC1934_SAMPLE_RATE_SHIFT;
wait_time = (1024 / samp_rate_map_tbl[idx]) * 1000;
/*
* wait the maximum amount of time to be on the safe side
* the maximum wait time is for 8sps
*/
usleep_range(wait_time, wait_time + 100);
INIT_DELAYED_WORK(&info->work_chip_rfsh, pac1934_work_periodic_rfsh);
/* Setup the latest moment for reading the regs before saturation */
schedule_delayed_work(&info->work_chip_rfsh,
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
return devm_add_action_or_reset(&client->dev, pac1934_cancel_delayed_work,
&info->work_chip_rfsh);
}
static int pac1934_prep_iio_channels(struct pac1934_chip_info *info, struct iio_dev *indio_dev)
{
struct iio_chan_spec *ch_sp;
int channel_size, attribute_count, cnt;
void *dyn_ch_struct, *tmp_data;
struct device *dev = &info->client->dev;
/* find out dynamically how many IIO channels we need */
attribute_count = 0;
channel_size = 0;
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if (!info->active_channels[cnt])
continue;
/* add the size of the properties of one chip physical channel */
channel_size += sizeof(pac1934_single_channel);
/* count how many enabled channels we have */
attribute_count += ARRAY_SIZE(pac1934_single_channel);
dev_dbg(dev, ":%s: Channel %d active\n", __func__, cnt + 1);
}
dyn_ch_struct = devm_kzalloc(dev, channel_size, GFP_KERNEL);
if (!dyn_ch_struct)
return -EINVAL;
tmp_data = dyn_ch_struct;
/* populate the dynamic channels and make all the adjustments */
for (cnt = 0; cnt < info->phys_channels; cnt++) {
if (!info->active_channels[cnt])
continue;
memcpy(tmp_data, pac1934_single_channel, sizeof(pac1934_single_channel));
ch_sp = (struct iio_chan_spec *)tmp_data;
ch_sp[PAC1934_CH_ENERGY].channel = cnt + 1;
ch_sp[PAC1934_CH_ENERGY].scan_index = cnt;
ch_sp[PAC1934_CH_ENERGY].address = cnt + PAC1934_VPOWER_ACC_1_ADDR;
ch_sp[PAC1934_CH_POWER].channel = cnt + 1;
ch_sp[PAC1934_CH_POWER].scan_index = cnt;
ch_sp[PAC1934_CH_POWER].address = cnt + PAC1934_VPOWER_1_ADDR;
ch_sp[PAC1934_CH_VOLTAGE].channel = cnt + 1;
ch_sp[PAC1934_CH_VOLTAGE].scan_index = cnt;
ch_sp[PAC1934_CH_VOLTAGE].address = cnt + PAC1934_VBUS_1_ADDR;
ch_sp[PAC1934_CH_CURRENT].channel = cnt + 1;
ch_sp[PAC1934_CH_CURRENT].scan_index = cnt;
ch_sp[PAC1934_CH_CURRENT].address = cnt + PAC1934_VSENSE_1_ADDR;
/*
* In order to be able to use labels for PAC1934_CH_VOLTAGE, and
* PAC1934_CH_VOLTAGE_AVERAGE,respectively PAC1934_CH_CURRENT
* and PAC1934_CH_CURRENT_AVERAGE we need to use different
* channel numbers. We will add +5 (+1 to maximum PAC channels).
*/
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].channel = cnt + 5;
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_index = cnt;
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].address = cnt + PAC1934_VBUS_AVG_1_ADDR;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].channel = cnt + 5;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_index = cnt;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].address = cnt + PAC1934_VSENSE_AVG_1_ADDR;
/*
* now modify the parameters in all channels if the
* whole chip rail(channel) is bi-directional
*/
if (info->bi_dir[cnt]) {
ch_sp[PAC1934_CH_ENERGY].scan_type.sign = 's';
ch_sp[PAC1934_CH_ENERGY].scan_type.realbits = 47;
ch_sp[PAC1934_CH_POWER].scan_type.sign = 's';
ch_sp[PAC1934_CH_POWER].scan_type.realbits = 27;
ch_sp[PAC1934_CH_VOLTAGE].scan_type.sign = 's';
ch_sp[PAC1934_CH_VOLTAGE].scan_type.realbits = 15;
ch_sp[PAC1934_CH_CURRENT].scan_type.sign = 's';
ch_sp[PAC1934_CH_CURRENT].scan_type.realbits = 15;
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.sign = 's';
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.realbits = 15;
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.sign = 's';
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.realbits = 15;
}
tmp_data += sizeof(pac1934_single_channel);
}
/*
* send the updated dynamic channel structure information towards IIO
* prepare the required field for IIO class registration
*/
indio_dev->num_channels = attribute_count;
indio_dev->channels = (const struct iio_chan_spec *)dyn_ch_struct;
return 0;
}
static IIO_DEVICE_ATTR(in_shunt_resistor1, 0644,
pac1934_shunt_value_show, pac1934_shunt_value_store, 0);
static IIO_DEVICE_ATTR(in_shunt_resistor2, 0644,
pac1934_shunt_value_show, pac1934_shunt_value_store, 1);
static IIO_DEVICE_ATTR(in_shunt_resistor3, 0644,
pac1934_shunt_value_show, pac1934_shunt_value_store, 2);
static IIO_DEVICE_ATTR(in_shunt_resistor4, 0644,
pac1934_shunt_value_show, pac1934_shunt_value_store, 3);
static int pac1934_prep_custom_attributes(struct pac1934_chip_info *info,
struct iio_dev *indio_dev)
{
int i, active_channels_count = 0;
struct attribute **pac1934_custom_attr;
struct attribute_group *pac1934_group;
struct device *dev = &info->client->dev;
for (i = 0 ; i < info->phys_channels; i++)
if (info->active_channels[i])
active_channels_count++;
pac1934_group = devm_kzalloc(dev, sizeof(*pac1934_group), GFP_KERNEL);
if (!pac1934_group)
return -ENOMEM;
pac1934_custom_attr = devm_kzalloc(dev,
(PAC1934_CUSTOM_ATTR_FOR_CHANNEL *
active_channels_count)
* sizeof(*pac1934_group) + 1,
GFP_KERNEL);
if (!pac1934_custom_attr)
return -ENOMEM;
i = 0;
if (info->active_channels[0])
pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor1);
if (info->active_channels[1])
pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor2);
if (info->active_channels[2])
pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor3);
if (info->active_channels[3])
pac1934_custom_attr[i] = PAC1934_DEV_ATTR(in_shunt_resistor4);
pac1934_group->attrs = pac1934_custom_attr;
info->iio_info.attrs = pac1934_group;
return 0;
}
static void pac1934_mutex_destroy(void *data)
{
struct mutex *lock = data;
mutex_destroy(lock);
}
static const struct iio_info pac1934_info = {
.read_raw = pac1934_read_raw,
.write_raw = pac1934_write_raw,
.read_avail = pac1934_read_avail,
.read_label = pac1934_read_label,
};
static int pac1934_probe(struct i2c_client *client)
{
struct pac1934_chip_info *info;
const struct pac1934_features *chip;
struct iio_dev *indio_dev;
int cnt, ret;
bool match = false;
struct device *dev = &client->dev;
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->client = client;
/* always start with energy accumulation enabled */
for (cnt = 0; cnt < PAC1934_MAX_NUM_CHANNELS; cnt++)
info->enable_energy[cnt] = true;
ret = pac1934_chip_identify(info);
if (ret < 0) {
/*
* If failed to identify the hardware based on internal
* registers, try using fallback compatible in device tree
* to deal with some newer part number.
*/
chip = i2c_get_match_data(client);
if (!chip)
return -EINVAL;
info->phys_channels = chip->phys_channels;
indio_dev->name = chip->name;
} else {
info->phys_channels = pac1934_chip_config[ret].phys_channels;
indio_dev->name = pac1934_chip_config[ret].name;
}
if (acpi_match_device(dev->driver->acpi_match_table, dev))
match = pac1934_acpi_parse_channel_config(client, info);
else
/*
* This makes it possible to use also ACPI PRP0001 for
* registering the device using device tree properties.
*/
match = pac1934_of_parse_channel_config(client, info);
if (!match)
return dev_err_probe(dev, -EINVAL,
"parameter parsing returned an error\n");
mutex_init(&info->lock);
ret = devm_add_action_or_reset(dev, pac1934_mutex_destroy,
&info->lock);
if (ret < 0)
return ret;
/*
* do now any chip specific initialization (e.g. read/write
* some registers), enable/disable certain channels, change the sampling
* rate to the requested value
*/
ret = pac1934_chip_configure(info);
if (ret < 0)
return ret;
/* prepare the channel information */
ret = pac1934_prep_iio_channels(info, indio_dev);
if (ret < 0)
return ret;
info->iio_info = pac1934_info;
indio_dev->info = &info->iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = pac1934_prep_custom_attributes(info, indio_dev);
if (ret < 0)
return dev_err_probe(dev, ret,
"Can't configure custom attributes for PAC1934 device\n");
/*
* read whatever has been accumulated in the chip so far
* and reset the accumulators
*/
ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
PAC1934_MIN_UPDATE_WAIT_TIME_US);
if (ret < 0)
return ret;
ret = devm_iio_device_register(dev, indio_dev);
if (ret < 0)
return dev_err_probe(dev, ret,
"Can't register IIO device\n");
return 0;
}
static const struct i2c_device_id pac1934_id[] = {
{ .name = "pac1931", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1931] },
{ .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] },
{ .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] },
{ .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] },
{}
};
MODULE_DEVICE_TABLE(i2c, pac1934_id);
static const struct of_device_id pac1934_of_match[] = {
{
.compatible = "microchip,pac1931",
.data = &pac1934_chip_config[PAC1931]
},
{
.compatible = "microchip,pac1932",
.data = &pac1934_chip_config[PAC1932]
},
{
.compatible = "microchip,pac1933",
.data = &pac1934_chip_config[PAC1933]
},
{
.compatible = "microchip,pac1934",
.data = &pac1934_chip_config[PAC1934]
},
{}
};
MODULE_DEVICE_TABLE(of, pac1934_of_match);
/*
* using MCHP1930 to be compatible with BIOS ACPI. See example:
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
*/
static const struct acpi_device_id pac1934_acpi_match[] = {
{ "MCHP1930", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] },
{}
};
MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match);
static struct i2c_driver pac1934_driver = {
.driver = {
.name = "pac1934",
.of_match_table = pac1934_of_match,
.acpi_match_table = pac1934_acpi_match
},
.probe = pac1934_probe,
.id_table = pac1934_id,
};
module_i2c_driver(pac1934_driver);
MODULE_AUTHOR("Bogdan Bolocan <bogdan.bolocan@microchip.com>");
MODULE_AUTHOR("Victor Tudose");
MODULE_AUTHOR("Marius Cristea <marius.cristea@microchip.com>");
MODULE_DESCRIPTION("IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor");
MODULE_LICENSE("GPL");
......@@ -372,7 +372,6 @@ static const struct xoadc_channel pm8921_xoadc_channels[] = {
* @name: name of this channel
* @hwchan: pointer to hardware channel information (muxing & scaling settings)
* @calibration: whether to use absolute or ratiometric calibration
* @scale_fn_type: scaling function type
* @decimation: 0,1,2,3
* @amux_ip_rsv: ratiometric scale value if using ratiometric
* calibration: 0, 1, 2, 4, 5.
......
......@@ -52,7 +52,7 @@
#define SARADC2_START BIT(4)
#define SARADC2_SINGLE_MODE BIT(5)
#define SARADC2_CONV_CHANNELS GENMASK(15, 0)
#define SARADC2_CONV_CHANNELS GENMASK(3, 0)
struct rockchip_saradc;
......@@ -102,12 +102,12 @@ static void rockchip_saradc_start_v2(struct rockchip_saradc *info, int chn)
writel_relaxed(0xc, info->regs + SARADC_T_DAS_SOC);
writel_relaxed(0x20, info->regs + SARADC_T_PD_SOC);
val = FIELD_PREP(SARADC2_EN_END_INT, 1);
val |= val << 16;
val |= SARADC2_EN_END_INT << 16;
writel_relaxed(val, info->regs + SARADC2_END_INT_EN);
val = FIELD_PREP(SARADC2_START, 1) |
FIELD_PREP(SARADC2_SINGLE_MODE, 1) |
FIELD_PREP(SARADC2_CONV_CHANNELS, chn);
val |= val << 16;
val |= (SARADC2_START | SARADC2_SINGLE_MODE | SARADC2_CONV_CHANNELS) << 16;
writel(val, info->regs + SARADC2_CONV_CON);
}
......@@ -450,16 +450,11 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
* The reset should be an optional property, as it should work
* with old devicetrees as well
*/
info->reset = devm_reset_control_get_exclusive(&pdev->dev,
"saradc-apb");
info->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
"saradc-apb");
if (IS_ERR(info->reset)) {
ret = PTR_ERR(info->reset);
if (ret != -ENOENT)
return dev_err_probe(&pdev->dev, ret,
"failed to get saradc-apb\n");
dev_dbg(&pdev->dev, "no reset control found\n");
info->reset = NULL;
return dev_err_probe(&pdev->dev, ret, "failed to get saradc-apb\n");
}
init_completion(&info->completion);
......
......@@ -258,6 +258,8 @@ static int ads1298_set_samp_freq(struct ads1298_private *priv, int val)
rate = ADS1298_CLK_RATE_HZ;
if (!rate)
return -EINVAL;
if (val <= 0)
return -EINVAL;
factor = (rate >> ADS1298_SHIFT_DR_HR) / val;
if (factor >= BIT(ADS1298_SHIFT_DR_LP))
......@@ -657,7 +659,7 @@ static int ads1298_probe(struct spi_device *spi)
priv->reg_vref = devm_regulator_get_optional(dev, "vref");
if (IS_ERR(priv->reg_vref)) {
if (PTR_ERR(priv->reg_vref) != -ENODEV)
return dev_err_probe(dev, PTR_ERR(priv->reg_avdd),
return dev_err_probe(dev, PTR_ERR(priv->reg_vref),
"Failed to get vref regulator\n");
priv->reg_vref = NULL;
......
......@@ -11,7 +11,7 @@
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
......
......@@ -2,9 +2,10 @@
/*
* HMC425A and similar Gain Amplifiers
*
* Copyright 2020 Analog Devices Inc.
* Copyright 2020, 2024 Analog Devices Inc.
*/
#include <linux/bits.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
......@@ -12,6 +13,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/kernel.h>
#include <linux/math.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
......@@ -20,10 +22,24 @@
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
/*
* The LTC6373 amplifier supports configuring gain using GPIO's with the following
* values (OUTPUT_V / INPUT_V): 0(shutdown), 0.25, 0.5, 1, 2, 4, 8, 16
*
* Except for the shutdown value, all can be converted to dB using 20 * log10(x)
* From here, it is observed that all values are multiples of the '2' gain setting,
* with the correspondent of 6.020dB.
*/
#define LTC6373_CONVERSION_CONSTANT 6020
#define LTC6373_MIN_GAIN_CODE 0x6
#define LTC6373_CONVERSION_MASK GENMASK(2, 0)
#define LTC6373_SHUTDOWN GENMASK(2, 0)
enum hmc425a_type {
ID_HMC425A,
ID_HMC540S,
ID_ADRF5740
ID_ADRF5740,
ID_LTC6373,
};
struct hmc425a_chip_info {
......@@ -34,16 +50,110 @@ struct hmc425a_chip_info {
int gain_min;
int gain_max;
int default_gain;
int powerdown_val;
bool has_powerdown;
int (*gain_dB_to_code)(int gain, int *code);
int (*code_to_gain_dB)(int code, int *val, int *val2);
};
struct hmc425a_state {
struct mutex lock; /* protect sensor state */
struct hmc425a_chip_info *chip_info;
const struct hmc425a_chip_info *chip_info;
struct gpio_descs *gpios;
enum hmc425a_type type;
u32 gain;
bool powerdown;
};
static int gain_dB_to_code(struct hmc425a_state *st, int val, int val2, int *code)
{
const struct hmc425a_chip_info *inf = st->chip_info;
int gain;
if (val < 0)
gain = (val * 1000) - (val2 / 1000);
else
gain = (val * 1000) + (val2 / 1000);
if (gain > inf->gain_max || gain < inf->gain_min)
return -EINVAL;
if (st->powerdown)
return -EPERM;
return st->chip_info->gain_dB_to_code(gain, code);
}
static int hmc425a_gain_dB_to_code(int gain, int *code)
{
*code = ~((abs(gain) / 500) & 0x3F);
return 0;
}
static int hmc540s_gain_dB_to_code(int gain, int *code)
{
*code = ~((abs(gain) / 1000) & 0xF);
return 0;
}
static int adrf5740_gain_dB_to_code(int gain, int *code)
{
int temp = (abs(gain) / 2000) & 0xF;
/* Bit [0-3]: 2dB 4dB 8dB 8dB */
*code = temp & BIT(3) ? temp | BIT(2) : temp;
return 0;
}
static int ltc6373_gain_dB_to_code(int gain, int *code)
{
*code = ~(DIV_ROUND_CLOSEST(gain, LTC6373_CONVERSION_CONSTANT) + 3)
& LTC6373_CONVERSION_MASK;
return 0;
}
static int code_to_gain_dB(struct hmc425a_state *st, int *val, int *val2)
{
if (st->powerdown)
return -EPERM;
return st->chip_info->code_to_gain_dB(st->gain, val, val2);
}
static int hmc425a_code_to_gain_dB(int code, int *val, int *val2)
{
*val = (~code * -500) / 1000;
*val2 = ((~code * -500) % 1000) * 1000;
return 0;
}
static int hmc540s_code_to_gain_dB(int code, int *val, int *val2)
{
*val = (~code * -1000) / 1000;
*val2 = ((~code * -1000) % 1000) * 1000;
return 0;
}
static int adrf5740_code_to_gain_dB(int code, int *val, int *val2)
{
/*
* Bit [0-3]: 2dB 4dB 8dB 8dB
* When BIT(3) is set, unset BIT(2) and use 3 as double the place value
*/
code = code & BIT(3) ? code & ~BIT(2) : code;
*val = (code * -2000) / 1000;
*val2 = ((code * -2000) % 1000) * 1000;
return 0;
}
static int ltc6373_code_to_gain_dB(int code, int *val, int *val2)
{
int gain = ((~code & LTC6373_CONVERSION_MASK) - 3) *
LTC6373_CONVERSION_CONSTANT;
*val = gain / 1000;
*val2 = (gain % 1000) * 1000;
return 0;
}
static int hmc425a_write(struct iio_dev *indio_dev, u32 value)
{
struct hmc425a_state *st = iio_priv(indio_dev);
......@@ -61,30 +171,14 @@ static int hmc425a_read_raw(struct iio_dev *indio_dev,
int *val2, long m)
{
struct hmc425a_state *st = iio_priv(indio_dev);
int code, gain = 0;
int ret;
mutex_lock(&st->lock);
switch (m) {
case IIO_CHAN_INFO_HARDWAREGAIN:
code = st->gain;
switch (st->type) {
case ID_HMC425A:
gain = ~code * -500;
ret = code_to_gain_dB(st, val, val2);
if (ret)
break;
case ID_HMC540S:
gain = ~code * -1000;
break;
case ID_ADRF5740:
code = code & BIT(3) ? code & ~BIT(2) : code;
gain = code * -2000;
break;
}
*val = gain / 1000;
*val2 = (gain % 1000) * 1000;
ret = IIO_VAL_INT_PLUS_MICRO_DB;
break;
default:
......@@ -100,34 +194,14 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct hmc425a_state *st = iio_priv(indio_dev);
struct hmc425a_chip_info *inf = st->chip_info;
int code = 0, gain;
int ret;
if (val < 0)
gain = (val * 1000) - (val2 / 1000);
else
gain = (val * 1000) + (val2 / 1000);
if (gain > inf->gain_max || gain < inf->gain_min)
return -EINVAL;
switch (st->type) {
case ID_HMC425A:
code = ~((abs(gain) / 500) & 0x3F);
break;
case ID_HMC540S:
code = ~((abs(gain) / 1000) & 0xF);
break;
case ID_ADRF5740:
code = (abs(gain) / 2000) & 0xF;
code = code & BIT(3) ? code | BIT(2) : code;
break;
}
int code = 0, ret;
mutex_lock(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
ret = gain_dB_to_code(st, val, val2, &code);
if (ret)
break;
st->gain = code;
ret = hmc425a_write(indio_dev, st->gain);
......@@ -158,6 +232,48 @@ static const struct iio_info hmc425a_info = {
.write_raw_get_fmt = &hmc425a_write_raw_get_fmt,
};
static ssize_t ltc6373_read_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct hmc425a_state *st = iio_priv(indio_dev);
return sysfs_emit(buf, "%d\n", st->powerdown);
}
static ssize_t ltc6373_write_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf,
size_t len)
{
struct hmc425a_state *st = iio_priv(indio_dev);
bool powerdown;
int code, ret;
ret = kstrtobool(buf, &powerdown);
if (ret)
return ret;
mutex_lock(&st->lock);
st->powerdown = powerdown;
code = (powerdown) ? LTC6373_SHUTDOWN : st->gain;
hmc425a_write(indio_dev, code);
mutex_unlock(&st->lock);
return len;
}
static const struct iio_chan_spec_ext_info ltc6373_ext_info[] = {
{
.name = "powerdown",
.read = ltc6373_read_powerdown,
.write = ltc6373_write_powerdown,
.shared = IIO_SEPARATE,
},
{}
};
#define HMC425A_CHAN(_channel) \
{ \
.type = IIO_VOLTAGE, \
......@@ -167,20 +283,25 @@ static const struct iio_info hmc425a_info = {
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
}
#define LTC6373_CHAN(_channel) \
{ \
.type = IIO_VOLTAGE, \
.output = 1, \
.indexed = 1, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
.ext_info = ltc6373_ext_info, \
}
static const struct iio_chan_spec hmc425a_channels[] = {
HMC425A_CHAN(0),
};
/* Match table for of_platform binding */
static const struct of_device_id hmc425a_of_match[] = {
{ .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A },
{ .compatible = "adi,hmc540s", .data = (void *)ID_HMC540S },
{ .compatible = "adi,adrf5740", .data = (void *)ID_ADRF5740 },
{},
static const struct iio_chan_spec ltc6373_channels[] = {
LTC6373_CHAN(0),
};
MODULE_DEVICE_TABLE(of, hmc425a_of_match);
static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
static const struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
[ID_HMC425A] = {
.name = "hmc425a",
.channels = hmc425a_channels,
......@@ -189,6 +310,8 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
.gain_min = -31500,
.gain_max = 0,
.default_gain = -0x40, /* set default gain -31.5db*/
.gain_dB_to_code = hmc425a_gain_dB_to_code,
.code_to_gain_dB = hmc425a_code_to_gain_dB,
},
[ID_HMC540S] = {
.name = "hmc540s",
......@@ -198,6 +321,8 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
.gain_min = -15000,
.gain_max = 0,
.default_gain = -0x10, /* set default gain -15.0db*/
.gain_dB_to_code = hmc540s_gain_dB_to_code,
.code_to_gain_dB = hmc540s_code_to_gain_dB,
},
[ID_ADRF5740] = {
.name = "adrf5740",
......@@ -207,6 +332,21 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
.gain_min = -22000,
.gain_max = 0,
.default_gain = 0xF, /* set default gain -22.0db*/
.gain_dB_to_code = adrf5740_gain_dB_to_code,
.code_to_gain_dB = adrf5740_code_to_gain_dB,
},
[ID_LTC6373] = {
.name = "ltc6373",
.channels = ltc6373_channels,
.num_channels = ARRAY_SIZE(ltc6373_channels),
.num_gpios = 3,
.gain_min = -12041, /* gain setting x0.25*/
.gain_max = 24082, /* gain setting x16 */
.default_gain = LTC6373_MIN_GAIN_CODE,
.powerdown_val = LTC6373_SHUTDOWN,
.has_powerdown = true,
.gain_dB_to_code = ltc6373_gain_dB_to_code,
.code_to_gain_dB = ltc6373_code_to_gain_dB,
},
};
......@@ -221,9 +361,8 @@ static int hmc425a_probe(struct platform_device *pdev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->type = (uintptr_t)device_get_match_data(&pdev->dev);
st->chip_info = &hmc425a_chip_info_tbl[st->type];
st->chip_info = device_get_match_data(&pdev->dev);
indio_dev->num_channels = st->chip_info->num_channels;
indio_dev->channels = st->chip_info->channels;
indio_dev->name = st->chip_info->name;
......@@ -249,12 +388,31 @@ static int hmc425a_probe(struct platform_device *pdev)
indio_dev->info = &hmc425a_info;
indio_dev->modes = INDIO_DIRECT_MODE;
/* Set default gain */
hmc425a_write(indio_dev, st->gain);
if (st->chip_info->has_powerdown) {
st->powerdown = true;
hmc425a_write(indio_dev, st->chip_info->powerdown_val);
} else {
/* Set default gain */
hmc425a_write(indio_dev, st->gain);
}
return devm_iio_device_register(&pdev->dev, indio_dev);
}
/* Match table for of_platform binding */
static const struct of_device_id hmc425a_of_match[] = {
{ .compatible = "adi,hmc425a",
.data = &hmc425a_chip_info_tbl[ID_HMC425A]},
{ .compatible = "adi,hmc540s",
.data = &hmc425a_chip_info_tbl[ID_HMC540S]},
{ .compatible = "adi,adrf5740",
.data = &hmc425a_chip_info_tbl[ID_ADRF5740]},
{ .compatible = "adi,ltc6373",
.data = &hmc425a_chip_info_tbl[ID_LTC6373]},
{}
};
MODULE_DEVICE_TABLE(of, hmc425a_of_match);
static struct platform_driver hmc425a_driver = {
.driver = {
.name = KBUILD_MODNAME,
......
......@@ -17,7 +17,7 @@
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
......
......@@ -17,7 +17,7 @@
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
......
......@@ -15,7 +15,6 @@
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
......
......@@ -20,7 +20,6 @@
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/util_macros.h>
......
......@@ -6,6 +6,18 @@
menu "Magnetometer sensors"
config AF8133J
tristate "Voltafield AF8133J 3-Axis Magnetometer"
depends on I2C
depends on OF
select REGMAP_I2C
help
Say yes here to build support for Voltafield AF8133J I2C-based
3-axis magnetometer chip.
To compile this driver as a module, choose M here: the module
will be called af8133j.
config AK8974
tristate "Asahi Kasei AK8974 3-Axis Magnetometer"
depends on I2C
......
......@@ -4,6 +4,7 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AF8133J) += af8133j.o
obj-$(CONFIG_AK8974) += ak8974.o
obj-$(CONFIG_AK8975) += ak8975.o
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* af8133j.c - Voltafield AF8133J magnetometer driver
*
* Copyright 2021 Icenowy Zheng <icenowy@aosc.io>
* Copyright 2024 Ondřej Jirman <megi@xff.cz>
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define AF8133J_REG_OUT 0x03
#define AF8133J_REG_PCODE 0x00
#define AF8133J_REG_PCODE_VAL 0x5e
#define AF8133J_REG_STATUS 0x02
#define AF8133J_REG_STATUS_ACQ BIT(0)
#define AF8133J_REG_STATE 0x0a
#define AF8133J_REG_STATE_STBY 0x00
#define AF8133J_REG_STATE_WORK 0x01
#define AF8133J_REG_RANGE 0x0b
#define AF8133J_REG_RANGE_22G 0x12
#define AF8133J_REG_RANGE_12G 0x34
#define AF8133J_REG_SWR 0x11
#define AF8133J_REG_SWR_PERFORM 0x81
static const char * const af8133j_supply_names[] = {
"avdd",
"dvdd",
};
struct af8133j_data {
struct i2c_client *client;
struct regmap *regmap;
/*
* Protect device internal state between starting a measurement
* and reading the result.
*/
struct mutex mutex;
struct iio_mount_matrix orientation;
struct gpio_desc *reset_gpiod;
struct regulator_bulk_data supplies[ARRAY_SIZE(af8133j_supply_names)];
u8 range;
};
enum af8133j_axis {
AXIS_X = 0,
AXIS_Y,
AXIS_Z,
};
static struct iio_mount_matrix *
af8133j_get_mount_matrix(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct af8133j_data *data = iio_priv(indio_dev);
return &data->orientation;
}
static const struct iio_chan_spec_ext_info af8133j_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, af8133j_get_mount_matrix),
{ }
};
#define AF8133J_CHANNEL(_si, _axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _axis, \
.address = AXIS_ ## _axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = af8133j_ext_info, \
.scan_index = _si, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const struct iio_chan_spec af8133j_channels[] = {
AF8133J_CHANNEL(0, X),
AF8133J_CHANNEL(1, Y),
AF8133J_CHANNEL(2, Z),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static int af8133j_product_check(struct af8133j_data *data)
{
struct device *dev = &data->client->dev;
unsigned int val;
int ret;
ret = regmap_read(data->regmap, AF8133J_REG_PCODE, &val);
if (ret) {
dev_err(dev, "Error reading product code (%d)\n", ret);
return ret;
}
if (val != AF8133J_REG_PCODE_VAL) {
dev_warn(dev, "Invalid product code (0x%02x)\n", val);
return 0; /* Allow unknown ID so fallback compatibles work */
}
return 0;
}
static int af8133j_reset(struct af8133j_data *data)
{
struct device *dev = &data->client->dev;
int ret;
if (data->reset_gpiod) {
/* If we have GPIO reset line, use it */
gpiod_set_value_cansleep(data->reset_gpiod, 1);
udelay(10);
gpiod_set_value_cansleep(data->reset_gpiod, 0);
} else {
/* Otherwise use software reset */
ret = regmap_write(data->regmap, AF8133J_REG_SWR,
AF8133J_REG_SWR_PERFORM);
if (ret) {
dev_err(dev, "Failed to reset the chip\n");
return ret;
}
}
/* Wait for reset to finish */
usleep_range(1000, 1100);
/* Restore range setting */
if (data->range == AF8133J_REG_RANGE_22G) {
ret = regmap_write(data->regmap, AF8133J_REG_RANGE, data->range);
if (ret)
return ret;
}
return 0;
}
static void af8133j_power_down(struct af8133j_data *data)
{
gpiod_set_value_cansleep(data->reset_gpiod, 1);
regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies);
}
static int af8133j_power_up(struct af8133j_data *data)
{
struct device *dev = &data->client->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies);
if (ret) {
dev_err(dev, "Could not enable regulators\n");
return ret;
}
gpiod_set_value_cansleep(data->reset_gpiod, 0);
/* Wait for power on reset */
usleep_range(15000, 16000);
ret = af8133j_reset(data);
if (ret) {
af8133j_power_down(data);
return ret;
}
return 0;
}
static int af8133j_take_measurement(struct af8133j_data *data)
{
unsigned int val;
int ret;
ret = regmap_write(data->regmap,
AF8133J_REG_STATE, AF8133J_REG_STATE_WORK);
if (ret)
return ret;
/* The datasheet says "Mesaure Time <1.5ms" */
ret = regmap_read_poll_timeout(data->regmap, AF8133J_REG_STATUS, val,
val & AF8133J_REG_STATUS_ACQ,
500, 1500);
if (ret)
return ret;
ret = regmap_write(data->regmap,
AF8133J_REG_STATE, AF8133J_REG_STATE_STBY);
if (ret)
return ret;
return 0;
}
static int af8133j_read_measurement(struct af8133j_data *data, __le16 buf[3])
{
struct device *dev = &data->client->dev;
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret) {
/*
* Ignore EACCES because that happens when RPM is disabled
* during system sleep, while userspace leave eg. hrtimer
* trigger attached and IIO core keeps trying to do measurements.
*/
if (ret != -EACCES)
dev_err(dev, "Failed to power on (%d)\n", ret);
return ret;
}
scoped_guard(mutex, &data->mutex) {
ret = af8133j_take_measurement(data);
if (ret)
goto out_rpm_put;
ret = regmap_bulk_read(data->regmap, AF8133J_REG_OUT,
buf, sizeof(__le16) * 3);
}
out_rpm_put:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
static const int af8133j_scales[][2] = {
[0] = { 0, 366210 }, /* 12 gauss */
[1] = { 0, 671386 }, /* 22 gauss */
};
static int af8133j_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct af8133j_data *data = iio_priv(indio_dev);
__le16 buf[3];
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = af8133j_read_measurement(data, buf);
if (ret)
return ret;
*val = sign_extend32(le16_to_cpu(buf[chan->address]),
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
if (data->range == AF8133J_REG_RANGE_12G)
*val2 = af8133j_scales[0][1];
else
*val2 = af8133j_scales[1][1];
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static int af8133j_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)af8133j_scales;
*length = ARRAY_SIZE(af8133j_scales) * 2;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int af8133j_set_scale(struct af8133j_data *data,
unsigned int val, unsigned int val2)
{
struct device *dev = &data->client->dev;
u8 range;
int ret = 0;
if (af8133j_scales[0][0] == val && af8133j_scales[0][1] == val2)
range = AF8133J_REG_RANGE_12G;
else if (af8133j_scales[1][0] == val && af8133j_scales[1][1] == val2)
range = AF8133J_REG_RANGE_22G;
else
return -EINVAL;
pm_runtime_disable(dev);
/*
* When suspended, just store the new range to data->range to be
* applied later during power up.
*/
if (!pm_runtime_status_suspended(dev))
scoped_guard(mutex, &data->mutex)
ret = regmap_write(data->regmap,
AF8133J_REG_RANGE, range);
pm_runtime_enable(dev);
data->range = range;
return ret;
}
static int af8133j_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct af8133j_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return af8133j_set_scale(data, val, val2);
default:
return -EINVAL;
}
}
static int af8133j_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
return IIO_VAL_INT_PLUS_NANO;
}
static const struct iio_info af8133j_info = {
.read_raw = af8133j_read_raw,
.read_avail = af8133j_read_avail,
.write_raw = af8133j_write_raw,
.write_raw_get_fmt = af8133j_write_raw_get_fmt,
};
static irqreturn_t af8133j_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct af8133j_data *data = iio_priv(indio_dev);
s64 timestamp = iio_get_time_ns(indio_dev);
struct {
__le16 values[3];
s64 timestamp __aligned(8);
} sample;
int ret;
memset(&sample, 0, sizeof(sample));
ret = af8133j_read_measurement(data, sample.values);
if (ret)
goto out_done;
iio_push_to_buffers_with_timestamp(indio_dev, &sample, timestamp);
out_done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct regmap_config af8133j_regmap_config = {
.name = "af8133j_regmap",
.reg_bits = 8,
.val_bits = 8,
.max_register = AF8133J_REG_SWR,
.cache_type = REGCACHE_NONE,
};
static void af8133j_power_down_action(void *ptr)
{
struct af8133j_data *data = ptr;
if (!pm_runtime_status_suspended(&data->client->dev))
af8133j_power_down(data);
}
static int af8133j_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct af8133j_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
int ret, i;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &af8133j_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"regmap initialization failed\n");
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->regmap = regmap;
data->range = AF8133J_REG_RANGE_12G;
mutex_init(&data->mutex);
data->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(data->reset_gpiod))
return dev_err_probe(dev, PTR_ERR(data->reset_gpiod),
"Failed to get reset gpio\n");
for (i = 0; i < ARRAY_SIZE(af8133j_supply_names); i++)
data->supplies[i].supply = af8133j_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
data->supplies);
if (ret)
return ret;
ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret)
return dev_err_probe(dev, ret, "Failed to read mount matrix\n");
ret = af8133j_power_up(data);
if (ret)
return ret;
pm_runtime_set_active(dev);
ret = devm_add_action_or_reset(dev, af8133j_power_down_action, data);
if (ret)
return ret;
ret = af8133j_product_check(data);
if (ret)
return ret;
pm_runtime_get_noresume(dev);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 500);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
pm_runtime_put_autosuspend(dev);
indio_dev->info = &af8133j_info;
indio_dev->name = "af8133j";
indio_dev->channels = af8133j_channels;
indio_dev->num_channels = ARRAY_SIZE(af8133j_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
&af8133j_trigger_handler, NULL);
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to setup iio triggered buffer\n");
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to register iio device");
return 0;
}
static int af8133j_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct af8133j_data *data = iio_priv(indio_dev);
af8133j_power_down(data);
return 0;
}
static int af8133j_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct af8133j_data *data = iio_priv(indio_dev);
return af8133j_power_up(data);
}
static const struct dev_pm_ops af8133j_pm_ops = {
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(af8133j_runtime_suspend, af8133j_runtime_resume, NULL)
};
static const struct of_device_id af8133j_of_match[] = {
{ .compatible = "voltafield,af8133j", },
{ }
};
MODULE_DEVICE_TABLE(of, af8133j_of_match);
static const struct i2c_device_id af8133j_id[] = {
{ "af8133j", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, af8133j_id);
static struct i2c_driver af8133j_driver = {
.driver = {
.name = "af8133j",
.of_match_table = af8133j_of_match,
.pm = pm_ptr(&af8133j_pm_ops),
},
.probe = af8133j_probe,
.id_table = af8133j_id,
};
module_i2c_driver(af8133j_driver);
MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
MODULE_AUTHOR("Ondřej Jirman <megi@xff.cz>");
MODULE_DESCRIPTION("Voltafield AF8133J magnetic sensor driver");
MODULE_LICENSE("GPL");
......@@ -995,17 +995,16 @@ static const struct i2c_device_id isl29501_id[] = {
MODULE_DEVICE_TABLE(i2c, isl29501_id);
#if defined(CONFIG_OF)
static const struct of_device_id isl29501_i2c_matches[] = {
{ .compatible = "renesas,isl29501" },
{ }
};
MODULE_DEVICE_TABLE(of, isl29501_i2c_matches);
#endif
static struct i2c_driver isl29501_driver = {
.driver = {
.name = "isl29501",
.of_match_table = isl29501_i2c_matches,
},
.id_table = isl29501_id,
.probe = isl29501_probe,
......
......@@ -207,6 +207,7 @@ enum {
container_of(_sensor, struct ltc2983_temp, sensor)
struct ltc2983_chip_info {
const char *name;
unsigned int max_channels_nr;
bool has_temp;
bool has_eeprom;
......@@ -1346,7 +1347,7 @@ static irqreturn_t ltc2983_irq_handler(int irq, void *data)
__chan; \
})
static int ltc2983_parse_dt(struct ltc2983_data *st)
static int ltc2983_parse_fw(struct ltc2983_data *st)
{
struct device *dev = &st->spi->dev;
struct fwnode_handle *child;
......@@ -1605,7 +1606,6 @@ static int ltc2983_probe(struct spi_device *spi)
struct ltc2983_data *st;
struct iio_dev *indio_dev;
struct gpio_desc *gpio;
const char *name = spi_get_device_id(spi)->name;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
......@@ -1614,9 +1614,7 @@ static int ltc2983_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
st->info = device_get_match_data(&spi->dev);
if (!st->info)
st->info = (void *)spi_get_device_id(spi)->driver_data;
st->info = spi_get_device_match_data(spi);
if (!st->info)
return -ENODEV;
......@@ -1632,7 +1630,7 @@ static int ltc2983_probe(struct spi_device *spi)
st->eeprom_key = cpu_to_be32(LTC2983_EEPROM_KEY);
spi_set_drvdata(spi, st);
ret = ltc2983_parse_dt(st);
ret = ltc2983_parse_fw(st);
if (ret)
return ret;
......@@ -1657,7 +1655,7 @@ static int ltc2983_probe(struct spi_device *spi)
return ret;
ret = devm_request_irq(&spi->dev, spi->irq, ltc2983_irq_handler,
IRQF_TRIGGER_RISING, name, st);
IRQF_TRIGGER_RISING, st->info->name, st);
if (ret) {
dev_err(&spi->dev, "failed to request an irq, %d", ret);
return ret;
......@@ -1672,7 +1670,7 @@ static int ltc2983_probe(struct spi_device *spi)
return ret;
}
indio_dev->name = name;
indio_dev->name = st->info->name;
indio_dev->num_channels = st->iio_channels;
indio_dev->channels = st->iio_chan;
indio_dev->modes = INDIO_DIRECT_MODE;
......@@ -1703,15 +1701,25 @@ static DEFINE_SIMPLE_DEV_PM_OPS(ltc2983_pm_ops, ltc2983_suspend,
ltc2983_resume);
static const struct ltc2983_chip_info ltc2983_chip_info_data = {
.name = "ltc2983",
.max_channels_nr = 20,
};
static const struct ltc2983_chip_info ltc2984_chip_info_data = {
.name = "ltc2984",
.max_channels_nr = 20,
.has_eeprom = true,
};
static const struct ltc2983_chip_info ltc2986_chip_info_data = {
.name = "ltc2986",
.max_channels_nr = 10,
.has_temp = true,
.has_eeprom = true,
};
static const struct ltc2983_chip_info ltm2985_chip_info_data = {
.name = "ltm2985",
.max_channels_nr = 10,
.has_temp = true,
.has_eeprom = true,
......@@ -1721,7 +1729,7 @@ static const struct spi_device_id ltc2983_id_table[] = {
{ "ltc2983", (kernel_ulong_t)&ltc2983_chip_info_data },
{ "ltc2984", (kernel_ulong_t)&ltc2984_chip_info_data },
{ "ltc2986", (kernel_ulong_t)&ltc2986_chip_info_data },
{ "ltm2985", (kernel_ulong_t)&ltc2986_chip_info_data },
{ "ltm2985", (kernel_ulong_t)&ltm2985_chip_info_data },
{},
};
MODULE_DEVICE_TABLE(spi, ltc2983_id_table);
......@@ -1730,7 +1738,7 @@ static const struct of_device_id ltc2983_of_match[] = {
{ .compatible = "adi,ltc2983", .data = &ltc2983_chip_info_data },
{ .compatible = "adi,ltc2984", .data = &ltc2984_chip_info_data },
{ .compatible = "adi,ltc2986", .data = &ltc2986_chip_info_data },
{ .compatible = "adi,ltm2985", .data = &ltc2986_chip_info_data },
{ .compatible = "adi,ltm2985", .data = &ltm2985_chip_info_data },
{},
};
MODULE_DEVICE_TABLE(of, ltc2983_of_match);
......
......@@ -9,6 +9,7 @@
* Note: This driver assumes that the sensor has been calibrated beforehand.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
......@@ -17,6 +18,7 @@
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
......@@ -148,10 +150,17 @@ static int tmp117_probe(struct i2c_client *client)
struct tmp117_data *data;
struct iio_dev *indio_dev;
int dev_id;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EOPNOTSUPP;
ret = devm_regulator_get_enable(&client->dev, "vcc");
if (ret)
return ret;
fsleep(1500);
dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID);
if (dev_id < 0)
return dev_id;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment