Merge 6.5-rc6 into char-misc-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Aug 2023 20:14:51 +0000 (22:14 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Aug 2023 20:14:51 +0000 (22:14 +0200)
We need the char/misc fixes in here as well to build on top of.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
186 files changed:
Documentation/ABI/stable/sysfs-bus-mhi
Documentation/ABI/testing/sysfs-bus-fsi-devices-sbefifo
Documentation/ABI/testing/sysfs-bus-iio
Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818
Documentation/admin-guide/dynamic-debug-howto.rst
Documentation/devicetree/bindings/fsi/ibm,i2cr-fsi-master.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml
Documentation/devicetree/bindings/iio/dac/microchip,mcp4728.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/iio/frequency/adi,admv1013.yaml
Documentation/devicetree/bindings/iio/frequency/adi,admv1014.yaml
Documentation/devicetree/bindings/iio/light/rohm,bu27010.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/iio/proximity/murata,irsd200.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/iio/proximity/semtech,sx9310.yaml
Documentation/devicetree/bindings/iio/proximity/semtech,sx9324.yaml
Documentation/devicetree/bindings/peci/nuvoton,npcm-peci.yaml [new file with mode: 0644]
MAINTAINERS
arch/arm/boot/dts/nuvoton/nuvoton-common-npcm7xx.dtsi
arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi
arch/x86/kernel/apm_32.c
drivers/android/binder.c
drivers/android/binderfs.c
drivers/bus/mhi/host/boot.c
drivers/bus/mhi/host/init.c
drivers/bus/mhi/host/main.c
drivers/bus/mhi/host/pci_generic.c
drivers/bus/mhi/host/pm.c
drivers/cdx/controller/cdx_controller.c
drivers/cdx/controller/cdx_rpmsg.c
drivers/char/agp/uninorth-agp.c
drivers/char/bsr.c
drivers/char/xillybus/Kconfig
drivers/firmware/google/Kconfig
drivers/firmware/stratix10-rsu.c
drivers/firmware/stratix10-svc.c
drivers/fsi/Kconfig
drivers/fsi/Makefile
drivers/fsi/fsi-core.c
drivers/fsi/fsi-master-aspeed.c
drivers/fsi/fsi-master-ast-cf.c
drivers/fsi/fsi-master-gpio.c
drivers/fsi/fsi-master-hub.c
drivers/fsi/fsi-master-i2cr.c [new file with mode: 0644]
drivers/fsi/fsi-master-i2cr.h [new file with mode: 0644]
drivers/fsi/fsi-master.h
drivers/fsi/fsi-occ.c
drivers/fsi/fsi-sbefifo.c
drivers/fsi/fsi-scom.c
drivers/fsi/fsi-slave.h [new file with mode: 0644]
drivers/fsi/i2cr-scom.c [new file with mode: 0644]
drivers/hwmon/peci/cputemp.c
drivers/hwmon/peci/dimmtemp.c
drivers/iio/accel/adxl313_i2c.c
drivers/iio/accel/adxl355_i2c.c
drivers/iio/accel/adxl372_spi.c
drivers/iio/accel/bma180.c
drivers/iio/accel/da280.c
drivers/iio/accel/kxsd9-spi.c
drivers/iio/accel/mma8452.c
drivers/iio/adc/Kconfig
drivers/iio/adc/Makefile
drivers/iio/adc/ab8500-gpadc.c
drivers/iio/adc/ad7124.c
drivers/iio/adc/ad7192.c
drivers/iio/adc/ad9467.c
drivers/iio/adc/adi-axi-adc.c
drivers/iio/adc/at91_adc.c
drivers/iio/adc/bcm_iproc_adc.c
drivers/iio/adc/cc10001_adc.c
drivers/iio/adc/imx7d_adc.c
drivers/iio/adc/ina2xx-adc.c
drivers/iio/adc/lpc32xx_adc.c
drivers/iio/adc/men_z188_adc.c
drivers/iio/adc/meson_saradc.c
drivers/iio/adc/npcm_adc.c
drivers/iio/adc/palmas_gpadc.c
drivers/iio/adc/qcom-spmi-adc5.c
drivers/iio/adc/qcom-spmi-iadc.c
drivers/iio/adc/rockchip_saradc.c
drivers/iio/adc/sc27xx_adc.c
drivers/iio/adc/spear_adc.c
drivers/iio/adc/stm32-adc-core.c
drivers/iio/adc/stm32-dfsdm-adc.c
drivers/iio/adc/stm32-dfsdm-core.c
drivers/iio/adc/stmpe-adc.c
drivers/iio/adc/sun20i-gpadc-iio.c [new file with mode: 0644]
drivers/iio/adc/sun4i-gpadc-iio.c
drivers/iio/adc/ti-lmp92064.c
drivers/iio/adc/ti_am335x_adc.c
drivers/iio/amplifiers/Kconfig
drivers/iio/amplifiers/ad8366.c
drivers/iio/cdc/ad7150.c
drivers/iio/chemical/scd4x.c
drivers/iio/common/Kconfig
drivers/iio/common/Makefile
drivers/iio/common/inv_sensors/Kconfig [new file with mode: 0644]
drivers/iio/common/inv_sensors/Makefile [new file with mode: 0644]
drivers/iio/common/inv_sensors/inv_sensors_timestamp.c [moved from drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c with 56% similarity]
drivers/iio/dac/Kconfig
drivers/iio/dac/Makefile
drivers/iio/dac/mcp4728.c [new file with mode: 0644]
drivers/iio/filter/admv8818.c
drivers/iio/frequency/admv1013.c
drivers/iio/imu/adis16475.c
drivers/iio/imu/inv_icm42600/Kconfig
drivers/iio/imu/inv_icm42600/Makefile
drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h [deleted file]
drivers/iio/imu/inv_mpu6050/Kconfig
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
drivers/iio/industrialio-core.c
drivers/iio/industrialio-event.c
drivers/iio/industrialio-trigger.c
drivers/iio/light/cm3605.c
drivers/iio/light/rohm-bu27008.c
drivers/iio/light/vcnl4000.c
drivers/iio/potentiometer/mcp4018.c
drivers/iio/potentiometer/mcp4531.c
drivers/iio/proximity/Kconfig
drivers/iio/proximity/Makefile
drivers/iio/proximity/irsd200.c [new file with mode: 0644]
drivers/iio/proximity/isl29501.c
drivers/iio/proximity/mb1232.c
drivers/iio/temperature/mlx90614.c
drivers/iio/trigger/stm32-lptimer-trigger.c
drivers/misc/Kconfig
drivers/misc/atmel-ssc.c
drivers/misc/bcm-vk/bcm_vk.h
drivers/misc/bcm-vk/bcm_vk_msg.c
drivers/misc/cxl/base.c
drivers/misc/eeprom/idt_89hpesx.c
drivers/misc/fastrpc.c
drivers/misc/genwqe/card_base.c
drivers/misc/genwqe/card_base.h
drivers/misc/hi6421v600-irq.c
drivers/misc/hpilo.c
drivers/misc/lis3lv02d/lis3lv02d.c
drivers/misc/mchp_pci1xxxx/Kconfig
drivers/misc/mchp_pci1xxxx/Makefile
drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c [new file with mode: 0644]
drivers/misc/mei/bus-fixup.c
drivers/misc/mei/bus.c
drivers/misc/mei/gsc-me.c
drivers/misc/mei/init.c
drivers/misc/mei/mei_dev.h
drivers/misc/mei/pxp/mei_pxp.c
drivers/misc/qcom-coincell.c
drivers/misc/sram.c
drivers/misc/tps6594-esm.c
drivers/misc/tps6594-pfsm.c
drivers/misc/vcpu_stall_detector.c
drivers/misc/xilinx_sdfec.c
drivers/misc/xilinx_tmr_inject.c
drivers/misc/xilinx_tmr_manager.c
drivers/pcmcia/Kconfig
drivers/peci/controller/Kconfig
drivers/peci/controller/Makefile
drivers/peci/controller/peci-aspeed.c
drivers/peci/controller/peci-npcm.c [new file with mode: 0644]
drivers/peci/core.c
drivers/peci/cpu.c
drivers/uio/uio_pruss.c
include/dt-bindings/iio/qcom,spmi-adc7-pm8350.h
include/dt-bindings/iio/qcom,spmi-adc7-pm8350b.h
include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h
include/dt-bindings/iio/qcom,spmi-adc7-pmr735a.h
include/dt-bindings/iio/qcom,spmi-adc7-pmr735b.h
include/linux/dynamic_debug.h
include/linux/firmware/intel/stratix10-smc.h
include/linux/firmware/intel/stratix10-svc-client.h
include/linux/iio/common/inv_sensors_timestamp.h [new file with mode: 0644]
include/linux/iio/types.h
include/linux/mhi.h
include/linux/peci.h
include/trace/events/fsi.h
include/trace/events/fsi_master_i2cr.h [new file with mode: 0644]
include/uapi/linux/fsi.h
include/uapi/linux/sync_file.h
lib/dynamic_debug.c
tools/iio/iio_generic_buffer.c

index 96ccc33..1a47f9e 100644 (file)
@@ -1,7 +1,7 @@
 What:          /sys/bus/mhi/devices/.../serialnumber
 Date:          Sept 2020
 KernelVersion: 5.10
-Contact:       Bhaumik Bhatt <bbhatt@codeaurora.org>
+Contact:       mhi@lists.linux.dev
 Description:   The file holds the serial number of the client device obtained
                using a BHI (Boot Host Interface) register read after at least
                one attempt to power up the device has been done. If read
@@ -12,7 +12,7 @@ Users:                Any userspace application or clients interested in device info.
 What:          /sys/bus/mhi/devices/.../oem_pk_hash
 Date:          Sept 2020
 KernelVersion: 5.10
-Contact:       Bhaumik Bhatt <bbhatt@codeaurora.org>
+Contact:       mhi@lists.linux.dev
 Description:   The file holds the OEM PK Hash value of the endpoint device
                obtained using a BHI (Boot Host Interface) register read after
                at least one attempt to power up the device has been done. If
index 531fe9d..c7393b4 100644 (file)
@@ -5,6 +5,6 @@ Description:
                Indicates whether or not this SBE device has experienced a
                timeout; i.e. the SBE did not respond within the time allotted
                by the driver. A value of 1 indicates that a timeout has
-               ocurred and no transfers have completed since the timeout. A
-               value of 0 indicates that no timeout has ocurred, or if one
-               has, more recent transfers have completed successful.
+               occurred and no transfers have completed since the timeout. A
+               value of 0 indicates that no timeout has occurred, or if one
+               has, more recent transfers have completed successfully.
index 7140e8e..a2854dc 100644 (file)
@@ -2163,3 +2163,19 @@ Contact: linux-iio@vger.kernel.org
 Description:
                An example format is 16-bytes, 2-digits-per-byte, HEX-string
                representing the sensor unique ID number.
+
+What:          /sys/.../events/in_proximity_thresh_either_runningperiod
+KernelVersion: 6.6
+Contact:       linux-iio@vger.kernel.org
+Description:
+               A running period of time (in seconds) for which
+               in_proximity_thresh_either_runningcount amount of conditions
+               must occur before an event is generated. If direction is not
+               specified then this period applies to both directions.
+
+What:          /sys/.../events/in_proximity_thresh_either_runningcount
+KernelVersion: 6.6
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Number of conditions that must occur, during a running
+               period, before an event is generated.
index f6c0357..31dbb39 100644 (file)
@@ -7,6 +7,8 @@ Description:
 
                - auto -> Adjust bandpass filter to track changes in input clock rate.
                - manual -> disable/unregister the clock rate notifier / input clock tracking.
+               - bypass -> bypass low pass filter, high pass filter and disable/unregister
+                                                               the clock rate notifier
 
 What:          /sys/bus/iio/devices/iio:deviceX/filter_mode
 KernelVersion:
index 8dc668c..0b3d39c 100644 (file)
@@ -216,13 +216,14 @@ The flags are::
   t    Include thread ID, or <intr>
   m    Include module name
   f    Include the function name
+  s    Include the source file name
   l    Include line number
 
 For ``print_hex_dump_debug()`` and ``print_hex_dump_bytes()``, only
 the ``p`` flag has meaning, other flags are ignored.
 
-Note the regexp ``^[-+=][flmpt_]+$`` matches a flags specification.
-To clear all flags at once, use ``=_`` or ``-flmpt``.
+Note the regexp ``^[-+=][fslmpt_]+$`` matches a flags specification.
+To clear all flags at once, use ``=_`` or ``-fslmpt``.
 
 
 Debug messages during Boot Process
diff --git a/Documentation/devicetree/bindings/fsi/ibm,i2cr-fsi-master.yaml b/Documentation/devicetree/bindings/fsi/ibm,i2cr-fsi-master.yaml
new file mode 100644 (file)
index 0000000..442cecd
--- /dev/null
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/fsi/ibm,i2cr-fsi-master.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: IBM I2C Responder virtual FSI master
+
+maintainers:
+  - Eddie James <eajames@linux.ibm.com>
+
+description: |
+  The I2C Responder (I2CR) is a an I2C device that's connected to an FSI CFAM
+  (see fsi.txt). The I2CR translates I2C bus operations to FSI CFAM reads and
+  writes or SCOM operations, thereby acting as an FSI master.
+
+properties:
+  compatible:
+    enum:
+      - ibm,i2cr-fsi-master
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      i2cr@20 {
+        compatible = "ibm,i2cr-fsi-master";
+        reg = <0x20>;
+      };
+    };
diff --git a/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml b/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml
new file mode 100644 (file)
index 0000000..7ef46c9
--- /dev/null
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/allwinner,sun20i-d1-gpadc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner D1 General Purpose ADC
+
+maintainers:
+  - Maksim Kiselev <bigunclemax@gmail.com>
+
+properties:
+  compatible:
+    enum:
+      - allwinner,sun20i-d1-gpadc
+
+  "#io-channel-cells":
+    const: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  reg:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+patternProperties:
+  "^channel@[0-9a-f]+$":
+    $ref: adc.yaml
+    type: object
+    description:
+      Represents the internal channels of the ADC.
+
+    properties:
+      reg:
+        items:
+          minimum: 0
+          maximum: 15
+
+    required:
+      - reg
+
+    unevaluatedProperties: false
+
+required:
+  - "#io-channel-cells"
+  - clocks
+  - compatible
+  - interrupts
+  - reg
+  - resets
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/sun20i-d1-ccu.h>
+    #include <dt-bindings/reset/sun20i-d1-ccu.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    gpadc: adc@2009000 {
+        compatible = "allwinner,sun20i-d1-gpadc";
+        reg = <0x2009000 0x400>;
+        clocks = <&ccu CLK_BUS_GPADC>;
+        resets = <&ccu RST_BUS_GPADC>;
+        interrupts = <73 IRQ_TYPE_LEVEL_HIGH>;
+        #io-channel-cells = <1>;
+
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        channel@0 {
+            reg = <0>;
+        };
+
+        channel@1 {
+            reg = <1>;
+        };
+    };
+...
index 2127d63..e004659 100644 (file)
@@ -78,9 +78,9 @@ patternProperties:
       ti,datarate:
         $ref: /schemas/types.yaml#/definitions/uint32
         minimum: 0
-        maximum: 6
+        maximum: 7
         description: |
-          Data acquisition rate in samples per second
+          Data acquisition rate in samples per second for ADS1015, TLA2024
           0: 128
           1: 250
           2: 490
@@ -88,6 +88,17 @@ patternProperties:
           4: 1600 (default)
           5: 2400
           6: 3300
+          7: 3300
+
+          Data acquisition rate in samples per second for ADS1115
+          0: 8
+          1: 16
+          2: 32
+          3: 64
+          4: 128 (default)
+          5: 250
+          6: 475
+          7: 860
 
     required:
       - reg
diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp4728.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp4728.yaml
new file mode 100644 (file)
index 0000000..99831d7
--- /dev/null
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+
+$id: http://devicetree.org/schemas/iio/dac/microchip,mcp4728.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip MCP4728 DAC
+
+maintainers:
+  - Andrea Collamati <andrea.collamati@gmail.com>
+
+description: |
+  MCP4728 is a quad channel, 12-bit voltage output
+  Digital-to-Analog Converter with non-volatile
+  memory and I2C compatible Serial Interface.
+  https://www.microchip.com/en-us/product/mcp4728
+
+properties:
+  compatible:
+    const: microchip,mcp4728
+
+  reg:
+    maxItems: 1
+
+  vdd-supply:
+    description: |
+      Provides both power and acts as the reference supply on the MCP4728
+      when Internal Vref is not selected.
+
+required:
+  - compatible
+  - reg
+  - vdd-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        dac@60 {
+            compatible = "microchip,mcp4728";
+            reg = <0x60>;
+            vdd-supply = <&vdac_vdd>;
+        };
+    };
index fc813bc..f2eb228 100644 (file)
@@ -39,6 +39,46 @@ properties:
     description:
       Analog voltage regulator.
 
+  vcc-drv-supply:
+    description:
+      RF Driver voltage regulator.
+
+  vcc2-drv-supply:
+    description:
+      RF predriver voltage regulator.
+
+  vcc-vva-supply:
+    description:
+      VVA Control Circuit voltage regulator.
+
+  vcc-amp1-supply:
+    description:
+      RF Amplifier 1 voltage regulator.
+
+  vcc-amp2-supply:
+    description:
+      RF Amplifier 2 voltage regulator.
+
+  vcc-env-supply:
+    description:
+      Envelope Detector voltage regulator.
+
+  vcc-bg-supply:
+    description:
+      Mixer Chip Band Gap Circuit voltage regulator.
+
+  vcc-bg2-supply:
+    description:
+      VGA Chip Band Gap Circuit voltage regulator.
+
+  vcc-mixer-supply:
+    description:
+      Mixer voltage regulator.
+
+  vcc-quad-supply:
+    description:
+      Quadruppler voltage regulator.
+
   adi,detector-enable:
     description:
       Enable the Envelope Detector available at output pins VENV_P and
@@ -69,6 +109,16 @@ required:
   - clocks
   - clock-names
   - vcm-supply
+  - vcc-drv-supply
+  - vcc2-drv-supply
+  - vcc-vva-supply
+  - vcc-amp1-supply
+  - vcc-amp2-supply
+  - vcc-env-supply
+  - vcc-bg-supply
+  - vcc-bg2-supply
+  - vcc-mixer-supply
+  - vcc-quad-supply
 
 allOf:
   - $ref: /schemas/spi/spi-peripheral-props.yaml#
@@ -87,6 +137,16 @@ examples:
         clocks = <&admv1013_lo>;
         clock-names = "lo_in";
         vcm-supply = <&vcm>;
+        vcc-drv-supply = <&vcc_drv>;
+        vcc2-drv-supply = <&vcc2_drv>;
+        vcc-vva-supply = <&vcc_vva>;
+        vcc-amp1-supply = <&vcc_amp1>;
+        vcc-amp2-supply = <&vcc_amp2>;
+        vcc-env-supply = <&vcc_env>;
+        vcc-bg-supply = <&vcc_bg>;
+        vcc-bg2-supply = <&vcc_bg2>;
+        vcc-mixer-supply = <&vcc_mixer>;
+        vcc-quad-supply = <&vcc_quad>;
         adi,quad-se-mode = "diff";
         adi,detector-enable;
       };
index ab86daa..8e4c5ff 100644 (file)
@@ -103,6 +103,14 @@ required:
   - clocks
   - clock-names
   - vcm-supply
+  - vcc-if-bb-supply
+  - vcc-vga-supply
+  - vcc-vva-supply
+  - vcc-lna-3p3-supply
+  - vcc-lna-1p5-supply
+  - vcc-bg-supply
+  - vcc-quad-supply
+  - vcc-mixer-supply
 
 allOf:
   - $ref: /schemas/spi/spi-peripheral-props.yaml#
diff --git a/Documentation/devicetree/bindings/iio/light/rohm,bu27010.yaml b/Documentation/devicetree/bindings/iio/light/rohm,bu27010.yaml
new file mode 100644 (file)
index 0000000..8376d64
--- /dev/null
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/light/rohm,bu27010.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BU27010 color sensor
+
+maintainers:
+  - Matti Vaittinen <mazziesaccount@gmail.com>
+
+description: |
+  The ROHM BU27010 is a sensor with 6 photodiodes (red, green, blue, clear,
+  IR and flickering detection) with five configurable channels. Red, green
+  and flickering detection being always available and two out of the rest
+  three (blue, clear, IR) can be selected to be simultaneously measured.
+  Typical application is adjusting LCD/OLED backlight of TVs, mobile phones
+  and tablet PCs.
+
+properties:
+  compatible:
+    const: rohm,bu27010
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  vdd-supply: true
+
+required:
+  - compatible
+  - reg
+  - vdd-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      light-sensor@38 {
+        compatible = "rohm,bu27010";
+        reg = <0x38>;
+      };
+    };
diff --git a/Documentation/devicetree/bindings/iio/proximity/murata,irsd200.yaml b/Documentation/devicetree/bindings/iio/proximity/murata,irsd200.yaml
new file mode 100644 (file)
index 0000000..67f5389
--- /dev/null
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/proximity/murata,irsd200.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Murata IRS-D200 PIR sensor
+
+maintainers:
+  - Waqar Hameed <waqar.hameed@axis.com>
+
+description:
+  PIR sensor for human detection.
+
+properties:
+  compatible:
+    const: murata,irsd200
+
+  reg:
+    items:
+      - enum:
+          - 0x48
+          - 0x49
+        description: |
+          When the AD pin is connected to GND, the slave address is 0x48.
+          When the AD pin is connected to VDD, the slave address is 0x49.
+
+  interrupts:
+    maxItems: 1
+    description:
+      Type should be IRQ_TYPE_EDGE_RISING.
+
+  vdd-supply:
+    description:
+      3.3 V supply voltage.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - vdd-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        proximity@48 {
+            compatible = "murata,irsd200";
+            reg = <0x48>;
+            interrupts = <24 IRQ_TYPE_EDGE_RISING>;
+            vdd-supply = <&regulator_3v3>;
+        };
+    };
+...
index 5de0bb2..775555d 100644 (file)
@@ -15,6 +15,9 @@ description: |
   Specifications about the devices can be found at:
   https://www.semtech.com/products/smart-sensing/sar-sensors/sx9310
 
+allOf:
+  - $ref: /schemas/iio/iio.yaml#
+
 properties:
   compatible:
     enum:
@@ -102,7 +105,7 @@ required:
   - reg
   - "#io-channel-cells"
 
-additionalProperties: false
+unevaluatedProperties: false
 
 examples:
   - |
index b3aa2eb..48f2214 100644 (file)
@@ -13,6 +13,9 @@ maintainers:
 description: |
   Semtech's SX9324 proximity sensor.
 
+allOf:
+  - $ref: /schemas/iio/iio.yaml#
+
 properties:
   compatible:
     const: semtech,sx9324
@@ -167,7 +170,7 @@ required:
   - reg
   - "#io-channel-cells"
 
-additionalProperties: false
+unevaluatedProperties: false
 
 examples:
   - |
diff --git a/Documentation/devicetree/bindings/peci/nuvoton,npcm-peci.yaml b/Documentation/devicetree/bindings/peci/nuvoton,npcm-peci.yaml
new file mode 100644 (file)
index 0000000..087e02a
--- /dev/null
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/nuvoton,npcm-peci.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton PECI Bus
+
+maintainers:
+  - Tomer Maimon <tmaimon77@gmail.com>
+
+allOf:
+  - $ref: peci-controller.yaml#
+
+properties:
+  compatible:
+    enum:
+      - nuvoton,npcm750-peci
+      - nuvoton,npcm845-peci
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    description:
+      Clock source for PECI controller. Should reference the APB clock.
+    maxItems: 1
+
+  cmd-timeout-ms:
+    minimum: 1
+    maximum: 1000
+    default: 1000
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    peci-controller@f0100000 {
+      compatible = "nuvoton,npcm750-peci";
+      reg = <0xf0100000 0x200>;
+      interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&clk NPCM7XX_CLK_APB3>;
+      cmd-timeout-ms = <1000>;
+    };
+...
index 0903d87..8ac8975 100644 (file)
@@ -8769,6 +8769,15 @@ S:       Supported
 F:     Documentation/networking/device_drivers/ethernet/google/gve.rst
 F:     drivers/net/ethernet/google
 
+GOOGLE FIRMWARE DRIVERS
+M:     Tzung-Bi Shih <tzungbi@kernel.org>
+R:     Brian Norris <briannorris@chromium.org>
+R:     Julius Werner <jwerner@chromium.org>
+L:     chrome-platform@lists.linux.dev
+S:     Maintained
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git
+F:     drivers/firmware/google/
+
 GPD POCKET FAN DRIVER
 M:     Hans de Goede <hdegoede@redhat.com>
 L:     platform-driver-x86@vger.kernel.org
@@ -13917,12 +13926,14 @@ F:    drivers/nvmem/microchip-otpc.c
 F:     include/dt-bindings/nvmem/microchip,sama7g5-otpc.h
 
 MICROCHIP PCI1XXXX GP DRIVER
+M:     Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com>
 M:     Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
 L:     linux-gpio@vger.kernel.org
 S:     Supported
 F:     drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
 F:     drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h
 F:     drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
+F:     drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
 
 MICROCHIP PCI1XXXX I2C DRIVER
 M:     Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
index c7b5ef1..868454a 100644 (file)
                                };
                        };
 
+                       peci: peci-controller@f0100000 {
+                               compatible = "nuvoton,npcm750-peci";
+                               reg = <0xf0100000 0x200>;
+                               interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clk NPCM7XX_CLK_APB3>;
+                               cmd-timeout-ms = <1000>;
+                               status = "disabled";
+                       };
+
                        spi0: spi@200000 {
                                compatible = "nuvoton,npcm750-pspi";
                                reg = <0x200000 0x1000>;
index aa7aac8..ecd171b 100644 (file)
                        ranges = <0x0 0x0 0xf0000000 0x00300000>,
                                <0xfff00000 0x0 0xfff00000 0x00016000>;
 
+                       peci: peci-controller@100000 {
+                               compatible = "nuvoton,npcm845-peci";
+                               reg = <0x100000 0x1000>;
+                               interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&clk NPCM8XX_CLK_APB3>;
+                               cmd-timeout-ms = <1000>;
+                               status = "disabled";
+                       };
+
                        timer0: timer@8000 {
                                compatible = "nuvoton,npcm845-timer";
                                interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
index c6c15ce..5934ee5 100644 (file)
@@ -239,12 +239,6 @@ extern int (*console_blank_hook)(int);
 #endif
 
 /*
- * The apm_bios device is one of the misc char devices.
- * This is its minor number.
- */
-#define        APM_MINOR_DEV   134
-
-/*
  * Various options can be changed at boot time as follows:
  * (We allow underscores for compatibility with the modules code)
  *     apm=on/off                      enable/disable APM
index d720f93..367afac 100644 (file)
@@ -6557,6 +6557,7 @@ static int __init binder_init(void)
        struct binder_device *device;
        struct hlist_node *tmp;
        char *device_names = NULL;
+       const struct binder_debugfs_entry *db_entry;
 
        ret = binder_alloc_shrinker_init();
        if (ret)
@@ -6566,19 +6567,16 @@ static int __init binder_init(void)
        atomic_set(&binder_transaction_log_failed.cur, ~0U);
 
        binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
-       if (binder_debugfs_dir_entry_root) {
-               const struct binder_debugfs_entry *db_entry;
 
-               binder_for_each_debugfs_entry(db_entry)
-                       debugfs_create_file(db_entry->name,
-                                           db_entry->mode,
-                                           binder_debugfs_dir_entry_root,
-                                           db_entry->data,
-                                           db_entry->fops);
+       binder_for_each_debugfs_entry(db_entry)
+               debugfs_create_file(db_entry->name,
+                                       db_entry->mode,
+                                       binder_debugfs_dir_entry_root,
+                                       db_entry->data,
+                                       db_entry->fops);
 
-               binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
-                                                binder_debugfs_dir_entry_root);
-       }
+       binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
+                                               binder_debugfs_dir_entry_root);
 
        if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) &&
            strcmp(binder_devices_param, "") != 0) {
index 76e7d66..90f497c 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/mutex.h>
 #include <linux/mount.h>
 #include <linux/fs_parser.h>
-#include <linux/radix-tree.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
index d2a19b0..edc0ec5 100644 (file)
@@ -365,12 +365,10 @@ error_alloc_mhi_buf:
 }
 
 static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
-                             const struct firmware *firmware,
+                             const u8 *buf, size_t remainder,
                              struct image_info *img_info)
 {
-       size_t remainder = firmware->size;
        size_t to_cpy;
-       const u8 *buf = firmware->data;
        struct mhi_buf *mhi_buf = img_info->mhi_buf;
        struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
 
@@ -393,9 +391,10 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
        struct device *dev = &mhi_cntrl->mhi_dev->dev;
        enum mhi_pm_state new_state;
        const char *fw_name;
+       const u8 *fw_data;
        void *buf;
        dma_addr_t dma_addr;
-       size_t size;
+       size_t size, fw_sz;
        int i, ret;
 
        if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
@@ -425,6 +424,20 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
        fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
                mhi_cntrl->edl_image : mhi_cntrl->fw_image;
 
+       /* check if the driver has already provided the firmware data */
+       if (!fw_name && mhi_cntrl->fbc_download &&
+           mhi_cntrl->fw_data && mhi_cntrl->fw_sz) {
+               if (!mhi_cntrl->sbl_size) {
+                       dev_err(dev, "fw_data provided but no sbl_size\n");
+                       goto error_fw_load;
+               }
+
+               size = mhi_cntrl->sbl_size;
+               fw_data = mhi_cntrl->fw_data;
+               fw_sz = mhi_cntrl->fw_sz;
+               goto skip_req_fw;
+       }
+
        if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
                                                     !mhi_cntrl->seg_len))) {
                dev_err(dev,
@@ -444,6 +457,10 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
        if (size > firmware->size)
                size = firmware->size;
 
+       fw_data = firmware->data;
+       fw_sz = firmware->size;
+
+skip_req_fw:
        buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, size, &dma_addr,
                                 GFP_KERNEL);
        if (!buf) {
@@ -452,7 +469,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
        }
 
        /* Download image using BHI */
-       memcpy(buf, firmware->data, size);
+       memcpy(buf, fw_data, size);
        ret = mhi_fw_load_bhi(mhi_cntrl, dma_addr, size);
        dma_free_coherent(mhi_cntrl->cntrl_dev, size, buf, dma_addr);
 
@@ -464,7 +481,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
        }
 
        /* Wait for ready since EDL image was loaded */
-       if (fw_name == mhi_cntrl->edl_image) {
+       if (fw_name && fw_name == mhi_cntrl->edl_image) {
                release_firmware(firmware);
                goto fw_load_ready_state;
        }
@@ -478,15 +495,14 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
         * device transitioning into MHI READY state
         */
        if (mhi_cntrl->fbc_download) {
-               ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image,
-                                          firmware->size);
+               ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
                if (ret) {
                        release_firmware(firmware);
                        goto error_fw_load;
                }
 
                /* Load the firmware into BHIE vec table */
-               mhi_firmware_copy(mhi_cntrl, firmware, mhi_cntrl->fbc_image);
+               mhi_firmware_copy(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
        }
 
        release_firmware(firmware);
index f72fcb6..f78aefd 100644 (file)
@@ -759,7 +759,7 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
         * so to avoid any memory possible allocation failures, vzalloc is
         * used here
         */
-       mhi_cntrl->mhi_chan = vzalloc(mhi_cntrl->max_chan *
+       mhi_cntrl->mhi_chan = vcalloc(mhi_cntrl->max_chan,
                                      sizeof(*mhi_cntrl->mhi_chan));
        if (!mhi_cntrl->mhi_chan)
                return -ENOMEM;
index 74a7543..dcf627b 100644 (file)
@@ -938,7 +938,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
                                if (!mhi_chan->configured)
                                        break;
                                parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
-                               event_quota--;
                        }
                        break;
                default:
index db0a0b0..08f3f03 100644 (file)
@@ -212,6 +212,19 @@ struct mhi_pci_dev_info {
                .offload_channel = false,       \
        }
 
+#define MHI_EVENT_CONFIG_SW_DATA(ev_ring, el_count) \
+       {                                       \
+               .num_elements = el_count,       \
+               .irq_moderation_ms = 0,         \
+               .irq = (ev_ring) + 1,           \
+               .priority = 1,                  \
+               .mode = MHI_DB_BRST_DISABLE,    \
+               .data_type = MHI_ER_DATA,       \
+               .hardware_event = false,        \
+               .client_managed = false,        \
+               .offload_channel = false,       \
+       }
+
 #define MHI_EVENT_CONFIG_HW_DATA(ev_ring, el_count, ch_num) \
        {                                       \
                .num_elements = el_count,       \
@@ -237,8 +250,10 @@ static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
        MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(21, "IPCR", 8, 0),
        MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
        MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
-       MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 2),
-       MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 3),
+       MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 64, 2),
+       MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 64, 3),
+       MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 4),
+       MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 5),
 };
 
 static struct mhi_event_config modem_qcom_v1_mhi_events[] = {
@@ -246,9 +261,12 @@ static struct mhi_event_config modem_qcom_v1_mhi_events[] = {
        MHI_EVENT_CONFIG_CTRL(0, 64),
        /* DIAG dedicated event ring */
        MHI_EVENT_CONFIG_DATA(1, 128),
+       /* Software channels dedicated event ring */
+       MHI_EVENT_CONFIG_SW_DATA(2, 64),
+       MHI_EVENT_CONFIG_SW_DATA(3, 64),
        /* Hardware channels request dedicated hardware event rings */
-       MHI_EVENT_CONFIG_HW_DATA(2, 1024, 100),
-       MHI_EVENT_CONFIG_HW_DATA(3, 2048, 101)
+       MHI_EVENT_CONFIG_HW_DATA(4, 1024, 100),
+       MHI_EVENT_CONFIG_HW_DATA(5, 2048, 101)
 };
 
 static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
@@ -334,6 +352,16 @@ static const struct mhi_pci_dev_info mhi_quectel_em1xx_info = {
        .sideband_wake = true,
 };
 
+static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = {
+       .name = "quectel-rm5xx",
+       .edl = "qcom/prog_firehose_sdx6x.elf",
+       .config = &modem_quectel_em1xx_config,
+       .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+       .dma_data_width = 32,
+       .mru_default = 32768,
+       .sideband_wake = true,
+};
+
 static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
        MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
        MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
@@ -567,12 +595,23 @@ static const struct pci_device_id mhi_pci_id_table[] = {
        /* Telit FN990 */
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2010),
                .driver_data = (kernel_ulong_t) &mhi_telit_fn990_info },
+       /* Telit FE990 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015),
+               .driver_data = (kernel_ulong_t) &mhi_telit_fn990_info },
        { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308),
                .driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
        { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1001), /* EM120R-GL (sdx24) */
                .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
        { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1002), /* EM160R-GL (sdx24) */
                .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
+       /* RM520N-GL (sdx6x), eSIM */
+       { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1004),
+               .driver_data = (kernel_ulong_t) &mhi_quectel_rm5xx_info },
+       /* RM520N-GL (sdx6x), Lenovo variant */
+       { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1007),
+               .driver_data = (kernel_ulong_t) &mhi_quectel_rm5xx_info },
+       { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x100d), /* EM160R-GL (sdx24) */
+               .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
        { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x2001), /* EM120R-GL for FCCL (sdx24) */
                .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
        /* T99W175 (sdx55), Both for eSIM and Non-eSIM */
@@ -605,6 +644,12 @@ static const struct pci_device_id mhi_pci_id_table[] = {
        /* T99W510 (sdx24), variant 3 */
        { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f2),
                .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info },
+       /* DW5932e-eSIM (sdx62), With eSIM */
+       { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f5),
+               .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info },
+       /* DW5932e (sdx62), Non-eSIM */
+       { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f9),
+               .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info },
        /* MV31-W (Cinterion) */
        { PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00b3),
                .driver_data = (kernel_ulong_t) &mhi_mv31_info },
index 0834590..8a4362d 100644 (file)
@@ -470,6 +470,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
 
        /* Trigger MHI RESET so that the device will not access host memory */
        if (!MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
+               /* Skip MHI RESET if in RDDM state */
+               if (mhi_cntrl->rddm_image && mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM)
+                       goto skip_mhi_reset;
+
                dev_dbg(dev, "Triggering MHI Reset in device\n");
                mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
 
@@ -495,6 +499,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
                }
        }
 
+skip_mhi_reset:
        dev_dbg(dev,
                 "Waiting for all pending event ring processing to complete\n");
        mhi_event = mhi_cntrl->mhi_event;
index dc52f95..bb4ae79 100644 (file)
@@ -5,7 +5,8 @@
  * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
  */
 
-#include <linux/of_platform.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/cdx/cdx_bus.h>
 
index f37e639..04b578a 100644 (file)
@@ -7,7 +7,8 @@
 
 #include <linux/rpmsg.h>
 #include <linux/remoteproc.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/cdx/cdx_bus.h>
 #include <linux/module.h>
 
index 62de7f4..84411b1 100644 (file)
@@ -3,6 +3,7 @@
  * UniNorth AGPGART routines.
  */
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/init.h>
index 1214385..70d31ae 100644 (file)
@@ -6,11 +6,10 @@
  * Author: Sonny Rao <sonnyrao@us.ibm.com>
  */
 
+#include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
 #include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/cdev.h>
index a8036da..f51d533 100644 (file)
@@ -29,7 +29,7 @@ config XILLYBUS_PCIE
 
 config XILLYBUS_OF
        tristate "Xillybus over Device Tree"
-       depends on OF && HAS_DMA
+       depends on OF && HAS_DMA && HAS_IOMEM
        help
          Set to M if you want Xillybus to find its resources from the
          Open Firmware Flattened Device Tree. If the target is an embedded
index 1bc7cbf..41b78f5 100644 (file)
@@ -59,7 +59,7 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY
 
 config GOOGLE_FRAMEBUFFER_COREBOOT
        tristate "Coreboot Framebuffer"
-       depends on FB_SIMPLE
+       depends on FB_SIMPLE || DRM_SIMPLEDRM
        depends on GOOGLE_COREBOOT_TABLE
        help
          This option enables the kernel to search for a framebuffer in
index e51c95f..fbc5453 100644 (file)
 #define INVALID_RETRY_COUNTER          0xFF
 #define INVALID_DCMF_VERSION           0xFF
 #define INVALID_DCMF_STATUS            0xFFFFFFFF
+#define INVALID_SPT_ADDRESS            0x0
+
+#define RSU_GET_SPT_CMD                        0x5A
+#define RSU_GET_SPT_RESP_LEN           (4 * sizeof(unsigned int))
 
 typedef void (*rsu_callback)(struct stratix10_svc_client *client,
                             struct stratix10_svc_cb_data *data);
@@ -59,6 +63,9 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client,
  * @dcmf_status.dcmf3: dcmf3 status
  * @retry_counter: the current image's retry counter
  * @max_retry: the preset max retry value
+ * @spt0_address: address of spt0
+ * @spt1_address: address of spt1
+ * @get_spt_response_buf: response from sdm for get_spt command
  */
 struct stratix10_rsu_priv {
        struct stratix10_svc_chan *chan;
@@ -90,6 +97,11 @@ struct stratix10_rsu_priv {
 
        unsigned int retry_counter;
        unsigned int max_retry;
+
+       unsigned long spt0_address;
+       unsigned long spt1_address;
+
+       unsigned int *get_spt_response_buf;
 };
 
 /**
@@ -259,6 +271,36 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client,
        complete(&priv->completion);
 }
 
+static void rsu_get_spt_callback(struct stratix10_svc_client *client,
+                                struct stratix10_svc_cb_data *data)
+{
+       struct stratix10_rsu_priv *priv = client->priv;
+       unsigned long *mbox_err = (unsigned long *)data->kaddr1;
+       unsigned long *resp_len = (unsigned long *)data->kaddr2;
+
+       if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) ||
+           (*resp_len != RSU_GET_SPT_RESP_LEN))
+               goto error;
+
+       priv->spt0_address = priv->get_spt_response_buf[0];
+       priv->spt0_address <<= 32;
+       priv->spt0_address |= priv->get_spt_response_buf[1];
+
+       priv->spt1_address = priv->get_spt_response_buf[2];
+       priv->spt1_address <<= 32;
+       priv->spt1_address |= priv->get_spt_response_buf[3];
+
+       goto complete;
+
+error:
+       dev_err(client->dev, "failed to get SPTs\n");
+
+complete:
+       stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf);
+       priv->get_spt_response_buf = NULL;
+       complete(&priv->completion);
+}
+
 /**
  * rsu_send_msg() - send a message to Intel service layer
  * @priv: pointer to rsu private data
@@ -288,6 +330,14 @@ static int rsu_send_msg(struct stratix10_rsu_priv *priv,
        if (arg)
                msg.arg[0] = arg;
 
+       if (command == COMMAND_MBOX_SEND_CMD) {
+               msg.arg[1] = 0;
+               msg.payload = NULL;
+               msg.payload_length = 0;
+               msg.payload_output = priv->get_spt_response_buf;
+               msg.payload_length_output = RSU_GET_SPT_RESP_LEN;
+       }
+
        ret = stratix10_svc_send(priv->chan, &msg);
        if (ret < 0)
                goto status_done;
@@ -572,6 +622,34 @@ static ssize_t notify_store(struct device *dev,
        return count;
 }
 
+static ssize_t spt0_address_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
+
+       if (!priv)
+               return -ENODEV;
+
+       if (priv->spt0_address == INVALID_SPT_ADDRESS)
+               return -EIO;
+
+       return scnprintf(buf, PAGE_SIZE, "0x%08lx\n", priv->spt0_address);
+}
+
+static ssize_t spt1_address_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
+
+       if (!priv)
+               return -ENODEV;
+
+       if (priv->spt1_address == INVALID_SPT_ADDRESS)
+               return -EIO;
+
+       return scnprintf(buf, PAGE_SIZE, "0x%08lx\n", priv->spt1_address);
+}
+
 static DEVICE_ATTR_RO(current_image);
 static DEVICE_ATTR_RO(fail_image);
 static DEVICE_ATTR_RO(state);
@@ -590,6 +668,8 @@ static DEVICE_ATTR_RO(dcmf2_status);
 static DEVICE_ATTR_RO(dcmf3_status);
 static DEVICE_ATTR_WO(reboot_image);
 static DEVICE_ATTR_WO(notify);
+static DEVICE_ATTR_RO(spt0_address);
+static DEVICE_ATTR_RO(spt1_address);
 
 static struct attribute *rsu_attrs[] = {
        &dev_attr_current_image.attr,
@@ -610,6 +690,8 @@ static struct attribute *rsu_attrs[] = {
        &dev_attr_dcmf3_status.attr,
        &dev_attr_reboot_image.attr,
        &dev_attr_notify.attr,
+       &dev_attr_spt0_address.attr,
+       &dev_attr_spt1_address.attr,
        NULL
 };
 
@@ -639,11 +721,13 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
        priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION;
        priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION;
        priv->dcmf_version.dcmf3 = INVALID_DCMF_VERSION;
-       priv->max_retry = INVALID_RETRY_COUNTER;
        priv->dcmf_status.dcmf0 = INVALID_DCMF_STATUS;
        priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS;
        priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS;
        priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS;
+       priv->max_retry = INVALID_RETRY_COUNTER;
+       priv->spt0_address = INVALID_SPT_ADDRESS;
+       priv->spt1_address = INVALID_SPT_ADDRESS;
 
        mutex_init(&priv->lock);
        priv->chan = stratix10_svc_request_channel_byname(&priv->client,
@@ -693,6 +777,20 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
                stratix10_svc_free_channel(priv->chan);
        }
 
+       priv->get_spt_response_buf =
+               stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN);
+
+       if (IS_ERR(priv->get_spt_response_buf)) {
+               dev_err(dev, "failed to allocate get spt buffer\n");
+       } else {
+               ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD,
+                                  RSU_GET_SPT_CMD, rsu_get_spt_callback);
+               if (ret) {
+                       dev_err(dev, "Error, getting SPT table %i\n", ret);
+                       stratix10_svc_free_channel(priv->chan);
+               }
+       }
+
        return ret;
 }
 
index 2d67412..c693da6 100644 (file)
@@ -37,6 +37,7 @@
 #define SVC_NUM_CHANNEL                                3
 #define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS      200
 #define FPGA_CONFIG_STATUS_TIMEOUT_SEC         30
+#define BYTE_TO_WORD_SIZE              4
 
 /* stratix10 service layer clients */
 #define STRATIX10_RSU                          "stratix10-rsu"
@@ -361,6 +362,13 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
                cb_data->kaddr2 = svc_pa_to_va(res.a2);
                cb_data->kaddr3 = &res.a3;
                break;
+       case COMMAND_MBOX_SEND_CMD:
+               cb_data->status = BIT(SVC_STATUS_OK);
+               cb_data->kaddr1 = &res.a1;
+               /* SDM return size in u8. Convert size to u32 word */
+               res.a2 = res.a2 * BYTE_TO_WORD_SIZE;
+               cb_data->kaddr2 = &res.a2;
+               break;
        default:
                pr_warn("it shouldn't happen\n");
                break;
@@ -534,6 +542,15 @@ static int svc_normal_to_secure_thread(void *data)
                        a1 = 0;
                        a2 = 0;
                        break;
+               case COMMAND_MBOX_SEND_CMD:
+                       a0 = INTEL_SIP_SMC_MBOX_SEND_CMD;
+                       a1 = pdata->arg[0];
+                       a2 = (unsigned long)pdata->paddr;
+                       a3 = (unsigned long)pdata->size / BYTE_TO_WORD_SIZE;
+                       a4 = pdata->arg[1];
+                       a5 = (unsigned long)pdata->paddr_output;
+                       a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE;
+                       break;
                default:
                        pr_warn("it shouldn't happen\n");
                        break;
@@ -597,6 +614,7 @@ static int svc_normal_to_secure_thread(void *data)
                        case COMMAND_FCS_DATA_ENCRYPTION:
                        case COMMAND_FCS_DATA_DECRYPTION:
                        case COMMAND_FCS_RANDOM_NUMBER_GEN:
+                       case COMMAND_MBOX_SEND_CMD:
                                cbdata->status = BIT(SVC_STATUS_INVALID_PARAM);
                                cbdata->kaddr1 = NULL;
                                cbdata->kaddr2 = NULL;
@@ -756,7 +774,7 @@ svc_create_memory_pool(struct platform_device *pdev,
        paddr = begin;
        size = end - begin;
        va = devm_memremap(dev, paddr, size, MEMREMAP_WC);
-       if (!va) {
+       if (IS_ERR(va)) {
                dev_err(dev, "fail to remap shared memory\n");
                return ERR_PTR(-EINVAL);
        }
index e6668a8..79a3159 100644 (file)
@@ -62,6 +62,15 @@ config FSI_MASTER_ASPEED
 
         Enable it for your BMC kernel in an OpenPower or IBM Power system.
 
+config FSI_MASTER_I2CR
+       tristate "IBM I2C Responder virtual FSI master"
+       depends on I2C
+       help
+         This option enables a virtual FSI master in order to access a CFAM
+         behind an IBM I2C Responder (I2CR) chip. The I2CR is an I2C device
+         that translates I2C commands to CFAM or SCOM operations, effectively
+         implementing an FSI master and bus.
+
 config FSI_SCOM
        tristate "SCOM FSI client device driver"
        help
@@ -85,4 +94,12 @@ config FSI_OCC
        provide the raw sensor data as well as perform thermal and power
        management on the system.
 
+config I2CR_SCOM
+       tristate "IBM I2C Responder SCOM driver"
+       depends on FSI_MASTER_I2CR
+       help
+         This option enables an I2C Responder based SCOM device driver. The
+         I2CR has the capability to directly perform SCOM operations instead
+         of using the FSI2PIB engine.
+
 endif
index da218a1..5550aa1 100644 (file)
@@ -4,7 +4,9 @@ obj-$(CONFIG_FSI) += fsi-core.o
 obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
 obj-$(CONFIG_FSI_MASTER_ASPEED) += fsi-master-aspeed.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_MASTER_I2CR) += fsi-master-i2cr.o
 obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
 obj-$(CONFIG_FSI_OCC) += fsi-occ.o
+obj-$(CONFIG_I2CR_SCOM) += i2cr-scom.o
index 0b927c9..097d5a7 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/idr.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/slab.h>
 #include <linux/bitops.h>
 #include <linux/cdev.h>
 #include <linux/uaccess.h>
 
 #include "fsi-master.h"
+#include "fsi-slave.h"
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi.h>
 
 #define FSI_SLAVE_CONF_NEXT_MASK       GENMASK(31, 31)
 #define FSI_SLAVE_CONF_SLOTS_MASK      GENMASK(23, 16)
@@ -78,26 +84,6 @@ static const int engine_page_size = 0x400;
 
 static DEFINE_IDA(master_ida);
 
-struct fsi_slave {
-       struct device           dev;
-       struct fsi_master       *master;
-       struct cdev             cdev;
-       int                     cdev_idx;
-       int                     id;     /* FSI address */
-       int                     link;   /* FSI link# */
-       u32                     cfam_id;
-       int                     chip_id;
-       uint32_t                size;   /* size of slave address space */
-       u8                      t_send_delay;
-       u8                      t_echo_delay;
-};
-
-#define CREATE_TRACE_POINTS
-#include <trace/events/fsi.h>
-
-#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
-#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
-
 static const int slave_retries = 2;
 static int discard_errors;
 
@@ -415,28 +401,18 @@ EXPORT_SYMBOL_GPL(fsi_slave_release_range);
 static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
                uint32_t addr, uint32_t size)
 {
-       unsigned int len, na, ns;
-       const __be32 *prop;
-       uint32_t psize;
-
-       na = of_n_addr_cells(np);
-       ns = of_n_size_cells(np);
-
-       if (na != 1 || ns != 1)
-               return false;
+       u64 paddr, psize;
 
-       prop = of_get_property(np, "reg", &len);
-       if (!prop || len != 8)
+       if (of_property_read_reg(np, 0, &paddr, &psize))
                return false;
 
-       if (of_read_number(prop, 1) != addr)
+       if (paddr != addr)
                return false;
 
-       psize = of_read_number(prop + 1, 1);
        if (psize != size) {
                dev_warn(dev,
-                       "node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
-                       of_node_full_name(np), psize, size);
+                       "node %pOF matches probed address, but not size (got 0x%llx, expected 0x%x)",
+                       np, psize, size);
        }
 
        return true;
@@ -653,24 +629,12 @@ static void fsi_slave_release(struct device *dev)
 static bool fsi_slave_node_matches(struct device_node *np,
                int link, uint8_t id)
 {
-       unsigned int len, na, ns;
-       const __be32 *prop;
-
-       na = of_n_addr_cells(np);
-       ns = of_n_size_cells(np);
-
-       /* Ensure we have the correct format for addresses and sizes in
-        * reg properties
-        */
-       if (na != 2 || ns != 0)
-               return false;
+       u64 addr;
 
-       prop = of_get_property(np, "reg", &len);
-       if (!prop || len != 8)
+       if (of_property_read_reg(np, 0, &addr, NULL))
                return false;
 
-       return (of_read_number(prop, 1) == link) &&
-               (of_read_number(prop + 1, 1) == id);
+       return addr == (((u64)link << 32) | id);
 }
 
 /* Find a matching node for the slave at (link, id). Returns NULL if none
@@ -949,9 +913,13 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
 
        /* Check if we qualify for legacy numbering */
        if (cid >= 0 && cid < 16 && type < 4) {
-               /* Try reserving the legacy number */
-               id = (cid << 4) | type;
-               id = ida_simple_get(&fsi_minor_ida, id, id + 1, GFP_KERNEL);
+               /*
+                * Try reserving the legacy number, which has 0 - 0x3f reserved
+                * in the ida range. cid goes up to 0xf and type contains two
+                * bits, so construct the id with the below two bit shift.
+                */
+               id = (cid << 2) | type;
+               id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
                if (id >= 0) {
                        *out_index = fsi_adjust_index(cid);
                        *out_dev = fsi_base_dev + id;
@@ -962,8 +930,8 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
                        return id;
                /* Fallback to non-legacy allocation */
        }
-       id = ida_simple_get(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
-                           FSI_CHAR_MAX_DEVICES, GFP_KERNEL);
+       id = ida_alloc_range(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
+                            FSI_CHAR_MAX_DEVICES - 1, GFP_KERNEL);
        if (id < 0)
                return id;
        *out_index = fsi_adjust_index(id);
@@ -971,16 +939,42 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
        return 0;
 }
 
+static const char *const fsi_dev_type_names[] = {
+       "cfam",
+       "sbefifo",
+       "scom",
+       "occ",
+};
+
 int fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type,
                      dev_t *out_dev, int *out_index)
 {
+       if (fdev->dev.of_node) {
+               int aid = of_alias_get_id(fdev->dev.of_node, fsi_dev_type_names[type]);
+
+               if (aid >= 0) {
+                       /* Use the same scheme as the legacy numbers. */
+                       int id = (aid << 2) | type;
+
+                       id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
+                       if (id >= 0) {
+                               *out_index = aid;
+                               *out_dev = fsi_base_dev + id;
+                               return 0;
+                       }
+
+                       if (id != -ENOSPC)
+                               return id;
+               }
+       }
+
        return __fsi_get_new_minor(fdev->slave, type, out_dev, out_index);
 }
 EXPORT_SYMBOL_GPL(fsi_get_new_minor);
 
 void fsi_free_minor(dev_t dev)
 {
-       ida_simple_remove(&fsi_minor_ida, MINOR(dev));
+       ida_free(&fsi_minor_ida, MINOR(dev));
 }
 EXPORT_SYMBOL_GPL(fsi_free_minor);
 
@@ -1210,6 +1204,7 @@ static int fsi_master_scan(struct fsi_master *master)
 {
        int link, rc;
 
+       trace_fsi_master_scan(master, true);
        for (link = 0; link < master->n_links; link++) {
                rc = fsi_master_link_enable(master, link);
                if (rc) {
@@ -1251,6 +1246,7 @@ static int fsi_master_remove_slave(struct device *dev, void *arg)
 
 static void fsi_master_unscan(struct fsi_master *master)
 {
+       trace_fsi_master_scan(master, false);
        device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
 }
 
@@ -1313,41 +1309,53 @@ int fsi_master_register(struct fsi_master *master)
        struct device_node *np;
 
        mutex_init(&master->scan_lock);
-       master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
+
+       /* Alloc the requested index if it's non-zero */
+       if (master->idx) {
+               master->idx = ida_alloc_range(&master_ida, master->idx,
+                                             master->idx, GFP_KERNEL);
+       } else {
+               master->idx = ida_alloc(&master_ida, GFP_KERNEL);
+       }
+
        if (master->idx < 0)
                return master->idx;
 
-       dev_set_name(&master->dev, "fsi%d", master->idx);
+       if (!dev_name(&master->dev))
+               dev_set_name(&master->dev, "fsi%d", master->idx);
+
        master->dev.class = &fsi_master_class;
 
+       mutex_lock(&master->scan_lock);
        rc = device_register(&master->dev);
        if (rc) {
-               ida_simple_remove(&master_ida, master->idx);
-               return rc;
+               ida_free(&master_ida, master->idx);
+               goto out;
        }
 
        np = dev_of_node(&master->dev);
        if (!of_property_read_bool(np, "no-scan-on-init")) {
-               mutex_lock(&master->scan_lock);
                fsi_master_scan(master);
-               mutex_unlock(&master->scan_lock);
        }
-
-       return 0;
+out:
+       mutex_unlock(&master->scan_lock);
+       return rc;
 }
 EXPORT_SYMBOL_GPL(fsi_master_register);
 
 void fsi_master_unregister(struct fsi_master *master)
 {
-       if (master->idx >= 0) {
-               ida_simple_remove(&master_ida, master->idx);
-               master->idx = -1;
-       }
+       int idx = master->idx;
+
+       trace_fsi_master_unregister(master);
 
        mutex_lock(&master->scan_lock);
        fsi_master_unscan(master);
+       master->n_links = 0;
        mutex_unlock(&master->scan_lock);
+
        device_unregister(&master->dev);
+       ida_free(&master_ida, idx);
 }
 EXPORT_SYMBOL_GPL(fsi_master_unregister);
 
@@ -1366,8 +1374,14 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
                if (id->engine_type != fsi_dev->engine_type)
                        continue;
                if (id->version == FSI_VERSION_ANY ||
-                               id->version == fsi_dev->version)
-                       return 1;
+                   id->version == fsi_dev->version) {
+                       if (drv->of_match_table) {
+                               if (of_driver_match_device(dev, drv))
+                                       return 1;
+                       } else {
+                               return 1;
+                       }
+               }
        }
 
        return 0;
index 7cec177..f0a19cd 100644 (file)
@@ -376,7 +376,7 @@ static int aspeed_master_break(struct fsi_master *master, int link)
 static void aspeed_master_release(struct device *dev)
 {
        struct fsi_master_aspeed *aspeed =
-               to_fsi_master_aspeed(dev_to_fsi_master(dev));
+               to_fsi_master_aspeed(to_fsi_master(dev));
 
        kfree(aspeed);
 }
@@ -454,6 +454,8 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att
        gpiod_set_value(aspeed->cfam_reset_gpio, 1);
        usleep_range(900, 1000);
        gpiod_set_value(aspeed->cfam_reset_gpio, 0);
+       usleep_range(900, 1000);
+       opb_writel(aspeed, ctrl_base + FSI_MRESP0, cpu_to_be32(FSI_MRESP_RST_ALL_MASTER));
        mutex_unlock(&aspeed->lock);
        trace_fsi_master_aspeed_cfam_reset(false);
 
index 5f608ef..812dfa9 100644 (file)
@@ -1133,7 +1133,7 @@ static int fsi_master_acf_gpio_request(void *data)
 
        /* Note: This doesn't require holding out mutex */
 
-       /* Write reqest */
+       /* Write request */
        iowrite8(ARB_ARM_REQ, master->sram + ARB_REG);
 
        /*
@@ -1190,7 +1190,7 @@ static int fsi_master_acf_gpio_release(void *data)
 
 static void fsi_master_acf_release(struct device *dev)
 {
-       struct fsi_master_acf *master = to_fsi_master_acf(dev_to_fsi_master(dev));
+       struct fsi_master_acf *master = to_fsi_master_acf(to_fsi_master(dev));
 
        /* Cleanup, stop coprocessor */
        mutex_lock(&master->lock);
@@ -1441,3 +1441,4 @@ static struct platform_driver fsi_master_acf = {
 
 module_platform_driver(fsi_master_acf);
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FW_FILE_NAME);
index 7d5f29b..ed03da4 100644 (file)
@@ -761,7 +761,7 @@ static DEVICE_ATTR(external_mode, 0664,
 
 static void fsi_master_gpio_release(struct device *dev)
 {
-       struct fsi_master_gpio *master = to_fsi_master_gpio(dev_to_fsi_master(dev));
+       struct fsi_master_gpio *master = to_fsi_master_gpio(to_fsi_master(dev));
 
        of_node_put(dev_of_node(master->dev));
 
index 01f0a79..6d8b6e8 100644 (file)
@@ -105,7 +105,7 @@ static int hub_master_link_enable(struct fsi_master *master, int link,
 
 static void hub_master_release(struct device *dev)
 {
-       struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev));
+       struct fsi_master_hub *hub = to_fsi_master_hub(to_fsi_master(dev));
 
        kfree(hub);
 }
diff --git a/drivers/fsi/fsi-master-i2cr.c b/drivers/fsi/fsi-master-i2cr.c
new file mode 100644 (file)
index 0000000..61659c2
--- /dev/null
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) IBM Corporation 2023 */
+
+#include <linux/device.h>
+#include <linux/fsi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+
+#include "fsi-master-i2cr.h"
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi_master_i2cr.h>
+
+#define I2CR_ADDRESS_CFAM(a)   ((a) >> 2)
+#define I2CR_INITIAL_PARITY    true
+
+#define I2CR_STATUS_CMD                0x60002
+#define  I2CR_STATUS_ERR        BIT_ULL(61)
+#define I2CR_ERROR_CMD         0x60004
+#define I2CR_LOG_CMD           0x60008
+
+static const u8 i2cr_cfam[] = {
+       0xc0, 0x02, 0x0d, 0xa6,
+       0x80, 0x01, 0x10, 0x02,
+       0x80, 0x01, 0x10, 0x02,
+       0x80, 0x01, 0x10, 0x02,
+       0x80, 0x01, 0x80, 0x52,
+       0x80, 0x01, 0x10, 0x02,
+       0x80, 0x01, 0x10, 0x02,
+       0x80, 0x01, 0x10, 0x02,
+       0x80, 0x01, 0x10, 0x02,
+       0x80, 0x01, 0x22, 0x2d,
+       0x00, 0x00, 0x00, 0x00,
+       0xde, 0xad, 0xc0, 0xde
+};
+
+static bool i2cr_check_parity32(u32 v, bool parity)
+{
+       u32 i;
+
+       for (i = 0; i < 32; ++i) {
+               if (v & (1u << i))
+                       parity = !parity;
+       }
+
+       return parity;
+}
+
+static bool i2cr_check_parity64(u64 v)
+{
+       u32 i;
+       bool parity = I2CR_INITIAL_PARITY;
+
+       for (i = 0; i < 64; ++i) {
+               if (v & (1llu << i))
+                       parity = !parity;
+       }
+
+       return parity;
+}
+
+static u32 i2cr_get_command(u32 address, bool parity)
+{
+       address <<= 1;
+
+       if (i2cr_check_parity32(address, parity))
+               address |= 1;
+
+       return address;
+}
+
+static int i2cr_transfer(struct i2c_client *client, u32 command, u64 *data)
+{
+       struct i2c_msg msgs[2];
+       int ret;
+
+       msgs[0].addr = client->addr;
+       msgs[0].flags = 0;
+       msgs[0].len = sizeof(command);
+       msgs[0].buf = (__u8 *)&command;
+       msgs[1].addr = client->addr;
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].len = sizeof(*data);
+       msgs[1].buf = (__u8 *)data;
+
+       ret = i2c_transfer(client->adapter, msgs, 2);
+       if (ret == 2)
+               return 0;
+
+       trace_i2cr_i2c_error(client, command, ret);
+
+       if (ret < 0)
+               return ret;
+
+       return -EIO;
+}
+
+static int i2cr_check_status(struct i2c_client *client)
+{
+       u64 status;
+       int ret;
+
+       ret = i2cr_transfer(client, I2CR_STATUS_CMD, &status);
+       if (ret)
+               return ret;
+
+       if (status & I2CR_STATUS_ERR) {
+               u32 buf[3] = { 0, 0, 0 };
+               u64 error;
+               u64 log;
+
+               i2cr_transfer(client, I2CR_ERROR_CMD, &error);
+               i2cr_transfer(client, I2CR_LOG_CMD, &log);
+
+               trace_i2cr_status_error(client, status, error, log);
+
+               buf[0] = I2CR_STATUS_CMD;
+               i2c_master_send(client, (const char *)buf, sizeof(buf));
+
+               buf[0] = I2CR_ERROR_CMD;
+               i2c_master_send(client, (const char *)buf, sizeof(buf));
+
+               buf[0] = I2CR_LOG_CMD;
+               i2c_master_send(client, (const char *)buf, sizeof(buf));
+
+               dev_err(&client->dev, "status:%016llx error:%016llx log:%016llx\n", status, error,
+                       log);
+               return -EREMOTEIO;
+       }
+
+       trace_i2cr_status(client, status);
+       return 0;
+}
+
+int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data)
+{
+       u32 command = i2cr_get_command(addr, I2CR_INITIAL_PARITY);
+       int ret;
+
+       mutex_lock(&i2cr->lock);
+
+       ret = i2cr_transfer(i2cr->client, command, data);
+       if (ret)
+               goto unlock;
+
+       ret = i2cr_check_status(i2cr->client);
+       if (ret)
+               goto unlock;
+
+       trace_i2cr_read(i2cr->client, command, data);
+
+unlock:
+       mutex_unlock(&i2cr->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(fsi_master_i2cr_read);
+
+int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data)
+{
+       u32 buf[3] = { 0 };
+       int ret;
+
+       buf[0] = i2cr_get_command(addr, i2cr_check_parity64(data));
+       memcpy(&buf[1], &data, sizeof(data));
+
+       mutex_lock(&i2cr->lock);
+
+       ret = i2c_master_send(i2cr->client, (const char *)buf, sizeof(buf));
+       if (ret == sizeof(buf)) {
+               ret = i2cr_check_status(i2cr->client);
+               if (!ret)
+                       trace_i2cr_write(i2cr->client, buf[0], data);
+       } else {
+               trace_i2cr_i2c_error(i2cr->client, buf[0], ret);
+
+               if (ret >= 0)
+                       ret = -EIO;
+       }
+
+       mutex_unlock(&i2cr->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(fsi_master_i2cr_write);
+
+static int i2cr_read(struct fsi_master *master, int link, uint8_t id, uint32_t addr, void *val,
+                    size_t size)
+{
+       struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
+       u64 data;
+       size_t i;
+       int ret;
+
+       if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
+               return -EINVAL;
+
+       /*
+        * The I2CR doesn't have CFAM or FSI slave address space - only the
+        * engines. In order for this to work with the FSI core, we need to
+        * emulate at minimum the CFAM config table so that the appropriate
+        * engines are discovered.
+        */
+       if (addr < 0xc00) {
+               if (addr > sizeof(i2cr_cfam) - 4)
+                       addr = (addr & 0x3) + (sizeof(i2cr_cfam) - 4);
+
+               memcpy(val, &i2cr_cfam[addr], size);
+               return 0;
+       }
+
+       ret = fsi_master_i2cr_read(i2cr, I2CR_ADDRESS_CFAM(addr), &data);
+       if (ret)
+               return ret;
+
+       /*
+        * FSI core expects up to 4 bytes BE back, while I2CR replied with LE
+        * bytes on the wire.
+        */
+       for (i = 0; i < size; ++i)
+               ((u8 *)val)[i] = ((u8 *)&data)[7 - i];
+
+       return 0;
+}
+
+static int i2cr_write(struct fsi_master *master, int link, uint8_t id, uint32_t addr,
+                     const void *val, size_t size)
+{
+       struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
+       u64 data = 0;
+       size_t i;
+
+       if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
+               return -EINVAL;
+
+       /* I2CR writes to CFAM or FSI slave address are a successful no-op. */
+       if (addr < 0xc00)
+               return 0;
+
+       /*
+        * FSI core passes up to 4 bytes BE, while the I2CR expects LE bytes on
+        * the wire.
+        */
+       for (i = 0; i < size; ++i)
+               ((u8 *)&data)[7 - i] = ((u8 *)val)[i];
+
+       return fsi_master_i2cr_write(i2cr, I2CR_ADDRESS_CFAM(addr), data);
+}
+
+static void i2cr_release(struct device *dev)
+{
+       struct fsi_master_i2cr *i2cr = to_fsi_master_i2cr(to_fsi_master(dev));
+
+       of_node_put(dev->of_node);
+
+       kfree(i2cr);
+}
+
+static int i2cr_probe(struct i2c_client *client)
+{
+       struct fsi_master_i2cr *i2cr;
+       int ret;
+
+       i2cr = kzalloc(sizeof(*i2cr), GFP_KERNEL);
+       if (!i2cr)
+               return -ENOMEM;
+
+       /* Only one I2CR on any given I2C bus (fixed I2C device address) */
+       i2cr->master.idx = client->adapter->nr;
+       dev_set_name(&i2cr->master.dev, "i2cr%d", i2cr->master.idx);
+       i2cr->master.dev.parent = &client->dev;
+       i2cr->master.dev.of_node = of_node_get(dev_of_node(&client->dev));
+       i2cr->master.dev.release = i2cr_release;
+
+       i2cr->master.n_links = 1;
+       i2cr->master.read = i2cr_read;
+       i2cr->master.write = i2cr_write;
+
+       mutex_init(&i2cr->lock);
+       i2cr->client = client;
+
+       ret = fsi_master_register(&i2cr->master);
+       if (ret)
+               return ret;
+
+       i2c_set_clientdata(client, i2cr);
+       return 0;
+}
+
+static void i2cr_remove(struct i2c_client *client)
+{
+       struct fsi_master_i2cr *i2cr = i2c_get_clientdata(client);
+
+       fsi_master_unregister(&i2cr->master);
+}
+
+static const struct of_device_id i2cr_ids[] = {
+       { .compatible = "ibm,i2cr-fsi-master" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, i2cr_ids);
+
+static struct i2c_driver i2cr_driver = {
+       .probe_new = i2cr_probe,
+       .remove = i2cr_remove,
+       .driver = {
+               .name = "fsi-master-i2cr",
+               .of_match_table = i2cr_ids,
+       },
+};
+
+module_i2c_driver(i2cr_driver)
+
+MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
+MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/fsi/fsi-master-i2cr.h b/drivers/fsi/fsi-master-i2cr.h
new file mode 100644 (file)
index 0000000..96636bf
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) IBM Corporation 2023 */
+
+#ifndef DRIVERS_FSI_MASTER_I2CR_H
+#define DRIVERS_FSI_MASTER_I2CR_H
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+#include "fsi-master.h"
+
+struct i2c_client;
+
+struct fsi_master_i2cr {
+       struct fsi_master master;
+       struct mutex lock;      /* protect HW access */
+       struct i2c_client *client;
+};
+
+#define to_fsi_master_i2cr(m)  container_of(m, struct fsi_master_i2cr, master)
+
+int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data);
+int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data);
+
+static inline bool is_fsi_master_i2cr(struct fsi_master *master)
+{
+       if (master->dev.parent && master->dev.parent->type == &i2c_client_type)
+               return true;
+
+       return false;
+}
+
+#endif /* DRIVERS_FSI_MASTER_I2CR_H */
index 4762315..967622c 100644 (file)
@@ -136,7 +136,7 @@ struct fsi_master {
                                       u8 t_send_delay, u8 t_echo_delay);
 };
 
-#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
+#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
 
 /**
  * fsi_master registration & lifetime: the fsi_master_register() and
index abdd37d..da35ca9 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/mutex.h>
 #include <linux/fsi-occ.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
index 9912b7a..0a98517 100644 (file)
@@ -22,8 +22,8 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
@@ -81,7 +81,7 @@
 
 enum sbe_state
 {
-       SBE_STATE_UNKNOWN = 0x0, // Unkown, initial state
+       SBE_STATE_UNKNOWN = 0x0, // Unknown, initial state
        SBE_STATE_IPLING  = 0x1, // IPL'ing - autonomous mode (transient)
        SBE_STATE_ISTEP   = 0x2, // ISTEP - Running IPL by steps (transient)
        SBE_STATE_MPIPL   = 0x3, // MPIPL
@@ -127,6 +127,7 @@ struct sbefifo {
        bool                    dead;
        bool                    async_ffdc;
        bool                    timed_out;
+       u32                     timeout_in_cmd_ms;
        u32                     timeout_start_rsp_ms;
 };
 
@@ -136,6 +137,7 @@ struct sbefifo_user {
        void                    *cmd_page;
        void                    *pending_cmd;
        size_t                  pending_len;
+       u32                     cmd_timeout_ms;
        u32                     read_timeout_ms;
 };
 
@@ -508,7 +510,7 @@ static int sbefifo_send_command(struct sbefifo *sbefifo,
                rc = sbefifo_wait(sbefifo, true, &status, timeout);
                if (rc < 0)
                        return rc;
-               timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_CMD);
+               timeout = msecs_to_jiffies(sbefifo->timeout_in_cmd_ms);
 
                vacant = sbefifo_vacant(status);
                len = chunk = min(vacant, remaining);
@@ -730,7 +732,7 @@ static int __sbefifo_submit(struct sbefifo *sbefifo,
  * @response: The output response buffer
  * @resp_len: In: Response buffer size, Out: Response size
  *
- * This will perform the entire operation. If the reponse buffer
+ * This will perform the entire operation. If the response buffer
  * overflows, returns -EOVERFLOW
  */
 int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
@@ -802,6 +804,7 @@ static int sbefifo_user_open(struct inode *inode, struct file *file)
                return -ENOMEM;
        }
        mutex_init(&user->file_lock);
+       user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD;
        user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
 
        return 0;
@@ -845,9 +848,11 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
        rc = mutex_lock_interruptible(&sbefifo->lock);
        if (rc)
                goto bail;
+       sbefifo->timeout_in_cmd_ms = user->cmd_timeout_ms;
        sbefifo->timeout_start_rsp_ms = user->read_timeout_ms;
        rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
        sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
+       sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD;
        mutex_unlock(&sbefifo->lock);
        if (rc < 0)
                goto bail;
@@ -937,7 +942,7 @@ static int sbefifo_user_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
+static int sbefifo_cmd_timeout(struct sbefifo_user *user, void __user *argp)
 {
        struct device *dev = &user->sbefifo->dev;
        u32 timeout;
@@ -946,18 +951,32 @@ static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
                return -EFAULT;
 
        if (timeout == 0) {
-               user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
-               dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms);
+               user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD;
+               dev_dbg(dev, "Command timeout reset to %us\n", user->cmd_timeout_ms / 1000);
                return 0;
        }
 
-       if (timeout < 10 || timeout > 120)
-               return -EINVAL;
+       user->cmd_timeout_ms = timeout * 1000; /* user timeout is in sec */
+       dev_dbg(dev, "Command timeout set to %us\n", timeout);
+       return 0;
+}
 
-       user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
+static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
+{
+       struct device *dev = &user->sbefifo->dev;
+       u32 timeout;
 
-       dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms);
+       if (get_user(timeout, (__u32 __user *)argp))
+               return -EFAULT;
 
+       if (timeout == 0) {
+               user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
+               dev_dbg(dev, "Timeout reset to %us\n", user->read_timeout_ms / 1000);
+               return 0;
+       }
+
+       user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
+       dev_dbg(dev, "Timeout set to %us\n", timeout);
        return 0;
 }
 
@@ -971,6 +990,9 @@ static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned lon
 
        mutex_lock(&user->file_lock);
        switch (cmd) {
+       case FSI_SBEFIFO_CMD_TIMEOUT_SECONDS:
+               rc = sbefifo_cmd_timeout(user, (void __user *)arg);
+               break;
        case FSI_SBEFIFO_READ_TIMEOUT_SECONDS:
                rc = sbefifo_read_timeout(user, (void __user *)arg);
                break;
@@ -1025,16 +1047,9 @@ static int sbefifo_probe(struct device *dev)
        sbefifo->fsi_dev = fsi_dev;
        dev_set_drvdata(dev, sbefifo);
        mutex_init(&sbefifo->lock);
+       sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD;
        sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
 
-       /*
-        * Try cleaning up the FIFO. If this fails, we still register the
-        * driver and will try cleaning things up again on the next access.
-        */
-       rc = sbefifo_cleanup_hw(sbefifo);
-       if (rc && rc != -ESHUTDOWN)
-               dev_err(dev, "Initial HW cleanup failed, will retry later\n");
-
        /* Create chardev for userspace access */
        sbefifo->dev.type = &fsi_cdev_type;
        sbefifo->dev.parent = dev;
index bcb756d..61dbda9 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/cdev.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
+#include <linux/mod_devicetable.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/list.h>
@@ -587,6 +588,12 @@ static int scom_remove(struct device *dev)
        return 0;
 }
 
+static const struct of_device_id scom_of_ids[] = {
+       { .compatible = "ibm,fsi2pib" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, scom_of_ids);
+
 static const struct fsi_device_id scom_ids[] = {
        {
                .engine_type = FSI_ENGID_SCOM,
@@ -600,6 +607,7 @@ static struct fsi_driver scom_drv = {
        .drv = {
                .name = "scom",
                .bus = &fsi_bus_type,
+               .of_match_table = scom_of_ids,
                .probe = scom_probe,
                .remove = scom_remove,
        }
diff --git a/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
new file mode 100644 (file)
index 0000000..1d63a58
--- /dev/null
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) IBM Corporation 2023 */
+
+#ifndef DRIVERS_FSI_SLAVE_H
+#define DRIVERS_FSI_SLAVE_H
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+struct fsi_master;
+
+struct fsi_slave {
+       struct device           dev;
+       struct fsi_master       *master;
+       struct cdev             cdev;
+       int                     cdev_idx;
+       int                     id;     /* FSI address */
+       int                     link;   /* FSI link# */
+       u32                     cfam_id;
+       int                     chip_id;
+       uint32_t                size;   /* size of slave address space */
+       u8                      t_send_delay;
+       u8                      t_echo_delay;
+};
+
+#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+
+#endif /* DRIVERS_FSI_SLAVE_H */
diff --git a/drivers/fsi/i2cr-scom.c b/drivers/fsi/i2cr-scom.c
new file mode 100644 (file)
index 0000000..cb7e022
--- /dev/null
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) IBM Corporation 2023 */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+
+#include "fsi-master-i2cr.h"
+#include "fsi-slave.h"
+
+struct i2cr_scom {
+       struct device dev;
+       struct cdev cdev;
+       struct fsi_master_i2cr *i2cr;
+};
+
+static loff_t i2cr_scom_llseek(struct file *file, loff_t offset, int whence)
+{
+       switch (whence) {
+       case SEEK_CUR:
+               break;
+       case SEEK_SET:
+               file->f_pos = offset;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return offset;
+}
+
+static ssize_t i2cr_scom_read(struct file *filep, char __user *buf, size_t len, loff_t *offset)
+{
+       struct i2cr_scom *scom = filep->private_data;
+       u64 data;
+       int ret;
+
+       if (len != sizeof(data))
+               return -EINVAL;
+
+       ret = fsi_master_i2cr_read(scom->i2cr, (u32)*offset, &data);
+       if (ret)
+               return ret;
+
+       ret = copy_to_user(buf, &data, len);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t i2cr_scom_write(struct file *filep, const char __user *buf, size_t len,
+                              loff_t *offset)
+{
+       struct i2cr_scom *scom = filep->private_data;
+       u64 data;
+       int ret;
+
+       if (len != sizeof(data))
+               return -EINVAL;
+
+       ret = copy_from_user(&data, buf, len);
+       if (ret)
+               return ret;
+
+       ret = fsi_master_i2cr_write(scom->i2cr, (u32)*offset, data);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static const struct file_operations i2cr_scom_fops = {
+       .owner          = THIS_MODULE,
+       .open           = simple_open,
+       .llseek         = i2cr_scom_llseek,
+       .read           = i2cr_scom_read,
+       .write          = i2cr_scom_write,
+};
+
+static int i2cr_scom_probe(struct device *dev)
+{
+       struct fsi_device *fsi_dev = to_fsi_dev(dev);
+       struct i2cr_scom *scom;
+       int didx;
+       int ret;
+
+       if (!is_fsi_master_i2cr(fsi_dev->slave->master))
+               return -ENODEV;
+
+       scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
+       if (!scom)
+               return -ENOMEM;
+
+       scom->i2cr = to_fsi_master_i2cr(fsi_dev->slave->master);
+       dev_set_drvdata(dev, scom);
+
+       scom->dev.type = &fsi_cdev_type;
+       scom->dev.parent = dev;
+       device_initialize(&scom->dev);
+
+       ret = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
+       if (ret)
+               return ret;
+
+       dev_set_name(&scom->dev, "scom%d", didx);
+       cdev_init(&scom->cdev, &i2cr_scom_fops);
+       ret = cdev_device_add(&scom->cdev, &scom->dev);
+       if (ret)
+               fsi_free_minor(scom->dev.devt);
+
+       return ret;
+}
+
+static int i2cr_scom_remove(struct device *dev)
+{
+       struct i2cr_scom *scom = dev_get_drvdata(dev);
+
+       cdev_device_del(&scom->cdev, &scom->dev);
+       fsi_free_minor(scom->dev.devt);
+
+       return 0;
+}
+
+static const struct of_device_id i2cr_scom_of_ids[] = {
+       { .compatible = "ibm,i2cr-scom" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, i2cr_scom_of_ids);
+
+static const struct fsi_device_id i2cr_scom_ids[] = {
+       { 0x5, FSI_VERSION_ANY },
+       { }
+};
+
+static struct fsi_driver i2cr_scom_driver = {
+       .id_table = i2cr_scom_ids,
+       .drv = {
+               .name = "i2cr_scom",
+               .bus = &fsi_bus_type,
+               .of_match_table = i2cr_scom_of_ids,
+               .probe = i2cr_scom_probe,
+               .remove = i2cr_scom_remove,
+       }
+};
+
+module_fsi_driver(i2cr_scom_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
+MODULE_DESCRIPTION("IBM I2C Responder SCOM driver");
+MODULE_LICENSE("GPL");
index e5b65a3..a812c15 100644 (file)
@@ -363,6 +363,7 @@ static int init_core_mask(struct peci_cputemp *priv)
        switch (peci_dev->info.model) {
        case INTEL_FAM6_ICELAKE_X:
        case INTEL_FAM6_ICELAKE_D:
+       case INTEL_FAM6_SAPPHIRERAPIDS_X:
                ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev,
                                             reg->func, reg->offset + 4, &data);
                if (ret)
@@ -531,6 +532,13 @@ static struct resolved_cores_reg resolved_cores_reg_icx = {
        .offset = 0xd0,
 };
 
+static struct resolved_cores_reg resolved_cores_reg_spr = {
+       .bus = 31,
+       .dev = 30,
+       .func = 6,
+       .offset = 0x80,
+};
+
 static const struct cpu_info cpu_hsx = {
        .reg            = &resolved_cores_reg_hsx,
        .min_peci_revision = 0x33,
@@ -549,6 +557,12 @@ static const struct cpu_info cpu_icx = {
        .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree,
 };
 
+static const struct cpu_info cpu_spr = {
+       .reg            = &resolved_cores_reg_spr,
+       .min_peci_revision = 0x40,
+       .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree,
+};
+
 static const struct auxiliary_device_id peci_cputemp_ids[] = {
        {
                .name = "peci_cpu.cputemp.hsx",
@@ -574,6 +588,10 @@ static const struct auxiliary_device_id peci_cputemp_ids[] = {
                .name = "peci_cpu.cputemp.icxd",
                .driver_data = (kernel_ulong_t)&cpu_icx,
        },
+       {
+               .name = "peci_cpu.cputemp.spr",
+               .driver_data = (kernel_ulong_t)&cpu_spr,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids);
index ed96840..b235879 100644 (file)
@@ -30,6 +30,8 @@
 #define DIMM_IDX_MAX_ON_ICX    2
 #define CHAN_RANK_MAX_ON_ICXD  4
 #define DIMM_IDX_MAX_ON_ICXD   2
+#define CHAN_RANK_MAX_ON_SPR   8
+#define DIMM_IDX_MAX_ON_SPR    2
 
 #define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
 #define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
@@ -530,6 +532,43 @@ read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u
        return 0;
 }
 
+static int
+read_thresholds_spr(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
+{
+       u32 reg_val;
+       u64 offset;
+       int ret;
+       u8 dev;
+
+       ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd4, &reg_val);
+       if (ret || !(reg_val & BIT(31)))
+               return -ENODATA; /* Use default or previous value */
+
+       ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd0, &reg_val);
+       if (ret)
+               return -ENODATA; /* Use default or previous value */
+
+       /*
+        * Device 26, Offset 219a8: IMC 0 channel 0 -> rank 0
+        * Device 26, Offset 299a8: IMC 0 channel 1 -> rank 1
+        * Device 27, Offset 219a8: IMC 1 channel 0 -> rank 2
+        * Device 27, Offset 299a8: IMC 1 channel 1 -> rank 3
+        * Device 28, Offset 219a8: IMC 2 channel 0 -> rank 4
+        * Device 28, Offset 299a8: IMC 2 channel 1 -> rank 5
+        * Device 29, Offset 219a8: IMC 3 channel 0 -> rank 6
+        * Device 29, Offset 299a8: IMC 3 channel 1 -> rank 7
+        */
+       dev = 26 + chan_rank / 2;
+       offset = 0x219a8 + dimm_order * 4 + (chan_rank % 2) * 0x8000;
+
+       ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val),
+                            dev, 0, offset, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 static const struct dimm_info dimm_hsx = {
        .chan_rank_max  = CHAN_RANK_MAX_ON_HSX,
        .dimm_idx_max   = DIMM_IDX_MAX_ON_HSX,
@@ -572,6 +611,13 @@ static const struct dimm_info dimm_icxd = {
        .read_thresholds = &read_thresholds_icx,
 };
 
+static const struct dimm_info dimm_spr = {
+       .chan_rank_max  = CHAN_RANK_MAX_ON_SPR,
+       .dimm_idx_max   = DIMM_IDX_MAX_ON_SPR,
+       .min_peci_revision = 0x40,
+       .read_thresholds = &read_thresholds_spr,
+};
+
 static const struct auxiliary_device_id peci_dimmtemp_ids[] = {
        {
                .name = "peci_cpu.dimmtemp.hsx",
@@ -597,6 +643,10 @@ static const struct auxiliary_device_id peci_dimmtemp_ids[] = {
                .name = "peci_cpu.dimmtemp.icxd",
                .driver_data = (kernel_ulong_t)&dimm_icxd,
        },
+       {
+               .name = "peci_cpu.dimmtemp.spr",
+               .driver_data = (kernel_ulong_t)&dimm_spr,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids);
index 524327e..a4cf0cf 100644 (file)
@@ -40,8 +40,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
 
 static const struct i2c_device_id adxl313_i2c_id[] = {
        { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
-       { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
-       { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
+       { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL313] },
+       { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL314] },
        { }
 };
 
@@ -65,9 +65,7 @@ static int adxl313_i2c_probe(struct i2c_client *client)
         * Retrieves device specific data as a pointer to a
         * adxl313_chip_info structure
         */
-       chip_data = device_get_match_data(&client->dev);
-       if (!chip_data)
-               chip_data = (const struct adxl313_chip_info *)i2c_match_id(adxl313_i2c_id, client)->driver_data;
+       chip_data = i2c_get_match_data(client);
 
        regmap = devm_regmap_init_i2c(client,
                                      &adxl31x_i2c_regmap_config[chip_data->type]);
index d5beea6..32398cd 100644 (file)
@@ -24,19 +24,10 @@ static int adxl355_i2c_probe(struct i2c_client *client)
 {
        struct regmap *regmap;
        const struct adxl355_chip_info *chip_data;
-       const struct i2c_device_id *adxl355;
 
-       chip_data = device_get_match_data(&client->dev);
-       if (!chip_data) {
-               adxl355 = to_i2c_driver(client->dev.driver)->id_table;
-               if (!adxl355)
-                       return -EINVAL;
-
-               chip_data = (void *)i2c_match_id(adxl355, client)->driver_data;
-
-               if (!chip_data)
-                       return -EINVAL;
-       }
+       chip_data = i2c_get_match_data(client);
+       if (!chip_data)
+               return -ENODEV;
 
        regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config);
        if (IS_ERR(regmap)) {
index 2bd267a..75a88f1 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/module.h>
 #include <linux/regmap.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/spi/spi.h>
 
 #include "adxl372.h"
index e8ab0d2..13439f5 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-#include <linux/of_device.h>
 #include <linux/of.h>
 #include <linux/bitops.h>
 #include <linux/regulator/consumer.h>
index 2f27a5d..572bfe9 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * IIO driver for the MiraMEMS DA280 3-axis accelerometer and
+ * IIO driver for the MiraMEMS DA217 and DA280 3-axis accelerometer and
  * IIO driver for the MiraMEMS DA226 2-axis accelerometer
  *
  * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
@@ -23,7 +23,7 @@
 #define DA280_MODE_ENABLE              0x1e
 #define DA280_MODE_DISABLE             0x9e
 
-enum da280_chipset { da226, da280 };
+enum da280_chipset { da217, da226, da280 };
 
 /*
  * a value of + or -4096 corresponds to + or - 1G
@@ -134,7 +134,10 @@ static int da280_probe(struct i2c_client *client)
                chip = id->driver_data;
        }
 
-       if (chip == da226) {
+       if (chip == da217) {
+               indio_dev->name = "da217";
+               indio_dev->num_channels = 3;
+       } else if (chip == da226) {
                indio_dev->name = "da226";
                indio_dev->num_channels = 2;
        } else {
@@ -166,12 +169,14 @@ static int da280_resume(struct device *dev)
 static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
 
 static const struct acpi_device_id da280_acpi_match[] = {
+       {"NSA2513", da217},
        {"MIRAACC", da280},
        {},
 };
 MODULE_DEVICE_TABLE(acpi, da280_acpi_match);
 
 static const struct i2c_device_id da280_i2c_id[] = {
+       { "da217", da217 },
        { "da226", da226 },
        { "da280", da280 },
        {}
index 07f14a9..1719a9f 100644 (file)
@@ -2,7 +2,6 @@
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/spi/spi.h>
 #include <linux/module.h>
 #include <linux/slab.h>
index 6e7399e..f42a887 100644 (file)
@@ -28,7 +28,7 @@
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/events.h>
 #include <linux/delay.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
index dc14bde..517b3db 100644 (file)
@@ -1116,7 +1116,7 @@ config STMPE_ADC
          built-in ADC block (stmpe811).
 
 config SUN4I_GPADC
-       tristate "Support for the Allwinner SoCs GPADC"
+       tristate "Allwinner A10/A13/A31 and similar GPADCs driver"
        depends on IIO
        depends on MFD_SUN4I_GPADC || MACH_SUN8I
        depends on THERMAL || !THERMAL_OF
@@ -1134,6 +1134,16 @@ config SUN4I_GPADC
          To compile this driver as a module, choose M here: the module will be
          called sun4i-gpadc-iio.
 
+config SUN20I_GPADC
+       tristate "Allwinner D1/T113s/T507/R329 and similar GPADCs driver"
+       depends on ARCH_SUNXI || COMPILE_TEST
+       help
+         Say yes here to build support for Allwinner (D1, T113, T507 and R329)
+         SoCs GPADC. This ADC provides up to 16 channels.
+
+         To compile this driver as a module, choose M here: the module will be
+         called sun20i-gpadc-iio.
+
 config TI_ADC081C
        tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
        depends on I2C
index eb6e891..2facf97 100644 (file)
@@ -96,6 +96,7 @@ obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
 obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
 obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
 obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
+obj-$(CONFIG_SUN20I_GPADC) += sun20i-gpadc-iio.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
 obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
index 4fa2126..3b1bdd0 100644 (file)
@@ -1099,14 +1099,12 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
 
        gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
        if (gpadc->irq_sw < 0)
-               return dev_err_probe(dev, gpadc->irq_sw,
-                                    "failed to get platform sw_conv_end irq\n");
+               return gpadc->irq_sw;
 
        if (is_ab8500(gpadc->ab8500)) {
                gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
                if (gpadc->irq_hw < 0)
-                       return dev_err_probe(dev, gpadc->irq_hw,
-                                            "failed to get platform hw_conv_end irq\n");
+                       return gpadc->irq_hw;
        } else {
                gpadc->irq_hw = 0;
        }
index 050a2fb..b9b206f 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/kernel.h>
 #include <linux/kfifo.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 
index 7bc3ebf..69d1103 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/err.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
@@ -402,8 +402,8 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
        id &= AD7192_ID_MASK;
 
        if (id != st->chip_info->chip_id)
-               dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
-                        id);
+               dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n",
+                        id, st->chip_info->chip_id);
 
        st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) |
                AD7192_MODE_CLKSRC(st->clock_sel) |
@@ -561,9 +561,8 @@ static ssize_t ad7192_show_filter_avail(struct device *dev,
        ad7192_get_available_filter_freq(st, freq_avail);
 
        for (i = 0; i < ARRAY_SIZE(freq_avail); i++)
-               len += scnprintf(buf + len, PAGE_SIZE - len,
-                                "%d.%d ", freq_avail[i] / 1000,
-                                freq_avail[i] % 1000);
+               len += sysfs_emit_at(buf, len, "%d.%03d ", freq_avail[i] / 1000,
+                                    freq_avail[i] % 1000);
 
        buf[len - 1] = '\n';
 
@@ -972,11 +971,6 @@ static void ad7192_reg_disable(void *reg)
        regulator_disable(reg);
 }
 
-static void ad7192_clk_disable(void *clk)
-{
-       clk_disable_unprepare(clk);
-}
-
 static int ad7192_probe(struct spi_device *spi)
 {
        struct ad7192_state *st;
@@ -1036,7 +1030,9 @@ static int ad7192_probe(struct spi_device *spi)
        else
                indio_dev->info = &ad7192_info;
 
-       ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
+       ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
+       if (ret)
+               return ret;
 
        ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
        if (ret)
@@ -1044,7 +1040,7 @@ static int ad7192_probe(struct spi_device *spi)
 
        st->fclk = AD7192_INT_FREQ_MHZ;
 
-       st->mclk = devm_clk_get_optional(&spi->dev, "mclk");
+       st->mclk = devm_clk_get_optional_enabled(&spi->dev, "mclk");
        if (IS_ERR(st->mclk))
                return PTR_ERR(st->mclk);
 
@@ -1052,15 +1048,6 @@ static int ad7192_probe(struct spi_device *spi)
 
        if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
            st->clock_sel == AD7192_CLK_EXT_MCLK2) {
-               ret = clk_prepare_enable(st->mclk);
-               if (ret < 0)
-                       return ret;
-
-               ret = devm_add_action_or_reset(&spi->dev, ad7192_clk_disable,
-                                              st->mclk);
-               if (ret)
-                       return ret;
-
                st->fclk = clk_get_rate(st->mclk);
                if (!ad7192_valid_external_frequency(st->fclk)) {
                        dev_err(&spi->dev,
index 0621cf5..39eccc2 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 
 
 #include <linux/iio/iio.h>
index e8a8ea4..aff0532 100644 (file)
@@ -11,8 +11,9 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/slab.h>
 
 #include <linux/iio/iio.h>
index 366e252..de6650f 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
index 44e1e53..0d68854 100644 (file)
@@ -540,8 +540,8 @@ static int iproc_adc_probe(struct platform_device *pdev)
        }
 
        adc_priv->irqno = platform_get_irq(pdev, 0);
-       if (adc_priv->irqno <= 0)
-               return -ENODEV;
+       if (adc_priv->irqno < 0)
+               return adc_priv->irqno;
 
        ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
                                IPROC_ADC_AUXIN_SCAN_ENA, 0);
index 2cde4b4..a432342 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
index 22da81b..828d3fe 100644 (file)
@@ -496,7 +496,7 @@ static int imx7d_adc_probe(struct platform_device *pdev)
 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
-               return dev_err_probe(dev, irq, "Failed getting irq\n");
+               return irq;
 
        info->clk = devm_clk_get(dev, "adc");
        if (IS_ERR(info->clk))
index aea83f3..9e52207 100644 (file)
@@ -28,7 +28,7 @@
 #include <linux/iio/sysfs.h>
 #include <linux/kthread.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/sched/task.h>
 #include <linux/util_macros.h>
index 732c924..e34ed7d 100644 (file)
@@ -176,8 +176,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
        }
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0)
-               return -ENXIO;
+       if (irq < 0)
+               return irq;
 
        retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
                                  LPC32XXAD_NAME, st);
index adc5cea..198c7e6 100644 (file)
@@ -161,7 +161,6 @@ MODULE_DEVICE_TABLE(mcb, men_z188_ids);
 static struct mcb_driver men_z188_driver = {
        .driver = {
                .name = "z188-adc",
-               .owner = THIS_MODULE,
        },
        .probe = men_z188_probe,
        .remove = men_z188_remove,
index eb78a6f..320e3e7 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/interrupt.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
  */
 #define MESON_SAR_ADC_REG11                                    0x2c
        #define MESON_SAR_ADC_REG11_BANDGAP_EN                  BIT(13)
+       #define MESON_SAR_ADC_REG11_CMV_SEL                     BIT(6)
+       #define MESON_SAR_ADC_REG11_VREF_VOLTAGE                BIT(5)
+       #define MESON_SAR_ADC_REG11_EOC                         BIT(1)
+       #define MESON_SAR_ADC_REG11_VREF_SEL                    BIT(0)
 
 #define MESON_SAR_ADC_REG13                                    0x34
        #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK      GENMASK(13, 8)
 #define MESON_SAR_ADC_MAX_FIFO_SIZE                            32
 #define MESON_SAR_ADC_TIMEOUT                                  100 /* ms */
 #define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL                 6
+#define MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL                  7
 #define MESON_SAR_ADC_TEMP_OFFSET                              27
 
 /* temperature sensor calibration information in eFuse */
        .datasheet_name = "TEMP_SENSOR",                                \
 }
 
-static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
-       MESON_SAR_ADC_CHAN(0),
-       MESON_SAR_ADC_CHAN(1),
-       MESON_SAR_ADC_CHAN(2),
-       MESON_SAR_ADC_CHAN(3),
-       MESON_SAR_ADC_CHAN(4),
-       MESON_SAR_ADC_CHAN(5),
-       MESON_SAR_ADC_CHAN(6),
-       MESON_SAR_ADC_CHAN(7),
-       IIO_CHAN_SOFT_TIMESTAMP(8),
-};
+#define MESON_SAR_ADC_MUX(_chan, _sel) {                               \
+       .type = IIO_VOLTAGE,                                            \
+       .channel = _chan,                                               \
+       .indexed = 1,                                                   \
+       .address = MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL,               \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |                  \
+                               BIT(IIO_CHAN_INFO_AVERAGE_RAW),         \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),           \
+       .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) |       \
+                               BIT(IIO_CHAN_INFO_CALIBSCALE),          \
+       .datasheet_name = "SAR_ADC_MUX_"#_sel,                          \
+}
 
-static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
-       MESON_SAR_ADC_CHAN(0),
-       MESON_SAR_ADC_CHAN(1),
-       MESON_SAR_ADC_CHAN(2),
-       MESON_SAR_ADC_CHAN(3),
-       MESON_SAR_ADC_CHAN(4),
-       MESON_SAR_ADC_CHAN(5),
-       MESON_SAR_ADC_CHAN(6),
-       MESON_SAR_ADC_CHAN(7),
-       MESON_SAR_ADC_TEMP_CHAN(8),
-       IIO_CHAN_SOFT_TIMESTAMP(9),
+enum meson_sar_adc_vref_sel {
+       VREF_CALIBATION_VOLTAGE = 0,
+       VREF_VDDA = 1,
 };
 
 enum meson_sar_adc_avg_mode {
@@ -249,6 +246,72 @@ enum meson_sar_adc_chan7_mux_sel {
        CHAN7_MUX_CH7_INPUT = 0x7,
 };
 
+enum meson_sar_adc_channel_index {
+       NUM_CHAN_0,
+       NUM_CHAN_1,
+       NUM_CHAN_2,
+       NUM_CHAN_3,
+       NUM_CHAN_4,
+       NUM_CHAN_5,
+       NUM_CHAN_6,
+       NUM_CHAN_7,
+       NUM_CHAN_TEMP,
+       NUM_MUX_0_VSS,
+       NUM_MUX_1_VDD_DIV4,
+       NUM_MUX_2_VDD_DIV2,
+       NUM_MUX_3_VDD_MUL3_DIV4,
+       NUM_MUX_4_VDD,
+};
+
+static enum meson_sar_adc_chan7_mux_sel chan7_mux_values[] = {
+       CHAN7_MUX_VSS,
+       CHAN7_MUX_VDD_DIV4,
+       CHAN7_MUX_VDD_DIV2,
+       CHAN7_MUX_VDD_MUL3_DIV4,
+       CHAN7_MUX_VDD,
+};
+
+static const char * const chan7_mux_names[] = {
+       [CHAN7_MUX_VSS] = "gnd",
+       [CHAN7_MUX_VDD_DIV4] = "0.25vdd",
+       [CHAN7_MUX_VDD_DIV2] = "0.5vdd",
+       [CHAN7_MUX_VDD_MUL3_DIV4] = "0.75vdd",
+       [CHAN7_MUX_VDD] = "vdd",
+};
+
+static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
+       MESON_SAR_ADC_CHAN(NUM_CHAN_0),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_1),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_2),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_3),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_4),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_5),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_6),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_7),
+       MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0),
+       MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1),
+       MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2),
+       MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3),
+       MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4),
+};
+
+static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
+       MESON_SAR_ADC_CHAN(NUM_CHAN_0),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_1),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_2),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_3),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_4),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_5),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_6),
+       MESON_SAR_ADC_CHAN(NUM_CHAN_7),
+       MESON_SAR_ADC_TEMP_CHAN(NUM_CHAN_TEMP),
+       MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0),
+       MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1),
+       MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2),
+       MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3),
+       MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4),
+};
+
 struct meson_sar_adc_param {
        bool                                    has_bl30_integration;
        unsigned long                           clock_rate;
@@ -258,6 +321,13 @@ struct meson_sar_adc_param {
        u8                                      temperature_trimming_bits;
        unsigned int                            temperature_multiplier;
        unsigned int                            temperature_divider;
+       u8                                      disable_ring_counter;
+       bool                                    has_reg11;
+       bool                                    has_vref_select;
+       u8                                      vref_select;
+       u8                                      cmv_select;
+       u8                                      adc_eoc;
+       enum meson_sar_adc_vref_sel             vref_volatge;
 };
 
 struct meson_sar_adc_data {
@@ -285,6 +355,7 @@ struct meson_sar_adc_priv {
        bool                                    temperature_sensor_calibrated;
        u8                                      temperature_sensor_coefficient;
        u16                                     temperature_sensor_adc_val;
+       enum meson_sar_adc_chan7_mux_sel        chan7_mux_sel;
 };
 
 static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
@@ -301,6 +372,17 @@ static const struct regmap_config meson_sar_adc_regmap_config_meson8 = {
        .max_register = MESON_SAR_ADC_DELTA_10,
 };
 
+static const struct iio_chan_spec *
+find_channel_by_num(struct iio_dev *indio_dev, int num)
+{
+       int i;
+
+       for (i = 0; i < indio_dev->num_channels; i++)
+               if (indio_dev->channels[i].channel == num)
+                       return &indio_dev->channels[i];
+       return NULL;
+}
+
 static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
 {
        struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
@@ -338,6 +420,21 @@ static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
                                               1, 10000);
 }
 
+static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
+                                       enum meson_sar_adc_chan7_mux_sel sel)
+{
+       struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+       u32 regval;
+
+       regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
+       regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+                          MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
+
+       usleep_range(10, 20);
+
+       priv->chan7_mux_sel = sel;
+}
+
 static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
                                         const struct iio_chan_spec *chan,
                                         int *val)
@@ -431,20 +528,16 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
                regmap_update_bits(priv->regmap,
                                   MESON_SAR_ADC_DELTA_10,
                                   MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval);
-       }
-}
-
-static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
-                                       enum meson_sar_adc_chan7_mux_sel sel)
-{
-       struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
-       u32 regval;
-
-       regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
-       regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
-                          MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
+       } else if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL) {
+               enum meson_sar_adc_chan7_mux_sel sel;
 
-       usleep_range(10, 20);
+               if (chan->channel == NUM_CHAN_7)
+                       sel = CHAN7_MUX_CH7_INPUT;
+               else
+                       sel = chan7_mux_values[chan->channel - NUM_MUX_0_VSS];
+               if (sel != priv->chan7_mux_sel)
+                       meson_sar_adc_set_chan7_mux(indio_dev, sel);
+       }
 }
 
 static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
@@ -821,6 +914,22 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
                           MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK,
                           regval);
 
+       regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW);
+
+       regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW);
+
+       regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW);
+
+       regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW,
+                          MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW);
+
        /*
         * set up the input channel muxes in MESON_SAR_ADC_AUX_SW
         * (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable
@@ -873,6 +982,35 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
                                   MESON_SAR_ADC_DELTA_10_TS_REVE0, 0);
        }
 
+       regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN,
+                           priv->param->disable_ring_counter);
+       regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+                          MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN,
+                          regval);
+
+       if (priv->param->has_reg11) {
+               regval = FIELD_PREP(MESON_SAR_ADC_REG11_EOC, priv->param->adc_eoc);
+               regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
+                                  MESON_SAR_ADC_REG11_EOC, regval);
+
+               if (priv->param->has_vref_select) {
+                       regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_SEL,
+                                           priv->param->vref_select);
+                       regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
+                                          MESON_SAR_ADC_REG11_VREF_SEL, regval);
+               }
+
+               regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_VOLTAGE,
+                                   priv->param->vref_volatge);
+               regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
+                                  MESON_SAR_ADC_REG11_VREF_VOLTAGE, regval);
+
+               regval = FIELD_PREP(MESON_SAR_ADC_REG11_CMV_SEL,
+                                   priv->param->cmv_select);
+               regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
+                                  MESON_SAR_ADC_REG11_CMV_SEL, regval);
+       }
+
        ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
        if (ret)
                return dev_err_probe(dev, ret, "failed to set adc parent to clkin\n");
@@ -1006,7 +1144,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
        meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
        usleep_range(10, 20);
        ret = meson_sar_adc_get_sample(indio_dev,
-                                      &indio_dev->channels[7],
+                                      find_channel_by_num(indio_dev,
+                                                          NUM_MUX_1_VDD_DIV4),
                                       MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
        if (ret < 0)
                goto out;
@@ -1014,7 +1153,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
        meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
        usleep_range(10, 20);
        ret = meson_sar_adc_get_sample(indio_dev,
-                                      &indio_dev->channels[7],
+                                      find_channel_by_num(indio_dev,
+                                                          NUM_MUX_3_VDD_MUL3_DIV4),
                                       MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
        if (ret < 0)
                goto out;
@@ -1035,8 +1175,23 @@ out:
        return ret;
 }
 
+static int read_label(struct iio_dev *indio_dev,
+                     struct iio_chan_spec const *chan,
+                     char *label)
+{
+       if (chan->type == IIO_TEMP)
+               return sprintf(label, "temp-sensor\n");
+       if (chan->type == IIO_VOLTAGE && chan->channel >= NUM_MUX_0_VSS)
+               return sprintf(label, "%s\n",
+                              chan7_mux_names[chan->channel - NUM_MUX_0_VSS]);
+       if (chan->type == IIO_VOLTAGE)
+               return sprintf(label, "channel-%d\n", chan->channel);
+       return 0;
+}
+
 static const struct iio_info meson_sar_adc_iio_info = {
        .read_raw = meson_sar_adc_iio_info_read_raw,
+       .read_label = read_label,
 };
 
 static const struct meson_sar_adc_param meson_sar_adc_meson8_param = {
@@ -1067,6 +1222,9 @@ static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
        .bandgap_reg = MESON_SAR_ADC_REG11,
        .regmap_config = &meson_sar_adc_regmap_config_gxbb,
        .resolution = 10,
+       .has_reg11 = true,
+       .vref_volatge = 1,
+       .cmv_select = 1,
 };
 
 static const struct meson_sar_adc_param meson_sar_adc_gxl_param = {
@@ -1075,6 +1233,10 @@ static const struct meson_sar_adc_param meson_sar_adc_gxl_param = {
        .bandgap_reg = MESON_SAR_ADC_REG11,
        .regmap_config = &meson_sar_adc_regmap_config_gxbb,
        .resolution = 12,
+       .disable_ring_counter = 1,
+       .has_reg11 = true,
+       .vref_volatge = 1,
+       .cmv_select = 1,
 };
 
 static const struct meson_sar_adc_param meson_sar_adc_g12a_param = {
@@ -1083,6 +1245,11 @@ static const struct meson_sar_adc_param meson_sar_adc_g12a_param = {
        .bandgap_reg = MESON_SAR_ADC_REG11,
        .regmap_config = &meson_sar_adc_regmap_config_gxbb,
        .resolution = 12,
+       .disable_ring_counter = 1,
+       .has_reg11 = true,
+       .adc_eoc = 1,
+       .has_vref_select = true,
+       .vref_select = VREF_VDDA,
 };
 
 static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {
index ba4cd8f..3d9207c 100644 (file)
@@ -244,8 +244,8 @@ static int npcm_adc_probe(struct platform_device *pdev)
        info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0) {
-               ret = -EINVAL;
+       if (irq < 0) {
+               ret = irq;
                goto err_disable_clk;
        }
 
index 27b2632..e202ea1 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/mfd/palmas.h>
 #include <linux/completion.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/iio/events.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/machine.h>
@@ -916,8 +915,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
 
        adc->irq_auto_0 = platform_get_irq(pdev, 1);
        if (adc->irq_auto_0 < 0)
-               return dev_err_probe(adc->dev, adc->irq_auto_0,
-                                    "get auto0 irq failed\n");
+               return adc->irq_auto_0;
 
        ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, NULL,
                                        palmas_gpadc_irq_auto, IRQF_ONESHOT,
@@ -929,8 +927,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
 
        adc->irq_auto_1 = platform_get_irq(pdev, 2);
        if (adc->irq_auto_1 < 0)
-               return dev_err_probe(adc->dev, adc->irq_auto_1,
-                                    "get auto1 irq failed\n");
+               return adc->irq_auto_1;
 
        ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, NULL,
                                        palmas_gpadc_irq_auto, IRQF_ONESHOT,
index 0a4fd3a..b6b612d 100644 (file)
@@ -555,6 +555,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
                                        SCALE_HW_CALIB_PM5_SMB_TEMP)
        [ADC5_GPIO1_100K_PU]    = ADC5_CHAN_TEMP("gpio1_100k_pu", 0,
                                        SCALE_HW_CALIB_THERM_100K_PULLUP)
+       [ADC5_GPIO2_100K_PU]    = ADC5_CHAN_TEMP("gpio2_100k_pu", 0,
+                                       SCALE_HW_CALIB_THERM_100K_PULLUP)
        [ADC5_GPIO3_100K_PU]    = ADC5_CHAN_TEMP("gpio3_100k_pu", 0,
                                        SCALE_HW_CALIB_THERM_100K_PULLUP)
        [ADC5_GPIO4_100K_PU]    = ADC5_CHAN_TEMP("gpio4_100k_pu", 0,
index acbda66..7fb8b24 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
index 4b011f7..dd94667 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
@@ -467,7 +466,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
-               return dev_err_probe(&pdev->dev, irq, "failed to get irq\n");
+               return irq;
 
        ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
                               0, dev_name(&pdev->dev), info);
index ff1fc32..b4a2e05 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/mutex.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
index d93e580..ad54ef7 100644 (file)
@@ -310,8 +310,8 @@ static int spear_adc_probe(struct platform_device *pdev)
        }
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0) {
-               ret = -EINVAL;
+       if (irq < 0) {
+               ret = irq;
                goto errout2;
        }
 
index 48f02dc..2f08200 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
@@ -723,8 +725,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
        priv->nb_adc_max = priv->cfg->num_adcs;
        spin_lock_init(&priv->common.lock);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->common.base = devm_ioremap_resource(&pdev->dev, res);
+       priv->common.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(priv->common.base))
                return PTR_ERR(priv->common.base);
        priv->common.phys_base = res->start;
index a428bdb..b5cc43d 100644 (file)
@@ -19,7 +19,8 @@
 #include <linux/iio/triggered_buffer.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
index 0362df2..0f6ebb3 100644 (file)
 #include <linux/iio/sysfs.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
index 67518e4..8e56def 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/kernel.h>
 #include <linux/mfd/stmpe.h>
 #include <linux/module.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/device.h>
 
diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c
new file mode 100644 (file)
index 0000000..6a893d4
--- /dev/null
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPADC driver for sunxi platforms (D1, T113-S3 and R329)
+ * Copyright (c) 2023 Maksim Kiselev <bigunclemax@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reset.h>
+
+#include <linux/iio/iio.h>
+
+#define SUN20I_GPADC_DRIVER_NAME       "sun20i-gpadc"
+
+/* Register map definition */
+#define SUN20I_GPADC_SR                        0x00
+#define SUN20I_GPADC_CTRL              0x04
+#define SUN20I_GPADC_CS_EN             0x08
+#define SUN20I_GPADC_FIFO_INTC         0x0c
+#define SUN20I_GPADC_FIFO_INTS         0x10
+#define SUN20I_GPADC_FIFO_DATA         0X14
+#define SUN20I_GPADC_CB_DATA           0X18
+#define SUN20I_GPADC_DATAL_INTC                0x20
+#define SUN20I_GPADC_DATAH_INTC                0x24
+#define SUN20I_GPADC_DATA_INTC         0x28
+#define SUN20I_GPADC_DATAL_INTS                0x30
+#define SUN20I_GPADC_DATAH_INTS                0x34
+#define SUN20I_GPADC_DATA_INTS         0x38
+#define SUN20I_GPADC_CH_CMP_DATA(x)    (0x40 + (x) * 4)
+#define SUN20I_GPADC_CH_DATA(x)                (0x80 + (x) * 4)
+
+#define SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK         BIT(23)
+#define SUN20I_GPADC_CTRL_WORK_MODE_MASK               GENMASK(19, 18)
+#define SUN20I_GPADC_CTRL_ADC_EN_MASK                  BIT(16)
+#define SUN20I_GPADC_CS_EN_ADC_CH(x)                   BIT(x)
+#define SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(x)       BIT(x)
+
+#define SUN20I_GPADC_WORK_MODE_SINGLE                  0
+
+struct sun20i_gpadc_iio {
+       void __iomem            *regs;
+       struct completion       completion;
+       int                     last_channel;
+       /*
+        * Lock to protect the device state during a potential concurrent
+        * read access from userspace. Reading a raw value requires a sequence
+        * of register writes, then a wait for a completion callback,
+        * and finally a register read, during which userspace could issue
+        * another read request. This lock protects a read access from
+        * ocurring before another one has finished.
+        */
+       struct mutex            lock;
+};
+
+static int sun20i_gpadc_adc_read(struct sun20i_gpadc_iio *info,
+                                struct iio_chan_spec const *chan, int *val)
+{
+       u32 ctrl;
+       int ret = IIO_VAL_INT;
+
+       mutex_lock(&info->lock);
+
+       reinit_completion(&info->completion);
+
+       if (info->last_channel != chan->channel) {
+               info->last_channel = chan->channel;
+
+               /* enable the analog input channel */
+               writel(SUN20I_GPADC_CS_EN_ADC_CH(chan->channel),
+                      info->regs + SUN20I_GPADC_CS_EN);
+
+               /* enable the data irq for input channel */
+               writel(SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(chan->channel),
+                      info->regs + SUN20I_GPADC_DATA_INTC);
+       }
+
+       /* enable the ADC function */
+       ctrl = readl(info->regs + SUN20I_GPADC_CTRL);
+       ctrl |= FIELD_PREP(SUN20I_GPADC_CTRL_ADC_EN_MASK, 1);
+       writel(ctrl, info->regs + SUN20I_GPADC_CTRL);
+
+       /*
+        * According to the datasheet maximum acquire time(TACQ) can be
+        * (65535+1)/24Mhz and conversion time(CONV_TIME) is always constant
+        * and equal to 14/24Mhz, so (TACQ+CONV_TIME) <= 2.73125ms.
+        * A 10ms delay should be enough to make sure an interrupt occurs in
+        * normal conditions. If it doesn't occur, then there is a timeout.
+        */
+       if (!wait_for_completion_timeout(&info->completion, msecs_to_jiffies(10))) {
+               ret = -ETIMEDOUT;
+               goto err_unlock;
+       }
+
+       /* read the ADC data */
+       *val = readl(info->regs + SUN20I_GPADC_CH_DATA(chan->channel));
+
+err_unlock:
+       mutex_unlock(&info->lock);
+
+       return ret;
+}
+
+static int sun20i_gpadc_read_raw(struct iio_dev *indio_dev,
+                                struct iio_chan_spec const *chan, int *val,
+                                int *val2, long mask)
+{
+       struct sun20i_gpadc_iio *info = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               return sun20i_gpadc_adc_read(info, chan, val);
+       case IIO_CHAN_INFO_SCALE:
+               /* value in mv = 1800mV / 4096 raw */
+               *val = 1800;
+               *val2 = 12;
+               return IIO_VAL_FRACTIONAL_LOG2;
+       default:
+               return -EINVAL;
+       }
+}
+
+static irqreturn_t sun20i_gpadc_irq_handler(int irq, void *data)
+{
+       struct sun20i_gpadc_iio *info = data;
+
+       /* clear data interrupt status register */
+       writel(GENMASK(31, 0), info->regs + SUN20I_GPADC_DATA_INTS);
+
+       complete(&info->completion);
+
+       return IRQ_HANDLED;
+}
+
+static const struct iio_info sun20i_gpadc_iio_info = {
+       .read_raw = sun20i_gpadc_read_raw,
+};
+
+static void sun20i_gpadc_reset_assert(void *data)
+{
+       struct reset_control *rst = data;
+
+       reset_control_assert(rst);
+}
+
+static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev,
+                                      struct device *dev)
+{
+       unsigned int channel;
+       int num_channels, i, ret;
+       struct iio_chan_spec *channels;
+       struct fwnode_handle *node;
+
+       num_channels = device_get_child_node_count(dev);
+       if (num_channels == 0)
+               return dev_err_probe(dev, -ENODEV, "no channel children\n");
+
+       channels = devm_kcalloc(dev, num_channels, sizeof(*channels),
+                               GFP_KERNEL);
+       if (!channels)
+               return -ENOMEM;
+
+       i = 0;
+       device_for_each_child_node(dev, node) {
+               ret = fwnode_property_read_u32(node, "reg", &channel);
+               if (ret) {
+                       fwnode_handle_put(node);
+                       return dev_err_probe(dev, ret, "invalid channel number\n");
+               }
+
+               channels[i].type = IIO_VOLTAGE;
+               channels[i].indexed = 1;
+               channels[i].channel = channel;
+               channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+               channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+
+               i++;
+       }
+
+       indio_dev->channels = channels;
+       indio_dev->num_channels = num_channels;
+
+       return 0;
+}
+
+static int sun20i_gpadc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct iio_dev *indio_dev;
+       struct sun20i_gpadc_iio *info;
+       struct reset_control *rst;
+       struct clk *clk;
+       int irq;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       info = iio_priv(indio_dev);
+       info->last_channel = -1;
+
+       mutex_init(&info->lock);
+       init_completion(&info->completion);
+
+       ret = sun20i_gpadc_alloc_channels(indio_dev, dev);
+       if (ret)
+               return ret;
+
+       indio_dev->info = &sun20i_gpadc_iio_info;
+       indio_dev->name = SUN20I_GPADC_DRIVER_NAME;
+
+       info->regs = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(info->regs))
+               return PTR_ERR(info->regs);
+
+       clk = devm_clk_get_enabled(dev, NULL);
+       if (IS_ERR(clk))
+               return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock\n");
+
+       rst = devm_reset_control_get_exclusive(dev, NULL);
+       if (IS_ERR(rst))
+               return dev_err_probe(dev, PTR_ERR(rst), "failed to get reset control\n");
+
+       ret = reset_control_deassert(rst);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to deassert reset\n");
+
+       ret = devm_add_action_or_reset(dev, sun20i_gpadc_reset_assert, rst);
+       if (ret)
+               return ret;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       ret = devm_request_irq(dev, irq, sun20i_gpadc_irq_handler, 0,
+                              dev_name(dev), info);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq);
+
+       writel(FIELD_PREP(SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK, 1) |
+              FIELD_PREP(SUN20I_GPADC_CTRL_WORK_MODE_MASK, SUN20I_GPADC_WORK_MODE_SINGLE),
+              info->regs + SUN20I_GPADC_CTRL);
+
+       ret = devm_iio_device_register(dev, indio_dev);
+       if (ret)
+               return dev_err_probe(dev, ret, "could not register the device\n");
+
+       return 0;
+}
+
+static const struct of_device_id sun20i_gpadc_of_id[] = {
+       { .compatible = "allwinner,sun20i-d1-gpadc" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun20i_gpadc_of_id);
+
+static struct platform_driver sun20i_gpadc_driver = {
+       .driver = {
+               .name = SUN20I_GPADC_DRIVER_NAME,
+               .of_match_table = sun20i_gpadc_of_id,
+       },
+       .probe = sun20i_gpadc_probe,
+};
+module_platform_driver(sun20i_gpadc_driver);
+
+MODULE_DESCRIPTION("ADC driver for sunxi platforms");
+MODULE_AUTHOR("Maksim Kiselev <bigunclemax@gmail.com>");
+MODULE_LICENSE("GPL");
index a532255..25bba96 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
index c30ed82..84ba5c4 100644 (file)
 #include <linux/spi/spi.h>
 
 #include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
 #include <linux/iio/driver.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
 
 #define TI_LMP92064_REG_CONFIG_A 0x0000
 #define TI_LMP92064_REG_CONFIG_B 0x0001
@@ -91,6 +94,12 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
                .address = TI_LMP92064_CHAN_INC,
                .info_mask_separate =
                        BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = TI_LMP92064_CHAN_INC,
+               .scan_type = {
+                       .sign = 'u',
+                       .realbits = 12,
+                       .storagebits = 16,
+               },
                .datasheet_name = "INC",
        },
        {
@@ -98,8 +107,20 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
                .address = TI_LMP92064_CHAN_INV,
                .info_mask_separate =
                        BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = TI_LMP92064_CHAN_INV,
+               .scan_type = {
+                       .sign = 'u',
+                       .realbits = 12,
+                       .storagebits = 16,
+               },
                .datasheet_name = "INV",
        },
+       IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const unsigned long lmp92064_scan_masks[] = {
+       BIT(TI_LMP92064_CHAN_INC) | BIT(TI_LMP92064_CHAN_INV),
+       0
 };
 
 static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
@@ -171,6 +192,32 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev,
        }
 }
 
+static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
+       struct {
+               u16 values[2];
+               int64_t timestamp __aligned(8);
+       } data;
+       int ret;
+
+       memset(&data, 0, sizeof(data));
+
+       ret = lmp92064_read_meas(priv, data.values);
+       if (ret)
+               goto err;
+
+       iio_push_to_buffers_with_timestamp(indio_dev, &data,
+                                          iio_get_time_ns(indio_dev));
+
+err:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
 static int lmp92064_reset(struct lmp92064_adc_priv *priv,
                          struct gpio_desc *gpio_reset)
 {
@@ -301,6 +348,12 @@ static int lmp92064_adc_probe(struct spi_device *spi)
        indio_dev->channels = lmp92064_adc_channels;
        indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels);
        indio_dev->info = &lmp92064_adc_info;
+       indio_dev->available_scan_masks = lmp92064_scan_masks;
+
+       ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+                                             lmp92064_trigger_handler, NULL);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to setup buffered read\n");
 
        return devm_iio_device_register(dev, indio_dev);
 }
index 642c5c4..8db7a01 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/io.h>
 #include <linux/iio/iio.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/iio/machine.h>
 #include <linux/iio/driver.h>
 #include <linux/iopoll.h>
index f217a2a..b54fe01 100644 (file)
@@ -18,6 +18,7 @@ config AD8366
            AD8366 Dual-Digital Variable Gain Amplifier (VGA)
            ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
            ADL5240 Digitally controlled variable gain amplifier (VGA)
+           HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator
            HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
 
          To compile this driver as a module, choose M here: the
index 8d8c8ea..31564af 100644 (file)
@@ -5,6 +5,7 @@
  *   AD8366 Dual-Digital Variable Gain Amplifier (VGA)
  *   ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
  *   ADL5240 Digitally controlled variable gain amplifier (VGA)
+ *   HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator
  *   HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
  *
  * Copyright 2012-2019 Analog Devices Inc.
@@ -28,6 +29,7 @@ enum ad8366_type {
        ID_AD8366,
        ID_ADA4961,
        ID_ADL5240,
+       ID_HMC792,
        ID_HMC1119,
 };
 
@@ -64,6 +66,10 @@ static struct ad8366_info ad8366_infos[] = {
                .gain_min = -11500,
                .gain_max = 20000,
        },
+       [ID_HMC792] = {
+               .gain_min = -15750,
+               .gain_max = 0,
+       },
        [ID_HMC1119] = {
                .gain_min = -31750,
                .gain_max = 0,
@@ -90,6 +96,7 @@ static int ad8366_write(struct iio_dev *indio_dev,
        case ID_ADL5240:
                st->data[0] = (ch_a & 0x3F);
                break;
+       case ID_HMC792:
        case ID_HMC1119:
                st->data[0] = ch_a;
                break;
@@ -127,6 +134,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev,
                case ID_ADL5240:
                        gain = 20000 - 31500 + code * 500;
                        break;
+               case ID_HMC792:
+                       gain = -1 * code * 500;
+                       break;
                case ID_HMC1119:
                        gain = -1 * code * 250;
                        break;
@@ -176,6 +186,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
        case ID_ADL5240:
                code = ((gain - 500 - 20000) / 500) & 0x3F;
                break;
+       case ID_HMC792:
+               code = (abs(gain) / 500) & 0x3F;
+               break;
        case ID_HMC1119:
                code = (abs(gain) / 250) & 0x7F;
                break;
@@ -261,6 +274,7 @@ static int ad8366_probe(struct spi_device *spi)
                break;
        case ID_ADA4961:
        case ID_ADL5240:
+       case ID_HMC792:
        case ID_HMC1119:
                st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH);
                if (IS_ERR(st->reset_gpio)) {
@@ -314,6 +328,7 @@ static const struct spi_device_id ad8366_id[] = {
        {"ad8366",  ID_AD8366},
        {"ada4961", ID_ADA4961},
        {"adl5240", ID_ADL5240},
+       {"hmc792a", ID_HMC792},
        {"hmc1119", ID_HMC1119},
        {}
 };
index d656d2f..4c03b9e 100644 (file)
@@ -541,6 +541,7 @@ static int ad7150_probe(struct i2c_client *client)
        const struct i2c_device_id *id = i2c_client_get_device_id(client);
        struct ad7150_chip_info *chip;
        struct iio_dev *indio_dev;
+       bool use_irq = true;
        int ret;
 
        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
@@ -561,14 +562,13 @@ static int ad7150_probe(struct i2c_client *client)
 
        chip->interrupts[0] = fwnode_irq_get(dev_fwnode(&client->dev), 0);
        if (chip->interrupts[0] < 0)
-               return chip->interrupts[0];
-       if (id->driver_data == AD7150) {
+               use_irq = false;
+       else if (id->driver_data == AD7150) {
                chip->interrupts[1] = fwnode_irq_get(dev_fwnode(&client->dev), 1);
                if (chip->interrupts[1] < 0)
-                       return chip->interrupts[1];
+                       use_irq = false;
        }
-       if (chip->interrupts[0] &&
-           (id->driver_data == AD7151 || chip->interrupts[1])) {
+       if (use_irq) {
                irq_set_status_flags(chip->interrupts[0], IRQ_NOAUTOEN);
                ret = devm_request_threaded_irq(&client->dev,
                                                chip->interrupts[0],
index a4f22d9..ca6b202 100644 (file)
@@ -36,6 +36,8 @@
 #define SCD4X_WRITE_BUF_SIZE 5
 #define SCD4X_FRC_MIN_PPM 0
 #define SCD4X_FRC_MAX_PPM 2000
+#define SCD4X_PRESSURE_COMP_MIN_MBAR 700
+#define SCD4X_PRESSURE_COMP_MAX_MBAR 1200
 #define SCD4X_READY_MASK 0x01
 
 /*Commands SCD4X*/
@@ -45,6 +47,8 @@ enum scd4x_cmd {
        CMD_STOP_MEAS           = 0x3f86,
        CMD_SET_TEMP_OFFSET     = 0x241d,
        CMD_GET_TEMP_OFFSET     = 0x2318,
+       CMD_SET_AMB_PRESSURE    = 0xe000,
+       CMD_GET_AMB_PRESSURE    = 0xe000,
        CMD_FRC                 = 0x362f,
        CMD_SET_ASC             = 0x2416,
        CMD_GET_ASC             = 0x2313,
@@ -137,7 +141,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd,
         * Measurement needs to be stopped before sending commands.
         * Except for reading measurement and data ready command.
         */
-       if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) {
+       if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&
+           (cmd != CMD_GET_AMB_PRESSURE)) {
                ret = scd4x_send_command(state, CMD_STOP_MEAS);
                if (ret)
                        return ret;
@@ -166,7 +171,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd,
        }
 
        /* start measurement */
-       if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) {
+       if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&
+           (cmd != CMD_GET_AMB_PRESSURE)) {
                ret = scd4x_send_command(state, CMD_START_MEAS);
                if (ret)
                        return ret;
@@ -188,9 +194,11 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a
        buf[4] = crc;
 
        /* measurement needs to be stopped before sending commands */
-       ret = scd4x_send_command(state, CMD_STOP_MEAS);
-       if (ret)
-               return ret;
+       if (cmd != CMD_SET_AMB_PRESSURE) {
+               ret = scd4x_send_command(state, CMD_STOP_MEAS);
+               if (ret)
+                       return ret;
+       }
 
        /* execution time */
        msleep_interruptible(500);
@@ -200,7 +208,7 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a
                return ret;
 
        /* start measurement, except for forced calibration command */
-       if (cmd != CMD_FRC) {
+       if ((cmd != CMD_FRC) && (cmd != CMD_SET_AMB_PRESSURE)) {
                ret = scd4x_send_command(state, CMD_START_MEAS);
                if (ret)
                        return ret;
@@ -338,6 +346,18 @@ static int scd4x_read_raw(struct iio_dev *indio_dev,
 
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
+               if (chan->output) {
+                       mutex_lock(&state->lock);
+                       ret = scd4x_read(state, CMD_GET_AMB_PRESSURE, &tmp, sizeof(tmp));
+                       mutex_unlock(&state->lock);
+
+                       if (ret)
+                               return ret;
+
+                       *val = be16_to_cpu(tmp);
+                       return IIO_VAL_INT;
+               }
+
                ret = iio_device_claim_direct_mode(indio_dev);
                if (ret)
                        return ret;
@@ -386,6 +406,25 @@ static int scd4x_read_raw(struct iio_dev *indio_dev,
        }
 }
 
+static const int scd4x_pressure_calibbias_available[] = {
+       SCD4X_PRESSURE_COMP_MIN_MBAR, 1, SCD4X_PRESSURE_COMP_MAX_MBAR,
+};
+
+static int scd4x_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+                           const int **vals, int *type, int *length, long mask)
+{
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               *vals = scd4x_pressure_calibbias_available;
+               *type = IIO_VAL_INT;
+
+               return IIO_AVAIL_RANGE;
+       }
+
+       return -EINVAL;
+}
+
+
 static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
                                int val, int val2, long mask)
 {
@@ -399,6 +438,21 @@ static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const
                mutex_unlock(&state->lock);
 
                return ret;
+       case IIO_CHAN_INFO_RAW:
+               switch (chan->type) {
+               case IIO_PRESSURE:
+                       if (val < SCD4X_PRESSURE_COMP_MIN_MBAR ||
+                           val > SCD4X_PRESSURE_COMP_MAX_MBAR)
+                               return -EINVAL;
+
+                       mutex_lock(&state->lock);
+                       ret = scd4x_write(state, CMD_SET_AMB_PRESSURE, val);
+                       mutex_unlock(&state->lock);
+
+                       return ret;
+               default:
+                       return -EINVAL;
+               }
        default:
                return -EINVAL;
        }
@@ -503,10 +557,23 @@ static const struct iio_info scd4x_info = {
        .attrs = &scd4x_attr_group,
        .read_raw = scd4x_read_raw,
        .write_raw = scd4x_write_raw,
+       .read_avail = scd4x_read_avail,
 };
 
 static const struct iio_chan_spec scd4x_channels[] = {
        {
+               /*
+                * this channel is special in a sense we are pretending that
+                * sensor is able to change measurement chamber pressure but in
+                * fact we're just setting pressure compensation value
+                */
+               .type = IIO_PRESSURE,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
+               .output = 1,
+               .scan_index = -1,
+       },
+       {
                .type = IIO_CONCENTRATION,
                .channel2 = IIO_MOD_CO2,
                .modified = 1,
index 0334b49..1ccb5cc 100644 (file)
@@ -5,6 +5,7 @@
 
 source "drivers/iio/common/cros_ec_sensors/Kconfig"
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/inv_sensors/Kconfig"
 source "drivers/iio/common/ms_sensors/Kconfig"
 source "drivers/iio/common/scmi_sensors/Kconfig"
 source "drivers/iio/common/ssp_sensors/Kconfig"
index fad40e1..d3e9522 100644 (file)
@@ -10,6 +10,7 @@
 # When adding new entries keep the list in alphabetical order
 obj-y += cros_ec_sensors/
 obj-y += hid-sensors/
+obj-y += inv_sensors/
 obj-y += ms_sensors/
 obj-y += scmi_sensors/
 obj-y += ssp_sensors/
diff --git a/drivers/iio/common/inv_sensors/Kconfig b/drivers/iio/common/inv_sensors/Kconfig
new file mode 100644 (file)
index 0000000..28815fb
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# TDK-InvenSense sensors common library
+#
+
+config IIO_INV_SENSORS_TIMESTAMP
+       tristate
diff --git a/drivers/iio/common/inv_sensors/Makefile b/drivers/iio/common/inv_sensors/Makefile
new file mode 100644 (file)
index 0000000..dcf39f2
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for TDK-InvenSense sensors module.
+#
+
+obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_sensors_timestamp.o
@@ -3,25 +3,23 @@
  * Copyright (C) 2020 Invensense, Inc.
  */
 
+#include <linux/errno.h>
 #include <linux/kernel.h>
-#include <linux/regmap.h>
 #include <linux/math64.h>
+#include <linux/module.h>
 
-#include "inv_icm42600.h"
-#include "inv_icm42600_timestamp.h"
+#include <linux/iio/common/inv_sensors_timestamp.h>
 
-/* internal chip period is 32kHz, 31250ns */
-#define INV_ICM42600_TIMESTAMP_PERIOD          31250
-/* allow a jitter of +/- 2% */
-#define INV_ICM42600_TIMESTAMP_JITTER          2
-/* compute min and max periods accepted */
-#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p)          \
-       (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100)
-#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p)          \
-       (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100)
+/* compute jitter, min and max following jitter in per mille */
+#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter)            \
+       (div_s64((_val) * (_jitter), 1000))
+#define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter)               \
+       (((_val) * (1000 - (_jitter))) / 1000)
+#define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter)               \
+       (((_val) * (1000 + (_jitter))) / 1000)
 
 /* Add a new value inside an accumulator and update the estimate value */
-static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
+static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val)
 {
        uint64_t sum = 0;
        size_t i;
@@ -40,65 +38,57 @@ static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
        acc->val = div_u64(sum, i);
 }
 
-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
-                                uint32_t period)
+void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
+                               const struct inv_sensors_timestamp_chip *chip)
 {
-       /* initial odr for sensor after reset is 1kHz */
-       const uint32_t default_period = 1000000;
+       memset(ts, 0, sizeof(*ts));
+
+       /* save chip parameters and compute min and max clock period */
+       ts->chip = *chip;
+       ts->min_period = INV_SENSORS_TIMESTAMP_MIN(chip->clock_period, chip->jitter);
+       ts->max_period = INV_SENSORS_TIMESTAMP_MAX(chip->clock_period, chip->jitter);
 
        /* current multiplier and period values after reset */
-       ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD;
-       ts->period = default_period;
-       /* new set multiplier is the one from chip initialization */
-       ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
+       ts->mult = chip->init_period / chip->clock_period;
+       ts->period = chip->init_period;
 
        /* use theoretical value for chip period */
-       inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD);
+       inv_update_acc(&ts->chip_period, chip->clock_period);
 }
+EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, IIO_INV_SENSORS_TIMESTAMP);
 
-int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
-{
-       unsigned int val;
-
-       /* enable timestamp register */
-       val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
-             INV_ICM42600_TMST_CONFIG_TMST_EN;
-       return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
-                                 INV_ICM42600_TMST_CONFIG_MASK, val);
-}
-
-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
-                                     uint32_t period, bool fifo)
+int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
+                                    uint32_t period, bool fifo)
 {
        /* when FIFO is on, prevent odr change if one is already pending */
        if (fifo && ts->new_mult != 0)
                return -EAGAIN;
 
-       ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
+       ts->new_mult = period / ts->chip.clock_period;
 
        return 0;
 }
+EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
 
-static bool inv_validate_period(uint32_t period, uint32_t mult)
+static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult)
 {
-       const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD;
        uint32_t period_min, period_max;
 
        /* check that period is acceptable */
-       period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult;
-       period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult;
+       period_min = ts->min_period * mult;
+       period_max = ts->max_period * mult;
        if (period > period_min && period < period_max)
                return true;
        else
                return false;
 }
 
-static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts,
-                                  uint32_t mult, uint32_t period)
+static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
+                                   uint32_t mult, uint32_t period)
 {
        uint32_t new_chip_period;
 
-       if (!inv_validate_period(period, mult))
+       if (!inv_validate_period(ts, period, mult))
                return false;
 
        /* update chip internal period estimation */
@@ -109,7 +99,7 @@ static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts,
        return true;
 }
 
-static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
+static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts)
 {
        int64_t delta, jitter;
        int64_t adjust;
@@ -118,7 +108,7 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
        delta = ts->it.lo - ts->timestamp;
 
        /* adjust timestamp while respecting jitter */
-       jitter = div_s64((int64_t)ts->period * INV_ICM42600_TIMESTAMP_JITTER, 100);
+       jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter);
        if (delta > jitter)
                adjust = jitter;
        else if (delta < -jitter)
@@ -129,13 +119,13 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
        ts->timestamp += adjust;
 }
 
-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
+void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
                                      uint32_t fifo_period, size_t fifo_nb,
                                      size_t sensor_nb, int64_t timestamp)
 {
-       struct inv_icm42600_timestamp_interval *it;
+       struct inv_sensors_timestamp_interval *it;
        int64_t delta, interval;
-       const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
+       const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
        uint32_t period = ts->period;
        bool valid = false;
 
@@ -165,10 +155,11 @@ void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
        if (valid)
                inv_align_timestamp_it(ts);
 }
+EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP);
 
-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
-                                     uint32_t fifo_period, size_t fifo_nb,
-                                     unsigned int fifo_no)
+void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
+                                    uint32_t fifo_period, size_t fifo_nb,
+                                    unsigned int fifo_no)
 {
        int64_t interval;
        uint32_t fifo_mult;
@@ -189,10 +180,15 @@ void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
         */
        if (ts->timestamp != 0) {
                /* compute measured fifo period */
-               fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
+               fifo_mult = fifo_period / ts->chip.clock_period;
                fifo_period = fifo_mult * ts->chip_period.val;
                /* computes time interval between interrupt and this sample */
                interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
                ts->timestamp = ts->it.up - interval;
        }
 }
+EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense sensors timestamp module");
+MODULE_LICENSE("GPL");
index 3acd9c3..93b8be1 100644 (file)
@@ -389,6 +389,17 @@ config MCP4725
          To compile this driver as a module, choose M here: the module
          will be called mcp4725.
 
+config MCP4728
+       tristate "MCP4728 DAC driver"
+       depends on I2C
+       help
+         Say Y here if you want to build a driver for the Microchip
+         MCP4728 quad channel, 12-bit digital-to-analog converter (DAC)
+         with I2C interface.
+
+         To compile this driver as a module, choose M here: the module
+         will be called mcp4728.
+
 config MCP4922
        tristate "MCP4902, MCP4912, MCP4922 DAC driver"
        depends on SPI
index addd97a..5b2bac9 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_MAX517) += max517.o
 obj-$(CONFIG_MAX5522) += max5522.o
 obj-$(CONFIG_MAX5821) += max5821.o
 obj-$(CONFIG_MCP4725) += mcp4725.o
+obj-$(CONFIG_MCP4728) += mcp4728.o
 obj-$(CONFIG_MCP4922) += mcp4922.o
 obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
 obj-$(CONFIG_STM32_DAC) += stm32-dac.o
diff --git a/drivers/iio/dac/mcp4728.c b/drivers/iio/dac/mcp4728.c
new file mode 100644 (file)
index 0000000..5113f67
--- /dev/null
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Support for Microchip MCP4728
+ *
+ * Copyright (C) 2023 Andrea Collamati <andrea.collamati@gmail.com>
+ *
+ * Based on mcp4725 by Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * Driver for the Microchip I2C 12-bit digital-to-analog quad channels
+ * converter (DAC).
+ *
+ * (7-bit I2C slave address 0x60, the three LSBs can be configured in
+ * hardware)
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#define MCP4728_RESOLUTION       12
+#define MCP4728_N_CHANNELS       4
+
+#define MCP4728_CMD_MASK         GENMASK(7, 3)
+#define MCP4728_CHSEL_MASK       GENMASK(2, 1)
+#define MCP4728_UDAC_MASK        BIT(0)
+
+#define MCP4728_VREF_MASK        BIT(7)
+#define MCP4728_PDMODE_MASK      GENMASK(6, 5)
+#define MCP4728_GAIN_MASK        BIT(4)
+
+#define MCP4728_DAC_H_MASK       GENMASK(3, 0)
+#define MCP4728_DAC_L_MASK       GENMASK(7, 0)
+
+#define MCP4728_RDY_MASK         BIT(7)
+
+#define MCP4728_MW_CMD           0x08 /* Multiwrite Command */
+#define MCP4728_SW_CMD           0x0A /* Sequential Write Command with EEPROM */
+
+#define MCP4728_READ_RESPONSE_LEN (MCP4728_N_CHANNELS * 3 * 2)
+#define MCP4728_WRITE_EEPROM_LEN  (1 + MCP4728_N_CHANNELS * 2)
+
+enum vref_mode {
+       MCP4728_VREF_EXTERNAL_VDD    = 0,
+       MCP4728_VREF_INTERNAL_2048mV = 1,
+};
+
+enum gain_mode {
+       MCP4728_GAIN_X1 = 0,
+       MCP4728_GAIN_X2 = 1,
+};
+
+enum iio_powerdown_mode {
+       MCP4728_IIO_1K,
+       MCP4728_IIO_100K,
+       MCP4728_IIO_500K,
+};
+
+struct mcp4728_channel_data {
+       enum vref_mode ref_mode;
+       enum iio_powerdown_mode pd_mode;
+       enum gain_mode g_mode;
+       u16 dac_value;
+};
+
+/* MCP4728 Full Scale Ranges
+ * the device available ranges are
+ * - VREF = VDD                                FSR = from 0.0V to VDD
+ * - VREF = Internal   Gain = 1        FSR = from 0.0V to VREF
+ * - VREF = Internal   Gain = 2        FSR = from 0.0V to 2*VREF
+ */
+enum mcp4728_scale {
+       MCP4728_SCALE_VDD,
+       MCP4728_SCALE_VINT_NO_GAIN,
+       MCP4728_SCALE_VINT_GAIN_X2,
+       MCP4728_N_SCALES
+};
+
+struct mcp4728_data {
+       struct i2c_client *client;
+       struct regulator *vdd_reg;
+       bool powerdown;
+       int scales_avail[MCP4728_N_SCALES * 2];
+       struct mcp4728_channel_data chdata[MCP4728_N_CHANNELS];
+};
+
+#define MCP4728_CHAN(chan) {                                           \
+       .type = IIO_VOLTAGE,                                            \
+       .output = 1,                                                    \
+       .indexed = 1,                                                   \
+       .channel = chan,                                                \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)    |               \
+                             BIT(IIO_CHAN_INFO_SCALE),                 \
+       .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+       .ext_info = mcp4728_ext_info,                                   \
+}
+
+static int mcp4728_suspend(struct device *dev);
+static int mcp4728_resume(struct device *dev);
+
+static ssize_t mcp4728_store_eeprom(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t len)
+{
+       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+       struct mcp4728_data *data = iio_priv(indio_dev);
+       u8 outbuf[MCP4728_WRITE_EEPROM_LEN];
+       int tries = 20;
+       u8 inbuf[3];
+       bool state;
+       int ret;
+       unsigned int i;
+
+       ret = kstrtobool(buf, &state);
+       if (ret < 0)
+               return ret;
+
+       if (!state)
+               return 0;
+
+       outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_SW_CMD);
+
+       for (i = 0; i < MCP4728_N_CHANNELS; i++) {
+               struct mcp4728_channel_data *ch = &data->chdata[i];
+               int offset                      = 1 + i * 2;
+
+               outbuf[offset] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode);
+
+               if (data->powerdown) {
+                       u8 mcp4728_pd_mode = ch->pd_mode + 1;
+
+                       outbuf[offset] |= FIELD_PREP(MCP4728_PDMODE_MASK,
+                                                    mcp4728_pd_mode);
+               }
+
+               outbuf[offset] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode);
+               outbuf[offset] |=
+                       FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8);
+               outbuf[offset + 1] =
+                       FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value);
+       }
+
+       ret = i2c_master_send(data->client, outbuf, MCP4728_WRITE_EEPROM_LEN);
+       if (ret < 0)
+               return ret;
+       else if (ret != MCP4728_WRITE_EEPROM_LEN)
+               return -EIO;
+
+       /* wait RDY signal for write complete, takes up to 50ms */
+       while (tries--) {
+               msleep(20);
+               ret = i2c_master_recv(data->client, inbuf, 3);
+               if (ret < 0)
+                       return ret;
+               else if (ret != 3)
+                       return -EIO;
+
+               if (FIELD_GET(MCP4728_RDY_MASK, inbuf[0]))
+                       break;
+       }
+
+       if (tries < 0) {
+               dev_err(&data->client->dev, "%s failed, incomplete\n",
+                       __func__);
+               return -EIO;
+       }
+       return len;
+}
+
+static IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, mcp4728_store_eeprom, 0);
+
+static struct attribute *mcp4728_attributes[] = {
+       &iio_dev_attr_store_eeprom.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group mcp4728_attribute_group = {
+       .attrs = mcp4728_attributes,
+};
+
+static int mcp4728_program_channel_cfg(int channel, struct iio_dev *indio_dev)
+{
+       struct mcp4728_data *data       = iio_priv(indio_dev);
+       struct mcp4728_channel_data *ch = &data->chdata[channel];
+       u8 outbuf[3];
+       int ret;
+
+       outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_MW_CMD);
+       outbuf[0] |= FIELD_PREP(MCP4728_CHSEL_MASK, channel);
+       outbuf[0] |= FIELD_PREP(MCP4728_UDAC_MASK, 0);
+
+       outbuf[1] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode);
+
+       if (data->powerdown)
+               outbuf[1] |= FIELD_PREP(MCP4728_PDMODE_MASK, ch->pd_mode + 1);
+
+       outbuf[1] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode);
+       outbuf[1] |= FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8);
+       outbuf[2] = FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value);
+
+       ret = i2c_master_send(data->client, outbuf, 3);
+       if (ret < 0)
+               return ret;
+       else if (ret != 3)
+               return -EIO;
+
+       return 0;
+}
+
+static const char *const mcp4728_powerdown_modes[] = { "1kohm_to_gnd",
+                                                      "100kohm_to_gnd",
+                                                      "500kohm_to_gnd" };
+
+static int mcp4728_get_powerdown_mode(struct iio_dev *indio_dev,
+                                     const struct iio_chan_spec *chan)
+{
+       struct mcp4728_data *data = iio_priv(indio_dev);
+
+       return data->chdata[chan->channel].pd_mode;
+}
+
+static int mcp4728_set_powerdown_mode(struct iio_dev *indio_dev,
+                                     const struct iio_chan_spec *chan,
+                                     unsigned int mode)
+{
+       struct mcp4728_data *data = iio_priv(indio_dev);
+
+       data->chdata[chan->channel].pd_mode = mode;
+
+       return 0;
+}
+
+static ssize_t mcp4728_read_powerdown(struct iio_dev *indio_dev,
+                                     uintptr_t private,
+                                     const struct iio_chan_spec *chan,
+                                     char *buf)
+{
+       struct mcp4728_data *data = iio_priv(indio_dev);
+
+       return sysfs_emit(buf, "%d\n", data->powerdown);
+}
+
+static ssize_t mcp4728_write_powerdown(struct iio_dev *indio_dev,
+                                      uintptr_t private,
+                                      const struct iio_chan_spec *chan,
+                                      const char *buf, size_t len)
+{
+       struct mcp4728_data *data = iio_priv(indio_dev);
+       bool state;
+       int ret;
+
+       ret = kstrtobool(buf, &state);
+       if (ret)
+               return ret;
+
+       if (state)
+               ret = mcp4728_suspend(&data->client->dev);
+       else
+               ret = mcp4728_resume(&data->client->dev);
+
+       if (ret < 0)
+               return ret;
+
+       return len;
+}
+
+static const struct iio_enum mcp4728_powerdown_mode_enum = {
+       .items     = mcp4728_powerdown_modes,
+       .num_items = ARRAY_SIZE(mcp4728_powerdown_modes),
+       .get       = mcp4728_get_powerdown_mode,
+       .set       = mcp4728_set_powerdown_mode,
+};
+
+static const struct iio_chan_spec_ext_info mcp4728_ext_info[] = {
+       {
+               .name   = "powerdown",
+               .read   = mcp4728_read_powerdown,
+               .write  = mcp4728_write_powerdown,
+               .shared = IIO_SEPARATE,
+       },
+       IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4728_powerdown_mode_enum),
+       IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE,
+                          &mcp4728_powerdown_mode_enum),
+       {},
+};
+
+static const struct iio_chan_spec mcp4728_channels[MCP4728_N_CHANNELS] = {
+       MCP4728_CHAN(0),
+       MCP4728_CHAN(1),
+       MCP4728_CHAN(2),
+       MCP4728_CHAN(3),
+};
+
+static void mcp4728_get_scale_avail(enum mcp4728_scale scale,
+                                   struct mcp4728_data *data, int *val,
+                                   int *val2)
+{
+       *val  = data->scales_avail[scale * 2];
+       *val2 = data->scales_avail[scale * 2 + 1];
+}
+
+static void mcp4728_get_scale(int channel, struct mcp4728_data *data, int *val,
+                             int *val2)
+{
+       int ref_mode = data->chdata[channel].ref_mode;
+       int g_mode   = data->chdata[channel].g_mode;
+
+       if (ref_mode == MCP4728_VREF_EXTERNAL_VDD) {
+               mcp4728_get_scale_avail(MCP4728_SCALE_VDD, data, val, val2);
+       } else {
+               if (g_mode == MCP4728_GAIN_X1) {
+                       mcp4728_get_scale_avail(MCP4728_SCALE_VINT_NO_GAIN,
+                                               data, val, val2);
+               } else {
+                       mcp4728_get_scale_avail(MCP4728_SCALE_VINT_GAIN_X2,
+                                               data, val, val2);
+               }
+       }
+}
+
+static int mcp4728_find_matching_scale(struct mcp4728_data *data, int val,
+                                      int val2)
+{
+       for (int i = 0; i < MCP4728_N_SCALES; i++) {
+               if (data->scales_avail[i * 2] == val &&
+                   data->scales_avail[i * 2 + 1] == val2)
+                       return i;
+       }
+       return -EINVAL;
+}
+
+static int mcp4728_set_scale(int channel, struct mcp4728_data *data, int val,
+                            int val2)
+{
+       int scale = mcp4728_find_matching_scale(data, val, val2);
+
+       if (scale < 0)
+               return scale;
+
+       switch (scale) {
+       case MCP4728_SCALE_VDD:
+               data->chdata[channel].ref_mode = MCP4728_VREF_EXTERNAL_VDD;
+               return 0;
+       case MCP4728_SCALE_VINT_NO_GAIN:
+               data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV;
+               data->chdata[channel].g_mode   = MCP4728_GAIN_X1;
+               return 0;
+       case MCP4728_SCALE_VINT_GAIN_X2:
+               data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV;
+               data->chdata[channel].g_mode   = MCP4728_GAIN_X2;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int mcp4728_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan, int *val,
+                           int *val2, long mask)
+{
+       struct mcp4728_data *data = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               *val = data->chdata[chan->channel].dac_value;
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               mcp4728_get_scale(chan->channel, data, val, val2);
+               return IIO_VAL_INT_PLUS_MICRO;
+       }
+       return -EINVAL;
+}
+
+static int mcp4728_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan, int val,
+                            int val2, long mask)
+{
+       struct mcp4728_data *data = iio_priv(indio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               if (val < 0 || val > GENMASK(MCP4728_RESOLUTION - 1, 0))
+                       return -EINVAL;
+               data->chdata[chan->channel].dac_value = val;
+               return mcp4728_program_channel_cfg(chan->channel, indio_dev);
+       case IIO_CHAN_INFO_SCALE:
+               ret = mcp4728_set_scale(chan->channel, data, val, val2);
+               if (ret)
+                       return ret;
+
+               return mcp4728_program_channel_cfg(chan->channel, indio_dev);
+       default:
+               return -EINVAL;
+       }
+}
+
+static void mcp4728_init_scale_avail(enum mcp4728_scale scale, int vref_mv,
+                                    struct mcp4728_data *data)
+{
+       s64 tmp;
+       int value_micro;
+       int value_int;
+
+       tmp       = (s64)vref_mv * 1000000LL >> MCP4728_RESOLUTION;
+       value_int = div_s64_rem(tmp, 1000000LL, &value_micro);
+
+       data->scales_avail[scale * 2]     = value_int;
+       data->scales_avail[scale * 2 + 1] = value_micro;
+}
+
+static int mcp4728_init_scales_avail(struct mcp4728_data *data)
+{
+       int ret;
+
+       ret = regulator_get_voltage(data->vdd_reg);
+       if (ret < 0)
+               return ret;
+
+       mcp4728_init_scale_avail(MCP4728_SCALE_VDD, ret / 1000, data);
+       mcp4728_init_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 2048, data);
+       mcp4728_init_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 4096, data);
+
+       return 0;
+}
+
+static int mcp4728_read_avail(struct iio_dev *indio_dev,
+                             struct iio_chan_spec const *chan,
+                             const int **vals, int *type, int *length,
+                             long info)
+{
+       struct mcp4728_data *data = iio_priv(indio_dev);
+
+       switch (info) {
+       case IIO_CHAN_INFO_SCALE:
+               *type = IIO_VAL_INT_PLUS_MICRO;
+
+               switch (chan->type) {
+               case IIO_VOLTAGE:
+                       *vals   = data->scales_avail;
+                       *length = MCP4728_N_SCALES * 2;
+                       return IIO_AVAIL_LIST;
+               default:
+                       return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct iio_info mcp4728_info = {
+       .read_raw   = mcp4728_read_raw,
+       .write_raw  = mcp4728_write_raw,
+       .read_avail = &mcp4728_read_avail,
+       .attrs      = &mcp4728_attribute_group,
+};
+
+static int mcp4728_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct mcp4728_data *data = iio_priv(indio_dev);
+       unsigned int i;
+
+       data->powerdown = true;
+
+       for (i = 0; i < MCP4728_N_CHANNELS; i++) {
+               int err = mcp4728_program_channel_cfg(i, indio_dev);
+
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int mcp4728_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct mcp4728_data *data = iio_priv(indio_dev);
+       int err                   = 0;
+       unsigned int i;
+
+       data->powerdown = false;
+
+       for (i = 0; i < MCP4728_N_CHANNELS; i++) {
+               int ret = mcp4728_program_channel_cfg(i, indio_dev);
+
+               if (ret)
+                       err = ret;
+       }
+       return err;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mcp4728_pm_ops, mcp4728_suspend,
+                               mcp4728_resume);
+
+static int mcp4728_init_channels_data(struct mcp4728_data *data)
+{
+       u8 inbuf[MCP4728_READ_RESPONSE_LEN];
+       int ret;
+       unsigned int i;
+
+       ret = i2c_master_recv(data->client, inbuf, MCP4728_READ_RESPONSE_LEN);
+       if (ret < 0) {
+               return dev_err_probe(&data->client->dev, ret,
+                                    "failed to read mcp4728 conf.\n");
+       } else if (ret != MCP4728_READ_RESPONSE_LEN) {
+               return dev_err_probe(&data->client->dev, -EIO,
+                       "failed to read mcp4728 conf. Wrong Response Len ret=%d\n",
+                       ret);
+       }
+
+       for (i = 0; i < MCP4728_N_CHANNELS; i++) {
+               struct mcp4728_channel_data *ch = &data->chdata[i];
+               u8 r2                           = inbuf[i * 6 + 1];
+               u8 r3                           = inbuf[i * 6 + 2];
+
+               ch->dac_value = FIELD_GET(MCP4728_DAC_H_MASK, r2) << 8 |
+                               FIELD_GET(MCP4728_DAC_L_MASK, r3);
+               ch->ref_mode = FIELD_GET(MCP4728_VREF_MASK, r2);
+               ch->pd_mode  = FIELD_GET(MCP4728_PDMODE_MASK, r2);
+               ch->g_mode   = FIELD_GET(MCP4728_GAIN_MASK, r2);
+       }
+
+       return 0;
+}
+
+static void mcp4728_reg_disable(void *reg)
+{
+       regulator_disable(reg);
+}
+
+static int mcp4728_probe(struct i2c_client *client)
+{
+       const struct i2c_device_id *id = i2c_client_get_device_id(client);
+       struct mcp4728_data *data;
+       struct iio_dev *indio_dev;
+       int err;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+       data->client = client;
+
+       data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
+       if (IS_ERR(data->vdd_reg))
+               return PTR_ERR(data->vdd_reg);
+
+       err = regulator_enable(data->vdd_reg);
+       if (err)
+               return err;
+
+       err = devm_add_action_or_reset(&client->dev, mcp4728_reg_disable,
+                                      data->vdd_reg);
+       if (err)
+               return err;
+
+       /*
+        * MCP4728 has internal EEPROM that save each channel boot
+        * configuration. It means that device configuration is unknown to the
+        * driver at kernel boot. mcp4728_init_channels_data() reads back DAC
+        * settings and stores them in data structure.
+        */
+       err = mcp4728_init_channels_data(data);
+       if (err) {
+               return dev_err_probe(&client->dev, err,
+                       "failed to read mcp4728 current configuration\n");
+       }
+
+       err = mcp4728_init_scales_avail(data);
+       if (err) {
+               return dev_err_probe(&client->dev, err,
+                                    "failed to init scales\n");
+       }
+
+       indio_dev->name         = id->name;
+       indio_dev->info         = &mcp4728_info;
+       indio_dev->channels     = mcp4728_channels;
+       indio_dev->num_channels = MCP4728_N_CHANNELS;
+       indio_dev->modes        = INDIO_DIRECT_MODE;
+
+       return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id mcp4728_id[] = {
+       { "mcp4728", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, mcp4728_id);
+
+static const struct of_device_id mcp4728_of_match[] = {
+       { .compatible = "microchip,mcp4728" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mcp4728_of_match);
+
+static struct i2c_driver mcp4728_driver = {
+       .driver = {
+               .name = "mcp4728",
+               .of_match_table = mcp4728_of_match,
+               .pm = pm_sleep_ptr(&mcp4728_pm_ops),
+       },
+       .probe = mcp4728_probe,
+       .id_table = mcp4728_id,
+};
+module_i2c_driver(mcp4728_driver);
+
+MODULE_AUTHOR("Andrea Collamati <andrea.collamati@gmail.com>");
+MODULE_DESCRIPTION("MCP4728 12-bit DAC");
+MODULE_LICENSE("GPL");
index fe8d46c..848baa6 100644 (file)
@@ -78,6 +78,7 @@ enum {
 enum {
        ADMV8818_AUTO_MODE,
        ADMV8818_MANUAL_MODE,
+       ADMV8818_BYPASS_MODE,
 };
 
 struct admv8818_state {
@@ -114,7 +115,8 @@ static const struct regmap_config admv8818_regmap_config = {
 
 static const char * const admv8818_modes[] = {
        [0] = "auto",
-       [1] = "manual"
+       [1] = "manual",
+       [2] = "bypass"
 };
 
 static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq)
@@ -394,6 +396,36 @@ static int admv8818_reg_access(struct iio_dev *indio_dev,
                return regmap_write(st->regmap, reg, write_val);
 }
 
+static int admv8818_filter_bypass(struct admv8818_state *st)
+{
+       int ret;
+
+       mutex_lock(&st->lock);
+
+       ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW,
+                                ADMV8818_SW_IN_SET_WR0_MSK |
+                                ADMV8818_SW_IN_WR0_MSK |
+                                ADMV8818_SW_OUT_SET_WR0_MSK |
+                                ADMV8818_SW_OUT_WR0_MSK,
+                                FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) |
+                                FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, 0) |
+                                FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) |
+                                FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, 0));
+       if (ret)
+               goto exit;
+
+       ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER,
+                                ADMV8818_HPF_WR0_MSK |
+                                ADMV8818_LPF_WR0_MSK,
+                                FIELD_PREP(ADMV8818_HPF_WR0_MSK, 0) |
+                                FIELD_PREP(ADMV8818_LPF_WR0_MSK, 0));
+
+exit:
+       mutex_unlock(&st->lock);
+
+       return ret;
+}
+
 static int admv8818_get_mode(struct iio_dev *indio_dev,
                             const struct iio_chan_spec *chan)
 {
@@ -411,14 +443,22 @@ static int admv8818_set_mode(struct iio_dev *indio_dev,
 
        if (!st->clkin) {
                if (mode == ADMV8818_MANUAL_MODE)
-                       return 0;
+                       goto set_mode;
+
+               if (mode == ADMV8818_BYPASS_MODE) {
+                       ret = admv8818_filter_bypass(st);
+                       if (ret)
+                               return ret;
+
+                       goto set_mode;
+               }
 
                return -EINVAL;
        }
 
        switch (mode) {
        case ADMV8818_AUTO_MODE:
-               if (!st->filter_mode)
+               if (st->filter_mode == ADMV8818_AUTO_MODE)
                        return 0;
 
                ret = clk_prepare_enable(st->clkin);
@@ -434,20 +474,27 @@ static int admv8818_set_mode(struct iio_dev *indio_dev,
 
                break;
        case ADMV8818_MANUAL_MODE:
-               if (st->filter_mode)
-                       return 0;
+       case ADMV8818_BYPASS_MODE:
+               if (st->filter_mode == ADMV8818_AUTO_MODE) {
+                       clk_disable_unprepare(st->clkin);
 
-               clk_disable_unprepare(st->clkin);
+                       ret = clk_notifier_unregister(st->clkin, &st->nb);
+                       if (ret)
+                               return ret;
+               }
 
-               ret = clk_notifier_unregister(st->clkin, &st->nb);
-               if (ret)
-                       return ret;
+               if (mode == ADMV8818_BYPASS_MODE) {
+                       ret = admv8818_filter_bypass(st);
+                       if (ret)
+                               return ret;
+               }
 
                break;
        default:
                return -EINVAL;
        }
 
+set_mode:
        st->filter_mode = mode;
 
        return ret;
index 8c8e0bb..6355c1f 100644 (file)
@@ -382,6 +382,11 @@ static const struct iio_info admv1013_info = {
        .debugfs_reg_access = &admv1013_reg_access,
 };
 
+static const char * const admv1013_vcc_regs[] = {
+        "vcc-drv", "vcc2-drv", "vcc-vva", "vcc-amp1", "vcc-amp2",
+        "vcc-env", "vcc-bg", "vcc-bg2", "vcc-mixer", "vcc-quad"
+};
+
 static int admv1013_freq_change(struct notifier_block *nb, unsigned long action, void *data)
 {
        struct admv1013_state *st = container_of(nb, struct admv1013_state, nb);
@@ -557,6 +562,15 @@ static int admv1013_properties_parse(struct admv1013_state *st)
                return dev_err_probe(&spi->dev, PTR_ERR(st->reg),
                                     "failed to get the common-mode voltage\n");
 
+       ret = devm_regulator_bulk_get_enable(&st->spi->dev,
+                                            ARRAY_SIZE(admv1013_vcc_regs),
+                                            admv1013_vcc_regs);
+       if (ret) {
+               dev_err_probe(&spi->dev, ret,
+                             "Failed to request VCC regulators\n");
+               return ret;
+       }
+
        return 0;
 }
 
index 3abffb0..17275a5 100644 (file)
@@ -115,8 +115,6 @@ enum {
        ADIS16475_SCAN_ACCEL_Y,
        ADIS16475_SCAN_ACCEL_Z,
        ADIS16475_SCAN_TEMP,
-       ADIS16475_SCAN_DIAG_S_FLAGS,
-       ADIS16475_SCAN_CRC_FAILURE,
 };
 
 static bool low_rate_allow;
@@ -728,6 +726,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
                .max_dec = 1999,
                .sync = adis16475_sync_mode,
                .num_sync = ARRAY_SIZE(adis16475_sync_mode),
+               .has_burst32 = true,
                .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
        },
        [ADIS16477_2] = {
@@ -743,6 +742,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
                .max_dec = 1999,
                .sync = adis16475_sync_mode,
                .num_sync = ARRAY_SIZE(adis16475_sync_mode),
+               .has_burst32 = true,
                .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
        },
        [ADIS16477_3] = {
@@ -758,6 +758,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
                .max_dec = 1999,
                .sync = adis16475_sync_mode,
                .num_sync = ARRAY_SIZE(adis16475_sync_mode),
+               .has_burst32 = true,
                .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
        },
        [ADIS16465_1] = {
index 50cbcfc..f56b081 100644 (file)
@@ -3,6 +3,7 @@
 config INV_ICM42600
        tristate
        select IIO_BUFFER
+       select IIO_INV_SENSORS_TIMESTAMP
 
 config INV_ICM42600_I2C
        tristate "InvenSense ICM-426xx I2C driver"
index 291714d..0f49f6d 100644 (file)
@@ -6,7 +6,6 @@ inv-icm42600-y += inv_icm42600_gyro.o
 inv-icm42600-y += inv_icm42600_accel.o
 inv-icm42600-y += inv_icm42600_temp.o
 inv-icm42600-y += inv_icm42600_buffer.o
-inv-icm42600-y += inv_icm42600_timestamp.o
 
 obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o
 inv-icm42600-i2c-y += inv_icm42600_i2c.o
index c3f433a..b1e4fde 100644 (file)
 #include <linux/regmap.h>
 #include <linux/delay.h>
 #include <linux/math64.h>
-#include <linux/iio/iio.h>
+
 #include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
 #include <linux/iio/kfifo_buf.h>
 
 #include "inv_icm42600.h"
 #include "inv_icm42600_temp.h"
 #include "inv_icm42600_buffer.h"
-#include "inv_icm42600_timestamp.h"
 
 #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info)          \
        {                                                               \
@@ -98,7 +99,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
                                               const unsigned long *scan_mask)
 {
        struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
-       struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
+       struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
        struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
        unsigned int fifo_en = 0;
        unsigned int sleep_temp = 0;
@@ -126,7 +127,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
        }
 
        /* update data FIFO write */
-       inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
+       inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
        ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
        if (ret)
                goto out_unlock;
@@ -311,7 +312,7 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev,
                                        int val, int val2)
 {
        struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
-       struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
+       struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
        struct device *dev = regmap_get_device(st->map);
        unsigned int idx;
        struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
@@ -330,8 +331,8 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev,
        pm_runtime_get_sync(dev);
        mutex_lock(&st->lock);
 
-       ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
-                                               iio_buffer_enabled(indio_dev));
+       ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
+                                              iio_buffer_enabled(indio_dev));
        if (ret)
                goto out_unlock;
 
@@ -707,7 +708,8 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
 {
        struct device *dev = regmap_get_device(st->map);
        const char *name;
-       struct inv_icm42600_timestamp *ts;
+       struct inv_sensors_timestamp_chip ts_chip;
+       struct inv_sensors_timestamp *ts;
        struct iio_dev *indio_dev;
        int ret;
 
@@ -719,8 +721,15 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
        if (!indio_dev)
                return ERR_PTR(-ENOMEM);
 
+       /*
+        * clock period is 32kHz (31250ns)
+        * jitter is +/- 2% (20 per mille)
+        */
+       ts_chip.clock_period = 31250;
+       ts_chip.jitter = 20;
+       ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
        ts = iio_priv(indio_dev);
-       inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
+       inv_sensors_timestamp_init(ts, &ts_chip);
 
        iio_device_set_drvdata(indio_dev, st);
        indio_dev->name = name;
@@ -745,7 +754,7 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
 int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
 {
        struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
-       struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
+       struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
        ssize_t i, size;
        unsigned int no;
        const void *accel, *gyro, *timestamp;
@@ -768,15 +777,15 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
 
                /* update odr */
                if (odr & INV_ICM42600_SENSOR_ACCEL)
-                       inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
-                                                        st->fifo.nb.total, no);
+                       inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+                                                       st->fifo.nb.total, no);
 
                /* buffer is copied to userspace, zeroing it to avoid any data leak */
                memset(&buffer, 0, sizeof(buffer));
                memcpy(&buffer.accel, accel, sizeof(buffer.accel));
                /* convert 8 bits FIFO temperature in high resolution format */
                buffer.temp = temp ? (*temp * 64) : 0;
-               ts_val = inv_icm42600_timestamp_pop(ts);
+               ts_val = inv_sensors_timestamp_pop(ts);
                iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
        }
 
index 32d7f83..6ef1df9 100644 (file)
@@ -9,11 +9,12 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/delay.h>
-#include <linux/iio/iio.h>
+
 #include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
 
 #include "inv_icm42600.h"
-#include "inv_icm42600_timestamp.h"
 #include "inv_icm42600_buffer.h"
 
 /* FIFO header: 1 byte */
@@ -275,12 +276,12 @@ static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev)
 {
        struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
        struct device *dev = regmap_get_device(st->map);
-       struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
+       struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
 
        pm_runtime_get_sync(dev);
 
        mutex_lock(&st->lock);
-       inv_icm42600_timestamp_reset(ts);
+       inv_sensors_timestamp_reset(ts);
        mutex_unlock(&st->lock);
 
        return 0;
@@ -504,7 +505,7 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
 
 int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
 {
-       struct inv_icm42600_timestamp *ts;
+       struct inv_sensors_timestamp *ts;
        int ret;
 
        if (st->fifo.nb.total == 0)
@@ -512,8 +513,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
 
        /* handle gyroscope timestamp and FIFO data parsing */
        ts = iio_priv(st->indio_gyro);
-       inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
-                                        st->fifo.nb.gyro, st->timestamp.gyro);
+       inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
+                                       st->fifo.nb.gyro, st->timestamp.gyro);
        if (st->fifo.nb.gyro > 0) {
                ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
                if (ret)
@@ -522,8 +523,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
 
        /* handle accelerometer timestamp and FIFO data parsing */
        ts = iio_priv(st->indio_accel);
-       inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
-                                        st->fifo.nb.accel, st->timestamp.accel);
+       inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
+                                       st->fifo.nb.accel, st->timestamp.accel);
        if (st->fifo.nb.accel > 0) {
                ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
                if (ret)
@@ -536,7 +537,7 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
 int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
                                     unsigned int count)
 {
-       struct inv_icm42600_timestamp *ts;
+       struct inv_sensors_timestamp *ts;
        int64_t gyro_ts, accel_ts;
        int ret;
 
@@ -552,9 +553,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
 
        if (st->fifo.nb.gyro > 0) {
                ts = iio_priv(st->indio_gyro);
-               inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
-                                                st->fifo.nb.total, st->fifo.nb.gyro,
-                                                gyro_ts);
+               inv_sensors_timestamp_interrupt(ts, st->fifo.period,
+                                               st->fifo.nb.total, st->fifo.nb.gyro,
+                                               gyro_ts);
                ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
                if (ret)
                        return ret;
@@ -562,9 +563,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
 
        if (st->fifo.nb.accel > 0) {
                ts = iio_priv(st->indio_accel);
-               inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
-                                                st->fifo.nb.total, st->fifo.nb.accel,
-                                                accel_ts);
+               inv_sensors_timestamp_interrupt(ts, st->fifo.period,
+                                               st->fifo.nb.total, st->fifo.nb.accel,
+                                               accel_ts);
                ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
                if (ret)
                        return ret;
index 7b3a2a0..a5e8190 100644 (file)
 #include <linux/pm_runtime.h>
 #include <linux/property.h>
 #include <linux/regmap.h>
+
 #include <linux/iio/iio.h>
 
 #include "inv_icm42600.h"
 #include "inv_icm42600_buffer.h"
-#include "inv_icm42600_timestamp.h"
 
 static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
        {
@@ -516,6 +516,17 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq,
                                         "inv_icm42600", st);
 }
 
+static int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
+{
+       unsigned int val;
+
+       /* enable timestamp register */
+       val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
+             INV_ICM42600_TMST_CONFIG_TMST_EN;
+       return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
+                                 INV_ICM42600_TMST_CONFIG_MASK, val);
+}
+
 static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st)
 {
        int ret;
@@ -788,3 +799,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42600_pm_ops, IIO_ICM42600) = {
 MODULE_AUTHOR("InvenSense, Inc.");
 MODULE_DESCRIPTION("InvenSense ICM-426xx device driver");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);
index 9d94a85..3bf946e 100644 (file)
 #include <linux/regmap.h>
 #include <linux/delay.h>
 #include <linux/math64.h>
-#include <linux/iio/iio.h>
+
 #include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
 #include <linux/iio/kfifo_buf.h>
 
 #include "inv_icm42600.h"
 #include "inv_icm42600_temp.h"
 #include "inv_icm42600_buffer.h"
-#include "inv_icm42600_timestamp.h"
 
 #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info)           \
        {                                                               \
@@ -98,7 +99,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
                                              const unsigned long *scan_mask)
 {
        struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
-       struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
+       struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
        struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
        unsigned int fifo_en = 0;
        unsigned int sleep_gyro = 0;
@@ -126,7 +127,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
        }
 
        /* update data FIFO write */
-       inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
+       inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
        ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
        if (ret)
                goto out_unlock;
@@ -323,7 +324,7 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev,
                                       int val, int val2)
 {
        struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
-       struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
+       struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
        struct device *dev = regmap_get_device(st->map);
        unsigned int idx;
        struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
@@ -342,8 +343,8 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev,
        pm_runtime_get_sync(dev);
        mutex_lock(&st->lock);
 
-       ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
-                                               iio_buffer_enabled(indio_dev));
+       ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
+                                              iio_buffer_enabled(indio_dev));
        if (ret)
                goto out_unlock;
 
@@ -718,7 +719,8 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
 {
        struct device *dev = regmap_get_device(st->map);
        const char *name;
-       struct inv_icm42600_timestamp *ts;
+       struct inv_sensors_timestamp_chip ts_chip;
+       struct inv_sensors_timestamp *ts;
        struct iio_dev *indio_dev;
        int ret;
 
@@ -730,8 +732,15 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
        if (!indio_dev)
                return ERR_PTR(-ENOMEM);
 
+       /*
+        * clock period is 32kHz (31250ns)
+        * jitter is +/- 2% (20 per mille)
+        */
+       ts_chip.clock_period = 31250;
+       ts_chip.jitter = 20;
+       ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
        ts = iio_priv(indio_dev);
-       inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
+       inv_sensors_timestamp_init(ts, &ts_chip);
 
        iio_device_set_drvdata(indio_dev, st);
        indio_dev->name = name;
@@ -757,7 +766,7 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
 int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
 {
        struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
-       struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
+       struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
        ssize_t i, size;
        unsigned int no;
        const void *accel, *gyro, *timestamp;
@@ -780,15 +789,15 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
 
                /* update odr */
                if (odr & INV_ICM42600_SENSOR_GYRO)
-                       inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
-                                                        st->fifo.nb.total, no);
+                       inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+                                                       st->fifo.nb.total, no);
 
                /* buffer is copied to userspace, zeroing it to avoid any data leak */
                memset(&buffer, 0, sizeof(buffer));
                memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
                /* convert 8 bits FIFO temperature in high resolution format */
                buffer.temp = temp ? (*temp * 64) : 0;
-               ts_val = inv_icm42600_timestamp_pop(ts);
+               ts_val = inv_sensors_timestamp_pop(ts);
                iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
        }
 
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h
deleted file mode 100644 (file)
index 4e4f331..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2020 Invensense, Inc.
- */
-
-#ifndef INV_ICM42600_TIMESTAMP_H_
-#define INV_ICM42600_TIMESTAMP_H_
-
-#include <linux/kernel.h>
-
-struct inv_icm42600_state;
-
-/**
- * struct inv_icm42600_timestamp_interval - timestamps interval
- * @lo:        interval lower bound
- * @up:        interval upper bound
- */
-struct inv_icm42600_timestamp_interval {
-       int64_t lo;
-       int64_t up;
-};
-
-/**
- * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation
- * @val:       current estimation of the value, the mean of all values
- * @idx:       current index of the next free place in values table
- * @values:    table of all measured values, use for computing the mean
- */
-struct inv_icm42600_timestamp_acc {
-       uint32_t val;
-       size_t idx;
-       uint32_t values[32];
-};
-
-/**
- * struct inv_icm42600_timestamp - timestamp management states
- * @it:                        interrupts interval timestamps
- * @timestamp:         store last timestamp for computing next data timestamp
- * @mult:              current internal period multiplier
- * @new_mult:          new set internal period multiplier (not yet effective)
- * @period:            measured current period of the sensor
- * @chip_period:       accumulator for computing internal chip period
- */
-struct inv_icm42600_timestamp {
-       struct inv_icm42600_timestamp_interval it;
-       int64_t timestamp;
-       uint32_t mult;
-       uint32_t new_mult;
-       uint32_t period;
-       struct inv_icm42600_timestamp_acc chip_period;
-};
-
-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
-                                uint32_t period);
-
-int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st);
-
-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
-                                     uint32_t period, bool fifo);
-
-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
-                                     uint32_t fifo_period, size_t fifo_nb,
-                                     size_t sensor_nb, int64_t timestamp);
-
-static inline int64_t
-inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts)
-{
-       ts->timestamp += ts->period;
-       return ts->timestamp;
-}
-
-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
-                                     uint32_t fifo_period, size_t fifo_nb,
-                                     unsigned int fifo_no);
-
-static inline void
-inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts)
-{
-       const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL};
-
-       ts->it = interval_init;
-       ts->timestamp = 0;
-}
-
-#endif
index 64dd73d..5f62e4f 100644 (file)
@@ -7,6 +7,7 @@ config INV_MPU6050_IIO
        tristate
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
+       select IIO_INV_SENSORS_TIMESTAMP
 
 config INV_MPU6050_I2C
        tristate "Invensense MPU6050 devices (I2C)"
index 592a6e6..29f906c 100644 (file)
 #include <linux/jiffies.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
-#include <linux/iio/iio.h>
 #include <linux/acpi.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
+
 #include "inv_mpu_iio.h"
 #include "inv_mpu_magn.h"
 
@@ -521,6 +524,7 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
        int result;
        u8 d;
        struct inv_mpu6050_state *st = iio_priv(indio_dev);
+       struct inv_sensors_timestamp_chip timestamp;
 
        result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr);
        if (result)
@@ -544,12 +548,12 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
        if (result)
                return result;
 
-       /*
-        * Internal chip period is 1ms (1kHz).
-        * Let's use at the beginning the theorical value before measuring
-        * with interrupt timestamps.
-        */
-       st->chip_period = NSEC_PER_MSEC;
+       /* clock jitter is +/- 2% */
+       timestamp.clock_period = NSEC_PER_SEC / INV_MPU6050_INTERNAL_FREQ_HZ;
+       timestamp.jitter = 20;
+       timestamp.init_period =
+                       NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
+       inv_sensors_timestamp_init(&st->timestamp, &timestamp);
 
        /* magn chip init, noop if not present in the chip */
        result = inv_mpu_magn_probe(st);
@@ -936,6 +940,8 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
        int fifo_rate;
+       u32 fifo_period;
+       bool fifo_on;
        u8 d;
        int result;
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -952,12 +958,21 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
        d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
        /* compute back the fifo rate to handle truncation cases */
        fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d);
+       fifo_period = NSEC_PER_SEC / fifo_rate;
 
        mutex_lock(&st->lock);
        if (d == st->chip_config.divider) {
                result = 0;
                goto fifo_rate_fail_unlock;
        }
+
+       fifo_on = st->chip_config.accl_fifo_enable ||
+                 st->chip_config.gyro_fifo_enable ||
+                 st->chip_config.magn_fifo_enable;
+       result = inv_sensors_timestamp_update_odr(&st->timestamp, fifo_period, fifo_on);
+       if (result)
+               goto fifo_rate_fail_unlock;
+
        result = pm_runtime_resume_and_get(pdev);
        if (result)
                goto fifo_rate_fail_unlock;
@@ -1330,6 +1345,9 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
        st->reg = hw_info[st->chip_type].reg;
        memcpy(&st->chip_config, hw_info[st->chip_type].config,
               sizeof(st->chip_config));
+       st->data = devm_kzalloc(regmap_get_device(st->map), st->hw->fifo_size, GFP_KERNEL);
+       if (st->data == NULL)
+               return -ENOMEM;
 
        /* check chip self-identification */
        result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, &regval);
@@ -1785,3 +1803,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_mpu_pmops, IIO_MPU6050) = {
 MODULE_AUTHOR("Invensense Corporation");
 MODULE_DESCRIPTION("Invensense device MPU6050 driver");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);
index b4ab2c3..ed5a96e 100644 (file)
@@ -9,15 +9,17 @@
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
 #include <linux/mutex.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/buffer.h>
+#include <linux/platform_data/invensense_mpu6050.h>
 #include <linux/regmap.h>
-#include <linux/iio/sysfs.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
 #include <linux/iio/kfifo_buf.h>
 #include <linux/iio/trigger.h>
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/trigger_consumer.h>
-#include <linux/platform_data/invensense_mpu6050.h>
+#include <linux/iio/sysfs.h>
 
 /**
  *  struct inv_mpu6050_reg_map - Notable registers.
@@ -170,16 +172,14 @@ struct inv_mpu6050_hw {
  *  @map               regmap pointer.
  *  @irq               interrupt number.
  *  @irq_mask          the int_pin_cfg mask to configure interrupt type.
- *  @chip_period:      chip internal period estimation (~1kHz).
- *  @it_timestamp:     timestamp from previous interrupt.
- *  @data_timestamp:   timestamp for next data sample.
+ *  @timestamp:                timestamping module
  *  @vdd_supply:       VDD voltage regulator for the chip.
  *  @vddio_supply      I/O voltage regulator for the chip.
  *  @magn_disabled:     magnetometer disabled for backward compatibility reason.
  *  @magn_raw_to_gauss:        coefficient to convert mag raw value to Gauss.
  *  @magn_orient:       magnetometer sensor chip orientation if available.
  *  @suspended_sensors:        sensors mask of sensors turned off for suspend
- *  @data:             dma safe buffer used for bulk reads.
+ *  @data:             read buffer used for bulk reads.
  */
 struct inv_mpu6050_state {
        struct mutex lock;
@@ -196,16 +196,14 @@ struct inv_mpu6050_state {
        int irq;
        u8 irq_mask;
        unsigned skip_samples;
-       s64 chip_period;
-       s64 it_timestamp;
-       s64 data_timestamp;
+       struct inv_sensors_timestamp timestamp;
        struct regulator *vdd_supply;
        struct regulator *vddio_supply;
        bool magn_disabled;
        s32 magn_raw_to_gauss[3];
        struct iio_mount_matrix magn_orient;
        unsigned int suspended_sensors;
-       u8 data[INV_MPU6050_OUTPUT_DATA_SIZE] __aligned(IIO_DMA_MINALIGN);
+       u8 *data;
 };
 
 /*register and associated bit definition*/
index 45c3752..66d4ba0 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/poll.h>
 #include <linux/math64.h>
-#include "inv_mpu_iio.h"
-
-/**
- *  inv_mpu6050_update_period() - Update chip internal period estimation
- *
- *  @st:               driver state
- *  @timestamp:                the interrupt timestamp
- *  @nb:               number of data set in the fifo
- *
- *  This function uses interrupt timestamps to estimate the chip period and
- *  to choose the data timestamp to come.
- */
-static void inv_mpu6050_update_period(struct inv_mpu6050_state *st,
-                                     s64 timestamp, size_t nb)
-{
-       /* Period boundaries for accepting timestamp */
-       const s64 period_min =
-               (NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100;
-       const s64 period_max =
-               (NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100;
-       const s32 divider = INV_MPU6050_FREQ_DIVIDER(st);
-       s64 delta, interval;
-       bool use_it_timestamp = false;
-
-       if (st->it_timestamp == 0) {
-               /* not initialized, forced to use it_timestamp */
-               use_it_timestamp = true;
-       } else if (nb == 1) {
-               /*
-                * Validate the use of it timestamp by checking if interrupt
-                * has been delayed.
-                * nb > 1 means interrupt was delayed for more than 1 sample,
-                * so it's obviously not good.
-                * Compute the chip period between 2 interrupts for validating.
-                */
-               delta = div_s64(timestamp - st->it_timestamp, divider);
-               if (delta > period_min && delta < period_max) {
-                       /* update chip period and use it timestamp */
-                       st->chip_period = (st->chip_period + delta) / 2;
-                       use_it_timestamp = true;
-               }
-       }
 
-       if (use_it_timestamp) {
-               /*
-                * Manage case of multiple samples in the fifo (nb > 1):
-                * compute timestamp corresponding to the first sample using
-                * estimated chip period.
-                */
-               interval = (nb - 1) * st->chip_period * divider;
-               st->data_timestamp = timestamp - interval;
-       }
+#include <linux/iio/common/inv_sensors_timestamp.h>
 
-       /* save it timestamp */
-       st->it_timestamp = timestamp;
-}
-
-/**
- *  inv_mpu6050_get_timestamp() - Return the current data timestamp
- *
- *  @st:               driver state
- *  @return:           current data timestamp
- *
- *  This function returns the current data timestamp and prepares for next one.
- */
-static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
-{
-       s64 ts;
-
-       /* return current data timestamp and increment */
-       ts = st->data_timestamp;
-       st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st);
-
-       return ts;
-}
+#include "inv_mpu_iio.h"
 
 static int inv_reset_fifo(struct iio_dev *indio_dev)
 {
@@ -121,7 +50,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
        size_t bytes_per_datum;
        int result;
        u16 fifo_count;
+       u32 fifo_period;
        s64 timestamp;
+       u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
        int int_status;
        size_t i, nb;
 
@@ -175,21 +106,30 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
                goto flush_fifo;
        }
 
-       /* compute and process all complete datum */
+       /* compute and process only all complete datum */
        nb = fifo_count / bytes_per_datum;
-       inv_mpu6050_update_period(st, pf->timestamp, nb);
+       fifo_count = nb * bytes_per_datum;
+       /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */
+       fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
+       inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp);
+       inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0);
+
+       /* clear internal data buffer for avoiding kernel data leak */
+       memset(data, 0, sizeof(data));
+
+       /* read all data once and process every samples */
+       result = regmap_noinc_read(st->map, st->reg->fifo_r_w, st->data, fifo_count);
+       if (result)
+               goto flush_fifo;
        for (i = 0; i < nb; ++i) {
-               result = regmap_noinc_read(st->map, st->reg->fifo_r_w,
-                                          st->data, bytes_per_datum);
-               if (result)
-                       goto flush_fifo;
                /* skip first samples if needed */
                if (st->skip_samples) {
                        st->skip_samples--;
                        continue;
                }
-               timestamp = inv_mpu6050_get_timestamp(st);
-               iio_push_to_buffers_with_timestamp(indio_dev, st->data, timestamp);
+               memcpy(data, &st->data[i * bytes_per_datum], bytes_per_datum);
+               timestamp = inv_sensors_timestamp_pop(&st->timestamp);
+               iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
        }
 
 end_session:
index 8825468..676704f 100644 (file)
@@ -4,6 +4,9 @@
 */
 
 #include <linux/pm_runtime.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
 #include "inv_mpu_iio.h"
 
 static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev)
@@ -106,7 +109,8 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
        int ret;
 
        if (enable) {
-               st->it_timestamp = 0;
+               /* reset timestamping */
+               inv_sensors_timestamp_reset(&st->timestamp);
                /* reset FIFO */
                d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
                ret = regmap_write(st->map, st->reg->user_ctrl, d);
index adcba83..d752e9c 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* The industrial I/O core
+/*
+ * The industrial I/O core
  *
  * Copyright (c) 2008 Jonathan Cameron
  *
@@ -183,7 +184,9 @@ static const char * const iio_chan_info_postfix[] = {
  * @indio_dev:         Device structure whose ID is being queried
  *
  * The IIO device ID is a unique index used for example for the naming
- * of the character device /dev/iio\:device[ID]
+ * of the character device /dev/iio\:device[ID].
+ *
+ * Returns: Unique ID for the device.
  */
 int iio_device_id(struct iio_dev *indio_dev)
 {
@@ -196,14 +199,16 @@ EXPORT_SYMBOL_GPL(iio_device_id);
 /**
  * iio_buffer_enabled() - helper function to test if the buffer is enabled
  * @indio_dev:         IIO device structure for device
+ *
+ * Returns: True, if the buffer is enabled.
  */
 bool iio_buffer_enabled(struct iio_dev *indio_dev)
 {
        struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
 
-       return iio_dev_opaque->currentmode
-               & (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE |
-                  INDIO_BUFFER_SOFTWARE);
+       return iio_dev_opaque->currentmode &
+              (INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE |
+               INDIO_BUFFER_TRIGGERED);
 }
 EXPORT_SYMBOL_GPL(iio_buffer_enabled);
 
@@ -225,6 +230,9 @@ EXPORT_SYMBOL_GPL(iio_get_debugfs_dentry);
  * iio_find_channel_from_si() - get channel from its scan index
  * @indio_dev:         device
  * @si:                        scan index to match
+ *
+ * Returns:
+ * Constant pointer to iio_chan_spec, if scan index matches, NULL on failure.
  */
 const struct iio_chan_spec
 *iio_find_channel_from_si(struct iio_dev *indio_dev, int si)
@@ -249,7 +257,9 @@ EXPORT_SYMBOL(iio_read_const_attr);
 /**
  * iio_device_set_clock() - Set current timestamping clock for the device
  * @indio_dev: IIO device structure containing the device
- * @clock_id: timestamping clock posix identifier to set.
+ * @clock_id: timestamping clock POSIX identifier to set.
+ *
+ * Returns: 0 on success, or a negative error code.
  */
 int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
 {
@@ -275,6 +285,8 @@ EXPORT_SYMBOL(iio_device_set_clock);
 /**
  * iio_device_get_clock() - Retrieve current timestamping clock for the device
  * @indio_dev: IIO device structure containing the device
+ *
+ * Returns: Clock ID of the current timestamping clock for the device.
  */
 clockid_t iio_device_get_clock(const struct iio_dev *indio_dev)
 {
@@ -287,6 +299,8 @@ EXPORT_SYMBOL(iio_device_get_clock);
 /**
  * iio_get_time_ns() - utility function to get a time stamp for events etc
  * @indio_dev: device
+ *
+ * Returns: Timestamp of the event in nanoseconds.
  */
 s64 iio_get_time_ns(const struct iio_dev *indio_dev)
 {
@@ -372,8 +386,8 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf,
        }
 
        iio_dev_opaque->read_buf_len = snprintf(iio_dev_opaque->read_buf,
-                                             sizeof(iio_dev_opaque->read_buf),
-                                             "0x%X\n", val);
+                                               sizeof(iio_dev_opaque->read_buf),
+                                               "0x%X\n", val);
 
        return simple_read_from_buffer(userbuf, count, ppos,
                                       iio_dev_opaque->read_buf,
@@ -389,7 +403,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file,
        char buf[80];
        int ret;
 
-       count = min_t(size_t, count, (sizeof(buf)-1));
+       count = min(count, sizeof(buf) - 1);
        if (copy_from_user(buf, userbuf, count))
                return -EFAULT;
 
@@ -476,8 +490,7 @@ static ssize_t iio_read_channel_ext_info(struct device *dev,
 
 static ssize_t iio_write_channel_ext_info(struct device *dev,
                                     struct device_attribute *attr,
-                                    const char *buf,
-                                        size_t len)
+                                    const char *buf, size_t len)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
@@ -524,7 +537,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev,
        i = e->get(indio_dev, chan);
        if (i < 0)
                return i;
-       else if (i >= e->num_items || !e->items[i])
+       if (i >= e->num_items || !e->items[i])
                return -EINVAL;
 
        return sysfs_emit(buf, "%s\n", e->items[i]);
@@ -569,9 +582,9 @@ static int iio_setup_mount_idmatrix(const struct device *dev,
 ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv,
                              const struct iio_chan_spec *chan, char *buf)
 {
-       const struct iio_mount_matrix *mtx = ((iio_get_mount_matrix_t *)
-                                             priv)(indio_dev, chan);
+       const struct iio_mount_matrix *mtx;
 
+       mtx = ((iio_get_mount_matrix_t *)priv)(indio_dev, chan);
        if (IS_ERR(mtx))
                return PTR_ERR(mtx);
 
@@ -594,7 +607,7 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix);
  * If device is assigned no mounting matrix property, a default 3x3 identity
  * matrix will be filled in.
  *
- * Return: 0 if success, or a negative error code on failure.
+ * Returns: 0 if success, or a negative error code on failure.
  */
 int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix)
 {
@@ -692,9 +705,9 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
  * @vals:      Pointer to the values, exact meaning depends on the
  *             type parameter.
  *
- * Return: 0 by default, a negative number on failure or the
- *        total number of characters written for a type that belongs
- *        to the IIO_VAL_* constant.
+ * Returns:
+ * 0 by default, a negative number on failure or the total number of characters
+ * written for a type that belongs to the IIO_VAL_* constant.
  */
 ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
 {
@@ -847,8 +860,8 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
  * @fract: The fractional part of the number
  * @scale_db: True if this should parse as dB
  *
- * Returns 0 on success, or a negative error code if the string could not be
- * parsed.
+ * Returns:
+ * 0 on success, or a negative error code if the string could not be parsed.
  */
 static int __iio_str_to_fixpoint(const char *str, int fract_mult,
                                 int *integer, int *fract, bool scale_db)
@@ -917,8 +930,8 @@ static int __iio_str_to_fixpoint(const char *str, int fract_mult,
  * @integer: The integer part of the number
  * @fract: The fractional part of the number
  *
- * Returns 0 on success, or a negative error code if the string could not be
- * parsed.
+ * Returns:
+ * 0 on success, or a negative error code if the string could not be parsed.
  */
 int iio_str_to_fixpoint(const char *str, int fract_mult,
                        int *integer, int *fract)
@@ -1009,14 +1022,12 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
        if (chan->modified && (shared_by == IIO_SEPARATE)) {
                if (chan->extend_name)
                        full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
-                                                iio_modifier_names[chan
-                                                                   ->channel2],
+                                                iio_modifier_names[chan->channel2],
                                                 chan->extend_name,
                                                 postfix);
                else
                        full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
-                                                iio_modifier_names[chan
-                                                                   ->channel2],
+                                                iio_modifier_names[chan->channel2],
                                                 postfix);
        } else {
                if (chan->extend_name == NULL || shared_by != IIO_SEPARATE)
@@ -1217,7 +1228,7 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
                                             &iio_dev_opaque->channel_attr_list);
                if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
                        continue;
-               else if (ret < 0)
+               if (ret < 0)
                        return ret;
                attrcount++;
        }
@@ -1255,7 +1266,7 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
                kfree(avail_postfix);
                if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
                        continue;
-               else if (ret < 0)
+               if (ret < 0)
                        return ret;
                attrcount++;
        }
@@ -1400,50 +1411,42 @@ static ssize_t label_show(struct device *dev, struct device_attribute *attr,
 
 static DEVICE_ATTR_RO(label);
 
+static const char * const clock_names[] = {
+       [CLOCK_REALTIME]                = "realtime",
+       [CLOCK_MONOTONIC]               = "monotonic",
+       [CLOCK_PROCESS_CPUTIME_ID]      = "process_cputime_id",
+       [CLOCK_THREAD_CPUTIME_ID]       = "thread_cputime_id",
+       [CLOCK_MONOTONIC_RAW]           = "monotonic_raw",
+       [CLOCK_REALTIME_COARSE]         = "realtime_coarse",
+       [CLOCK_MONOTONIC_COARSE]        = "monotonic_coarse",
+       [CLOCK_BOOTTIME]                = "boottime",
+       [CLOCK_REALTIME_ALARM]          = "realtime_alarm",
+       [CLOCK_BOOTTIME_ALARM]          = "boottime_alarm",
+       [CLOCK_SGI_CYCLE]               = "sgi_cycle",
+       [CLOCK_TAI]                     = "tai",
+};
+
 static ssize_t current_timestamp_clock_show(struct device *dev,
                                            struct device_attribute *attr,
                                            char *buf)
 {
        const struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        const clockid_t clk = iio_device_get_clock(indio_dev);
-       const char *name;
-       ssize_t sz;
 
        switch (clk) {
        case CLOCK_REALTIME:
-               name = "realtime\n";
-               sz = sizeof("realtime\n");
-               break;
        case CLOCK_MONOTONIC:
-               name = "monotonic\n";
-               sz = sizeof("monotonic\n");
-               break;
        case CLOCK_MONOTONIC_RAW:
-               name = "monotonic_raw\n";
-               sz = sizeof("monotonic_raw\n");
-               break;
        case CLOCK_REALTIME_COARSE:
-               name = "realtime_coarse\n";
-               sz = sizeof("realtime_coarse\n");
-               break;
        case CLOCK_MONOTONIC_COARSE:
-               name = "monotonic_coarse\n";
-               sz = sizeof("monotonic_coarse\n");
-               break;
        case CLOCK_BOOTTIME:
-               name = "boottime\n";
-               sz = sizeof("boottime\n");
-               break;
        case CLOCK_TAI:
-               name = "tai\n";
-               sz = sizeof("tai\n");
                break;
        default:
                BUG();
        }
 
-       memcpy(buf, name, sz);
-       return sz;
+       return sysfs_emit(buf, "%s\n", clock_names[clk]);
 }
 
 static ssize_t current_timestamp_clock_store(struct device *dev,
@@ -1453,22 +1456,23 @@ static ssize_t current_timestamp_clock_store(struct device *dev,
        clockid_t clk;
        int ret;
 
-       if (sysfs_streq(buf, "realtime"))
-               clk = CLOCK_REALTIME;
-       else if (sysfs_streq(buf, "monotonic"))
-               clk = CLOCK_MONOTONIC;
-       else if (sysfs_streq(buf, "monotonic_raw"))
-               clk = CLOCK_MONOTONIC_RAW;
-       else if (sysfs_streq(buf, "realtime_coarse"))
-               clk = CLOCK_REALTIME_COARSE;
-       else if (sysfs_streq(buf, "monotonic_coarse"))
-               clk = CLOCK_MONOTONIC_COARSE;
-       else if (sysfs_streq(buf, "boottime"))
-               clk = CLOCK_BOOTTIME;
-       else if (sysfs_streq(buf, "tai"))
-               clk = CLOCK_TAI;
-       else
+       ret = sysfs_match_string(clock_names, buf);
+       if (ret < 0)
+               return ret;
+       clk = ret;
+
+       switch (clk) {
+       case CLOCK_REALTIME:
+       case CLOCK_MONOTONIC:
+       case CLOCK_MONOTONIC_RAW:
+       case CLOCK_REALTIME_COARSE:
+       case CLOCK_MONOTONIC_COARSE:
+       case CLOCK_BOOTTIME:
+       case CLOCK_TAI:
+               break;
+       default:
                return -EINVAL;
+       }
 
        ret = iio_device_set_clock(dev_to_iio_dev(dev), clk);
        if (ret)
@@ -1484,7 +1488,7 @@ int iio_device_register_sysfs_group(struct iio_dev *indio_dev,
        const struct attribute_group **new, **old = iio_dev_opaque->groups;
        unsigned int cnt = iio_dev_opaque->groupcounter;
 
-       new = krealloc(old, sizeof(*new) * (cnt + 2), GFP_KERNEL);
+       new = krealloc_array(old, cnt + 2, sizeof(*new), GFP_KERNEL);
        if (!new)
                return -ENOMEM;
 
@@ -1621,7 +1625,10 @@ const struct device_type iio_device_type = {
  * iio_device_alloc() - allocate an iio_dev from a driver
  * @parent:            Parent device.
  * @sizeof_priv:       Space to allocate for private structure.
- **/
+ *
+ * Returns:
+ * Pointer to allocated iio_dev on success, NULL on failure.
+ */
 struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
 {
        struct iio_dev_opaque *iio_dev_opaque;
@@ -1677,7 +1684,7 @@ EXPORT_SYMBOL(iio_device_alloc);
 /**
  * iio_device_free() - free an iio_dev from a driver
  * @dev:               the iio_dev associated with the device
- **/
+ */
 void iio_device_free(struct iio_dev *dev)
 {
        if (dev)
@@ -1698,7 +1705,7 @@ static void devm_iio_device_release(void *iio_dev)
  * Managed iio_device_alloc. iio_dev allocated with this function is
  * automatically freed on driver detach.
  *
- * RETURNS:
+ * Returns:
  * Pointer to allocated iio_dev on success, NULL on failure.
  */
 struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv)
@@ -1725,8 +1732,8 @@ EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
  * @filp:      File structure for iio device used to keep and later access
  *             private data
  *
- * Return: 0 on success or -EBUSY if the device is already opened
- **/
+ * Returns: 0 on success or -EBUSY if the device is already opened
+ */
 static int iio_chrdev_open(struct inode *inode, struct file *filp)
 {
        struct iio_dev_opaque *iio_dev_opaque =
@@ -1759,7 +1766,7 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
  * @inode:     Inode structure pointer for the char device
  * @filp:      File structure pointer for the char device
  *
- * Return: 0 for successful release
+ * Returns: 0 for successful release.
  */
 static int iio_chrdev_release(struct inode *inode, struct file *filp)
 {
@@ -1798,7 +1805,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
        mutex_lock(&iio_dev_opaque->info_exist_lock);
 
-       /**
+       /*
         * The NULL check here is required to prevent crashing when a device
         * is being removed while userspace would still have open file handles
         * to try to access this device.
@@ -1976,7 +1983,7 @@ EXPORT_SYMBOL(__iio_device_register);
 /**
  * iio_device_unregister() - unregister a device from the IIO subsystem
  * @indio_dev:         Device structure representing the device.
- **/
+ */
 void iio_device_unregister(struct iio_dev *indio_dev)
 {
        struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
@@ -2027,7 +2034,7 @@ EXPORT_SYMBOL_GPL(__devm_iio_device_register);
  *
  * Use with iio_device_release_direct_mode()
  *
- * Returns: 0 on success, -EBUSY on failure
+ * Returns: 0 on success, -EBUSY on failure.
  */
 int iio_device_claim_direct_mode(struct iio_dev *indio_dev)
 {
index f77ce49..19f7a91 100644 (file)
@@ -252,6 +252,8 @@ static const char * const iio_ev_info_text[] = {
        [IIO_EV_INFO_TIMEOUT] = "timeout",
        [IIO_EV_INFO_RESET_TIMEOUT] = "reset_timeout",
        [IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay",
+       [IIO_EV_INFO_RUNNING_PERIOD] = "runningperiod",
+       [IIO_EV_INFO_RUNNING_COUNT] = "runningcount",
 };
 
 static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)
index f207e36..18f8315 100644 (file)
@@ -313,7 +313,7 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig,
        /* Enable trigger in driver */
        if (trig->ops && trig->ops->set_trigger_state && notinuse) {
                ret = trig->ops->set_trigger_state(trig, true);
-               if (ret < 0)
+               if (ret)
                        goto out_free_irq;
        }
 
index 0b30db7..e7f0b81 100644 (file)
@@ -227,7 +227,7 @@ static int cm3605_probe(struct platform_device *pdev)
 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
-               ret = dev_err_probe(dev, irq, "failed to get irq\n");
+               ret = irq;
                goto out_disable_aset;
        }
 
index b50bf89..6a6d778 100644 (file)
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * BU27008 ROHM Colour Sensor
+ * ROHM Colour Sensor driver for
+ * - BU27008 RGBC sensor
+ * - BU27010 RGBC + Flickering sensor
  *
  * Copyright (c) 2023, ROHM Semiconductor.
  */
 #include <linux/iio/trigger_consumer.h>
 #include <linux/iio/triggered_buffer.h>
 
+/*
+ * A word about register address and mask definitions.
+ *
+ * At a quick glance to the data-sheet register tables, the BU27010 has all the
+ * registers that the BU27008 has. On top of that the BU27010 adds couple of new
+ * ones.
+ *
+ * So, all definitions BU27008_REG_* are there also for BU27010 but none of the
+ * BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds
+ * some features (Flicker FIFO, more power control) on top of the BU27008.
+ *
+ * Unfortunately, some of the wheel has been re-invented. Even though the names
+ * of the registers have stayed the same, pretty much all of the functionality
+ * provided by the registers has changed place. Contents of all MODE_CONTROL
+ * registers on BU27008 and BU27010 are different.
+ *
+ * Chip-specific mapping from register addresses/bits to functionality is done
+ * in bu27_chip_data structures.
+ */
 #define BU27008_REG_SYSTEM_CONTROL     0x40
 #define BU27008_MASK_SW_RESET          BIT(7)
 #define BU27008_MASK_PART_ID           GENMASK(5, 0)
 #define BU27008_REG_MANUFACTURER_ID    0x92
 #define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID
 
+/* BU27010 specific definitions */
+
+#define BU27010_MASK_SW_RESET          BIT(7)
+#define BU27010_ID                     0x1b
+#define BU27010_REG_POWER              0x3e
+#define BU27010_MASK_POWER             BIT(0)
+
+#define BU27010_REG_RESET              0x3f
+#define BU27010_MASK_RESET             BIT(0)
+#define BU27010_RESET_RELEASE          BU27010_MASK_RESET
+
+#define BU27010_MASK_MEAS_EN           BIT(1)
+
+#define BU27010_MASK_CHAN_SEL          GENMASK(7, 6)
+#define BU27010_MASK_MEAS_MODE         GENMASK(5, 4)
+#define BU27010_MASK_RGBC_GAIN         GENMASK(3, 0)
+
+#define BU27010_MASK_DATA3_GAIN                GENMASK(7, 6)
+#define BU27010_MASK_DATA2_GAIN                GENMASK(5, 4)
+#define BU27010_MASK_DATA1_GAIN                GENMASK(3, 2)
+#define BU27010_MASK_DATA0_GAIN                GENMASK(1, 0)
+
+#define BU27010_MASK_FLC_MODE          BIT(7)
+#define BU27010_MASK_FLC_GAIN          GENMASK(4, 0)
+
+#define BU27010_REG_MODE_CONTROL4      0x44
+/* If flicker is ever to be supported the IRQ must be handled as a field */
+#define BU27010_IRQ_DIS_ALL            GENMASK(1, 0)
+#define BU27010_DRDY_EN                        BIT(0)
+#define BU27010_MASK_INT_SEL           GENMASK(1, 0)
+
+#define BU27010_REG_MODE_CONTROL5      0x45
+#define BU27010_MASK_RGB_VALID         BIT(7)
+#define BU27010_MASK_FLC_VALID         BIT(6)
+#define BU27010_MASK_WAIT_EN           BIT(3)
+#define BU27010_MASK_FIFO_EN           BIT(2)
+#define BU27010_MASK_RGB_EN            BIT(1)
+#define BU27010_MASK_FLC_EN            BIT(0)
+
+#define BU27010_REG_DATA_FLICKER_LO    0x56
+#define BU27010_MASK_DATA_FLICKER_HI   GENMASK(2, 0)
+#define BU27010_REG_FLICKER_COUNT      0x5a
+#define BU27010_REG_FIFO_LEVEL_LO      0x5b
+#define BU27010_MASK_FIFO_LEVEL_HI     BIT(0)
+#define BU27010_REG_FIFO_DATA_LO       0x5d
+#define BU27010_REG_FIFO_DATA_HI       0x5e
+#define BU27010_MASK_FIFO_DATA_HI      GENMASK(2, 0)
+#define BU27010_REG_MANUFACTURER_ID    0x92
+#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID
+
 /**
  * enum bu27008_chan_type - BU27008 channel types
  * @BU27008_RED:       Red channel. Always via data0.
@@ -117,6 +188,17 @@ static const unsigned long bu27008_scan_masks[] = {
  */
 #define BU27008_SCALE_1X 16
 
+/*
+ * On BU27010 available scales with gain 1x - 4096x,
+ * timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x.
+ *
+ * => Max total gain is HWGAIN * gain by integration time (8 * 4096)
+ *
+ * Using NANO precision for scale we must use scale 64x corresponding gain 1x
+ * to avoid precision loss.
+ */
+#define BU27010_SCALE_1X 64
+
 /* See the data sheet for the "Gain Setting" table */
 #define BU27008_GSEL_1X                0x00
 #define BU27008_GSEL_4X                0x08
@@ -152,10 +234,44 @@ static const struct iio_gain_sel_pair bu27008_gains_ir[] = {
        GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X),
 };
 
+#define BU27010_GSEL_1X                0x00    /* 000000 */
+#define BU27010_GSEL_4X                0x08    /* 001000 */
+#define BU27010_GSEL_16X       0x09    /* 001001 */
+#define BU27010_GSEL_64X       0x0e    /* 001110 */
+#define BU27010_GSEL_256X      0x1e    /* 011110 */
+#define BU27010_GSEL_1024X     0x2e    /* 101110 */
+#define BU27010_GSEL_4096X     0x3f    /* 111111 */
+
+static const struct iio_gain_sel_pair bu27010_gains[] = {
+       GAIN_SCALE_GAIN(1, BU27010_GSEL_1X),
+       GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
+       GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
+       GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
+       GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
+       GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
+       GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
+};
+
+static const struct iio_gain_sel_pair bu27010_gains_ir[] = {
+       GAIN_SCALE_GAIN(2, BU27010_GSEL_1X),
+       GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
+       GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
+       GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
+       GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
+       GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
+       GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
+};
+
 #define BU27008_MEAS_MODE_100MS                0x00
 #define BU27008_MEAS_MODE_55MS         0x01
 #define BU27008_MEAS_MODE_200MS                0x02
 #define BU27008_MEAS_MODE_400MS                0x04
+
+#define BU27010_MEAS_MODE_100MS                0x00
+#define BU27010_MEAS_MODE_55MS         0x03
+#define BU27010_MEAS_MODE_200MS                0x01
+#define BU27010_MEAS_MODE_400MS                0x02
+
 #define BU27008_MEAS_TIME_MAX_MS       400
 
 static const struct iio_itime_sel_mul bu27008_itimes[] = {
@@ -165,6 +281,13 @@ static const struct iio_itime_sel_mul bu27008_itimes[] = {
        GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1),
 };
 
+static const struct iio_itime_sel_mul bu27010_itimes[] = {
+       GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8),
+       GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4),
+       GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2),
+       GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1),
+};
+
 /*
  * All the RGBC channels share the same gain.
  * IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this
@@ -211,7 +334,35 @@ static const struct iio_chan_spec bu27008_channels[] = {
        IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS),
 };
 
+struct bu27008_data;
+
+struct bu27_chip_data {
+       const char *name;
+       int (*chip_init)(struct bu27008_data *data);
+       int (*get_gain_sel)(struct bu27008_data *data, int *sel);
+       int (*write_gain_sel)(struct bu27008_data *data, int sel);
+       const struct regmap_config *regmap_cfg;
+       const struct iio_gain_sel_pair *gains;
+       const struct iio_gain_sel_pair *gains_ir;
+       const struct iio_itime_sel_mul *itimes;
+       int num_gains;
+       int num_gains_ir;
+       int num_itimes;
+       int scale1x;
+
+       int drdy_en_reg;
+       int drdy_en_mask;
+       int meas_en_reg;
+       int meas_en_mask;
+       int valid_reg;
+       int chan_sel_reg;
+       int chan_sel_mask;
+       int int_time_mask;
+       u8 part_id;
+};
+
 struct bu27008_data {
+       const struct bu27_chip_data *cd;
        struct regmap *regmap;
        struct iio_trigger *trig;
        struct device *dev;
@@ -240,11 +391,29 @@ static const struct regmap_range bu27008_volatile_ranges[] = {
        },
 };
 
+static const struct regmap_range bu27010_volatile_ranges[] = {
+       {
+               .range_min = BU27010_REG_RESET,                 /* RSTB */
+               .range_max = BU27008_REG_SYSTEM_CONTROL,        /* RESET */
+       }, {
+               .range_min = BU27010_REG_MODE_CONTROL5,         /* VALID bits */
+               .range_max = BU27010_REG_MODE_CONTROL5,
+       }, {
+               .range_min = BU27008_REG_DATA0_LO,
+               .range_max = BU27010_REG_FIFO_DATA_HI,
+       },
+};
+
 static const struct regmap_access_table bu27008_volatile_regs = {
        .yes_ranges = &bu27008_volatile_ranges[0],
        .n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges),
 };
 
+static const struct regmap_access_table bu27010_volatile_regs = {
+       .yes_ranges = &bu27010_volatile_ranges[0],
+       .n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges),
+};
+
 static const struct regmap_range bu27008_read_only_ranges[] = {
        {
                .range_min = BU27008_REG_DATA0_LO,
@@ -255,11 +424,26 @@ static const struct regmap_range bu27008_read_only_ranges[] = {
        },
 };
 
+static const struct regmap_range bu27010_read_only_ranges[] = {
+       {
+               .range_min = BU27008_REG_DATA0_LO,
+               .range_max = BU27010_REG_FIFO_DATA_HI,
+       }, {
+               .range_min = BU27010_REG_MANUFACTURER_ID,
+               .range_max = BU27010_REG_MANUFACTURER_ID,
+       }
+};
+
 static const struct regmap_access_table bu27008_ro_regs = {
        .no_ranges = &bu27008_read_only_ranges[0],
        .n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges),
 };
 
+static const struct regmap_access_table bu27010_ro_regs = {
+       .no_ranges = &bu27010_read_only_ranges[0],
+       .n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges),
+};
+
 static const struct regmap_config bu27008_regmap = {
        .reg_bits = 8,
        .val_bits = 8,
@@ -282,50 +466,16 @@ static const struct regmap_config bu27008_regmap = {
        .disable_locking = true,
 };
 
-#define BU27008_MAX_VALID_RESULT_WAIT_US       50000
-#define BU27008_VALID_RESULT_WAIT_QUANTA_US    1000
-
-static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
-{
-       int ret, valid;
-       __le16 tmp;
-
-       ret = regmap_read_poll_timeout(data->regmap, BU27008_REG_MODE_CONTROL3,
-                                      valid, (valid & BU27008_MASK_VALID),
-                                      BU27008_VALID_RESULT_WAIT_QUANTA_US,
-                                      BU27008_MAX_VALID_RESULT_WAIT_US);
-       if (ret)
-               return ret;
-
-       ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp));
-       if (ret)
-               dev_err(data->dev, "Reading channel data failed\n");
-
-       *val = le16_to_cpu(tmp);
-
-       return ret;
-}
-
-static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
-{
-       int ret, sel;
-
-       ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, &sel);
-       if (ret)
-               return ret;
-
-       sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, sel);
+static const struct regmap_config bu27010_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
 
-       ret = iio_gts_find_gain_by_sel(gts, sel);
-       if (ret < 0) {
-               dev_err(data->dev, "unknown gain value 0x%x\n", sel);
-               return ret;
-       }
-
-       *gain = ret;
-
-       return 0;
-}
+       .max_register   = BU27010_REG_MAX,
+       .cache_type     = REGCACHE_RBTREE,
+       .volatile_table = &bu27010_volatile_regs,
+       .wr_table       = &bu27010_ro_regs,
+       .disable_locking = true,
+};
 
 static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
 {
@@ -368,6 +518,264 @@ static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
                                  BU27008_MASK_RGBC_GAIN, regval);
 }
 
+static int bu27010_write_gain_sel(struct bu27008_data *data, int sel)
+{
+       unsigned int regval;
+       int ret, chan_selector;
+
+       /*
+        * Gain 'selector' is composed of two registers. Selector is 6bit value,
+        * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and
+        * two low bits being the channel specific gain in MODE_CONTROL2.
+        *
+        * Let's take the 4 high bits of whole 6 bit selector, and prepare
+        * the MODE_CONTROL1 value (RGBC gain part).
+        */
+       regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2));
+
+       ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
+                                 BU27010_MASK_RGBC_GAIN, regval);
+       if (ret)
+               return ret;
+
+       /*
+        * Two low two bits of the selector must be written for all 4
+        * channels in the MODE_CONTROL2 register. Copy these two bits for
+        * all channels.
+        */
+       chan_selector = sel & GENMASK(1, 0);
+
+       regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector);
+       regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector);
+       regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector);
+       regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector);
+
+       return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval);
+}
+
+static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel)
+{
+       int ret;
+
+       /*
+        * If we always "lock" the gain selectors for all channels to prevent
+        * unsupported configs, then it does not matter which channel is used
+        * we can just return selector from any of them.
+        *
+        * This, however is not true if we decide to support only 4X and 16X
+        * and then individual gains for channels. Currently this is not the
+        * case.
+        *
+        * If we some day decide to support individual gains, then we need to
+        * have channel information here.
+        */
+
+       ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
+       if (ret)
+               return ret;
+
+       *sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel);
+
+       return 0;
+}
+
+static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel)
+{
+       int ret, tmp;
+
+       /*
+        * We always "lock" the gain selectors for all channels to prevent
+        * unsupported configs. It does not matter which channel is used
+        * we can just return selector from any of them.
+        *
+        * Read the channel0 gain.
+        */
+       ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
+       if (ret)
+               return ret;
+
+       *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel);
+
+       /* Read the shared gain */
+       ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp);
+       if (ret)
+               return ret;
+
+       /*
+        * The gain selector is made as a combination of common RGBC gain and
+        * the channel specific gain. The channel specific gain forms the low
+        * bits of selector and RGBC gain is appended right after it.
+        *
+        * Compose the selector from channel0 gain and shared RGBC gain.
+        */
+       *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN);
+
+       return ret;
+}
+
+static int bu27008_chip_init(struct bu27008_data *data)
+{
+       int ret;
+
+       ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
+                               BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET);
+       if (ret)
+               return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
+
+       /*
+        * The data-sheet does not tell how long performing the IC reset takes.
+        * However, the data-sheet says the minimum time it takes the IC to be
+        * able to take inputs after power is applied, is 100 uS. I'd assume
+        * > 1 mS is enough.
+        */
+       msleep(1);
+
+       ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg);
+       if (ret)
+               dev_err(data->dev, "Failed to reinit reg cache\n");
+
+       return ret;
+}
+
+static int bu27010_chip_init(struct bu27008_data *data)
+{
+       int ret;
+
+       ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
+                               BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET);
+       if (ret)
+               return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
+
+       msleep(1);
+
+       /* Power ON*/
+       ret = regmap_write_bits(data->regmap, BU27010_REG_POWER,
+                               BU27010_MASK_POWER, BU27010_MASK_POWER);
+       if (ret)
+               return dev_err_probe(data->dev, ret, "Sensor power-on failed\n");
+
+       msleep(1);
+
+       /* Release blocks from reset */
+       ret = regmap_write_bits(data->regmap, BU27010_REG_RESET,
+                               BU27010_MASK_RESET, BU27010_RESET_RELEASE);
+       if (ret)
+               return dev_err_probe(data->dev, ret, "Sensor powering failed\n");
+
+       msleep(1);
+
+       /*
+        * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ
+        * enabling is not a bit mask where individual IRQs could be enabled but
+        * a field which values are:
+        * 00 => IRQs disabled
+        * 01 => Data-ready (RGBC/IR)
+        * 10 => Data-ready (flicker)
+        * 11 => Flicker FIFO
+        *
+        * So, only one IRQ can be enabled at a time and enabling for example
+        * flicker FIFO would automagically disable data-ready IRQ.
+        *
+        * Currently the driver does not support the flicker. Hence, we can
+        * just treat the RGBC data-ready as single bit which can be enabled /
+        * disabled. This works for as long as the second bit in the field
+        * stays zero. Here we ensure it gets zeroed.
+        */
+       return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4,
+                                BU27010_IRQ_DIS_ALL);
+}
+
+static const struct bu27_chip_data bu27010_chip = {
+       .name = "bu27010",
+       .chip_init = bu27010_chip_init,
+       .get_gain_sel = bu27010_get_gain_sel,
+       .write_gain_sel = bu27010_write_gain_sel,
+       .regmap_cfg = &bu27010_regmap,
+       .gains = &bu27010_gains[0],
+       .gains_ir = &bu27010_gains_ir[0],
+       .itimes = &bu27010_itimes[0],
+       .num_gains = ARRAY_SIZE(bu27010_gains),
+       .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir),
+       .num_itimes = ARRAY_SIZE(bu27010_itimes),
+       .scale1x = BU27010_SCALE_1X,
+       .drdy_en_reg = BU27010_REG_MODE_CONTROL4,
+       .drdy_en_mask = BU27010_DRDY_EN,
+       .meas_en_reg = BU27010_REG_MODE_CONTROL5,
+       .meas_en_mask = BU27010_MASK_MEAS_EN,
+       .valid_reg = BU27010_REG_MODE_CONTROL5,
+       .chan_sel_reg = BU27008_REG_MODE_CONTROL1,
+       .chan_sel_mask = BU27010_MASK_CHAN_SEL,
+       .int_time_mask = BU27010_MASK_MEAS_MODE,
+       .part_id = BU27010_ID,
+};
+
+static const struct bu27_chip_data bu27008_chip = {
+       .name = "bu27008",
+       .chip_init = bu27008_chip_init,
+       .get_gain_sel = bu27008_get_gain_sel,
+       .write_gain_sel = bu27008_write_gain_sel,
+       .regmap_cfg = &bu27008_regmap,
+       .gains = &bu27008_gains[0],
+       .gains_ir = &bu27008_gains_ir[0],
+       .itimes = &bu27008_itimes[0],
+       .num_gains = ARRAY_SIZE(bu27008_gains),
+       .num_gains_ir = ARRAY_SIZE(bu27008_gains_ir),
+       .num_itimes = ARRAY_SIZE(bu27008_itimes),
+       .scale1x = BU27008_SCALE_1X,
+       .drdy_en_reg = BU27008_REG_MODE_CONTROL3,
+       .drdy_en_mask = BU27008_MASK_INT_EN,
+       .valid_reg = BU27008_REG_MODE_CONTROL3,
+       .meas_en_reg = BU27008_REG_MODE_CONTROL3,
+       .meas_en_mask = BU27008_MASK_MEAS_EN,
+       .chan_sel_reg = BU27008_REG_MODE_CONTROL3,
+       .chan_sel_mask = BU27008_MASK_CHAN_SEL,
+       .int_time_mask = BU27008_MASK_MEAS_MODE,
+       .part_id = BU27008_ID,
+};
+
+#define BU27008_MAX_VALID_RESULT_WAIT_US       50000
+#define BU27008_VALID_RESULT_WAIT_QUANTA_US    1000
+
+static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
+{
+       int ret, valid;
+       __le16 tmp;
+
+       ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg,
+                                      valid, (valid & BU27008_MASK_VALID),
+                                      BU27008_VALID_RESULT_WAIT_QUANTA_US,
+                                      BU27008_MAX_VALID_RESULT_WAIT_US);
+       if (ret)
+               return ret;
+
+       ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp));
+       if (ret)
+               dev_err(data->dev, "Reading channel data failed\n");
+
+       *val = le16_to_cpu(tmp);
+
+       return ret;
+}
+
+static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
+{
+       int ret, sel;
+
+       ret = data->cd->get_gain_sel(data, &sel);
+       if (ret)
+               return ret;
+
+       ret = iio_gts_find_gain_by_sel(gts, sel);
+       if (ret < 0) {
+               dev_err(data->dev, "unknown gain value 0x%x\n", sel);
+               return ret;
+       }
+
+       *gain = ret;
+
+       return 0;
+}
+
 static int bu27008_set_gain(struct bu27008_data *data, int gain)
 {
        int ret;
@@ -376,7 +784,7 @@ static int bu27008_set_gain(struct bu27008_data *data, int gain)
        if (ret < 0)
                return ret;
 
-       return bu27008_write_gain_sel(data, ret);
+       return data->cd->write_gain_sel(data, ret);
 }
 
 static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
@@ -384,15 +792,23 @@ static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
        int ret, val;
 
        ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val);
-       *sel = FIELD_GET(BU27008_MASK_MEAS_MODE, val);
+       if (ret)
+               return ret;
 
-       return ret;
+       val &= data->cd->int_time_mask;
+       val >>= ffs(data->cd->int_time_mask) - 1;
+
+       *sel = val;
+
+       return 0;
 }
 
 static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel)
 {
+       sel <<= ffs(data->cd->int_time_mask) - 1;
+
        return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
-                                 BU27008_MASK_MEAS_MODE, sel);
+                                 data->cd->int_time_mask, sel);
 }
 
 static int bu27008_get_int_time_us(struct bu27008_data *data)
@@ -448,8 +864,7 @@ static int bu27008_set_int_time(struct bu27008_data *data, int time)
        if (ret < 0)
                return ret;
 
-       return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
-                                 BU27008_MASK_MEAS_MODE, ret);
+       return bu27008_set_int_time_sel(data, ret);
 }
 
 /* Try to change the time so that the scale is maintained */
@@ -527,10 +942,13 @@ unlock_out:
        return ret;
 }
 
-static int bu27008_meas_set(struct bu27008_data *data, int state)
+static int bu27008_meas_set(struct bu27008_data *data, bool enable)
 {
-       return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
-                                 BU27008_MASK_MEAS_EN, state);
+       if (enable)
+               return regmap_set_bits(data->regmap, data->cd->meas_en_reg,
+                                      data->cd->meas_en_mask);
+       return regmap_clear_bits(data->regmap, data->cd->meas_en_reg,
+                                data->cd->meas_en_mask);
 }
 
 static int bu27008_chan_cfg(struct bu27008_data *data,
@@ -543,9 +961,15 @@ static int bu27008_chan_cfg(struct bu27008_data *data,
        else
                chan_sel = BU27008_CLEAR2_IR3;
 
-       chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel);
+       /*
+        * prepare bitfield for channel sel. The FIELD_PREP works only when
+        * mask is constant. In our case the mask is assigned based on the
+        * chip type. Hence the open-coded FIELD_PREP here. We don't bother
+        * zeroing the irrelevant bits though - update_bits takes care of that.
+        */
+       chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
 
-       return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
+       return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
                                  BU27008_MASK_CHAN_SEL, chan_sel);
 }
 
@@ -558,7 +982,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
        if (ret)
                return ret;
 
-       ret = bu27008_meas_set(data, BU27008_MEAS_EN);
+       ret = bu27008_meas_set(data, true);
        if (ret)
                return ret;
 
@@ -574,7 +998,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
        if (!ret)
                ret = IIO_VAL_INT;
 
-       if (bu27008_meas_set(data, BU27008_MEAS_DIS))
+       if (bu27008_meas_set(data, false))
                dev_warn(data->dev, "measurement disabling failed\n");
 
        return ret;
@@ -669,7 +1093,7 @@ static int bu27008_set_scale(struct bu27008_data *data,
                        goto unlock_out;
 
        }
-       ret = bu27008_write_gain_sel(data, gain_sel);
+       ret = data->cd->write_gain_sel(data, gain_sel);
 
 unlock_out:
        mutex_unlock(&data->mutex);
@@ -762,10 +1186,10 @@ static int bu27008_update_scan_mode(struct iio_dev *idev,
                chan_sel = BU27008_CLEAR2_IR3;
        }
 
-       chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel);
+       chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
 
-       return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
-                                BU27008_MASK_CHAN_SEL, chan_sel);
+       return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
+                                 data->cd->chan_sel_mask, chan_sel);
 }
 
 static const struct iio_info bu27008_info = {
@@ -777,46 +1201,18 @@ static const struct iio_info bu27008_info = {
        .validate_trigger = iio_validate_own_trigger,
 };
 
-static int bu27008_chip_init(struct bu27008_data *data)
-{
-       int ret;
-
-       ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
-                               BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET);
-       if (ret)
-               return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
-
-       /*
-        * The data-sheet does not tell how long performing the IC reset takes.
-        * However, the data-sheet says the minimum time it takes the IC to be
-        * able to take inputs after power is applied, is 100 uS. I'd assume
-        * > 1 mS is enough.
-        */
-       msleep(1);
-
-       ret = regmap_reinit_cache(data->regmap, &bu27008_regmap);
-       if (ret)
-               dev_err(data->dev, "Failed to reinit reg cache\n");
-
-       return ret;
-}
-
-static int bu27008_set_drdy_irq(struct bu27008_data *data, int state)
-{
-       return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
-                                BU27008_MASK_INT_EN, state);
-}
-
-static int bu27008_trigger_set_state(struct iio_trigger *trig,
-                                    bool state)
+static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state)
 {
        struct bu27008_data *data = iio_trigger_get_drvdata(trig);
        int ret;
 
+
        if (state)
-               ret = bu27008_set_drdy_irq(data, BU27008_INT_EN);
+               ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg,
+                                     data->cd->drdy_en_mask);
        else
-               ret = bu27008_set_drdy_irq(data, BU27008_INT_DIS);
+               ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg,
+                                       data->cd->drdy_en_mask);
        if (ret)
                dev_err(data->dev, "Failed to set trigger state\n");
 
@@ -852,7 +1248,7 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p)
         * After some measurements, it seems reading the
         * BU27008_REG_MODE_CONTROL3 debounces the IRQ line
         */
-       ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL3, &dummy);
+       ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy);
        if (ret < 0)
                goto err_read;
 
@@ -872,14 +1268,14 @@ static int bu27008_buffer_preenable(struct iio_dev *idev)
 {
        struct bu27008_data *data = iio_priv(idev);
 
-       return bu27008_meas_set(data, BU27008_MEAS_EN);
+       return bu27008_meas_set(data, true);
 }
 
 static int bu27008_buffer_postdisable(struct iio_dev *idev)
 {
        struct bu27008_data *data = iio_priv(idev);
 
-       return bu27008_meas_set(data, BU27008_MEAS_DIS);
+       return bu27008_meas_set(data, false);
 }
 
 static const struct iio_buffer_setup_ops bu27008_buffer_ops = {
@@ -952,11 +1348,6 @@ static int bu27008_probe(struct i2c_client *i2c)
        struct iio_dev *idev;
        int ret;
 
-       regmap = devm_regmap_init_i2c(i2c, &bu27008_regmap);
-       if (IS_ERR(regmap))
-               return dev_err_probe(dev, PTR_ERR(regmap),
-                                    "Failed to initialize Regmap\n");
-
        idev = devm_iio_device_alloc(dev, sizeof(*data));
        if (!idev)
                return -ENOMEM;
@@ -967,24 +1358,34 @@ static int bu27008_probe(struct i2c_client *i2c)
 
        data = iio_priv(idev);
 
+       data->cd = device_get_match_data(&i2c->dev);
+       if (!data->cd)
+               return -ENODEV;
+
+       regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg);
+       if (IS_ERR(regmap))
+               return dev_err_probe(dev, PTR_ERR(regmap),
+                                    "Failed to initialize Regmap\n");
+
+
        ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, &reg);
        if (ret)
                return dev_err_probe(dev, ret, "Failed to access sensor\n");
 
        part_id = FIELD_GET(BU27008_MASK_PART_ID, reg);
 
-       if (part_id != BU27008_ID)
+       if (part_id != data->cd->part_id)
                dev_warn(dev, "unknown device 0x%x\n", part_id);
 
-       ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains,
-                                   ARRAY_SIZE(bu27008_gains), bu27008_itimes,
-                                   ARRAY_SIZE(bu27008_itimes), &data->gts);
+       ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains,
+                                   data->cd->num_gains, data->cd->itimes,
+                                   data->cd->num_itimes, &data->gts);
        if (ret)
                return ret;
 
-       ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains_ir,
-                                   ARRAY_SIZE(bu27008_gains_ir), bu27008_itimes,
-                                   ARRAY_SIZE(bu27008_itimes), &data->gts_ir);
+       ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir,
+                                   data->cd->num_gains_ir, data->cd->itimes,
+                                   data->cd->num_itimes, &data->gts_ir);
        if (ret)
                return ret;
 
@@ -995,12 +1396,12 @@ static int bu27008_probe(struct i2c_client *i2c)
 
        idev->channels = bu27008_channels;
        idev->num_channels = ARRAY_SIZE(bu27008_channels);
-       idev->name = "bu27008";
+       idev->name = data->cd->name;
        idev->info = &bu27008_info;
        idev->modes = INDIO_DIRECT_MODE;
        idev->available_scan_masks = bu27008_scan_masks;
 
-       ret = bu27008_chip_init(data);
+       ret = data->cd->chip_init(data);
        if (ret)
                return ret;
 
@@ -1021,7 +1422,8 @@ static int bu27008_probe(struct i2c_client *i2c)
 }
 
 static const struct of_device_id bu27008_of_match[] = {
-       { .compatible = "rohm,bu27008" },
+       { .compatible = "rohm,bu27008", .data = &bu27008_chip },
+       { .compatible = "rohm,bu27010", .data = &bu27010_chip },
        { }
 };
 MODULE_DEVICE_TABLE(of, bu27008_of_match);
@@ -1036,7 +1438,7 @@ static struct i2c_driver bu27008_i2c_driver = {
 };
 module_i2c_driver(bu27008_i2c_driver);
 
-MODULE_DESCRIPTION("ROHM BU27008 colour sensor driver");
+MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver");
 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
 MODULE_LICENSE("GPL");
 MODULE_IMPORT_NS(IIO_GTS_HELPER);
index 7c7362e..3a52b09 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
 #include <linux/interrupt.h>
+#include <linux/units.h>
 
 #include <linux/iio/buffer.h>
 #include <linux/iio/events.h>
 
 #define VCNL4200_AL_CONF       0x00 /* Ambient light configuration */
 #define VCNL4200_PS_CONF1      0x03 /* Proximity configuration */
+#define VCNL4200_PS_CONF3      0x04 /* Proximity configuration */
 #define VCNL4040_PS_THDL_LM    0x06 /* Proximity threshold low */
 #define VCNL4040_PS_THDH_LM    0x07 /* Proximity threshold high */
+#define VCNL4040_ALS_THDL_LM   0x02 /* Ambient light threshold low */
+#define VCNL4040_ALS_THDH_LM   0x01 /* Ambient light threshold high */
 #define VCNL4200_PS_DATA       0x08 /* Proximity data */
 #define VCNL4200_AL_DATA       0x09 /* Ambient light data */
 #define VCNL4040_INT_FLAGS     0x0b /* Interrupt register */
+#define VCNL4200_INT_FLAGS     0x0d /* Interrupt register */
 #define VCNL4200_DEV_ID                0x0e /* Device ID, slave address and version */
 
 #define VCNL4040_DEV_ID                0x0c /* Device ID and version */
 #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */
 
 #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0)
+#define VCNL4040_ALS_CONF_IT           GENMASK(7, 6) /* Ambient integration time */
+#define VCNL4040_ALS_CONF_INT_EN       BIT(1) /* Ambient light Interrupt enable */
+#define VCNL4040_ALS_CONF_PERS GENMASK(3, 2) /* Ambient interrupt persistence setting */
 #define VCNL4040_PS_CONF1_PS_SHUTDOWN  BIT(0)
 #define VCNL4040_PS_CONF2_PS_IT        GENMASK(3, 1) /* Proximity integration time */
+#define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */
 #define VCNL4040_PS_CONF2_PS_INT       GENMASK(9, 8) /* Proximity interrupt mode */
+#define VCNL4040_PS_CONF3_MPS          GENMASK(6, 5) /* Proximity multi pulse number */
+#define VCNL4040_PS_MS_LED_I           GENMASK(10, 8) /* Proximity current */
 #define VCNL4040_PS_IF_AWAY            BIT(8) /* Proximity event cross low threshold */
 #define VCNL4040_PS_IF_CLOSE           BIT(9) /* Proximity event cross high threshold */
+#define VCNL4040_ALS_RISING            BIT(12) /* Ambient Light cross high threshold */
+#define VCNL4040_ALS_FALLING           BIT(13) /* Ambient Light cross low threshold */
 
 /* Bit masks for interrupt registers. */
 #define VCNL4010_INT_THR_SEL   BIT(0) /* Select threshold interrupt source */
@@ -123,6 +136,44 @@ static const int vcnl4040_ps_it_times[][2] = {
        {0, 800},
 };
 
+static const int vcnl4200_ps_it_times[][2] = {
+       {0, 96},
+       {0, 144},
+       {0, 192},
+       {0, 384},
+       {0, 768},
+       {0, 864},
+};
+
+static const int vcnl4040_als_it_times[][2] = {
+       {0, 80000},
+       {0, 160000},
+       {0, 320000},
+       {0, 640000},
+};
+
+static const int vcnl4200_als_it_times[][2] = {
+       {0, 50000},
+       {0, 100000},
+       {0, 200000},
+       {0, 400000},
+};
+
+static const int vcnl4040_ps_calibbias_ua[][2] = {
+       {0, 50000},
+       {0, 75000},
+       {0, 100000},
+       {0, 120000},
+       {0, 140000},
+       {0, 160000},
+       {0, 180000},
+       {0, 200000},
+};
+
+static const int vcnl4040_als_persistence[] = {1, 2, 4, 8};
+static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4};
+static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8};
+
 #define VCNL4000_SLEEP_DELAY_MS        2000 /* before we enter pm_runtime_suspend */
 
 enum vcnl4000_device_ids {
@@ -145,6 +196,7 @@ struct vcnl4000_data {
        int rev;
        int al_scale;
        u8 ps_int;              /* proximity interrupt mode */
+       u8 als_int;             /* ambient light interrupt mode*/
        const struct vcnl4000_chip_spec *chip_spec;
        struct mutex vcnl4000_lock;
        struct vcnl4200_channel vcnl4200_al;
@@ -164,6 +216,13 @@ struct vcnl4000_chip_spec {
        int (*set_power_state)(struct vcnl4000_data *data, bool on);
        irqreturn_t (*irq_thread)(int irq, void *priv);
        irqreturn_t (*trig_buffer_func)(int irq, void *priv);
+
+       u8 int_reg;
+       const int(*ps_it_times)[][2];
+       const int num_ps_it_times;
+       const int(*als_it_times)[][2];
+       const int num_als_it_times;
+       const unsigned int ulux_step;
 };
 
 static const struct i2c_device_id vcnl4000_id[] = {
@@ -263,7 +322,7 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
        int ret;
 
        /* Do not power down if interrupts are enabled */
-       if (!on && data->ps_int)
+       if (!on && (data->ps_int || data->als_int))
                return 0;
 
        ret = vcnl4000_write_als_enable(data, on);
@@ -308,6 +367,7 @@ static int vcnl4200_init(struct vcnl4000_data *data)
 
        data->rev = (ret >> 8) & 0xf;
        data->ps_int = 0;
+       data->als_int = 0;
 
        data->vcnl4200_al.reg = VCNL4200_AL_DATA;
        data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
@@ -317,16 +377,15 @@ static int vcnl4200_init(struct vcnl4000_data *data)
                data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
                /* Default wait time is 4.8ms, add 20% tolerance. */
                data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
-               data->al_scale = 24000;
                break;
        case VCNL4040_PROD_ID:
                /* Default wait time is 80ms, add 20% tolerance. */
                data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
                /* Default wait time is 5ms, add 20% tolerance. */
                data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
-               data->al_scale = 120000;
                break;
        }
+       data->al_scale = data->chip_spec->ulux_step;
        mutex_init(&data->vcnl4200_al.lock);
        mutex_init(&data->vcnl4200_ps.lock);
 
@@ -496,6 +555,60 @@ static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
        return ret;
 }
 
+static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2)
+{
+       int ret;
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+       if (ret < 0)
+               return ret;
+
+       ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
+       if (ret >= data->chip_spec->num_als_it_times)
+               return -EINVAL;
+
+       *val = (*data->chip_spec->als_it_times)[ret][0];
+       *val2 = (*data->chip_spec->als_it_times)[ret][1];
+
+       return 0;
+}
+
+static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val)
+{
+       unsigned int i;
+       int ret;
+       u16 regval;
+
+       for (i = 0; i < data->chip_spec->num_als_it_times; i++) {
+               if (val == (*data->chip_spec->als_it_times)[i][1])
+                       break;
+       }
+
+       if (i == data->chip_spec->num_als_it_times)
+               return -EINVAL;
+
+       data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200);
+       data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step,
+                        (*data->chip_spec->als_it_times)[0][1]),
+                        val);
+
+       mutex_lock(&data->vcnl4000_lock);
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+       if (ret < 0)
+               goto out_unlock;
+
+       regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i);
+       regval |= (ret & ~VCNL4040_ALS_CONF_IT);
+       ret = i2c_smbus_write_word_data(data->client,
+                                       VCNL4200_AL_CONF,
+                                       regval);
+
+out_unlock:
+       mutex_unlock(&data->vcnl4000_lock);
+       return ret;
+}
+
 static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
 {
        int ret;
@@ -506,11 +619,11 @@ static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
 
        ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
 
-       if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times))
+       if (ret >= data->chip_spec->num_ps_it_times)
                return -EINVAL;
 
-       *val = vcnl4040_ps_it_times[ret][0];
-       *val2 = vcnl4040_ps_it_times[ret][1];
+       *val = (*data->chip_spec->ps_it_times)[ret][0];
+       *val2 = (*data->chip_spec->ps_it_times)[ret][1];
 
        return 0;
 }
@@ -521,8 +634,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
        int ret, index = -1;
        u16 regval;
 
-       for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) {
-               if (val == vcnl4040_ps_it_times[i][1]) {
+       for (i = 0; i < data->chip_spec->num_ps_it_times; i++) {
+               if (val == (*data->chip_spec->ps_it_times)[i][1]) {
                        index = i;
                        break;
                }
@@ -531,6 +644,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
        if (index < 0)
                return -EINVAL;
 
+       data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC);
+
        mutex_lock(&data->vcnl4000_lock);
 
        ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
@@ -547,6 +662,224 @@ out:
        return ret;
 }
 
+static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2)
+{
+       int ret, ret_pers, it;
+       int64_t val_c;
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+       if (ret < 0)
+               return ret;
+
+       ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret);
+       if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence))
+               return -EINVAL;
+
+       it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
+       if (it >= data->chip_spec->num_als_it_times)
+               return -EINVAL;
+
+       val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1],
+                           vcnl4040_als_persistence[ret_pers]);
+       *val = div_u64_rem(val_c, MICRO, val2);
+
+       return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2)
+{
+       unsigned int i;
+       int ret, it;
+       u16 regval;
+       u64 val_n = mul_u32_u32(val, MICRO) + val2;
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+       if (ret < 0)
+               return ret;
+
+       it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
+       if (it >= data->chip_spec->num_als_it_times)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) {
+               if (val_n < mul_u32_u32(vcnl4040_als_persistence[i],
+                                       (*data->chip_spec->als_it_times)[it][1]))
+                       break;
+       }
+
+       mutex_lock(&data->vcnl4000_lock);
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+       if (ret < 0)
+               goto out_unlock;
+
+       regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i);
+       regval |= (ret & ~VCNL4040_ALS_CONF_PERS);
+       ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
+                                       regval);
+
+out_unlock:
+       mutex_unlock(&data->vcnl4000_lock);
+       return ret;
+}
+
+static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2)
+{
+       int ret, ret_pers, it;
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+       if (ret < 0)
+               return ret;
+
+       ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret);
+       if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence))
+               return -EINVAL;
+
+       it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
+       if (it >= data->chip_spec->num_ps_it_times)
+               return -EINVAL;
+
+       *val = (*data->chip_spec->ps_it_times)[it][0];
+       *val2 = (*data->chip_spec->ps_it_times)[it][1] *
+               vcnl4040_ps_persistence[ret_pers];
+
+       return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2)
+{
+       int ret, it, i;
+       u16 regval;
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+       if (ret < 0)
+               return ret;
+
+       it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
+       if (it >= data->chip_spec->num_ps_it_times)
+               return -EINVAL;
+
+       if (val > 0)
+               i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1;
+       else {
+               for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) {
+                       if (val2 <= vcnl4040_ps_persistence[i] *
+                                       (*data->chip_spec->ps_it_times)[it][1])
+                               break;
+               }
+       }
+
+       mutex_lock(&data->vcnl4000_lock);
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+       if (ret < 0)
+               goto out_unlock;
+
+       regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i);
+       regval |= (ret & ~VCNL4040_CONF1_PS_PERS);
+       ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
+                                       regval);
+
+out_unlock:
+       mutex_unlock(&data->vcnl4000_lock);
+       return ret;
+}
+
+static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
+       if (ret < 0)
+               return ret;
+
+       ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret);
+       if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
+               return -EINVAL;
+
+       *val = vcnl4040_ps_oversampling_ratio[ret];
+
+       return ret;
+}
+
+static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val)
+{
+       unsigned int i;
+       int ret;
+       u16 regval;
+
+       for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) {
+               if (val == vcnl4040_ps_oversampling_ratio[i])
+                       break;
+       }
+
+       if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
+               return -EINVAL;
+
+       mutex_lock(&data->vcnl4000_lock);
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
+       if (ret < 0)
+               goto out_unlock;
+
+       regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i);
+       regval |= (ret & ~VCNL4040_PS_CONF3_MPS);
+       ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
+                                       regval);
+
+out_unlock:
+       mutex_unlock(&data->vcnl4000_lock);
+       return ret;
+}
+
+static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2)
+{
+       int ret;
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
+       if (ret < 0)
+               return ret;
+
+       ret = FIELD_GET(VCNL4040_PS_MS_LED_I, ret);
+       if (ret >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
+               return -EINVAL;
+
+       *val = vcnl4040_ps_calibbias_ua[ret][0];
+       *val2 = vcnl4040_ps_calibbias_ua[ret][1];
+
+       return ret;
+}
+
+static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val)
+{
+       unsigned int i;
+       int ret;
+       u16 regval;
+
+       for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_calibbias_ua); i++) {
+               if (val == vcnl4040_ps_calibbias_ua[i][1])
+                       break;
+       }
+
+       if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
+               return -EINVAL;
+
+       mutex_lock(&data->vcnl4000_lock);
+
+       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
+       if (ret < 0)
+               goto out_unlock;
+
+       regval = (ret & ~VCNL4040_PS_MS_LED_I);
+       regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i);
+       ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
+                                       regval);
+
+out_unlock:
+       mutex_unlock(&data->vcnl4000_lock);
+       return ret;
+}
+
 static int vcnl4000_read_raw(struct iio_dev *indio_dev,
                                struct iio_chan_spec const *chan,
                                int *val, int *val2, long mask)
@@ -584,12 +917,39 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
                *val2 = data->al_scale;
                return IIO_VAL_INT_PLUS_MICRO;
        case IIO_CHAN_INFO_INT_TIME:
-               if (chan->type != IIO_PROXIMITY)
+               switch (chan->type) {
+               case IIO_LIGHT:
+                       ret = vcnl4040_read_als_it(data, val, val2);
+                       break;
+               case IIO_PROXIMITY:
+                       ret = vcnl4040_read_ps_it(data, val, val2);
+                       break;
+               default:
                        return -EINVAL;
-               ret = vcnl4040_read_ps_it(data, val, val2);
+               }
                if (ret < 0)
                        return ret;
                return IIO_VAL_INT_PLUS_MICRO;
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       ret = vcnl4040_read_ps_oversampling_ratio(data, val);
+                       if (ret < 0)
+                               return ret;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_CALIBBIAS:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       ret = vcnl4040_read_ps_calibbias(data, val, val2);
+                       if (ret < 0)
+                               return ret;
+                       return IIO_VAL_INT_PLUS_MICRO;
+               default:
+                       return -EINVAL;
+               }
        default:
                return -EINVAL;
        }
@@ -605,9 +965,28 @@ static int vcnl4040_write_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_INT_TIME:
                if (val != 0)
                        return -EINVAL;
-               if (chan->type != IIO_PROXIMITY)
+               switch (chan->type) {
+               case IIO_LIGHT:
+                       return vcnl4040_write_als_it(data, val2);
+               case IIO_PROXIMITY:
+                       return vcnl4040_write_ps_it(data, val2);
+               default:
                        return -EINVAL;
-               return vcnl4040_write_ps_it(data, val2);
+               }
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       return vcnl4040_write_ps_oversampling_ratio(data, val);
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_CALIBBIAS:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       return vcnl4040_write_ps_calibbias(data, val2);
+               default:
+                       return -EINVAL;
+               }
        default:
                return -EINVAL;
        }
@@ -618,12 +997,44 @@ static int vcnl4040_read_avail(struct iio_dev *indio_dev,
                               const int **vals, int *type, int *length,
                               long mask)
 {
+       struct vcnl4000_data *data = iio_priv(indio_dev);
+
        switch (mask) {
        case IIO_CHAN_INFO_INT_TIME:
-               *vals = (int *)vcnl4040_ps_it_times;
+               switch (chan->type) {
+               case IIO_LIGHT:
+                       *vals = (int *)(*data->chip_spec->als_it_times);
+                       *length = 2 * data->chip_spec->num_als_it_times;
+                       break;
+               case IIO_PROXIMITY:
+                       *vals = (int *)(*data->chip_spec->ps_it_times);
+                       *length = 2 * data->chip_spec->num_ps_it_times;
+                       break;
+               default:
+                       return -EINVAL;
+               }
                *type = IIO_VAL_INT_PLUS_MICRO;
-               *length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times);
                return IIO_AVAIL_LIST;
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       *vals = (int *)vcnl4040_ps_oversampling_ratio;
+                       *length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio);
+                       *type = IIO_VAL_INT;
+                       return IIO_AVAIL_LIST;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_CALIBBIAS:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       *vals = (int *)vcnl4040_ps_calibbias_ua;
+                       *length = 2 * ARRAY_SIZE(vcnl4040_ps_calibbias_ua);
+                       *type = IIO_VAL_INT_PLUS_MICRO;
+                       return IIO_AVAIL_LIST;
+               default:
+                       return -EINVAL;
+               }
        default:
                return -EINVAL;
        }
@@ -818,24 +1229,58 @@ static int vcnl4040_read_event(struct iio_dev *indio_dev,
        int ret;
        struct vcnl4000_data *data = iio_priv(indio_dev);
 
-       switch (dir) {
-       case IIO_EV_DIR_RISING:
-               ret = i2c_smbus_read_word_data(data->client,
-                                              VCNL4040_PS_THDH_LM);
-               if (ret < 0)
-                       return ret;
-               *val = ret;
-               return IIO_VAL_INT;
-       case IIO_EV_DIR_FALLING:
-               ret = i2c_smbus_read_word_data(data->client,
-                                              VCNL4040_PS_THDL_LM);
-               if (ret < 0)
-                       return ret;
-               *val = ret;
-               return IIO_VAL_INT;
+       switch (chan->type) {
+       case IIO_LIGHT:
+               switch (info) {
+               case IIO_EV_INFO_PERIOD:
+                       return vcnl4040_read_als_period(data, val, val2);
+               case IIO_EV_INFO_VALUE:
+                       switch (dir) {
+                       case IIO_EV_DIR_RISING:
+                               ret = i2c_smbus_read_word_data(data->client,
+                                                              VCNL4040_ALS_THDH_LM);
+                               break;
+                       case IIO_EV_DIR_FALLING:
+                               ret = i2c_smbus_read_word_data(data->client,
+                                                              VCNL4040_ALS_THDL_LM);
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case IIO_PROXIMITY:
+               switch (info) {
+               case IIO_EV_INFO_PERIOD:
+                       return vcnl4040_read_ps_period(data, val, val2);
+               case IIO_EV_INFO_VALUE:
+                       switch (dir) {
+                       case IIO_EV_DIR_RISING:
+                               ret = i2c_smbus_read_word_data(data->client,
+                                                              VCNL4040_PS_THDH_LM);
+                               break;
+                       case IIO_EV_DIR_FALLING:
+                               ret = i2c_smbus_read_word_data(data->client,
+                                                              VCNL4040_PS_THDL_LM);
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
        default:
                return -EINVAL;
        }
+       if (ret < 0)
+               return ret;
+       *val = ret;
+       return IIO_VAL_INT;
 }
 
 static int vcnl4040_write_event(struct iio_dev *indio_dev,
@@ -848,22 +1293,61 @@ static int vcnl4040_write_event(struct iio_dev *indio_dev,
        int ret;
        struct vcnl4000_data *data = iio_priv(indio_dev);
 
-       switch (dir) {
-       case IIO_EV_DIR_RISING:
-               ret = i2c_smbus_write_word_data(data->client,
-                                               VCNL4040_PS_THDH_LM, val);
-               if (ret < 0)
-                       return ret;
-               return IIO_VAL_INT;
-       case IIO_EV_DIR_FALLING:
-               ret = i2c_smbus_write_word_data(data->client,
-                                               VCNL4040_PS_THDL_LM, val);
-               if (ret < 0)
-                       return ret;
-               return IIO_VAL_INT;
+       switch (chan->type) {
+       case IIO_LIGHT:
+               switch (info) {
+               case IIO_EV_INFO_PERIOD:
+                       return vcnl4040_write_als_period(data, val, val2);
+               case IIO_EV_INFO_VALUE:
+                       switch (dir) {
+                       case IIO_EV_DIR_RISING:
+                               ret = i2c_smbus_write_word_data(data->client,
+                                                               VCNL4040_ALS_THDH_LM,
+                                                               val);
+                               break;
+                       case IIO_EV_DIR_FALLING:
+                               ret = i2c_smbus_write_word_data(data->client,
+                                                               VCNL4040_ALS_THDL_LM,
+                                                               val);
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case IIO_PROXIMITY:
+               switch (info) {
+               case IIO_EV_INFO_PERIOD:
+                       return vcnl4040_write_ps_period(data, val, val2);
+               case IIO_EV_INFO_VALUE:
+                       switch (dir) {
+                       case IIO_EV_DIR_RISING:
+                               ret = i2c_smbus_write_word_data(data->client,
+                                                               VCNL4040_PS_THDH_LM,
+                                                               val);
+                               break;
+                       case IIO_EV_DIR_FALLING:
+                               ret = i2c_smbus_write_word_data(data->client,
+                                                               VCNL4040_PS_THDL_LM,
+                                                               val);
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
        default:
                return -EINVAL;
        }
+       if (ret < 0)
+               return ret;
+       return IIO_VAL_INT;
 }
 
 static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
@@ -956,15 +1440,28 @@ static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
        int ret;
        struct vcnl4000_data *data = iio_priv(indio_dev);
 
-       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
-       if (ret < 0)
-               return ret;
+       switch (chan->type) {
+       case IIO_LIGHT:
+               ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+               if (ret < 0)
+                       return ret;
+
+               data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret);
 
-       data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
+               return data->als_int;
+       case IIO_PROXIMITY:
+               ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+               if (ret < 0)
+                       return ret;
 
-       return (dir == IIO_EV_DIR_RISING) ?
-               FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
-               FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
+               data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
+
+               return (dir == IIO_EV_DIR_RISING) ?
+                       FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
+                       FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
+       default:
+               return -EINVAL;
+       }
 }
 
 static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
@@ -972,29 +1469,51 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
                                       enum iio_event_type type,
                                       enum iio_event_direction dir, int state)
 {
-       int ret;
+       int ret = -EINVAL;
        u16 val, mask;
        struct vcnl4000_data *data = iio_priv(indio_dev);
 
        mutex_lock(&data->vcnl4000_lock);
 
-       ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
-       if (ret < 0)
-               goto out;
+       switch (chan->type) {
+       case IIO_LIGHT:
+               ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+               if (ret < 0)
+                       goto out;
 
-       if (dir == IIO_EV_DIR_RISING)
-               mask = VCNL4040_PS_IF_AWAY;
-       else
-               mask = VCNL4040_PS_IF_CLOSE;
+               mask = VCNL4040_ALS_CONF_INT_EN;
+               if (state)
+                       val = (ret | mask);
+               else
+                       val = (ret & ~mask);
 
-       val = state ? (ret | mask) : (ret & ~mask);
+               data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val);
+               ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
+                                               val);
+               break;
+       case IIO_PROXIMITY:
+               ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+               if (ret < 0)
+                       goto out;
 
-       data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
-       ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
+               if (dir == IIO_EV_DIR_RISING)
+                       mask = VCNL4040_PS_IF_AWAY;
+               else
+                       mask = VCNL4040_PS_IF_CLOSE;
+
+               val = state ? (ret | mask) : (ret & ~mask);
+
+               data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
+               ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
+                                               val);
+               break;
+       default:
+               break;
+       }
 
 out:
        mutex_unlock(&data->vcnl4000_lock);
-       data->chip_spec->set_power_state(data, data->ps_int != 0);
+       data->chip_spec->set_power_state(data, data->ps_int || data->als_int);
 
        return ret;
 }
@@ -1005,7 +1524,7 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
        struct vcnl4000_data *data = iio_priv(indio_dev);
        int ret;
 
-       ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS);
+       ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg);
        if (ret < 0)
                return IRQ_HANDLED;
 
@@ -1025,6 +1544,22 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
                               iio_get_time_ns(indio_dev));
        }
 
+       if (ret & VCNL4040_ALS_FALLING) {
+               iio_push_event(indio_dev,
+                              IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+                                                   IIO_EV_TYPE_THRESH,
+                                                   IIO_EV_DIR_FALLING),
+                              iio_get_time_ns(indio_dev));
+       }
+
+       if (ret & VCNL4040_ALS_RISING) {
+               iio_push_event(indio_dev,
+                              IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+                                                   IIO_EV_TYPE_THRESH,
+                                                   IIO_EV_DIR_RISING),
+                              iio_get_time_ns(indio_dev));
+       }
+
        return IRQ_HANDLED;
 }
 
@@ -1191,6 +1726,22 @@ static const struct iio_event_spec vcnl4000_event_spec[] = {
        }
 };
 
+static const struct iio_event_spec vcnl4040_als_event_spec[] = {
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_EITHER,
+               .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD),
+       },
+};
+
 static const struct iio_event_spec vcnl4040_event_spec[] = {
        {
                .type = IIO_EV_TYPE_THRESH,
@@ -1200,6 +1751,10 @@ static const struct iio_event_spec vcnl4040_event_spec[] = {
                .type = IIO_EV_TYPE_THRESH,
                .dir = IIO_EV_DIR_FALLING,
                .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_EITHER,
+               .mask_separate = BIT(IIO_EV_INFO_PERIOD),
        },
 };
 
@@ -1244,12 +1799,20 @@ static const struct iio_chan_spec vcnl4040_channels[] = {
        {
                .type = IIO_LIGHT,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-                       BIT(IIO_CHAN_INFO_SCALE),
+                       BIT(IIO_CHAN_INFO_SCALE) |
+                       BIT(IIO_CHAN_INFO_INT_TIME),
+               .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
+               .event_spec = vcnl4040_als_event_spec,
+               .num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec),
        }, {
                .type = IIO_PROXIMITY,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-                       BIT(IIO_CHAN_INFO_INT_TIME),
-               .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
+                       BIT(IIO_CHAN_INFO_INT_TIME) |
+                       BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+                       BIT(IIO_CHAN_INFO_CALIBBIAS),
+               .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+                       BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+                       BIT(IIO_CHAN_INFO_CALIBBIAS),
                .ext_info = vcnl4000_ext_info,
                .event_spec = vcnl4040_event_spec,
                .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec),
@@ -1314,6 +1877,12 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
                .num_channels = ARRAY_SIZE(vcnl4040_channels),
                .info = &vcnl4040_info,
                .irq_thread = vcnl4040_irq_thread,
+               .int_reg = VCNL4040_INT_FLAGS,
+               .ps_it_times = &vcnl4040_ps_it_times,
+               .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times),
+               .als_it_times = &vcnl4040_als_it_times,
+               .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times),
+               .ulux_step = 100000,
        },
        [VCNL4200] = {
                .prod = "VCNL4200",
@@ -1321,9 +1890,16 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
                .measure_light = vcnl4200_measure_light,
                .measure_proximity = vcnl4200_measure_proximity,
                .set_power_state = vcnl4200_set_power_state,
-               .channels = vcnl4000_channels,
+               .channels = vcnl4040_channels,
                .num_channels = ARRAY_SIZE(vcnl4000_channels),
-               .info = &vcnl4000_info,
+               .info = &vcnl4040_info,
+               .irq_thread = vcnl4040_irq_thread,
+               .int_reg = VCNL4200_INT_FLAGS,
+               .ps_it_times = &vcnl4200_ps_it_times,
+               .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times),
+               .als_it_times = &vcnl4200_als_it_times,
+               .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times),
+               .ulux_step = 24000,
        },
 };
 
index 89daecc..44678d3 100644 (file)
@@ -99,20 +99,25 @@ static const struct iio_info mcp4018_info = {
        .write_raw = mcp4018_write_raw,
 };
 
+#define MCP4018_ID_TABLE(_name, cfg) {                         \
+       .name = _name,                                          \
+       .driver_data = (kernel_ulong_t)&mcp4018_cfg[cfg],       \
+}
+
 static const struct i2c_device_id mcp4018_id[] = {
-       { "mcp4017-502", MCP4018_502 },
-       { "mcp4017-103", MCP4018_103 },
-       { "mcp4017-503", MCP4018_503 },
-       { "mcp4017-104", MCP4018_104 },
-       { "mcp4018-502", MCP4018_502 },
-       { "mcp4018-103", MCP4018_103 },
-       { "mcp4018-503", MCP4018_503 },
-       { "mcp4018-104", MCP4018_104 },
-       { "mcp4019-502", MCP4018_502 },
-       { "mcp4019-103", MCP4018_103 },
-       { "mcp4019-503", MCP4018_503 },
-       { "mcp4019-104", MCP4018_104 },
-       {}
+       MCP4018_ID_TABLE("mcp4017-502", MCP4018_502),
+       MCP4018_ID_TABLE("mcp4017-103", MCP4018_103),
+       MCP4018_ID_TABLE("mcp4017-503", MCP4018_503),
+       MCP4018_ID_TABLE("mcp4017-104", MCP4018_104),
+       MCP4018_ID_TABLE("mcp4018-502", MCP4018_502),
+       MCP4018_ID_TABLE("mcp4018-103", MCP4018_103),
+       MCP4018_ID_TABLE("mcp4018-503", MCP4018_503),
+       MCP4018_ID_TABLE("mcp4018-104", MCP4018_104),
+       MCP4018_ID_TABLE("mcp4019-502", MCP4018_502),
+       MCP4018_ID_TABLE("mcp4019-103", MCP4018_103),
+       MCP4018_ID_TABLE("mcp4019-503", MCP4018_503),
+       MCP4018_ID_TABLE("mcp4019-104", MCP4018_104),
+       { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(i2c, mcp4018_id);
 
@@ -157,9 +162,7 @@ static int mcp4018_probe(struct i2c_client *client)
        i2c_set_clientdata(client, indio_dev);
        data->client = client;
 
-       data->cfg = device_get_match_data(dev);
-       if (!data->cfg)
-               data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data];
+       data->cfg = i2c_get_match_data(client);
 
        indio_dev->info = &mcp4018_info;
        indio_dev->channels = &mcp4018_channel;
index c513c00..f28880e 100644 (file)
@@ -206,72 +206,77 @@ static const struct iio_info mcp4531_info = {
        .write_raw = mcp4531_write_raw,
 };
 
+#define MCP4531_ID_TABLE(_name, cfg) {                         \
+       .name = _name,                                          \
+       .driver_data = (kernel_ulong_t)&mcp4531_cfg[cfg],       \
+}
+
 static const struct i2c_device_id mcp4531_id[] = {
-       { "mcp4531-502", MCP453x_502 },
-       { "mcp4531-103", MCP453x_103 },
-       { "mcp4531-503", MCP453x_503 },
-       { "mcp4531-104", MCP453x_104 },
-       { "mcp4532-502", MCP453x_502 },
-       { "mcp4532-103", MCP453x_103 },
-       { "mcp4532-503", MCP453x_503 },
-       { "mcp4532-104", MCP453x_104 },
-       { "mcp4541-502", MCP454x_502 },
-       { "mcp4541-103", MCP454x_103 },
-       { "mcp4541-503", MCP454x_503 },
-       { "mcp4541-104", MCP454x_104 },
-       { "mcp4542-502", MCP454x_502 },
-       { "mcp4542-103", MCP454x_103 },
-       { "mcp4542-503", MCP454x_503 },
-       { "mcp4542-104", MCP454x_104 },
-       { "mcp4551-502", MCP455x_502 },
-       { "mcp4551-103", MCP455x_103 },
-       { "mcp4551-503", MCP455x_503 },
-       { "mcp4551-104", MCP455x_104 },
-       { "mcp4552-502", MCP455x_502 },
-       { "mcp4552-103", MCP455x_103 },
-       { "mcp4552-503", MCP455x_503 },
-       { "mcp4552-104", MCP455x_104 },
-       { "mcp4561-502", MCP456x_502 },
-       { "mcp4561-103", MCP456x_103 },
-       { "mcp4561-503", MCP456x_503 },
-       { "mcp4561-104", MCP456x_104 },
-       { "mcp4562-502", MCP456x_502 },
-       { "mcp4562-103", MCP456x_103 },
-       { "mcp4562-503", MCP456x_503 },
-       { "mcp4562-104", MCP456x_104 },
-       { "mcp4631-502", MCP463x_502 },
-       { "mcp4631-103", MCP463x_103 },
-       { "mcp4631-503", MCP463x_503 },
-       { "mcp4631-104", MCP463x_104 },
-       { "mcp4632-502", MCP463x_502 },
-       { "mcp4632-103", MCP463x_103 },
-       { "mcp4632-503", MCP463x_503 },
-       { "mcp4632-104", MCP463x_104 },
-       { "mcp4641-502", MCP464x_502 },
-       { "mcp4641-103", MCP464x_103 },
-       { "mcp4641-503", MCP464x_503 },
-       { "mcp4641-104", MCP464x_104 },
-       { "mcp4642-502", MCP464x_502 },
-       { "mcp4642-103", MCP464x_103 },
-       { "mcp4642-503", MCP464x_503 },
-       { "mcp4642-104", MCP464x_104 },
-       { "mcp4651-502", MCP465x_502 },
-       { "mcp4651-103", MCP465x_103 },
-       { "mcp4651-503", MCP465x_503 },
-       { "mcp4651-104", MCP465x_104 },
-       { "mcp4652-502", MCP465x_502 },
-       { "mcp4652-103", MCP465x_103 },
-       { "mcp4652-503", MCP465x_503 },
-       { "mcp4652-104", MCP465x_104 },
-       { "mcp4661-502", MCP466x_502 },
-       { "mcp4661-103", MCP466x_103 },
-       { "mcp4661-503", MCP466x_503 },
-       { "mcp4661-104", MCP466x_104 },
-       { "mcp4662-502", MCP466x_502 },
-       { "mcp4662-103", MCP466x_103 },
-       { "mcp4662-503", MCP466x_503 },
-       { "mcp4662-104", MCP466x_104 },
-       {}
+       MCP4531_ID_TABLE("mcp4531-502", MCP453x_502),
+       MCP4531_ID_TABLE("mcp4531-103", MCP453x_103),
+       MCP4531_ID_TABLE("mcp4531-503", MCP453x_503),
+       MCP4531_ID_TABLE("mcp4531-104", MCP453x_104),
+       MCP4531_ID_TABLE("mcp4532-502", MCP453x_502),
+       MCP4531_ID_TABLE("mcp4532-103", MCP453x_103),
+       MCP4531_ID_TABLE("mcp4532-503", MCP453x_503),
+       MCP4531_ID_TABLE("mcp4532-104", MCP453x_104),
+       MCP4531_ID_TABLE("mcp4541-502", MCP454x_502),
+       MCP4531_ID_TABLE("mcp4541-103", MCP454x_103),
+       MCP4531_ID_TABLE("mcp4541-503", MCP454x_503),
+       MCP4531_ID_TABLE("mcp4541-104", MCP454x_104),
+       MCP4531_ID_TABLE("mcp4542-502", MCP454x_502),
+       MCP4531_ID_TABLE("mcp4542-103", MCP454x_103),
+       MCP4531_ID_TABLE("mcp4542-503", MCP454x_503),
+       MCP4531_ID_TABLE("mcp4542-104", MCP454x_104),
+       MCP4531_ID_TABLE("mcp4551-502", MCP455x_502),
+       MCP4531_ID_TABLE("mcp4551-103", MCP455x_103),
+       MCP4531_ID_TABLE("mcp4551-503", MCP455x_503),
+       MCP4531_ID_TABLE("mcp4551-104", MCP455x_104),
+       MCP4531_ID_TABLE("mcp4552-502", MCP455x_502),
+       MCP4531_ID_TABLE("mcp4552-103", MCP455x_103),
+       MCP4531_ID_TABLE("mcp4552-503", MCP455x_503),
+       MCP4531_ID_TABLE("mcp4552-104", MCP455x_104),
+       MCP4531_ID_TABLE("mcp4561-502", MCP456x_502),
+       MCP4531_ID_TABLE("mcp4561-103", MCP456x_103),
+       MCP4531_ID_TABLE("mcp4561-503", MCP456x_503),
+       MCP4531_ID_TABLE("mcp4561-104", MCP456x_104),
+       MCP4531_ID_TABLE("mcp4562-502", MCP456x_502),
+       MCP4531_ID_TABLE("mcp4562-103", MCP456x_103),
+       MCP4531_ID_TABLE("mcp4562-503", MCP456x_503),
+       MCP4531_ID_TABLE("mcp4562-104", MCP456x_104),
+       MCP4531_ID_TABLE("mcp4631-502", MCP463x_502),
+       MCP4531_ID_TABLE("mcp4631-103", MCP463x_103),
+       MCP4531_ID_TABLE("mcp4631-503", MCP463x_503),
+       MCP4531_ID_TABLE("mcp4631-104", MCP463x_104),
+       MCP4531_ID_TABLE("mcp4632-502", MCP463x_502),
+       MCP4531_ID_TABLE("mcp4632-103", MCP463x_103),
+       MCP4531_ID_TABLE("mcp4632-503", MCP463x_503),
+       MCP4531_ID_TABLE("mcp4632-104", MCP463x_104),
+       MCP4531_ID_TABLE("mcp4641-502", MCP464x_502),
+       MCP4531_ID_TABLE("mcp4641-103", MCP464x_103),
+       MCP4531_ID_TABLE("mcp4641-503", MCP464x_503),
+       MCP4531_ID_TABLE("mcp4641-104", MCP464x_104),
+       MCP4531_ID_TABLE("mcp4642-502", MCP464x_502),
+       MCP4531_ID_TABLE("mcp4642-103", MCP464x_103),
+       MCP4531_ID_TABLE("mcp4642-503", MCP464x_503),
+       MCP4531_ID_TABLE("mcp4642-104", MCP464x_104),
+       MCP4531_ID_TABLE("mcp4651-502", MCP465x_502),
+       MCP4531_ID_TABLE("mcp4651-103", MCP465x_103),
+       MCP4531_ID_TABLE("mcp4651-503", MCP465x_503),
+       MCP4531_ID_TABLE("mcp4651-104", MCP465x_104),
+       MCP4531_ID_TABLE("mcp4652-502", MCP465x_502),
+       MCP4531_ID_TABLE("mcp4652-103", MCP465x_103),
+       MCP4531_ID_TABLE("mcp4652-503", MCP465x_503),
+       MCP4531_ID_TABLE("mcp4652-104", MCP465x_104),
+       MCP4531_ID_TABLE("mcp4661-502", MCP466x_502),
+       MCP4531_ID_TABLE("mcp4661-103", MCP466x_103),
+       MCP4531_ID_TABLE("mcp4661-503", MCP466x_503),
+       MCP4531_ID_TABLE("mcp4661-104", MCP466x_104),
+       MCP4531_ID_TABLE("mcp4662-502", MCP466x_502),
+       MCP4531_ID_TABLE("mcp4662-103", MCP466x_103),
+       MCP4531_ID_TABLE("mcp4662-503", MCP466x_503),
+       MCP4531_ID_TABLE("mcp4662-104", MCP466x_104),
+       { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(i2c, mcp4531_id);
 
@@ -368,9 +373,7 @@ static int mcp4531_probe(struct i2c_client *client)
        i2c_set_clientdata(client, indio_dev);
        data->client = client;
 
-       data->cfg = device_get_match_data(dev);
-       if (!data->cfg)
-               data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data];
+       data->cfg = i2c_get_match_data(client);
 
        indio_dev->info = &mcp4531_info;
        indio_dev->channels = mcp4531_channels;
index 0e5c175..2ca3b0b 100644 (file)
@@ -32,6 +32,18 @@ config CROS_EC_MKBP_PROXIMITY
          To compile this driver as a module, choose M here: the
          module will be called cros_ec_mkbp_proximity.
 
+config IRSD200
+       tristate "Murata IRS-D200 PIR sensor"
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       select REGMAP_I2C
+       depends on I2C
+       help
+         Say Y here to build a driver for the Murata IRS-D200 PIR sensor.
+
+         To compile this driver as a module, choose M here: the module will be
+         called irsd200.
+
 config ISL29501
        tristate "Intersil ISL29501 Time Of Flight sensor"
        depends on I2C
index cc838bb..f365983 100644 (file)
@@ -6,6 +6,7 @@
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_AS3935)           += as3935.o
 obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o
+obj-$(CONFIG_IRSD200)          += irsd200.o
 obj-$(CONFIG_ISL29501)         += isl29501.o
 obj-$(CONFIG_LIDAR_LITE_V2)    += pulsedlight-lidar-lite-v2.o
 obj-$(CONFIG_MB1232)           += mb1232.o
diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c
new file mode 100644 (file)
index 0000000..5bd791b
--- /dev/null
@@ -0,0 +1,958 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Murata IRS-D200 PIR sensor.
+ *
+ * Copyright (C) 2023 Axis Communications AB
+ */
+
+#include <asm/unaligned.h>
+#include <linux/bitfield.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/types.h>
+
+#define IRS_DRV_NAME "irsd200"
+
+/* Registers. */
+#define IRS_REG_OP             0x00    /* Operation mode. */
+#define IRS_REG_DATA_LO                0x02    /* Sensor data LSB. */
+#define IRS_REG_DATA_HI                0x03    /* Sensor data MSB. */
+#define IRS_REG_STATUS         0x04    /* Interrupt status. */
+#define IRS_REG_COUNT          0x05    /* Count of exceeding threshold. */
+#define IRS_REG_DATA_RATE      0x06    /* Output data rate. */
+#define IRS_REG_FILTER         0x07    /* High-pass and low-pass filter. */
+#define IRS_REG_INTR           0x09    /* Interrupt mode. */
+#define IRS_REG_NR_COUNT       0x0a    /* Number of counts before interrupt. */
+#define IRS_REG_THR_HI         0x0b    /* Upper threshold. */
+#define IRS_REG_THR_LO         0x0c    /* Lower threshold. */
+#define IRS_REG_TIMER_LO       0x0d    /* Timer setting LSB. */
+#define IRS_REG_TIMER_HI       0x0e    /* Timer setting MSB. */
+
+/* Interrupt status bits. */
+#define IRS_INTR_DATA          0       /* Data update. */
+#define IRS_INTR_TIMER         1       /* Timer expiration. */
+#define IRS_INTR_COUNT_THR_AND 2       /* Count "AND" threshold. */
+#define IRS_INTR_COUNT_THR_OR  3       /* Count "OR" threshold. */
+
+/* Operation states. */
+#define IRS_OP_ACTIVE          0x00
+#define IRS_OP_SLEEP           0x01
+
+/*
+ * Quantization scale value for threshold. Used for conversion from/to register
+ * value.
+ */
+#define IRS_THR_QUANT_SCALE    128
+
+#define IRS_UPPER_COUNT(count) FIELD_GET(GENMASK(7, 4), count)
+#define IRS_LOWER_COUNT(count) FIELD_GET(GENMASK(3, 0), count)
+
+/* Index corresponds to the value of IRS_REG_DATA_RATE register. */
+static const int irsd200_data_rates[] = {
+       50,
+       100,
+};
+
+/* Index corresponds to the (field) value of IRS_REG_FILTER register. */
+static const unsigned int irsd200_lp_filter_freq[] = {
+       10,
+       7,
+};
+
+/*
+ * Index corresponds to the (field) value of IRS_REG_FILTER register. Note that
+ * this represents a fractional value (e.g the first value corresponds to 3 / 10
+ * = 0.3 Hz).
+ */
+static const unsigned int irsd200_hp_filter_freq[][2] = {
+       { 3, 10 },
+       { 5, 10 },
+};
+
+/* Register fields. */
+enum irsd200_regfield {
+       /* Data interrupt. */
+       IRS_REGF_INTR_DATA,
+       /* Timer interrupt. */
+       IRS_REGF_INTR_TIMER,
+       /* AND count threshold interrupt. */
+       IRS_REGF_INTR_COUNT_THR_AND,
+       /* OR count threshold interrupt. */
+       IRS_REGF_INTR_COUNT_THR_OR,
+
+       /* Low-pass filter frequency. */
+       IRS_REGF_LP_FILTER,
+       /* High-pass filter frequency. */
+       IRS_REGF_HP_FILTER,
+
+       /* Sentinel value. */
+       IRS_REGF_MAX
+};
+
+static const struct reg_field irsd200_regfields[] = {
+       [IRS_REGF_INTR_DATA] =
+               REG_FIELD(IRS_REG_INTR, IRS_INTR_DATA, IRS_INTR_DATA),
+       [IRS_REGF_INTR_TIMER] =
+               REG_FIELD(IRS_REG_INTR, IRS_INTR_TIMER, IRS_INTR_TIMER),
+       [IRS_REGF_INTR_COUNT_THR_AND] = REG_FIELD(
+               IRS_REG_INTR, IRS_INTR_COUNT_THR_AND, IRS_INTR_COUNT_THR_AND),
+       [IRS_REGF_INTR_COUNT_THR_OR] = REG_FIELD(
+               IRS_REG_INTR, IRS_INTR_COUNT_THR_OR, IRS_INTR_COUNT_THR_OR),
+
+       [IRS_REGF_LP_FILTER] = REG_FIELD(IRS_REG_FILTER, 1, 1),
+       [IRS_REGF_HP_FILTER] = REG_FIELD(IRS_REG_FILTER, 0, 0),
+};
+
+static const struct regmap_config irsd200_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = IRS_REG_TIMER_HI,
+};
+
+struct irsd200_data {
+       struct regmap *regmap;
+       struct regmap_field *regfields[IRS_REGF_MAX];
+       struct device *dev;
+};
+
+static int irsd200_setup(struct irsd200_data *data)
+{
+       unsigned int val;
+       int ret;
+
+       /* Disable all interrupt sources. */
+       ret = regmap_write(data->regmap, IRS_REG_INTR, 0);
+       if (ret) {
+               dev_err(data->dev, "Could not set interrupt sources (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       /* Set operation to active. */
+       ret = regmap_write(data->regmap, IRS_REG_OP, IRS_OP_ACTIVE);
+       if (ret) {
+               dev_err(data->dev, "Could not set operation mode (%d)\n", ret);
+               return ret;
+       }
+
+       /* Clear threshold count. */
+       ret = regmap_read(data->regmap, IRS_REG_COUNT, &val);
+       if (ret) {
+               dev_err(data->dev, "Could not clear threshold count (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       /* Clear status. */
+       ret = regmap_write(data->regmap, IRS_REG_STATUS, 0x0f);
+       if (ret) {
+               dev_err(data->dev, "Could not clear status (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int irsd200_read_threshold(struct irsd200_data *data,
+                                 enum iio_event_direction dir, int *val)
+{
+       unsigned int regval;
+       unsigned int reg;
+       int scale;
+       int ret;
+
+       /* Set quantization scale. */
+       if (dir == IIO_EV_DIR_RISING) {
+               scale = IRS_THR_QUANT_SCALE;
+               reg = IRS_REG_THR_HI;
+       } else if (dir == IIO_EV_DIR_FALLING) {
+               scale = -IRS_THR_QUANT_SCALE;
+               reg = IRS_REG_THR_LO;
+       } else {
+               return -EINVAL;
+       }
+
+       ret = regmap_read(data->regmap, reg, &regval);
+       if (ret) {
+               dev_err(data->dev, "Could not read threshold (%d)\n", ret);
+               return ret;
+       }
+
+       *val = ((int)regval) * scale;
+
+       return 0;
+}
+
+static int irsd200_write_threshold(struct irsd200_data *data,
+                                  enum iio_event_direction dir, int val)
+{
+       unsigned int regval;
+       unsigned int reg;
+       int scale;
+       int ret;
+
+       /* Set quantization scale. */
+       if (dir == IIO_EV_DIR_RISING) {
+               if (val < 0)
+                       return -ERANGE;
+
+               scale = IRS_THR_QUANT_SCALE;
+               reg = IRS_REG_THR_HI;
+       } else if (dir == IIO_EV_DIR_FALLING) {
+               if (val > 0)
+                       return -ERANGE;
+
+               scale = -IRS_THR_QUANT_SCALE;
+               reg = IRS_REG_THR_LO;
+       } else {
+               return -EINVAL;
+       }
+
+       regval = val / scale;
+
+       if (regval >= BIT(8))
+               return -ERANGE;
+
+       ret = regmap_write(data->regmap, reg, regval);
+       if (ret) {
+               dev_err(data->dev, "Could not write threshold (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int irsd200_read_data(struct irsd200_data *data, s16 *val)
+{
+       __le16 buf;
+       int ret;
+
+       ret = regmap_bulk_read(data->regmap, IRS_REG_DATA_LO, &buf,
+                              sizeof(buf));
+       if (ret) {
+               dev_err(data->dev, "Could not bulk read data (%d)\n", ret);
+               return ret;
+       }
+
+       *val = le16_to_cpu(buf);
+
+       return 0;
+}
+
+static int irsd200_read_data_rate(struct irsd200_data *data, int *val)
+{
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_read(data->regmap, IRS_REG_DATA_RATE, &regval);
+       if (ret) {
+               dev_err(data->dev, "Could not read data rate (%d)\n", ret);
+               return ret;
+       }
+
+       if (regval >= ARRAY_SIZE(irsd200_data_rates))
+               return -ERANGE;
+
+       *val = irsd200_data_rates[regval];
+
+       return 0;
+}
+
+static int irsd200_write_data_rate(struct irsd200_data *data, int val)
+{
+       size_t idx;
+       int ret;
+
+       for (idx = 0; idx < ARRAY_SIZE(irsd200_data_rates); ++idx) {
+               if (irsd200_data_rates[idx] == val)
+                       break;
+       }
+
+       if (idx == ARRAY_SIZE(irsd200_data_rates))
+               return -ERANGE;
+
+       ret = regmap_write(data->regmap, IRS_REG_DATA_RATE, idx);
+       if (ret) {
+               dev_err(data->dev, "Could not write data rate (%d)\n", ret);
+               return ret;
+       }
+
+       /*
+        * Data sheet says the device needs 3 seconds of settling time. The
+        * device operates normally during this period though. This is more of a
+        * "guarantee" than trying to prevent other user space reads/writes.
+        */
+       ssleep(3);
+
+       return 0;
+}
+
+static int irsd200_read_timer(struct irsd200_data *data, int *val, int *val2)
+{
+       __le16 buf;
+       int ret;
+
+       ret = regmap_bulk_read(data->regmap, IRS_REG_TIMER_LO, &buf,
+                              sizeof(buf));
+       if (ret) {
+               dev_err(data->dev, "Could not bulk read timer (%d)\n", ret);
+               return ret;
+       }
+
+       ret = irsd200_read_data_rate(data, val2);
+       if (ret)
+               return ret;
+
+       *val = le16_to_cpu(buf);
+
+       return 0;
+}
+
+static int irsd200_write_timer(struct irsd200_data *data, int val, int val2)
+{
+       unsigned int regval;
+       int data_rate;
+       __le16 buf;
+       int ret;
+
+       if (val < 0 || val2 < 0)
+               return -ERANGE;
+
+       ret = irsd200_read_data_rate(data, &data_rate);
+       if (ret)
+               return ret;
+
+       /* Quantize from seconds. */
+       regval = val * data_rate + (val2 * data_rate) / 1000000;
+
+       /* Value is 10 bits. */
+       if (regval >= BIT(10))
+               return -ERANGE;
+
+       buf = cpu_to_le16((u16)regval);
+
+       ret = regmap_bulk_write(data->regmap, IRS_REG_TIMER_LO, &buf,
+                               sizeof(buf));
+       if (ret) {
+               dev_err(data->dev, "Could not bulk write timer (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int irsd200_read_nr_count(struct irsd200_data *data, int *val)
+{
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_read(data->regmap, IRS_REG_NR_COUNT, &regval);
+       if (ret) {
+               dev_err(data->dev, "Could not read nr count (%d)\n", ret);
+               return ret;
+       }
+
+       *val = regval;
+
+       return 0;
+}
+
+static int irsd200_write_nr_count(struct irsd200_data *data, int val)
+{
+       unsigned int regval;
+       int ret;
+
+       /* A value of zero means that IRS_REG_STATUS is never set. */
+       if (val <= 0 || val >= 8)
+               return -ERANGE;
+
+       regval = val;
+
+       if (regval >= 2) {
+               /*
+                * According to the data sheet, timer must be also set in this
+                * case (i.e. be non-zero). Check and enforce that.
+                */
+               ret = irsd200_read_timer(data, &val, &val);
+               if (ret)
+                       return ret;
+
+               if (val == 0) {
+                       dev_err(data->dev,
+                               "Timer must be non-zero when nr count is %u\n",
+                               regval);
+                       return -EPERM;
+               }
+       }
+
+       ret = regmap_write(data->regmap, IRS_REG_NR_COUNT, regval);
+       if (ret) {
+               dev_err(data->dev, "Could not write nr count (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int irsd200_read_lp_filter(struct irsd200_data *data, int *val)
+{
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_field_read(data->regfields[IRS_REGF_LP_FILTER], &regval);
+       if (ret) {
+               dev_err(data->dev, "Could not read lp filter frequency (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       *val = irsd200_lp_filter_freq[regval];
+
+       return 0;
+}
+
+static int irsd200_write_lp_filter(struct irsd200_data *data, int val)
+{
+       size_t idx;
+       int ret;
+
+       for (idx = 0; idx < ARRAY_SIZE(irsd200_lp_filter_freq); ++idx) {
+               if (irsd200_lp_filter_freq[idx] == val)
+                       break;
+       }
+
+       if (idx == ARRAY_SIZE(irsd200_lp_filter_freq))
+               return -ERANGE;
+
+       ret = regmap_field_write(data->regfields[IRS_REGF_LP_FILTER], idx);
+       if (ret) {
+               dev_err(data->dev, "Could not write lp filter frequency (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int irsd200_read_hp_filter(struct irsd200_data *data, int *val,
+                                 int *val2)
+{
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_field_read(data->regfields[IRS_REGF_HP_FILTER], &regval);
+       if (ret) {
+               dev_err(data->dev, "Could not read hp filter frequency (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       *val = irsd200_hp_filter_freq[regval][0];
+       *val2 = irsd200_hp_filter_freq[regval][1];
+
+       return 0;
+}
+
+static int irsd200_write_hp_filter(struct irsd200_data *data, int val, int val2)
+{
+       size_t idx;
+       int ret;
+
+       /* Truncate fractional part to one digit. */
+       val2 /= 100000;
+
+       for (idx = 0; idx < ARRAY_SIZE(irsd200_hp_filter_freq); ++idx) {
+               if (irsd200_hp_filter_freq[idx][0] == val2)
+                       break;
+       }
+
+       if (idx == ARRAY_SIZE(irsd200_hp_filter_freq) || val != 0)
+               return -ERANGE;
+
+       ret = regmap_field_write(data->regfields[IRS_REGF_HP_FILTER], idx);
+       if (ret) {
+               dev_err(data->dev, "Could not write hp filter frequency (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int irsd200_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan, int *val,
+                           int *val2, long mask)
+{
+       struct irsd200_data *data = iio_priv(indio_dev);
+       int ret;
+       s16 buf;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = irsd200_read_data(data, &buf);
+               if (ret)
+                       return ret;
+
+               *val = buf;
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               ret = irsd200_read_data_rate(data, val);
+               if (ret)
+                       return ret;
+
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+               ret = irsd200_read_lp_filter(data, val);
+               if (ret)
+                       return ret;
+
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+               ret = irsd200_read_hp_filter(data, val, val2);
+               if (ret)
+                       return ret;
+
+               return IIO_VAL_FRACTIONAL;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int irsd200_read_avail(struct iio_dev *indio_dev,
+                             struct iio_chan_spec const *chan,
+                             const int **vals, int *type, int *length,
+                             long mask)
+{
+       switch (mask) {
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               *vals = irsd200_data_rates;
+               *type = IIO_VAL_INT;
+               *length = ARRAY_SIZE(irsd200_data_rates);
+               return IIO_AVAIL_LIST;
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+               *vals = irsd200_lp_filter_freq;
+               *type = IIO_VAL_INT;
+               *length = ARRAY_SIZE(irsd200_lp_filter_freq);
+               return IIO_AVAIL_LIST;
+       case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+               *vals = (int *)irsd200_hp_filter_freq;
+               *type = IIO_VAL_FRACTIONAL;
+               *length = 2 * ARRAY_SIZE(irsd200_hp_filter_freq);
+               return IIO_AVAIL_LIST;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int irsd200_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan, int val,
+                            int val2, long mask)
+{
+       struct irsd200_data *data = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               return irsd200_write_data_rate(data, val);
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+               return irsd200_write_lp_filter(data, val);
+       case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+               return irsd200_write_hp_filter(data, val, val2);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int irsd200_read_event(struct iio_dev *indio_dev,
+                             const struct iio_chan_spec *chan,
+                             enum iio_event_type type,
+                             enum iio_event_direction dir,
+                             enum iio_event_info info, int *val, int *val2)
+{
+       struct irsd200_data *data = iio_priv(indio_dev);
+       int ret;
+
+       switch (info) {
+       case IIO_EV_INFO_VALUE:
+               ret = irsd200_read_threshold(data, dir, val);
+               if (ret)
+                       return ret;
+
+               return IIO_VAL_INT;
+       case IIO_EV_INFO_RUNNING_PERIOD:
+               ret = irsd200_read_timer(data, val, val2);
+               if (ret)
+                       return ret;
+
+               return IIO_VAL_FRACTIONAL;
+       case IIO_EV_INFO_RUNNING_COUNT:
+               ret = irsd200_read_nr_count(data, val);
+               if (ret)
+                       return ret;
+
+               return IIO_VAL_INT;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int irsd200_write_event(struct iio_dev *indio_dev,
+                              const struct iio_chan_spec *chan,
+                              enum iio_event_type type,
+                              enum iio_event_direction dir,
+                              enum iio_event_info info, int val, int val2)
+{
+       struct irsd200_data *data = iio_priv(indio_dev);
+
+       switch (info) {
+       case IIO_EV_INFO_VALUE:
+               return irsd200_write_threshold(data, dir, val);
+       case IIO_EV_INFO_RUNNING_PERIOD:
+               return irsd200_write_timer(data, val, val2);
+       case IIO_EV_INFO_RUNNING_COUNT:
+               return irsd200_write_nr_count(data, val);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int irsd200_read_event_config(struct iio_dev *indio_dev,
+                                    const struct iio_chan_spec *chan,
+                                    enum iio_event_type type,
+                                    enum iio_event_direction dir)
+{
+       struct irsd200_data *data = iio_priv(indio_dev);
+       unsigned int val;
+       int ret;
+
+       switch (type) {
+       case IIO_EV_TYPE_THRESH:
+               ret = regmap_field_read(
+                       data->regfields[IRS_REGF_INTR_COUNT_THR_OR], &val);
+               if (ret)
+                       return ret;
+
+               return val;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int irsd200_write_event_config(struct iio_dev *indio_dev,
+                                     const struct iio_chan_spec *chan,
+                                     enum iio_event_type type,
+                                     enum iio_event_direction dir, int state)
+{
+       struct irsd200_data *data = iio_priv(indio_dev);
+       unsigned int tmp;
+       int ret;
+
+       switch (type) {
+       case IIO_EV_TYPE_THRESH:
+               /* Clear the count register (by reading from it). */
+               ret = regmap_read(data->regmap, IRS_REG_COUNT, &tmp);
+               if (ret)
+                       return ret;
+
+               return regmap_field_write(
+                       data->regfields[IRS_REGF_INTR_COUNT_THR_OR], !!state);
+       default:
+               return -EINVAL;
+       }
+}
+
+static irqreturn_t irsd200_irq_thread(int irq, void *dev_id)
+{
+       struct iio_dev *indio_dev = dev_id;
+       struct irsd200_data *data = iio_priv(indio_dev);
+       enum iio_event_direction dir;
+       unsigned int lower_count;
+       unsigned int upper_count;
+       unsigned int status = 0;
+       unsigned int source = 0;
+       unsigned int clear = 0;
+       unsigned int count = 0;
+       int ret;
+
+       ret = regmap_read(data->regmap, IRS_REG_INTR, &source);
+       if (ret) {
+               dev_err(data->dev, "Could not read interrupt source (%d)\n",
+                       ret);
+               return IRQ_HANDLED;
+       }
+
+       ret = regmap_read(data->regmap, IRS_REG_STATUS, &status);
+       if (ret) {
+               dev_err(data->dev, "Could not acknowledge interrupt (%d)\n",
+                       ret);
+               return IRQ_HANDLED;
+       }
+
+       if (status & BIT(IRS_INTR_DATA) && iio_buffer_enabled(indio_dev)) {
+               iio_trigger_poll_nested(indio_dev->trig);
+               clear |= BIT(IRS_INTR_DATA);
+       }
+
+       if (status & BIT(IRS_INTR_COUNT_THR_OR) &&
+           source & BIT(IRS_INTR_COUNT_THR_OR)) {
+               /*
+                * The register value resets to zero after reading. We therefore
+                * need to read once and manually extract the lower and upper
+                * count register fields.
+                */
+               ret = regmap_read(data->regmap, IRS_REG_COUNT, &count);
+               if (ret)
+                       dev_err(data->dev, "Could not read count (%d)\n", ret);
+
+               upper_count = IRS_UPPER_COUNT(count);
+               lower_count = IRS_LOWER_COUNT(count);
+
+               /*
+                * We only check the OR mode to be able to push events for
+                * rising and falling thresholds. AND mode is covered when both
+                * upper and lower count is non-zero, and is signaled with
+                * IIO_EV_DIR_EITHER.
+                */
+               if (upper_count && !lower_count)
+                       dir = IIO_EV_DIR_RISING;
+               else if (!upper_count && lower_count)
+                       dir = IIO_EV_DIR_FALLING;
+               else
+                       dir = IIO_EV_DIR_EITHER;
+
+               iio_push_event(indio_dev,
+                              IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+                                                   IIO_EV_TYPE_THRESH, dir),
+                              iio_get_time_ns(indio_dev));
+
+               /*
+                * The OR mode will always trigger when the AND mode does, but
+                * not vice versa. However, it seems like the AND bit needs to
+                * be cleared if data capture _and_ threshold count interrupts
+                * are desirable, even though it hasn't explicitly been selected
+                * (with IRS_REG_INTR). Either way, it doesn't hurt...
+                */
+               clear |= BIT(IRS_INTR_COUNT_THR_OR) |
+                        BIT(IRS_INTR_COUNT_THR_AND);
+       }
+
+       if (!clear)
+               return IRQ_NONE;
+
+       ret = regmap_write(data->regmap, IRS_REG_STATUS, clear);
+       if (ret)
+               dev_err(data->dev,
+                       "Could not clear interrupt status (%d)\n", ret);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t irsd200_trigger_handler(int irq, void *pollf)
+{
+       struct iio_dev *indio_dev = ((struct iio_poll_func *)pollf)->indio_dev;
+       struct irsd200_data *data = iio_priv(indio_dev);
+       s16 buf = 0;
+       int ret;
+
+       ret = irsd200_read_data(data, &buf);
+       if (ret)
+               goto end;
+
+       iio_push_to_buffers_with_timestamp(indio_dev, &buf,
+                                          iio_get_time_ns(indio_dev));
+
+end:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int irsd200_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+       struct irsd200_data *data = iio_trigger_get_drvdata(trig);
+       int ret;
+
+       ret = regmap_field_write(data->regfields[IRS_REGF_INTR_DATA], state);
+       if (ret) {
+               dev_err(data->dev, "Could not %s data interrupt source (%d)\n",
+                       state ? "enable" : "disable", ret);
+       }
+
+       return ret;
+}
+
+static const struct iio_info irsd200_info = {
+       .read_raw = irsd200_read_raw,
+       .read_avail = irsd200_read_avail,
+       .write_raw = irsd200_write_raw,
+       .read_event_value = irsd200_read_event,
+       .write_event_value = irsd200_write_event,
+       .read_event_config = irsd200_read_event_config,
+       .write_event_config = irsd200_write_event_config,
+};
+
+static const struct iio_trigger_ops irsd200_trigger_ops = {
+       .set_trigger_state = irsd200_set_trigger_state,
+       .validate_device = iio_trigger_validate_own_device,
+};
+
+static const struct iio_event_spec irsd200_event_spec[] = {
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       },
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       },
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_EITHER,
+               .mask_separate =
+                       BIT(IIO_EV_INFO_RUNNING_PERIOD) |
+                       BIT(IIO_EV_INFO_RUNNING_COUNT) |
+                       BIT(IIO_EV_INFO_ENABLE),
+       },
+};
+
+static const struct iio_chan_spec irsd200_channels[] = {
+       {
+               .type = IIO_PROXIMITY,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) |
+                       BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+                       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+                       BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY),
+               .info_mask_separate_available =
+                       BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+                       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+                       BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY),
+               .event_spec = irsd200_event_spec,
+               .num_event_specs = ARRAY_SIZE(irsd200_event_spec),
+               .scan_type = {
+                       .sign = 's',
+                       .realbits = 16,
+                       .storagebits = 16,
+                       .endianness = IIO_CPU,
+               },
+       },
+};
+
+static int irsd200_probe(struct i2c_client *client)
+{
+       struct iio_trigger *trigger;
+       struct irsd200_data *data;
+       struct iio_dev *indio_dev;
+       size_t i;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return dev_err_probe(&client->dev, -ENOMEM,
+                                    "Could not allocate iio device\n");
+
+       data = iio_priv(indio_dev);
+       data->dev = &client->dev;
+
+       data->regmap = devm_regmap_init_i2c(client, &irsd200_regmap_config);
+       if (IS_ERR(data->regmap))
+               return dev_err_probe(data->dev, PTR_ERR(data->regmap),
+                                    "Could not initialize regmap\n");
+
+       for (i = 0; i < IRS_REGF_MAX; ++i) {
+               data->regfields[i] = devm_regmap_field_alloc(
+                       data->dev, data->regmap, irsd200_regfields[i]);
+               if (IS_ERR(data->regfields[i]))
+                       return dev_err_probe(
+                               data->dev, PTR_ERR(data->regfields[i]),
+                               "Could not allocate register field %zu\n", i);
+       }
+
+       ret = devm_regulator_get_enable(data->dev, "vdd");
+       if (ret)
+               return dev_err_probe(
+                       data->dev, ret,
+                       "Could not get and enable regulator (%d)\n", ret);
+
+       ret = irsd200_setup(data);
+       if (ret)
+               return ret;
+
+       indio_dev->info = &irsd200_info;
+       indio_dev->name = IRS_DRV_NAME;
+       indio_dev->channels = irsd200_channels;
+       indio_dev->num_channels = ARRAY_SIZE(irsd200_channels);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+
+       if (!client->irq)
+               return dev_err_probe(data->dev, -ENXIO, "No irq available\n");
+
+       ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev, NULL,
+                                             irsd200_trigger_handler, NULL);
+       if (ret)
+               return dev_err_probe(
+                       data->dev, ret,
+                       "Could not setup iio triggered buffer (%d)\n", ret);
+
+       ret = devm_request_threaded_irq(data->dev, client->irq, NULL,
+                                       irsd200_irq_thread,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       NULL, indio_dev);
+       if (ret)
+               return dev_err_probe(data->dev, ret,
+                                    "Could not request irq (%d)\n", ret);
+
+       trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name,
+                                        iio_device_id(indio_dev));
+       if (!trigger)
+               return dev_err_probe(data->dev, -ENOMEM,
+                                    "Could not allocate iio trigger\n");
+
+       trigger->ops = &irsd200_trigger_ops;
+       iio_trigger_set_drvdata(trigger, data);
+
+       ret = devm_iio_trigger_register(data->dev, trigger);
+       if (ret)
+               return dev_err_probe(data->dev, ret,
+                                    "Could not register iio trigger (%d)\n",
+                                    ret);
+
+       ret = devm_iio_device_register(data->dev, indio_dev);
+       if (ret)
+               return dev_err_probe(data->dev, ret,
+                                    "Could not register iio device (%d)\n",
+                                    ret);
+
+       return 0;
+}
+
+static const struct of_device_id irsd200_of_match[] = {
+       {
+               .compatible = "murata,irsd200",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, irsd200_of_match);
+
+static struct i2c_driver irsd200_driver = {
+       .driver = {
+               .name = IRS_DRV_NAME,
+               .of_match_table = irsd200_of_match,
+       },
+       .probe = irsd200_probe,
+};
+module_i2c_driver(irsd200_driver);
+
+MODULE_AUTHOR("Waqar Hameed <waqar.hameed@axis.com>");
+MODULE_DESCRIPTION("Murata IRS-D200 PIR sensor driver");
+MODULE_LICENSE("GPL");
index fe45ca3..bcebaca 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/err.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 
index fb1073c..614e65c 100644 (file)
@@ -76,7 +76,7 @@ static s16 mb1232_read_distance(struct mb1232_data *data)
                goto error_unlock;
        }
 
-       if (data->irqnr >= 0) {
+       if (data->irqnr > 0) {
                /* it cannot take more than 100 ms */
                ret = wait_for_completion_killable_timeout(&data->ranging,
                                                                        HZ/10);
@@ -212,10 +212,7 @@ static int mb1232_probe(struct i2c_client *client)
        init_completion(&data->ranging);
 
        data->irqnr = fwnode_irq_get(dev_fwnode(&client->dev), 0);
-       if (data->irqnr <= 0) {
-               /* usage of interrupt is optional */
-               data->irqnr = -1;
-       } else {
+       if (data->irqnr > 0) {
                ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq,
                                IRQF_TRIGGER_FALLING, id->name, indio_dev);
                if (ret < 0) {
index 676dc87..07bb5df 100644 (file)
@@ -27,8 +27,8 @@
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 
 #include <linux/iio/iio.h>
index 2e447a3..f1e1891 100644 (file)
@@ -73,7 +73,6 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev)
 {
        struct stm32_lptim_trigger *priv;
        u32 index;
-       int ret;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -88,13 +87,7 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev)
        priv->dev = &pdev->dev;
        priv->trg = stm32_lptim_triggers[index];
 
-       ret = stm32_lptim_setup_trig(priv);
-       if (ret)
-               return ret;
-
-       platform_set_drvdata(pdev, priv);
-
-       return 0;
+       return stm32_lptim_setup_trig(priv);
 }
 
 static const struct of_device_id stm32_lptim_trig_of_match[] = {
index 75e427f..cadd4a8 100644 (file)
@@ -496,6 +496,7 @@ config HISI_HIKEY_USB
 config OPEN_DICE
        tristate "Open Profile for DICE driver"
        depends on OF_RESERVED_MEM
+       depends on HAS_IOMEM
        help
          This driver exposes a DICE reserved memory region to userspace via
          a character device. The memory region contains Compound Device
index 7f9f562..ee590c4 100644 (file)
@@ -212,8 +212,7 @@ static int ssc_probe(struct platform_device *pdev)
                        of_property_read_bool(np, "atmel,clk-from-rk-pin");
        }
 
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       ssc->regs = devm_ioremap_resource(&pdev->dev, regs);
+       ssc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
        if (IS_ERR(ssc->regs))
                return PTR_ERR(ssc->regs);
 
index 25d5122..386884c 100644 (file)
@@ -340,7 +340,7 @@ struct bcm_vk_proc_mon_info {
 };
 
 struct bcm_vk_hb_ctrl {
-       struct timer_list timer;
+       struct delayed_work work;
        u32 last_uptime;
        u32 lost_cnt;
 };
index 3c08150..e17d812 100644 (file)
@@ -137,11 +137,11 @@ void bcm_vk_set_host_alert(struct bcm_vk *vk, u32 bit_mask)
 #define BCM_VK_HB_TIMER_VALUE (BCM_VK_HB_TIMER_S * HZ)
 #define BCM_VK_HB_LOST_MAX (27 / BCM_VK_HB_TIMER_S)
 
-static void bcm_vk_hb_poll(struct timer_list *t)
+static void bcm_vk_hb_poll(struct work_struct *work)
 {
        u32 uptime_s;
-       struct bcm_vk_hb_ctrl *hb = container_of(t, struct bcm_vk_hb_ctrl,
-                                                timer);
+       struct bcm_vk_hb_ctrl *hb = container_of(to_delayed_work(work), struct bcm_vk_hb_ctrl,
+                                                work);
        struct bcm_vk *vk = container_of(hb, struct bcm_vk, hb_ctrl);
 
        if (bcm_vk_drv_access_ok(vk) && hb_mon_is_on()) {
@@ -177,22 +177,22 @@ static void bcm_vk_hb_poll(struct timer_list *t)
                bcm_vk_set_host_alert(vk, ERR_LOG_HOST_HB_FAIL);
        }
        /* re-arm timer */
-       mod_timer(&hb->timer, jiffies + BCM_VK_HB_TIMER_VALUE);
+       schedule_delayed_work(&hb->work, BCM_VK_HB_TIMER_VALUE);
 }
 
 void bcm_vk_hb_init(struct bcm_vk *vk)
 {
        struct bcm_vk_hb_ctrl *hb = &vk->hb_ctrl;
 
-       timer_setup(&hb->timer, bcm_vk_hb_poll, 0);
-       mod_timer(&hb->timer, jiffies + BCM_VK_HB_TIMER_VALUE);
+       INIT_DELAYED_WORK(&hb->work, bcm_vk_hb_poll);
+       schedule_delayed_work(&hb->work, BCM_VK_HB_TIMER_VALUE);
 }
 
 void bcm_vk_hb_deinit(struct bcm_vk *vk)
 {
        struct bcm_vk_hb_ctrl *hb = &vk->hb_ctrl;
 
-       del_timer(&hb->timer);
+       cancel_delayed_work_sync(&hb->work);
 }
 
 static void bcm_vk_msgid_bitmap_clear(struct bcm_vk *vk,
index cc0caf9..b054562 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/rcupdate.h>
 #include <asm/errno.h>
 #include <misc/cxl-base.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include "cxl.h"
 
index 740c063..1d1f30b 100644 (file)
@@ -913,15 +913,9 @@ static ssize_t idt_dbgfs_csr_write(struct file *filep, const char __user *ubuf,
                return 0;
 
        /* Copy data from User-space */
-       buf = kmalloc(count + 1, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       if (copy_from_user(buf, ubuf, count)) {
-               ret = -EFAULT;
-               goto free_buf;
-       }
-       buf[count] = 0;
+       buf = memdup_user_nul(ubuf, count);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
 
        /* Find position of colon in the buffer */
        colon_ch = strnchr(buf, count, ':');
@@ -1294,14 +1288,15 @@ static int idt_create_sysfs_files(struct idt_89hpesx_dev *pdev)
                return 0;
        }
 
-       /* Allocate memory for attribute file */
-       pdev->ee_file = devm_kmalloc(dev, sizeof(*pdev->ee_file), GFP_KERNEL);
+       /*
+        * Allocate memory for attribute file and copy the declared EEPROM attr
+        * structure to change some of fields
+        */
+       pdev->ee_file = devm_kmemdup(dev, &bin_attr_eeprom,
+                                    sizeof(*pdev->ee_file), GFP_KERNEL);
        if (!pdev->ee_file)
                return -ENOMEM;
 
-       /* Copy the declared EEPROM attr structure to change some of fields */
-       memcpy(pdev->ee_file, &bin_attr_eeprom, sizeof(*pdev->ee_file));
-
        /* In case of read-only EEPROM get rid of write ability */
        if (pdev->eero) {
                pdev->ee_file->attr.mode &= ~0200;
index 9666d28..1c7c053 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/sort.h>
 #include <linux/of_platform.h>
 #include <linux/rpmsg.h>
index b030108..224a7e9 100644 (file)
@@ -42,7 +42,7 @@ MODULE_VERSION(DRV_VERSION);
 MODULE_LICENSE("GPL");
 
 static char genwqe_driver_name[] = GENWQE_DEVNAME;
-static struct class *class_genwqe;
+
 static struct dentry *debugfs_genwqe;
 static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX];
 
@@ -105,6 +105,26 @@ static const struct pci_device_id genwqe_device_table[] = {
 MODULE_DEVICE_TABLE(pci, genwqe_device_table);
 
 /**
+ * genwqe_devnode() - Set default access mode for genwqe devices.
+ * @dev:       Pointer to device (unused)
+ * @mode:      Carrier to pass-back given mode (permissions)
+ *
+ * Default mode should be rw for everybody. Do not change default
+ * device name.
+ */
+static char *genwqe_devnode(const struct device *dev, umode_t *mode)
+{
+       if (mode)
+               *mode = 0666;
+       return NULL;
+}
+
+static const struct class class_genwqe = {
+       .name = GENWQE_DEVNAME,
+       .devnode = genwqe_devnode,
+};
+
+/**
  * genwqe_dev_alloc() - Create and prepare a new card descriptor
  *
  * Return: Pointer to card descriptor, or ERR_PTR(err) on error
@@ -126,7 +146,7 @@ static struct genwqe_dev *genwqe_dev_alloc(void)
                return ERR_PTR(-ENOMEM);
 
        cd->card_idx = i;
-       cd->class_genwqe = class_genwqe;
+       cd->class_genwqe = &class_genwqe;
        cd->debugfs_genwqe = debugfs_genwqe;
 
        /*
@@ -1340,35 +1360,18 @@ static struct pci_driver genwqe_driver = {
 };
 
 /**
- * genwqe_devnode() - Set default access mode for genwqe devices.
- * @dev:       Pointer to device (unused)
- * @mode:      Carrier to pass-back given mode (permissions)
- *
- * Default mode should be rw for everybody. Do not change default
- * device name.
- */
-static char *genwqe_devnode(const struct device *dev, umode_t *mode)
-{
-       if (mode)
-               *mode = 0666;
-       return NULL;
-}
-
-/**
  * genwqe_init_module() - Driver registration and initialization
  */
 static int __init genwqe_init_module(void)
 {
        int rc;
 
-       class_genwqe = class_create(GENWQE_DEVNAME);
-       if (IS_ERR(class_genwqe)) {
+       rc = class_register(&class_genwqe);
+       if (rc) {
                pr_err("[%s] create class failed\n", __func__);
                return -ENOMEM;
        }
 
-       class_genwqe->devnode = genwqe_devnode;
-
        debugfs_genwqe = debugfs_create_dir(GENWQE_DEVNAME, NULL);
 
        rc = pci_register_driver(&genwqe_driver);
@@ -1381,7 +1384,7 @@ static int __init genwqe_init_module(void)
 
  err_out0:
        debugfs_remove(debugfs_genwqe);
-       class_destroy(class_genwqe);
+       class_unregister(&class_genwqe);
        return rc;
 }
 
@@ -1392,7 +1395,7 @@ static void __exit genwqe_exit_module(void)
 {
        pci_unregister_driver(&genwqe_driver);
        debugfs_remove(debugfs_genwqe);
-       class_destroy(class_genwqe);
+       class_unregister(&class_genwqe);
 }
 
 module_init(genwqe_init_module);
index 0e90297..d700266 100644 (file)
@@ -289,7 +289,7 @@ struct genwqe_dev {
 
        /* char device */
        dev_t  devnum_genwqe;           /* major/minor num card */
-       struct class *class_genwqe;     /* reference to class object */
+       const struct class *class_genwqe;       /* reference to class object */
        struct device *dev;             /* for device creation */
        struct cdev cdev_genwqe;        /* char device for card */
 
index caa3de3..b075d80 100644 (file)
@@ -244,10 +244,8 @@ static int hi6421v600_irq_probe(struct platform_device *pdev)
        pmic_pdev = container_of(pmic_dev, struct platform_device, dev);
 
        priv->irq = platform_get_irq(pmic_pdev, 0);
-       if (priv->irq < 0) {
-               dev_err(dev, "Error %d when getting IRQs\n", priv->irq);
+       if (priv->irq < 0)
                return priv->irq;
-       }
 
        platform_set_drvdata(pdev, priv);
 
index 2fde8d6..f1b74d3 100644 (file)
@@ -25,7 +25,9 @@
 #include <linux/slab.h>
 #include "hpilo.h"
 
-static struct class *ilo_class;
+static const struct class ilo_class = {
+       .name = "iLO",
+};
 static unsigned int ilo_major;
 static unsigned int max_ccb = 16;
 static char ilo_hwdev[MAX_ILO_DEV];
@@ -746,7 +748,7 @@ static void ilo_remove(struct pci_dev *pdev)
 
        minor = MINOR(ilo_hw->cdev.dev);
        for (i = minor; i < minor + max_ccb; i++)
-               device_destroy(ilo_class, MKDEV(ilo_major, i));
+               device_destroy(&ilo_class, MKDEV(ilo_major, i));
 
        cdev_del(&ilo_hw->cdev);
        ilo_disable_interrupts(ilo_hw);
@@ -839,7 +841,7 @@ static int ilo_probe(struct pci_dev *pdev,
 
        for (minor = 0 ; minor < max_ccb; minor++) {
                struct device *dev;
-               dev = device_create(ilo_class, &pdev->dev,
+               dev = device_create(&ilo_class, &pdev->dev,
                                    MKDEV(ilo_major, minor), NULL,
                                    "hpilo!d%dccb%d", devnum, minor);
                if (IS_ERR(dev))
@@ -882,11 +884,9 @@ static int __init ilo_init(void)
        int error;
        dev_t dev;
 
-       ilo_class = class_create("iLO");
-       if (IS_ERR(ilo_class)) {
-               error = PTR_ERR(ilo_class);
+       error = class_register(&ilo_class);
+       if (error)
                goto out;
-       }
 
        error = alloc_chrdev_region(&dev, 0, MAX_OPEN, ILO_NAME);
        if (error)
@@ -902,7 +902,7 @@ static int __init ilo_init(void)
 chr_remove:
        unregister_chrdev_region(dev, MAX_OPEN);
 class_destroy:
-       class_destroy(ilo_class);
+       class_unregister(&ilo_class);
 out:
        return error;
 }
@@ -911,7 +911,7 @@ static void __exit ilo_exit(void)
 {
        pci_unregister_driver(&ilo_driver);
        unregister_chrdev_region(MKDEV(ilo_major, 0), MAX_OPEN);
-       class_destroy(ilo_class);
+       class_unregister(&ilo_class);
 }
 
 MODULE_VERSION("1.5.0");
index 299d316..49868a4 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/miscdevice.h>
 #include <linux/pm_runtime.h>
 #include <linux/atomic.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include "lis3lv02d.h"
 
 #define DRIVER_NAME     "lis3lv02d"
index 4abb47d..64e4575 100644 (file)
@@ -2,6 +2,7 @@ config GP_PCI1XXXX
        tristate "Microchip PCI1XXXX PCIe to GPIO Expander + OTP/EEPROM manager"
        depends on PCI
        depends on GPIOLIB
+       depends on NVMEM_SYSFS
        select GPIOLIB_IRQCHIP
        select AUXILIARY_BUS
        help
index fc4615c..ae31251 100644 (file)
@@ -1 +1 @@
-obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o mchp_pci1xxxx_gpio.o
+obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o mchp_pci1xxxx_gpio.o mchp_pci1xxxx_otpe2p.o
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
new file mode 100644 (file)
index 0000000..16695cb
--- /dev/null
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Microchip Technology Inc.
+// PCI1xxxx OTP/EEPROM driver
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+
+#include "mchp_pci1xxxx_gp.h"
+
+#define AUX_DRIVER_NAME                        "PCI1xxxxOTPE2P"
+#define EEPROM_NAME                    "pci1xxxx_eeprom"
+#define OTP_NAME                       "pci1xxxx_otp"
+
+#define PERI_PF3_SYSTEM_REG_ADDR_BASE  0x2000
+#define PERI_PF3_SYSTEM_REG_LENGTH     0x4000
+
+#define EEPROM_SIZE_BYTES              8192
+#define OTP_SIZE_BYTES                 8192
+
+#define CONFIG_REG_ADDR_BASE           0
+#define EEPROM_REG_ADDR_BASE           0x0E00
+#define OTP_REG_ADDR_BASE              0x1000
+
+#define MMAP_OTP_OFFSET(x)             (OTP_REG_ADDR_BASE + (x))
+#define MMAP_EEPROM_OFFSET(x)          (EEPROM_REG_ADDR_BASE + (x))
+#define MMAP_CFG_OFFSET(x)             (CONFIG_REG_ADDR_BASE + (x))
+
+#define EEPROM_CMD_REG                 0x00
+#define EEPROM_DATA_REG                        0x04
+
+#define EEPROM_CMD_EPC_WRITE           (BIT(29) | BIT(28))
+#define EEPROM_CMD_EPC_TIMEOUT_BIT     BIT(17)
+#define EEPROM_CMD_EPC_BUSY_BIT                BIT(31)
+
+#define STATUS_READ_DELAY_US           1
+#define STATUS_READ_TIMEOUT_US         20000
+
+#define OTP_ADDR_HIGH_OFFSET           0x04
+#define OTP_ADDR_LOW_OFFSET            0x08
+#define OTP_PRGM_DATA_OFFSET           0x10
+#define OTP_PRGM_MODE_OFFSET           0x14
+#define OTP_RD_DATA_OFFSET             0x18
+#define OTP_FUNC_CMD_OFFSET            0x20
+#define OTP_CMD_GO_OFFSET              0x28
+#define OTP_PASS_FAIL_OFFSET           0x2C
+#define OTP_STATUS_OFFSET              0x30
+
+#define OTP_FUNC_RD_BIT                        BIT(0)
+#define OTP_FUNC_PGM_BIT               BIT(1)
+#define OTP_CMD_GO_BIT                 BIT(0)
+#define OTP_STATUS_BUSY_BIT            BIT(0)
+#define OTP_PGM_MODE_BYTE_BIT          BIT(0)
+#define OTP_FAIL_BIT                   BIT(0)
+
+#define OTP_PWR_DN_BIT                 BIT(0)
+#define OTP_PWR_DN_OFFSET              0x00
+
+#define CFG_SYS_LOCK_OFFSET            0xA0
+#define CFG_SYS_LOCK_PF3               BIT(5)
+
+#define BYTE_LOW                       (GENMASK(7, 0))
+#define BYTE_HIGH                      (GENMASK(12, 8))
+
+struct pci1xxxx_otp_eeprom_device {
+       struct auxiliary_device *pdev;
+       void __iomem *reg_base;
+       struct nvmem_config nvmem_config_eeprom;
+       struct nvmem_device *nvmem_eeprom;
+       struct nvmem_config nvmem_config_otp;
+       struct nvmem_device *nvmem_otp;
+};
+
+static int set_sys_lock(struct pci1xxxx_otp_eeprom_device *priv)
+{
+       void __iomem *sys_lock = priv->reg_base +
+                                MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
+       u8 data;
+
+       writel(CFG_SYS_LOCK_PF3, sys_lock);
+       data = readl(sys_lock);
+       if (data != CFG_SYS_LOCK_PF3)
+               return -EPERM;
+
+       return 0;
+}
+
+static void release_sys_lock(struct pci1xxxx_otp_eeprom_device *priv)
+{
+       void __iomem *sys_lock = priv->reg_base +
+                                MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
+       writel(0, sys_lock);
+}
+
+static bool is_eeprom_responsive(struct pci1xxxx_otp_eeprom_device *priv)
+{
+       void __iomem *rb = priv->reg_base;
+       u32 regval;
+       int ret;
+
+       writel(EEPROM_CMD_EPC_TIMEOUT_BIT,
+              rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+       writel(EEPROM_CMD_EPC_BUSY_BIT,
+              rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+
+       /* Wait for the EPC_BUSY bit to get cleared or timeout bit to get set*/
+       ret = read_poll_timeout(readl, regval, !(regval & EEPROM_CMD_EPC_BUSY_BIT),
+                               STATUS_READ_DELAY_US, STATUS_READ_TIMEOUT_US,
+                               true, rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+
+       /* Return failure if either of software or hardware timeouts happen */
+       if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT)))
+               return false;
+
+       return true;
+}
+
+static int pci1xxxx_eeprom_read(void *priv_t, unsigned int off,
+                               void *buf_t, size_t count)
+{
+       struct pci1xxxx_otp_eeprom_device *priv = priv_t;
+       void __iomem *rb = priv->reg_base;
+       char *buf = buf_t;
+       u32 regval;
+       u32 byte;
+       int ret;
+
+       if (off >= priv->nvmem_config_eeprom.size)
+               return -EFAULT;
+
+       if ((off + count) > priv->nvmem_config_eeprom.size)
+               count = priv->nvmem_config_eeprom.size - off;
+
+       ret = set_sys_lock(priv);
+       if (ret)
+               return ret;
+
+       for (byte = 0; byte < count; byte++) {
+               writel(EEPROM_CMD_EPC_BUSY_BIT | (off + byte), rb +
+                      MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+
+               ret = read_poll_timeout(readl, regval,
+                                       !(regval & EEPROM_CMD_EPC_BUSY_BIT),
+                                       STATUS_READ_DELAY_US,
+                                       STATUS_READ_TIMEOUT_US, true,
+                                       rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+               if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {
+                       ret = -EIO;
+                       goto error;
+               }
+
+               buf[byte] = readl(rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));
+       }
+       ret = byte;
+error:
+       release_sys_lock(priv);
+       return ret;
+}
+
+static int pci1xxxx_eeprom_write(void *priv_t, unsigned int off,
+                                void *value_t, size_t count)
+{
+       struct pci1xxxx_otp_eeprom_device *priv = priv_t;
+       void __iomem *rb = priv->reg_base;
+       char *value = value_t;
+       u32 regval;
+       u32 byte;
+       int ret;
+
+       if (off >= priv->nvmem_config_eeprom.size)
+               return -EFAULT;
+
+       if ((off + count) > priv->nvmem_config_eeprom.size)
+               count = priv->nvmem_config_eeprom.size - off;
+
+       ret = set_sys_lock(priv);
+       if (ret)
+               return ret;
+
+       for (byte = 0; byte < count; byte++) {
+               writel(*(value + byte), rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));
+               regval = EEPROM_CMD_EPC_TIMEOUT_BIT | EEPROM_CMD_EPC_WRITE |
+                        (off + byte);
+               writel(regval, rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+               writel(EEPROM_CMD_EPC_BUSY_BIT | regval,
+                      rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+
+               ret = read_poll_timeout(readl, regval,
+                                       !(regval & EEPROM_CMD_EPC_BUSY_BIT),
+                                       STATUS_READ_DELAY_US,
+                                       STATUS_READ_TIMEOUT_US, true,
+                                       rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
+               if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {
+                       ret = -EIO;
+                       goto error;
+               }
+       }
+       ret = byte;
+error:
+       release_sys_lock(priv);
+       return ret;
+}
+
+static void otp_device_set_address(struct pci1xxxx_otp_eeprom_device *priv,
+                                  u16 address)
+{
+       u16 lo, hi;
+
+       lo = address & BYTE_LOW;
+       hi = (address & BYTE_HIGH) >> 8;
+       writew(lo, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_LOW_OFFSET));
+       writew(hi, priv->reg_base + MMAP_OTP_OFFSET(OTP_ADDR_HIGH_OFFSET));
+}
+
+static int pci1xxxx_otp_read(void *priv_t, unsigned int off,
+                            void *buf_t, size_t count)
+{
+       struct pci1xxxx_otp_eeprom_device *priv = priv_t;
+       void __iomem *rb = priv->reg_base;
+       char *buf = buf_t;
+       u32 regval;
+       u32 byte;
+       int ret;
+       u8 data;
+
+       if (off >= priv->nvmem_config_otp.size)
+               return -EFAULT;
+
+       if ((off + count) > priv->nvmem_config_otp.size)
+               count = priv->nvmem_config_otp.size - off;
+
+       ret = set_sys_lock(priv);
+       if (ret)
+               return ret;
+
+       for (byte = 0; byte < count; byte++) {
+               otp_device_set_address(priv, (u16)(off + byte));
+               data = readl(rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+               writel(data | OTP_FUNC_RD_BIT,
+                      rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+               data = readl(rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+               writel(data | OTP_CMD_GO_BIT,
+                      rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+
+               ret = read_poll_timeout(readl, regval,
+                                       !(regval & OTP_STATUS_BUSY_BIT),
+                                       STATUS_READ_DELAY_US,
+                                       STATUS_READ_TIMEOUT_US, true,
+                                       rb + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));
+
+               data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));
+               if (ret < 0 || data & OTP_FAIL_BIT) {
+                       ret = -EIO;
+                       goto error;
+               }
+
+               buf[byte] = readl(rb + MMAP_OTP_OFFSET(OTP_RD_DATA_OFFSET));
+       }
+       ret = byte;
+error:
+       release_sys_lock(priv);
+       return ret;
+}
+
+static int pci1xxxx_otp_write(void *priv_t, unsigned int off,
+                             void *value_t, size_t count)
+{
+       struct pci1xxxx_otp_eeprom_device *priv = priv_t;
+       void __iomem *rb = priv->reg_base;
+       char *value = value_t;
+       u32 regval;
+       u32 byte;
+       int ret;
+       u8 data;
+
+       if (off >= priv->nvmem_config_otp.size)
+               return -EFAULT;
+
+       if ((off + count) > priv->nvmem_config_otp.size)
+               count = priv->nvmem_config_otp.size - off;
+
+       ret = set_sys_lock(priv);
+       if (ret)
+               return ret;
+
+       for (byte = 0; byte < count; byte++) {
+               otp_device_set_address(priv, (u16)(off + byte));
+
+               /*
+                * Set OTP_PGM_MODE_BYTE command bit in OTP_PRGM_MODE register
+                * to enable Byte programming
+                */
+               data = readl(rb + MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));
+               writel(data | OTP_PGM_MODE_BYTE_BIT,
+                      rb + MMAP_OTP_OFFSET(OTP_PRGM_MODE_OFFSET));
+               writel(*(value + byte), rb + MMAP_OTP_OFFSET(OTP_PRGM_DATA_OFFSET));
+               data = readl(rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+               writel(data | OTP_FUNC_PGM_BIT,
+                      rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
+               data = readl(rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+               writel(data | OTP_CMD_GO_BIT,
+                      rb + MMAP_OTP_OFFSET(OTP_CMD_GO_OFFSET));
+
+               ret = read_poll_timeout(readl, regval,
+                                       !(regval & OTP_STATUS_BUSY_BIT),
+                                       STATUS_READ_DELAY_US,
+                                       STATUS_READ_TIMEOUT_US, true,
+                                       rb + MMAP_OTP_OFFSET(OTP_STATUS_OFFSET));
+
+               data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));
+               if (ret < 0 || data & OTP_FAIL_BIT) {
+                       ret = -EIO;
+                       goto error;
+               }
+       }
+       ret = byte;
+error:
+       release_sys_lock(priv);
+       return ret;
+}
+
+static int pci1xxxx_otp_eeprom_probe(struct auxiliary_device *aux_dev,
+                                    const struct auxiliary_device_id *id)
+{
+       struct auxiliary_device_wrapper *aux_dev_wrapper;
+       struct pci1xxxx_otp_eeprom_device *priv;
+       struct gp_aux_data_type *pdata;
+       int ret;
+       u8 data;
+
+       aux_dev_wrapper = container_of(aux_dev, struct auxiliary_device_wrapper,
+                                      aux_dev);
+       pdata = &aux_dev_wrapper->gp_aux_data;
+       if (!pdata)
+               return -EINVAL;
+
+       priv = devm_kzalloc(&aux_dev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->pdev = aux_dev;
+
+       if (!devm_request_mem_region(&aux_dev->dev, pdata->region_start +
+                                    PERI_PF3_SYSTEM_REG_ADDR_BASE,
+                                    PERI_PF3_SYSTEM_REG_LENGTH,
+                                    aux_dev->name))
+               return -ENOMEM;
+
+       priv->reg_base = devm_ioremap(&aux_dev->dev, pdata->region_start +
+                                     PERI_PF3_SYSTEM_REG_ADDR_BASE,
+                                     PERI_PF3_SYSTEM_REG_LENGTH);
+       if (!priv->reg_base)
+               return -ENOMEM;
+
+       ret = set_sys_lock(priv);
+       if (ret)
+               return ret;
+
+       /* Set OTP_PWR_DN to 0 to make OTP Operational */
+       data = readl(priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
+       writel(data & ~OTP_PWR_DN_BIT,
+              priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
+
+       dev_set_drvdata(&aux_dev->dev, priv);
+
+       if (is_eeprom_responsive(priv)) {
+               priv->nvmem_config_eeprom.type = NVMEM_TYPE_EEPROM;
+               priv->nvmem_config_eeprom.name = EEPROM_NAME;
+               priv->nvmem_config_eeprom.dev = &aux_dev->dev;
+               priv->nvmem_config_eeprom.owner = THIS_MODULE;
+               priv->nvmem_config_eeprom.reg_read = pci1xxxx_eeprom_read;
+               priv->nvmem_config_eeprom.reg_write = pci1xxxx_eeprom_write;
+               priv->nvmem_config_eeprom.priv = priv;
+               priv->nvmem_config_eeprom.stride = 1;
+               priv->nvmem_config_eeprom.word_size = 1;
+               priv->nvmem_config_eeprom.size = EEPROM_SIZE_BYTES;
+
+               priv->nvmem_eeprom = devm_nvmem_register(&aux_dev->dev,
+                                                        &priv->nvmem_config_eeprom);
+               if (IS_ERR(priv->nvmem_eeprom))
+                       return PTR_ERR(priv->nvmem_eeprom);
+       }
+
+       release_sys_lock(priv);
+
+       priv->nvmem_config_otp.type = NVMEM_TYPE_OTP;
+       priv->nvmem_config_otp.name = OTP_NAME;
+       priv->nvmem_config_otp.dev = &aux_dev->dev;
+       priv->nvmem_config_otp.owner = THIS_MODULE;
+       priv->nvmem_config_otp.reg_read = pci1xxxx_otp_read;
+       priv->nvmem_config_otp.reg_write = pci1xxxx_otp_write;
+       priv->nvmem_config_otp.priv = priv;
+       priv->nvmem_config_otp.stride = 1;
+       priv->nvmem_config_otp.word_size = 1;
+       priv->nvmem_config_otp.size = OTP_SIZE_BYTES;
+
+       priv->nvmem_otp = devm_nvmem_register(&aux_dev->dev,
+                                             &priv->nvmem_config_otp);
+       if (IS_ERR(priv->nvmem_otp))
+               return PTR_ERR(priv->nvmem_otp);
+
+       return ret;
+}
+
+static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev)
+{
+       struct pci1xxxx_otp_eeprom_device *priv;
+       void __iomem *sys_lock;
+
+       priv = dev_get_drvdata(&aux_dev->dev);
+       sys_lock = priv->reg_base + MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
+       writel(CFG_SYS_LOCK_PF3, sys_lock);
+
+       /* Shut down OTP */
+       writel(OTP_PWR_DN_BIT,
+              priv->reg_base + MMAP_OTP_OFFSET(OTP_PWR_DN_OFFSET));
+
+       writel(0, sys_lock);
+}
+
+static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = {
+       {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"},
+       {},
+};
+MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table);
+
+static struct auxiliary_driver pci1xxxx_otp_eeprom_driver = {
+       .driver = {
+               .name = AUX_DRIVER_NAME,
+       },
+       .probe = pci1xxxx_otp_eeprom_probe,
+       .remove = pci1xxxx_otp_eeprom_remove,
+       .id_table = pci1xxxx_otp_eeprom_auxiliary_id_table
+};
+module_auxiliary_driver(pci1xxxx_otp_eeprom_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
+MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>");
+MODULE_AUTHOR("Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com>");
+MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx OTP EEPROM Programmer");
index b8b716f..2733070 100644 (file)
@@ -184,6 +184,7 @@ static int mei_fwver(struct mei_cl_device *cldev)
                cldev->bus->fw_ver[i].hotfix = fwver->ver[i].hotfix;
                cldev->bus->fw_ver[i].buildno = fwver->ver[i].buildno;
        }
+       cldev->bus->fw_ver_received = 1;
 
        return ret;
 }
@@ -237,8 +238,11 @@ static void mei_gsc_mkhi_ver(struct mei_cl_device *cldev)
 {
        int ret;
 
-       /* No need to enable the client if nothing is needed from it */
-       if (!cldev->bus->fw_f_fw_ver_supported)
+       /*
+        * No need to enable the client if nothing is needed from it.
+        * No need to fill in version if it is already filled in by the fix address client.
+        */
+       if (!cldev->bus->fw_f_fw_ver_supported || cldev->bus->fw_ver_received)
                return;
 
        ret = mei_cldev_enable(cldev);
@@ -555,8 +559,8 @@ static struct mei_fixup {
        MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc),
        MEI_FIXUP(MEI_UUID_WD, mei_wd),
        MEI_FIXUP(MEI_UUID_MKHIF_FIX, mei_mkhi_fix),
-       MEI_FIXUP(MEI_UUID_IGSC_MKHI, mei_gsc_mkhi_ver),
        MEI_FIXUP(MEI_UUID_IGSC_MKHI_FIX, mei_gsc_mkhi_fix_ver),
+       MEI_FIXUP(MEI_UUID_IGSC_MKHI, mei_gsc_mkhi_ver),
        MEI_FIXUP(MEI_UUID_HDCP, whitelist),
        MEI_FIXUP(MEI_UUID_ANY, vt_support),
        MEI_FIXUP(MEI_UUID_PAVP, pxp_is_ready),
index 33ec642..2e65ce6 100644 (file)
@@ -1329,6 +1329,7 @@ static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus,
        mei_cl_bus_set_name(cldev);
        cldev->is_added   = 0;
        INIT_LIST_HEAD(&cldev->bus_list);
+       device_enable_async_suspend(&cldev->dev);
 
        return cldev;
 }
index e63cabd..6be8f1c 100644 (file)
@@ -312,4 +312,5 @@ module_auxiliary_driver(mei_gsc_driver);
 MODULE_AUTHOR("Intel Corporation");
 MODULE_ALIAS("auxiliary:i915.mei-gsc");
 MODULE_ALIAS("auxiliary:i915.mei-gscfi");
+MODULE_DESCRIPTION("Intel(R) Graphics System Controller");
 MODULE_LICENSE("GPL");
index bac8852..c35e005 100644 (file)
@@ -142,6 +142,9 @@ int mei_reset(struct mei_device *dev)
 
        mei_hbm_reset(dev);
 
+       /* clean stale FW version */
+       dev->fw_ver_received = 0;
+
        memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
 
        if (ret) {
@@ -157,7 +160,10 @@ int mei_reset(struct mei_device *dev)
 
        ret = mei_hw_start(dev);
        if (ret) {
-               dev_err(dev->dev, "hw_start failed ret = %d\n", ret);
+               char fw_sts_str[MEI_FW_STATUS_STR_SZ];
+
+               mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ);
+               dev_err(dev->dev, "hw_start failed ret = %d fw status = %s\n", ret, fw_sts_str);
                return ret;
        }
 
index 895011b..cdf8a2e 100644 (file)
@@ -512,6 +512,7 @@ struct mei_dev_timeouts {
  * @fw_ver : FW versions
  *
  * @fw_f_fw_ver_supported : fw feature: fw version supported
+ * @fw_ver_received : fw version received
  *
  * @me_clients_rwsem: rw lock over me_clients list
  * @me_clients  : list of FW clients
@@ -604,6 +605,7 @@ struct mei_device {
        struct mei_fw_version fw_ver[MEI_MAX_FW_VER_BLOCKS];
 
        unsigned int fw_f_fw_ver_supported:1;
+       unsigned int fw_ver_received:1;
 
        struct rw_semaphore me_clients_rwsem;
        struct list_head me_clients;
index 3bf560b..2dcb916 100644 (file)
@@ -40,8 +40,7 @@ mei_pxp_send_message(struct device *dev, const void *message, size_t size)
 
        cldev = to_mei_cl_device(dev);
 
-       /* temporary drop const qualifier till the API is fixed */
-       byte = mei_cldev_send(cldev, (u8 *)message, size);
+       byte = mei_cldev_send(cldev, message, size);
        if (byte < 0) {
                dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
                return byte;
index 54d4f6e..3c57f74 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/regmap.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 
 struct qcom_coincell {
index 6120973..e248c0a 100644 (file)
@@ -10,8 +10,8 @@
 #include <linux/genalloc.h>
 #include <linux/io.h>
 #include <linux/list_sort.h>
+#include <linux/of.h>
 #include <linux/of_address.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
index 05e2c15..b4d67a1 100644 (file)
@@ -56,8 +56,7 @@ static int tps6594_esm_probe(struct platform_device *pdev)
        for (i = 0; i < pdev->num_resources; i++) {
                irq = platform_get_irq_byname(pdev, pdev->resource[i].name);
                if (irq < 0)
-                       return dev_err_probe(dev, irq, "Failed to get %s irq\n",
-                                            pdev->resource[i].name);
+                       return irq;
 
                ret = devm_request_threaded_irq(dev, irq, NULL,
                                                tps6594_esm_isr, IRQF_ONESHOT,
@@ -82,7 +81,7 @@ static int tps6594_esm_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int tps6594_esm_remove(struct platform_device *pdev)
+static void tps6594_esm_remove(struct platform_device *pdev)
 {
        struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
        struct device *dev = &pdev->dev;
@@ -103,8 +102,6 @@ static int tps6594_esm_remove(struct platform_device *pdev)
 out:
        pm_runtime_put_sync(dev);
        pm_runtime_disable(dev);
-
-       return ret;
 }
 
 static int tps6594_esm_suspend(struct device *dev)
@@ -138,7 +135,7 @@ static struct platform_driver tps6594_esm_driver = {
                .pm = pm_sleep_ptr(&tps6594_esm_pm_ops),
        },
        .probe = tps6594_esm_probe,
-       .remove = tps6594_esm_remove,
+       .remove_new = tps6594_esm_remove,
 };
 
 module_platform_driver(tps6594_esm_driver);
index 5223d15..88dcac8 100644 (file)
@@ -266,8 +266,7 @@ static int tps6594_pfsm_probe(struct platform_device *pdev)
        for (i = 0 ; i < pdev->num_resources ; i++) {
                irq = platform_get_irq_byname(pdev, pdev->resource[i].name);
                if (irq < 0)
-                       return dev_err_probe(dev, irq, "Failed to get %s irq\n",
-                                            pdev->resource[i].name);
+                       return irq;
 
                ret = devm_request_threaded_irq(dev, irq, NULL,
                                                tps6594_pfsm_isr, IRQF_ONESHOT,
@@ -281,13 +280,11 @@ static int tps6594_pfsm_probe(struct platform_device *pdev)
        return misc_register(&pfsm->miscdev);
 }
 
-static int tps6594_pfsm_remove(struct platform_device *pdev)
+static void tps6594_pfsm_remove(struct platform_device *pdev)
 {
        struct tps6594_pfsm *pfsm = platform_get_drvdata(pdev);
 
        misc_deregister(&pfsm->miscdev);
-
-       return 0;
 }
 
 static struct platform_driver tps6594_pfsm_driver = {
@@ -295,7 +292,7 @@ static struct platform_driver tps6594_pfsm_driver = {
                .name = "tps6594-pfsm",
        },
        .probe = tps6594_pfsm_probe,
-       .remove = tps6594_pfsm_remove,
+       .remove_new = tps6594_pfsm_remove,
 };
 
 module_platform_driver(tps6594_pfsm_driver);
index 53b5506..6479c96 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/module.h>
 #include <linux/nmi.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/param.h>
 #include <linux/percpu.h>
 #include <linux/platform_device.h>
index 270ff4c..94a0ee1 100644 (file)
@@ -15,7 +15,8 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/poll.h>
 #include <linux/slab.h>
 #include <linux/clk.h>
@@ -1347,7 +1348,6 @@ static int xsdfec_probe(struct platform_device *pdev)
 {
        struct xsdfec_dev *xsdfec;
        struct device *dev;
-       struct resource *res;
        int err;
        bool irq_enabled = true;
 
@@ -1363,8 +1363,7 @@ static int xsdfec_probe(struct platform_device *pdev)
                return err;
 
        dev = xsdfec->dev;
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       xsdfec->regs = devm_ioremap_resource(dev, res);
+       xsdfec->regs = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(xsdfec->regs)) {
                err = PTR_ERR(xsdfec->regs);
                goto err_xsdfec_dev;
index d96f6d7..9fc5835 100644 (file)
@@ -11,7 +11,8 @@
 
 #include <asm/xilinx_mb_manager.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/fault-inject.h>
 
 /* TMR Inject Register offsets */
index 0ef55e0..03912a9 100644 (file)
@@ -15,7 +15,8 @@
 
 #include <asm/xilinx_mb_manager.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 
 /* TMR Manager Register offsets */
 #define XTMR_MANAGER_CR_OFFSET         0x0
@@ -170,8 +171,7 @@ static int xtmr_manager_probe(struct platform_device *pdev)
        if (!xtmr_manager)
                return -ENOMEM;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       xtmr_manager->regs =  devm_ioremap_resource(&pdev->dev, res);
+       xtmr_manager->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(xtmr_manager->regs))
                return PTR_ERR(xtmr_manager->regs);
 
index e72419d..dddb235 100644 (file)
@@ -19,6 +19,7 @@ if PCCARD
 
 config PCMCIA
        tristate "16-bit PCMCIA support"
+       depends on HAS_IOMEM
        select CRC32
        default y
        help
index 2fc5e2a..4f9c245 100644 (file)
@@ -16,3 +16,19 @@ config PECI_ASPEED
 
          This driver can also be built as a module. If so, the module will
          be called peci-aspeed.
+
+config PECI_NPCM
+       tristate "Nuvoton NPCM PECI support"
+       depends on ARCH_NPCM || COMPILE_TEST
+       depends on OF
+       select REGMAP_MMIO
+       help
+         This option enables PECI controller driver for Nuvoton NPCM7XX
+         and NPCM8XX SoCs. It allows BMC to discover devices connected
+         to it and communicate with them using PECI protocol.
+
+         Say Y here if you want support for the Platform Environment Control
+         Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
+
+         This support is also available as a module. If so, the module
+         will be called peci-npcm.
index 022c28e..e247449 100644 (file)
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
 obj-$(CONFIG_PECI_ASPEED)      += peci-aspeed.o
+obj-$(CONFIG_PECI_NPCM)                += peci-npcm.o
index 731c5d8..7fdc25a 100644 (file)
@@ -468,7 +468,7 @@ static void aspeed_peci_property_setup(struct aspeed_peci *priv)
                                      ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT, &priv->cmd_timeout_ms);
 }
 
-static struct peci_controller_ops aspeed_ops = {
+static const struct peci_controller_ops aspeed_ops = {
        .xfer = aspeed_peci_xfer,
 };
 
diff --git a/drivers/peci/controller/peci-npcm.c b/drivers/peci/controller/peci-npcm.c
new file mode 100644 (file)
index 0000000..ec613d3
--- /dev/null
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/* NPCM GCR module */
+#define NPCM_INTCR3_OFFSET     0x9C
+#define NPCM_INTCR3_PECIVSEL   BIT(19)
+
+/* NPCM PECI Registers */
+#define NPCM_PECI_CTL_STS      0x00
+#define NPCM_PECI_RD_LENGTH    0x04
+#define NPCM_PECI_ADDR         0x08
+#define NPCM_PECI_CMD          0x0C
+#define NPCM_PECI_CTL2         0x10
+#define NPCM_PECI_WR_LENGTH    0x1C
+#define NPCM_PECI_PDDR         0x2C
+#define NPCM_PECI_DAT_INOUT(n) (0x100 + ((n) * 4))
+
+#define NPCM_PECI_MAX_REG      0x200
+
+/* NPCM_PECI_CTL_STS - 0x00 : Control Register */
+#define NPCM_PECI_CTRL_DONE_INT_EN     BIT(6)
+#define NPCM_PECI_CTRL_ABRT_ERR                BIT(4)
+#define NPCM_PECI_CTRL_CRC_ERR         BIT(3)
+#define NPCM_PECI_CTRL_DONE            BIT(1)
+#define NPCM_PECI_CTRL_START_BUSY      BIT(0)
+
+/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */
+#define NPCM_PECI_RD_LEN_MASK          GENMASK(6, 0)
+
+/* NPCM_PECI_CMD - 0x10 : Command Register */
+#define NPCM_PECI_CTL2_MASK            GENMASK(7, 6)
+
+/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */
+#define NPCM_PECI_WR_LEN_MASK          GENMASK(6, 0)
+
+/* NPCM_PECI_PDDR - 0x2C : Command Register */
+#define NPCM_PECI_PDDR_MASK            GENMASK(4, 0)
+
+#define NPCM_PECI_INT_MASK             (NPCM_PECI_CTRL_ABRT_ERR | \
+                                        NPCM_PECI_CTRL_CRC_ERR  | \
+                                        NPCM_PECI_CTRL_DONE)
+
+#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC      (50 * USEC_PER_MSEC)
+#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC     (10 * USEC_PER_MSEC)
+#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT       1000
+#define NPCM_PECI_CMD_TIMEOUT_MS_MAX           60000
+#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT    15
+#define NPCM_PECI_PULL_DOWN_DEFAULT            0
+
+struct npcm_peci {
+       u32                     cmd_timeout_ms;
+       struct completion       xfer_complete;
+       struct regmap           *regmap;
+       u32                     status;
+       spinlock_t              lock; /* to sync completion status handling */
+       struct peci_controller *controller;
+       struct device           *dev;
+       struct clk              *clk;
+       int                     irq;
+};
+
+static int npcm_peci_xfer(struct peci_controller *controller, u8 addr, struct peci_request *req)
+{
+       struct npcm_peci *priv = dev_get_drvdata(controller->dev.parent);
+       unsigned long timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+       unsigned int msg_rd;
+       u32 cmd_sts;
+       int i, ret;
+
+       /* Check command sts and bus idle state */
+       ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+                                      !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+                                      NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+                                      NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+       if (ret)
+               return ret; /* -ETIMEDOUT */
+
+       spin_lock_irq(&priv->lock);
+       reinit_completion(&priv->xfer_complete);
+
+       regmap_write(priv->regmap, NPCM_PECI_ADDR, addr);
+       regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH, NPCM_PECI_WR_LEN_MASK & req->rx.len);
+       regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH, NPCM_PECI_WR_LEN_MASK & req->tx.len);
+
+       if (req->tx.len) {
+               regmap_write(priv->regmap, NPCM_PECI_CMD, req->tx.buf[0]);
+
+               for (i = 0; i < (req->tx.len - 1); i++)
+                       regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i), req->tx.buf[i + 1]);
+       }
+
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
+       dev_dbg(priv->dev, "addr : %#02x, tx.len : %#02x, rx.len : %#02x\n",
+               addr, req->tx.len, req->rx.len);
+       print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req->tx.len);
+#endif
+
+       priv->status = 0;
+       regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS, NPCM_PECI_CTRL_START_BUSY,
+                          NPCM_PECI_CTRL_START_BUSY);
+
+       spin_unlock_irq(&priv->lock);
+
+       ret = wait_for_completion_interruptible_timeout(&priv->xfer_complete, timeout);
+       if (ret < 0)
+               return ret;
+
+       if (ret == 0) {
+               dev_dbg(priv->dev, "timeout waiting for a response\n");
+               return -ETIMEDOUT;
+       }
+
+       spin_lock_irq(&priv->lock);
+
+       if (priv->status != NPCM_PECI_CTRL_DONE) {
+               spin_unlock_irq(&priv->lock);
+               dev_dbg(priv->dev, "no valid response, status: %#02x\n", priv->status);
+               return -EIO;
+       }
+
+       regmap_write(priv->regmap, NPCM_PECI_CMD, 0);
+
+       for (i = 0; i < req->rx.len; i++) {
+               regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd);
+               req->rx.buf[i] = (u8)msg_rd;
+       }
+
+       spin_unlock_irq(&priv->lock);
+
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
+       print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req->rx.len);
+#endif
+       return 0;
+}
+
+static irqreturn_t npcm_peci_irq_handler(int irq, void *arg)
+{
+       struct npcm_peci *priv = arg;
+       u32 status_ack = 0;
+       u32 status;
+
+       spin_lock(&priv->lock);
+       regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status);
+       priv->status |= (status & NPCM_PECI_INT_MASK);
+
+       if (status & NPCM_PECI_CTRL_CRC_ERR)
+               status_ack |= NPCM_PECI_CTRL_CRC_ERR;
+
+       if (status & NPCM_PECI_CTRL_ABRT_ERR)
+               status_ack |= NPCM_PECI_CTRL_ABRT_ERR;
+
+       /*
+        * All commands should be ended up with a NPCM_PECI_CTRL_DONE
+        * bit set even in an error case.
+        */
+       if (status & NPCM_PECI_CTRL_DONE) {
+               status_ack |= NPCM_PECI_CTRL_DONE;
+               complete(&priv->xfer_complete);
+       }
+
+       regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS, NPCM_PECI_INT_MASK, status_ack);
+
+       spin_unlock(&priv->lock);
+       return IRQ_HANDLED;
+}
+
+static int npcm_peci_init_ctrl(struct npcm_peci *priv)
+{
+       u32 cmd_sts;
+       int ret;
+
+       priv->clk = devm_clk_get_enabled(priv->dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               dev_err(priv->dev, "failed to get ref clock\n");
+               return PTR_ERR(priv->clk);
+       }
+
+       ret = device_property_read_u32(priv->dev, "cmd-timeout-ms", &priv->cmd_timeout_ms);
+       if (ret) {
+               priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
+       } else if (priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX ||
+                  priv->cmd_timeout_ms == 0) {
+               dev_warn(priv->dev, "invalid cmd-timeout-ms: %u, falling back to: %u\n",
+                        priv->cmd_timeout_ms, NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+
+               priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
+       }
+
+       regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK,
+                          NPCM_PECI_PULL_DOWN_DEFAULT << 6);
+
+       regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK,
+                          NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+
+       ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+                                      !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+                                      NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+                                      NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+       if (ret)
+               return ret; /* -ETIMEDOUT */
+
+       /* PECI interrupt enable */
+       regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS, NPCM_PECI_CTRL_DONE_INT_EN,
+                          NPCM_PECI_CTRL_DONE_INT_EN);
+
+       return 0;
+}
+
+static const struct regmap_config npcm_peci_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = NPCM_PECI_MAX_REG,
+       .fast_io = true,
+};
+
+static struct peci_controller_ops npcm_ops = {
+       .xfer = npcm_peci_xfer,
+};
+
+static int npcm_peci_probe(struct platform_device *pdev)
+{
+       struct peci_controller *controller;
+       struct npcm_peci *priv;
+       void __iomem *base;
+       int ret;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->dev = &pdev->dev;
+       dev_set_drvdata(&pdev->dev, priv);
+
+       base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, &npcm_peci_regmap_config);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
+
+       priv->irq = platform_get_irq(pdev, 0);
+       if (priv->irq < 0)
+               return priv->irq;
+
+       ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
+                              0, "peci-npcm-irq", priv);
+       if (ret)
+               return ret;
+
+       init_completion(&priv->xfer_complete);
+       spin_lock_init(&priv->lock);
+
+       ret = npcm_peci_init_ctrl(priv);
+       if (ret)
+               return ret;
+
+       controller = devm_peci_controller_add(priv->dev, &npcm_ops);
+       if (IS_ERR(controller))
+               return dev_err_probe(priv->dev, PTR_ERR(controller),
+                                    "failed to add npcm peci controller\n");
+
+       priv->controller = controller;
+
+       return 0;
+}
+
+static const struct of_device_id npcm_peci_of_table[] = {
+       { .compatible = "nuvoton,npcm750-peci", },
+       { .compatible = "nuvoton,npcm845-peci", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, npcm_peci_of_table);
+
+static struct platform_driver npcm_peci_driver = {
+       .probe  = npcm_peci_probe,
+       .driver = {
+               .name           = KBUILD_MODNAME,
+               .of_match_table = npcm_peci_of_table,
+       },
+};
+module_platform_driver(npcm_peci_driver);
+
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM PECI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PECI);
index 9c8cf07..0f83a9c 100644 (file)
@@ -44,7 +44,7 @@ int peci_controller_scan_devices(struct peci_controller *controller)
 }
 
 static struct peci_controller *peci_controller_alloc(struct device *dev,
-                                                    struct peci_controller_ops *ops)
+                                                    const struct peci_controller_ops *ops)
 {
        struct peci_controller *controller;
        int ret;
@@ -113,7 +113,7 @@ static void unregister_controller(void *_controller)
  * Return: Pointer to the newly allocated controller or ERR_PTR() in case of failure.
  */
 struct peci_controller *devm_peci_controller_add(struct device *dev,
-                                                struct peci_controller_ops *ops)
+                                                const struct peci_controller_ops *ops)
 {
        struct peci_controller *controller;
        int ret;
index de4a7b3..bd990ac 100644 (file)
@@ -323,6 +323,11 @@ static const struct peci_device_id peci_cpu_device_ids[] = {
                .model  = INTEL_FAM6_ICELAKE_D,
                .data   = "icxd",
        },
+       { /* Sapphire Rapids Xeon */
+               .family = 6,
+               .model  = INTEL_FAM6_SAPPHIRERAPIDS_X,
+               .data   = "spr",
+       },
        { }
 };
 MODULE_DEVICE_TABLE(peci, peci_cpu_device_ids);
index 83966db..122c38e 100644 (file)
@@ -175,8 +175,12 @@ static int pruss_probe(struct platform_device *pdev)
                goto err_free_ddr_vaddr;
        }
 
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0)
+               goto err_free_ddr_vaddr;
+
+       gdev->hostirq_start = ret;
        gdev->pintc_base = pdata->pintc_base;
-       gdev->hostirq_start = platform_get_irq(pdev, 0);
 
        for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) {
                p->mem[0].addr = regs_prussio->start;
index 09fd169..5d98f7d 100644 (file)
@@ -6,58 +6,60 @@
 #ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
 #define _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
 
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
 /* ADC channels for PM8350_ADC for PMIC7 */
-#define PM8350_ADC7_REF_GND(sid)                       ((sid) << 8 | 0x0)
-#define PM8350_ADC7_1P25VREF(sid)                      ((sid) << 8 | 0x01)
-#define PM8350_ADC7_VREF_VADC(sid)                     ((sid) << 8 | 0x02)
-#define PM8350_ADC7_DIE_TEMP(sid)                      ((sid) << 8 | 0x03)
-
-#define PM8350_ADC7_AMUX_THM1(sid)                     ((sid) << 8 | 0x04)
-#define PM8350_ADC7_AMUX_THM2(sid)                     ((sid) << 8 | 0x05)
-#define PM8350_ADC7_AMUX_THM3(sid)                     ((sid) << 8 | 0x06)
-#define PM8350_ADC7_AMUX_THM4(sid)                     ((sid) << 8 | 0x07)
-#define PM8350_ADC7_AMUX_THM5(sid)                     ((sid) << 8 | 0x08)
-#define PM8350_ADC7_GPIO1(sid)                         ((sid) << 8 | 0x0a)
-#define PM8350_ADC7_GPIO2(sid)                         ((sid) << 8 | 0x0b)
-#define PM8350_ADC7_GPIO3(sid)                         ((sid) << 8 | 0x0c)
-#define PM8350_ADC7_GPIO4(sid)                         ((sid) << 8 | 0x0d)
+#define PM8350_ADC7_REF_GND(sid)                       ((sid) << 8 | ADC7_REF_GND)
+#define PM8350_ADC7_1P25VREF(sid)                      ((sid) << 8 | ADC7_1P25VREF)
+#define PM8350_ADC7_VREF_VADC(sid)                     ((sid) << 8 | ADC7_VREF_VADC)
+#define PM8350_ADC7_DIE_TEMP(sid)                      ((sid) << 8 | ADC7_DIE_TEMP)
+
+#define PM8350_ADC7_AMUX_THM1(sid)                     ((sid) << 8 | ADC7_AMUX_THM1)
+#define PM8350_ADC7_AMUX_THM2(sid)                     ((sid) << 8 | ADC7_AMUX_THM2)
+#define PM8350_ADC7_AMUX_THM3(sid)                     ((sid) << 8 | ADC7_AMUX_THM3)
+#define PM8350_ADC7_AMUX_THM4(sid)                     ((sid) << 8 | ADC7_AMUX_THM4)
+#define PM8350_ADC7_AMUX_THM5(sid)                     ((sid) << 8 | ADC7_AMUX_THM5)
+#define PM8350_ADC7_GPIO1(sid)                         ((sid) << 8 | ADC7_GPIO1)
+#define PM8350_ADC7_GPIO2(sid)                         ((sid) << 8 | ADC7_GPIO2)
+#define PM8350_ADC7_GPIO3(sid)                         ((sid) << 8 | ADC7_GPIO3)
+#define PM8350_ADC7_GPIO4(sid)                         ((sid) << 8 | ADC7_GPIO4)
 
 /* 30k pull-up1 */
-#define PM8350_ADC7_AMUX_THM1_30K_PU(sid)              ((sid) << 8 | 0x24)
-#define PM8350_ADC7_AMUX_THM2_30K_PU(sid)              ((sid) << 8 | 0x25)
-#define PM8350_ADC7_AMUX_THM3_30K_PU(sid)              ((sid) << 8 | 0x26)
-#define PM8350_ADC7_AMUX_THM4_30K_PU(sid)              ((sid) << 8 | 0x27)
-#define PM8350_ADC7_AMUX_THM5_30K_PU(sid)              ((sid) << 8 | 0x28)
-#define PM8350_ADC7_GPIO1_30K_PU(sid)                  ((sid) << 8 | 0x2a)
-#define PM8350_ADC7_GPIO2_30K_PU(sid)                  ((sid) << 8 | 0x2b)
-#define PM8350_ADC7_GPIO3_30K_PU(sid)                  ((sid) << 8 | 0x2c)
-#define PM8350_ADC7_GPIO4_30K_PU(sid)                  ((sid) << 8 | 0x2d)
+#define PM8350_ADC7_AMUX_THM1_30K_PU(sid)              ((sid) << 8 | ADC7_AMUX_THM1_30K_PU)
+#define PM8350_ADC7_AMUX_THM2_30K_PU(sid)              ((sid) << 8 | ADC7_AMUX_THM2_30K_PU)
+#define PM8350_ADC7_AMUX_THM3_30K_PU(sid)              ((sid) << 8 | ADC7_AMUX_THM3_30K_PU)
+#define PM8350_ADC7_AMUX_THM4_30K_PU(sid)              ((sid) << 8 | ADC7_AMUX_THM4_30K_PU)
+#define PM8350_ADC7_AMUX_THM5_30K_PU(sid)              ((sid) << 8 | ADC7_AMUX_THM5_30K_PU)
+#define PM8350_ADC7_GPIO1_30K_PU(sid)                  ((sid) << 8 | ADC7_GPIO1_30K_PU)
+#define PM8350_ADC7_GPIO2_30K_PU(sid)                  ((sid) << 8 | ADC7_GPIO2_30K_PU)
+#define PM8350_ADC7_GPIO3_30K_PU(sid)                  ((sid) << 8 | ADC7_GPIO3_30K_PU)
+#define PM8350_ADC7_GPIO4_30K_PU(sid)                  ((sid) << 8 | ADC7_GPIO4_30K_PU)
 
 /* 100k pull-up2 */
-#define PM8350_ADC7_AMUX_THM1_100K_PU(sid)             ((sid) << 8 | 0x44)
-#define PM8350_ADC7_AMUX_THM2_100K_PU(sid)             ((sid) << 8 | 0x45)
-#define PM8350_ADC7_AMUX_THM3_100K_PU(sid)             ((sid) << 8 | 0x46)
-#define PM8350_ADC7_AMUX_THM4_100K_PU(sid)             ((sid) << 8 | 0x47)
-#define PM8350_ADC7_AMUX_THM5_100K_PU(sid)             ((sid) << 8 | 0x48)
-#define PM8350_ADC7_GPIO1_100K_PU(sid)                 ((sid) << 8 | 0x4a)
-#define PM8350_ADC7_GPIO2_100K_PU(sid)                 ((sid) << 8 | 0x4b)
-#define PM8350_ADC7_GPIO3_100K_PU(sid)                 ((sid) << 8 | 0x4c)
-#define PM8350_ADC7_GPIO4_100K_PU(sid)                 ((sid) << 8 | 0x4d)
+#define PM8350_ADC7_AMUX_THM1_100K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM1_100K_PU)
+#define PM8350_ADC7_AMUX_THM2_100K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM2_100K_PU)
+#define PM8350_ADC7_AMUX_THM3_100K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM3_100K_PU)
+#define PM8350_ADC7_AMUX_THM4_100K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM4_100K_PU)
+#define PM8350_ADC7_AMUX_THM5_100K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM5_100K_PU)
+#define PM8350_ADC7_GPIO1_100K_PU(sid)                 ((sid) << 8 | ADC7_GPIO1_100K_PU)
+#define PM8350_ADC7_GPIO2_100K_PU(sid)                 ((sid) << 8 | ADC7_GPIO2_100K_PU)
+#define PM8350_ADC7_GPIO3_100K_PU(sid)                 ((sid) << 8 | ADC7_GPIO3_100K_PU)
+#define PM8350_ADC7_GPIO4_100K_PU(sid)                 ((sid) << 8 | ADC7_GPIO4_100K_PU)
 
 /* 400k pull-up3 */
-#define PM8350_ADC7_AMUX_THM1_400K_PU(sid)             ((sid) << 8 | 0x64)
-#define PM8350_ADC7_AMUX_THM2_400K_PU(sid)             ((sid) << 8 | 0x65)
-#define PM8350_ADC7_AMUX_THM3_400K_PU(sid)             ((sid) << 8 | 0x66)
-#define PM8350_ADC7_AMUX_THM4_400K_PU(sid)             ((sid) << 8 | 0x67)
-#define PM8350_ADC7_AMUX_THM5_400K_PU(sid)             ((sid) << 8 | 0x68)
-#define PM8350_ADC7_GPIO1_400K_PU(sid)                 ((sid) << 8 | 0x6a)
-#define PM8350_ADC7_GPIO2_400K_PU(sid)                 ((sid) << 8 | 0x6b)
-#define PM8350_ADC7_GPIO3_400K_PU(sid)                 ((sid) << 8 | 0x6c)
-#define PM8350_ADC7_GPIO4_400K_PU(sid)                 ((sid) << 8 | 0x6d)
+#define PM8350_ADC7_AMUX_THM1_400K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM1_400K_PU)
+#define PM8350_ADC7_AMUX_THM2_400K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM2_400K_PU)
+#define PM8350_ADC7_AMUX_THM3_400K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM3_400K_PU)
+#define PM8350_ADC7_AMUX_THM4_400K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM4_400K_PU)
+#define PM8350_ADC7_AMUX_THM5_400K_PU(sid)             ((sid) << 8 | ADC7_AMUX_THM5_400K_PU)
+#define PM8350_ADC7_GPIO1_400K_PU(sid)                 ((sid) << 8 | ADC7_GPIO1_400K_PU)
+#define PM8350_ADC7_GPIO2_400K_PU(sid)                 ((sid) << 8 | ADC7_GPIO2_400K_PU)
+#define PM8350_ADC7_GPIO3_400K_PU(sid)                 ((sid) << 8 | ADC7_GPIO3_400K_PU)
+#define PM8350_ADC7_GPIO4_400K_PU(sid)                 ((sid) << 8 | ADC7_GPIO4_400K_PU)
 
 /* 1/3 Divider */
-#define PM8350_ADC7_GPIO4_DIV3(sid)                    ((sid) << 8 | 0x8d)
+#define PM8350_ADC7_GPIO4_DIV3(sid)                    ((sid) << 8 | ADC7_GPIO4_DIV3)
 
-#define PM8350_ADC7_VPH_PWR(sid)                       ((sid) << 8 | 0x8e)
+#define PM8350_ADC7_VPH_PWR(sid)                       ((sid) << 8 | ADC7_VPH_PWR)
 
 #endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H */
index dc2497c..57c7977 100644 (file)
 #define PM8350B_SID                                    3
 #endif
 
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
 /* ADC channels for PM8350B_ADC for PMIC7 */
-#define PM8350B_ADC7_REF_GND                   (PM8350B_SID << 8 | 0x0)
-#define PM8350B_ADC7_1P25VREF                  (PM8350B_SID << 8 | 0x01)
-#define PM8350B_ADC7_VREF_VADC                 (PM8350B_SID << 8 | 0x02)
-#define PM8350B_ADC7_DIE_TEMP                  (PM8350B_SID << 8 | 0x03)
+#define PM8350B_ADC7_REF_GND                   (PM8350B_SID << 8 | ADC7_REF_GND)
+#define PM8350B_ADC7_1P25VREF                  (PM8350B_SID << 8 | ADC7_1P25VREF)
+#define PM8350B_ADC7_VREF_VADC                 (PM8350B_SID << 8 | ADC7_VREF_VADC)
+#define PM8350B_ADC7_DIE_TEMP                  (PM8350B_SID << 8 | ADC7_DIE_TEMP)
 
-#define PM8350B_ADC7_AMUX_THM1                 (PM8350B_SID << 8 | 0x04)
-#define PM8350B_ADC7_AMUX_THM2                 (PM8350B_SID << 8 | 0x05)
-#define PM8350B_ADC7_AMUX_THM3                 (PM8350B_SID << 8 | 0x06)
-#define PM8350B_ADC7_AMUX_THM4                 (PM8350B_SID << 8 | 0x07)
-#define PM8350B_ADC7_AMUX_THM5                 (PM8350B_SID << 8 | 0x08)
-#define PM8350B_ADC7_AMUX_THM6                 (PM8350B_SID << 8 | 0x09)
-#define PM8350B_ADC7_GPIO1                     (PM8350B_SID << 8 | 0x0a)
-#define PM8350B_ADC7_GPIO2                     (PM8350B_SID << 8 | 0x0b)
-#define PM8350B_ADC7_GPIO3                     (PM8350B_SID << 8 | 0x0c)
-#define PM8350B_ADC7_GPIO4                     (PM8350B_SID << 8 | 0x0d)
+#define PM8350B_ADC7_AMUX_THM1                 (PM8350B_SID << 8 | ADC7_AMUX_THM1)
+#define PM8350B_ADC7_AMUX_THM2                 (PM8350B_SID << 8 | ADC7_AMUX_THM2)
+#define PM8350B_ADC7_AMUX_THM3                 (PM8350B_SID << 8 | ADC7_AMUX_THM3)
+#define PM8350B_ADC7_AMUX_THM4                 (PM8350B_SID << 8 | ADC7_AMUX_THM4)
+#define PM8350B_ADC7_AMUX_THM5                 (PM8350B_SID << 8 | ADC7_AMUX_THM5)
+#define PM8350B_ADC7_AMUX_THM6                 (PM8350B_SID << 8 | ADC7_AMUX_THM6)
+#define PM8350B_ADC7_GPIO1                     (PM8350B_SID << 8 | ADC7_GPIO1)
+#define PM8350B_ADC7_GPIO2                     (PM8350B_SID << 8 | ADC7_GPIO2)
+#define PM8350B_ADC7_GPIO3                     (PM8350B_SID << 8 | ADC7_GPIO3)
+#define PM8350B_ADC7_GPIO4                     (PM8350B_SID << 8 | ADC7_GPIO4)
 
-#define PM8350B_ADC7_CHG_TEMP                  (PM8350B_SID << 8 | 0x10)
-#define PM8350B_ADC7_USB_IN_V_16               (PM8350B_SID << 8 | 0x11)
-#define PM8350B_ADC7_VDC_16                    (PM8350B_SID << 8 | 0x12)
-#define PM8350B_ADC7_CC1_ID                    (PM8350B_SID << 8 | 0x13)
-#define PM8350B_ADC7_VREF_BAT_THERM            (PM8350B_SID << 8 | 0x15)
-#define PM8350B_ADC7_IIN_FB                    (PM8350B_SID << 8 | 0x17)
+#define PM8350B_ADC7_CHG_TEMP                  (PM8350B_SID << 8 | ADC7_CHG_TEMP)
+#define PM8350B_ADC7_USB_IN_V_16               (PM8350B_SID << 8 | ADC7_USB_IN_V_16)
+#define PM8350B_ADC7_VDC_16                    (PM8350B_SID << 8 | ADC7_VDC_16)
+#define PM8350B_ADC7_CC1_ID                    (PM8350B_SID << 8 | ADC7_CC1_ID)
+#define PM8350B_ADC7_VREF_BAT_THERM            (PM8350B_SID << 8 | ADC7_VREF_BAT_THERM)
+#define PM8350B_ADC7_IIN_FB                    (PM8350B_SID << 8 | ADC7_IIN_FB)
 
 /* 30k pull-up1 */
-#define PM8350B_ADC7_AMUX_THM1_30K_PU          (PM8350B_SID << 8 | 0x24)
-#define PM8350B_ADC7_AMUX_THM2_30K_PU          (PM8350B_SID << 8 | 0x25)
-#define PM8350B_ADC7_AMUX_THM3_30K_PU          (PM8350B_SID << 8 | 0x26)
-#define PM8350B_ADC7_AMUX_THM4_30K_PU          (PM8350B_SID << 8 | 0x27)
-#define PM8350B_ADC7_AMUX_THM5_30K_PU          (PM8350B_SID << 8 | 0x28)
-#define PM8350B_ADC7_AMUX_THM6_30K_PU          (PM8350B_SID << 8 | 0x29)
-#define PM8350B_ADC7_GPIO1_30K_PU              (PM8350B_SID << 8 | 0x2a)
-#define PM8350B_ADC7_GPIO2_30K_PU              (PM8350B_SID << 8 | 0x2b)
-#define PM8350B_ADC7_GPIO3_30K_PU              (PM8350B_SID << 8 | 0x2c)
-#define PM8350B_ADC7_GPIO4_30K_PU              (PM8350B_SID << 8 | 0x2d)
-#define PM8350B_ADC7_CC1_ID_30K_PU             (PM8350B_SID << 8 | 0x33)
+#define PM8350B_ADC7_AMUX_THM1_30K_PU          (PM8350B_SID << 8 | ADC7_AMUX_THM1_30K_PU)
+#define PM8350B_ADC7_AMUX_THM2_30K_PU          (PM8350B_SID << 8 | ADC7_AMUX_THM2_30K_PU)
+#define PM8350B_ADC7_AMUX_THM3_30K_PU          (PM8350B_SID << 8 | ADC7_AMUX_THM3_30K_PU)
+#define PM8350B_ADC7_AMUX_THM4_30K_PU          (PM8350B_SID << 8 | ADC7_AMUX_THM4_30K_PU)
+#define PM8350B_ADC7_AMUX_THM5_30K_PU          (PM8350B_SID << 8 | ADC7_AMUX_THM5_30K_PU)
+#define PM8350B_ADC7_AMUX_THM6_30K_PU          (PM8350B_SID << 8 | ADC7_AMUX_THM6_30K_PU)
+#define PM8350B_ADC7_GPIO1_30K_PU              (PM8350B_SID << 8 | ADC7_GPIO1_30K_PU)
+#define PM8350B_ADC7_GPIO2_30K_PU              (PM8350B_SID << 8 | ADC7_GPIO2_30K_PU)
+#define PM8350B_ADC7_GPIO3_30K_PU              (PM8350B_SID << 8 | ADC7_GPIO3_30K_PU)
+#define PM8350B_ADC7_GPIO4_30K_PU              (PM8350B_SID << 8 | ADC7_GPIO4_30K_PU)
+#define PM8350B_ADC7_CC1_ID_30K_PU             (PM8350B_SID << 8 | ADC7_CC1_ID_30K_PU)
 
 /* 100k pull-up2 */
-#define PM8350B_ADC7_AMUX_THM1_100K_PU         (PM8350B_SID << 8 | 0x44)
-#define PM8350B_ADC7_AMUX_THM2_100K_PU         (PM8350B_SID << 8 | 0x45)
-#define PM8350B_ADC7_AMUX_THM3_100K_PU         (PM8350B_SID << 8 | 0x46)
-#define PM8350B_ADC7_AMUX_THM4_100K_PU         (PM8350B_SID << 8 | 0x47)
-#define PM8350B_ADC7_AMUX_THM5_100K_PU         (PM8350B_SID << 8 | 0x48)
-#define PM8350B_ADC7_AMUX_THM6_100K_PU         (PM8350B_SID << 8 | 0x49)
-#define PM8350B_ADC7_GPIO1_100K_PU             (PM8350B_SID << 8 | 0x4a)
-#define PM8350B_ADC7_GPIO2_100K_PU             (PM8350B_SID << 8 | 0x4b)
-#define PM8350B_ADC7_GPIO3_100K_PU             (PM8350B_SID << 8 | 0x4c)
-#define PM8350B_ADC7_GPIO4_100K_PU             (PM8350B_SID << 8 | 0x4d)
-#define PM8350B_ADC7_CC1_ID_100K_PU            (PM8350B_SID << 8 | 0x53)
+#define PM8350B_ADC7_AMUX_THM1_100K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM1_100K_PU)
+#define PM8350B_ADC7_AMUX_THM2_100K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM2_100K_PU)
+#define PM8350B_ADC7_AMUX_THM3_100K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM3_100K_PU)
+#define PM8350B_ADC7_AMUX_THM4_100K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM4_100K_PU)
+#define PM8350B_ADC7_AMUX_THM5_100K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM5_100K_PU)
+#define PM8350B_ADC7_AMUX_THM6_100K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM6_100K_PU)
+#define PM8350B_ADC7_GPIO1_100K_PU             (PM8350B_SID << 8 | ADC7_GPIO1_100K_PU)
+#define PM8350B_ADC7_GPIO2_100K_PU             (PM8350B_SID << 8 | ADC7_GPIO2_100K_PU)
+#define PM8350B_ADC7_GPIO3_100K_PU             (PM8350B_SID << 8 | ADC7_GPIO3_100K_PU)
+#define PM8350B_ADC7_GPIO4_100K_PU             (PM8350B_SID << 8 | ADC7_GPIO4_100K_PU)
+#define PM8350B_ADC7_CC1_ID_100K_PU            (PM8350B_SID << 8 | ADC7_CC1_ID_100K_PU)
 
 /* 400k pull-up3 */
-#define PM8350B_ADC7_AMUX_THM1_400K_PU         (PM8350B_SID << 8 | 0x64)
-#define PM8350B_ADC7_AMUX_THM2_400K_PU         (PM8350B_SID << 8 | 0x65)
-#define PM8350B_ADC7_AMUX_THM3_400K_PU         (PM8350B_SID << 8 | 0x66)
-#define PM8350B_ADC7_AMUX_THM4_400K_PU         (PM8350B_SID << 8 | 0x67)
-#define PM8350B_ADC7_AMUX_THM5_400K_PU         (PM8350B_SID << 8 | 0x68)
-#define PM8350B_ADC7_AMUX_THM6_400K_PU         (PM8350B_SID << 8 | 0x69)
-#define PM8350B_ADC7_GPIO1_400K_PU             (PM8350B_SID << 8 | 0x6a)
-#define PM8350B_ADC7_GPIO2_400K_PU             (PM8350B_SID << 8 | 0x6b)
-#define PM8350B_ADC7_GPIO3_400K_PU             (PM8350B_SID << 8 | 0x6c)
-#define PM8350B_ADC7_GPIO4_400K_PU             (PM8350B_SID << 8 | 0x6d)
-#define PM8350B_ADC7_CC1_ID_400K_PU            (PM8350B_SID << 8 | 0x73)
+#define PM8350B_ADC7_AMUX_THM1_400K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM1_400K_PU)
+#define PM8350B_ADC7_AMUX_THM2_400K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM2_400K_PU)
+#define PM8350B_ADC7_AMUX_THM3_400K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM3_400K_PU)
+#define PM8350B_ADC7_AMUX_THM4_400K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM4_400K_PU)
+#define PM8350B_ADC7_AMUX_THM5_400K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM5_400K_PU)
+#define PM8350B_ADC7_AMUX_THM6_400K_PU         (PM8350B_SID << 8 | ADC7_AMUX_THM6_400K_PU)
+#define PM8350B_ADC7_GPIO1_400K_PU             (PM8350B_SID << 8 | ADC7_GPIO1_400K_PU)
+#define PM8350B_ADC7_GPIO2_400K_PU             (PM8350B_SID << 8 | ADC7_GPIO2_400K_PU)
+#define PM8350B_ADC7_GPIO3_400K_PU             (PM8350B_SID << 8 | ADC7_GPIO3_400K_PU)
+#define PM8350B_ADC7_GPIO4_400K_PU             (PM8350B_SID << 8 | ADC7_GPIO4_400K_PU)
+#define PM8350B_ADC7_CC1_ID_400K_PU            (PM8350B_SID << 8 | ADC7_CC1_ID_400K_PU)
 
 /* 1/3 Divider */
-#define PM8350B_ADC7_GPIO1_DIV3                        (PM8350B_SID << 8 | 0x8a)
-#define PM8350B_ADC7_GPIO2_DIV3                        (PM8350B_SID << 8 | 0x8b)
-#define PM8350B_ADC7_GPIO3_DIV3                        (PM8350B_SID << 8 | 0x8c)
-#define PM8350B_ADC7_GPIO4_DIV3                        (PM8350B_SID << 8 | 0x8d)
+#define PM8350B_ADC7_GPIO1_DIV3                        (PM8350B_SID << 8 | ADC7_GPIO1_DIV3)
+#define PM8350B_ADC7_GPIO2_DIV3                        (PM8350B_SID << 8 | ADC7_GPIO2_DIV3)
+#define PM8350B_ADC7_GPIO3_DIV3                        (PM8350B_SID << 8 | ADC7_GPIO3_DIV3)
+#define PM8350B_ADC7_GPIO4_DIV3                        (PM8350B_SID << 8 | ADC7_GPIO4_DIV3)
 
-#define PM8350B_ADC7_VPH_PWR                   (PM8350B_SID << 8 | 0x8e)
-#define PM8350B_ADC7_VBAT_SNS                  (PM8350B_SID << 8 | 0x8f)
+#define PM8350B_ADC7_VPH_PWR                   (PM8350B_SID << 8 | ADC7_VPH_PWR)
+#define PM8350B_ADC7_VBAT_SNS                  (PM8350B_SID << 8 | ADC7_VBAT_SNS)
 
-#define PM8350B_ADC7_SBUx                      (PM8350B_SID << 8 | 0x94)
-#define PM8350B_ADC7_VBAT_2S_MID               (PM8350B_SID << 8 | 0x96)
+#define PM8350B_ADC7_SBUx                      (PM8350B_SID << 8 | ADC7_SBU)
+#define PM8350B_ADC7_VBAT_2S_MID               (PM8350B_SID << 8 | ADC7_VBAT_2S_MID)
 
 #endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350B_H */
index 6c29687..3d1a41a 100644 (file)
 #define PMK8350_SID                                    0
 #endif
 
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
 /* ADC channels for PMK8350_ADC for PMIC7 */
-#define PMK8350_ADC7_REF_GND                   (PMK8350_SID << 8 | 0x0)
-#define PMK8350_ADC7_1P25VREF                  (PMK8350_SID << 8 | 0x01)
-#define PMK8350_ADC7_VREF_VADC                 (PMK8350_SID << 8 | 0x02)
-#define PMK8350_ADC7_DIE_TEMP                  (PMK8350_SID << 8 | 0x03)
+#define PMK8350_ADC7_REF_GND                   (PMK8350_SID << 8 | ADC7_REF_GND)
+#define PMK8350_ADC7_1P25VREF                  (PMK8350_SID << 8 | ADC7_1P25VREF)
+#define PMK8350_ADC7_VREF_VADC                 (PMK8350_SID << 8 | ADC7_VREF_VADC)
+#define PMK8350_ADC7_DIE_TEMP                  (PMK8350_SID << 8 | ADC7_DIE_TEMP)
 
-#define PMK8350_ADC7_AMUX_THM1                 (PMK8350_SID << 8 | 0x04)
-#define PMK8350_ADC7_AMUX_THM2                 (PMK8350_SID << 8 | 0x05)
-#define PMK8350_ADC7_AMUX_THM3                 (PMK8350_SID << 8 | 0x06)
-#define PMK8350_ADC7_AMUX_THM4                 (PMK8350_SID << 8 | 0x07)
-#define PMK8350_ADC7_AMUX_THM5                 (PMK8350_SID << 8 | 0x08)
+#define PMK8350_ADC7_AMUX_THM1                 (PMK8350_SID << 8 | ADC7_AMUX_THM1)
+#define PMK8350_ADC7_AMUX_THM2                 (PMK8350_SID << 8 | ADC7_AMUX_THM2)
+#define PMK8350_ADC7_AMUX_THM3                 (PMK8350_SID << 8 | ADC7_AMUX_THM3)
+#define PMK8350_ADC7_AMUX_THM4                 (PMK8350_SID << 8 | ADC7_AMUX_THM4)
+#define PMK8350_ADC7_AMUX_THM5                 (PMK8350_SID << 8 | ADC7_AMUX_THM5)
 
 /* 30k pull-up1 */
-#define PMK8350_ADC7_AMUX_THM1_30K_PU          (PMK8350_SID << 8 | 0x24)
-#define PMK8350_ADC7_AMUX_THM2_30K_PU          (PMK8350_SID << 8 | 0x25)
-#define PMK8350_ADC7_AMUX_THM3_30K_PU          (PMK8350_SID << 8 | 0x26)
-#define PMK8350_ADC7_AMUX_THM4_30K_PU          (PMK8350_SID << 8 | 0x27)
-#define PMK8350_ADC7_AMUX_THM5_30K_PU          (PMK8350_SID << 8 | 0x28)
+#define PMK8350_ADC7_AMUX_THM1_30K_PU          (PMK8350_SID << 8 | ADC7_AMUX_THM1_30K_PU)
+#define PMK8350_ADC7_AMUX_THM2_30K_PU          (PMK8350_SID << 8 | ADC7_AMUX_THM2_30K_PU)
+#define PMK8350_ADC7_AMUX_THM3_30K_PU          (PMK8350_SID << 8 | ADC7_AMUX_THM3_30K_PU)
+#define PMK8350_ADC7_AMUX_THM4_30K_PU          (PMK8350_SID << 8 | ADC7_AMUX_THM4_30K_PU)
+#define PMK8350_ADC7_AMUX_THM5_30K_PU          (PMK8350_SID << 8 | ADC7_AMUX_THM5_30K_PU)
 
 /* 100k pull-up2 */
-#define PMK8350_ADC7_AMUX_THM1_100K_PU         (PMK8350_SID << 8 | 0x44)
-#define PMK8350_ADC7_AMUX_THM2_100K_PU         (PMK8350_SID << 8 | 0x45)
-#define PMK8350_ADC7_AMUX_THM3_100K_PU         (PMK8350_SID << 8 | 0x46)
-#define PMK8350_ADC7_AMUX_THM4_100K_PU         (PMK8350_SID << 8 | 0x47)
-#define PMK8350_ADC7_AMUX_THM5_100K_PU         (PMK8350_SID << 8 | 0x48)
+#define PMK8350_ADC7_AMUX_THM1_100K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM1_100K_PU)
+#define PMK8350_ADC7_AMUX_THM2_100K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM2_100K_PU)
+#define PMK8350_ADC7_AMUX_THM3_100K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM3_100K_PU)
+#define PMK8350_ADC7_AMUX_THM4_100K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM4_100K_PU)
+#define PMK8350_ADC7_AMUX_THM5_100K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM5_100K_PU)
 
 /* 400k pull-up3 */
-#define PMK8350_ADC7_AMUX_THM1_400K_PU         (PMK8350_SID << 8 | 0x64)
-#define PMK8350_ADC7_AMUX_THM2_400K_PU         (PMK8350_SID << 8 | 0x65)
-#define PMK8350_ADC7_AMUX_THM3_400K_PU         (PMK8350_SID << 8 | 0x66)
-#define PMK8350_ADC7_AMUX_THM4_400K_PU         (PMK8350_SID << 8 | 0x67)
-#define PMK8350_ADC7_AMUX_THM5_400K_PU         (PMK8350_SID << 8 | 0x68)
+#define PMK8350_ADC7_AMUX_THM1_400K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM1_400K_PU)
+#define PMK8350_ADC7_AMUX_THM2_400K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM2_400K_PU)
+#define PMK8350_ADC7_AMUX_THM3_400K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM3_400K_PU)
+#define PMK8350_ADC7_AMUX_THM4_400K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM4_400K_PU)
+#define PMK8350_ADC7_AMUX_THM5_400K_PU         (PMK8350_SID << 8 | ADC7_AMUX_THM5_400K_PU)
 
 #endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H */
index d6df1b1..c5adfa8 100644 (file)
 #define PMR735A_SID                                    4
 #endif
 
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
 /* ADC channels for PMR735A_ADC for PMIC7 */
-#define PMR735A_ADC7_REF_GND                   (PMR735A_SID << 8 | 0x0)
-#define PMR735A_ADC7_1P25VREF                  (PMR735A_SID << 8 | 0x01)
-#define PMR735A_ADC7_VREF_VADC                 (PMR735A_SID << 8 | 0x02)
-#define PMR735A_ADC7_DIE_TEMP                  (PMR735A_SID << 8 | 0x03)
+#define PMR735A_ADC7_REF_GND                   (PMR735A_SID << 8 | ADC7_REF_GND)
+#define PMR735A_ADC7_1P25VREF                  (PMR735A_SID << 8 | ADC7_1P25VREF)
+#define PMR735A_ADC7_VREF_VADC                 (PMR735A_SID << 8 | ADC7_VREF_VADC)
+#define PMR735A_ADC7_DIE_TEMP                  (PMR735A_SID << 8 | ADC7_DIE_TEMP)
 
-#define PMR735A_ADC7_GPIO1                     (PMR735A_SID << 8 | 0x0a)
-#define PMR735A_ADC7_GPIO2                     (PMR735A_SID << 8 | 0x0b)
-#define PMR735A_ADC7_GPIO3                     (PMR735A_SID << 8 | 0x0c)
+#define PMR735A_ADC7_GPIO1                     (PMR735A_SID << 8 | ADC7_GPIO1)
+#define PMR735A_ADC7_GPIO2                     (PMR735A_SID << 8 | ADC7_GPIO2)
+#define PMR735A_ADC7_GPIO3                     (PMR735A_SID << 8 | ADC7_GPIO3)
 
 /* 100k pull-up2 */
-#define PMR735A_ADC7_GPIO1_100K_PU             (PMR735A_SID << 8 | 0x4a)
-#define PMR735A_ADC7_GPIO2_100K_PU             (PMR735A_SID << 8 | 0x4b)
-#define PMR735A_ADC7_GPIO3_100K_PU             (PMR735A_SID << 8 | 0x4c)
+#define PMR735A_ADC7_GPIO1_100K_PU             (PMR735A_SID << 8 | ADC7_GPIO1_100K_PU)
+#define PMR735A_ADC7_GPIO2_100K_PU             (PMR735A_SID << 8 | ADC7_GPIO2_100K_PU)
+#define PMR735A_ADC7_GPIO3_100K_PU             (PMR735A_SID << 8 | ADC7_GPIO3_100K_PU)
 
 #endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735A_H */
index 8da0e7d..fdb8dd9 100644 (file)
 #define PMR735B_SID                                    5
 #endif
 
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
 /* ADC channels for PMR735B_ADC for PMIC7 */
-#define PMR735B_ADC7_REF_GND                   (PMR735B_SID << 8 | 0x0)
-#define PMR735B_ADC7_1P25VREF                  (PMR735B_SID << 8 | 0x01)
-#define PMR735B_ADC7_VREF_VADC                 (PMR735B_SID << 8 | 0x02)
-#define PMR735B_ADC7_DIE_TEMP                  (PMR735B_SID << 8 | 0x03)
+#define PMR735B_ADC7_REF_GND                   (PMR735B_SID << 8 | ADC7_REF_GND)
+#define PMR735B_ADC7_1P25VREF                  (PMR735B_SID << 8 | ADC7_1P25VREF)
+#define PMR735B_ADC7_VREF_VADC                 (PMR735B_SID << 8 | ADC7_VREF_VADC)
+#define PMR735B_ADC7_DIE_TEMP                  (PMR735B_SID << 8 | ADC7_DIE_TEMP)
 
-#define PMR735B_ADC7_GPIO1                     (PMR735B_SID << 8 | 0x0a)
-#define PMR735B_ADC7_GPIO2                     (PMR735B_SID << 8 | 0x0b)
-#define PMR735B_ADC7_GPIO3                     (PMR735B_SID << 8 | 0x0c)
+#define PMR735B_ADC7_GPIO1                     (PMR735B_SID << 8 | ADC7_GPIO1)
+#define PMR735B_ADC7_GPIO2                     (PMR735B_SID << 8 | ADC7_GPIO2)
+#define PMR735B_ADC7_GPIO3                     (PMR735B_SID << 8 | ADC7_GPIO3)
 
 /* 100k pull-up2 */
-#define PMR735B_ADC7_GPIO1_100K_PU             (PMR735B_SID << 8 | 0x4a)
-#define PMR735B_ADC7_GPIO2_100K_PU             (PMR735B_SID << 8 | 0x4b)
-#define PMR735B_ADC7_GPIO3_100K_PU             (PMR735B_SID << 8 | 0x4c)
+#define PMR735B_ADC7_GPIO1_100K_PU             (PMR735B_SID << 8 | ADC7_GPIO1_100K_PU)
+#define PMR735B_ADC7_GPIO2_100K_PU             (PMR735B_SID << 8 | ADC7_GPIO2_100K_PU)
+#define PMR735B_ADC7_GPIO3_100K_PU             (PMR735B_SID << 8 | ADC7_GPIO3_100K_PU)
 
 #endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735B_H */
index 061dd84..4fcbf4d 100644 (file)
@@ -37,10 +37,12 @@ struct _ddebug {
 #define _DPRINTK_FLAGS_INCL_FUNCNAME   (1<<2)
 #define _DPRINTK_FLAGS_INCL_LINENO     (1<<3)
 #define _DPRINTK_FLAGS_INCL_TID                (1<<4)
+#define _DPRINTK_FLAGS_INCL_SOURCENAME (1<<5)
 
 #define _DPRINTK_FLAGS_INCL_ANY                \
        (_DPRINTK_FLAGS_INCL_MODNAME | _DPRINTK_FLAGS_INCL_FUNCNAME |\
-        _DPRINTK_FLAGS_INCL_LINENO  | _DPRINTK_FLAGS_INCL_TID)
+        _DPRINTK_FLAGS_INCL_LINENO  | _DPRINTK_FLAGS_INCL_TID |\
+        _DPRINTK_FLAGS_INCL_SOURCENAME)
 
 #if defined DEBUG
 #define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT
index a718f85..ee80ca4 100644 (file)
@@ -467,6 +467,31 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE)
         INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FIRMWARE_VERSION)
 
 /**
+ * SMC call protocol for Mailbox, starting FUNCID from 60
+ *
+ * Call register usage:
+ * a0 INTEL_SIP_SMC_MBOX_SEND_CMD
+ * a1 mailbox command code
+ * a2 physical address that contain mailbox command data (not include header)
+ * a3 mailbox command data size in word
+ * a4 set to 0 for CASUAL, set to 1 for URGENT
+ * a5 physical address for secure firmware to put response data
+ *    (not include header)
+ * a6 maximum size in word of physical address to store response data
+ * a7 not used
+ *
+ * Return status
+ * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_STATUS_REJECTED or
+ *    INTEL_SIP_SMC_STATUS_ERROR
+ * a1 mailbox error code
+ * a2 response data length in word
+ * a3 not used
+ */
+#define INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD 60
+       #define INTEL_SIP_SMC_MBOX_SEND_CMD \
+       INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD)
+
+/**
  * Request INTEL_SIP_SMC_SVC_VERSION
  *
  * Sync call used to query the SIP SMC API Version
index 0c16037..60ed821 100644 (file)
@@ -118,6 +118,9 @@ struct stratix10_svc_chan;
  * @COMMAND_SMC_SVC_VERSION: Non-mailbox SMC SVC API Version,
  * return status is SVC_STATUS_OK
  *
+ * @COMMAND_MBOX_SEND_CMD: send generic mailbox command, return status is
+ * SVC_STATUS_OK or SVC_STATUS_ERROR
+ *
  * @COMMAND_RSU_DCMF_STATUS: query firmware for the DCMF status
  * return status is SVC_STATUS_OK or SVC_STATUS_ERROR
  *
@@ -164,6 +167,8 @@ enum stratix10_svc_command_code {
        COMMAND_FCS_RANDOM_NUMBER_GEN,
        /* for general status poll */
        COMMAND_POLL_SERVICE_STATUS = 40,
+       /* for generic mailbox send command */
+       COMMAND_MBOX_SEND_CMD = 100,
        /* Non-mailbox SMC Call */
        COMMAND_SMC_SVC_VERSION = 200,
 };
diff --git a/include/linux/iio/common/inv_sensors_timestamp.h b/include/linux/iio/common/inv_sensors_timestamp.h
new file mode 100644 (file)
index 0000000..a47d304
--- /dev/null
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020 Invensense, Inc.
+ */
+
+#ifndef INV_SENSORS_TIMESTAMP_H_
+#define INV_SENSORS_TIMESTAMP_H_
+
+/**
+ * struct inv_sensors_timestamp_chip - chip internal properties
+ * @clock_period:      internal clock period in ns
+ * @jitter:            acceptable jitter in per-mille
+ * @init_period:       chip initial period at reset in ns
+ */
+struct inv_sensors_timestamp_chip {
+       uint32_t clock_period;
+       uint32_t jitter;
+       uint32_t init_period;
+};
+
+/**
+ * struct inv_sensors_timestamp_interval - timestamps interval
+ * @lo:        interval lower bound
+ * @up:        interval upper bound
+ */
+struct inv_sensors_timestamp_interval {
+       int64_t lo;
+       int64_t up;
+};
+
+/**
+ * struct inv_sensors_timestamp_acc - accumulator for computing an estimation
+ * @val:       current estimation of the value, the mean of all values
+ * @idx:       current index of the next free place in values table
+ * @values:    table of all measured values, use for computing the mean
+ */
+struct inv_sensors_timestamp_acc {
+       uint32_t val;
+       size_t idx;
+       uint32_t values[32];
+};
+
+/**
+ * struct inv_sensors_timestamp - timestamp management states
+ * @chip:              chip internal characteristics
+ * @min_period:                minimal acceptable clock period
+ * @max_period:                maximal acceptable clock period
+ * @it:                        interrupts interval timestamps
+ * @timestamp:         store last timestamp for computing next data timestamp
+ * @mult:              current internal period multiplier
+ * @new_mult:          new set internal period multiplier (not yet effective)
+ * @period:            measured current period of the sensor
+ * @chip_period:       accumulator for computing internal chip period
+ */
+struct inv_sensors_timestamp {
+       struct inv_sensors_timestamp_chip chip;
+       uint32_t min_period;
+       uint32_t max_period;
+       struct inv_sensors_timestamp_interval it;
+       int64_t timestamp;
+       uint32_t mult;
+       uint32_t new_mult;
+       uint32_t period;
+       struct inv_sensors_timestamp_acc chip_period;
+};
+
+void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
+                               const struct inv_sensors_timestamp_chip *chip);
+
+int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
+                                    uint32_t period, bool fifo);
+
+void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
+                                    uint32_t fifo_period, size_t fifo_nb,
+                                    size_t sensor_nb, int64_t timestamp);
+
+static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts)
+{
+       ts->timestamp += ts->period;
+       return ts->timestamp;
+}
+
+void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
+                                    uint32_t fifo_period, size_t fifo_nb,
+                                    unsigned int fifo_no);
+
+static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts)
+{
+       const struct inv_sensors_timestamp_interval interval_init = {0LL, 0LL};
+
+       ts->it = interval_init;
+       ts->timestamp = 0;
+}
+
+#endif
index 82faa98..117bde7 100644 (file)
@@ -19,6 +19,8 @@ enum iio_event_info {
        IIO_EV_INFO_TIMEOUT,
        IIO_EV_INFO_RESET_TIMEOUT,
        IIO_EV_INFO_TAP2_MIN_DELAY,
+       IIO_EV_INFO_RUNNING_PERIOD,
+       IIO_EV_INFO_RUNNING_COUNT,
 };
 
 #define IIO_VAL_INT 1
index f6de4b6..039943e 100644 (file)
@@ -299,6 +299,10 @@ struct mhi_controller_config {
  * @iova_start: IOMMU starting address for data (required)
  * @iova_stop: IOMMU stop address for data (required)
  * @fw_image: Firmware image name for normal booting (optional)
+ * @fw_data: Firmware image data content for normal booting, used only
+ *           if fw_image is NULL and fbc_download is true (optional)
+ * @fw_sz: Firmware image data size for normal booting, used only if fw_image
+ *         is NULL and fbc_download is true (optional)
  * @edl_image: Firmware image name for emergency download mode (optional)
  * @rddm_size: RAM dump size that host should allocate for debugging purpose
  * @sbl_size: SBL image size downloaded through BHIe (optional)
@@ -384,6 +388,8 @@ struct mhi_controller {
        dma_addr_t iova_start;
        dma_addr_t iova_stop;
        const char *fw_image;
+       const u8 *fw_data;
+       size_t fw_sz;
        const char *edl_image;
        size_t rddm_size;
        size_t sbl_size;
index 06e6ef9..9b3d36a 100644 (file)
@@ -42,13 +42,13 @@ struct peci_controller_ops {
  */
 struct peci_controller {
        struct device dev;
-       struct peci_controller_ops *ops;
+       const struct peci_controller_ops *ops;
        struct mutex bus_lock; /* held for the duration of xfer */
        u8 id;
 };
 
 struct peci_controller *devm_peci_controller_add(struct device *parent,
-                                                struct peci_controller_ops *ops);
+                                                const struct peci_controller_ops *ops);
 
 static inline struct peci_controller *to_peci_controller(void *d)
 {
index c9a72e8..5ff1512 100644 (file)
@@ -122,6 +122,37 @@ TRACE_EVENT(fsi_master_break,
        )
 );
 
+TRACE_EVENT(fsi_master_scan,
+       TP_PROTO(const struct fsi_master *master, bool scan),
+       TP_ARGS(master, scan),
+       TP_STRUCT__entry(
+               __field(int,    master_idx)
+               __field(int,    n_links)
+               __field(bool,   scan)
+       ),
+       TP_fast_assign(
+               __entry->master_idx = master->idx;
+               __entry->n_links = master->n_links;
+               __entry->scan = scan;
+       ),
+       TP_printk("fsi%d (%d links) %s", __entry->master_idx, __entry->n_links,
+                 __entry->scan ? "scan" : "unscan")
+);
+
+TRACE_EVENT(fsi_master_unregister,
+       TP_PROTO(const struct fsi_master *master),
+       TP_ARGS(master),
+       TP_STRUCT__entry(
+               __field(int,    master_idx)
+               __field(int,    n_links)
+       ),
+       TP_fast_assign(
+               __entry->master_idx = master->idx;
+               __entry->n_links = master->n_links;
+       ),
+       TP_printk("fsi%d (%d links)", __entry->master_idx, __entry->n_links)
+);
+
 TRACE_EVENT(fsi_slave_init,
        TP_PROTO(const struct fsi_slave *slave),
        TP_ARGS(slave),
diff --git a/include/trace/events/fsi_master_i2cr.h b/include/trace/events/fsi_master_i2cr.h
new file mode 100644 (file)
index 0000000..c33eba1
--- /dev/null
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fsi_master_i2cr
+
+#if !defined(_TRACE_FSI_MASTER_I2CR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FSI_MASTER_I2CR_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(i2cr_i2c_error,
+       TP_PROTO(const struct i2c_client *client, uint32_t command, int rc),
+       TP_ARGS(client, command, rc),
+       TP_STRUCT__entry(
+               __field(int, bus)
+               __field(int, rc)
+               __array(unsigned char, command, sizeof(uint32_t))
+               __field(unsigned short, addr)
+       ),
+       TP_fast_assign(
+               __entry->bus = client->adapter->nr;
+               __entry->rc = rc;
+               memcpy(__entry->command, &command, sizeof(uint32_t));
+               __entry->addr = client->addr;
+       ),
+       TP_printk("%d-%02x command:{ %*ph } rc:%d", __entry->bus, __entry->addr,
+                 (int)sizeof(uint32_t), __entry->command, __entry->rc)
+);
+
+TRACE_EVENT(i2cr_read,
+       TP_PROTO(const struct i2c_client *client, uint32_t command, uint64_t *data),
+       TP_ARGS(client, command, data),
+       TP_STRUCT__entry(
+               __field(int, bus)
+               __array(unsigned char, data, sizeof(uint64_t))
+               __array(unsigned char, command, sizeof(uint32_t))
+               __field(unsigned short, addr)
+       ),
+       TP_fast_assign(
+               __entry->bus = client->adapter->nr;
+               memcpy(__entry->data, data, sizeof(uint64_t));
+               memcpy(__entry->command, &command, sizeof(uint32_t));
+               __entry->addr = client->addr;
+       ),
+       TP_printk("%d-%02x command:{ %*ph } { %*ph }", __entry->bus, __entry->addr,
+                 (int)sizeof(uint32_t), __entry->command, (int)sizeof(uint64_t), __entry->data)
+);
+
+TRACE_EVENT(i2cr_status,
+       TP_PROTO(const struct i2c_client *client, uint64_t status),
+       TP_ARGS(client, status),
+       TP_STRUCT__entry(
+               __field(uint64_t, status)
+               __field(int, bus)
+               __field(unsigned short, addr)
+       ),
+       TP_fast_assign(
+               __entry->status = status;
+               __entry->bus = client->adapter->nr;
+               __entry->addr = client->addr;
+       ),
+       TP_printk("%d-%02x %016llx", __entry->bus, __entry->addr, __entry->status)
+);
+
+TRACE_EVENT(i2cr_status_error,
+       TP_PROTO(const struct i2c_client *client, uint64_t status, uint64_t error, uint64_t log),
+       TP_ARGS(client, status, error, log),
+       TP_STRUCT__entry(
+               __field(uint64_t, error)
+               __field(uint64_t, log)
+               __field(uint64_t, status)
+               __field(int, bus)
+               __field(unsigned short, addr)
+       ),
+       TP_fast_assign(
+               __entry->error = error;
+               __entry->log = log;
+               __entry->status = status;
+               __entry->bus = client->adapter->nr;
+               __entry->addr = client->addr;
+       ),
+       TP_printk("%d-%02x status:%016llx error:%016llx log:%016llx", __entry->bus, __entry->addr,
+                 __entry->status, __entry->error, __entry->log)
+);
+
+TRACE_EVENT(i2cr_write,
+       TP_PROTO(const struct i2c_client *client, uint32_t command, uint64_t data),
+       TP_ARGS(client, command, data),
+       TP_STRUCT__entry(
+               __field(int, bus)
+               __array(unsigned char, data, sizeof(uint64_t))
+               __array(unsigned char, command, sizeof(uint32_t))
+               __field(unsigned short, addr)
+       ),
+       TP_fast_assign(
+               __entry->bus = client->adapter->nr;
+               memcpy(__entry->data, &data, sizeof(uint64_t));
+               memcpy(__entry->command, &command, sizeof(uint32_t));
+               __entry->addr = client->addr;
+       ),
+       TP_printk("%d-%02x command:{ %*ph } { %*ph }", __entry->bus, __entry->addr,
+                 (int)sizeof(uint32_t), __entry->command, (int)sizeof(uint64_t), __entry->data)
+);
+
+#endif
+
+#include <trace/define_trace.h>
index b2f1977..a2e730f 100644 (file)
@@ -60,6 +60,16 @@ struct scom_access {
  */
 
 /**
+ * FSI_SBEFIFO_CMD_TIMEOUT sets the timeout for writing data to the SBEFIFO.
+ *
+ * The command timeout is specified in seconds.  The minimum value of command
+ * timeout is 1 seconds (default) and the maximum value of command timeout is
+ * 120 seconds.  A command timeout of 0 will reset the value to the default of
+ * 1 seconds.
+ */
+#define FSI_SBEFIFO_CMD_TIMEOUT_SECONDS                _IOW('s', 0x01, __u32)
+
+/**
  * FSI_SBEFIFO_READ_TIMEOUT sets the read timeout for response from SBE.
  *
  * The read timeout is specified in seconds.  The minimum value of read
index 7e42a5b..ff0a931 100644 (file)
@@ -56,7 +56,7 @@ struct sync_fence_info {
  * @name:      name of fence
  * @status:    status of fence. 1: signaled 0:active <0:error
  * @flags:     sync_file_info flags
- * @num_fences number of fences in the sync_file
+ * @num_fences:        number of fences in the sync_file
  * @pad:       padding for 64-bit alignment, should always be zero
  * @sync_fence_info: pointer to array of struct &sync_fence_info with all
  *              fences in the sync_file
index fdd6d98..6fba642 100644 (file)
@@ -88,10 +88,11 @@ static inline const char *trim_prefix(const char *path)
        return path + skip;
 }
 
-static struct { unsigned flag:8; char opt_char; } opt_array[] = {
+static const struct { unsigned flag:8; char opt_char; } opt_array[] = {
        { _DPRINTK_FLAGS_PRINT, 'p' },
        { _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
        { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
+       { _DPRINTK_FLAGS_INCL_SOURCENAME, 's' },
        { _DPRINTK_FLAGS_INCL_LINENO, 'l' },
        { _DPRINTK_FLAGS_INCL_TID, 't' },
        { _DPRINTK_FLAGS_NONE, '_' },
@@ -808,7 +809,7 @@ const struct kernel_param_ops param_ops_dyndbg_classes = {
 };
 EXPORT_SYMBOL(param_ops_dyndbg_classes);
 
-#define PREFIX_SIZE 64
+#define PREFIX_SIZE 128
 
 static int remaining(int wrote)
 {
@@ -836,6 +837,9 @@ static char *__dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
        if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
                pos += snprintf(buf + pos, remaining(pos), "%s:",
                                desc->function);
+       if (desc->flags & _DPRINTK_FLAGS_INCL_SOURCENAME)
+               pos += snprintf(buf + pos, remaining(pos), "%s:",
+                               trim_prefix(desc->filename));
        if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
                pos += snprintf(buf + pos, remaining(pos), "%d:",
                                desc->lineno);
index f8deae4..44bbf80 100644 (file)
@@ -51,9 +51,9 @@ enum autochan {
  * Has the side effect of filling the channels[i].location values used
  * in processing the buffer output.
  **/
-static int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
+static unsigned int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
 {
-       int bytes = 0;
+       unsigned int bytes = 0;
        int i = 0;
 
        while (i < num_channels) {
@@ -348,7 +348,7 @@ int main(int argc, char **argv)
        ssize_t read_size;
        int dev_num = -1, trig_num = -1;
        char *buffer_access = NULL;
-       int scan_size;
+       unsigned int scan_size;
        int noevents = 0;
        int notrigger = 0;
        char *dummy;
@@ -674,7 +674,16 @@ int main(int argc, char **argv)
        }
 
        scan_size = size_from_channelarray(channels, num_channels);
-       data = malloc(scan_size * buf_len);
+
+       size_t total_buf_len = scan_size * buf_len;
+
+       if (scan_size > 0 && total_buf_len / scan_size != buf_len) {
+               ret = -EFAULT;
+               perror("Integer overflow happened when calculate scan_size * buf_len");
+               goto error;
+       }
+
+       data = malloc(total_buf_len);
        if (!data) {
                ret = -ENOMEM;
                goto error;