diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934 b/Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934 new file mode 100644 index 0000000000000..625b7f8678478 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934 @@ -0,0 +1,9 @@ +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. diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index 2616017297456..36775f8f71dfd 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -22,7 +22,6 @@ properties: maxItems: 1 label: - $ref: /schemas/types.yaml#/definitions/string description: Unique name to identify which channel this is. bipolar: diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml new file mode 100644 index 0000000000000..47a11a9ac95e2 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml @@ -0,0 +1,120 @@ +# 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 + +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; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml index dacc526dc6953..dfc3f512918f6 100644 --- a/Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml @@ -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 = , , - , - ; + ; clocks = <&clk IMX93_CLK_ADC1_GATE>; clock-names = "ipg"; vref-supply = <®_vref_1v8>; diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml index 40fa0710f1f0f..c28db0d635a0a 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml @@ -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 diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml index 67de9d4e3a1df..3a470459b9658 100644 --- a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml @@ -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 diff --git a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml index 1414ba9977c16..3c6fe74af0b83 100644 --- a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml +++ b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml @@ -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: - | diff --git a/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml b/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml index 79e75a8675cba..e3eca89175175 100644 --- a/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml +++ b/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml @@ -27,6 +27,9 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml index abee04cd126e4..91c318746bf30 100644 --- a/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml +++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml @@ -21,6 +21,7 @@ properties: required: - compatible - reg + - vdd-supply additionalProperties: false diff --git a/Documentation/devicetree/bindings/iio/magnetometer/voltafield,af8133j.yaml b/Documentation/devicetree/bindings/iio/magnetometer/voltafield,af8133j.yaml new file mode 100644 index 0000000000000..b6ab01a6914ad --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/voltafield,af8133j.yaml @@ -0,0 +1,60 @@ +# 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 + +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 + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@1c { + compatible = "voltafield,af8133j"; + reg = <0x1c>; + avdd-supply = <®_dldo1>; + dvdd-supply = <®_dldo1>; + reset-gpios = <&pio 1 1 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml index 8c6d7735e8750..58aa1542776b5 100644 --- a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml @@ -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>; }; }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 1a0dc04f1db47..82e9f64c90ff4 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -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,.*": diff --git a/Documentation/iio/adis16475.rst b/Documentation/iio/adis16475.rst new file mode 100644 index 0000000000000..91cabb7d8d057 --- /dev/null +++ b/Documentation/iio/adis16475.rst @@ -0,0 +1,407 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================ +ADIS16475 driver +================ + +This driver supports Analog Device's IMUs on SPI bus. + +1. Supported devices +==================== + +* `ADIS16465 `_ +* `ADIS16467 `_ +* `ADIS16470 `_ +* `ADIS16475 `_ +* `ADIS16477 `_ +* `ADIS16500 `_ +* `ADIS16505 `_ +* `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 diff --git a/Documentation/iio/iio_devbuf.rst b/Documentation/iio/iio_devbuf.rst new file mode 100644 index 0000000000000..9919e4792d0e4 --- /dev/null +++ b/Documentation/iio/iio_devbuf.rst @@ -0,0 +1,152 @@ +.. 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. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 1b7292c58cd07..30b09eefe75ed 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index d59d52ec8b066..2662ec49b2978 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 +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 +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 M: Kumaravel Thiagarajan diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 75a88f16c6c90..787699773f96f 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -6,8 +6,8 @@ */ #include +#include #include -#include #include #include "adxl372.h" diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index ab4fccb24b6c0..6581772cb0c46 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -13,10 +13,10 @@ */ #include +#include #include #include #include -#include #include #include #include diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index c5f5b1ce7954b..126e8bdd6d0ed 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -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, diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 1719a9f1d90ad..4414670dfb436 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include -#include #include #include +#include #include #include diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d4462c202784c..0d9282fa67f59 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -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 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index a64326f40fcbe..b3c434722364a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -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 diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index f3b81798b3c93..da1421bd7b629 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -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); diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c new file mode 100644 index 0000000000000..e0c2742da5236 --- /dev/null +++ b/drivers/iio/adc/pac1934.c @@ -0,0 +1,1636 @@ +// 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 + * Author: Victor Tudose + * Author: Marius Cristea + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 *)®_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 = ®_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 "); +MODULE_AUTHOR("Victor Tudose"); +MODULE_AUTHOR("Marius Cristea "); +MODULE_DESCRIPTION("IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 01c5586df56dc..c9d2c66434e4f 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -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. diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index dd94667a623bd..bbe954a738c7d 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -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); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index ed895a30beedc..1d1eaba3d6d12 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -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; diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index ef06a897421ac..9440a268a78c4 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index ed4d729226961..2ee4c0d70281e 100644 --- a/drivers/iio/amplifiers/hmc425a.c +++ b/drivers/iio/amplifiers/hmc425a.c @@ -2,9 +2,10 @@ /* * HMC425A and similar Gain Amplifiers * - * Copyright 2020 Analog Devices Inc. + * Copyright 2020, 2024 Analog Devices Inc. */ +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -20,10 +22,24 @@ #include #include +/* + * 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, diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index 8a0480d338450..782e8f6b77829 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -17,7 +17,7 @@ */ #include -#include +#include #include #include diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 8f0119f392b70..53569587ccb7b 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index d5957d85c2786..105f379b9b414 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index d4948dfc31ff1..dcadf6428a87d 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 38532d840f2a3..cd2917d719047 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -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 diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index b1c784ea71c8e..ec5c46fbf999b 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -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 diff --git a/drivers/iio/magnetometer/af8133j.c b/drivers/iio/magnetometer/af8133j.c new file mode 100644 index 0000000000000..742bbdf25f08c --- /dev/null +++ b/drivers/iio/magnetometer/af8133j.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * af8133j.c - Voltafield AF8133J magnetometer driver + * + * Copyright 2021 Icenowy Zheng + * Copyright 2024 Ondřej Jirman + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Ondřej Jirman "); +MODULE_DESCRIPTION("Voltafield AF8133J magnetic sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index bcebacaf3dab0..4982686fb4c30 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -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, diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index fcb96c44d9540..39447c786af34 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -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)<c2983_chip_info_data }, { "ltc2984", (kernel_ulong_t)<c2984_chip_info_data }, { "ltc2986", (kernel_ulong_t)<c2986_chip_info_data }, - { "ltm2985", (kernel_ulong_t)<c2986_chip_info_data }, + { "ltm2985", (kernel_ulong_t)<m2985_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 = <c2983_chip_info_data }, { .compatible = "adi,ltc2984", .data = <c2984_chip_info_data }, { .compatible = "adi,ltc2986", .data = <c2986_chip_info_data }, - { .compatible = "adi,ltm2985", .data = <c2986_chip_info_data }, + { .compatible = "adi,ltm2985", .data = <m2985_chip_info_data }, {}, }; MODULE_DEVICE_TABLE(of, ltc2983_of_match); diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 059953015ae72..8972083d903a2 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -9,6 +9,7 @@ * Note: This driver assumes that the sensor has been calibrated beforehand. */ +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include @@ -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;