summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/stable/sysfs-bus-mhi13
-rw-r--r--Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x2
-rw-r--r--Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc2
-rw-r--r--Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm2
-rw-r--r--Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt (renamed from Documentation/ABI/testing/sysfs-devices-hisi_ptt)12
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio3
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-ad9739a19
-rw-r--r--Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml34
-rw-r--r--Documentation/devicetree/bindings/fpga/xlnx,fpga-selectmap.yaml86
-rw-r--r--Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml2
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml279
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml213
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml5
-rw-r--r--Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml9
-rw-r--r--Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml95
-rw-r--r--Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml62
-rw-r--r--Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml12
-rw-r--r--Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml5
-rw-r--r--Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml2
-rw-r--r--Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml17
-rw-r--r--Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml20
-rw-r--r--Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml44
-rw-r--r--Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml4
-rw-r--r--Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml4
-rw-r--r--Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml2
-rw-r--r--Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml17
-rw-r--r--Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.yaml1
-rw-r--r--Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml136
-rw-r--r--Documentation/driver-api/fpga/fpga-bridge.rst7
-rw-r--r--Documentation/driver-api/fpga/fpga-mgr.rst34
-rw-r--r--Documentation/driver-api/fpga/fpga-region.rst13
-rw-r--r--Documentation/iio/ad7944.rst156
-rw-r--r--Documentation/iio/adis16475.rst8
-rw-r--r--Documentation/iio/index.rst1
-rw-r--r--Documentation/trace/hisi-ptt.rst4
-rw-r--r--Documentation/userspace-api/ioctl/ioctl-number.rst2
-rw-r--r--MAINTAINERS60
-rw-r--r--drivers/accessibility/speakup/devsynth.c59
-rw-r--r--drivers/accessibility/speakup/speakup.h2
-rw-r--r--drivers/accessibility/speakup/synth.c92
-rw-r--r--drivers/acpi/arm64/amba.c8
-rw-r--r--drivers/android/binder.c2
-rw-r--r--drivers/android/binder_internal.h2
-rw-r--r--drivers/base/property.c14
-rw-r--r--drivers/bus/mhi/host/init.c41
-rw-r--r--drivers/bus/mhi/host/main.c16
-rw-r--r--drivers/bus/mhi/host/pci_generic.c45
-rw-r--r--drivers/cdx/controller/cdx_controller.c6
-rw-r--r--drivers/char/mem.c6
-rw-r--r--drivers/char/powernv-op-panel.c5
-rw-r--r--drivers/char/ppdev.c15
-rw-r--r--drivers/char/sonypi.c6
-rw-r--r--drivers/comedi/drivers/cb_pcidas64.c5
-rw-r--r--drivers/counter/counter-core.c4
-rw-r--r--drivers/counter/stm32-timer-cnt.c461
-rw-r--r--drivers/counter/ti-ecap-capture.c8
-rw-r--r--drivers/counter/ti-eqep.c6
-rw-r--r--drivers/extcon/Kconfig3
-rw-r--r--drivers/extcon/extcon-adc-jack.c8
-rw-r--r--drivers/extcon/extcon-intel-cht-wc.c6
-rw-r--r--drivers/extcon/extcon-intel-mrfld.c26
-rw-r--r--drivers/extcon/extcon-max3355.c6
-rw-r--r--drivers/extcon/extcon-max77843.c6
-rw-r--r--drivers/extcon/extcon-rtk-type-c.c1
-rw-r--r--drivers/extcon/extcon-usb-gpio.c6
-rw-r--r--drivers/extcon/extcon-usbc-cros-ec.c6
-rw-r--r--drivers/fpga/Kconfig12
-rw-r--r--drivers/fpga/Makefile2
-rw-r--r--drivers/fpga/altera-cvp.c1
-rw-r--r--drivers/fpga/altera-ps-spi.c1
-rw-r--r--drivers/fpga/dfl-afu-main.c2
-rw-r--r--drivers/fpga/dfl-afu.h3
-rw-r--r--drivers/fpga/dfl-fme-main.c2
-rw-r--r--drivers/fpga/dfl-fme.h2
-rw-r--r--drivers/fpga/dfl.h5
-rw-r--r--drivers/fpga/fpga-bridge.c57
-rw-r--r--drivers/fpga/fpga-mgr.c82
-rw-r--r--drivers/fpga/fpga-region.c24
-rw-r--r--drivers/fpga/ice40-spi.c4
-rw-r--r--drivers/fpga/tests/fpga-bridge-test.c33
-rw-r--r--drivers/fpga/tests/fpga-mgr-test.c16
-rw-r--r--drivers/fpga/tests/fpga-region-test.c41
-rw-r--r--drivers/fpga/xilinx-core.c229
-rw-r--r--drivers/fpga/xilinx-core.h27
-rw-r--r--drivers/fpga/xilinx-selectmap.c95
-rw-r--r--drivers/fpga/xilinx-spi.c224
-rw-r--r--drivers/greybus/interface.c1
-rw-r--r--drivers/hv/Makefile2
-rw-r--r--drivers/hv/channel_mgmt.c15
-rw-r--r--drivers/hv/hv_fcopy.c427
-rw-r--r--drivers/hv/hv_util.c12
-rw-r--r--drivers/hv/hyperv_vmbus.h5
-rw-r--r--drivers/hwtracing/coresight/coresight-catu.c137
-rw-r--r--drivers/hwtracing/coresight/coresight-catu.h1
-rw-r--r--drivers/hwtracing/coresight/coresight-core.c29
-rw-r--r--drivers/hwtracing/coresight/coresight-cpu-debug.c137
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c29
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.h31
-rw-r--r--drivers/hwtracing/coresight/coresight-funnel.c87
-rw-r--r--drivers/hwtracing/coresight/coresight-priv.h10
-rw-r--r--drivers/hwtracing/coresight/coresight-replicator.c82
-rw-r--r--drivers/hwtracing/coresight/coresight-stm.c114
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-core.c181
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.h2
-rw-r--r--drivers/hwtracing/coresight/coresight-tpiu.c116
-rw-r--r--drivers/hwtracing/intel_th/acpi.c6
-rw-r--r--drivers/hwtracing/intel_th/core.c8
-rw-r--r--drivers/hwtracing/intel_th/gth.c8
-rw-r--r--drivers/hwtracing/intel_th/msu.c12
-rw-r--r--drivers/hwtracing/intel_th/pci.c30
-rw-r--r--drivers/hwtracing/intel_th/sth.c2
-rw-r--r--drivers/hwtracing/ptt/hisi_ptt.c1
-rw-r--r--drivers/hwtracing/stm/console.c1
-rw-r--r--drivers/hwtracing/stm/core.c19
-rw-r--r--drivers/hwtracing/stm/ftrace.c1
-rw-r--r--drivers/hwtracing/stm/heartbeat.c1
-rw-r--r--drivers/hwtracing/stm/p_basic.c3
-rw-r--r--drivers/hwtracing/stm/p_sys-t.c93
-rw-r--r--drivers/hwtracing/stm/stm.h2
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/accel/adxl345.h36
-rw-r--r--drivers/iio/accel/adxl345_core.c92
-rw-r--r--drivers/iio/accel/adxl345_i2c.c2
-rw-r--r--drivers/iio/accel/adxl345_spi.c10
-rw-r--r--drivers/iio/accel/adxl367.c2
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c44
-rw-r--r--drivers/iio/accel/fxls8962af-core.c10
-rw-r--r--drivers/iio/accel/kxcjk-1013.c80
-rw-r--r--drivers/iio/accel/mma8452.c6
-rw-r--r--drivers/iio/accel/mxc4005.c22
-rw-r--r--drivers/iio/adc/Kconfig27
-rw-r--r--drivers/iio/adc/Makefile2
-rw-r--r--drivers/iio/adc/ab8500-gpadc.c8
-rw-r--r--drivers/iio/adc/ad4130.c7
-rw-r--r--drivers/iio/adc/ad7124.c55
-rw-r--r--drivers/iio/adc/ad7173.c1180
-rw-r--r--drivers/iio/adc/ad7192.c38
-rw-r--r--drivers/iio/adc/ad7266.c1
-rw-r--r--drivers/iio/adc/ad7292.c13
-rw-r--r--drivers/iio/adc/ad7944.c690
-rw-r--r--drivers/iio/adc/ad799x.c7
-rw-r--r--drivers/iio/adc/ad9467.c374
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c29
-rw-r--r--drivers/iio/adc/adi-axi-adc.c147
-rw-r--r--drivers/iio/adc/exynos_adc.c16
-rw-r--r--drivers/iio/adc/fsl-imx25-gcq.c150
-rw-r--r--drivers/iio/adc/hx711.c5
-rw-r--r--drivers/iio/adc/intel_mrfld_adc.c12
-rw-r--r--drivers/iio/adc/max11410.c27
-rw-r--r--drivers/iio/adc/mcp3564.c16
-rw-r--r--drivers/iio/adc/mxs-lradc-adc.c1
-rw-r--r--drivers/iio/adc/pac1934.c86
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c7
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c21
-rw-r--r--drivers/iio/adc/rtq6056.c34
-rw-r--r--drivers/iio/adc/rzg2l_adc.c11
-rw-r--r--drivers/iio/adc/spear_adc.c25
-rw-r--r--drivers/iio/adc/stm32-adc.c71
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c12
-rw-r--r--drivers/iio/adc/ti-ads1015.c5
-rw-r--r--drivers/iio/adc/ti-ads131e08.c12
-rw-r--r--drivers/iio/adc/twl4030-madc.c19
-rw-r--r--drivers/iio/adc/twl6030-gpadc.c8
-rw-r--r--drivers/iio/addac/ad74413r.c10
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dma.c100
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c86
-rw-r--r--drivers/iio/common/inv_sensors/inv_sensors_timestamp.c33
-rw-r--r--drivers/iio/dac/Kconfig37
-rw-r--r--drivers/iio/dac/Makefile2
-rw-r--r--drivers/iio/dac/ad3552r.c110
-rw-r--r--drivers/iio/dac/ad5755.c24
-rw-r--r--drivers/iio/dac/ad5770r.c19
-rw-r--r--drivers/iio/dac/ad9739a.c464
-rw-r--r--drivers/iio/dac/adi-axi-dac.c635
-rw-r--r--drivers/iio/dac/ltc2688.c28
-rw-r--r--drivers/iio/dac/ti-dac5571.c3
-rw-r--r--drivers/iio/frequency/admfm2000.c24
-rw-r--r--drivers/iio/health/max30102.c2
-rw-r--r--drivers/iio/humidity/hdc3020.c111
-rw-r--r--drivers/iio/humidity/hts221_core.c2
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600.h37
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c75
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c31
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_core.c26
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c84
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c6
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c6
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_core.c542
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h36
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c19
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c83
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c2
-rw-r--r--drivers/iio/industrialio-acpi.c85
-rw-r--r--drivers/iio/industrialio-backend.c305
-rw-r--r--drivers/iio/industrialio-buffer.c122
-rw-r--r--drivers/iio/industrialio-core.c49
-rw-r--r--drivers/iio/industrialio-trigger.c71
-rw-r--r--drivers/iio/inkern.c263
-rw-r--r--drivers/iio/light/Kconfig12
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/apds9306.c1361
-rw-r--r--drivers/iio/light/st_uvis25_core.c2
-rw-r--r--drivers/iio/light/stk3310.c1
-rw-r--r--drivers/iio/pressure/bmp280-core.c16
-rw-r--r--drivers/iio/pressure/dps310.c138
-rw-r--r--drivers/iio/pressure/hsc030pa_spi.c7
-rw-r--r--drivers/iio/pressure/zpa2326.c10
-rw-r--r--drivers/iio/temperature/ltc2983.c142
-rw-r--r--drivers/iio/temperature/mcp9600.c3
-rw-r--r--drivers/interconnect/qcom/qcm2290.c2
-rw-r--r--drivers/interconnect/qcom/sm6115.c33
-rw-r--r--drivers/mcb/mcb-lpc.c6
-rw-r--r--drivers/misc/Kconfig31
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.c12
-rw-r--r--drivers/misc/ds1682.c37
-rw-r--r--drivers/misc/eeprom/at25.c1
-rw-r--r--drivers/misc/eeprom/eeprom_93xx46.c2
-rw-r--r--drivers/misc/mei/bus.c2
-rw-r--r--drivers/misc/mei/hw.h2
-rw-r--r--drivers/misc/ntsync.c249
-rw-r--r--drivers/misc/pvpanic/pvpanic.c43
-rw-r--r--drivers/misc/ti-st/st_kim.c4
-rw-r--r--drivers/misc/tifm_core.c2
-rw-r--r--drivers/misc/vmw_vmci/vmci_event.c6
-rw-r--r--drivers/misc/vmw_vmci/vmci_guest.c10
-rw-r--r--drivers/nvmem/core.c2
-rw-r--r--drivers/nvmem/layouts.c6
-rw-r--r--drivers/nvmem/layouts/onie-tlv.c1
-rw-r--r--drivers/nvmem/layouts/sl28vpd.c1
-rw-r--r--drivers/nvmem/lpc18xx_eeprom.c6
-rw-r--r--drivers/nvmem/meson-mx-efuse.c6
-rw-r--r--drivers/nvmem/sc27xx-efuse.c1
-rw-r--r--drivers/nvmem/sprd-efuse.c1
-rw-r--r--drivers/parport/parport_mfc3.c3
-rw-r--r--drivers/peci/core.c4
-rw-r--r--drivers/peci/device.c2
-rw-r--r--drivers/peci/internal.h6
-rw-r--r--drivers/slimbus/qcom-ctrl.c6
-rw-r--r--drivers/slimbus/qcom-ngd-ctrl.c14
-rw-r--r--drivers/spmi/hisi-spmi-controller.c1
-rw-r--r--drivers/spmi/spmi-pmic-arb.c964
-rw-r--r--drivers/spmi/spmi.c2
-rw-r--r--drivers/uio/Kconfig18
-rw-r--r--drivers/uio/Makefile1
-rw-r--r--drivers/uio/uio.c24
-rw-r--r--drivers/uio/uio_fsl_elbc_gpcm.c6
-rw-r--r--drivers/uio/uio_hv_generic.c19
-rw-r--r--drivers/uio/uio_pdrv_genirq.c10
-rw-r--r--drivers/uio/uio_pruss.c255
-rw-r--r--drivers/w1/masters/w1-gpio.c62
-rw-r--r--include/linux/coresight.h6
-rw-r--r--include/linux/counter.h7
-rw-r--r--include/linux/fpga/fpga-bridge.h10
-rw-r--r--include/linux/fpga/fpga-mgr.h26
-rw-r--r--include/linux/fpga/fpga-region.h13
-rw-r--r--include/linux/hyperv.h2
-rw-r--r--include/linux/iio/adc/ad_sigma_delta.h3
-rw-r--r--include/linux/iio/backend.h107
-rw-r--r--include/linux/iio/buffer-dma.h4
-rw-r--r--include/linux/iio/buffer-dmaengine.h24
-rw-r--r--include/linux/iio/common/inv_sensors_timestamp.h3
-rw-r--r--include/linux/iio/iio.h13
-rw-r--r--include/linux/mfd/stm32-timers.h13
-rw-r--r--include/linux/mhi.h11
-rw-r--r--include/linux/nvmem-provider.h5
-rw-r--r--include/linux/platform_data/uio_pruss.h18
-rw-r--r--include/linux/property.h22
-rw-r--r--include/linux/stm.h12
-rw-r--r--include/uapi/linux/ntsync.h23
-rw-r--r--include/uapi/misc/pvpanic.h7
-rw-r--r--samples/acrn/vm-sample.c4
-rwxr-xr-xscripts/spdxcheck.py3
-rw-r--r--tools/hv/Build3
-rw-r--r--tools/hv/Makefile14
-rw-r--r--tools/hv/hv_fcopy_daemon.c266
-rw-r--r--tools/hv/hv_fcopy_uio_daemon.c490
-rw-r--r--tools/hv/vmbus_bufring.c318
-rw-r--r--tools/hv/vmbus_bufring.h158
280 files changed, 12847 insertions, 3974 deletions
diff --git a/Documentation/ABI/stable/sysfs-bus-mhi b/Documentation/ABI/stable/sysfs-bus-mhi
index 1a47f9e0cc84..8b9698fa0beb 100644
--- a/Documentation/ABI/stable/sysfs-bus-mhi
+++ b/Documentation/ABI/stable/sysfs-bus-mhi
@@ -29,3 +29,16 @@ Description: Initiates a SoC reset on the MHI controller. A SoC reset is
This can be useful as a method of recovery if the device is
non-responsive, or as a means of loading new firmware as a
system administration task.
+
+What: /sys/bus/mhi/devices/.../trigger_edl
+Date: April 2024
+KernelVersion: 6.10
+Contact: mhi@lists.linux.dev
+Description: Writing a non-zero value to this file will force devices to
+ enter EDL (Emergency Download) mode. This entry only exists for
+ devices capable of entering the EDL mode using the standard EDL
+ triggering mechanism defined in the MHI spec v1.2. Once in EDL
+ mode, the flash programmer image can be downloaded to the
+ device to enter the flash programmer execution environment.
+ This can be useful if user wants to use QDL (Qualcomm Download,
+ which is used to download firmware over EDL) to update firmware.
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x
index 3acf7fc31659..271b57c571aa 100644
--- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x
@@ -22,7 +22,7 @@ Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Used in conjunction with @addr_idx. Specifies
characteristics about the address comparator being configure,
for example the access type, the kind of instruction to trace,
- processor contect ID to trigger on, etc. Individual fields in
+ processor context ID to trigger on, etc. Individual fields in
the access type register may vary on the version of the trace
entity.
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc
index 96aafa66b4a5..339cec3b2f1a 100644
--- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc
@@ -97,7 +97,7 @@ Date: August 2023
KernelVersion: 6.7
Contact: Anshuman Khandual <anshuman.khandual@arm.com>
Description: (Read) Shows all supported Coresight TMC-ETR buffer modes available
- for the users to configure explicitly. This file is avaialble only
+ for the users to configure explicitly. This file is available only
for TMC ETR devices.
What: /sys/bus/coresight/devices/<memory_map>.tmc/buf_mode_preferred
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm
index b4d0fc8d319d..bf710ea6e0ef 100644
--- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm
@@ -244,7 +244,7 @@ KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Read or write the status of timestamp upon all interface.
- Only value 0 and 1 can be written to this node. Set this node to 1 to requeset
+ Only value 0 and 1 can be written to this node. Set this node to 1 to request
timestamp to all trace packet.
Accepts only one of the 2 values - 0 or 1.
0 : Disable the timestamp of all trace packets.
diff --git a/Documentation/ABI/testing/sysfs-devices-hisi_ptt b/Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt
index d7e206b4901c..1119766564d7 100644
--- a/Documentation/ABI/testing/sysfs-devices-hisi_ptt
+++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt
@@ -1,4 +1,4 @@
-What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune
+What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune
Date: October 2022
KernelVersion: 6.1
Contact: Yicong Yang <yangyicong@hisilicon.com>
@@ -8,7 +8,7 @@ Description: This directory contains files for tuning the PCIe link
See Documentation/trace/hisi-ptt.rst for more information.
-What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_cpl
+What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_cpl
Date: October 2022
KernelVersion: 6.1
Contact: Yicong Yang <yangyicong@hisilicon.com>
@@ -18,7 +18,7 @@ Description: (RW) Controls the weight of Tx completion TLPs, which influence
will return an error, and out of range values will be converted
to 2. The value indicates a probable level of the event.
-What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_np
+What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_np
Date: October 2022
KernelVersion: 6.1
Contact: Yicong Yang <yangyicong@hisilicon.com>
@@ -28,7 +28,7 @@ Description: (RW) Controls the weight of Tx non-posted TLPs, which influence
will return an error, and out of range values will be converted
to 2. The value indicates a probable level of the event.
-What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_p
+What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_p
Date: October 2022
KernelVersion: 6.1
Contact: Yicong Yang <yangyicong@hisilicon.com>
@@ -38,7 +38,7 @@ Description: (RW) Controls the weight of Tx posted TLPs, which influence the
will return an error, and out of range values will be converted
to 2. The value indicates a probable level of the event.
-What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/rx_alloc_buf_level
+What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/rx_alloc_buf_level
Date: October 2022
KernelVersion: 6.1
Contact: Yicong Yang <yangyicong@hisilicon.com>
@@ -49,7 +49,7 @@ Description: (RW) Control the allocated buffer watermark for inbound packets.
will return an error, and out of range values will be converted
to 2. The value indicates a probable level of the event.
-What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/tx_alloc_buf_level
+What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/tx_alloc_buf_level
Date: October 2022
KernelVersion: 6.1
Contact: Yicong Yang <yangyicong@hisilicon.com>
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 2e6d5ebfd3c7..7cee78ad4108 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -243,7 +243,8 @@ Description:
less measurements. Units after application of scale and offset
are milli degrees Celsius.
-What: /sys/bus/iio/devices/iio:deviceX/in_tempX_input
+What: /sys/bus/iio/devices/iio:deviceX/in_tempY_input
+What: /sys/bus/iio/devices/iio:deviceX/in_temp_input
KernelVersion: 2.6.38
Contact: linux-iio@vger.kernel.org
Description:
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
new file mode 100644
index 000000000000..ed59299e6f8d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
@@ -0,0 +1,19 @@
+What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
+KernelVersion: 6.9
+Contact: linux-iio@vger.kernel.org
+Description:
+ DAC operating mode. One of the following modes can be selected:
+
+ * normal: This is DAC normal mode.
+ * mixed-mode: In this mode the output is effectively chopped at
+ the DAC sample rate. This has the effect of
+ reducing the power of the fundamental signal while
+ increasing the power of the images centered around
+ the DAC sample rate, thus improving the output
+ power of these images.
+
+What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
+KernelVersion: 6.9
+Contact: linux-iio@vger.kernel.org
+Description:
+ Available operating modes.
diff --git a/Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml b/Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml
index ea3c5db6b52d..76163abed655 100644
--- a/Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml
+++ b/Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml
@@ -66,13 +66,11 @@ properties:
- const: apb_pclk
in-ports:
- type: object
description: |
Input connections from TPDM to TPDA
$ref: /schemas/graph.yaml#/properties/ports
out-ports:
- type: object
description: |
Output connections from the TPDA to legacy CoreSight trace bus.
$ref: /schemas/graph.yaml#/properties/ports
@@ -97,33 +95,31 @@ examples:
# minimum tpda definition.
- |
tpda@6004000 {
- compatible = "qcom,coresight-tpda", "arm,primecell";
- reg = <0x6004000 0x1000>;
+ compatible = "qcom,coresight-tpda", "arm,primecell";
+ reg = <0x6004000 0x1000>;
- clocks = <&aoss_qmp>;
- clock-names = "apb_pclk";
+ clocks = <&aoss_qmp>;
+ clock-names = "apb_pclk";
- in-ports {
- #address-cells = <1>;
- #size-cells = <0>;
+ in-ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
port@0 {
reg = <0>;
tpda_qdss_0_in_tpdm_dcc: endpoint {
- remote-endpoint =
- <&tpdm_dcc_out_tpda_qdss_0>;
- };
+ remote-endpoint = <&tpdm_dcc_out_tpda_qdss_0>;
+ };
};
};
- out-ports {
- port {
- tpda_qdss_out_funnel_in0: endpoint {
- remote-endpoint =
- <&funnel_in0_in_tpda_qdss>;
- };
+ out-ports {
+ port {
+ tpda_qdss_out_funnel_in0: endpoint {
+ remote-endpoint = <&funnel_in0_in_tpda_qdss>;
};
- };
+ };
+ };
};
...
diff --git a/Documentation/devicetree/bindings/fpga/xlnx,fpga-selectmap.yaml b/Documentation/devicetree/bindings/fpga/xlnx,fpga-selectmap.yaml
new file mode 100644
index 000000000000..05775746fd70
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/xlnx,fpga-selectmap.yaml
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/fpga/xlnx,fpga-selectmap.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xilinx SelectMAP FPGA interface
+
+maintainers:
+ - Charles Perry <charles.perry@savoirfairelinux.com>
+
+description: |
+ Xilinx 7 Series FPGAs support a method of loading the bitstream over a
+ parallel port named the SelectMAP interface in the documentation. Only
+ the x8 mode is supported where data is loaded at one byte per rising edge of
+ the clock, with the MSB of each byte presented to the D0 pin.
+
+ Datasheets:
+ https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
+
+allOf:
+ - $ref: /schemas/memory-controllers/mc-peripheral-props.yaml#
+
+properties:
+ compatible:
+ enum:
+ - xlnx,fpga-xc7s-selectmap
+ - xlnx,fpga-xc7a-selectmap
+ - xlnx,fpga-xc7k-selectmap
+ - xlnx,fpga-xc7v-selectmap
+
+ reg:
+ description:
+ At least 1 byte of memory mapped IO
+ maxItems: 1
+
+ prog-gpios:
+ description:
+ config pin (referred to as PROGRAM_B in the manual)
+ maxItems: 1
+
+ done-gpios:
+ description:
+ config status pin (referred to as DONE in the manual)
+ maxItems: 1
+
+ init-gpios:
+ description:
+ initialization status and configuration error pin
+ (referred to as INIT_B in the manual)
+ maxItems: 1
+
+ csi-gpios:
+ description:
+ chip select pin (referred to as CSI_B in the manual)
+ Optional gpio for if the bus controller does not provide a chip select.
+ maxItems: 1
+
+ rdwr-gpios:
+ description:
+ read/write select pin (referred to as RDWR_B in the manual)
+ Optional gpio for if the bus controller does not provide this pin.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - prog-gpios
+ - done-gpios
+ - init-gpios
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ fpga-mgr@8000000 {
+ compatible = "xlnx,fpga-xc7s-selectmap";
+ reg = <0x8000000 0x4>;
+ prog-gpios = <&gpio5 5 GPIO_ACTIVE_LOW>;
+ init-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
+ done-gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>;
+ csi-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
+ rdwr-gpios = <&gpio3 10 GPIO_ACTIVE_LOW>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
index 07cacc3f6a97..280ed479ef5a 100644
--- a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
+++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
@@ -32,6 +32,8 @@ properties:
spi-cpol: true
+ spi-3wire: true
+
interrupts:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
new file mode 100644
index 000000000000..ea6cfcd0aff4
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
@@ -0,0 +1,279 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Analog Devices Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad7173.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD7173 ADC
+
+maintainers:
+ - Ceclan Dumitru <dumitru.ceclan@analog.com>
+
+description: |
+ Analog Devices AD717x ADC's:
+ The AD717x family offer a complete integrated Sigma-Delta ADC solution which
+ can be used in high precision, low noise single channel applications
+ (Life Science measurements) or higher speed multiplexed applications
+ (Factory Automation PLC Input modules). The Sigma-Delta ADC is intended
+ primarily for measurement of signals close to DC but also delivers
+ outstanding performance with input bandwidths out to ~10kHz.
+
+ Datasheets for supported chips:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-2.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-4.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7173-8.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-2.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-8.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7176-2.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7177-2.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,ad7172-2
+ - adi,ad7172-4
+ - adi,ad7173-8
+ - adi,ad7175-2
+ - adi,ad7175-8
+ - adi,ad7176-2
+ - adi,ad7177-2
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 1
+ items:
+ - description: |
+ Ready: multiplexed with SPI data out. While SPI CS is low,
+ can be used to indicate the completion of a conversion.
+
+ - description: |
+ Error: The three error bits in the status register (ADC_ERROR, CRC_ERROR,
+ and REG_ERROR) are OR'ed, inverted, and mapped to the ERROR pin.
+ Therefore, the ERROR pin indicates that an error has occurred.
+
+ interrupt-names:
+ minItems: 1
+ items:
+ - const: rdy
+ - const: err
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ spi-max-frequency:
+ maximum: 20000000
+
+ gpio-controller:
+ description: Marks the device node as a GPIO controller.
+
+ '#gpio-cells':
+ const: 2
+ description:
+ The first cell is the GPIO number and the second cell specifies
+ GPIO flags, as defined in <dt-bindings/gpio/gpio.h>.
+
+ vref-supply:
+ description: |
+ Differential external reference supply used for conversion. The reference
+ voltage (Vref) specified here must be the voltage difference between the
+ REF+ and REF- pins: Vref = (REF+) - (REF-).
+
+ vref2-supply:
+ description: |
+ Differential external reference supply used for conversion. The reference
+ voltage (Vref2) specified here must be the voltage difference between the
+ REF2+ and REF2- pins: Vref2 = (REF2+) - (REF2-).
+
+ avdd-supply:
+ description: Avdd supply, can be used as reference for conversion.
+ This supply is referenced to AVSS, voltage specified here
+ represents (AVDD1 - AVSS).
+
+ avdd2-supply:
+ description: Avdd2 supply, used as the input to the internal voltage regulator.
+ This supply is referenced to AVSS, voltage specified here
+ represents (AVDD2 - AVSS).
+
+ iovdd-supply:
+ description: iovdd supply, used for the chip digital interface.
+
+ clocks:
+ maxItems: 1
+ description: |
+ Optional external clock source. Can include one clock source: external
+ clock or external crystal.
+
+ clock-names:
+ enum:
+ - ext-clk
+ - xtal
+
+ '#clock-cells':
+ const: 0
+
+patternProperties:
+ "^channel@[0-9a-f]$":
+ type: object
+ $ref: adc.yaml
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ minimum: 0
+ maximum: 15
+
+ diff-channels:
+ items:
+ minimum: 0
+ maximum: 31
+
+ adi,reference-select:
+ description: |
+ Select the reference source to use when converting on
+ the specific channel. Valid values are:
+ vref : REF+ /REF−
+ vref2 : REF2+ /REF2−
+ refout-avss: REFOUT/AVSS (Internal reference)
+ avdd : AVDD /AVSS
+
+ External reference ref2 only available on ad7173-8 and ad7172-4.
+ Internal reference refout-avss not available on ad7172-4.
+
+ If not specified, internal reference used (if available).
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - vref
+ - vref2
+ - refout-avss
+ - avdd
+ default: refout-avss
+
+ required:
+ - reg
+ - diff-channels
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+ # Only ad7172-4, ad7173-8 and ad7175-8 support vref2
+ # Other models have [0-3] channel registers
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ enum:
+ - adi,ad7172-4
+ - adi,ad7173-8
+ - adi,ad7175-8
+ then:
+ properties:
+ vref2-supply: false
+ patternProperties:
+ "^channel@[0-9a-f]$":
+ properties:
+ adi,reference-select:
+ enum:
+ - vref
+ - refout-avss
+ - avdd
+ reg:
+ maximum: 3
+
+ # Model ad7172-4 does not support internal reference
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: adi,ad7172-4
+ then:
+ patternProperties:
+ "^channel@[0-9a-f]$":
+ properties:
+ reg:
+ maximum: 7
+ adi,reference-select:
+ enum:
+ - vref
+ - vref2
+ - avdd
+ required:
+ - adi,reference-select
+
+ - if:
+ anyOf:
+ - required: [clock-names]
+ - required: [clocks]
+ then:
+ properties:
+ '#clock-cells': false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "adi,ad7173-8";
+ reg = <0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-names = "rdy";
+ interrupt-parent = <&gpio>;
+ spi-max-frequency = <5000000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ #clock-cells = <0>;
+
+ vref-supply = <&dummy_regulator>;
+
+ channel@0 {
+ reg = <0>;
+ bipolar;
+ diff-channels = <0 1>;
+ adi,reference-select = "vref";
+ };
+
+ channel@1 {
+ reg = <1>;
+ diff-channels = <2 3>;
+ };
+
+ channel@2 {
+ reg = <2>;
+ bipolar;
+ diff-channels = <4 5>;
+ };
+
+ channel@3 {
+ reg = <3>;
+ bipolar;
+ diff-channels = <6 7>;
+ };
+
+ channel@4 {
+ reg = <4>;
+ diff-channels = <8 9>;
+ adi,reference-select = "avdd";
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
new file mode 100644
index 000000000000..d17d184842d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
@@ -0,0 +1,213 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad7944.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices PulSAR LFCSP Analog to Digital Converters
+
+maintainers:
+ - Michael Hennerich <Michael.Hennerich@analog.com>
+ - Nuno Sá <nuno.sa@analog.com>
+
+description: |
+ A family of pin-compatible single channel differential analog to digital
+ converters with SPI support in a LFCSP package.
+
+ * https://www.analog.com/en/products/ad7944.html
+ * https://www.analog.com/en/products/ad7985.html
+ * https://www.analog.com/en/products/ad7986.html
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ enum:
+ - adi,ad7944
+ - adi,ad7985
+ - adi,ad7986
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 111111111
+
+ spi-cpol: true
+ spi-cpha: true
+
+ adi,spi-mode:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [ single, chain ]
+ description: |
+ This property indicates the SPI wiring configuration.
+
+ When this property is omitted, it is assumed that the device is using what
+ the datasheet calls "4-wire mode". This is the conventional SPI mode used
+ when there are multiple devices on the same bus. In this mode, the CNV
+ line is used to initiate the conversion and the SDI line is connected to
+ CS on the SPI controller.
+
+ When this property is present, it indicates that the device is using one
+ of the following alternative wiring configurations:
+
+ * single: The datasheet calls this "3-wire mode". (NOTE: The datasheet's
+ definition of 3-wire mode is NOT at all related to the standard
+ spi-3wire property!) This mode is often used when the ADC is the only
+ device on the bus. In this mode, SDI is tied to VIO, and the CNV line
+ can be connected to the CS line of the SPI controller or to a GPIO, in
+ which case the CS line of the controller is unused.
+ * chain: The datasheet calls this "chain mode". This mode is used to save
+ on wiring when multiple ADCs are used. In this mode, the SDI line of
+ one chip is tied to the SDO of the next chip in the chain and the SDI of
+ the last chip in the chain is tied to GND. Only the first chip in the
+ chain is connected to the SPI bus. The CNV line of all chips are tied
+ together. The CS line of the SPI controller can be used as the CNV line
+ only if it is active high.
+
+ '#daisy-chained-devices': true
+
+ avdd-supply:
+ description: A 2.5V supply that powers the analog circuitry.
+
+ dvdd-supply:
+ description: A 2.5V supply that powers the digital circuitry.
+
+ vio-supply:
+ description:
+ A 1.8V to 2.7V supply for the digital inputs and outputs.
+
+ bvdd-supply:
+ description:
+ A voltage supply for the buffered power. When using an external reference
+ without an internal buffer (PDREF high, REFIN low), this should be
+ connected to the same supply as ref-supply. Otherwise, when using an
+ internal reference or an external reference with an internal buffer, this
+ is connected to a 5V supply.
+
+ ref-supply:
+ description:
+ Voltage regulator for the external reference voltage (REF). This property
+ is omitted when using an internal reference.
+
+ refin-supply:
+ description:
+ Voltage regulator for the reference buffer input (REFIN). When using an
+ external buffer with internal reference, this should be connected to a
+ 1.2V external reference voltage supply. Otherwise, this property is
+ omitted.
+
+ cnv-gpios:
+ description:
+ The Convert Input (CNV). This input has multiple functions. It initiates
+ the conversions and selects the SPI mode of the device (chain or CS). In
+ 'single' mode, this property is omitted if the CNV pin is connected to the
+ CS line of the SPI controller.
+ maxItems: 1
+
+ turbo-gpios:
+ description:
+ GPIO connected to the TURBO line. If omitted, it is assumed that the TURBO
+ line is hard-wired and the state is determined by the adi,always-turbo
+ property.
+ maxItems: 1
+
+ adi,always-turbo:
+ type: boolean
+ description:
+ When present, this property indicates that the TURBO line is hard-wired
+ and the state is always high. If neither this property nor turbo-gpios is
+ present, the TURBO line is assumed to be hard-wired and the state is
+ always low.
+
+ interrupts:
+ description:
+ The SDO pin can also function as a busy indicator. This node should be
+ connected to an interrupt that is triggered when the SDO line goes low
+ while the SDI line is high and the CNV line is low ('single' mode) or the
+ SDI line is low and the CNV line is high ('multi' mode); or when the SDO
+ line goes high while the SDI and CNV lines are high (chain mode),
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - dvdd-supply
+ - vio-supply
+ - bvdd-supply
+
+allOf:
+ # ref-supply and refin-supply are mutually exclusive (neither is also valid)
+ - if:
+ required:
+ - ref-supply
+ then:
+ properties:
+ refin-supply: false
+ - if:
+ required:
+ - refin-supply
+ then:
+ properties:
+ ref-supply: false
+ # in '4-wire' mode, cnv-gpios is required, for other modes it is optional
+ - if:
+ not:
+ required:
+ - adi,spi-mode
+ then:
+ required:
+ - cnv-gpios
+ # chain mode has lower SCLK max rate and doesn't work when TURBO is enabled
+ - if:
+ required:
+ - adi,spi-mode
+ properties:
+ adi,spi-mode:
+ const: chain
+ then:
+ properties:
+ spi-max-frequency:
+ maximum: 90909090
+ adi,always-turbo: false
+ required:
+ - '#daisy-chained-devices'
+ else:
+ properties:
+ '#daisy-chained-devices': false
+ # turbo-gpios and adi,always-turbo are mutually exclusive
+ - if:
+ required:
+ - turbo-gpios
+ then:
+ properties:
+ adi,always-turbo: false
+ - if:
+ required:
+ - adi,always-turbo
+ then:
+ properties:
+ turbo-gpios: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adc@0 {
+ compatible = "adi,ad7944";
+ reg = <0>;
+ spi-cpha;
+ spi-max-frequency = <111111111>;
+ avdd-supply = <&supply_2_5V>;
+ dvdd-supply = <&supply_2_5V>;
+ vio-supply = <&supply_1_8V>;
+ bvdd-supply = <&supply_5V>;
+ cnv-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+ turbo-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml
index 3d49d21ad33d..e1f450b80db2 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml
@@ -28,6 +28,9 @@ properties:
reg:
maxItems: 1
+ clocks:
+ maxItems: 1
+
dmas:
maxItems: 1
@@ -48,6 +51,7 @@ required:
- compatible
- dmas
- reg
+ - clocks
additionalProperties: false
@@ -58,6 +62,7 @@ examples:
reg = <0x44a00000 0x10000>;
dmas = <&rx_dma 0>;
dma-names = "rx";
+ clocks = <&axi_clk>;
#io-backend-cells = <0>;
};
...
diff --git a/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml b/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml
index 7ef46c90ebc8..da605a051b94 100644
--- a/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml
@@ -11,8 +11,13 @@ maintainers:
properties:
compatible:
- enum:
- - allwinner,sun20i-d1-gpadc
+ oneOf:
+ - enum:
+ - allwinner,sun20i-d1-gpadc
+ - items:
+ - enum:
+ - allwinner,sun50i-h616-gpadc
+ - const: allwinner,sun20i-d1-gpadc
"#io-channel-cells":
const: 1
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
new file mode 100644
index 000000000000..c0b36476113a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD9739A RF DAC
+
+maintainers:
+ - Dragos Bogdan <dragos.bogdan@analog.com>
+ - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+ The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
+ of synthesizing wideband signals from dc up to 3 GHz.
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,ad9739a
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ vdd-3p3-supply:
+ description: 3.3V Digital input supply.
+
+ vdd-supply:
+ description: 1.8V Digital input supply.
+
+ vdda-supply:
+ description: 3.3V Analog input supply.
+
+ vddc-supply:
+ description: 1.8V Clock input supply.
+
+ vref-supply:
+ description: Input/Output reference supply.
+
+ io-backends:
+ maxItems: 1
+
+ adi,full-scale-microamp:
+ description: This property represents the DAC full scale current.
+ minimum: 8580
+ maximum: 31700
+ default: 20000
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - io-backends
+ - vdd-3p3-supply
+ - vdd-supply
+ - vdda-supply
+ - vddc-supply
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "adi,ad9739a";
+ reg = <0>;
+
+ clocks = <&dac_clk>;
+
+ io-backends = <&iio_backend>;
+
+ vdd-3p3-supply = <&vdd_3_3>;
+ vdd-supply = <&vdd>;
+ vdda-supply = <&vdd_3_3>;
+ vddc-supply = <&vdd>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
new file mode 100644
index 000000000000..a55e9bfc66d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AXI DAC IP core
+
+maintainers:
+ - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+ Analog Devices Generic AXI DAC IP core for interfacing a DAC device
+ with a high speed serial (JESD204B/C) or source synchronous parallel
+ interface (LVDS/CMOS).
+ Usually, some other interface type (i.e SPI) is used as a control
+ interface for the actual DAC, while this IP core will interface
+ to the data-lines of the DAC and handle the streaming of data from
+ memory via DMA into the DAC.
+
+ https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+
+properties:
+ compatible:
+ enum:
+ - adi,axi-dac-9.1.b
+
+ reg:
+ maxItems: 1
+
+ dmas:
+ maxItems: 1
+
+ dma-names:
+ items:
+ - const: tx
+
+ clocks:
+ maxItems: 1
+
+ '#io-backend-cells':
+ const: 0
+
+required:
+ - compatible
+ - dmas
+ - reg
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ dac@44a00000 {
+ compatible = "adi,axi-dac-9.1.b";
+ reg = <0x44a00000 0x10000>;
+ dmas = <&tx_dma 0>;
+ dma-names = "tx";
+ #io-backend-cells = <0>;
+ clocks = <&axi_clk>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml
index 79da0323c327..e59db861e2eb 100644
--- a/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml
@@ -21,6 +21,7 @@ properties:
- ti,dac5573
- ti,dac6573
- ti,dac7573
+ - ti,dac081c081
- ti,dac121c081
reg:
diff --git a/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml b/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml
index eed0df9d3a23..205d352ab467 100644
--- a/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml
+++ b/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml
@@ -4,16 +4,20 @@
$id: http://devicetree.org/schemas/iio/health/maxim,max30102.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Maxim MAX30102 heart rate and pulse oximeter and MAX30105 particle-sensor
+title: Maxim MAX30101/2 heart rate and pulse oximeter and MAX30105 particle-sensor
maintainers:
- Matt Ranostay <matt.ranostay@konsulko.com>
properties:
compatible:
- enum:
- - maxim,max30102
- - maxim,max30105
+ oneOf:
+ - enum:
+ - maxim,max30102
+ - maxim,max30105
+ - items:
+ - const: maxim,max30101
+ - const: maxim,max30105
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml b/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml
index 8b5dedd1a598..b375d307513f 100644
--- a/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml
+++ b/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml
@@ -34,6 +34,9 @@ properties:
reg:
maxItems: 1
+ reset-gpios:
+ maxItems: 1
+
required:
- compatible
- reg
@@ -43,6 +46,7 @@ additionalProperties: false
examples:
- |
+ #include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@@ -54,5 +58,6 @@ examples:
vdd-supply = <&vcc_3v3>;
interrupt-parent = <&gpio3>;
interrupts = <23 IRQ_TYPE_EDGE_RISING>;
+ reset-gpios = <&gpio3 27 GPIO_ACTIVE_LOW>;
};
};
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 7cd05bcbee31..3769f8e8e98c 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -32,6 +32,8 @@ properties:
- invensense,icm42605
- invensense,icm42622
- invensense,icm42631
+ - invensense,icm42686
+ - invensense,icm42688
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml
index 297b8a1a7ffb..587ff2bced2d 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml
@@ -62,14 +62,15 @@ properties:
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
- not:
- properties:
- compatible:
- contains:
- enum:
- - invensense,mpu9150
- - invensense,mpu9250
- - invensense,mpu9255
+ properties:
+ compatible:
+ contains:
+ enum:
+ - invensense,iam20680
+ - invensense,icm20602
+ - invensense,icm20608
+ - invensense,icm20609
+ - invensense,icm20689
then:
properties:
i2c-gate: false
diff --git a/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml b/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml
index 206af44f2c43..b750096530bc 100644
--- a/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml
+++ b/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml
@@ -4,17 +4,22 @@
$id: http://devicetree.org/schemas/iio/light/avago,apds9300.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Avago APDS9300 ambient light sensor
+title: Avago Gesture/RGB/ALS/Proximity sensors
maintainers:
- - Jonathan Cameron <jic23@kernel.org>
+ - Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
description: |
- Datasheet at https://www.avagotech.com/docs/AV02-1077EN
+ Datasheet: https://www.avagotech.com/docs/AV02-1077EN
+ Datasheet: https://www.avagotech.com/docs/AV02-4191EN
+ Datasheet: https://www.avagotech.com/docs/AV02-4755EN
properties:
compatible:
- const: avago,apds9300
+ enum:
+ - avago,apds9300
+ - avago,apds9306
+ - avago,apds9960
reg:
maxItems: 1
@@ -22,6 +27,8 @@ properties:
interrupts:
maxItems: 1
+ vdd-supply: true
+
additionalProperties: false
required:
@@ -30,6 +37,8 @@ required:
examples:
- |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
i2c {
#address-cells = <1>;
#size-cells = <0>;
@@ -38,7 +47,8 @@ examples:
compatible = "avago,apds9300";
reg = <0x39>;
interrupt-parent = <&gpio2>;
- interrupts = <29 8>;
+ interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
+ vdd-supply = <&regulator_3v3>;
};
};
...
diff --git a/Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml b/Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml
deleted file mode 100644
index f06e0fda5629..000000000000
--- a/Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/iio/light/avago,apds9960.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Avago APDS9960 gesture/RGB/ALS/proximity sensor
-
-maintainers:
- - Matt Ranostay <matt.ranostay@konsulko.com>
-
-description: |
- Datasheet at https://www.avagotech.com/docs/AV02-4191EN
-
-properties:
- compatible:
- const: avago,apds9960
-
- reg:
- maxItems: 1
-
- interrupts:
- maxItems: 1
-
-additionalProperties: false
-
-required:
- - compatible
- - reg
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- light-sensor@39 {
- compatible = "avago,apds9960";
- reg = <0x39>;
- interrupt-parent = <&gpio1>;
- interrupts = <16 1>;
- };
- };
-...
diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
index dbb85135fd66..312febeeb3bb 100644
--- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
+++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
@@ -57,6 +57,8 @@ properties:
interrupts:
maxItems: 1
+ vdd-supply: true
+
adi,mux-delay-config-us:
description: |
Extra delay prior to each conversion, in addition to the internal 1ms
@@ -460,6 +462,7 @@ required:
- compatible
- reg
- interrupts
+ - vdd-supply
additionalProperties: false
@@ -489,6 +492,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
+ vdd-supply = <&supply>;
interrupts = <20 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio>;
diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
index 8c8f05d9eaf1..80845c722ae4 100644
--- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
+++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
@@ -34,6 +34,7 @@ properties:
- qcom,qcs404-qfprom
- qcom,sc7180-qfprom
- qcom,sc7280-qfprom
+ - qcom,sc8280xp-qfprom
- qcom,sdm630-qfprom
- qcom,sdm670-qfprom
- qcom,sdm845-qfprom
@@ -42,6 +43,9 @@ properties:
- qcom,sm6375-qfprom
- qcom,sm8150-qfprom
- qcom,sm8250-qfprom
+ - qcom,sm8450-qfprom
+ - qcom,sm8550-qfprom
+ - qcom,sm8650-qfprom
- const: qcom,qfprom
reg:
diff --git a/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml b/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml
index 068bedf5dbc9..5d7be0b34536 100644
--- a/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml
+++ b/Documentation/devicetree/bindings/nvmem/qcom,spmi-sdam.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. SPMI SDAM
maintainers:
- - Shyam Kumar Thella <sthella@codeaurora.org>
+ - David Collins <quic_collinsd@quicinc.com>
description: |
The SDAM provides scratch register space for the PMIC clients. This
diff --git a/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml
index f882903769f9..3ccf35de3719 100644
--- a/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml
+++ b/Documentation/devicetree/bindings/spmi/hisilicon,hisi-spmi-controller.yaml
@@ -14,7 +14,7 @@ description: |
It is a MIPI System Power Management (SPMI) controller.
The PMIC part is provided by
- ./Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml.
+ Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml.
allOf:
- $ref: spmi.yaml#
@@ -48,26 +48,23 @@ patternProperties:
PMIC properties, which are specific to the used SPMI PMIC device(s).
When used in combination with HiSilicon 6421v600, the properties
are documented at
- drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml.
+ Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml
unevaluatedProperties: false
examples:
- |
- bus {
- #address-cells = <2>;
- #size-cells = <2>;
+ #include <dt-bindings/spmi/spmi.h>
- spmi: spmi@fff24000 {
+ spmi@fff24000 {
compatible = "hisilicon,kirin970-spmi-controller";
+ reg = <0xfff24000 0x1000>;
#address-cells = <2>;
#size-cells = <0>;
- reg = <0x0 0xfff24000 0x0 0x1000>;
hisilicon,spmi-channel = <2>;
pmic@0 {
- reg = <0 0>;
- /* pmic properties */
+ reg = <0 SPMI_USID>;
+ /* pmic properties */
};
- };
};
diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.yaml b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.yaml
index f983b4af6db9..51daf1b847a9 100644
--- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.yaml
+++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.yaml
@@ -92,6 +92,7 @@ properties:
description: >
SPMI bus instance. only applicable to PMIC arbiter version 7 and beyond.
Supported values, 0 = primary bus, 1 = secondary bus
+ deprecated: true
required:
- compatible
diff --git a/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml b/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml
new file mode 100644
index 000000000000..a28b70fb330a
--- /dev/null
+++ b/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spmi/qcom,x1e80100-spmi-pmic-arb.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm X1E80100 SPMI Controller (PMIC Arbiter v7)
+
+maintainers:
+ - Stephen Boyd <sboyd@kernel.org>
+
+description: |
+ The X1E80100 SPMI PMIC Arbiter implements HW version 7 and it's an SPMI
+ controller with wrapping arbitration logic to allow for multiple on-chip
+ devices to control up to 2 SPMI separate buses.
+
+ The PMIC Arbiter can also act as an interrupt controller, providing interrupts
+ to slave devices.
+
+properties:
+ compatible:
+ const: qcom,x1e80100-spmi-pmic-arb
+
+ reg:
+ items:
+ - description: core registers
+ - description: tx-channel per virtual slave registers
+ - description: rx-channel (called observer) per virtual slave registers
+
+ reg-names:
+ items:
+ - const: core
+ - const: chnls
+ - const: obsrvr
+
+ ranges: true
+
+ '#address-cells':
+ const: 2
+
+ '#size-cells':
+ const: 2
+
+ qcom,ee:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 5
+ description: >
+ indicates the active Execution Environment identifier
+
+ qcom,channel:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 5
+ description: >
+ which of the PMIC Arb provided channels to use for accesses
+
+patternProperties:
+ "^spmi@[a-f0-9]+$":
+ type: object
+ $ref: /schemas/spmi/spmi.yaml
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ items:
+ - description: configuration registers
+ - description: interrupt controller registers
+
+ reg-names:
+ items:
+ - const: cnfg
+ - const: intr
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ const: periph_irq
+
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 4
+ description: |
+ cell 1: slave ID for the requested interrupt (0-15)
+ cell 2: peripheral ID for requested interrupt (0-255)
+ cell 3: the requested peripheral interrupt (0-7)
+ cell 4: interrupt flags indicating level-sense information,
+ as defined in dt-bindings/interrupt-controller/irq.h
+
+required:
+ - compatible
+ - reg-names
+ - qcom,ee
+ - qcom,channel
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ spmi: arbiter@c400000 {
+ compatible = "qcom,x1e80100-spmi-pmic-arb";
+ reg = <0 0x0c400000 0 0x3000>,
+ <0 0x0c500000 0 0x4000000>,
+ <0 0x0c440000 0 0x80000>;
+ reg-names = "core", "chnls", "obsrvr";
+
+ qcom,ee = <0>;
+ qcom,channel = <0>;
+
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ spmi_bus0: spmi@c42d000 {
+ reg = <0 0x0c42d000 0 0x4000>,
+ <0 0x0c4c0000 0 0x10000>;
+ reg-names = "cnfg", "intr";
+
+ interrupt-names = "periph_irq";
+ interrupts-extended = <&pdc 1 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-controller;
+ #interrupt-cells = <4>;
+
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+ };
+ };
diff --git a/Documentation/driver-api/fpga/fpga-bridge.rst b/Documentation/driver-api/fpga/fpga-bridge.rst
index 604208534095..833f68fb0700 100644
--- a/Documentation/driver-api/fpga/fpga-bridge.rst
+++ b/Documentation/driver-api/fpga/fpga-bridge.rst
@@ -6,9 +6,12 @@ API to implement a new FPGA bridge
* struct fpga_bridge - The FPGA Bridge structure
* struct fpga_bridge_ops - Low level Bridge driver ops
-* fpga_bridge_register() - Create and register a bridge
+* __fpga_bridge_register() - Create and register a bridge
* fpga_bridge_unregister() - Unregister a bridge
+The helper macro ``fpga_bridge_register()`` automatically sets
+the module that registers the FPGA bridge as the owner.
+
.. kernel-doc:: include/linux/fpga/fpga-bridge.h
:functions: fpga_bridge
@@ -16,7 +19,7 @@ API to implement a new FPGA bridge
:functions: fpga_bridge_ops
.. kernel-doc:: drivers/fpga/fpga-bridge.c
- :functions: fpga_bridge_register
+ :functions: __fpga_bridge_register
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_unregister
diff --git a/Documentation/driver-api/fpga/fpga-mgr.rst b/Documentation/driver-api/fpga/fpga-mgr.rst
index 49c0a9512653..8d2b79f696c1 100644
--- a/Documentation/driver-api/fpga/fpga-mgr.rst
+++ b/Documentation/driver-api/fpga/fpga-mgr.rst
@@ -24,7 +24,8 @@ How to support a new FPGA device
--------------------------------
To add another FPGA manager, write a driver that implements a set of ops. The
-probe function calls fpga_mgr_register() or fpga_mgr_register_full(), such as::
+probe function calls ``fpga_mgr_register()`` or ``fpga_mgr_register_full()``,
+such as::
static const struct fpga_manager_ops socfpga_fpga_ops = {
.write_init = socfpga_fpga_ops_configure_init,
@@ -69,10 +70,11 @@ probe function calls fpga_mgr_register() or fpga_mgr_register_full(), such as::
}
Alternatively, the probe function could call one of the resource managed
-register functions, devm_fpga_mgr_register() or devm_fpga_mgr_register_full().
-When these functions are used, the parameter syntax is the same, but the call
-to fpga_mgr_unregister() should be removed. In the above example, the
-socfpga_fpga_remove() function would not be required.
+register functions, ``devm_fpga_mgr_register()`` or
+``devm_fpga_mgr_register_full()``. When these functions are used, the
+parameter syntax is the same, but the call to ``fpga_mgr_unregister()`` should be
+removed. In the above example, the ``socfpga_fpga_remove()`` function would not be
+required.
The ops will implement whatever device specific register writes are needed to
do the programming sequence for this particular FPGA. These ops return 0 for
@@ -125,15 +127,19 @@ API for implementing a new FPGA Manager driver
* struct fpga_manager - the FPGA manager struct
* struct fpga_manager_ops - Low level FPGA manager driver ops
* struct fpga_manager_info - Parameter structure for fpga_mgr_register_full()
-* fpga_mgr_register_full() - Create and register an FPGA manager using the
+* __fpga_mgr_register_full() - Create and register an FPGA manager using the
fpga_mgr_info structure to provide the full flexibility of options
-* fpga_mgr_register() - Create and register an FPGA manager using standard
+* __fpga_mgr_register() - Create and register an FPGA manager using standard
arguments
-* devm_fpga_mgr_register_full() - Resource managed version of
- fpga_mgr_register_full()
-* devm_fpga_mgr_register() - Resource managed version of fpga_mgr_register()
+* __devm_fpga_mgr_register_full() - Resource managed version of
+ __fpga_mgr_register_full()
+* __devm_fpga_mgr_register() - Resource managed version of __fpga_mgr_register()
* fpga_mgr_unregister() - Unregister an FPGA manager
+Helper macros ``fpga_mgr_register_full()``, ``fpga_mgr_register()``,
+``devm_fpga_mgr_register_full()``, and ``devm_fpga_mgr_register()`` are available
+to ease the registration.
+
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_mgr_states
@@ -147,16 +153,16 @@ API for implementing a new FPGA Manager driver
:functions: fpga_manager_info
.. kernel-doc:: drivers/fpga/fpga-mgr.c
- :functions: fpga_mgr_register_full
+ :functions: __fpga_mgr_register_full
.. kernel-doc:: drivers/fpga/fpga-mgr.c
- :functions: fpga_mgr_register
+ :functions: __fpga_mgr_register
.. kernel-doc:: drivers/fpga/fpga-mgr.c
- :functions: devm_fpga_mgr_register_full
+ :functions: __devm_fpga_mgr_register_full
.. kernel-doc:: drivers/fpga/fpga-mgr.c
- :functions: devm_fpga_mgr_register
+ :functions: __devm_fpga_mgr_register
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_unregister
diff --git a/Documentation/driver-api/fpga/fpga-region.rst b/Documentation/driver-api/fpga/fpga-region.rst
index dc55d60a0b4a..2d03b5fb7657 100644
--- a/Documentation/driver-api/fpga/fpga-region.rst
+++ b/Documentation/driver-api/fpga/fpga-region.rst
@@ -46,13 +46,16 @@ API to add a new FPGA region
----------------------------
* struct fpga_region - The FPGA region struct
-* struct fpga_region_info - Parameter structure for fpga_region_register_full()
-* fpga_region_register_full() - Create and register an FPGA region using the
+* struct fpga_region_info - Parameter structure for __fpga_region_register_full()
+* __fpga_region_register_full() - Create and register an FPGA region using the
fpga_region_info structure to provide the full flexibility of options
-* fpga_region_register() - Create and register an FPGA region using standard
+* __fpga_region_register() - Create and register an FPGA region using standard
arguments
* fpga_region_unregister() - Unregister an FPGA region
+Helper macros ``fpga_region_register()`` and ``fpga_region_register_full()``
+automatically set the module that registers the FPGA region as the owner.
+
The FPGA region's probe function will need to get a reference to the FPGA
Manager it will be using to do the programming. This usually would happen
during the region's probe function.
@@ -82,10 +85,10 @@ following APIs to handle building or tearing down that list.
:functions: fpga_region_info
.. kernel-doc:: drivers/fpga/fpga-region.c
- :functions: fpga_region_register_full
+ :functions: __fpga_region_register_full
.. kernel-doc:: drivers/fpga/fpga-region.c
- :functions: fpga_region_register
+ :functions: __fpga_region_register
.. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_unregister
diff --git a/Documentation/iio/ad7944.rst b/Documentation/iio/ad7944.rst
new file mode 100644
index 000000000000..0d26e56aba88
--- /dev/null
+++ b/Documentation/iio/ad7944.rst
@@ -0,0 +1,156 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+=============
+AD7944 driver
+=============
+
+ADC driver for Analog Devices Inc. AD7944 and similar devices. The module name
+is ``ad7944``.
+
+
+Supported devices
+=================
+
+The following chips are supported by this driver:
+
+* `AD7944 <https://www.analog.com/AD7944>`_
+* `AD7985 <https://www.analog.com/AD7985>`_
+* `AD7986 <https://www.analog.com/AD7986>`_
+
+
+Supported features
+==================
+
+SPI wiring modes
+----------------
+
+The driver currently supports three of the many possible SPI wiring configurations.
+
+CS mode, 3-wire, without busy indicator
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block::
+
+ +-------------+
+ +--------------------| CS |
+ v | |
+ VIO +--------------------+ | HOST |
+ | | CNV | | |
+ +--->| SDI AD7944 SDO |-------->| SDI |
+ | SCK | | |
+ +--------------------+ | |
+ ^ | |
+ +--------------------| SCLK |
+ +-------------+
+
+To select this mode in the device tree, set the ``adi,spi-mode`` property to
+``"single"`` and omit the ``cnv-gpios`` property.
+
+CS mode, 4-wire, without busy indicator
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block::
+
+ +-------------+
+ +-----------------------------------| CS |
+ | | |
+ | +--------------------| GPIO |
+ | v | |
+ | +--------------------+ | HOST |
+ | | CNV | | |
+ +--->| SDI AD7944 SDO |-------->| SDI |
+ | SCK | | |
+ +--------------------+ | |
+ ^ | |
+ +--------------------| SCLK |
+ +-------------+
+
+To select this mode in the device tree, omit the ``adi,spi-mode`` property and
+provide the ``cnv-gpios`` property.
+
+Chain mode, without busy indicator
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block::
+
+ +-------------+
+ +-------------------------+--------------------| CS |
+ v v | |
+ +--------------------+ +--------------------+ | HOST |
+ | CNV | | CNV | | |
+ +--->| SDI AD7944 SDO |--->| SDI AD7944 SDO |-------->| SDI |
+ | | SCK | | SCK | | |
+ GND +--------------------+ +--------------------+ | |
+ ^ ^ | |
+ +-------------------------+--------------------| SCLK |
+ +-------------+
+
+To select this mode in the device tree, set the ``adi,spi-mode`` property to
+``"chain"``, add the ``spi-cs-high`` flag, add the ``#daisy-chained-devices``
+property, and omit the ``cnv-gpios`` property.
+
+Reference voltage
+-----------------
+
+All 3 possible reference voltage sources are supported:
+
+- Internal reference
+- External 1.2V reference and internal buffer
+- External reference
+
+The source is determined by the device tree. If ``ref-supply`` is present, then
+the external reference is used. If ``refin-supply`` is present, then the internal
+buffer is used. If neither is present, then the internal reference is used.
+
+Unimplemented features
+----------------------
+
+- ``BUSY`` indication
+- ``TURBO`` mode
+
+
+Device attributes
+=================
+
+There are two types of ADCs in this family, pseudo-differential and fully
+differential. The channel name is different depending on the type of ADC.
+
+Pseudo-differential ADCs
+------------------------
+
+AD7944 and AD7985 are pseudo-differential ADCs and have the following attributes:
+
++---------------------------------------+--------------------------------------------------------------+
+| Attribute | Description |
++=======================================+==============================================================+
+| ``in_voltage0_raw`` | Raw ADC voltage value (*IN+* referenced to ground sense). |
++---------------------------------------+--------------------------------------------------------------+
+| ``in_voltage0_scale`` | Scale factor to convert raw value to mV. |
++---------------------------------------+--------------------------------------------------------------+
+
+In "chain" mode, additional chips will appear as additional voltage input
+channels, e.g. ``in_voltage1_raw``.
+
+Fully-differential ADCs
+-----------------------
+
+AD7986 is a fully-differential ADC and has the following attributes:
+
++---------------------------------------+--------------------------------------------------------------+
+| Attribute | Description |
++=======================================+==============================================================+
+| ``in_voltage0-voltage1_raw`` | Raw ADC voltage value (*IN+* - *IN-*). |
++---------------------------------------+--------------------------------------------------------------+
+| ``in_voltage0-voltage1_scale`` | Scale factor to convert raw value to mV. |
++---------------------------------------+--------------------------------------------------------------+
+
+In "chain" mode, additional chips will appear as additional voltage input
+channels, e.g. ``in_voltage2-voltage3_raw``.
+
+
+Device buffers
+==============
+
+This driver supports IIO triggered buffers.
+
+See :doc:`iio_devbuf` for more information.
diff --git a/Documentation/iio/adis16475.rst b/Documentation/iio/adis16475.rst
index 91cabb7d8d05..130f9e97cc17 100644
--- a/Documentation/iio/adis16475.rst
+++ b/Documentation/iio/adis16475.rst
@@ -66,11 +66,9 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
-| in_accel_calibbias_x | x-axis acceleration offset correction |
-+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
-| in_accel_calibbias_y | y-axis acceleration offset correction |
+| in_accel_y_calibbias | Calibration offset for the Y-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
@@ -94,11 +92,9 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+---------------------------------------+------------------------------------------------------+
| in_anglvel_x_calibbias | Calibration offset for the X-axis gyroscope channel. |
+---------------------------------------+------------------------------------------------------+
-| in_anglvel_calibbias_x | x-axis gyroscope offset correction |
-+---------------------------------------+------------------------------------------------------+
| in_anglvel_x_raw | Raw X-axis gyroscope channel value. |
+---------------------------------------+------------------------------------------------------+
-| in_anglvel_calibbias_y | y-axis gyroscope offset correction |
+| in_anglvel_y_calibbias | Calibration offset for the Y-axis gyroscope channel. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_y_raw | Raw Y-axis gyroscope channel value. |
+---------------------------------------+------------------------------------------------------+
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 30b09eefe75e..fb6f9d743211 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -16,6 +16,7 @@ Industrial I/O Kernel Drivers
.. toctree::
:maxdepth: 1
+ ad7944
adis16475
bno055
ep93xx_adc
diff --git a/Documentation/trace/hisi-ptt.rst b/Documentation/trace/hisi-ptt.rst
index 989255eb5622..6eef28ebb0c7 100644
--- a/Documentation/trace/hisi-ptt.rst
+++ b/Documentation/trace/hisi-ptt.rst
@@ -40,7 +40,7 @@ IO dies (SICL, Super I/O Cluster), where there's one PCIe Root
Complex for each SICL.
::
- /sys/devices/hisi_ptt<sicl_id>_<core_id>
+ /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>
Tune
====
@@ -53,7 +53,7 @@ Each event is presented as a file under $(PTT PMU dir)/tune, and
a simple open/read/write/close cycle will be used to tune the event.
::
- $ cd /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune
+ $ cd /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune
$ ls
qos_tx_cpl qos_tx_np qos_tx_p
tx_path_rx_req_alloc_buf_level
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index c472423412bf..a141e8e65c5d 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -174,6 +174,8 @@ Code Seq# Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'N' 40-7F drivers/block/nvme.c
+'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives
+ <mailto:wine-devel@winehq.org>
'O' 00-06 mtd/ubi-user.h UBI
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
diff --git a/MAINTAINERS b/MAINTAINERS
index 2cce3a8d2595..7af431daf77b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,44 +210,44 @@ S: Maintained
F: drivers/hwmon/abituguru3.c
ACCES 104-DIO-48E GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-104-dio-48e.c
ACCES 104-IDI-48 GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-104-idi-48.c
ACCES 104-IDIO-16 GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-104-idio-16.c
ACCES 104-QUAD-8 DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/counter/104-quad-8.c
ACCES IDIO-16 GPIO LIBRARY
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-idio-16.c
F: drivers/gpio/gpio-idio-16.h
ACCES PCI-IDIO-16 GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-pci-idio-16.c
ACCES PCIe-IDIO-24 GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-pcie-idio-24.c
@@ -453,6 +453,16 @@ W: http://wiki.analog.com/AD7879
W: https://ez.analog.com/linux-software-drivers
F: drivers/input/touchscreen/ad7879.c
+AD7944 ADC DRIVER (AD7944/AD7985/AD7986)
+M: Michael Hennerich <michael.hennerich@analog.com>
+M: Nuno Sá <nuno.sa@analog.com>
+R: David Lechner <dlechner@baylibre.com>
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
+F: Documentation/iio/ad7944.rst
+F: drivers/iio/adc/ad7944.c
+
ADAFRUIT MINI I2C GAMEPAD
M: Anshul Dalal <anshulusr@gmail.com>
L: linux-input@vger.kernel.org
@@ -1255,6 +1265,15 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
F: drivers/iio/adc/ad7780.c
+ANALOG DEVICES INC AD9739a DRIVER
+M: Nuno Sa <nuno.sa@analog.com>
+M: Dragos Bogdan <dragos.bogdan@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+F: drivers/iio/dac/ad9739a.c
+
ANALOG DEVICES INC ADA4250 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -1420,6 +1439,14 @@ F: sound/soc/codecs/adav*
F: sound/soc/codecs/sigmadsp.*
F: sound/soc/codecs/ssm*
+ANALOG DEVICES INC AXI DAC DRIVER
+M: Nuno Sa <nuno.sa@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+F: drivers/iio/dac/adi-axi-dac.c
+
ANALOG DEVICES INC DMA DRIVERS
M: Lars-Peter Clausen <lars@metafoo.de>
S: Supported
@@ -1484,7 +1511,7 @@ S: Maintained
F: sound/aoa/
APEX EMBEDDED SYSTEMS STX104 IIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/addac/stx104.c
@@ -5585,7 +5612,7 @@ F: Documentation/hwmon/corsair-psu.rst
F: drivers/hwmon/corsair-psu.c
COUNTER SUBSYSTEM
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-iio@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wbg/counter.git
@@ -6371,7 +6398,7 @@ F: include/sound/da[79]*.h
F: sound/soc/codecs/da[79]*.[ch]
DIAMOND SYSTEMS GPIO-MM GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-gpio-mm.c
@@ -9943,7 +9970,7 @@ M: Yicong Yang <yangyicong@hisilicon.com>
M: Jonathan Cameron <jonathan.cameron@huawei.com>
L: linux-kernel@vger.kernel.org
S: Maintained
-F: Documentation/ABI/testing/sysfs-devices-hisi_ptt
+F: Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt
F: Documentation/trace/hisi-ptt.rst
F: drivers/hwtracing/ptt/
F: tools/perf/arch/arm64/util/hisi-ptt.c
@@ -10704,6 +10731,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
F: Documentation/ABI/testing/configfs-iio*
F: Documentation/ABI/testing/sysfs-bus-iio*
F: Documentation/devicetree/bindings/iio/
+F: Documentation/iio/
F: drivers/iio/
F: drivers/staging/iio/
F: include/dt-bindings/iio/
@@ -10911,14 +10939,14 @@ S: Maintained
F: drivers/video/fbdev/i810/
INTEL 8254 COUNTER DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/counter/i8254.c
F: include/linux/i8254.h
INTEL 8255 GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-i8255.c
@@ -11632,7 +11660,7 @@ F: drivers/irqchip/
F: include/linux/irqchip.h
ISA
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
S: Maintained
F: Documentation/driver-api/isa.rst
F: drivers/base/isa.c
@@ -13686,7 +13714,7 @@ F: drivers/net/mdio/mdio-regmap.c
F: include/linux/mdio/mdio-regmap.h
MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/dac/cio-dac.c
@@ -24202,7 +24230,7 @@ S: Orphan
F: drivers/watchdog/ebc-c384_wdt.c
WINSYSTEMS WS16C48 GPIO DRIVER
-M: William Breathitt Gray <william.gray@linaro.org>
+M: William Breathitt Gray <wbg@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-ws16c48.c
diff --git a/drivers/accessibility/speakup/devsynth.c b/drivers/accessibility/speakup/devsynth.c
index cb7e1114e8eb..e3d909bd0480 100644
--- a/drivers/accessibility/speakup/devsynth.c
+++ b/drivers/accessibility/speakup/devsynth.c
@@ -39,13 +39,13 @@ static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
size_t nbytes, loff_t *ppos)
{
- size_t count = nbytes, want;
+ size_t count = nbytes, consumed, want;
const char __user *ptr = buffer;
size_t bytes;
unsigned long flags;
unsigned char buf[256];
u16 ubuf[256];
- size_t in, in2, out;
+ size_t in, out;
if (!synth)
return -ENODEV;
@@ -58,57 +58,24 @@ static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
return -EFAULT;
/* Convert to u16 */
- for (in = 0, out = 0; in < bytes; in++) {
- unsigned char c = buf[in];
- int nbytes = 8 - fls(c ^ 0xff);
- u32 value;
-
- switch (nbytes) {
- case 8: /* 0xff */
- case 7: /* 0xfe */
- case 1: /* 0x80 */
- /* Invalid, drop */
- goto drop;
-
- case 0:
- /* ASCII, copy */
- ubuf[out++] = c;
- continue;
+ for (in = 0, out = 0; in < bytes; in += consumed) {
+ s32 value;
- default:
- /* 2..6-byte UTF-8 */
+ value = synth_utf8_get(buf + in, bytes - in, &consumed, &want);
+ if (value == -1) {
+ /* Invalid or incomplete */
- if (bytes - in < nbytes) {
+ if (want > bytes - in)
/* We don't have it all yet, stop here
* and wait for the rest
*/
bytes = in;
- want = nbytes;
- continue;
- }
-
- /* First byte */
- value = c & ((1u << (7 - nbytes)) - 1);
-
- /* Other bytes */
- for (in2 = 2; in2 <= nbytes; in2++) {
- c = buf[in + 1];
- if ((c & 0xc0) != 0x80) {
- /* Invalid, drop the head */
- want = 1;
- goto drop;
- }
- value = (value << 6) | (c & 0x3f);
- in++;
- }
-
- if (value < 0x10000)
- ubuf[out++] = value;
- want = 1;
- break;
+
+ continue;
}
-drop:
- /* empty statement */;
+
+ if (value < 0x10000)
+ ubuf[out++] = value;
}
count -= bytes;
diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h
index 364fde99749e..54f1226ea061 100644
--- a/drivers/accessibility/speakup/speakup.h
+++ b/drivers/accessibility/speakup/speakup.h
@@ -76,7 +76,9 @@ int speakup_paste_selection(struct tty_struct *tty);
void speakup_cancel_paste(void);
void speakup_register_devsynth(void);
void speakup_unregister_devsynth(void);
+s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want);
void synth_write(const char *buf, size_t count);
+void synth_writeu(const char *buf, size_t count);
int synth_supports_indexing(void);
extern struct vc_data *spk_sel_cons;
diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c
index 45f906103133..85062e605d79 100644
--- a/drivers/accessibility/speakup/synth.c
+++ b/drivers/accessibility/speakup/synth.c
@@ -217,10 +217,95 @@ void synth_write(const char *_buf, size_t count)
synth_start();
}
+/* Consume one utf-8 character from buf (that contains up to count bytes),
+ * returns the unicode codepoint if valid, -1 otherwise.
+ * In all cases, returns the number of consumed bytes in *consumed,
+ * and the minimum number of bytes that would be needed for the next character
+ * in *want.
+ */
+s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want)
+{
+ unsigned char c = buf[0];
+ int nbytes = 8 - fls(c ^ 0xff);
+ u32 value;
+ size_t i;
+
+ switch (nbytes) {
+ case 8: /* 0xff */
+ case 7: /* 0xfe */
+ case 1: /* 0x80 */
+ /* Invalid, drop */
+ *consumed = 1;
+ *want = 1;
+ return -1;
+
+ case 0:
+ /* ASCII, take as such */
+ *consumed = 1;
+ *want = 1;
+ return c;
+
+ default:
+ /* 2..6-byte UTF-8 */
+
+ if (count < nbytes) {
+ /* We don't have it all */
+ *consumed = 0;
+ *want = nbytes;
+ return -1;
+ }
+
+ /* First byte */
+ value = c & ((1u << (7 - nbytes)) - 1);
+
+ /* Other bytes */
+ for (i = 1; i < nbytes; i++) {
+ c = buf[i];
+ if ((c & 0xc0) != 0x80) {
+ /* Invalid, drop the head */
+ *consumed = i;
+ *want = 1;
+ return -1;
+ }
+ value = (value << 6) | (c & 0x3f);
+ }
+
+ *consumed = nbytes;
+ *want = 1;
+ return value;
+ }
+}
+
+void synth_writeu(const char *buf, size_t count)
+{
+ size_t i, consumed, want;
+
+ /* Convert to u16 */
+ for (i = 0; i < count; i++) {
+ s32 value;
+
+ value = synth_utf8_get(buf + i, count - i, &consumed, &want);
+ if (value == -1) {
+ /* Invalid or incomplete */
+
+ if (want > count - i)
+ /* We don't have it all, stop */
+ count = i;
+
+ continue;
+ }
+
+ if (value < 0x10000)
+ synth_buffer_add(value);
+ }
+
+ synth_start();
+}
+
void synth_printf(const char *fmt, ...)
{
va_list args;
- unsigned char buf[160], *p;
+ unsigned char buf[160];
int r;
va_start(args, fmt);
@@ -229,10 +314,7 @@ void synth_printf(const char *fmt, ...)
if (r > sizeof(buf) - 1)
r = sizeof(buf) - 1;
- p = buf;
- while (r--)
- synth_buffer_add(*p++);
- synth_start();
+ synth_writeu(buf, r);
}
EXPORT_SYMBOL_GPL(synth_printf);
diff --git a/drivers/acpi/arm64/amba.c b/drivers/acpi/arm64/amba.c
index 171b5c2c7edd..e1f0bbb8f393 100644
--- a/drivers/acpi/arm64/amba.c
+++ b/drivers/acpi/arm64/amba.c
@@ -22,14 +22,6 @@
static const struct acpi_device_id amba_id_list[] = {
{"ARMH0061", 0}, /* PL061 GPIO Device */
{"ARMH0330", 0}, /* ARM DMA Controller DMA-330 */
- {"ARMHC501", 0}, /* ARM CoreSight ETR */
- {"ARMHC502", 0}, /* ARM CoreSight STM */
- {"ARMHC503", 0}, /* ARM CoreSight Debug */
- {"ARMHC979", 0}, /* ARM CoreSight TPIU */
- {"ARMHC97C", 0}, /* ARM CoreSight SoC-400 TMC, SoC-600 ETF/ETB */
- {"ARMHC98D", 0}, /* ARM CoreSight Dynamic Replicator */
- {"ARMHC9CA", 0}, /* ARM CoreSight CATU */
- {"ARMHC9FF", 0}, /* ARM CoreSight Dynamic Funnel */
{"", 0},
};
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index dd6923d37931..b21a7b246a0d 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -5367,7 +5367,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
goto err;
break;
case BINDER_SET_MAX_THREADS: {
- int max_threads;
+ u32 max_threads;
if (copy_from_user(&max_threads, ubuf,
sizeof(max_threads))) {
diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
index 7270d4d22207..5b7c80b99ae8 100644
--- a/drivers/android/binder_internal.h
+++ b/drivers/android/binder_internal.h
@@ -421,7 +421,7 @@ struct binder_proc {
struct list_head todo;
struct binder_stats stats;
struct list_head delivered_death;
- int max_threads;
+ u32 max_threads;
int requested_threads;
int requested_threads_started;
int tmp_ref;
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 89a06fd16a99..837d77e3af2b 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -869,20 +869,6 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(fwnode_handle_get);
/**
- * fwnode_handle_put - Drop reference to a device node
- * @fwnode: Pointer to the device node to drop the reference to.
- *
- * This has to be used when terminating device_for_each_child_node() iteration
- * with break or return to prevent stale device node references from being left
- * behind.
- */
-void fwnode_handle_put(struct fwnode_handle *fwnode)
-{
- fwnode_call_void_op(fwnode, put);
-}
-EXPORT_SYMBOL_GPL(fwnode_handle_put);
-
-/**
* fwnode_device_is_available - check if a device is available for use
* @fwnode: Pointer to the fwnode of the device.
*
diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c
index 44f934981de8..173f79918741 100644
--- a/drivers/bus/mhi/host/init.c
+++ b/drivers/bus/mhi/host/init.c
@@ -127,6 +127,30 @@ static ssize_t soc_reset_store(struct device *dev,
}
static DEVICE_ATTR_WO(soc_reset);
+static ssize_t trigger_edl_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhi_device *mhi_dev = to_mhi_device(dev);
+ struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ if (!val)
+ return -EINVAL;
+
+ ret = mhi_cntrl->edl_trigger(mhi_cntrl);
+ if (ret)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_WO(trigger_edl);
+
static struct attribute *mhi_dev_attrs[] = {
&dev_attr_serial_number.attr,
&dev_attr_oem_pk_hash.attr,
@@ -517,11 +541,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
dev_dbg(dev, "Initializing MHI registers\n");
/* Read channel db offset */
- ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val);
- if (ret) {
- dev_err(dev, "Unable to read CHDBOFF register\n");
- return -EIO;
- }
+ ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val);
+ if (ret)
+ return ret;
if (val >= mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)) {
dev_err(dev, "CHDB offset: 0x%x is out of range: 0x%zx\n",
@@ -1018,6 +1040,12 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
if (ret)
goto err_release_dev;
+ if (mhi_cntrl->edl_trigger) {
+ ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
+ if (ret)
+ goto err_release_dev;
+ }
+
mhi_cntrl->mhi_dev = mhi_dev;
mhi_create_debugfs(mhi_cntrl);
@@ -1051,6 +1079,9 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl)
mhi_deinit_free_irq(mhi_cntrl);
mhi_destroy_debugfs(mhi_cntrl);
+ if (mhi_cntrl->edl_trigger)
+ sysfs_remove_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
+
destroy_workqueue(mhi_cntrl->hiprio_wq);
kfree(mhi_cntrl->mhi_cmd);
kfree(mhi_cntrl->mhi_event);
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index 15d657af9b5b..4de75674f193 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -1691,3 +1691,19 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
}
}
EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset)
+{
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ void __iomem *base = mhi_cntrl->regs;
+ int ret;
+
+ ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, chdb_offset);
+ if (ret) {
+ dev_err(dev, "Unable to read CHDBOFF register\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mhi_get_channel_doorbell_offset);
diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index 51639bfcfec7..08844ee79654 100644
--- a/drivers/bus/mhi/host/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -27,12 +27,16 @@
#define PCI_VENDOR_ID_THALES 0x1269
#define PCI_VENDOR_ID_QUECTEL 0x1eac
+#define MHI_EDL_DB 91
+#define MHI_EDL_COOKIE 0xEDEDEDED
+
/**
* struct mhi_pci_dev_info - MHI PCI device specific information
* @config: MHI controller configuration
* @name: name of the PCI module
* @fw: firmware path (if any)
* @edl: emergency download mode firmware path (if any)
+ * @edl_trigger: capable of triggering EDL mode in the device (if supported)
* @bar_num: PCI base address register to use for MHI MMIO register space
* @dma_data_width: DMA transfer word size (32 or 64 bits)
* @mru_default: default MRU size for MBIM network packets
@@ -44,6 +48,7 @@ struct mhi_pci_dev_info {
const char *name;
const char *fw;
const char *edl;
+ bool edl_trigger;
unsigned int bar_num;
unsigned int dma_data_width;
unsigned int mru_default;
@@ -292,6 +297,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = {
.name = "qcom-sdx75m",
.fw = "qcom/sdx75m/xbl.elf",
.edl = "qcom/sdx75m/edl.mbn",
+ .edl_trigger = true,
.config = &modem_qcom_v2_mhiv_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
@@ -302,6 +308,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx65_info = {
.name = "qcom-sdx65m",
.fw = "qcom/sdx65m/xbl.elf",
.edl = "qcom/sdx65m/edl.mbn",
+ .edl_trigger = true,
.config = &modem_qcom_v1_mhiv_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
@@ -312,6 +319,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx55_info = {
.name = "qcom-sdx55m",
.fw = "qcom/sdx55m/sbl1.mbn",
.edl = "qcom/sdx55m/edl.mbn",
+ .edl_trigger = true,
.config = &modem_qcom_v1_mhiv_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
@@ -928,6 +936,40 @@ static void health_check(struct timer_list *t)
mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
}
+static int mhi_pci_generic_edl_trigger(struct mhi_controller *mhi_cntrl)
+{
+ void __iomem *base = mhi_cntrl->regs;
+ void __iomem *edl_db;
+ int ret;
+ u32 val;
+
+ ret = mhi_device_get_sync(mhi_cntrl->mhi_dev);
+ if (ret) {
+ dev_err(mhi_cntrl->cntrl_dev, "Failed to wakeup the device\n");
+ return ret;
+ }
+
+ pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
+ mhi_cntrl->runtime_get(mhi_cntrl);
+
+ ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val);
+ if (ret)
+ goto err_get_chdb;
+
+ edl_db = base + val + (8 * MHI_EDL_DB);
+
+ mhi_cntrl->write_reg(mhi_cntrl, edl_db + 4, upper_32_bits(MHI_EDL_COOKIE));
+ mhi_cntrl->write_reg(mhi_cntrl, edl_db, lower_32_bits(MHI_EDL_COOKIE));
+
+ mhi_soc_reset(mhi_cntrl);
+
+err_get_chdb:
+ mhi_cntrl->runtime_put(mhi_cntrl);
+ mhi_device_put(mhi_cntrl->mhi_dev);
+
+ return ret;
+}
+
static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
const struct mhi_pci_dev_info *info = (struct mhi_pci_dev_info *) id->driver_data;
@@ -962,6 +1004,9 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_cntrl->runtime_put = mhi_pci_runtime_put;
mhi_cntrl->mru = info->mru_default;
+ if (info->edl_trigger)
+ mhi_cntrl->edl_trigger = mhi_pci_generic_edl_trigger;
+
if (info->sideband_wake) {
mhi_cntrl->wake_get = mhi_pci_wake_get_nop;
mhi_cntrl->wake_put = mhi_pci_wake_put_nop;
diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c
index 112a1541de6d..201f9a6fbde7 100644
--- a/drivers/cdx/controller/cdx_controller.c
+++ b/drivers/cdx/controller/cdx_controller.c
@@ -222,7 +222,7 @@ mcdi_init_fail:
return ret;
}
-static int xlnx_cdx_remove(struct platform_device *pdev)
+static void xlnx_cdx_remove(struct platform_device *pdev)
{
struct cdx_controller *cdx = platform_get_drvdata(pdev);
struct cdx_mcdi *cdx_mcdi = cdx->priv;
@@ -234,8 +234,6 @@ static int xlnx_cdx_remove(struct platform_device *pdev)
cdx_mcdi_finish(cdx_mcdi);
kfree(cdx_mcdi);
-
- return 0;
}
static const struct of_device_id cdx_match_table[] = {
@@ -252,7 +250,7 @@ static struct platform_driver cdx_pdriver = {
.of_match_table = cdx_match_table,
},
.probe = xlnx_cdx_probe,
- .remove = xlnx_cdx_remove,
+ .remove_new = xlnx_cdx_remove,
};
static int __init cdx_controller_init(void)
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 9b80e622ae80..7c359cc406d5 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -383,6 +383,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
return 0;
}
+#ifdef CONFIG_DEVPORT
static ssize_t read_port(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -424,6 +425,7 @@ static ssize_t write_port(struct file *file, const char __user *buf,
*ppos = i;
return tmp-buf;
}
+#endif
static ssize_t read_null(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
@@ -653,12 +655,14 @@ static const struct file_operations null_fops = {
.uring_cmd = uring_cmd_null,
};
-static const struct file_operations __maybe_unused port_fops = {
+#ifdef CONFIG_DEVPORT
+static const struct file_operations port_fops = {
.llseek = memory_lseek,
.read = read_port,
.write = write_port,
.open = open_port,
};
+#endif
static const struct file_operations zero_fops = {
.llseek = zero_lseek,
diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c
index 3c99696b145e..f2cff1a6fed5 100644
--- a/drivers/char/powernv-op-panel.c
+++ b/drivers/char/powernv-op-panel.c
@@ -195,12 +195,11 @@ free_oppanel_data:
return rc;
}
-static int oppanel_remove(struct platform_device *pdev)
+static void oppanel_remove(struct platform_device *pdev)
{
misc_deregister(&oppanel_dev);
kfree(oppanel_lines);
kfree(oppanel_data);
- return 0;
}
static const struct of_device_id oppanel_match[] = {
@@ -214,7 +213,7 @@ static struct platform_driver oppanel_driver = {
.of_match_table = oppanel_match,
},
.probe = oppanel_probe,
- .remove = oppanel_remove,
+ .remove_new = oppanel_remove,
};
module_platform_driver(oppanel_driver);
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index ee951b265213..58e9dcc2a308 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -296,28 +296,35 @@ static int register_device(int minor, struct pp_struct *pp)
if (!port) {
pr_warn("%s: no associated port!\n", name);
rc = -ENXIO;
- goto err;
+ goto err_free_name;
}
index = ida_alloc(&ida_index, GFP_KERNEL);
+ if (index < 0) {
+ pr_warn("%s: failed to get index!\n", name);
+ rc = index;
+ goto err_put_port;
+ }
+
memset(&ppdev_cb, 0, sizeof(ppdev_cb));
ppdev_cb.irq_func = pp_irq;
ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
ppdev_cb.private = pp;
pdev = parport_register_dev_model(port, name, &ppdev_cb, index);
- parport_put_port(port);
if (!pdev) {
pr_warn("%s: failed to register device!\n", name);
rc = -ENXIO;
ida_free(&ida_index, index);
- goto err;
+ goto err_put_port;
}
pp->pdev = pdev;
pp->index = index;
dev_dbg(&pdev->dev, "registered pardevice\n");
-err:
+err_put_port:
+ parport_put_port(port);
+err_free_name:
kfree(name);
return rc;
}
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
index 22d249333f53..bb5115b1736a 100644
--- a/drivers/char/sonypi.c
+++ b/drivers/char/sonypi.c
@@ -1408,7 +1408,7 @@ static int sonypi_probe(struct platform_device *dev)
return error;
}
-static int sonypi_remove(struct platform_device *dev)
+static void sonypi_remove(struct platform_device *dev)
{
sonypi_disable();
@@ -1432,8 +1432,6 @@ static int sonypi_remove(struct platform_device *dev)
}
kfifo_free(&sonypi_device.fifo);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -1470,7 +1468,7 @@ static struct platform_driver sonypi_driver = {
.pm = SONYPI_PM,
},
.probe = sonypi_probe,
- .remove = sonypi_remove,
+ .remove_new = sonypi_remove,
.shutdown = sonypi_shutdown,
};
diff --git a/drivers/comedi/drivers/cb_pcidas64.c b/drivers/comedi/drivers/cb_pcidas64.c
index ff19fc3859e4..d398c6df9482 100644
--- a/drivers/comedi/drivers/cb_pcidas64.c
+++ b/drivers/comedi/drivers/cb_pcidas64.c
@@ -374,11 +374,6 @@ static inline u16 pipe_full_bits(u16 hw_status_bits)
return (hw_status_bits >> 10) & 0x3;
};
-static inline unsigned int dma_chain_flag_bits(u16 prepost_bits)
-{
- return (prepost_bits >> 6) & 0x3;
-}
-
static inline unsigned int adc_upper_read_ptr_code(u16 prepost_bits)
{
return (prepost_bits >> 12) & 0x3;
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 3f24481fc04a..893b4f0726d2 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -49,12 +49,12 @@ static void counter_device_release(struct device *dev)
kfree(container_of(counter, struct counter_device_allochelper, counter));
}
-static struct device_type counter_device_type = {
+static const struct device_type counter_device_type = {
.name = "counter_device",
.release = counter_device_release,
};
-static struct bus_type counter_bus_type = {
+static const struct bus_type counter_bus_type = {
.name = "counter",
.dev_name = "counter",
};
diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
index 6206d2dc3d47..0664ef969f79 100644
--- a/drivers/counter/stm32-timer-cnt.c
+++ b/drivers/counter/stm32-timer-cnt.c
@@ -8,9 +8,11 @@
*
*/
#include <linux/counter.h>
+#include <linux/interrupt.h>
#include <linux/mfd/stm32-timers.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/types.h>
@@ -21,6 +23,12 @@
#define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \
TIM_CCER_CC2P | TIM_CCER_CC2NP)
+#define STM32_CH1_SIG 0
+#define STM32_CH2_SIG 1
+#define STM32_CLOCK_SIG 2
+#define STM32_CH3_SIG 3
+#define STM32_CH4_SIG 4
+
struct stm32_timer_regs {
u32 cr1;
u32 cnt;
@@ -34,6 +42,11 @@ struct stm32_timer_cnt {
u32 max_arr;
bool enabled;
struct stm32_timer_regs bak;
+ bool has_encoder;
+ unsigned int nchannels;
+ unsigned int nr_irqs;
+ spinlock_t lock; /* protects nb_ovf */
+ u64 nb_ovf;
};
static const enum counter_function stm32_count_functions[] = {
@@ -107,12 +120,18 @@ static int stm32_count_function_write(struct counter_device *counter,
sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
break;
case COUNTER_FUNCTION_QUADRATURE_X2_A:
+ if (!priv->has_encoder)
+ return -EOPNOTSUPP;
sms = TIM_SMCR_SMS_ENCODER_MODE_1;
break;
case COUNTER_FUNCTION_QUADRATURE_X2_B:
+ if (!priv->has_encoder)
+ return -EOPNOTSUPP;
sms = TIM_SMCR_SMS_ENCODER_MODE_2;
break;
case COUNTER_FUNCTION_QUADRATURE_X4:
+ if (!priv->has_encoder)
+ return -EOPNOTSUPP;
sms = TIM_SMCR_SMS_ENCODER_MODE_3;
break;
default:
@@ -216,11 +235,108 @@ static int stm32_count_enable_write(struct counter_device *counter,
return 0;
}
+static int stm32_count_prescaler_read(struct counter_device *counter,
+ struct counter_count *count, u64 *prescaler)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ u32 psc;
+
+ regmap_read(priv->regmap, TIM_PSC, &psc);
+
+ *prescaler = psc + 1;
+
+ return 0;
+}
+
+static int stm32_count_prescaler_write(struct counter_device *counter,
+ struct counter_count *count, u64 prescaler)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ u32 psc;
+
+ if (!prescaler || prescaler > MAX_TIM_PSC + 1)
+ return -ERANGE;
+
+ psc = prescaler - 1;
+
+ return regmap_write(priv->regmap, TIM_PSC, psc);
+}
+
+static int stm32_count_cap_read(struct counter_device *counter,
+ struct counter_count *count,
+ size_t ch, u64 *cap)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ u32 ccrx;
+
+ if (ch >= priv->nchannels)
+ return -EOPNOTSUPP;
+
+ switch (ch) {
+ case 0:
+ regmap_read(priv->regmap, TIM_CCR1, &ccrx);
+ break;
+ case 1:
+ regmap_read(priv->regmap, TIM_CCR2, &ccrx);
+ break;
+ case 2:
+ regmap_read(priv->regmap, TIM_CCR3, &ccrx);
+ break;
+ case 3:
+ regmap_read(priv->regmap, TIM_CCR4, &ccrx);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(counter->parent, "CCR%zu: 0x%08x\n", ch + 1, ccrx);
+
+ *cap = ccrx;
+
+ return 0;
+}
+
+static int stm32_count_nb_ovf_read(struct counter_device *counter,
+ struct counter_count *count, u64 *val)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&priv->lock, irqflags);
+ *val = priv->nb_ovf;
+ spin_unlock_irqrestore(&priv->lock, irqflags);
+
+ return 0;
+}
+
+static int stm32_count_nb_ovf_write(struct counter_device *counter,
+ struct counter_count *count, u64 val)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&priv->lock, irqflags);
+ priv->nb_ovf = val;
+ spin_unlock_irqrestore(&priv->lock, irqflags);
+
+ return 0;
+}
+
+static DEFINE_COUNTER_ARRAY_CAPTURE(stm32_count_cap_array, 4);
+
static struct counter_comp stm32_count_ext[] = {
COUNTER_COMP_DIRECTION(stm32_count_direction_read),
COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write),
COUNTER_COMP_CEILING(stm32_count_ceiling_read,
stm32_count_ceiling_write),
+ COUNTER_COMP_COUNT_U64("prescaler", stm32_count_prescaler_read,
+ stm32_count_prescaler_write),
+ COUNTER_COMP_ARRAY_CAPTURE(stm32_count_cap_read, NULL, stm32_count_cap_array),
+ COUNTER_COMP_COUNT_U64("num_overflows", stm32_count_nb_ovf_read, stm32_count_nb_ovf_write),
+};
+
+static const enum counter_synapse_action stm32_clock_synapse_actions[] = {
+ COUNTER_SYNAPSE_ACTION_RISING_EDGE,
};
static const enum counter_synapse_action stm32_synapse_actions[] = {
@@ -243,25 +359,152 @@ static int stm32_action_read(struct counter_device *counter,
switch (function) {
case COUNTER_FUNCTION_INCREASE:
/* counts on internal clock when CEN=1 */
- *action = COUNTER_SYNAPSE_ACTION_NONE;
+ if (synapse->signal->id == STM32_CLOCK_SIG)
+ *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+ else
+ *action = COUNTER_SYNAPSE_ACTION_NONE;
return 0;
case COUNTER_FUNCTION_QUADRATURE_X2_A:
/* counts up/down on TI1FP1 edge depending on TI2FP2 level */
- if (synapse->signal->id == count->synapses[0].signal->id)
+ if (synapse->signal->id == STM32_CH1_SIG)
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
else
*action = COUNTER_SYNAPSE_ACTION_NONE;
return 0;
case COUNTER_FUNCTION_QUADRATURE_X2_B:
/* counts up/down on TI2FP2 edge depending on TI1FP1 level */
- if (synapse->signal->id == count->synapses[1].signal->id)
+ if (synapse->signal->id == STM32_CH2_SIG)
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
else
*action = COUNTER_SYNAPSE_ACTION_NONE;
return 0;
case COUNTER_FUNCTION_QUADRATURE_X4:
/* counts up/down on both TI1FP1 and TI2FP2 edges */
- *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
+ if (synapse->signal->id == STM32_CH1_SIG || synapse->signal->id == STM32_CH2_SIG)
+ *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
+ else
+ *action = COUNTER_SYNAPSE_ACTION_NONE;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+struct stm32_count_cc_regs {
+ u32 ccmr_reg;
+ u32 ccmr_mask;
+ u32 ccmr_bits;
+ u32 ccer_bits;
+};
+
+static const struct stm32_count_cc_regs stm32_cc[] = {
+ { TIM_CCMR1, TIM_CCMR_CC1S, TIM_CCMR_CC1S_TI1,
+ TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC1NP },
+ { TIM_CCMR1, TIM_CCMR_CC2S, TIM_CCMR_CC2S_TI2,
+ TIM_CCER_CC2E | TIM_CCER_CC2P | TIM_CCER_CC2NP },
+ { TIM_CCMR2, TIM_CCMR_CC3S, TIM_CCMR_CC3S_TI3,
+ TIM_CCER_CC3E | TIM_CCER_CC3P | TIM_CCER_CC3NP },
+ { TIM_CCMR2, TIM_CCMR_CC4S, TIM_CCMR_CC4S_TI4,
+ TIM_CCER_CC4E | TIM_CCER_CC4P | TIM_CCER_CC4NP },
+};
+
+static int stm32_count_capture_configure(struct counter_device *counter, unsigned int ch,
+ bool enable)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ const struct stm32_count_cc_regs *cc;
+ u32 ccmr, ccer;
+
+ if (ch >= ARRAY_SIZE(stm32_cc) || ch >= priv->nchannels) {
+ dev_err(counter->parent, "invalid ch: %d\n", ch);
+ return -EINVAL;
+ }
+
+ cc = &stm32_cc[ch];
+
+ /*
+ * configure channel in input capture mode, map channel 1 on TI1, channel2 on TI2...
+ * Select both edges / non-inverted to trigger a capture.
+ */
+ if (enable) {
+ /* first clear possibly latched capture flag upon enabling */
+ if (!regmap_test_bits(priv->regmap, TIM_CCER, cc->ccer_bits))
+ regmap_write(priv->regmap, TIM_SR, ~TIM_SR_CC_IF(ch));
+ regmap_update_bits(priv->regmap, cc->ccmr_reg, cc->ccmr_mask,
+ cc->ccmr_bits);
+ regmap_set_bits(priv->regmap, TIM_CCER, cc->ccer_bits);
+ } else {
+ regmap_clear_bits(priv->regmap, TIM_CCER, cc->ccer_bits);
+ regmap_clear_bits(priv->regmap, cc->ccmr_reg, cc->ccmr_mask);
+ }
+
+ regmap_read(priv->regmap, cc->ccmr_reg, &ccmr);
+ regmap_read(priv->regmap, TIM_CCER, &ccer);
+ dev_dbg(counter->parent, "%s(%s) ch%d 0x%08x 0x%08x\n", __func__, enable ? "ena" : "dis",
+ ch, ccmr, ccer);
+
+ return 0;
+}
+
+static int stm32_count_events_configure(struct counter_device *counter)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ struct counter_event_node *event_node;
+ u32 dier = 0;
+ int i, ret;
+
+ list_for_each_entry(event_node, &counter->events_list, l) {
+ switch (event_node->event) {
+ case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
+ /* first clear possibly latched UIF before enabling */
+ if (!regmap_test_bits(priv->regmap, TIM_DIER, TIM_DIER_UIE))
+ regmap_write(priv->regmap, TIM_SR, (u32)~TIM_SR_UIF);
+ dier |= TIM_DIER_UIE;
+ break;
+ case COUNTER_EVENT_CAPTURE:
+ ret = stm32_count_capture_configure(counter, event_node->channel, true);
+ if (ret)
+ return ret;
+ dier |= TIM_DIER_CC_IE(event_node->channel);
+ break;
+ default:
+ /* should never reach this path */
+ return -EINVAL;
+ }
+ }
+
+ /* Enable / disable all events at once, from events_list, so write all DIER bits */
+ regmap_write(priv->regmap, TIM_DIER, dier);
+
+ /* check for disabled capture events */
+ for (i = 0 ; i < priv->nchannels; i++) {
+ if (!(dier & TIM_DIER_CC_IE(i))) {
+ ret = stm32_count_capture_configure(counter, i, false);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int stm32_count_watch_validate(struct counter_device *counter,
+ const struct counter_watch *watch)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+
+ /* Interrupts are optional */
+ if (!priv->nr_irqs)
+ return -EOPNOTSUPP;
+
+ switch (watch->event) {
+ case COUNTER_EVENT_CAPTURE:
+ if (watch->channel >= priv->nchannels) {
+ dev_err(counter->parent, "Invalid channel %d\n", watch->channel);
+ return -EINVAL;
+ }
+ return 0;
+ case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
return 0;
default:
return -EINVAL;
@@ -274,35 +517,89 @@ static const struct counter_ops stm32_timer_cnt_ops = {
.function_read = stm32_count_function_read,
.function_write = stm32_count_function_write,
.action_read = stm32_action_read,
+ .events_configure = stm32_count_events_configure,
+ .watch_validate = stm32_count_watch_validate,
+};
+
+static int stm32_count_clk_get_freq(struct counter_device *counter,
+ struct counter_signal *signal, u64 *freq)
+{
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+
+ *freq = clk_get_rate(priv->clk);
+
+ return 0;
+}
+
+static struct counter_comp stm32_count_clock_ext[] = {
+ COUNTER_COMP_FREQUENCY(stm32_count_clk_get_freq),
};
static struct counter_signal stm32_signals[] = {
+ /*
+ * Need to declare all the signals as a static array, and keep the signals order here,
+ * even if they're unused or unexisting on some timer instances. It's an abstraction,
+ * e.g. high level view of the counter features.
+ *
+ * Userspace programs may rely on signal0 to be "Channel 1", signal1 to be "Channel 2",
+ * and so on. When a signal is unexisting, the COUNTER_SYNAPSE_ACTION_NONE can be used,
+ * to indicate that a signal doesn't affect the counter.
+ */
{
- .id = 0,
- .name = "Channel 1 Quadrature A"
+ .id = STM32_CH1_SIG,
+ .name = "Channel 1"
},
{
- .id = 1,
- .name = "Channel 1 Quadrature B"
- }
+ .id = STM32_CH2_SIG,
+ .name = "Channel 2"
+ },
+ {
+ .id = STM32_CLOCK_SIG,
+ .name = "Clock",
+ .ext = stm32_count_clock_ext,
+ .num_ext = ARRAY_SIZE(stm32_count_clock_ext),
+ },
+ {
+ .id = STM32_CH3_SIG,
+ .name = "Channel 3"
+ },
+ {
+ .id = STM32_CH4_SIG,
+ .name = "Channel 4"
+ },
};
static struct counter_synapse stm32_count_synapses[] = {
{
.actions_list = stm32_synapse_actions,
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
- .signal = &stm32_signals[0]
+ .signal = &stm32_signals[STM32_CH1_SIG]
},
{
.actions_list = stm32_synapse_actions,
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
- .signal = &stm32_signals[1]
- }
+ .signal = &stm32_signals[STM32_CH2_SIG]
+ },
+ {
+ .actions_list = stm32_clock_synapse_actions,
+ .num_actions = ARRAY_SIZE(stm32_clock_synapse_actions),
+ .signal = &stm32_signals[STM32_CLOCK_SIG]
+ },
+ {
+ .actions_list = stm32_synapse_actions,
+ .num_actions = ARRAY_SIZE(stm32_synapse_actions),
+ .signal = &stm32_signals[STM32_CH3_SIG]
+ },
+ {
+ .actions_list = stm32_synapse_actions,
+ .num_actions = ARRAY_SIZE(stm32_synapse_actions),
+ .signal = &stm32_signals[STM32_CH4_SIG]
+ },
};
static struct counter_count stm32_counts = {
.id = 0,
- .name = "Channel 1 Count",
+ .name = "STM32 Timer Counter",
.functions_list = stm32_count_functions,
.num_functions = ARRAY_SIZE(stm32_count_functions),
.synapses = stm32_count_synapses,
@@ -311,13 +608,111 @@ static struct counter_count stm32_counts = {
.num_ext = ARRAY_SIZE(stm32_count_ext)
};
+static irqreturn_t stm32_timer_cnt_isr(int irq, void *ptr)
+{
+ struct counter_device *counter = ptr;
+ struct stm32_timer_cnt *const priv = counter_priv(counter);
+ u32 clr = GENMASK(31, 0); /* SR flags can be cleared by writing 0 (wr 1 has no effect) */
+ u32 sr, dier;
+ int i;
+
+ regmap_read(priv->regmap, TIM_SR, &sr);
+ regmap_read(priv->regmap, TIM_DIER, &dier);
+ /*
+ * Some status bits in SR don't match with the enable bits in DIER. Only take care of
+ * the possibly enabled bits in DIER (that matches in between SR and DIER).
+ */
+ dier &= (TIM_DIER_UIE | TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE);
+ sr &= dier;
+
+ if (sr & TIM_SR_UIF) {
+ spin_lock(&priv->lock);
+ priv->nb_ovf++;
+ spin_unlock(&priv->lock);
+ counter_push_event(counter, COUNTER_EVENT_OVERFLOW_UNDERFLOW, 0);
+ dev_dbg(counter->parent, "COUNTER_EVENT_OVERFLOW_UNDERFLOW\n");
+ /* SR flags can be cleared by writing 0, only clear relevant flag */
+ clr &= ~TIM_SR_UIF;
+ }
+
+ /* Check capture events */
+ for (i = 0 ; i < priv->nchannels; i++) {
+ if (sr & TIM_SR_CC_IF(i)) {
+ counter_push_event(counter, COUNTER_EVENT_CAPTURE, i);
+ clr &= ~TIM_SR_CC_IF(i);
+ dev_dbg(counter->parent, "COUNTER_EVENT_CAPTURE, %d\n", i);
+ }
+ }
+
+ regmap_write(priv->regmap, TIM_SR, clr);
+
+ return IRQ_HANDLED;
+};
+
+static void stm32_timer_cnt_detect_channels(struct device *dev,
+ struct stm32_timer_cnt *priv)
+{
+ u32 ccer, ccer_backup;
+
+ regmap_read(priv->regmap, TIM_CCER, &ccer_backup);
+ regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE);
+ regmap_read(priv->regmap, TIM_CCER, &ccer);
+ regmap_write(priv->regmap, TIM_CCER, ccer_backup);
+ priv->nchannels = hweight32(ccer & TIM_CCER_CCXE);
+
+ dev_dbg(dev, "has %d cc channels\n", priv->nchannels);
+}
+
+/* encoder supported on TIM1 TIM2 TIM3 TIM4 TIM5 TIM8 */
+#define STM32_TIM_ENCODER_SUPPORTED (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(7))
+
+static const char * const stm32_timer_trigger_compat[] = {
+ "st,stm32-timer-trigger",
+ "st,stm32h7-timer-trigger",
+};
+
+static int stm32_timer_cnt_probe_encoder(struct device *dev,
+ struct stm32_timer_cnt *priv)
+{
+ struct device *parent = dev->parent;
+ struct device_node *tnode = NULL, *pnode = parent->of_node;
+ int i, ret;
+ u32 idx;
+
+ /*
+ * Need to retrieve the trigger node index from DT, to be able
+ * to determine if the counter supports encoder mode. It also
+ * enforce backward compatibility, and allow to support other
+ * counter modes in this driver (when the timer doesn't support
+ * encoder).
+ */
+ for (i = 0; i < ARRAY_SIZE(stm32_timer_trigger_compat) && !tnode; i++)
+ tnode = of_get_compatible_child(pnode, stm32_timer_trigger_compat[i]);
+ if (!tnode) {
+ dev_err(dev, "Can't find trigger node\n");
+ return -ENODATA;
+ }
+
+ ret = of_property_read_u32(tnode, "reg", &idx);
+ if (ret) {
+ dev_err(dev, "Can't get index (%d)\n", ret);
+ return ret;
+ }
+
+ priv->has_encoder = !!(STM32_TIM_ENCODER_SUPPORTED & BIT(idx));
+
+ dev_dbg(dev, "encoder support: %s\n", priv->has_encoder ? "yes" : "no");
+
+ return 0;
+}
+
static int stm32_timer_cnt_probe(struct platform_device *pdev)
{
struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct stm32_timer_cnt *priv;
struct counter_device *counter;
- int ret;
+ int i, ret;
if (IS_ERR_OR_NULL(ddata))
return -EINVAL;
@@ -331,6 +726,13 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
priv->regmap = ddata->regmap;
priv->clk = ddata->clk;
priv->max_arr = ddata->max_arr;
+ priv->nr_irqs = ddata->nr_irqs;
+
+ ret = stm32_timer_cnt_probe_encoder(dev, priv);
+ if (ret)
+ return ret;
+
+ stm32_timer_cnt_detect_channels(dev, priv);
counter->name = dev_name(dev);
counter->parent = dev;
@@ -340,8 +742,39 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
counter->signals = stm32_signals;
counter->num_signals = ARRAY_SIZE(stm32_signals);
+ spin_lock_init(&priv->lock);
+
platform_set_drvdata(pdev, priv);
+ /* STM32 Timers can have either 1 global, or 4 dedicated interrupts (optional) */
+ if (priv->nr_irqs == 1) {
+ /* All events reported through the global interrupt */
+ ret = devm_request_irq(&pdev->dev, ddata->irq[0], stm32_timer_cnt_isr,
+ 0, dev_name(dev), counter);
+ if (ret) {
+ dev_err(dev, "Failed to request irq %d (err %d)\n",
+ ddata->irq[0], ret);
+ return ret;
+ }
+ } else {
+ for (i = 0; i < priv->nr_irqs; i++) {
+ /*
+ * Only take care of update IRQ for overflow events, and cc for
+ * capture events.
+ */
+ if (i != STM32_TIMERS_IRQ_UP && i != STM32_TIMERS_IRQ_CC)
+ continue;
+
+ ret = devm_request_irq(&pdev->dev, ddata->irq[i], stm32_timer_cnt_isr,
+ 0, dev_name(dev), counter);
+ if (ret) {
+ dev_err(dev, "Failed to request irq %d (err %d)\n",
+ ddata->irq[i], ret);
+ return ret;
+ }
+ }
+ }
+
/* Reset input selector to its default input */
regmap_write(priv->regmap, TIM_TISEL, 0x0);
diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c
index fb1cb1774674..675447315caf 100644
--- a/drivers/counter/ti-ecap-capture.c
+++ b/drivers/counter/ti-ecap-capture.c
@@ -369,7 +369,7 @@ static const enum counter_synapse_action ecap_cnt_input_actions[] = {
};
static struct counter_comp ecap_cnt_clock_ext[] = {
- COUNTER_COMP_SIGNAL_U64("frequency", ecap_cnt_clk_get_freq, NULL),
+ COUNTER_COMP_FREQUENCY(ecap_cnt_clk_get_freq),
};
static const enum counter_signal_polarity ecap_cnt_pol_avail[] = {
@@ -537,15 +537,13 @@ static int ecap_cnt_probe(struct platform_device *pdev)
return 0;
}
-static int ecap_cnt_remove(struct platform_device *pdev)
+static void ecap_cnt_remove(struct platform_device *pdev)
{
struct counter_device *counter_dev = platform_get_drvdata(pdev);
struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev);
if (ecap_dev->enabled)
ecap_cnt_capture_disable(counter_dev);
-
- return 0;
}
static int ecap_cnt_suspend(struct device *dev)
@@ -600,7 +598,7 @@ MODULE_DEVICE_TABLE(of, ecap_cnt_of_match);
static struct platform_driver ecap_cnt_driver = {
.probe = ecap_cnt_probe,
- .remove = ecap_cnt_remove,
+ .remove_new = ecap_cnt_remove,
.driver = {
.name = "ecap-capture",
.of_match_table = ecap_cnt_of_match,
diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
index b0f24cf3e891..072b11fd6b32 100644
--- a/drivers/counter/ti-eqep.c
+++ b/drivers/counter/ti-eqep.c
@@ -425,7 +425,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
return 0;
}
-static int ti_eqep_remove(struct platform_device *pdev)
+static void ti_eqep_remove(struct platform_device *pdev)
{
struct counter_device *counter = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
@@ -433,8 +433,6 @@ static int ti_eqep_remove(struct platform_device *pdev)
counter_unregister(counter);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
-
- return 0;
}
static const struct of_device_id ti_eqep_of_match[] = {
@@ -445,7 +443,7 @@ MODULE_DEVICE_TABLE(of, ti_eqep_of_match);
static struct platform_driver ti_eqep_driver = {
.probe = ti_eqep_probe,
- .remove = ti_eqep_remove,
+ .remove_new = ti_eqep_remove,
.driver = {
.name = "ti-eqep-cnt",
.of_match_table = ti_eqep_of_match,
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 5f869eacd19a..3da94b382292 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -116,7 +116,8 @@ config EXTCON_MAX77843
config EXTCON_MAX8997
tristate "Maxim MAX8997 EXTCON Support"
- depends on MFD_MAX8997 && IRQ_DOMAIN
+ depends on MFD_MAX8997
+ select IRQ_DOMAIN
help
If you say yes here you get support for the MUIC device of
Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
index 0317b614b680..125016da7fde 100644
--- a/drivers/extcon/extcon-adc-jack.c
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -26,6 +26,7 @@
/**
* struct adc_jack_data - internal data for adc_jack device driver
+ * @dev: The device structure associated with the adc_jack.
* @edev: extcon device.
* @cable_names: list of supported cables.
* @adc_conditions: list of adc value conditions.
@@ -35,6 +36,7 @@
* handling at handling_delay jiffies.
* @handler: extcon event handler called by interrupt handler.
* @chan: iio channel being queried.
+ * @wakeup_source: Indicates if the device can wake up the system.
*/
struct adc_jack_data {
struct device *dev;
@@ -158,14 +160,12 @@ static int adc_jack_probe(struct platform_device *pdev)
return 0;
}
-static int adc_jack_remove(struct platform_device *pdev)
+static void adc_jack_remove(struct platform_device *pdev)
{
struct adc_jack_data *data = platform_get_drvdata(pdev);
free_irq(data->irq, data);
cancel_work_sync(&data->handler.work);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(adc_jack_pm_ops,
static struct platform_driver adc_jack_driver = {
.probe = adc_jack_probe,
- .remove = adc_jack_remove,
+ .remove_new = adc_jack_remove,
.driver = {
.name = "adc-jack",
.pm = &adc_jack_pm_ops,
diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c
index 2c55f06ba699..733c470c3102 100644
--- a/drivers/extcon/extcon-intel-cht-wc.c
+++ b/drivers/extcon/extcon-intel-cht-wc.c
@@ -617,13 +617,11 @@ disable_sw_control:
return ret;
}
-static int cht_wc_extcon_remove(struct platform_device *pdev)
+static void cht_wc_extcon_remove(struct platform_device *pdev)
{
struct cht_wc_extcon_data *ext = platform_get_drvdata(pdev);
cht_wc_extcon_sw_control(ext, false);
-
- return 0;
}
static const struct platform_device_id cht_wc_extcon_table[] = {
@@ -634,7 +632,7 @@ MODULE_DEVICE_TABLE(platform, cht_wc_extcon_table);
static struct platform_driver cht_wc_extcon_driver = {
.probe = cht_wc_extcon_probe,
- .remove = cht_wc_extcon_remove,
+ .remove_new = cht_wc_extcon_remove,
.id_table = cht_wc_extcon_table,
.driver = {
.name = "cht_wcove_pwrsrc",
diff --git a/drivers/extcon/extcon-intel-mrfld.c b/drivers/extcon/extcon-intel-mrfld.c
index cd1a5f230077..a1f737f13d49 100644
--- a/drivers/extcon/extcon-intel-mrfld.c
+++ b/drivers/extcon/extcon-intel-mrfld.c
@@ -214,27 +214,21 @@ static int mrfld_extcon_probe(struct platform_device *pdev)
data->edev = devm_extcon_dev_allocate(dev, mrfld_extcon_cable);
if (IS_ERR(data->edev))
- return -ENOMEM;
+ return PTR_ERR(data->edev);
ret = devm_extcon_dev_register(dev, data->edev);
- if (ret < 0) {
- dev_err(dev, "can't register extcon device: %d\n", ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "can't register extcon device\n");
ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_extcon_interrupt,
IRQF_ONESHOT | IRQF_SHARED, pdev->name,
data);
- if (ret) {
- dev_err(dev, "can't register IRQ handler: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't register IRQ handler\n");
ret = regmap_read(regmap, BCOVE_ID, &id);
- if (ret) {
- dev_err(dev, "can't read PMIC ID: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't read PMIC ID\n");
data->id = id;
@@ -263,13 +257,11 @@ static int mrfld_extcon_probe(struct platform_device *pdev)
return 0;
}
-static int mrfld_extcon_remove(struct platform_device *pdev)
+static void mrfld_extcon_remove(struct platform_device *pdev)
{
struct mrfld_extcon_data *data = platform_get_drvdata(pdev);
mrfld_extcon_sw_control(data, false);
-
- return 0;
}
static const struct platform_device_id mrfld_extcon_id_table[] = {
@@ -283,7 +275,7 @@ static struct platform_driver mrfld_extcon_driver = {
.name = "mrfld_bcove_pwrsrc",
},
.probe = mrfld_extcon_probe,
- .remove = mrfld_extcon_remove,
+ .remove_new = mrfld_extcon_remove,
.id_table = mrfld_extcon_id_table,
};
module_platform_driver(mrfld_extcon_driver);
diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c
index d7795607f693..e62ce7a8d131 100644
--- a/drivers/extcon/extcon-max3355.c
+++ b/drivers/extcon/extcon-max3355.c
@@ -112,13 +112,11 @@ static int max3355_probe(struct platform_device *pdev)
return 0;
}
-static int max3355_remove(struct platform_device *pdev)
+static void max3355_remove(struct platform_device *pdev)
{
struct max3355_data *data = platform_get_drvdata(pdev);
gpiod_set_value_cansleep(data->shdn_gpiod, 0);
-
- return 0;
}
static const struct of_device_id max3355_match_table[] = {
@@ -129,7 +127,7 @@ MODULE_DEVICE_TABLE(of, max3355_match_table);
static struct platform_driver max3355_driver = {
.probe = max3355_probe,
- .remove = max3355_remove,
+ .remove_new = max3355_remove,
.driver = {
.name = "extcon-max3355",
.of_match_table = max3355_match_table,
diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c
index acb11a54f875..9849e3b8327e 100644
--- a/drivers/extcon/extcon-max77843.c
+++ b/drivers/extcon/extcon-max77843.c
@@ -928,7 +928,7 @@ err_muic_irq:
return ret;
}
-static int max77843_muic_remove(struct platform_device *pdev)
+static void max77843_muic_remove(struct platform_device *pdev)
{
struct max77843_muic_info *info = platform_get_drvdata(pdev);
struct max77693_dev *max77843 = info->max77843;
@@ -936,8 +936,6 @@ static int max77843_muic_remove(struct platform_device *pdev)
cancel_work_sync(&info->irq_work);
regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
i2c_unregister_device(max77843->i2c_muic);
-
- return 0;
}
static const struct platform_device_id max77843_muic_id[] = {
@@ -958,7 +956,7 @@ static struct platform_driver max77843_muic_driver = {
.of_match_table = of_max77843_muic_dt_match,
},
.probe = max77843_muic_probe,
- .remove = max77843_muic_remove,
+ .remove_new = max77843_muic_remove,
.id_table = max77843_muic_id,
};
diff --git a/drivers/extcon/extcon-rtk-type-c.c b/drivers/extcon/extcon-rtk-type-c.c
index a592bab77538..19a01e663733 100644
--- a/drivers/extcon/extcon-rtk-type-c.c
+++ b/drivers/extcon/extcon-rtk-type-c.c
@@ -13,7 +13,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/syscalls.h>
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index 40d967a11e87..9b61eb99b7dc 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -193,14 +193,12 @@ static int usb_extcon_probe(struct platform_device *pdev)
return 0;
}
-static int usb_extcon_remove(struct platform_device *pdev)
+static void usb_extcon_remove(struct platform_device *pdev)
{
struct usb_extcon_info *info = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&info->wq_detcable);
device_init_wakeup(&pdev->dev, false);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -281,7 +279,7 @@ MODULE_DEVICE_TABLE(platform, usb_extcon_platform_ids);
static struct platform_driver usb_extcon_driver = {
.probe = usb_extcon_probe,
- .remove = usb_extcon_remove,
+ .remove_new = usb_extcon_remove,
.driver = {
.name = "extcon-usb-gpio",
.pm = &usb_extcon_pm_ops,
diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c
index fde1db62be0d..805a47230689 100644
--- a/drivers/extcon/extcon-usbc-cros-ec.c
+++ b/drivers/extcon/extcon-usbc-cros-ec.c
@@ -480,14 +480,12 @@ unregister_notifier:
return ret;
}
-static int extcon_cros_ec_remove(struct platform_device *pdev)
+static void extcon_cros_ec_remove(struct platform_device *pdev)
{
struct cros_ec_extcon_info *info = platform_get_drvdata(pdev);
blocking_notifier_chain_unregister(&info->ec->event_notifier,
&info->notifier);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -531,7 +529,7 @@ static struct platform_driver extcon_cros_ec_driver = {
.of_match_table = of_match_ptr(extcon_cros_ec_of_match),
.pm = DEV_PM_OPS,
},
- .remove = extcon_cros_ec_remove,
+ .remove_new = extcon_cros_ec_remove,
.probe = extcon_cros_ec_probe,
};
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 2f689ac4ba3a..37b35f58f0df 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -64,9 +64,21 @@ config FPGA_MGR_STRATIX10_SOC
help
FPGA manager driver support for the Intel Stratix10 SoC.
+config FPGA_MGR_XILINX_CORE
+ tristate
+
+config FPGA_MGR_XILINX_SELECTMAP
+ tristate "Xilinx Configuration over SelectMAP"
+ depends on HAS_IOMEM
+ select FPGA_MGR_XILINX_CORE
+ help
+ FPGA manager driver support for Xilinx FPGA configuration
+ over SelectMAP interface.
+
config FPGA_MGR_XILINX_SPI
tristate "Xilinx Configuration over Slave Serial (SPI)"
depends on SPI
+ select FPGA_MGR_XILINX_CORE
help
FPGA manager driver support for Xilinx FPGA configuration
over slave serial interface.
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 352a2612623e..aeb89bb13517 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -15,6 +15,8 @@ obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
obj-$(CONFIG_FPGA_MGR_STRATIX10_SOC) += stratix10-soc.o
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
+obj-$(CONFIG_FPGA_MGR_XILINX_CORE) += xilinx-core.o
+obj-$(CONFIG_FPGA_MGR_XILINX_SELECTMAP) += xilinx-selectmap.o
obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA) += zynqmp-fpga.o
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 4ffb9da537d8..6b0914432445 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -72,7 +72,6 @@ static bool altera_cvp_chkcfg;
struct cvp_priv;
struct altera_cvp_conf {
- struct fpga_manager *mgr;
struct pci_dev *pci_dev;
void __iomem *map;
void (*write_data)(struct altera_cvp_conf *conf,
diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
index 740980e7cef8..d0ec3539b31f 100644
--- a/drivers/fpga/altera-ps-spi.c
+++ b/drivers/fpga/altera-ps-spi.c
@@ -284,7 +284,6 @@ MODULE_DEVICE_TABLE(spi, altera_ps_spi_ids);
static struct spi_driver altera_ps_driver = {
.driver = {
.name = "altera-ps-spi",
- .owner = THIS_MODULE,
.of_match_table = of_ef_match,
},
.id_table = altera_ps_spi_ids,
diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
index c0a75ca360d6..6b97c073849e 100644
--- a/drivers/fpga/dfl-afu-main.c
+++ b/drivers/fpga/dfl-afu-main.c
@@ -858,8 +858,6 @@ static int afu_dev_init(struct platform_device *pdev)
if (!afu)
return -ENOMEM;
- afu->pdata = pdata;
-
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, afu);
afu_mmio_region_init(pdata);
diff --git a/drivers/fpga/dfl-afu.h b/drivers/fpga/dfl-afu.h
index 674e9772f0ea..7bef3e300aa2 100644
--- a/drivers/fpga/dfl-afu.h
+++ b/drivers/fpga/dfl-afu.h
@@ -67,7 +67,6 @@ struct dfl_afu_dma_region {
* @regions: the mmio region linked list of this afu feature device.
* @dma_regions: root of dma regions rb tree.
* @num_umsgs: num of umsgs.
- * @pdata: afu platform device's pdata.
*/
struct dfl_afu {
u64 region_cur_offset;
@@ -75,8 +74,6 @@ struct dfl_afu {
u8 num_umsgs;
struct list_head regions;
struct rb_root dma_regions;
-
- struct dfl_feature_platform_data *pdata;
};
/* hold pdata->lock when call __afu_port_enable/disable */
diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
index a2b5da0093da..864924f68f5e 100644
--- a/drivers/fpga/dfl-fme-main.c
+++ b/drivers/fpga/dfl-fme-main.c
@@ -679,8 +679,6 @@ static int fme_dev_init(struct platform_device *pdev)
if (!fme)
return -ENOMEM;
- fme->pdata = pdata;
-
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, fme);
mutex_unlock(&pdata->lock);
diff --git a/drivers/fpga/dfl-fme.h b/drivers/fpga/dfl-fme.h
index 4195dd68193e..a566dbc2b485 100644
--- a/drivers/fpga/dfl-fme.h
+++ b/drivers/fpga/dfl-fme.h
@@ -24,13 +24,11 @@
* @mgr: FME's FPGA manager platform device.
* @region_list: linked list of FME's FPGA regions.
* @bridge_list: linked list of FME's FPGA bridges.
- * @pdata: fme platform device's pdata.
*/
struct dfl_fme {
struct platform_device *mgr;
struct list_head region_list;
struct list_head bridge_list;
- struct dfl_feature_platform_data *pdata;
};
extern const struct dfl_feature_ops fme_pr_mgmt_ops;
diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
index 1d724a28f00a..5063d73b0d82 100644
--- a/drivers/fpga/dfl.h
+++ b/drivers/fpga/dfl.h
@@ -437,11 +437,6 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id)
return NULL;
}
-static inline bool is_dfl_feature_present(struct device *dev, u16 id)
-{
- return !!dfl_get_feature_ioaddr_by_id(dev, id);
-}
-
static inline
struct device *dfl_fpga_pdata_to_parent(struct dfl_feature_platform_data *pdata)
{
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
index 79c473b3c7c3..8ef395b49bf8 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -55,33 +55,26 @@ int fpga_bridge_disable(struct fpga_bridge *bridge)
}
EXPORT_SYMBOL_GPL(fpga_bridge_disable);
-static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
+static struct fpga_bridge *__fpga_bridge_get(struct device *bridge_dev,
struct fpga_image_info *info)
{
struct fpga_bridge *bridge;
- int ret = -ENODEV;
- bridge = to_fpga_bridge(dev);
+ bridge = to_fpga_bridge(bridge_dev);
bridge->info = info;
- if (!mutex_trylock(&bridge->mutex)) {
- ret = -EBUSY;
- goto err_dev;
- }
+ if (!mutex_trylock(&bridge->mutex))
+ return ERR_PTR(-EBUSY);
- if (!try_module_get(dev->parent->driver->owner))
- goto err_ll_mod;
+ if (!try_module_get(bridge->br_ops_owner)) {
+ mutex_unlock(&bridge->mutex);
+ return ERR_PTR(-ENODEV);
+ }
dev_dbg(&bridge->dev, "get\n");
return bridge;
-
-err_ll_mod:
- mutex_unlock(&bridge->mutex);
-err_dev:
- put_device(dev);
- return ERR_PTR(ret);
}
/**
@@ -98,13 +91,18 @@ err_dev:
struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
struct fpga_image_info *info)
{
- struct device *dev;
+ struct fpga_bridge *bridge;
+ struct device *bridge_dev;
- dev = class_find_device_by_of_node(&fpga_bridge_class, np);
- if (!dev)
+ bridge_dev = class_find_device_by_of_node(&fpga_bridge_class, np);
+ if (!bridge_dev)
return ERR_PTR(-ENODEV);
- return __fpga_bridge_get(dev, info);
+ bridge = __fpga_bridge_get(bridge_dev, info);
+ if (IS_ERR(bridge))
+ put_device(bridge_dev);
+
+ return bridge;
}
EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
@@ -125,6 +123,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data)
struct fpga_bridge *fpga_bridge_get(struct device *dev,
struct fpga_image_info *info)
{
+ struct fpga_bridge *bridge;
struct device *bridge_dev;
bridge_dev = class_find_device(&fpga_bridge_class, NULL, dev,
@@ -132,7 +131,11 @@ struct fpga_bridge *fpga_bridge_get(struct device *dev,
if (!bridge_dev)
return ERR_PTR(-ENODEV);
- return __fpga_bridge_get(bridge_dev, info);
+ bridge = __fpga_bridge_get(bridge_dev, info);
+ if (IS_ERR(bridge))
+ put_device(bridge_dev);
+
+ return bridge;
}
EXPORT_SYMBOL_GPL(fpga_bridge_get);
@@ -146,7 +149,7 @@ void fpga_bridge_put(struct fpga_bridge *bridge)
dev_dbg(&bridge->dev, "put\n");
bridge->info = NULL;
- module_put(bridge->dev.parent->driver->owner);
+ module_put(bridge->br_ops_owner);
mutex_unlock(&bridge->mutex);
put_device(&bridge->dev);
}
@@ -316,18 +319,19 @@ static struct attribute *fpga_bridge_attrs[] = {
ATTRIBUTE_GROUPS(fpga_bridge);
/**
- * fpga_bridge_register - create and register an FPGA Bridge device
+ * __fpga_bridge_register - create and register an FPGA Bridge device
* @parent: FPGA bridge device from pdev
* @name: FPGA bridge name
* @br_ops: pointer to structure of fpga bridge ops
* @priv: FPGA bridge private data
+ * @owner: owner module containing the br_ops
*
* Return: struct fpga_bridge pointer or ERR_PTR()
*/
struct fpga_bridge *
-fpga_bridge_register(struct device *parent, const char *name,
- const struct fpga_bridge_ops *br_ops,
- void *priv)
+__fpga_bridge_register(struct device *parent, const char *name,
+ const struct fpga_bridge_ops *br_ops,
+ void *priv, struct module *owner)
{
struct fpga_bridge *bridge;
int id, ret;
@@ -357,6 +361,7 @@ fpga_bridge_register(struct device *parent, const char *name,
bridge->name = name;
bridge->br_ops = br_ops;
+ bridge->br_ops_owner = owner;
bridge->priv = priv;
bridge->dev.groups = br_ops->groups;
@@ -386,7 +391,7 @@ error_kfree:
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(fpga_bridge_register);
+EXPORT_SYMBOL_GPL(__fpga_bridge_register);
/**
* fpga_bridge_unregister - unregister an FPGA bridge
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index 06651389c592..0f4035b089a2 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -664,20 +664,16 @@ static struct attribute *fpga_mgr_attrs[] = {
};
ATTRIBUTE_GROUPS(fpga_mgr);
-static struct fpga_manager *__fpga_mgr_get(struct device *dev)
+static struct fpga_manager *__fpga_mgr_get(struct device *mgr_dev)
{
struct fpga_manager *mgr;
- mgr = to_fpga_manager(dev);
+ mgr = to_fpga_manager(mgr_dev);
- if (!try_module_get(dev->parent->driver->owner))
- goto err_dev;
+ if (!try_module_get(mgr->mops_owner))
+ mgr = ERR_PTR(-ENODEV);
return mgr;
-
-err_dev:
- put_device(dev);
- return ERR_PTR(-ENODEV);
}
static int fpga_mgr_dev_match(struct device *dev, const void *data)
@@ -693,12 +689,18 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
*/
struct fpga_manager *fpga_mgr_get(struct device *dev)
{
- struct device *mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev,
- fpga_mgr_dev_match);
+ struct fpga_manager *mgr;
+ struct device *mgr_dev;
+
+ mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev, fpga_mgr_dev_match);
if (!mgr_dev)
return ERR_PTR(-ENODEV);
- return __fpga_mgr_get(mgr_dev);
+ mgr = __fpga_mgr_get(mgr_dev);
+ if (IS_ERR(mgr))
+ put_device(mgr_dev);
+
+ return mgr;
}
EXPORT_SYMBOL_GPL(fpga_mgr_get);
@@ -711,13 +713,18 @@ EXPORT_SYMBOL_GPL(fpga_mgr_get);
*/
struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
{
- struct device *dev;
+ struct fpga_manager *mgr;
+ struct device *mgr_dev;
- dev = class_find_device_by_of_node(&fpga_mgr_class, node);
- if (!dev)
+ mgr_dev = class_find_device_by_of_node(&fpga_mgr_class, node);
+ if (!mgr_dev)
return ERR_PTR(-ENODEV);
- return __fpga_mgr_get(dev);
+ mgr = __fpga_mgr_get(mgr_dev);
+ if (IS_ERR(mgr))
+ put_device(mgr_dev);
+
+ return mgr;
}
EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
@@ -727,7 +734,7 @@ EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
*/
void fpga_mgr_put(struct fpga_manager *mgr)
{
- module_put(mgr->dev.parent->driver->owner);
+ module_put(mgr->mops_owner);
put_device(&mgr->dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_put);
@@ -766,9 +773,10 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
/**
- * fpga_mgr_register_full - create and register an FPGA Manager device
+ * __fpga_mgr_register_full - create and register an FPGA Manager device
* @parent: fpga manager device from pdev
* @info: parameters for fpga manager
+ * @owner: owner module containing the ops
*
* The caller of this function is responsible for calling fpga_mgr_unregister().
* Using devm_fpga_mgr_register_full() instead is recommended.
@@ -776,7 +784,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
struct fpga_manager *
-fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
+__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
+ struct module *owner)
{
const struct fpga_manager_ops *mops = info->mops;
struct fpga_manager *mgr;
@@ -804,6 +813,8 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
mutex_init(&mgr->ref_mutex);
+ mgr->mops_owner = owner;
+
mgr->name = info->name;
mgr->mops = info->mops;
mgr->priv = info->priv;
@@ -841,14 +852,15 @@ error_kfree:
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
+EXPORT_SYMBOL_GPL(__fpga_mgr_register_full);
/**
- * fpga_mgr_register - create and register an FPGA Manager device
+ * __fpga_mgr_register - create and register an FPGA Manager device
* @parent: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
+ * @owner: owner module containing the ops
*
* The caller of this function is responsible for calling fpga_mgr_unregister().
* Using devm_fpga_mgr_register() instead is recommended. This simple
@@ -859,8 +871,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
struct fpga_manager *
-fpga_mgr_register(struct device *parent, const char *name,
- const struct fpga_manager_ops *mops, void *priv)
+__fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv, struct module *owner)
{
struct fpga_manager_info info = { 0 };
@@ -868,9 +880,9 @@ fpga_mgr_register(struct device *parent, const char *name,
info.mops = mops;
info.priv = priv;
- return fpga_mgr_register_full(parent, &info);
+ return __fpga_mgr_register_full(parent, &info, owner);
}
-EXPORT_SYMBOL_GPL(fpga_mgr_register);
+EXPORT_SYMBOL_GPL(__fpga_mgr_register);
/**
* fpga_mgr_unregister - unregister an FPGA manager
@@ -900,9 +912,10 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
}
/**
- * devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
+ * __devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
* @parent: fpga manager device from pdev
* @info: parameters for fpga manager
+ * @owner: owner module containing the ops
*
* Return: fpga manager pointer on success, negative error code otherwise.
*
@@ -910,7 +923,8 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
* function will be called automatically when the managing device is detached.
*/
struct fpga_manager *
-devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
+__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
+ struct module *owner)
{
struct fpga_mgr_devres *dr;
struct fpga_manager *mgr;
@@ -919,7 +933,7 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
if (!dr)
return ERR_PTR(-ENOMEM);
- mgr = fpga_mgr_register_full(parent, info);
+ mgr = __fpga_mgr_register_full(parent, info, owner);
if (IS_ERR(mgr)) {
devres_free(dr);
return mgr;
@@ -930,14 +944,15 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
return mgr;
}
-EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
+EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register_full);
/**
- * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
+ * __devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
* @parent: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
+ * @owner: owner module containing the ops
*
* Return: fpga manager pointer on success, negative error code otherwise.
*
@@ -946,8 +961,9 @@ EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
* device is detached.
*/
struct fpga_manager *
-devm_fpga_mgr_register(struct device *parent, const char *name,
- const struct fpga_manager_ops *mops, void *priv)
+__devm_fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv,
+ struct module *owner)
{
struct fpga_manager_info info = { 0 };
@@ -955,9 +971,9 @@ devm_fpga_mgr_register(struct device *parent, const char *name,
info.mops = mops;
info.priv = priv;
- return devm_fpga_mgr_register_full(parent, &info);
+ return __devm_fpga_mgr_register_full(parent, &info, owner);
}
-EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
+EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register);
static void fpga_mgr_dev_release(struct device *dev)
{
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index b364a929425c..753cd142503e 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -53,7 +53,7 @@ static struct fpga_region *fpga_region_get(struct fpga_region *region)
}
get_device(dev);
- if (!try_module_get(dev->parent->driver->owner)) {
+ if (!try_module_get(region->ops_owner)) {
put_device(dev);
mutex_unlock(&region->mutex);
return ERR_PTR(-ENODEV);
@@ -75,7 +75,7 @@ static void fpga_region_put(struct fpga_region *region)
dev_dbg(dev, "put\n");
- module_put(dev->parent->driver->owner);
+ module_put(region->ops_owner);
put_device(dev);
mutex_unlock(&region->mutex);
}
@@ -181,14 +181,16 @@ static struct attribute *fpga_region_attrs[] = {
ATTRIBUTE_GROUPS(fpga_region);
/**
- * fpga_region_register_full - create and register an FPGA Region device
+ * __fpga_region_register_full - create and register an FPGA Region device
* @parent: device parent
* @info: parameters for FPGA Region
+ * @owner: module containing the get_bridges function
*
* Return: struct fpga_region or ERR_PTR()
*/
struct fpga_region *
-fpga_region_register_full(struct device *parent, const struct fpga_region_info *info)
+__fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
+ struct module *owner)
{
struct fpga_region *region;
int id, ret = 0;
@@ -213,6 +215,7 @@ fpga_region_register_full(struct device *parent, const struct fpga_region_info *
region->compat_id = info->compat_id;
region->priv = info->priv;
region->get_bridges = info->get_bridges;
+ region->ops_owner = owner;
mutex_init(&region->mutex);
INIT_LIST_HEAD(&region->bridge_list);
@@ -241,13 +244,14 @@ err_free:
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(fpga_region_register_full);
+EXPORT_SYMBOL_GPL(__fpga_region_register_full);
/**
- * fpga_region_register - create and register an FPGA Region device
+ * __fpga_region_register - create and register an FPGA Region device
* @parent: device parent
* @mgr: manager that programs this region
* @get_bridges: optional function to get bridges to a list
+ * @owner: module containing the get_bridges function
*
* This simple version of the register function should be sufficient for most users.
* The fpga_region_register_full() function is available for users that need to
@@ -256,17 +260,17 @@ EXPORT_SYMBOL_GPL(fpga_region_register_full);
* Return: struct fpga_region or ERR_PTR()
*/
struct fpga_region *
-fpga_region_register(struct device *parent, struct fpga_manager *mgr,
- int (*get_bridges)(struct fpga_region *))
+__fpga_region_register(struct device *parent, struct fpga_manager *mgr,
+ int (*get_bridges)(struct fpga_region *), struct module *owner)
{
struct fpga_region_info info = { 0 };
info.mgr = mgr;
info.get_bridges = get_bridges;
- return fpga_region_register_full(parent, &info);
+ return __fpga_region_register_full(parent, &info, owner);
}
-EXPORT_SYMBOL_GPL(fpga_region_register);
+EXPORT_SYMBOL_GPL(__fpga_region_register);
/**
* fpga_region_unregister - unregister an FPGA region
diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
index c0028ae4c5b7..62c30266130d 100644
--- a/drivers/fpga/ice40-spi.c
+++ b/drivers/fpga/ice40-spi.c
@@ -10,8 +10,8 @@
#include <linux/fpga/fpga-mgr.h>
#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/stringify.h>
@@ -199,7 +199,7 @@ static struct spi_driver ice40_fpga_driver = {
.probe = ice40_fpga_probe,
.driver = {
.name = "ice40spi",
- .of_match_table = of_match_ptr(ice40_fpga_of_match),
+ .of_match_table = ice40_fpga_of_match,
},
.id_table = ice40_fpga_spi_ids,
};
diff --git a/drivers/fpga/tests/fpga-bridge-test.c b/drivers/fpga/tests/fpga-bridge-test.c
index 1d258002cdd7..2f7a24f23808 100644
--- a/drivers/fpga/tests/fpga-bridge-test.c
+++ b/drivers/fpga/tests/fpga-bridge-test.c
@@ -7,8 +7,8 @@
* Author: Marco Pagani <marpagan@redhat.com>
*/
+#include <kunit/device.h>
#include <kunit/test.h>
-#include <linux/device.h>
#include <linux/fpga/fpga-bridge.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -19,7 +19,7 @@ struct bridge_stats {
struct bridge_ctx {
struct fpga_bridge *bridge;
- struct platform_device *pdev;
+ struct device *dev;
struct bridge_stats stats;
};
@@ -43,30 +43,31 @@ static const struct fpga_bridge_ops fake_bridge_ops = {
/**
* register_test_bridge() - Register a fake FPGA bridge for testing.
* @test: KUnit test context object.
+ * @dev_name: name of the kunit device to be registered
*
* Return: Context of the newly registered FPGA bridge.
*/
-static struct bridge_ctx *register_test_bridge(struct kunit *test)
+static struct bridge_ctx *register_test_bridge(struct kunit *test, const char *dev_name)
{
struct bridge_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
- ctx->pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
+ ctx->dev = kunit_device_register(test, dev_name);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
- ctx->bridge = fpga_bridge_register(&ctx->pdev->dev, "Fake FPGA bridge", &fake_bridge_ops,
+ ctx->bridge = fpga_bridge_register(ctx->dev, "Fake FPGA bridge", &fake_bridge_ops,
&ctx->stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
return ctx;
}
-static void unregister_test_bridge(struct bridge_ctx *ctx)
+static void unregister_test_bridge(struct kunit *test, struct bridge_ctx *ctx)
{
fpga_bridge_unregister(ctx->bridge);
- platform_device_unregister(ctx->pdev);
+ kunit_device_unregister(test, ctx->dev);
}
static void fpga_bridge_test_get(struct kunit *test)
@@ -74,10 +75,10 @@ static void fpga_bridge_test_get(struct kunit *test)
struct bridge_ctx *ctx = test->priv;
struct fpga_bridge *bridge;
- bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
+ bridge = fpga_bridge_get(ctx->dev, NULL);
KUNIT_EXPECT_PTR_EQ(test, bridge, ctx->bridge);
- bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
+ bridge = fpga_bridge_get(ctx->dev, NULL);
KUNIT_EXPECT_EQ(test, PTR_ERR(bridge), -EBUSY);
fpga_bridge_put(ctx->bridge);
@@ -105,19 +106,19 @@ static void fpga_bridge_test_get_put_list(struct kunit *test)
int ret;
ctx_0 = test->priv;
- ctx_1 = register_test_bridge(test);
+ ctx_1 = register_test_bridge(test, "fpga-bridge-test-dev-1");
INIT_LIST_HEAD(&bridge_list);
/* Get bridge 0 and add it to the list */
- ret = fpga_bridge_get_to_list(&ctx_0->pdev->dev, NULL, &bridge_list);
+ ret = fpga_bridge_get_to_list(ctx_0->dev, NULL, &bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, ctx_0->bridge,
list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
/* Get bridge 1 and add it to the list */
- ret = fpga_bridge_get_to_list(&ctx_1->pdev->dev, NULL, &bridge_list);
+ ret = fpga_bridge_get_to_list(ctx_1->dev, NULL, &bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, ctx_1->bridge,
@@ -141,19 +142,19 @@ static void fpga_bridge_test_get_put_list(struct kunit *test)
KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list));
- unregister_test_bridge(ctx_1);
+ unregister_test_bridge(test, ctx_1);
}
static int fpga_bridge_test_init(struct kunit *test)
{
- test->priv = register_test_bridge(test);
+ test->priv = register_test_bridge(test, "fpga-bridge-test-dev-0");
return 0;
}
static void fpga_bridge_test_exit(struct kunit *test)
{
- unregister_test_bridge(test->priv);
+ unregister_test_bridge(test, test->priv);
}
static struct kunit_case fpga_bridge_test_cases[] = {
diff --git a/drivers/fpga/tests/fpga-mgr-test.c b/drivers/fpga/tests/fpga-mgr-test.c
index 6acec55b60ce..125b3a4d43c6 100644
--- a/drivers/fpga/tests/fpga-mgr-test.c
+++ b/drivers/fpga/tests/fpga-mgr-test.c
@@ -7,8 +7,8 @@
* Author: Marco Pagani <marpagan@redhat.com>
*/
+#include <kunit/device.h>
#include <kunit/test.h>
-#include <linux/device.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
@@ -40,7 +40,7 @@ struct mgr_stats {
struct mgr_ctx {
struct fpga_image_info *img_info;
struct fpga_manager *mgr;
- struct platform_device *pdev;
+ struct device *dev;
struct mgr_stats stats;
};
@@ -194,7 +194,7 @@ static void fpga_mgr_test_get(struct kunit *test)
struct mgr_ctx *ctx = test->priv;
struct fpga_manager *mgr;
- mgr = fpga_mgr_get(&ctx->pdev->dev);
+ mgr = fpga_mgr_get(ctx->dev);
KUNIT_EXPECT_PTR_EQ(test, mgr, ctx->mgr);
fpga_mgr_put(ctx->mgr);
@@ -284,14 +284,14 @@ static int fpga_mgr_test_init(struct kunit *test)
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
- ctx->pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
+ ctx->dev = kunit_device_register(test, "fpga-manager-test-dev");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
- ctx->mgr = devm_fpga_mgr_register(&ctx->pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
+ ctx->mgr = devm_fpga_mgr_register(ctx->dev, "Fake FPGA Manager", &fake_mgr_ops,
&ctx->stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
- ctx->img_info = fpga_image_info_alloc(&ctx->pdev->dev);
+ ctx->img_info = fpga_image_info_alloc(ctx->dev);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->img_info);
test->priv = ctx;
@@ -304,7 +304,7 @@ static void fpga_mgr_test_exit(struct kunit *test)
struct mgr_ctx *ctx = test->priv;
fpga_image_info_free(ctx->img_info);
- platform_device_unregister(ctx->pdev);
+ kunit_device_unregister(test, ctx->dev);
}
static struct kunit_case fpga_mgr_test_cases[] = {
diff --git a/drivers/fpga/tests/fpga-region-test.c b/drivers/fpga/tests/fpga-region-test.c
index baab07e3fc59..bcf0651df261 100644
--- a/drivers/fpga/tests/fpga-region-test.c
+++ b/drivers/fpga/tests/fpga-region-test.c
@@ -7,12 +7,12 @@
* Author: Marco Pagani <marpagan@redhat.com>
*/
+#include <kunit/device.h>
#include <kunit/test.h>
#include <linux/fpga/fpga-bridge.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/fpga/fpga-region.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/types.h>
struct mgr_stats {
@@ -26,11 +26,11 @@ struct bridge_stats {
struct test_ctx {
struct fpga_manager *mgr;
- struct platform_device *mgr_pdev;
+ struct device *mgr_dev;
struct fpga_bridge *bridge;
- struct platform_device *bridge_pdev;
+ struct device *bridge_dev;
struct fpga_region *region;
- struct platform_device *region_pdev;
+ struct device *region_dev;
struct bridge_stats bridge_stats;
struct mgr_stats mgr_stats;
};
@@ -91,7 +91,7 @@ static void fpga_region_test_class_find(struct kunit *test)
struct test_ctx *ctx = test->priv;
struct fpga_region *region;
- region = fpga_region_class_find(NULL, &ctx->region_pdev->dev, fake_region_match);
+ region = fpga_region_class_find(NULL, ctx->region_dev, fake_region_match);
KUNIT_EXPECT_PTR_EQ(test, region, ctx->region);
put_device(&region->dev);
@@ -108,7 +108,7 @@ static void fpga_region_test_program_fpga(struct kunit *test)
char img_buf[4];
int ret;
- img_info = fpga_image_info_alloc(&ctx->mgr_pdev->dev);
+ img_info = fpga_image_info_alloc(ctx->mgr_dev);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
img_info->buf = img_buf;
@@ -148,32 +148,30 @@ static int fpga_region_test_init(struct kunit *test)
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
- ctx->mgr_pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_pdev);
+ ctx->mgr_dev = kunit_device_register(test, "fpga-manager-test-dev");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_dev);
- ctx->mgr = devm_fpga_mgr_register(&ctx->mgr_pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
- &ctx->mgr_stats);
+ ctx->mgr = devm_fpga_mgr_register(ctx->mgr_dev, "Fake FPGA Manager",
+ &fake_mgr_ops, &ctx->mgr_stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
- ctx->bridge_pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO,
- NULL, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_pdev);
+ ctx->bridge_dev = kunit_device_register(test, "fpga-bridge-test-dev");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_dev);
- ctx->bridge = fpga_bridge_register(&ctx->bridge_pdev->dev, "Fake FPGA Bridge",
+ ctx->bridge = fpga_bridge_register(ctx->bridge_dev, "Fake FPGA Bridge",
&fake_bridge_ops, &ctx->bridge_stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
ctx->bridge_stats.enable = true;
- ctx->region_pdev = platform_device_register_simple("region_pdev", PLATFORM_DEVID_AUTO,
- NULL, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_pdev);
+ ctx->region_dev = kunit_device_register(test, "fpga-region-test-dev");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_dev);
region_info.mgr = ctx->mgr;
region_info.priv = ctx->bridge;
region_info.get_bridges = fake_region_get_bridges;
- ctx->region = fpga_region_register_full(&ctx->region_pdev->dev, &region_info);
+ ctx->region = fpga_region_register_full(ctx->region_dev, &region_info);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->region));
test->priv = ctx;
@@ -186,18 +184,17 @@ static void fpga_region_test_exit(struct kunit *test)
struct test_ctx *ctx = test->priv;
fpga_region_unregister(ctx->region);
- platform_device_unregister(ctx->region_pdev);
+ kunit_device_unregister(test, ctx->region_dev);
fpga_bridge_unregister(ctx->bridge);
- platform_device_unregister(ctx->bridge_pdev);
+ kunit_device_unregister(test, ctx->bridge_dev);
- platform_device_unregister(ctx->mgr_pdev);
+ kunit_device_unregister(test, ctx->mgr_dev);
}
static struct kunit_case fpga_region_test_cases[] = {
KUNIT_CASE(fpga_region_test_class_find),
KUNIT_CASE(fpga_region_test_program_fpga),
-
{}
};
diff --git a/drivers/fpga/xilinx-core.c b/drivers/fpga/xilinx-core.c
new file mode 100644
index 000000000000..39aeacf2e4f1
--- /dev/null
+++ b/drivers/fpga/xilinx-core.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Common parts of the Xilinx Spartan6 and 7 Series FPGA manager drivers.
+ *
+ * Copyright (C) 2017 DENX Software Engineering
+ *
+ * Anatolij Gustschin <agust@denx.de>
+ */
+
+#include "xilinx-core.h"
+
+#include <linux/delay.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+
+static int get_done_gpio(struct fpga_manager *mgr)
+{
+ struct xilinx_fpga_core *core = mgr->priv;
+ int ret;
+
+ ret = gpiod_get_value(core->done);
+ if (ret < 0)
+ dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret);
+
+ return ret;
+}
+
+static enum fpga_mgr_states xilinx_core_state(struct fpga_manager *mgr)
+{
+ if (!get_done_gpio(mgr))
+ return FPGA_MGR_STATE_RESET;
+
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+/**
+ * wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
+ * a given delay if the pin is unavailable
+ *
+ * @mgr: The FPGA manager object
+ * @value: Value INIT_B to wait for (1 = asserted = low)
+ * @alt_udelay: Delay to wait if the INIT_B GPIO is not available
+ *
+ * Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
+ * too much time passed waiting for that. If no INIT_B GPIO is available
+ * then always return 0.
+ */
+static int wait_for_init_b(struct fpga_manager *mgr, int value,
+ unsigned long alt_udelay)
+{
+ struct xilinx_fpga_core *core = mgr->priv;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ if (core->init_b) {
+ while (time_before(jiffies, timeout)) {
+ int ret = gpiod_get_value(core->init_b);
+
+ if (ret == value)
+ return 0;
+
+ if (ret < 0) {
+ dev_err(&mgr->dev,
+ "Error reading INIT_B (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(100, 400);
+ }
+
+ dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n",
+ value ? "assert" : "deassert");
+ return -ETIMEDOUT;
+ }
+
+ udelay(alt_udelay);
+
+ return 0;
+}
+
+static int xilinx_core_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info, const char *buf,
+ size_t count)
+{
+ struct xilinx_fpga_core *core = mgr->priv;
+ int err;
+
+ if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
+ dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
+ return -EINVAL;
+ }
+
+ gpiod_set_value(core->prog_b, 1);
+
+ err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
+ if (err) {
+ gpiod_set_value(core->prog_b, 0);
+ return err;
+ }
+
+ gpiod_set_value(core->prog_b, 0);
+
+ err = wait_for_init_b(mgr, 0, 0);
+ if (err)
+ return err;
+
+ if (get_done_gpio(mgr)) {
+ dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
+ return -EIO;
+ }
+
+ /* program latency */
+ usleep_range(7500, 7600);
+ return 0;
+}
+
+static int xilinx_core_write(struct fpga_manager *mgr, const char *buf,
+ size_t count)
+{
+ struct xilinx_fpga_core *core = mgr->priv;
+
+ return core->write(core, buf, count);
+}
+
+static int xilinx_core_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct xilinx_fpga_core *core = mgr->priv;
+ unsigned long timeout =
+ jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
+ bool expired = false;
+ int done;
+ int ret;
+ const char padding[1] = { 0xff };
+
+ /*
+ * This loop is carefully written such that if the driver is
+ * scheduled out for more than 'timeout', we still check for DONE
+ * before giving up and we apply 8 extra CCLK cycles in all cases.
+ */
+ while (!expired) {
+ expired = time_after(jiffies, timeout);
+
+ done = get_done_gpio(mgr);
+ if (done < 0)
+ return done;
+
+ ret = core->write(core, padding, sizeof(padding));
+ if (ret)
+ return ret;
+
+ if (done)
+ return 0;
+ }
+
+ if (core->init_b) {
+ ret = gpiod_get_value(core->init_b);
+
+ if (ret < 0) {
+ dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
+ return ret;
+ }
+
+ dev_err(&mgr->dev,
+ ret ? "CRC error or invalid device\n" :
+ "Missing sync word or incomplete bitstream\n");
+ } else {
+ dev_err(&mgr->dev, "Timeout after config data transfer\n");
+ }
+
+ return -ETIMEDOUT;
+}
+
+static inline struct gpio_desc *
+xilinx_core_devm_gpiod_get(struct device *dev, const char *con_id,
+ const char *legacy_con_id, enum gpiod_flags flags)
+{
+ struct gpio_desc *desc;
+
+ desc = devm_gpiod_get(dev, con_id, flags);
+ if (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT &&
+ of_device_is_compatible(dev->of_node, "xlnx,fpga-slave-serial"))
+ desc = devm_gpiod_get(dev, legacy_con_id, flags);
+
+ return desc;
+}
+
+static const struct fpga_manager_ops xilinx_core_ops = {
+ .state = xilinx_core_state,
+ .write_init = xilinx_core_write_init,
+ .write = xilinx_core_write,
+ .write_complete = xilinx_core_write_complete,
+};
+
+int xilinx_core_probe(struct xilinx_fpga_core *core)
+{
+ struct fpga_manager *mgr;
+
+ if (!core || !core->dev || !core->write)
+ return -EINVAL;
+
+ /* PROGRAM_B is active low */
+ core->prog_b = xilinx_core_devm_gpiod_get(core->dev, "prog", "prog_b",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(core->prog_b))
+ return dev_err_probe(core->dev, PTR_ERR(core->prog_b),
+ "Failed to get PROGRAM_B gpio\n");
+
+ core->init_b = xilinx_core_devm_gpiod_get(core->dev, "init", "init-b",
+ GPIOD_IN);
+ if (IS_ERR(core->init_b))
+ return dev_err_probe(core->dev, PTR_ERR(core->init_b),
+ "Failed to get INIT_B gpio\n");
+
+ core->done = devm_gpiod_get(core->dev, "done", GPIOD_IN);
+ if (IS_ERR(core->done))
+ return dev_err_probe(core->dev, PTR_ERR(core->done),
+ "Failed to get DONE gpio\n");
+
+ mgr = devm_fpga_mgr_register(core->dev,
+ "Xilinx Slave Serial FPGA Manager",
+ &xilinx_core_ops, core);
+ return PTR_ERR_OR_ZERO(mgr);
+}
+EXPORT_SYMBOL_GPL(xilinx_core_probe);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
+MODULE_DESCRIPTION("Xilinx 7 Series FPGA manager core");
diff --git a/drivers/fpga/xilinx-core.h b/drivers/fpga/xilinx-core.h
new file mode 100644
index 000000000000..f02ac67fce7b
--- /dev/null
+++ b/drivers/fpga/xilinx-core.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __XILINX_CORE_H
+#define __XILINX_CORE_H
+
+#include <linux/device.h>
+
+/**
+ * struct xilinx_fpga_core - interface between the driver and the core manager
+ * of Xilinx 7 Series FPGA manager
+ * @dev: device node
+ * @write: write callback of the driver
+ */
+struct xilinx_fpga_core {
+/* public: */
+ struct device *dev;
+ int (*write)(struct xilinx_fpga_core *core, const char *buf,
+ size_t count);
+/* private: handled by xilinx-core */
+ struct gpio_desc *prog_b;
+ struct gpio_desc *init_b;
+ struct gpio_desc *done;
+};
+
+int xilinx_core_probe(struct xilinx_fpga_core *core);
+
+#endif /* __XILINX_CORE_H */
diff --git a/drivers/fpga/xilinx-selectmap.c b/drivers/fpga/xilinx-selectmap.c
new file mode 100644
index 000000000000..2cd87e7e913f
--- /dev/null
+++ b/drivers/fpga/xilinx-selectmap.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Xilinx Spartan6 and 7 Series SelectMAP interface driver
+ *
+ * (C) 2024 Charles Perry <charles.perry@savoirfairelinux.com>
+ *
+ * Manage Xilinx FPGA firmware loaded over the SelectMAP configuration
+ * interface.
+ */
+
+#include "xilinx-core.h"
+
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+struct xilinx_selectmap_conf {
+ struct xilinx_fpga_core core;
+ void __iomem *base;
+};
+
+#define to_xilinx_selectmap_conf(obj) \
+ container_of(obj, struct xilinx_selectmap_conf, core)
+
+static int xilinx_selectmap_write(struct xilinx_fpga_core *core,
+ const char *buf, size_t count)
+{
+ struct xilinx_selectmap_conf *conf = to_xilinx_selectmap_conf(core);
+ size_t i;
+
+ for (i = 0; i < count; ++i)
+ writeb(buf[i], conf->base);
+
+ return 0;
+}
+
+static int xilinx_selectmap_probe(struct platform_device *pdev)
+{
+ struct xilinx_selectmap_conf *conf;
+ struct gpio_desc *gpio;
+ void __iomem *base;
+
+ conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL);
+ if (!conf)
+ return -ENOMEM;
+
+ conf->core.dev = &pdev->dev;
+ conf->core.write = xilinx_selectmap_write;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(base),
+ "ioremap error\n");
+ conf->base = base;
+
+ /* CSI_B is active low */
+ gpio = devm_gpiod_get_optional(&pdev->dev, "csi", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
+ "Failed to get CSI_B gpio\n");
+
+ /* RDWR_B is active low */
+ gpio = devm_gpiod_get_optional(&pdev->dev, "rdwr", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
+ "Failed to get RDWR_B gpio\n");
+
+ return xilinx_core_probe(&conf->core);
+}
+
+static const struct of_device_id xlnx_selectmap_of_match[] = {
+ { .compatible = "xlnx,fpga-xc7s-selectmap", }, // Spartan-7
+ { .compatible = "xlnx,fpga-xc7a-selectmap", }, // Artix-7
+ { .compatible = "xlnx,fpga-xc7k-selectmap", }, // Kintex-7
+ { .compatible = "xlnx,fpga-xc7v-selectmap", }, // Virtex-7
+ {},
+};
+MODULE_DEVICE_TABLE(of, xlnx_selectmap_of_match);
+
+static struct platform_driver xilinx_selectmap_driver = {
+ .driver = {
+ .name = "xilinx-selectmap",
+ .of_match_table = xlnx_selectmap_of_match,
+ },
+ .probe = xilinx_selectmap_probe,
+};
+
+module_platform_driver(xilinx_selectmap_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Charles Perry <charles.perry@savoirfairelinux.com>");
+MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SelectMap");
diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
index e1a227e7ff2a..8756504340de 100644
--- a/drivers/fpga/xilinx-spi.c
+++ b/drivers/fpga/xilinx-spi.c
@@ -10,127 +10,17 @@
* the slave serial configuration interface.
*/
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/fpga/fpga-mgr.h>
-#include <linux/gpio/consumer.h>
+#include "xilinx-core.h"
+
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
-#include <linux/sizes.h>
-
-struct xilinx_spi_conf {
- struct spi_device *spi;
- struct gpio_desc *prog_b;
- struct gpio_desc *init_b;
- struct gpio_desc *done;
-};
-
-static int get_done_gpio(struct fpga_manager *mgr)
-{
- struct xilinx_spi_conf *conf = mgr->priv;
- int ret;
-
- ret = gpiod_get_value(conf->done);
-
- if (ret < 0)
- dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret);
-
- return ret;
-}
-
-static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
-{
- if (!get_done_gpio(mgr))
- return FPGA_MGR_STATE_RESET;
-
- return FPGA_MGR_STATE_UNKNOWN;
-}
-
-/**
- * wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
- * a given delay if the pin is unavailable
- *
- * @mgr: The FPGA manager object
- * @value: Value INIT_B to wait for (1 = asserted = low)
- * @alt_udelay: Delay to wait if the INIT_B GPIO is not available
- *
- * Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
- * too much time passed waiting for that. If no INIT_B GPIO is available
- * then always return 0.
- */
-static int wait_for_init_b(struct fpga_manager *mgr, int value,
- unsigned long alt_udelay)
-{
- struct xilinx_spi_conf *conf = mgr->priv;
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
- if (conf->init_b) {
- while (time_before(jiffies, timeout)) {
- int ret = gpiod_get_value(conf->init_b);
-
- if (ret == value)
- return 0;
-
- if (ret < 0) {
- dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
- return ret;
- }
-
- usleep_range(100, 400);
- }
-
- dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n",
- value ? "assert" : "deassert");
- return -ETIMEDOUT;
- }
-
- udelay(alt_udelay);
-
- return 0;
-}
-
-static int xilinx_spi_write_init(struct fpga_manager *mgr,
- struct fpga_image_info *info,
- const char *buf, size_t count)
-{
- struct xilinx_spi_conf *conf = mgr->priv;
- int err;
-
- if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
- dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
- return -EINVAL;
- }
-
- gpiod_set_value(conf->prog_b, 1);
-
- err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
- if (err) {
- gpiod_set_value(conf->prog_b, 0);
- return err;
- }
-
- gpiod_set_value(conf->prog_b, 0);
-
- err = wait_for_init_b(mgr, 0, 0);
- if (err)
- return err;
-
- if (get_done_gpio(mgr)) {
- dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
- return -EIO;
- }
- /* program latency */
- usleep_range(7500, 7600);
- return 0;
-}
-
-static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
+static int xilinx_spi_write(struct xilinx_fpga_core *core, const char *buf,
size_t count)
{
- struct xilinx_spi_conf *conf = mgr->priv;
+ struct spi_device *spi = to_spi_device(core->dev);
const char *fw_data = buf;
const char *fw_data_end = fw_data + count;
@@ -141,9 +31,9 @@ static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
remaining = fw_data_end - fw_data;
stride = min_t(size_t, remaining, SZ_4K);
- ret = spi_write(conf->spi, fw_data, stride);
+ ret = spi_write(spi, fw_data, stride);
if (ret) {
- dev_err(&mgr->dev, "SPI error in firmware write: %d\n",
+ dev_err(core->dev, "SPI error in firmware write: %d\n",
ret);
return ret;
}
@@ -153,109 +43,25 @@ static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
return 0;
}
-static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf)
-{
- struct spi_device *spi = conf->spi;
- const u8 din_data[1] = { 0xff };
- int ret;
-
- ret = spi_write(conf->spi, din_data, sizeof(din_data));
- if (ret)
- dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret);
-
- return ret;
-}
-
-static int xilinx_spi_write_complete(struct fpga_manager *mgr,
- struct fpga_image_info *info)
-{
- struct xilinx_spi_conf *conf = mgr->priv;
- unsigned long timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
- bool expired = false;
- int done;
- int ret;
-
- /*
- * This loop is carefully written such that if the driver is
- * scheduled out for more than 'timeout', we still check for DONE
- * before giving up and we apply 8 extra CCLK cycles in all cases.
- */
- while (!expired) {
- expired = time_after(jiffies, timeout);
-
- done = get_done_gpio(mgr);
- if (done < 0)
- return done;
-
- ret = xilinx_spi_apply_cclk_cycles(conf);
- if (ret)
- return ret;
-
- if (done)
- return 0;
- }
-
- if (conf->init_b) {
- ret = gpiod_get_value(conf->init_b);
-
- if (ret < 0) {
- dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
- return ret;
- }
-
- dev_err(&mgr->dev,
- ret ? "CRC error or invalid device\n"
- : "Missing sync word or incomplete bitstream\n");
- } else {
- dev_err(&mgr->dev, "Timeout after config data transfer\n");
- }
-
- return -ETIMEDOUT;
-}
-
-static const struct fpga_manager_ops xilinx_spi_ops = {
- .state = xilinx_spi_state,
- .write_init = xilinx_spi_write_init,
- .write = xilinx_spi_write,
- .write_complete = xilinx_spi_write_complete,
-};
-
static int xilinx_spi_probe(struct spi_device *spi)
{
- struct xilinx_spi_conf *conf;
- struct fpga_manager *mgr;
+ struct xilinx_fpga_core *core;
- conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
- if (!conf)
+ core = devm_kzalloc(&spi->dev, sizeof(*core), GFP_KERNEL);
+ if (!core)
return -ENOMEM;
- conf->spi = spi;
+ core->dev = &spi->dev;
+ core->write = xilinx_spi_write;
- /* PROGRAM_B is active low */
- conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW);
- if (IS_ERR(conf->prog_b))
- return dev_err_probe(&spi->dev, PTR_ERR(conf->prog_b),
- "Failed to get PROGRAM_B gpio\n");
-
- conf->init_b = devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN);
- if (IS_ERR(conf->init_b))
- return dev_err_probe(&spi->dev, PTR_ERR(conf->init_b),
- "Failed to get INIT_B gpio\n");
-
- conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
- if (IS_ERR(conf->done))
- return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
- "Failed to get DONE gpio\n");
-
- mgr = devm_fpga_mgr_register(&spi->dev,
- "Xilinx Slave Serial FPGA Manager",
- &xilinx_spi_ops, conf);
- return PTR_ERR_OR_ZERO(mgr);
+ return xilinx_core_probe(core);
}
#ifdef CONFIG_OF
static const struct of_device_id xlnx_spi_of_match[] = {
- { .compatible = "xlnx,fpga-slave-serial", },
+ {
+ .compatible = "xlnx,fpga-slave-serial",
+ },
{}
};
MODULE_DEVICE_TABLE(of, xlnx_spi_of_match);
diff --git a/drivers/greybus/interface.c b/drivers/greybus/interface.c
index fd58a86b0888..d022bfb5e95d 100644
--- a/drivers/greybus/interface.c
+++ b/drivers/greybus/interface.c
@@ -693,6 +693,7 @@ static void gb_interface_release(struct device *dev)
trace_gb_interface_release(intf);
+ cancel_work_sync(&intf->mode_switch_work);
kfree(intf);
}
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index d76df5c8c2a9..b992c0ed182b 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -10,7 +10,7 @@ hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \
channel_mgmt.o ring_buffer.o hv_trace.o
hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o
-hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
+hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o
# Code that must be built-in
obj-$(subst m,y,$(CONFIG_HYPERV)) += hv_common.o
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 2f4d09ce027a..3c6011a48dab 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -120,7 +120,9 @@ const struct vmbus_device vmbus_devs[] = {
},
/* File copy */
- { .dev_type = HV_FCOPY,
+ /* fcopy always uses 16KB ring buffer size and is working well for last many years */
+ { .pref_ring_size = 0x4000,
+ .dev_type = HV_FCOPY,
HV_FCOPY_GUID,
.perf_device = false,
.allowed_in_isolated = false,
@@ -140,12 +142,19 @@ const struct vmbus_device vmbus_devs[] = {
.allowed_in_isolated = false,
},
- /* Unknown GUID */
- { .dev_type = HV_UNKNOWN,
+ /*
+ * Unknown GUID
+ * 64 KB ring buffer + 4 KB header should be sufficient size for any Hyper-V device apart
+ * from HV_NIC and HV_SCSI. This case avoid the fallback for unknown devices to allocate
+ * much bigger (2 MB) of ring size.
+ */
+ { .pref_ring_size = 0x11000,
+ .dev_type = HV_UNKNOWN,
.perf_device = false,
.allowed_in_isolated = false,
},
};
+EXPORT_SYMBOL_GPL(vmbus_devs);
static const struct {
guid_t guid;
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
deleted file mode 100644
index 922d83eb7ddf..000000000000
--- a/drivers/hv/hv_fcopy.c
+++ /dev/null
@@ -1,427 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * An implementation of file copy service.
- *
- * Copyright (C) 2014, Microsoft, Inc.
- *
- * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/nls.h>
-#include <linux/workqueue.h>
-#include <linux/hyperv.h>
-#include <linux/sched.h>
-#include <asm/hyperv-tlfs.h>
-
-#include "hyperv_vmbus.h"
-#include "hv_utils_transport.h"
-
-#define WIN8_SRV_MAJOR 1
-#define WIN8_SRV_MINOR 1
-#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
-
-#define FCOPY_VER_COUNT 1
-static const int fcopy_versions[] = {
- WIN8_SRV_VERSION
-};
-
-#define FW_VER_COUNT 1
-static const int fw_versions[] = {
- UTIL_FW_VERSION
-};
-
-/*
- * Global state maintained for transaction that is being processed.
- * For a class of integration services, including the "file copy service",
- * the specified protocol is a "request/response" protocol which means that
- * there can only be single outstanding transaction from the host at any
- * given point in time. We use this to simplify memory management in this
- * driver - we cache and process only one message at a time.
- *
- * While the request/response protocol is guaranteed by the host, we further
- * ensure this by serializing packet processing in this driver - we do not
- * read additional packets from the VMBUs until the current packet is fully
- * handled.
- */
-
-static struct {
- int state; /* hvutil_device_state */
- int recv_len; /* number of bytes received. */
- struct hv_fcopy_hdr *fcopy_msg; /* current message */
- struct vmbus_channel *recv_channel; /* chn we got the request */
- u64 recv_req_id; /* request ID. */
-} fcopy_transaction;
-
-static void fcopy_respond_to_host(int error);
-static void fcopy_send_data(struct work_struct *dummy);
-static void fcopy_timeout_func(struct work_struct *dummy);
-static DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func);
-static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
-static const char fcopy_devname[] = "vmbus/hv_fcopy";
-static u8 *recv_buffer;
-static struct hvutil_transport *hvt;
-/*
- * This state maintains the version number registered by the daemon.
- */
-static int dm_reg_value;
-
-static void fcopy_poll_wrapper(void *channel)
-{
- /* Transaction is finished, reset the state here to avoid races. */
- fcopy_transaction.state = HVUTIL_READY;
- tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
-}
-
-static void fcopy_timeout_func(struct work_struct *dummy)
-{
- /*
- * If the timer fires, the user-mode component has not responded;
- * process the pending transaction.
- */
- fcopy_respond_to_host(HV_E_FAIL);
- hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
-}
-
-static void fcopy_register_done(void)
-{
- pr_debug("FCP: userspace daemon registered\n");
- hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
-}
-
-static int fcopy_handle_handshake(u32 version)
-{
- u32 our_ver = FCOPY_CURRENT_VERSION;
-
- switch (version) {
- case FCOPY_VERSION_0:
- /* Daemon doesn't expect us to reply */
- dm_reg_value = version;
- break;
- case FCOPY_VERSION_1:
- /* Daemon expects us to reply with our own version */
- if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
- fcopy_register_done))
- return -EFAULT;
- dm_reg_value = version;
- break;
- default:
- /*
- * For now we will fail the registration.
- * If and when we have multiple versions to
- * deal with, we will be backward compatible.
- * We will add this code when needed.
- */
- return -EINVAL;
- }
- pr_debug("FCP: userspace daemon ver. %d connected\n", version);
- return 0;
-}
-
-static void fcopy_send_data(struct work_struct *dummy)
-{
- struct hv_start_fcopy *smsg_out = NULL;
- int operation = fcopy_transaction.fcopy_msg->operation;
- struct hv_start_fcopy *smsg_in;
- void *out_src;
- int rc, out_len;
-
- /*
- * The strings sent from the host are encoded in
- * utf16; convert it to utf8 strings.
- * The host assures us that the utf16 strings will not exceed
- * the max lengths specified. We will however, reserve room
- * for the string terminating character - in the utf16s_utf8s()
- * function we limit the size of the buffer where the converted
- * string is placed to W_MAX_PATH -1 to guarantee
- * that the strings can be properly terminated!
- */
-
- switch (operation) {
- case START_FILE_COPY:
- out_len = sizeof(struct hv_start_fcopy);
- smsg_out = kzalloc(sizeof(*smsg_out), GFP_KERNEL);
- if (!smsg_out)
- return;
-
- smsg_out->hdr.operation = operation;
- smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg;
-
- utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH,
- UTF16_LITTLE_ENDIAN,
- (__u8 *)&smsg_out->file_name, W_MAX_PATH - 1);
-
- utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH,
- UTF16_LITTLE_ENDIAN,
- (__u8 *)&smsg_out->path_name, W_MAX_PATH - 1);
-
- smsg_out->copy_flags = smsg_in->copy_flags;
- smsg_out->file_size = smsg_in->file_size;
- out_src = smsg_out;
- break;
-
- case WRITE_TO_FILE:
- out_src = fcopy_transaction.fcopy_msg;
- out_len = sizeof(struct hv_do_fcopy);
- break;
- default:
- out_src = fcopy_transaction.fcopy_msg;
- out_len = fcopy_transaction.recv_len;
- break;
- }
-
- fcopy_transaction.state = HVUTIL_USERSPACE_REQ;
- rc = hvutil_transport_send(hvt, out_src, out_len, NULL);
- if (rc) {
- pr_debug("FCP: failed to communicate to the daemon: %d\n", rc);
- if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
- fcopy_respond_to_host(HV_E_FAIL);
- fcopy_transaction.state = HVUTIL_READY;
- }
- }
- kfree(smsg_out);
-}
-
-/*
- * Send a response back to the host.
- */
-
-static void
-fcopy_respond_to_host(int error)
-{
- struct icmsg_hdr *icmsghdr;
- u32 buf_len;
- struct vmbus_channel *channel;
- u64 req_id;
-
- /*
- * Copy the global state for completing the transaction. Note that
- * only one transaction can be active at a time. This is guaranteed
- * by the file copy protocol implemented by the host. Furthermore,
- * the "transaction active" state we maintain ensures that there can
- * only be one active transaction at a time.
- */
-
- buf_len = fcopy_transaction.recv_len;
- channel = fcopy_transaction.recv_channel;
- req_id = fcopy_transaction.recv_req_id;
-
- icmsghdr = (struct icmsg_hdr *)
- &recv_buffer[sizeof(struct vmbuspipe_hdr)];
-
- if (channel->onchannel_callback == NULL)
- /*
- * We have raced with util driver being unloaded;
- * silently return.
- */
- return;
-
- icmsghdr->status = error;
- icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
- vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
- VM_PKT_DATA_INBAND, 0);
-}
-
-void hv_fcopy_onchannelcallback(void *context)
-{
- struct vmbus_channel *channel = context;
- u32 recvlen;
- u64 requestid;
- struct hv_fcopy_hdr *fcopy_msg;
- struct icmsg_hdr *icmsghdr;
- int fcopy_srv_version;
-
- if (fcopy_transaction.state > HVUTIL_READY)
- return;
-
- if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) {
- pr_err_ratelimited("Fcopy request received. Could not read into recv buf\n");
- return;
- }
-
- if (!recvlen)
- return;
-
- /* Ensure recvlen is big enough to read header data */
- if (recvlen < ICMSG_HDR) {
- pr_err_ratelimited("Fcopy request received. Packet length too small: %d\n",
- recvlen);
- return;
- }
-
- icmsghdr = (struct icmsg_hdr *)&recv_buffer[
- sizeof(struct vmbuspipe_hdr)];
-
- if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
- if (vmbus_prep_negotiate_resp(icmsghdr,
- recv_buffer, recvlen,
- fw_versions, FW_VER_COUNT,
- fcopy_versions, FCOPY_VER_COUNT,
- NULL, &fcopy_srv_version)) {
-
- pr_info("FCopy IC version %d.%d\n",
- fcopy_srv_version >> 16,
- fcopy_srv_version & 0xFFFF);
- }
- } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) {
- /* Ensure recvlen is big enough to contain hv_fcopy_hdr */
- if (recvlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) {
- pr_err_ratelimited("Invalid Fcopy hdr. Packet length too small: %u\n",
- recvlen);
- return;
- }
- fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ICMSG_HDR];
-
- /*
- * Stash away this global state for completing the
- * transaction; note transactions are serialized.
- */
-
- fcopy_transaction.recv_len = recvlen;
- fcopy_transaction.recv_req_id = requestid;
- fcopy_transaction.fcopy_msg = fcopy_msg;
-
- if (fcopy_transaction.state < HVUTIL_READY) {
- /* Userspace is not registered yet */
- fcopy_respond_to_host(HV_E_FAIL);
- return;
- }
- fcopy_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
-
- /*
- * Send the information to the user-level daemon.
- */
- schedule_work(&fcopy_send_work);
- schedule_delayed_work(&fcopy_timeout_work,
- HV_UTIL_TIMEOUT * HZ);
- return;
- } else {
- pr_err_ratelimited("Fcopy request received. Invalid msg type: %d\n",
- icmsghdr->icmsgtype);
- return;
- }
- icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
- vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
- VM_PKT_DATA_INBAND, 0);
-}
-
-/* Callback when data is received from userspace */
-static int fcopy_on_msg(void *msg, int len)
-{
- int *val = (int *)msg;
-
- if (len != sizeof(int))
- return -EINVAL;
-
- if (fcopy_transaction.state == HVUTIL_DEVICE_INIT)
- return fcopy_handle_handshake(*val);
-
- if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ)
- return -EINVAL;
-
- /*
- * Complete the transaction by forwarding the result
- * to the host. But first, cancel the timeout.
- */
- if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
- fcopy_transaction.state = HVUTIL_USERSPACE_RECV;
- fcopy_respond_to_host(*val);
- hv_poll_channel(fcopy_transaction.recv_channel,
- fcopy_poll_wrapper);
- }
-
- return 0;
-}
-
-static void fcopy_on_reset(void)
-{
- /*
- * The daemon has exited; reset the state.
- */
- fcopy_transaction.state = HVUTIL_DEVICE_INIT;
-
- if (cancel_delayed_work_sync(&fcopy_timeout_work))
- fcopy_respond_to_host(HV_E_FAIL);
-}
-
-int hv_fcopy_init(struct hv_util_service *srv)
-{
- recv_buffer = srv->recv_buffer;
- fcopy_transaction.recv_channel = srv->channel;
- fcopy_transaction.recv_channel->max_pkt_size = HV_HYP_PAGE_SIZE * 2;
-
- /*
- * When this driver loads, the user level daemon that
- * processes the host requests may not yet be running.
- * Defer processing channel callbacks until the daemon
- * has registered.
- */
- fcopy_transaction.state = HVUTIL_DEVICE_INIT;
-
- hvt = hvutil_transport_init(fcopy_devname, 0, 0,
- fcopy_on_msg, fcopy_on_reset);
- if (!hvt)
- return -EFAULT;
-
- return 0;
-}
-
-static void hv_fcopy_cancel_work(void)
-{
- cancel_delayed_work_sync(&fcopy_timeout_work);
- cancel_work_sync(&fcopy_send_work);
-}
-
-int hv_fcopy_pre_suspend(void)
-{
- struct vmbus_channel *channel = fcopy_transaction.recv_channel;
- struct hv_fcopy_hdr *fcopy_msg;
-
- /*
- * Fake a CANCEL_FCOPY message for the user space daemon in case the
- * daemon is in the middle of copying some file. It doesn't matter if
- * there is already a message pending to be delivered to the user
- * space since we force fcopy_transaction.state to be HVUTIL_READY, so
- * the user space daemon's write() will fail with EINVAL (see
- * fcopy_on_msg()), and the daemon will reset the device by closing
- * and re-opening it.
- */
- fcopy_msg = kzalloc(sizeof(*fcopy_msg), GFP_KERNEL);
- if (!fcopy_msg)
- return -ENOMEM;
-
- tasklet_disable(&channel->callback_event);
-
- fcopy_msg->operation = CANCEL_FCOPY;
-
- hv_fcopy_cancel_work();
-
- /* We don't care about the return value. */
- hvutil_transport_send(hvt, fcopy_msg, sizeof(*fcopy_msg), NULL);
-
- kfree(fcopy_msg);
-
- fcopy_transaction.state = HVUTIL_READY;
-
- /* tasklet_enable() will be called in hv_fcopy_pre_resume(). */
- return 0;
-}
-
-int hv_fcopy_pre_resume(void)
-{
- struct vmbus_channel *channel = fcopy_transaction.recv_channel;
-
- tasklet_enable(&channel->callback_event);
-
- return 0;
-}
-
-void hv_fcopy_deinit(void)
-{
- fcopy_transaction.state = HVUTIL_DEVICE_DYING;
-
- hv_fcopy_cancel_work();
-
- hvutil_transport_destroy(hvt);
-}
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 9c97c4065fe7..c4f525325790 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -154,14 +154,6 @@ static struct hv_util_service util_vss = {
.util_deinit = hv_vss_deinit,
};
-static struct hv_util_service util_fcopy = {
- .util_cb = hv_fcopy_onchannelcallback,
- .util_init = hv_fcopy_init,
- .util_pre_suspend = hv_fcopy_pre_suspend,
- .util_pre_resume = hv_fcopy_pre_resume,
- .util_deinit = hv_fcopy_deinit,
-};
-
static void perform_shutdown(struct work_struct *dummy)
{
orderly_poweroff(true);
@@ -700,10 +692,6 @@ static const struct hv_vmbus_device_id id_table[] = {
{ HV_VSS_GUID,
.driver_data = (unsigned long)&util_vss
},
- /* File copy GUID */
- { HV_FCOPY_GUID,
- .driver_data = (unsigned long)&util_fcopy
- },
{ },
};
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index f6b1e710f805..76ac5185a01a 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -417,6 +417,11 @@ static inline bool hv_is_perf_channel(struct vmbus_channel *channel)
return vmbus_devs[channel->device_id].perf_device;
}
+static inline size_t hv_dev_ring_size(struct vmbus_channel *channel)
+{
+ return vmbus_devs[channel->device_id].pref_ring_size;
+}
+
static inline bool hv_is_allocated_cpu(unsigned int cpu)
{
struct vmbus_channel *channel, *sc;
diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c
index 375bd0d89b0c..bfea880d6dfb 100644
--- a/drivers/hwtracing/coresight/coresight-catu.c
+++ b/drivers/hwtracing/coresight/coresight-catu.c
@@ -7,11 +7,13 @@
* Author: Suzuki K Poulose <suzuki.poulose@arm.com>
*/
+#include <linux/acpi.h>
#include <linux/amba/bus.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "coresight-catu.h"
@@ -502,28 +504,20 @@ static const struct coresight_ops catu_ops = {
.helper_ops = &catu_helper_ops,
};
-static int catu_probe(struct amba_device *adev, const struct amba_id *id)
+static int __catu_probe(struct device *dev, struct resource *res)
{
int ret = 0;
u32 dma_mask;
- struct catu_drvdata *drvdata;
+ struct catu_drvdata *drvdata = dev_get_drvdata(dev);
struct coresight_desc catu_desc;
struct coresight_platform_data *pdata = NULL;
- struct device *dev = &adev->dev;
void __iomem *base;
catu_desc.name = coresight_alloc_device_name(&catu_devs, dev);
if (!catu_desc.name)
return -ENOMEM;
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
- ret = -ENOMEM;
- goto out;
- }
-
- dev_set_drvdata(dev, drvdata);
- base = devm_ioremap_resource(dev, &adev->res);
+ base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
goto out;
@@ -567,19 +561,39 @@ static int catu_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->csdev = coresight_register(&catu_desc);
if (IS_ERR(drvdata->csdev))
ret = PTR_ERR(drvdata->csdev);
- else
- pm_runtime_put(&adev->dev);
out:
return ret;
}
-static void catu_remove(struct amba_device *adev)
+static int catu_probe(struct amba_device *adev, const struct amba_id *id)
{
- struct catu_drvdata *drvdata = dev_get_drvdata(&adev->dev);
+ struct catu_drvdata *drvdata;
+ int ret;
+
+ drvdata = devm_kzalloc(&adev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ amba_set_drvdata(adev, drvdata);
+ ret = __catu_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
+}
+
+static void __catu_remove(struct device *dev)
+{
+ struct catu_drvdata *drvdata = dev_get_drvdata(dev);
coresight_unregister(drvdata->csdev);
}
+static void catu_remove(struct amba_device *adev)
+{
+ __catu_remove(&adev->dev);
+}
+
static struct amba_id catu_ids[] = {
CS_AMBA_ID(0x000bb9ee),
{},
@@ -597,13 +611,98 @@ static struct amba_driver catu_driver = {
.id_table = catu_ids,
};
+static int catu_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct catu_drvdata *drvdata;
+ int ret = 0;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->pclk = coresight_get_enable_apb_pclk(&pdev->dev);
+ if (IS_ERR(drvdata->pclk))
+ return -ENODEV;
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ dev_set_drvdata(&pdev->dev, drvdata);
+ ret = __catu_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret) {
+ pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
+ }
+
+ return ret;
+}
+
+static void catu_platform_remove(struct platform_device *pdev)
+{
+ struct catu_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
+ __catu_remove(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
+}
+
+#ifdef CONFIG_PM
+static int catu_runtime_suspend(struct device *dev)
+{
+ struct catu_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
+ return 0;
+}
+
+static int catu_runtime_resume(struct device *dev)
+{
+ struct catu_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_prepare_enable(drvdata->pclk);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops catu_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(catu_runtime_suspend, catu_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id catu_acpi_ids[] = {
+ {"ARMHC9CA", 0, 0, 0}, /* ARM CoreSight CATU */
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, catu_acpi_ids);
+#endif
+
+static struct platform_driver catu_platform_driver = {
+ .probe = catu_platform_probe,
+ .remove_new = catu_platform_remove,
+ .driver = {
+ .name = "coresight-catu-platform",
+ .acpi_match_table = ACPI_PTR(catu_acpi_ids),
+ .suppress_bind_attrs = true,
+ .pm = &catu_dev_pm_ops,
+ },
+};
+
static int __init catu_init(void)
{
int ret;
- ret = amba_driver_register(&catu_driver);
- if (ret)
- pr_info("Error registering catu driver\n");
+ ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver);
tmc_etr_set_catu_ops(&etr_catu_buf_ops);
return ret;
}
@@ -611,7 +710,7 @@ static int __init catu_init(void)
static void __exit catu_exit(void)
{
tmc_etr_remove_catu_ops();
- amba_driver_unregister(&catu_driver);
+ coresight_remove_driver(&catu_driver, &catu_platform_driver);
}
module_init(catu_init);
diff --git a/drivers/hwtracing/coresight/coresight-catu.h b/drivers/hwtracing/coresight/coresight-catu.h
index 442e034bbfba..141feac1c14b 100644
--- a/drivers/hwtracing/coresight/coresight-catu.h
+++ b/drivers/hwtracing/coresight/coresight-catu.h
@@ -61,6 +61,7 @@
#define CATU_IRQEN_OFF 0x0
struct catu_drvdata {
+ struct clk *pclk;
void __iomem *base;
struct coresight_device *csdev;
int irq;
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index b83613e34289..9fc6f6b863e0 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1398,6 +1398,35 @@ static void __exit coresight_exit(void)
module_init(coresight_init);
module_exit(coresight_exit);
+int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
+ struct platform_driver *pdev_drv)
+{
+ int ret;
+
+ ret = amba_driver_register(amba_drv);
+ if (ret) {
+ pr_err("%s: error registering AMBA driver\n", drv);
+ return ret;
+ }
+
+ ret = platform_driver_register(pdev_drv);
+ if (!ret)
+ return 0;
+
+ pr_err("%s: error registering platform driver\n", drv);
+ amba_driver_unregister(amba_drv);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(coresight_init_driver);
+
+void coresight_remove_driver(struct amba_driver *amba_drv,
+ struct platform_driver *pdev_drv)
+{
+ amba_driver_unregister(amba_drv);
+ platform_driver_unregister(pdev_drv);
+}
+EXPORT_SYMBOL_GPL(coresight_remove_driver);
+
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
index 1874df7c6a73..75962dae9aa1 100644
--- a/drivers/hwtracing/coresight/coresight-cpu-debug.c
+++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
@@ -4,6 +4,7 @@
*
* Author: Leo Yan <leo.yan@linaro.org>
*/
+#include <linux/acpi.h>
#include <linux/amba/bus.h>
#include <linux/coresight.h>
#include <linux/cpu.h>
@@ -18,6 +19,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/panic_notifier.h>
+#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/smp.h>
@@ -84,6 +86,7 @@
#define DEBUG_WAIT_TIMEOUT 32000
struct debug_drvdata {
+ struct clk *pclk;
void __iomem *base;
struct device *dev;
int cpu;
@@ -557,18 +560,12 @@ static void debug_func_exit(void)
debugfs_remove_recursive(debug_debugfs_dir);
}
-static int debug_probe(struct amba_device *adev, const struct amba_id *id)
+static int __debug_probe(struct device *dev, struct resource *res)
{
+ struct debug_drvdata *drvdata = dev_get_drvdata(dev);
void __iomem *base;
- struct device *dev = &adev->dev;
- struct debug_drvdata *drvdata;
- struct resource *res = &adev->res;
int ret;
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
- return -ENOMEM;
-
drvdata->cpu = coresight_get_cpu(dev);
if (drvdata->cpu < 0)
return drvdata->cpu;
@@ -579,10 +576,7 @@ static int debug_probe(struct amba_device *adev, const struct amba_id *id)
return -EBUSY;
}
- drvdata->dev = &adev->dev;
- amba_set_drvdata(adev, drvdata);
-
- /* Validity for the resource is already checked by the AMBA core */
+ drvdata->dev = dev;
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -629,10 +623,21 @@ err:
return ret;
}
-static void debug_remove(struct amba_device *adev)
+static int debug_probe(struct amba_device *adev, const struct amba_id *id)
{
- struct device *dev = &adev->dev;
- struct debug_drvdata *drvdata = amba_get_drvdata(adev);
+ struct debug_drvdata *drvdata;
+
+ drvdata = devm_kzalloc(&adev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ amba_set_drvdata(adev, drvdata);
+ return __debug_probe(&adev->dev, &adev->res);
+}
+
+static void __debug_remove(struct device *dev)
+{
+ struct debug_drvdata *drvdata = dev_get_drvdata(dev);
per_cpu(debug_drvdata, drvdata->cpu) = NULL;
@@ -646,6 +651,11 @@ static void debug_remove(struct amba_device *adev)
debug_func_exit();
}
+static void debug_remove(struct amba_device *adev)
+{
+ __debug_remove(&adev->dev);
+}
+
static const struct amba_cs_uci_id uci_id_debug[] = {
{
/* CPU Debug UCI data */
@@ -677,7 +687,102 @@ static struct amba_driver debug_driver = {
.id_table = debug_ids,
};
-module_amba_driver(debug_driver);
+static int debug_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct debug_drvdata *drvdata;
+ int ret = 0;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->pclk = coresight_get_enable_apb_pclk(&pdev->dev);
+ if (IS_ERR(drvdata->pclk))
+ return -ENODEV;
+
+ dev_set_drvdata(&pdev->dev, drvdata);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = __debug_probe(&pdev->dev, res);
+ if (ret) {
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
+ }
+ return ret;
+}
+
+static void debug_platform_remove(struct platform_device *pdev)
+{
+ struct debug_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
+ __debug_remove(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id debug_platform_ids[] = {
+ {"ARMHC503", 0, 0, 0}, /* ARM CoreSight Debug */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, debug_platform_ids);
+#endif
+
+#ifdef CONFIG_PM
+static int debug_runtime_suspend(struct device *dev)
+{
+ struct debug_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
+ return 0;
+}
+
+static int debug_runtime_resume(struct device *dev)
+{
+ struct debug_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_prepare_enable(drvdata->pclk);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops debug_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(debug_runtime_suspend, debug_runtime_resume, NULL)
+};
+
+static struct platform_driver debug_platform_driver = {
+ .probe = debug_platform_probe,
+ .remove_new = debug_platform_remove,
+ .driver = {
+ .name = "coresight-debug-platform",
+ .acpi_match_table = ACPI_PTR(debug_platform_ids),
+ .suppress_bind_attrs = true,
+ .pm = &debug_dev_pm_ops,
+ },
+};
+
+static int __init debug_init(void)
+{
+ return coresight_init_driver("debug", &debug_driver, &debug_platform_driver);
+}
+
+static void __exit debug_exit(void)
+{
+ coresight_remove_driver(&debug_driver, &debug_platform_driver);
+}
+module_init(debug_init);
+module_exit(debug_exit);
MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
MODULE_DESCRIPTION("ARM Coresight CPU Debug Driver");
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index e6cd9705596c..bf01f01964cf 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -1240,6 +1240,8 @@ static void etm4_init_arch_data(void *info)
drvdata->nr_event = FIELD_GET(TRCIDR0_NUMEVENT_MASK, etmidr0);
/* QSUPP, bits[16:15] Q element support field */
drvdata->q_support = FIELD_GET(TRCIDR0_QSUPP_MASK, etmidr0);
+ if (drvdata->q_support)
+ drvdata->q_filt = !!(etmidr0 & TRCIDR0_QFILT);
/* TSSIZE, bits[28:24] Global timestamp size field */
drvdata->ts_size = FIELD_GET(TRCIDR0_TSSIZE_MASK, etmidr0);
@@ -1732,16 +1734,14 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
state->trcccctlr = etm4x_read32(csa, TRCCCCTLR);
state->trcbbctlr = etm4x_read32(csa, TRCBBCTLR);
state->trctraceidr = etm4x_read32(csa, TRCTRACEIDR);
- state->trcqctlr = etm4x_read32(csa, TRCQCTLR);
+ if (drvdata->q_filt)
+ state->trcqctlr = etm4x_read32(csa, TRCQCTLR);
state->trcvictlr = etm4x_read32(csa, TRCVICTLR);
state->trcviiectlr = etm4x_read32(csa, TRCVIIECTLR);
state->trcvissctlr = etm4x_read32(csa, TRCVISSCTLR);
if (drvdata->nr_pe_cmp)
state->trcvipcssctlr = etm4x_read32(csa, TRCVIPCSSCTLR);
- state->trcvdctlr = etm4x_read32(csa, TRCVDCTLR);
- state->trcvdsacctlr = etm4x_read32(csa, TRCVDSACCTLR);
- state->trcvdarcctlr = etm4x_read32(csa, TRCVDARCCTLR);
for (i = 0; i < drvdata->nrseqstate - 1; i++)
state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i));
@@ -1758,7 +1758,8 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
state->trccntvr[i] = etm4x_read32(csa, TRCCNTVRn(i));
}
- for (i = 0; i < drvdata->nr_resource * 2; i++)
+ /* Resource selector pair 0 is reserved */
+ for (i = 2; i < drvdata->nr_resource * 2; i++)
state->trcrsctlr[i] = etm4x_read32(csa, TRCRSCTLRn(i));
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
@@ -1843,8 +1844,10 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
{
int i;
struct etmv4_save_state *state = drvdata->save_state;
- struct csdev_access tmp_csa = CSDEV_ACCESS_IOMEM(drvdata->base);
- struct csdev_access *csa = &tmp_csa;
+ struct csdev_access *csa = &drvdata->csdev->access;
+
+ if (WARN_ON(!drvdata->csdev))
+ return;
etm4_cs_unlock(drvdata, csa);
etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET);
@@ -1863,16 +1866,14 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, state->trcccctlr, TRCCCCTLR);
etm4x_relaxed_write32(csa, state->trcbbctlr, TRCBBCTLR);
etm4x_relaxed_write32(csa, state->trctraceidr, TRCTRACEIDR);
- etm4x_relaxed_write32(csa, state->trcqctlr, TRCQCTLR);
+ if (drvdata->q_filt)
+ etm4x_relaxed_write32(csa, state->trcqctlr, TRCQCTLR);
etm4x_relaxed_write32(csa, state->trcvictlr, TRCVICTLR);
etm4x_relaxed_write32(csa, state->trcviiectlr, TRCVIIECTLR);
etm4x_relaxed_write32(csa, state->trcvissctlr, TRCVISSCTLR);
if (drvdata->nr_pe_cmp)
etm4x_relaxed_write32(csa, state->trcvipcssctlr, TRCVIPCSSCTLR);
- etm4x_relaxed_write32(csa, state->trcvdctlr, TRCVDCTLR);
- etm4x_relaxed_write32(csa, state->trcvdsacctlr, TRCVDSACCTLR);
- etm4x_relaxed_write32(csa, state->trcvdarcctlr, TRCVDARCCTLR);
for (i = 0; i < drvdata->nrseqstate - 1; i++)
etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i));
@@ -1889,7 +1890,8 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, state->trccntvr[i], TRCCNTVRn(i));
}
- for (i = 0; i < drvdata->nr_resource * 2; i++)
+ /* Resource selector pair 0 is reserved */
+ for (i = 2; i < drvdata->nr_resource * 2; i++)
etm4x_relaxed_write32(csa, state->trcrsctlr[i], TRCRSCTLRn(i));
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
@@ -2213,6 +2215,9 @@ static int etm4_probe_platform_dev(struct platform_device *pdev)
ret = etm4_probe(&pdev->dev);
pm_runtime_put(&pdev->dev);
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
return ret;
}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index 9ea678bc2e8e..9e9165f62e81 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -43,9 +43,6 @@
#define TRCVIIECTLR 0x084
#define TRCVISSCTLR 0x088
#define TRCVIPCSSCTLR 0x08C
-#define TRCVDCTLR 0x0A0
-#define TRCVDSACCTLR 0x0A4
-#define TRCVDARCCTLR 0x0A8
/* Derived resources registers */
#define TRCSEQEVRn(n) (0x100 + (n * 4)) /* n = 0-2 */
#define TRCSEQRSTEVR 0x118
@@ -90,9 +87,6 @@
/* Address Comparator registers n = 0-15 */
#define TRCACVRn(n) (0x400 + (n * 8))
#define TRCACATRn(n) (0x480 + (n * 8))
-/* Data Value Comparator Value registers, n = 0-7 */
-#define TRCDVCVRn(n) (0x500 + (n * 16))
-#define TRCDVCMRn(n) (0x580 + (n * 16))
/* ContextID/Virtual ContextID comparators, n = 0-7 */
#define TRCCIDCVRn(n) (0x600 + (n * 8))
#define TRCVMIDCVRn(n) (0x640 + (n * 8))
@@ -141,6 +135,7 @@
#define TRCIDR0_TRCCCI BIT(7)
#define TRCIDR0_RETSTACK BIT(9)
#define TRCIDR0_NUMEVENT_MASK GENMASK(11, 10)
+#define TRCIDR0_QFILT BIT(14)
#define TRCIDR0_QSUPP_MASK GENMASK(16, 15)
#define TRCIDR0_TSSIZE_MASK GENMASK(28, 24)
@@ -272,9 +267,6 @@
/* List of registers accessible via System instructions */
#define ETM4x_ONLY_SYSREG_LIST(op, val) \
CASE_##op((val), TRCPROCSELR) \
- CASE_##op((val), TRCVDCTLR) \
- CASE_##op((val), TRCVDSACCTLR) \
- CASE_##op((val), TRCVDARCCTLR) \
CASE_##op((val), TRCOSLAR)
#define ETM_COMMON_SYSREG_LIST(op, val) \
@@ -422,22 +414,6 @@
CASE_##op((val), TRCACATRn(13)) \
CASE_##op((val), TRCACATRn(14)) \
CASE_##op((val), TRCACATRn(15)) \
- CASE_##op((val), TRCDVCVRn(0)) \
- CASE_##op((val), TRCDVCVRn(1)) \
- CASE_##op((val), TRCDVCVRn(2)) \
- CASE_##op((val), TRCDVCVRn(3)) \
- CASE_##op((val), TRCDVCVRn(4)) \
- CASE_##op((val), TRCDVCVRn(5)) \
- CASE_##op((val), TRCDVCVRn(6)) \
- CASE_##op((val), TRCDVCVRn(7)) \
- CASE_##op((val), TRCDVCMRn(0)) \
- CASE_##op((val), TRCDVCMRn(1)) \
- CASE_##op((val), TRCDVCMRn(2)) \
- CASE_##op((val), TRCDVCMRn(3)) \
- CASE_##op((val), TRCDVCMRn(4)) \
- CASE_##op((val), TRCDVCMRn(5)) \
- CASE_##op((val), TRCDVCMRn(6)) \
- CASE_##op((val), TRCDVCMRn(7)) \
CASE_##op((val), TRCCIDCVRn(0)) \
CASE_##op((val), TRCCIDCVRn(1)) \
CASE_##op((val), TRCCIDCVRn(2)) \
@@ -907,9 +883,6 @@ struct etmv4_save_state {
u32 trcviiectlr;
u32 trcvissctlr;
u32 trcvipcssctlr;
- u32 trcvdctlr;
- u32 trcvdsacctlr;
- u32 trcvdarcctlr;
u32 trcseqevr[ETM_MAX_SEQ_STATES];
u32 trcseqrstevr;
@@ -982,6 +955,7 @@ struct etmv4_save_state {
* @os_unlock: True if access to management registers is allowed.
* @instrp0: Tracing of load and store instructions
* as P0 elements is supported.
+ * @q_filt: Q element filtering support, if Q elements are supported.
* @trcbb: Indicates if the trace unit supports branch broadcast tracing.
* @trccond: If the trace unit supports conditional
* instruction tracing.
@@ -1044,6 +1018,7 @@ struct etmv4_drvdata {
bool boot_enable;
bool os_unlock;
bool instrp0;
+ bool q_filt;
bool trcbb;
bool trccond;
bool retstack;
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 5ab1f592917a..5a819c8970fb 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -36,6 +36,7 @@ DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
* struct funnel_drvdata - specifics associated to a funnel component
* @base: memory mapped base address for this component.
* @atclk: optional clock for the core parts of the funnel.
+ * @pclk: APB clock if present, otherwise NULL
* @csdev: component vitals needed by the framework.
* @priority: port selection order.
* @spinlock: serialize enable/disable operations.
@@ -43,6 +44,7 @@ DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
struct funnel_drvdata {
void __iomem *base;
struct clk *atclk;
+ struct clk *pclk;
struct coresight_device *csdev;
unsigned long priority;
spinlock_t spinlock;
@@ -236,6 +238,10 @@ static int funnel_probe(struct device *dev, struct resource *res)
return ret;
}
+ drvdata->pclk = coresight_get_enable_apb_pclk(dev);
+ if (IS_ERR(drvdata->pclk))
+ return -ENODEV;
+
/*
* Map the device base for dynamic-funnel, which has been
* validated by AMBA core.
@@ -272,12 +278,13 @@ static int funnel_probe(struct device *dev, struct resource *res)
goto out_disable_clk;
}
- pm_runtime_put(dev);
ret = 0;
out_disable_clk:
if (ret && !IS_ERR_OR_NULL(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
+ if (ret && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
return ret;
}
@@ -298,6 +305,9 @@ static int funnel_runtime_suspend(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
+
return 0;
}
@@ -308,6 +318,8 @@ static int funnel_runtime_resume(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_prepare_enable(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_prepare_enable(drvdata->pclk);
return 0;
}
#endif
@@ -316,55 +328,61 @@ static const struct dev_pm_ops funnel_dev_pm_ops = {
SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
};
-static int static_funnel_probe(struct platform_device *pdev)
+static int funnel_platform_probe(struct platform_device *pdev)
{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int ret;
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- /* Static funnel do not have programming base */
- ret = funnel_probe(&pdev->dev, NULL);
-
- if (ret) {
- pm_runtime_put_noidle(&pdev->dev);
+ ret = funnel_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
pm_runtime_disable(&pdev->dev);
- }
return ret;
}
-static void static_funnel_remove(struct platform_device *pdev)
+static void funnel_platform_remove(struct platform_device *pdev)
{
+ struct funnel_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
funnel_remove(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
}
-static const struct of_device_id static_funnel_match[] = {
+static const struct of_device_id funnel_match[] = {
{.compatible = "arm,coresight-static-funnel"},
{}
};
-MODULE_DEVICE_TABLE(of, static_funnel_match);
+MODULE_DEVICE_TABLE(of, funnel_match);
#ifdef CONFIG_ACPI
-static const struct acpi_device_id static_funnel_ids[] = {
- {"ARMHC9FE", 0, 0, 0},
+static const struct acpi_device_id funnel_acpi_ids[] = {
+ {"ARMHC9FE", 0, 0, 0}, /* ARM Coresight Static Funnel */
+ {"ARMHC9FF", 0, 0, 0}, /* ARM CoreSight Dynamic Funnel */
{},
};
-MODULE_DEVICE_TABLE(acpi, static_funnel_ids);
+MODULE_DEVICE_TABLE(acpi, funnel_acpi_ids);
#endif
-static struct platform_driver static_funnel_driver = {
- .probe = static_funnel_probe,
- .remove_new = static_funnel_remove,
- .driver = {
- .name = "coresight-static-funnel",
+static struct platform_driver funnel_driver = {
+ .probe = funnel_platform_probe,
+ .remove_new = funnel_platform_remove,
+ .driver = {
+ .name = "coresight-funnel",
/* THIS_MODULE is taken care of by platform_driver_register() */
- .of_match_table = static_funnel_match,
- .acpi_match_table = ACPI_PTR(static_funnel_ids),
+ .of_match_table = funnel_match,
+ .acpi_match_table = ACPI_PTR(funnel_acpi_ids),
.pm = &funnel_dev_pm_ops,
.suppress_bind_attrs = true,
},
@@ -373,7 +391,13 @@ static struct platform_driver static_funnel_driver = {
static int dynamic_funnel_probe(struct amba_device *adev,
const struct amba_id *id)
{
- return funnel_probe(&adev->dev, &adev->res);
+ int ret;
+
+ ret = funnel_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
}
static void dynamic_funnel_remove(struct amba_device *adev)
@@ -409,27 +433,12 @@ static struct amba_driver dynamic_funnel_driver = {
static int __init funnel_init(void)
{
- int ret;
-
- ret = platform_driver_register(&static_funnel_driver);
- if (ret) {
- pr_info("Error registering platform driver\n");
- return ret;
- }
-
- ret = amba_driver_register(&dynamic_funnel_driver);
- if (ret) {
- pr_info("Error registering amba driver\n");
- platform_driver_unregister(&static_funnel_driver);
- }
-
- return ret;
+ return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver);
}
static void __exit funnel_exit(void)
{
- platform_driver_unregister(&static_funnel_driver);
- amba_driver_unregister(&dynamic_funnel_driver);
+ coresight_remove_driver(&dynamic_funnel_driver, &funnel_driver);
}
module_init(funnel_init);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index eb365236f9a9..fc3617642b01 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -222,6 +222,16 @@ static inline void *coresight_get_uci_data(const struct amba_id *id)
return uci_id->data;
}
+static inline void *coresight_get_uci_data_from_amba(const struct amba_id *table, u32 pid)
+{
+ while (table->mask) {
+ if ((pid & table->mask) == table->id)
+ return coresight_get_uci_data(table);
+ table++;
+ };
+ return NULL;
+}
+
void coresight_release_platform_data(struct coresight_device *csdev,
struct device *dev,
struct coresight_platform_data *pdata);
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
index 2bb9ba66e3c0..3e55be9c8418 100644
--- a/drivers/hwtracing/coresight/coresight-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-replicator.c
@@ -31,6 +31,7 @@ DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator");
* @base: memory mapped base address for this component. Also indicates
* whether this one is programmable or not.
* @atclk: optional clock for the core parts of the replicator.
+ * @pclk: APB clock if present, otherwise NULL
* @csdev: component vitals needed by the framework
* @spinlock: serialize enable/disable operations.
* @check_idfilter_val: check if the context is lost upon clock removal.
@@ -38,6 +39,7 @@ DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator");
struct replicator_drvdata {
void __iomem *base;
struct clk *atclk;
+ struct clk *pclk;
struct coresight_device *csdev;
spinlock_t spinlock;
bool check_idfilter_val;
@@ -243,6 +245,10 @@ static int replicator_probe(struct device *dev, struct resource *res)
return ret;
}
+ drvdata->pclk = coresight_get_enable_apb_pclk(dev);
+ if (IS_ERR(drvdata->pclk))
+ return -ENODEV;
+
/*
* Map the device base for dynamic-replicator, which has been
* validated by AMBA core
@@ -285,11 +291,12 @@ static int replicator_probe(struct device *dev, struct resource *res)
}
replicator_reset(drvdata);
- pm_runtime_put(dev);
out_disable_clk:
if (ret && !IS_ERR_OR_NULL(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
+ if (ret && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
return ret;
}
@@ -301,29 +308,34 @@ static int replicator_remove(struct device *dev)
return 0;
}
-static int static_replicator_probe(struct platform_device *pdev)
+static int replicator_platform_probe(struct platform_device *pdev)
{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int ret;
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- /* Static replicators do not have programming base */
- ret = replicator_probe(&pdev->dev, NULL);
-
- if (ret) {
- pm_runtime_put_noidle(&pdev->dev);
+ ret = replicator_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
pm_runtime_disable(&pdev->dev);
- }
return ret;
}
-static void static_replicator_remove(struct platform_device *pdev)
+static void replicator_platform_remove(struct platform_device *pdev)
{
+ struct replicator_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
replicator_remove(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
}
#ifdef CONFIG_PM
@@ -334,6 +346,8 @@ static int replicator_runtime_suspend(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
return 0;
}
@@ -344,6 +358,8 @@ static int replicator_runtime_resume(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_prepare_enable(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_prepare_enable(drvdata->pclk);
return 0;
}
#endif
@@ -353,31 +369,32 @@ static const struct dev_pm_ops replicator_dev_pm_ops = {
replicator_runtime_resume, NULL)
};
-static const struct of_device_id static_replicator_match[] = {
+static const struct of_device_id replicator_match[] = {
{.compatible = "arm,coresight-replicator"},
{.compatible = "arm,coresight-static-replicator"},
{}
};
-MODULE_DEVICE_TABLE(of, static_replicator_match);
+MODULE_DEVICE_TABLE(of, replicator_match);
#ifdef CONFIG_ACPI
-static const struct acpi_device_id static_replicator_acpi_ids[] = {
+static const struct acpi_device_id replicator_acpi_ids[] = {
{"ARMHC985", 0, 0, 0}, /* ARM CoreSight Static Replicator */
+ {"ARMHC98D", 0, 0, 0}, /* ARM CoreSight Dynamic Replicator */
{}
};
-MODULE_DEVICE_TABLE(acpi, static_replicator_acpi_ids);
+MODULE_DEVICE_TABLE(acpi, replicator_acpi_ids);
#endif
-static struct platform_driver static_replicator_driver = {
- .probe = static_replicator_probe,
- .remove_new = static_replicator_remove,
+static struct platform_driver replicator_driver = {
+ .probe = replicator_platform_probe,
+ .remove_new = replicator_platform_remove,
.driver = {
- .name = "coresight-static-replicator",
+ .name = "coresight-replicator",
/* THIS_MODULE is taken care of by platform_driver_register() */
- .of_match_table = of_match_ptr(static_replicator_match),
- .acpi_match_table = ACPI_PTR(static_replicator_acpi_ids),
+ .of_match_table = of_match_ptr(replicator_match),
+ .acpi_match_table = ACPI_PTR(replicator_acpi_ids),
.pm = &replicator_dev_pm_ops,
.suppress_bind_attrs = true,
},
@@ -386,7 +403,13 @@ static struct platform_driver static_replicator_driver = {
static int dynamic_replicator_probe(struct amba_device *adev,
const struct amba_id *id)
{
- return replicator_probe(&adev->dev, &adev->res);
+ int ret;
+
+ ret = replicator_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
}
static void dynamic_replicator_remove(struct amba_device *adev)
@@ -415,27 +438,12 @@ static struct amba_driver dynamic_replicator_driver = {
static int __init replicator_init(void)
{
- int ret;
-
- ret = platform_driver_register(&static_replicator_driver);
- if (ret) {
- pr_info("Error registering platform driver\n");
- return ret;
- }
-
- ret = amba_driver_register(&dynamic_replicator_driver);
- if (ret) {
- pr_info("Error registering amba driver\n");
- platform_driver_unregister(&static_replicator_driver);
- }
-
- return ret;
+ return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver);
}
static void __exit replicator_exit(void)
{
- platform_driver_unregister(&static_replicator_driver);
- amba_driver_unregister(&dynamic_replicator_driver);
+ coresight_remove_driver(&dynamic_replicator_driver, &replicator_driver);
}
module_init(replicator_init);
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 15b52358965c..117dbb484543 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -29,6 +29,7 @@
#include <linux/perf_event.h>
#include <linux/pm_runtime.h>
#include <linux/stm.h>
+#include <linux/platform_device.h>
#include "coresight-priv.h"
#include "coresight-trace-id.h"
@@ -115,6 +116,7 @@ DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm");
* struct stm_drvdata - specifics associated to an STM component
* @base: memory mapped base address for this component.
* @atclk: optional clock for the core parts of the STM.
+ * @pclk: APB clock if present, otherwise NULL
* @csdev: component vitals needed by the framework.
* @spinlock: only one at a time pls.
* @chs: the channels accociated to this STM.
@@ -131,6 +133,7 @@ DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm");
struct stm_drvdata {
void __iomem *base;
struct clk *atclk;
+ struct clk *pclk;
struct coresight_device *csdev;
spinlock_t spinlock;
struct channel_space chs;
@@ -800,14 +803,22 @@ static void stm_init_generic_data(struct stm_drvdata *drvdata,
drvdata->stm.set_options = stm_generic_set_options;
}
-static int stm_probe(struct amba_device *adev, const struct amba_id *id)
+static const struct amba_id stm_ids[];
+
+static char *stm_csdev_name(struct coresight_device *csdev)
+{
+ u32 stm_pid = coresight_get_pid(&csdev->access);
+ void *uci_data = coresight_get_uci_data_from_amba(stm_ids, stm_pid);
+
+ return uci_data ? (char *)uci_data : "STM";
+}
+
+static int __stm_probe(struct device *dev, struct resource *res)
{
int ret, trace_id;
void __iomem *base;
- struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct stm_drvdata *drvdata;
- struct resource *res = &adev->res;
struct resource ch_res;
struct coresight_desc desc = { 0 };
@@ -819,12 +830,16 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
if (!drvdata)
return -ENOMEM;
- drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
+ drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */
if (!IS_ERR(drvdata->atclk)) {
ret = clk_prepare_enable(drvdata->atclk);
if (ret)
return ret;
}
+
+ drvdata->pclk = coresight_get_enable_apb_pclk(dev);
+ if (IS_ERR(drvdata->pclk))
+ return -ENODEV;
dev_set_drvdata(dev, drvdata);
base = devm_ioremap_resource(dev, res);
@@ -872,7 +887,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
ret = PTR_ERR(pdata);
goto stm_unregister;
}
- adev->dev.platform_data = pdata;
+ dev->platform_data = pdata;
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
@@ -893,10 +908,8 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
}
drvdata->traceid = (u8)trace_id;
- pm_runtime_put(&adev->dev);
-
dev_info(&drvdata->csdev->dev, "%s initialized\n",
- (char *)coresight_get_uci_data(id));
+ stm_csdev_name(drvdata->csdev));
return 0;
cs_unregister:
@@ -907,9 +920,20 @@ stm_unregister:
return ret;
}
-static void stm_remove(struct amba_device *adev)
+static int stm_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ int ret;
+
+ ret = __stm_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
+}
+
+static void __stm_remove(struct device *dev)
{
- struct stm_drvdata *drvdata = dev_get_drvdata(&adev->dev);
+ struct stm_drvdata *drvdata = dev_get_drvdata(dev);
coresight_trace_id_put_system_id(drvdata->traceid);
coresight_unregister(drvdata->csdev);
@@ -917,6 +941,11 @@ static void stm_remove(struct amba_device *adev)
stm_unregister_device(&drvdata->stm);
}
+static void stm_remove(struct amba_device *adev)
+{
+ __stm_remove(&adev->dev);
+}
+
#ifdef CONFIG_PM
static int stm_runtime_suspend(struct device *dev)
{
@@ -925,6 +954,8 @@ static int stm_runtime_suspend(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
return 0;
}
@@ -935,6 +966,8 @@ static int stm_runtime_resume(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_prepare_enable(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_prepare_enable(drvdata->pclk);
return 0;
}
#endif
@@ -962,7 +995,66 @@ static struct amba_driver stm_driver = {
.id_table = stm_ids,
};
-module_amba_driver(stm_driver);
+static int stm_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int ret = 0;
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = __stm_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void stm_platform_remove(struct platform_device *pdev)
+{
+ struct stm_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
+ __stm_remove(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id stm_acpi_ids[] = {
+ {"ARMHC502", 0, 0, 0}, /* ARM CoreSight STM */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, stm_acpi_ids);
+#endif
+
+static struct platform_driver stm_platform_driver = {
+ .probe = stm_platform_probe,
+ .remove_new = stm_platform_remove,
+ .driver = {
+ .name = "coresight-stm-platform",
+ .acpi_match_table = ACPI_PTR(stm_acpi_ids),
+ .suppress_bind_attrs = true,
+ .pm = &stm_dev_pm_ops,
+ },
+};
+
+static int __init stm_init(void)
+{
+ return coresight_init_driver("stm", &stm_driver, &stm_platform_driver);
+}
+
+static void __exit stm_exit(void)
+{
+ coresight_remove_driver(&stm_driver, &stm_platform_driver);
+}
+module_init(stm_init);
+module_exit(stm_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_DESCRIPTION("Arm CoreSight System Trace Macrocell driver");
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index 0d251cae814f..4f11a739ae4d 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -4,6 +4,7 @@
* Description: CoreSight Trace Memory Controller driver
*/
+#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -24,6 +25,8 @@
#include <linux/of.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
#include "coresight-priv.h"
#include "coresight-tmc.h"
@@ -360,7 +363,32 @@ static const struct attribute_group *coresight_etr_groups[] = {
static inline bool tmc_etr_can_use_sg(struct device *dev)
{
- return fwnode_property_present(dev->fwnode, "arm,scatter-gather");
+ int ret;
+ u8 val_u8;
+
+ /*
+ * Presence of the property 'arm,scatter-gather' is checked
+ * on the platform for the feature support, rather than its
+ * value.
+ */
+ if (is_of_node(dev->fwnode)) {
+ return fwnode_property_present(dev->fwnode, "arm,scatter-gather");
+ } else if (is_acpi_device_node(dev->fwnode)) {
+ /*
+ * TMC_DEVID_NOSCAT test in tmc_etr_setup_caps(), has already ensured
+ * this property is only checked for Coresight SoC 400 TMC configured
+ * as ETR.
+ */
+ ret = fwnode_property_read_u8(dev->fwnode, "arm-armhc97c-sg-enable", &val_u8);
+ if (!ret)
+ return !!val_u8;
+
+ if (fwnode_property_present(dev->fwnode, "arm,scatter-gather")) {
+ pr_warn_once("Deprecated ACPI property - arm,scatter-gather\n");
+ return true;
+ }
+ }
+ return false;
}
static inline bool tmc_etr_has_non_secure_access(struct tmc_drvdata *drvdata)
@@ -370,16 +398,23 @@ static inline bool tmc_etr_has_non_secure_access(struct tmc_drvdata *drvdata)
return (auth & TMC_AUTH_NSID_MASK) == 0x3;
}
+static const struct amba_id tmc_ids[];
+
/* Detect and initialise the capabilities of a TMC ETR */
-static int tmc_etr_setup_caps(struct device *parent, u32 devid, void *dev_caps)
+static int tmc_etr_setup_caps(struct device *parent, u32 devid,
+ struct csdev_access *access)
{
int rc;
- u32 dma_mask = 0;
+ u32 tmc_pid, dma_mask = 0;
struct tmc_drvdata *drvdata = dev_get_drvdata(parent);
+ void *dev_caps;
if (!tmc_etr_has_non_secure_access(drvdata))
return -EACCES;
+ tmc_pid = coresight_get_pid(access);
+ dev_caps = coresight_get_uci_data_from_amba(tmc_ids, tmc_pid);
+
/* Set the unadvertised capabilities */
tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps);
@@ -437,24 +472,17 @@ static u32 tmc_etr_get_max_burst_size(struct device *dev)
return burst_size;
}
-static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
+static int __tmc_probe(struct device *dev, struct resource *res)
{
int ret = 0;
u32 devid;
void __iomem *base;
- struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
- struct tmc_drvdata *drvdata;
- struct resource *res = &adev->res;
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev);
struct coresight_desc desc = { 0 };
struct coresight_dev_list *dev_list = NULL;
ret = -ENOMEM;
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
- goto out;
-
- dev_set_drvdata(dev, drvdata);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
@@ -497,8 +525,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM;
desc.ops = &tmc_etr_cs_ops;
- ret = tmc_etr_setup_caps(dev, devid,
- coresight_get_uci_data(id));
+ ret = tmc_etr_setup_caps(dev, devid, &desc.access);
if (ret)
goto out;
idr_init(&drvdata->idr);
@@ -530,7 +557,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
ret = PTR_ERR(pdata);
goto out;
}
- adev->dev.platform_data = pdata;
+ dev->platform_data = pdata;
desc.pdata = pdata;
drvdata->csdev = coresight_register(&desc);
@@ -545,12 +572,27 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
ret = misc_register(&drvdata->miscdev);
if (ret)
coresight_unregister(drvdata->csdev);
- else
- pm_runtime_put(&adev->dev);
out:
return ret;
}
+static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ struct tmc_drvdata *drvdata;
+ int ret;
+
+ drvdata = devm_kzalloc(&adev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ amba_set_drvdata(adev, drvdata);
+ ret = __tmc_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
+}
+
static void tmc_shutdown(struct amba_device *adev)
{
unsigned long flags;
@@ -573,9 +615,9 @@ out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
}
-static void tmc_remove(struct amba_device *adev)
+static void __tmc_remove(struct device *dev)
{
- struct tmc_drvdata *drvdata = dev_get_drvdata(&adev->dev);
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev);
/*
* Since misc_open() holds a refcount on the f_ops, which is
@@ -586,6 +628,11 @@ static void tmc_remove(struct amba_device *adev)
coresight_unregister(drvdata->csdev);
}
+static void tmc_remove(struct amba_device *adev)
+{
+ __tmc_remove(&adev->dev);
+}
+
static const struct amba_id tmc_ids[] = {
CS_AMBA_ID(0x000bb961),
/* Coresight SoC 600 TMC-ETR/ETS */
@@ -610,7 +657,101 @@ static struct amba_driver tmc_driver = {
.id_table = tmc_ids,
};
-module_amba_driver(tmc_driver);
+static int tmc_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct tmc_drvdata *drvdata;
+ int ret = 0;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->pclk = coresight_get_enable_apb_pclk(&pdev->dev);
+ if (IS_ERR(drvdata->pclk))
+ return -ENODEV;
+
+ dev_set_drvdata(&pdev->dev, drvdata);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = __tmc_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void tmc_platform_remove(struct platform_device *pdev)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
+ __tmc_remove(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
+}
+
+#ifdef CONFIG_PM
+static int tmc_runtime_suspend(struct device *dev)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
+ return 0;
+}
+
+static int tmc_runtime_resume(struct device *dev)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_prepare_enable(drvdata->pclk);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops tmc_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(tmc_runtime_suspend, tmc_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id tmc_acpi_ids[] = {
+ {"ARMHC501", 0, 0, 0}, /* ARM CoreSight ETR */
+ {"ARMHC97C", 0, 0, 0}, /* ARM CoreSight SoC-400 TMC, SoC-600 ETF/ETB */
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, tmc_acpi_ids);
+#endif
+
+static struct platform_driver tmc_platform_driver = {
+ .probe = tmc_platform_probe,
+ .remove_new = tmc_platform_remove,
+ .driver = {
+ .name = "coresight-tmc-platform",
+ .acpi_match_table = ACPI_PTR(tmc_acpi_ids),
+ .suppress_bind_attrs = true,
+ .pm = &tmc_dev_pm_ops,
+ },
+};
+
+static int __init tmc_init(void)
+{
+ return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver);
+}
+
+static void __exit tmc_exit(void)
+{
+ coresight_remove_driver(&tmc_driver, &tmc_platform_driver);
+}
+module_init(tmc_init);
+module_exit(tmc_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_DESCRIPTION("Arm CoreSight Trace Memory Controller driver");
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index cef979c897e6..c77763b49de0 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -166,6 +166,7 @@ struct etr_buf {
/**
* struct tmc_drvdata - specifics associated to an TMC component
+ * @pclk: APB clock if present, otherwise NULL
* @base: memory mapped base address for this component.
* @csdev: component vitals needed by the framework.
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
@@ -189,6 +190,7 @@ struct etr_buf {
* @perf_buf: PERF buffer for ETR.
*/
struct tmc_drvdata {
+ struct clk *pclk;
void __iomem *base;
struct coresight_device *csdev;
struct miscdevice miscdev;
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index 7dc9ea564bca..b048e146fbb1 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -5,17 +5,19 @@
* Description: CoreSight Trace Port Interface Unit driver
*/
+#include <linux/acpi.h>
+#include <linux/amba/bus.h>
#include <linux/atomic.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/coresight.h>
#include <linux/device.h>
-#include <linux/io.h>
#include <linux/err.h>
-#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/coresight.h>
-#include <linux/amba/bus.h>
-#include <linux/clk.h>
+#include <linux/slab.h>
#include "coresight-priv.h"
@@ -52,11 +54,13 @@ DEFINE_CORESIGHT_DEVLIST(tpiu_devs, "tpiu");
/*
* @base: memory mapped base address for this component.
* @atclk: optional clock for the core parts of the TPIU.
+ * @pclk: APB clock if present, otherwise NULL
* @csdev: component vitals needed by the framework.
*/
struct tpiu_drvdata {
void __iomem *base;
struct clk *atclk;
+ struct clk *pclk;
struct coresight_device *csdev;
spinlock_t spinlock;
};
@@ -122,14 +126,12 @@ static const struct coresight_ops tpiu_cs_ops = {
.sink_ops = &tpiu_sink_ops,
};
-static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
+static int __tpiu_probe(struct device *dev, struct resource *res)
{
int ret;
void __iomem *base;
- struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct tpiu_drvdata *drvdata;
- struct resource *res = &adev->res;
struct coresight_desc desc = { 0 };
desc.name = coresight_alloc_device_name(&tpiu_devs, dev);
@@ -142,12 +144,16 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init(&drvdata->spinlock);
- drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
+ drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */
if (!IS_ERR(drvdata->atclk)) {
ret = clk_prepare_enable(drvdata->atclk);
if (ret)
return ret;
}
+
+ drvdata->pclk = coresight_get_enable_apb_pclk(dev);
+ if (IS_ERR(drvdata->pclk))
+ return -ENODEV;
dev_set_drvdata(dev, drvdata);
/* Validity for the resource is already checked by the AMBA core */
@@ -173,21 +179,34 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
desc.dev = dev;
drvdata->csdev = coresight_register(&desc);
- if (!IS_ERR(drvdata->csdev)) {
- pm_runtime_put(&adev->dev);
+ if (!IS_ERR(drvdata->csdev))
return 0;
- }
return PTR_ERR(drvdata->csdev);
}
-static void tpiu_remove(struct amba_device *adev)
+static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
{
- struct tpiu_drvdata *drvdata = dev_get_drvdata(&adev->dev);
+ int ret;
+
+ ret = __tpiu_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+ return ret;
+}
+
+static void __tpiu_remove(struct device *dev)
+{
+ struct tpiu_drvdata *drvdata = dev_get_drvdata(dev);
coresight_unregister(drvdata->csdev);
}
+static void tpiu_remove(struct amba_device *adev)
+{
+ __tpiu_remove(&adev->dev);
+}
+
#ifdef CONFIG_PM
static int tpiu_runtime_suspend(struct device *dev)
{
@@ -196,6 +215,8 @@ static int tpiu_runtime_suspend(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_disable_unprepare(drvdata->pclk);
return 0;
}
@@ -206,6 +227,8 @@ static int tpiu_runtime_resume(struct device *dev)
if (drvdata && !IS_ERR(drvdata->atclk))
clk_prepare_enable(drvdata->atclk);
+ if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
+ clk_prepare_enable(drvdata->pclk);
return 0;
}
#endif
@@ -244,7 +267,66 @@ static struct amba_driver tpiu_driver = {
.id_table = tpiu_ids,
};
-module_amba_driver(tpiu_driver);
+static int tpiu_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int ret;
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = __tpiu_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void tpiu_platform_remove(struct platform_device *pdev)
+{
+ struct tpiu_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
+ __tpiu_remove(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR_OR_NULL(drvdata->pclk))
+ clk_put(drvdata->pclk);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id tpiu_acpi_ids[] = {
+ {"ARMHC979", 0, 0, 0}, /* ARM CoreSight TPIU */
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tpiu_acpi_ids);
+#endif
+
+static struct platform_driver tpiu_platform_driver = {
+ .probe = tpiu_platform_probe,
+ .remove_new = tpiu_platform_remove,
+ .driver = {
+ .name = "coresight-tpiu-platform",
+ .acpi_match_table = ACPI_PTR(tpiu_acpi_ids),
+ .suppress_bind_attrs = true,
+ .pm = &tpiu_dev_pm_ops,
+ },
+};
+
+static int __init tpiu_init(void)
+{
+ return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver);
+}
+
+static void __exit tpiu_exit(void)
+{
+ coresight_remove_driver(&tpiu_driver, &tpiu_platform_driver);
+}
+module_init(tpiu_init);
+module_exit(tpiu_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
diff --git a/drivers/hwtracing/intel_th/acpi.c b/drivers/hwtracing/intel_th/acpi.c
index 87f9024e4bbb..503620e9fd10 100644
--- a/drivers/hwtracing/intel_th/acpi.c
+++ b/drivers/hwtracing/intel_th/acpi.c
@@ -60,18 +60,16 @@ static int intel_th_acpi_probe(struct platform_device *pdev)
return 0;
}
-static int intel_th_acpi_remove(struct platform_device *pdev)
+static void intel_th_acpi_remove(struct platform_device *pdev)
{
struct intel_th *th = platform_get_drvdata(pdev);
intel_th_free(th);
-
- return 0;
}
static struct platform_driver intel_th_acpi_driver = {
.probe = intel_th_acpi_probe,
- .remove = intel_th_acpi_remove,
+ .remove_new = intel_th_acpi_remove,
.driver = {
.name = DRIVER_NAME,
.acpi_match_table = intel_th_acpi_ids,
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 86c8efecd7c2..a121dc5cbd61 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -180,7 +180,7 @@ static void intel_th_device_release(struct device *dev)
intel_th_device_free(to_intel_th_device(dev));
}
-static struct device_type intel_th_source_device_type = {
+static const struct device_type intel_th_source_device_type = {
.name = "intel_th_source_device",
.release = intel_th_device_release,
};
@@ -333,19 +333,19 @@ static struct attribute *intel_th_output_attrs[] = {
ATTRIBUTE_GROUPS(intel_th_output);
-static struct device_type intel_th_output_device_type = {
+static const struct device_type intel_th_output_device_type = {
.name = "intel_th_output_device",
.groups = intel_th_output_groups,
.release = intel_th_device_release,
.devnode = intel_th_output_devnode,
};
-static struct device_type intel_th_switch_device_type = {
+static const struct device_type intel_th_switch_device_type = {
.name = "intel_th_switch_device",
.release = intel_th_device_release,
};
-static struct device_type *intel_th_device_type[] = {
+static const struct device_type *intel_th_device_type[] = {
[INTEL_TH_SOURCE] = &intel_th_source_device_type,
[INTEL_TH_OUTPUT] = &intel_th_output_device_type,
[INTEL_TH_SWITCH] = &intel_th_switch_device_type,
diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c
index b3308934a687..3883f99fd5d5 100644
--- a/drivers/hwtracing/intel_th/gth.c
+++ b/drivers/hwtracing/intel_th/gth.c
@@ -154,9 +154,9 @@ static ssize_t master_attr_show(struct device *dev,
spin_unlock(&gth->gth_lock);
if (port >= 0)
- count = snprintf(buf, PAGE_SIZE, "%x\n", port);
+ count = sysfs_emit(buf, "%x\n", port);
else
- count = snprintf(buf, PAGE_SIZE, "disabled\n");
+ count = sysfs_emit(buf, "disabled\n");
return count;
}
@@ -332,8 +332,8 @@ static ssize_t output_attr_show(struct device *dev,
pm_runtime_get_sync(dev);
spin_lock(&gth->gth_lock);
- count = snprintf(buf, PAGE_SIZE, "%x\n",
- gth_output_parm_get(gth, oa->port, oa->parm));
+ count = sysfs_emit(buf, "%x\n",
+ gth_output_parm_get(gth, oa->port, oa->parm));
spin_unlock(&gth->gth_lock);
pm_runtime_put(dev);
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 9621efe0e95c..be63d5b8f193 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -61,6 +61,7 @@ enum lockout_state {
* @lo_lock: lockout state serialization
* @nr_blocks: number of blocks (pages) in this window
* @nr_segs: number of segments in this window (<= @nr_blocks)
+ * @msc: pointer to the MSC device
* @_sgt: array of block descriptors
* @sgt: array of block descriptors
*/
@@ -119,7 +120,6 @@ struct msc_iter {
* @user_count: number of users of the buffer
* @mmap_count: number of mappings
* @buf_mutex: mutex to serialize access to buffer-related bits
-
* @enabled: MSC is enabled
* @wrap: wrapping is enabled
* @mode: MSC operating mode
@@ -755,6 +755,8 @@ unlock:
* Program storage mode, wrapping, burst length and trace buffer address
* into a given MSC. Then, enable tracing and set msc::enabled.
* The latter is serialized on msc::buf_mutex, so make sure to hold it.
+ *
+ * Return: %0 for success or a negative error code otherwise.
*/
static int msc_configure(struct msc *msc)
{
@@ -1291,7 +1293,8 @@ static void msc_buffer_free(struct msc *msc)
/**
* msc_buffer_alloc() - allocate a buffer for MSC
* @msc: MSC device
- * @size: allocation size in bytes
+ * @nr_pages: number of pages for each window
+ * @nr_wins: number of windows
*
* Allocate a storage buffer for MSC, depending on the msc::mode, it will be
* either done via msc_buffer_contig_alloc() for SINGLE operation mode or
@@ -1370,6 +1373,9 @@ static int msc_buffer_unlocked_free_unless_used(struct msc *msc)
* @msc: MSC device
*
* This is a locked version of msc_buffer_unlocked_free_unless_used().
+ *
+ * Return: 0 on successful deallocation or if there was no buffer to
+ * deallocate, -EBUSY if there are active users.
*/
static int msc_buffer_free_unless_used(struct msc *msc)
{
@@ -1438,6 +1444,8 @@ struct msc_win_to_user_struct {
* @data: callback's private data
* @src: source buffer
* @len: amount of data to copy from the source buffer
+ *
+ * Return: >= %0 for success or -errno for error.
*/
static unsigned long msc_win_to_user(void *data, void *src, size_t len)
{
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index 147d338c191e..0d7b9839e5b6 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -290,6 +290,16 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
{
+ /* Meteor Lake-S */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7f26),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
+ /* Meteor Lake-S CPU */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xae24),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
/* Raptor Lake-S */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7a26),
.driver_data = (kernel_ulong_t)&intel_th_2x,
@@ -300,6 +310,26 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
.driver_data = (kernel_ulong_t)&intel_th_2x,
},
{
+ /* Granite Rapids */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0963),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
+ /* Granite Rapids SOC */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3256),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
+ /* Sapphire Rapids SOC */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3456),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
+ /* Lunar Lake */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa824),
+ .driver_data = (kernel_ulong_t)&intel_th_2x,
+ },
+ {
/* Alder Lake CPU */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x466f),
.driver_data = (kernel_ulong_t)&intel_th_2x,
diff --git a/drivers/hwtracing/intel_th/sth.c b/drivers/hwtracing/intel_th/sth.c
index 9ca8c4e045f8..428f595a28a0 100644
--- a/drivers/hwtracing/intel_th/sth.c
+++ b/drivers/hwtracing/intel_th/sth.c
@@ -70,8 +70,8 @@ static ssize_t notrace sth_stm_packet(struct stm_data *stm_data,
struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
struct intel_th_channel __iomem *out =
sth_channel(sth, master, channel);
- u64 __iomem *outp = &out->Dn;
unsigned long reg = REG_STH_TRIG;
+ u64 __iomem *outp;
#ifndef CONFIG_64BIT
if (size > 4)
diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c
index 4bf04a977840..3090479a2979 100644
--- a/drivers/hwtracing/ptt/hisi_ptt.c
+++ b/drivers/hwtracing/ptt/hisi_ptt.c
@@ -1221,6 +1221,7 @@ static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt)
hisi_ptt->hisi_ptt_pmu = (struct pmu) {
.module = THIS_MODULE,
+ .parent = &hisi_ptt->pdev->dev,
.capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_NO_EXCLUDE,
.task_ctx_nr = perf_sw_context,
.attr_groups = hisi_ptt_pmu_groups,
diff --git a/drivers/hwtracing/stm/console.c b/drivers/hwtracing/stm/console.c
index a00f65e21747..097a00ac43a7 100644
--- a/drivers/hwtracing/stm/console.c
+++ b/drivers/hwtracing/stm/console.c
@@ -22,6 +22,7 @@ static struct stm_console {
.data = {
.name = "console",
.nr_chans = 1,
+ .type = STM_USER,
.link = stm_console_link,
.unlink = stm_console_unlink,
},
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index 534fbefc7f6a..ccf39a80dc4f 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -600,7 +600,7 @@ EXPORT_SYMBOL_GPL(stm_data_write);
static ssize_t notrace
stm_write(struct stm_device *stm, struct stm_output *output,
- unsigned int chan, const char *buf, size_t count)
+ unsigned int chan, const char *buf, size_t count, struct stm_source_data *source)
{
int err;
@@ -608,7 +608,7 @@ stm_write(struct stm_device *stm, struct stm_output *output,
if (!stm->pdrv)
return -ENODEV;
- err = stm->pdrv->write(stm->data, output, chan, buf, count);
+ err = stm->pdrv->write(stm->data, output, chan, buf, count, source);
if (err < 0)
return err;
@@ -657,7 +657,7 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf,
pm_runtime_get_sync(&stm->dev);
- count = stm_write(stm, &stmf->output, 0, kbuf, count);
+ count = stm_write(stm, &stmf->output, 0, kbuf, count, NULL);
pm_runtime_mark_last_busy(&stm->dev);
pm_runtime_put_autosuspend(&stm->dev);
@@ -868,8 +868,11 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
return -ENOMEM;
stm->major = register_chrdev(0, stm_data->name, &stm_fops);
- if (stm->major < 0)
- goto err_free;
+ if (stm->major < 0) {
+ err = stm->major;
+ vfree(stm);
+ return err;
+ }
device_initialize(&stm->dev);
stm->dev.devt = MKDEV(stm->major, 0);
@@ -913,10 +916,8 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
err_device:
unregister_chrdev(stm->major, stm_data->name);
- /* matches device_initialize() above */
+ /* calls stm_device_release() */
put_device(&stm->dev);
-err_free:
- vfree(stm);
return err;
}
@@ -1298,7 +1299,7 @@ int notrace stm_source_write(struct stm_source_data *data,
stm = srcu_dereference(src->link, &stm_source_srcu);
if (stm)
- count = stm_write(stm, &src->output, chan, buf, count);
+ count = stm_write(stm, &src->output, chan, buf, count, data);
else
count = -ENODEV;
diff --git a/drivers/hwtracing/stm/ftrace.c b/drivers/hwtracing/stm/ftrace.c
index 3bb606dfa634..a7cea7ea0163 100644
--- a/drivers/hwtracing/stm/ftrace.c
+++ b/drivers/hwtracing/stm/ftrace.c
@@ -23,6 +23,7 @@ static struct stm_ftrace {
.data = {
.name = "ftrace",
.nr_chans = STM_FTRACE_NR_CHANNELS,
+ .type = STM_FTRACE,
.link = stm_ftrace_link,
.unlink = stm_ftrace_unlink,
},
diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c
index 81d7b21d31ec..e9496fe97baa 100644
--- a/drivers/hwtracing/stm/heartbeat.c
+++ b/drivers/hwtracing/stm/heartbeat.c
@@ -78,6 +78,7 @@ static int stm_heartbeat_init(void)
}
stm_heartbeat[i].data.nr_chans = 1;
+ stm_heartbeat[i].data.type = STM_USER;
stm_heartbeat[i].data.link = stm_heartbeat_link;
stm_heartbeat[i].data.unlink = stm_heartbeat_unlink;
hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC,
diff --git a/drivers/hwtracing/stm/p_basic.c b/drivers/hwtracing/stm/p_basic.c
index 8980a6a5fd6c..5525c975cc6f 100644
--- a/drivers/hwtracing/stm/p_basic.c
+++ b/drivers/hwtracing/stm/p_basic.c
@@ -10,7 +10,8 @@
#include "stm.h"
static ssize_t basic_write(struct stm_data *data, struct stm_output *output,
- unsigned int chan, const char *buf, size_t count)
+ unsigned int chan, const char *buf, size_t count,
+ struct stm_source_data *source)
{
unsigned int c = output->channel + chan;
unsigned int m = output->master;
diff --git a/drivers/hwtracing/stm/p_sys-t.c b/drivers/hwtracing/stm/p_sys-t.c
index 8254971c02e7..1e75aa0025a3 100644
--- a/drivers/hwtracing/stm/p_sys-t.c
+++ b/drivers/hwtracing/stm/p_sys-t.c
@@ -20,6 +20,7 @@ enum sys_t_message_type {
MIPI_SYST_TYPE_RAW = 6,
MIPI_SYST_TYPE_SHORT64,
MIPI_SYST_TYPE_CLOCK,
+ MIPI_SYST_TYPE_SBD,
};
enum sys_t_message_severity {
@@ -53,6 +54,19 @@ enum sys_t_message_string_subtype {
MIPI_SYST_STRING_PRINTF_64 = 12,
};
+/**
+ * enum sys_t_message_sbd_subtype - SyS-T SBD message subtypes
+ * @MIPI_SYST_SBD_ID32: SBD message with 32-bit message ID
+ * @MIPI_SYST_SBD_ID64: SBD message with 64-bit message ID
+ *
+ * Structured Binary Data messages can send information of arbitrary length,
+ * together with ID's that describe BLOB's content and layout.
+ */
+enum sys_t_message_sbd_subtype {
+ MIPI_SYST_SBD_ID32 = 0,
+ MIPI_SYST_SBD_ID64 = 1,
+};
+
#define MIPI_SYST_TYPE(t) ((u32)(MIPI_SYST_TYPE_ ## t))
#define MIPI_SYST_SEVERITY(s) ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4)
#define MIPI_SYST_OPT_LOC BIT(8)
@@ -75,6 +89,20 @@ enum sys_t_message_string_subtype {
#define CLOCK_SYNC_HEADER (MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC) | \
MIPI_SYST_SEVERITY(MAX))
+/*
+ * SyS-T and ftrace headers are compatible to an extent that ftrace event ID
+ * and args can be treated as SyS-T SBD message with 64-bit ID and arguments
+ * BLOB right behind the header without modification. Bits [16:63] coming
+ * together with message ID from ftrace aren't used by SBD and must be zeroed.
+ *
+ * 0 15 16 23 24 31 32 39 40 63
+ * ftrace: <event_id> <flags> <preempt> <-pid-> <----> <args>
+ * SBD: <------- msg_id ------------------------------> <BLOB>
+ */
+#define SBD_HEADER (MIPI_SYST_TYPES(SBD, ID64) | \
+ MIPI_SYST_SEVERITY(INFO) | \
+ MIPI_SYST_OPT_GUID)
+
struct sys_t_policy_node {
uuid_t uuid;
bool do_len;
@@ -284,14 +312,67 @@ sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c)
return sizeof(header) + sizeof(payload);
}
+static inline u32 sys_t_header(struct stm_source_data *source)
+{
+ if (source && source->type == STM_FTRACE)
+ return SBD_HEADER;
+ return DATA_HEADER;
+}
+
+static ssize_t sys_t_write_data(struct stm_data *data,
+ struct stm_source_data *source,
+ unsigned int master, unsigned int channel,
+ bool ts_first, const void *buf, size_t count)
+{
+ ssize_t sz;
+ const unsigned char nil = 0;
+
+ /*
+ * Ftrace is zero-copy compatible with SyS-T SBD, but requires
+ * special handling of first 64 bits. Trim and send them separately
+ * to avoid damage on original ftrace buffer.
+ */
+ if (source && source->type == STM_FTRACE) {
+ u64 compat_ftrace_header;
+ ssize_t header_sz;
+ ssize_t buf_sz;
+
+ if (count < sizeof(compat_ftrace_header))
+ return -EINVAL;
+
+ /* SBD only makes use of low 16 bits (event ID) from ftrace event */
+ compat_ftrace_header = *(u64 *)buf & 0xffff;
+ header_sz = stm_data_write(data, master, channel, false,
+ &compat_ftrace_header,
+ sizeof(compat_ftrace_header));
+ if (header_sz != sizeof(compat_ftrace_header))
+ return header_sz;
+
+ buf_sz = stm_data_write(data, master, channel, false,
+ buf + header_sz, count - header_sz);
+ if (buf_sz != count - header_sz)
+ return buf_sz;
+ sz = header_sz + buf_sz;
+ } else {
+ sz = stm_data_write(data, master, channel, false, buf, count);
+ }
+
+ if (sz <= 0)
+ return sz;
+
+ data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil);
+
+ return sz;
+}
+
static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
- unsigned int chan, const char *buf, size_t count)
+ unsigned int chan, const char *buf, size_t count,
+ struct stm_source_data *source)
{
struct sys_t_output *op = output->pdrv_private;
unsigned int c = output->channel + chan;
unsigned int m = output->master;
- const unsigned char nil = 0;
- u32 header = DATA_HEADER;
+ u32 header = sys_t_header(source);
u8 uuid[UUID_SIZE];
ssize_t sz;
@@ -348,11 +429,7 @@ static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
}
/* DATA */
- sz = stm_data_write(data, m, c, false, buf, count);
- if (sz > 0)
- data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
-
- return sz;
+ return sys_t_write_data(data, source, m, c, false, buf, count);
}
static const struct stm_protocol_driver sys_t_pdrv = {
diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h
index a9be49fc7a6b..85dda6e0d10c 100644
--- a/drivers/hwtracing/stm/stm.h
+++ b/drivers/hwtracing/stm/stm.h
@@ -96,7 +96,7 @@ struct stm_protocol_driver {
const char *name;
ssize_t (*write)(struct stm_data *data,
struct stm_output *output, unsigned int chan,
- const char *buf, size_t count);
+ const char *buf, size_t count, struct stm_source_data *source);
void (*policy_node_init)(void *arg);
int (*output_open)(void *priv, struct stm_output *output);
void (*output_close)(struct stm_output *output);
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 0ba0e1521ba4..cb80ef837e84 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_IIO) += industrialio.o
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+industrialio-$(CONFIG_ACPI) += industrialio-acpi.o
obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o
diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h
index 284bd387ce69..3d5c8719db3d 100644
--- a/drivers/iio/accel/adxl345.h
+++ b/drivers/iio/accel/adxl345.h
@@ -8,6 +8,39 @@
#ifndef _ADXL345_H_
#define _ADXL345_H_
+#define ADXL345_REG_DEVID 0x00
+#define ADXL345_REG_OFSX 0x1E
+#define ADXL345_REG_OFSY 0x1F
+#define ADXL345_REG_OFSZ 0x20
+#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
+#define ADXL345_REG_BW_RATE 0x2C
+#define ADXL345_REG_POWER_CTL 0x2D
+#define ADXL345_REG_DATA_FORMAT 0x31
+#define ADXL345_REG_DATAX0 0x32
+#define ADXL345_REG_DATAY0 0x34
+#define ADXL345_REG_DATAZ0 0x36
+#define ADXL345_REG_DATA_AXIS(index) \
+ (ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
+
+#define ADXL345_BW_RATE GENMASK(3, 0)
+#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
+
+#define ADXL345_POWER_CTL_MEASURE BIT(3)
+#define ADXL345_POWER_CTL_STANDBY 0x00
+
+#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */
+#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */
+#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
+#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */
+#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */
+
+#define ADXL345_DATA_FORMAT_2G 0
+#define ADXL345_DATA_FORMAT_4G 1
+#define ADXL345_DATA_FORMAT_8G 2
+#define ADXL345_DATA_FORMAT_16G 3
+
+#define ADXL345_DEVID 0xE5
+
/*
* In full-resolution mode, scale factor is maintained at ~4 mg/LSB
* in all g ranges.
@@ -28,6 +61,7 @@ struct adxl345_chip_info {
int uscale;
};
-int adxl345_core_probe(struct device *dev, struct regmap *regmap);
+int adxl345_core_probe(struct device *dev, struct regmap *regmap,
+ int (*setup)(struct device*, struct regmap*));
#endif /* _ADXL345_H_ */
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index 8bd30a23ed3b..006ce66c0aa3 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -17,38 +17,9 @@
#include "adxl345.h"
-#define ADXL345_REG_DEVID 0x00
-#define ADXL345_REG_OFSX 0x1e
-#define ADXL345_REG_OFSY 0x1f
-#define ADXL345_REG_OFSZ 0x20
-#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
-#define ADXL345_REG_BW_RATE 0x2C
-#define ADXL345_REG_POWER_CTL 0x2D
-#define ADXL345_REG_DATA_FORMAT 0x31
-#define ADXL345_REG_DATAX0 0x32
-#define ADXL345_REG_DATAY0 0x34
-#define ADXL345_REG_DATAZ0 0x36
-#define ADXL345_REG_DATA_AXIS(index) \
- (ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
-
-#define ADXL345_BW_RATE GENMASK(3, 0)
-#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
-
-#define ADXL345_POWER_CTL_MEASURE BIT(3)
-#define ADXL345_POWER_CTL_STANDBY 0x00
-
-#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
-#define ADXL345_DATA_FORMAT_2G 0
-#define ADXL345_DATA_FORMAT_4G 1
-#define ADXL345_DATA_FORMAT_8G 2
-#define ADXL345_DATA_FORMAT_16G 3
-
-#define ADXL345_DEVID 0xE5
-
struct adxl345_data {
const struct adxl345_chip_info *info;
struct regmap *regmap;
- u8 data_range;
};
#define ADXL345_CHANNEL(index, axis) { \
@@ -197,44 +168,75 @@ static void adxl345_powerdown(void *regmap)
regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY);
}
-int adxl345_core_probe(struct device *dev, struct regmap *regmap)
+/**
+ * adxl345_core_probe() - probe and setup for the adxl345 accelerometer,
+ * also covers the adlx375 accelerometer
+ * @dev: Driver model representation of the device
+ * @regmap: Regmap instance for the device
+ * @setup: Setup routine to be executed right before the standard device
+ * setup
+ *
+ * Return: 0 on success, negative errno on error
+ */
+int adxl345_core_probe(struct device *dev, struct regmap *regmap,
+ int (*setup)(struct device*, struct regmap*))
{
struct adxl345_data *data;
struct iio_dev *indio_dev;
u32 regval;
+ unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE |
+ ADXL345_DATA_FORMAT_JUSTIFY |
+ ADXL345_DATA_FORMAT_FULL_RES |
+ ADXL345_DATA_FORMAT_SELF_TEST);
int ret;
- ret = regmap_read(regmap, ADXL345_REG_DEVID, &regval);
- if (ret < 0)
- return dev_err_probe(dev, ret, "Error reading device ID\n");
-
- if (regval != ADXL345_DEVID)
- return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",
- regval, ADXL345_DEVID);
-
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = regmap;
- /* Enable full-resolution mode */
- data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
data->info = device_get_match_data(dev);
if (!data->info)
return -ENODEV;
- ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
- data->data_range);
- if (ret < 0)
- return dev_err_probe(dev, ret, "Failed to set data range\n");
-
indio_dev->name = data->info->name;
indio_dev->info = &adxl345_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl345_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
+ if (setup) {
+ /* Perform optional initial bus specific configuration */
+ ret = setup(dev, data->regmap);
+ if (ret)
+ return ret;
+
+ /* Enable full-resolution mode */
+ ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT,
+ data_format_mask,
+ ADXL345_DATA_FORMAT_FULL_RES);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set data range\n");
+
+ } else {
+ /* Enable full-resolution mode (init all data_format bits) */
+ ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
+ ADXL345_DATA_FORMAT_FULL_RES);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set data range\n");
+ }
+
+ ret = regmap_read(data->regmap, ADXL345_REG_DEVID, &regval);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error reading device ID\n");
+
+ if (regval != ADXL345_DEVID)
+ return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",
+ regval, ADXL345_DEVID);
+
/* Enable measurement mode */
ret = adxl345_powerup(data->regmap);
if (ret < 0)
diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c
index a3084b0a8f78..4065b8f7c8a8 100644
--- a/drivers/iio/accel/adxl345_i2c.c
+++ b/drivers/iio/accel/adxl345_i2c.c
@@ -27,7 +27,7 @@ static int adxl345_i2c_probe(struct i2c_client *client)
if (IS_ERR(regmap))
return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n");
- return adxl345_core_probe(&client->dev, regmap);
+ return adxl345_core_probe(&client->dev, regmap, NULL);
}
static const struct adxl345_chip_info adxl345_i2c_info = {
diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c
index 93ca349f1780..57e16b441702 100644
--- a/drivers/iio/accel/adxl345_spi.c
+++ b/drivers/iio/accel/adxl345_spi.c
@@ -20,6 +20,11 @@ static const struct regmap_config adxl345_spi_regmap_config = {
.read_flag_mask = BIT(7) | BIT(6),
};
+static int adxl345_spi_setup(struct device *dev, struct regmap *regmap)
+{
+ return regmap_write(regmap, ADXL345_REG_DATA_FORMAT, ADXL345_DATA_FORMAT_SPI_3WIRE);
+}
+
static int adxl345_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
@@ -33,7 +38,10 @@ static int adxl345_spi_probe(struct spi_device *spi)
if (IS_ERR(regmap))
return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n");
- return adxl345_core_probe(&spi->dev, regmap);
+ if (spi->mode & SPI_3WIRE)
+ return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup);
+ else
+ return adxl345_core_probe(&spi->dev, regmap, NULL);
}
static const struct adxl345_chip_info adxl345_spi_info = {
diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c
index 210228affb80..5cf4828a5eb5 100644
--- a/drivers/iio/accel/adxl367.c
+++ b/drivers/iio/accel/adxl367.c
@@ -621,7 +621,7 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- struct adxl367_state *st = iio_priv(indio_dev);;
+ struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 110591804b4c..ae0cd48a3e29 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -386,13 +386,9 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
static bool bmc150_apply_bosc0200_acpi_orientation(struct device *dev,
struct iio_mount_matrix *orientation)
{
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct acpi_device *adev = ACPI_COMPANION(dev);
- char *name, *alt_name, *label, *str;
- union acpi_object *obj, *elements;
- acpi_status status;
- int i, j, val[3];
+ char *name, *alt_name, *label;
if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) {
alt_name = "ROMK";
@@ -411,43 +407,7 @@ static bool bmc150_apply_bosc0200_acpi_orientation(struct device *dev,
return false;
}
- status = acpi_evaluate_object(adev->handle, name, NULL, &buffer);
- if (ACPI_FAILURE(status)) {
- dev_warn(dev, "Failed to get ACPI mount matrix: %d\n", status);
- return false;
- }
-
- obj = buffer.pointer;
- if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3)
- goto unknown_format;
-
- elements = obj->package.elements;
- for (i = 0; i < 3; i++) {
- if (elements[i].type != ACPI_TYPE_STRING)
- goto unknown_format;
-
- str = elements[i].string.pointer;
- if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3)
- goto unknown_format;
-
- for (j = 0; j < 3; j++) {
- switch (val[j]) {
- case -1: str = "-1"; break;
- case 0: str = "0"; break;
- case 1: str = "1"; break;
- default: goto unknown_format;
- }
- orientation->rotation[i * 3 + j] = str;
- }
- }
-
- kfree(buffer.pointer);
- return true;
-
-unknown_format:
- dev_warn(dev, "Unknown ACPI mount matrix format, ignoring\n");
- kfree(buffer.pointer);
- return false;
+ return iio_read_acpi_mount_matrix(dev, orientation, name);
}
static bool bmc150_apply_dual250e_acpi_orientation(struct device *dev,
diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c
index be8a15cb945f..4fbc01bda62e 100644
--- a/drivers/iio/accel/fxls8962af-core.c
+++ b/drivers/iio/accel/fxls8962af-core.c
@@ -15,9 +15,11 @@
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/i2c.h>
+#include <linux/irq.h>
#include <linux/module.h>
-#include <linux/of_irq.h>
+#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
@@ -1062,12 +1064,12 @@ static void fxls8962af_pm_disable(void *dev_ptr)
fxls8962af_standby(iio_priv(indio_dev));
}
-static void fxls8962af_get_irq(struct device_node *of_node,
+static void fxls8962af_get_irq(struct device *dev,
enum fxls8962af_int_pin *pin)
{
int irq;
- irq = of_irq_get_byname(of_node, "INT2");
+ irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
if (irq > 0) {
*pin = FXLS8962AF_PIN_INT2;
return;
@@ -1086,7 +1088,7 @@ static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq)
u8 int_pin_sel;
int ret;
- fxls8962af_get_irq(dev->of_node, &int_pin);
+ fxls8962af_get_irq(dev, &int_pin);
switch (int_pin) {
case FXLS8962AF_PIN_INT1:
int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT1;
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index 126e8bdd6d0e..8280d2bef0a3 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -636,84 +636,6 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
return 0;
}
-#ifdef CONFIG_ACPI
-static bool kxj_acpi_orientation(struct device *dev,
- struct iio_mount_matrix *orientation)
-{
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- struct acpi_device *adev = ACPI_COMPANION(dev);
- char *str;
- union acpi_object *obj, *elements;
- acpi_status status;
- int i, j, val[3];
- bool ret = false;
-
- if (!acpi_has_method(adev->handle, "ROTM"))
- return false;
-
- status = acpi_evaluate_object(adev->handle, "ROTM", NULL, &buffer);
- if (ACPI_FAILURE(status)) {
- dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status);
- return false;
- }
-
- obj = buffer.pointer;
- if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) {
- dev_err(dev, "Unknown ACPI mount matrix package format\n");
- goto out_free_buffer;
- }
-
- elements = obj->package.elements;
- for (i = 0; i < 3; i++) {
- if (elements[i].type != ACPI_TYPE_STRING) {
- dev_err(dev, "Unknown ACPI mount matrix element format\n");
- goto out_free_buffer;
- }
-
- str = elements[i].string.pointer;
- if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) {
- dev_err(dev, "Incorrect ACPI mount matrix string format\n");
- goto out_free_buffer;
- }
-
- for (j = 0; j < 3; j++) {
- switch (val[j]) {
- case -1: str = "-1"; break;
- case 0: str = "0"; break;
- case 1: str = "1"; break;
- default:
- dev_err(dev, "Invalid value in ACPI mount matrix: %d\n", val[j]);
- goto out_free_buffer;
- }
- orientation->rotation[i * 3 + j] = str;
- }
- }
-
- ret = true;
-
-out_free_buffer:
- kfree(buffer.pointer);
- return ret;
-}
-
-static bool kxj1009_apply_acpi_orientation(struct device *dev,
- struct iio_mount_matrix *orientation)
-{
- struct acpi_device *adev = ACPI_COMPANION(dev);
-
- if (adev && acpi_dev_hid_uid_match(adev, "KIOX000A", NULL))
- return kxj_acpi_orientation(dev, orientation);
-
- return false;
-}
-#else
-static bool kxj1009_apply_acpi_orientation(struct device *dev,
- struct iio_mount_matrix *orientation)
-{
- return false;
-}
-#endif
-
static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data)
{
int ret;
@@ -1544,7 +1466,7 @@ static int kxcjk1013_probe(struct i2c_client *client)
} else {
data->active_high_intr = true; /* default polarity */
- if (!kxj1009_apply_acpi_orientation(&client->dev, &data->orientation)) {
+ if (!iio_read_acpi_mount_matrix(&client->dev, &data->orientation, "ROTM")) {
ret = iio_read_mount_matrix(&client->dev, &data->orientation);
if (ret)
return ret;
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index d3fd0318e47b..62e6369e2269 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -19,6 +19,8 @@
*/
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -28,8 +30,6 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/events.h>
#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -1642,7 +1642,7 @@ static int mma8452_probe(struct i2c_client *client)
if (client->irq) {
int irq2;
- irq2 = of_irq_get_byname(client->dev.of_node, "INT2");
+ irq2 = fwnode_irq_get_byname(dev_fwnode(&client->dev), "INT2");
if (irq2 == client->irq) {
dev_dbg(&client->dev, "using interrupt line INT2\n");
diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
index 63c3566a533b..e56407b6f204 100644
--- a/drivers/iio/accel/mxc4005.c
+++ b/drivers/iio/accel/mxc4005.c
@@ -65,6 +65,7 @@ struct mxc4005_data {
struct mutex mutex;
struct regmap *regmap;
struct iio_trigger *dready_trig;
+ struct iio_mount_matrix orientation;
/* Ensure timestamp is naturally aligned */
struct {
__be16 chans[3];
@@ -272,6 +273,20 @@ static int mxc4005_write_raw(struct iio_dev *indio_dev,
}
}
+static const struct iio_mount_matrix *
+mxc4005_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct mxc4005_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
+static const struct iio_chan_spec_ext_info mxc4005_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mxc4005_get_mount_matrix),
+ { }
+};
+
static const struct iio_info mxc4005_info = {
.read_raw = mxc4005_read_raw,
.write_raw = mxc4005_write_raw,
@@ -298,6 +313,7 @@ static const unsigned long mxc4005_scan_masks[] = {
.shift = 4, \
.endianness = IIO_BE, \
}, \
+ .ext_info = mxc4005_ext_info, \
}
static const struct iio_chan_spec mxc4005_channels[] = {
@@ -440,6 +456,12 @@ static int mxc4005_probe(struct i2c_client *client)
mutex_init(&data->mutex);
+ if (!iio_read_acpi_mount_matrix(&client->dev, &data->orientation, "ROTM")) {
+ ret = iio_read_mount_matrix(&client->dev, &data->orientation);
+ if (ret)
+ return ret;
+ }
+
indio_dev->channels = mxc4005_channels;
indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels);
indio_dev->available_scan_masks = mxc4005_scan_masks;
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 0d9282fa67f5..8db68b80b391 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -70,6 +70,23 @@ config AD7124
To compile this driver as a module, choose M here: the module will be
called ad7124.
+config AD7173
+ tristate "Analog Devices AD7173 driver"
+ depends on SPI_MASTER
+ select AD_SIGMA_DELTA
+ select GPIO_REGMAP if GPIOLIB
+ select REGMAP_SPI if GPIOLIB
+ help
+ Say yes here to build support for Analog Devices AD7173 and similar ADC
+ Currently supported models:
+ - AD7172-2
+ - AD7173-8
+ - AD7175-2
+ - AD7176-2
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7173.
+
config AD7192
tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver"
depends on SPI
@@ -264,6 +281,16 @@ config AD7923
To compile this driver as a module, choose M here: the
module will be called ad7923.
+config AD7944
+ tristate "Analog Devices AD7944 and similar ADCs driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices
+ AD7944, AD7985, AD7986 ADCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7944
+
config AD7949
tristate "Analog Devices AD7949 and similar ADCs driver"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index b3c434722364..edb32ce2af02 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_AD7091R) += ad7091r-base.o
obj-$(CONFIG_AD7091R5) += ad7091r5.o
obj-$(CONFIG_AD7091R8) += ad7091r8.o
obj-$(CONFIG_AD7124) += ad7124.o
+obj-$(CONFIG_AD7173) += ad7173.o
obj-$(CONFIG_AD7192) += ad7192.o
obj-$(CONFIG_AD7266) += ad7266.o
obj-$(CONFIG_AD7280) += ad7280a.o
@@ -28,6 +29,7 @@ obj-$(CONFIG_AD7780) += ad7780.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
+obj-$(CONFIG_AD7944) += ad7944.o
obj-$(CONFIG_AD7949) += ad7949.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AD9467) += ad9467.o
diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c
index 80645fee79a4..59f66e9cb0e8 100644
--- a/drivers/iio/adc/ab8500-gpadc.c
+++ b/drivers/iio/adc/ab8500-gpadc.c
@@ -1021,14 +1021,13 @@ static int ab8500_gpadc_parse_channel(struct device *dev,
/**
* ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT
* @gpadc: the GPADC to configure the channels for
- * @chans: the IIO channels we parsed
- * @nchans: the number of IIO channels we parsed
+ * @chans_parsed: the IIO channels we parsed
+ * @nchans_parsed: the number of IIO channels we parsed
*/
static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
struct iio_chan_spec **chans_parsed,
unsigned int *nchans_parsed)
{
- struct fwnode_handle *child;
struct ab8500_gpadc_chan_info *ch;
struct iio_chan_spec *iio_chans;
unsigned int nchans;
@@ -1052,7 +1051,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
return -ENOMEM;
i = 0;
- device_for_each_child_node(gpadc->dev, child) {
+ device_for_each_child_node_scoped(gpadc->dev, child) {
struct iio_chan_spec *iio_chan;
int ret;
@@ -1062,7 +1061,6 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch,
iio_chan);
if (ret) {
- fwnode_handle_put(child);
return ret;
}
i++;
diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c
index febb64e67955..aaf1fb0ac447 100644
--- a/drivers/iio/adc/ad4130.c
+++ b/drivers/iio/adc/ad4130.c
@@ -1600,17 +1600,14 @@ static int ad4130_parse_fw_children(struct iio_dev *indio_dev)
{
struct ad4130_state *st = iio_priv(indio_dev);
struct device *dev = &st->spi->dev;
- struct fwnode_handle *child;
int ret;
indio_dev->channels = st->chans;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
ret = ad4130_parse_fw_channel(indio_dev, child);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
}
return 0;
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index b9b206fcd748..e7b1d517d3de 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -14,7 +14,8 @@
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -807,22 +808,19 @@ static int ad7124_check_chip_id(struct ad7124_state *st)
return 0;
}
-static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
- struct device_node *np)
+static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
+ struct device *dev)
{
struct ad7124_state *st = iio_priv(indio_dev);
struct ad7124_channel_config *cfg;
struct ad7124_channel *channels;
- struct device_node *child;
struct iio_chan_spec *chan;
unsigned int ain[2], channel = 0, tmp;
int ret;
- st->num_channels = of_get_available_child_count(np);
- if (!st->num_channels) {
- dev_err(indio_dev->dev.parent, "no channel children\n");
- return -ENODEV;
- }
+ st->num_channels = device_get_child_node_count(dev);
+ if (!st->num_channels)
+ return dev_err_probe(dev, -ENODEV, "no channel children\n");
chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
sizeof(*chan), GFP_KERNEL);
@@ -838,39 +836,38 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
indio_dev->num_channels = st->num_channels;
st->channels = channels;
- for_each_available_child_of_node(np, child) {
+ device_for_each_child_node_scoped(dev, child) {
cfg = &st->channels[channel].cfg;
- ret = of_property_read_u32(child, "reg", &channel);
+ ret = fwnode_property_read_u32(child, "reg", &channel);
if (ret)
- goto err;
+ return ret;
- if (channel >= indio_dev->num_channels) {
- dev_err(indio_dev->dev.parent,
+ if (channel >= indio_dev->num_channels)
+ return dev_err_probe(dev, -EINVAL,
"Channel index >= number of channels\n");
- ret = -EINVAL;
- goto err;
- }
- ret = of_property_read_u32_array(child, "diff-channels",
- ain, 2);
+ ret = fwnode_property_read_u32_array(child, "diff-channels",
+ ain, 2);
if (ret)
- goto err;
+ return ret;
st->channels[channel].nr = channel;
st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
AD7124_CHANNEL_AINM(ain[1]);
- cfg->bipolar = of_property_read_bool(child, "bipolar");
+ cfg->bipolar = fwnode_property_read_bool(child, "bipolar");
- ret = of_property_read_u32(child, "adi,reference-select", &tmp);
+ ret = fwnode_property_read_u32(child, "adi,reference-select", &tmp);
if (ret)
cfg->refsel = AD7124_INT_REF;
else
cfg->refsel = tmp;
- cfg->buf_positive = of_property_read_bool(child, "adi,buffered-positive");
- cfg->buf_negative = of_property_read_bool(child, "adi,buffered-negative");
+ cfg->buf_positive =
+ fwnode_property_read_bool(child, "adi,buffered-positive");
+ cfg->buf_negative =
+ fwnode_property_read_bool(child, "adi,buffered-negative");
chan[channel] = ad7124_channel_template;
chan[channel].address = channel;
@@ -880,10 +877,6 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
}
return 0;
-err:
- of_node_put(child);
-
- return ret;
}
static int ad7124_setup(struct ad7124_state *st)
@@ -943,9 +936,7 @@ static int ad7124_probe(struct spi_device *spi)
struct iio_dev *indio_dev;
int i, ret;
- info = of_device_get_match_data(&spi->dev);
- if (!info)
- info = (void *)spi_get_device_id(spi)->driver_data;
+ info = spi_get_device_match_data(spi);
if (!info)
return -ENODEV;
@@ -965,7 +956,7 @@ static int ad7124_probe(struct spi_device *spi)
if (ret < 0)
return ret;
- ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node);
+ ret = ad7124_parse_channel_config(indio_dev, &spi->dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
new file mode 100644
index 000000000000..a7826bba0852
--- /dev/null
+++ b/drivers/iio/adc/ad7173.c
@@ -0,0 +1,1180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AD717x family SPI ADC driver
+ *
+ * Supported devices:
+ * AD7172-2/AD7172-4/AD7173-8/AD7175-2
+ * AD7175-8/AD7176-2/AD7177-2
+ *
+ * Copyright (C) 2015, 2024 Analog Devices, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/container_of.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#define AD7173_REG_COMMS 0x00
+#define AD7173_REG_ADC_MODE 0x01
+#define AD7173_REG_INTERFACE_MODE 0x02
+#define AD7173_REG_CRC 0x03
+#define AD7173_REG_DATA 0x04
+#define AD7173_REG_GPIO 0x06
+#define AD7173_REG_ID 0x07
+#define AD7173_REG_CH(x) (0x10 + (x))
+#define AD7173_REG_SETUP(x) (0x20 + (x))
+#define AD7173_REG_FILTER(x) (0x28 + (x))
+#define AD7173_REG_OFFSET(x) (0x30 + (x))
+#define AD7173_REG_GAIN(x) (0x38 + (x))
+
+#define AD7173_RESET_LENGTH BITS_TO_BYTES(64)
+
+#define AD7173_CH_ENABLE BIT(15)
+#define AD7173_CH_SETUP_SEL_MASK GENMASK(14, 12)
+#define AD7173_CH_SETUP_AINPOS_MASK GENMASK(9, 5)
+#define AD7173_CH_SETUP_AINNEG_MASK GENMASK(4, 0)
+
+#define AD7173_CH_ADDRESS(pos, neg) \
+ (FIELD_PREP(AD7173_CH_SETUP_AINPOS_MASK, pos) | \
+ FIELD_PREP(AD7173_CH_SETUP_AINNEG_MASK, neg))
+#define AD7173_AIN_TEMP_POS 17
+#define AD7173_AIN_TEMP_NEG 18
+
+#define AD7172_2_ID 0x00d0
+#define AD7175_ID 0x0cd0
+#define AD7176_ID 0x0c90
+#define AD7175_2_ID 0x0cd0
+#define AD7172_4_ID 0x2050
+#define AD7173_ID 0x30d0
+#define AD7175_8_ID 0x3cd0
+#define AD7177_ID 0x4fd0
+#define AD7173_ID_MASK GENMASK(15, 4)
+
+#define AD7173_ADC_MODE_REF_EN BIT(15)
+#define AD7173_ADC_MODE_SING_CYC BIT(13)
+#define AD7173_ADC_MODE_MODE_MASK GENMASK(6, 4)
+#define AD7173_ADC_MODE_CLOCKSEL_MASK GENMASK(3, 2)
+#define AD7173_ADC_MODE_CLOCKSEL_INT 0x0
+#define AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT 0x1
+#define AD7173_ADC_MODE_CLOCKSEL_EXT 0x2
+#define AD7173_ADC_MODE_CLOCKSEL_XTAL 0x3
+
+#define AD7173_GPIO_PDSW BIT(14)
+#define AD7173_GPIO_OP_EN2_3 BIT(13)
+#define AD7173_GPIO_MUX_IO BIT(12)
+#define AD7173_GPIO_SYNC_EN BIT(11)
+#define AD7173_GPIO_ERR_EN BIT(10)
+#define AD7173_GPIO_ERR_DAT BIT(9)
+#define AD7173_GPIO_GP_DATA3 BIT(7)
+#define AD7173_GPIO_GP_DATA2 BIT(6)
+#define AD7173_GPIO_IP_EN1 BIT(5)
+#define AD7173_GPIO_IP_EN0 BIT(4)
+#define AD7173_GPIO_OP_EN1 BIT(3)
+#define AD7173_GPIO_OP_EN0 BIT(2)
+#define AD7173_GPIO_GP_DATA1 BIT(1)
+#define AD7173_GPIO_GP_DATA0 BIT(0)
+
+#define AD7173_GPO12_DATA(x) BIT((x) + 0)
+#define AD7173_GPO23_DATA(x) BIT((x) + 4)
+#define AD7173_GPO_DATA(x) ((x) < 2 ? AD7173_GPO12_DATA(x) : AD7173_GPO23_DATA(x))
+
+#define AD7173_INTERFACE_DATA_STAT BIT(6)
+#define AD7173_INTERFACE_DATA_STAT_EN(x) \
+ FIELD_PREP(AD7173_INTERFACE_DATA_STAT, x)
+
+#define AD7173_SETUP_BIPOLAR BIT(12)
+#define AD7173_SETUP_AREF_BUF_MASK GENMASK(11, 10)
+#define AD7173_SETUP_AIN_BUF_MASK GENMASK(9, 8)
+
+#define AD7173_SETUP_REF_SEL_MASK GENMASK(5, 4)
+#define AD7173_SETUP_REF_SEL_AVDD1_AVSS 0x3
+#define AD7173_SETUP_REF_SEL_INT_REF 0x2
+#define AD7173_SETUP_REF_SEL_EXT_REF2 0x1
+#define AD7173_SETUP_REF_SEL_EXT_REF 0x0
+#define AD7173_VOLTAGE_INT_REF_uV 2500000
+#define AD7173_TEMP_SENSIIVITY_uV_per_C 477
+#define AD7177_ODR_START_VALUE 0x07
+
+#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0)
+#define AD7173_MAX_CONFIGS 8
+
+enum ad7173_ids {
+ ID_AD7172_2,
+ ID_AD7172_4,
+ ID_AD7173_8,
+ ID_AD7175_2,
+ ID_AD7175_8,
+ ID_AD7176_2,
+ ID_AD7177_2,
+};
+
+struct ad7173_device_info {
+ const unsigned int *sinc5_data_rates;
+ unsigned int num_sinc5_data_rates;
+ unsigned int odr_start_value;
+ unsigned int num_channels;
+ unsigned int num_configs;
+ unsigned int num_inputs;
+ unsigned int clock;
+ unsigned int id;
+ char *name;
+ bool has_temp;
+ bool has_int_ref;
+ bool has_ref2;
+ u8 num_gpios;
+};
+
+struct ad7173_channel_config {
+ u8 cfg_slot;
+ bool live;
+
+ /* Following fields are used to compare equality. */
+ struct_group(config_props,
+ bool bipolar;
+ bool input_buf;
+ u8 odr;
+ u8 ref_sel;
+ );
+};
+
+struct ad7173_channel {
+ unsigned int chan_reg;
+ unsigned int ain;
+ struct ad7173_channel_config cfg;
+};
+
+struct ad7173_state {
+ struct ad_sigma_delta sd;
+ const struct ad7173_device_info *info;
+ struct ad7173_channel *channels;
+ struct regulator_bulk_data regulators[3];
+ unsigned int adc_mode;
+ unsigned int interface_mode;
+ unsigned int num_channels;
+ struct ida cfg_slots_status;
+ unsigned long long config_usage_counter;
+ unsigned long long *config_cnts;
+ struct clk *ext_clk;
+ struct clk_hw int_clk_hw;
+#if IS_ENABLED(CONFIG_GPIOLIB)
+ struct regmap *reg_gpiocon_regmap;
+ struct gpio_regmap *gpio_regmap;
+#endif
+};
+
+static const unsigned int ad7173_sinc5_data_rates[] = {
+ 6211000, 6211000, 6211000, 6211000, 6211000, 6211000, 5181000, 4444000, /* 0-7 */
+ 3115000, 2597000, 1007000, 503800, 381000, 200300, 100500, 59520, /* 8-15 */
+ 49680, 20010, 16333, 10000, 5000, 2500, 1250, /* 16-22 */
+};
+
+static const unsigned int ad7175_sinc5_data_rates[] = {
+ 50000000, 41667000, 31250000, 27778000, /* 0-3 */
+ 20833000, 17857000, 12500000, 10000000, /* 4-7 */
+ 5000000, 2500000, 1000000, 500000, /* 8-11 */
+ 397500, 200000, 100000, 59920, /* 12-15 */
+ 49960, 20000, 16666, 10000, /* 16-19 */
+ 5000, /* 20 */
+};
+
+static const struct ad7173_device_info ad7173_device_info[] = {
+ [ID_AD7172_2] = {
+ .name = "ad7172-2",
+ .id = AD7172_2_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_int_ref = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+ },
+ [ID_AD7172_4] = {
+ .id = AD7172_4_ID,
+ .num_inputs = 9,
+ .num_channels = 8,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = false,
+ .has_ref2 = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+ },
+ [ID_AD7173_8] = {
+ .name = "ad7173-8",
+ .id = AD7173_ID,
+ .num_inputs = 17,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = true,
+ .has_int_ref = true,
+ .has_ref2 = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+ },
+ [ID_AD7175_2] = {
+ .name = "ad7175-2",
+ .id = AD7175_2_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_int_ref = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+ [ID_AD7175_8] = {
+ .id = AD7175_8_ID,
+ .num_inputs = 17,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = true,
+ .has_int_ref = true,
+ .has_ref2 = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+ [ID_AD7176_2] = {
+ .name = "ad7176-2",
+ .id = AD7176_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = false,
+ .has_int_ref = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+ [ID_AD7177_2] = {
+ .id = AD7177_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_int_ref = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .odr_start_value = AD7177_ODR_START_VALUE,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+};
+
+static const char *const ad7173_ref_sel_str[] = {
+ [AD7173_SETUP_REF_SEL_EXT_REF] = "vref",
+ [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2",
+ [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss",
+ [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd",
+};
+
+static const char *const ad7173_clk_sel[] = {
+ "ext-clk", "xtal"
+};
+
+#if IS_ENABLED(CONFIG_GPIOLIB)
+
+static const struct regmap_range ad7173_range_gpio[] = {
+ regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO),
+};
+
+static const struct regmap_access_table ad7173_access_table = {
+ .yes_ranges = ad7173_range_gpio,
+ .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio),
+};
+
+static const struct regmap_config ad7173_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .rd_table = &ad7173_access_table,
+ .wr_table = &ad7173_access_table,
+ .read_flag_mask = BIT(6),
+};
+
+static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
+ unsigned int offset, unsigned int *reg,
+ unsigned int *mask)
+{
+ *mask = AD7173_GPO_DATA(offset);
+ *reg = base;
+ return 0;
+}
+
+static void ad7173_gpio_disable(void *data)
+{
+ struct ad7173_state *st = data;
+ unsigned int mask;
+
+ mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3;
+ regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, ~mask);
+}
+
+static int ad7173_gpio_init(struct ad7173_state *st)
+{
+ struct gpio_regmap_config gpio_regmap = {};
+ struct device *dev = &st->sd.spi->dev;
+ unsigned int mask;
+ int ret;
+
+ st->reg_gpiocon_regmap = devm_regmap_init_spi(st->sd.spi, &ad7173_regmap_config);
+ ret = PTR_ERR_OR_ZERO(st->reg_gpiocon_regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to init regmap\n");
+
+ mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3;
+ regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, mask);
+
+ ret = devm_add_action_or_reset(dev, ad7173_gpio_disable, st);
+ if (ret)
+ return ret;
+
+ gpio_regmap.parent = dev;
+ gpio_regmap.regmap = st->reg_gpiocon_regmap;
+ gpio_regmap.ngpio = st->info->num_gpios;
+ gpio_regmap.reg_set_base = AD7173_REG_GPIO;
+ gpio_regmap.reg_mask_xlate = ad7173_mask_xlate;
+
+ st->gpio_regmap = devm_gpio_regmap_register(dev, &gpio_regmap);
+ ret = PTR_ERR_OR_ZERO(st->gpio_regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to init gpio-regmap\n");
+
+ return 0;
+}
+#else
+static int ad7173_gpio_init(struct ad7173_state *st)
+{
+ return 0;
+}
+#endif /* CONFIG_GPIOLIB */
+
+static struct ad7173_state *ad_sigma_delta_to_ad7173(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7173_state, sd);
+}
+
+static struct ad7173_state *clk_hw_to_ad7173(struct clk_hw *hw)
+{
+ return container_of(hw, struct ad7173_state, int_clk_hw);
+}
+
+static void ad7173_ida_destroy(void *data)
+{
+ struct ad7173_state *st = data;
+
+ ida_destroy(&st->cfg_slots_status);
+}
+
+static void ad7173_reset_usage_cnts(struct ad7173_state *st)
+{
+ memset64(st->config_cnts, 0, st->info->num_configs);
+ st->config_usage_counter = 0;
+}
+
+static struct ad7173_channel_config *
+ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
+{
+ struct ad7173_channel_config *cfg_aux;
+ ptrdiff_t cmp_size;
+ int i;
+
+ cmp_size = sizeof_field(struct ad7173_channel_config, config_props);
+ for (i = 0; i < st->num_channels; i++) {
+ cfg_aux = &st->channels[i].cfg;
+
+ if (cfg_aux->live &&
+ !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size))
+ return cfg_aux;
+ }
+ return NULL;
+}
+
+/* Could be replaced with a generic LRU implementation */
+static int ad7173_free_config_slot_lru(struct ad7173_state *st)
+{
+ int i, lru_position = 0;
+
+ for (i = 1; i < st->info->num_configs; i++)
+ if (st->config_cnts[i] < st->config_cnts[lru_position])
+ lru_position = i;
+
+ for (i = 0; i < st->num_channels; i++)
+ if (st->channels[i].cfg.cfg_slot == lru_position)
+ st->channels[i].cfg.live = false;
+
+ ida_free(&st->cfg_slots_status, lru_position);
+ return ida_alloc(&st->cfg_slots_status, GFP_KERNEL);
+}
+
+/* Could be replaced with a generic LRU implementation */
+static int ad7173_load_config(struct ad7173_state *st,
+ struct ad7173_channel_config *cfg)
+{
+ unsigned int config;
+ int free_cfg_slot, ret;
+
+ free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0,
+ st->info->num_configs - 1, GFP_KERNEL);
+ if (free_cfg_slot < 0)
+ free_cfg_slot = ad7173_free_config_slot_lru(st);
+
+ cfg->cfg_slot = free_cfg_slot;
+ config = FIELD_PREP(AD7173_SETUP_REF_SEL_MASK, cfg->ref_sel);
+
+ if (cfg->bipolar)
+ config |= AD7173_SETUP_BIPOLAR;
+
+ if (cfg->input_buf)
+ config |= AD7173_SETUP_AIN_BUF_MASK;
+
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_SETUP(free_cfg_slot), 2, config);
+ if (ret)
+ return ret;
+
+ return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
+ AD7173_FILTER_ODR0_MASK & cfg->odr);
+}
+
+static int ad7173_config_channel(struct ad7173_state *st, int addr)
+{
+ struct ad7173_channel_config *cfg = &st->channels[addr].cfg;
+ struct ad7173_channel_config *live_cfg;
+ int ret;
+
+ if (!cfg->live) {
+ live_cfg = ad7173_find_live_config(st, cfg);
+ if (live_cfg) {
+ cfg->cfg_slot = live_cfg->cfg_slot;
+ } else {
+ ret = ad7173_load_config(st, cfg);
+ if (ret)
+ return ret;
+ cfg->live = true;
+ }
+ }
+
+ if (st->config_usage_counter == U64_MAX)
+ ad7173_reset_usage_cnts(st);
+
+ st->config_usage_counter++;
+ st->config_cnts[cfg->cfg_slot] = st->config_usage_counter;
+
+ return 0;
+}
+
+static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+ unsigned int val;
+ int ret;
+
+ ret = ad7173_config_channel(st, channel);
+ if (ret)
+ return ret;
+
+ val = AD7173_CH_ENABLE |
+ FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) |
+ st->channels[channel].ain;
+
+ return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val);
+}
+
+static int ad7173_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+
+ st->adc_mode &= ~AD7173_ADC_MODE_MODE_MASK;
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_MODE_MASK, mode);
+
+ return ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 2, st->adc_mode);
+}
+
+static int ad7173_append_status(struct ad_sigma_delta *sd, bool append)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+ unsigned int interface_mode = st->interface_mode;
+ int ret;
+
+ interface_mode |= AD7173_INTERFACE_DATA_STAT_EN(append);
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_INTERFACE_MODE, 2, interface_mode);
+ if (ret)
+ return ret;
+
+ st->interface_mode = interface_mode;
+
+ return 0;
+}
+
+static int ad7173_disable_all(struct ad_sigma_delta *sd)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+ int ret;
+ int i;
+
+ for (i = 0; i < st->num_channels; i++) {
+ ret = ad_sd_write_reg(sd, AD7173_REG_CH(i), 2, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct ad_sigma_delta_info ad7173_sigma_delta_info = {
+ .set_channel = ad7173_set_channel,
+ .append_status = ad7173_append_status,
+ .disable_all = ad7173_disable_all,
+ .set_mode = ad7173_set_mode,
+ .has_registers = true,
+ .addr_shift = 0,
+ .read_mask = BIT(6),
+ .status_ch_mask = GENMASK(3, 0),
+ .data_reg = AD7173_REG_DATA,
+};
+
+static int ad7173_setup(struct iio_dev *indio_dev)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->sd.spi->dev;
+ u8 buf[AD7173_RESET_LENGTH];
+ unsigned int id;
+ int ret;
+
+ /* reset the serial interface */
+ memset(buf, 0xff, AD7173_RESET_LENGTH);
+ ret = spi_write_then_read(st->sd.spi, buf, sizeof(buf), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* datasheet recommends a delay of at least 500us after reset */
+ fsleep(500);
+
+ ret = ad_sd_read_reg(&st->sd, AD7173_REG_ID, 2, &id);
+ if (ret)
+ return ret;
+
+ id &= AD7173_ID_MASK;
+ if (id != st->info->id)
+ dev_warn(dev, "Unexpected device id: 0x%04X, expected: 0x%04X\n",
+ id, st->info->id);
+
+ st->adc_mode |= AD7173_ADC_MODE_SING_CYC;
+ st->interface_mode = 0x0;
+
+ st->config_usage_counter = 0;
+ st->config_cnts = devm_kcalloc(dev, st->info->num_configs,
+ sizeof(*st->config_cnts), GFP_KERNEL);
+ if (!st->config_cnts)
+ return -ENOMEM;
+
+ /* All channels are enabled by default after a reset */
+ return ad7173_disable_all(&st->sd);
+}
+
+static unsigned int ad7173_get_ref_voltage_milli(struct ad7173_state *st,
+ u8 reference_select)
+{
+ int vref;
+
+ switch (reference_select) {
+ case AD7173_SETUP_REF_SEL_EXT_REF:
+ vref = regulator_get_voltage(st->regulators[0].consumer);
+ break;
+
+ case AD7173_SETUP_REF_SEL_EXT_REF2:
+ vref = regulator_get_voltage(st->regulators[1].consumer);
+ break;
+
+ case AD7173_SETUP_REF_SEL_INT_REF:
+ vref = AD7173_VOLTAGE_INT_REF_uV;
+ break;
+
+ case AD7173_SETUP_REF_SEL_AVDD1_AVSS:
+ vref = regulator_get_voltage(st->regulators[2].consumer);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (vref < 0)
+ return vref;
+
+ return vref / (MICRO / MILLI);
+}
+
+static int ad7173_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct ad7173_channel *ch = &st->channels[chan->address];
+ unsigned int reg;
+ u64 temp;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ if (ret < 0)
+ return ret;
+
+ /* disable channel after single conversion */
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(chan->address), 2, 0);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ temp = AD7173_VOLTAGE_INT_REF_uV * MILLI;
+ temp /= AD7173_TEMP_SENSIIVITY_uV_per_C;
+ *val = temp;
+ *val2 = chan->scan_type.realbits;
+ } else {
+ *val = ad7173_get_ref_voltage_milli(st, ch->cfg.ref_sel);
+ *val2 = chan->scan_type.realbits - !!(ch->cfg.bipolar);
+ }
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ /* 0 Kelvin -> raw sample */
+ temp = -ABSOLUTE_ZERO_MILLICELSIUS;
+ temp *= AD7173_TEMP_SENSIIVITY_uV_per_C;
+ temp <<= chan->scan_type.realbits;
+ temp = DIV_U64_ROUND_CLOSEST(temp,
+ AD7173_VOLTAGE_INT_REF_uV *
+ MILLI);
+ *val = -temp;
+ } else {
+ *val = -BIT(chan->scan_type.realbits - 1);
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ reg = st->channels[chan->address].cfg.odr;
+
+ *val = st->info->sinc5_data_rates[reg] / MILLI;
+ *val2 = (st->info->sinc5_data_rates[reg] % MILLI) * (MICRO / MILLI);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7173_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct ad7173_channel_config *cfg;
+ unsigned int freq, i, reg;
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ freq = val * MILLI + val2 / MILLI;
+ for (i = st->info->odr_start_value; i < st->info->num_sinc5_data_rates - 1; i++)
+ if (freq >= st->info->sinc5_data_rates[i])
+ break;
+
+ cfg = &st->channels[chan->address].cfg;
+ cfg->odr = i;
+
+ if (!cfg->live)
+ break;
+
+ ret = ad_sd_read_reg(&st->sd, AD7173_REG_FILTER(cfg->cfg_slot), 2, &reg);
+ if (ret)
+ break;
+ reg &= ~AD7173_FILTER_ODR0_MASK;
+ reg |= FIELD_PREP(AD7173_FILTER_ODR0_MASK, i);
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(cfg->cfg_slot), 2, reg);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+}
+
+static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ int i, ret;
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ if (test_bit(i, scan_mask))
+ ret = ad7173_set_channel(&st->sd, i);
+ else
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ u8 reg_size;
+
+ if (reg == AD7173_REG_COMMS)
+ reg_size = 1;
+ else if (reg == AD7173_REG_CRC || reg == AD7173_REG_DATA ||
+ reg >= AD7173_REG_OFFSET(0))
+ reg_size = 3;
+ else
+ reg_size = 2;
+
+ if (readval)
+ return ad_sd_read_reg(&st->sd, reg, reg_size, readval);
+
+ return ad_sd_write_reg(&st->sd, reg, reg_size, writeval);
+}
+
+static const struct iio_info ad7173_info = {
+ .read_raw = &ad7173_read_raw,
+ .write_raw = &ad7173_write_raw,
+ .debugfs_reg_access = &ad7173_debug_reg_access,
+ .validate_trigger = ad_sd_validate_trigger,
+ .update_scan_mode = ad7173_update_scan_mode,
+};
+
+static const struct iio_chan_spec ad7173_channel_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+};
+
+static const struct iio_chan_spec ad7173_temp_iio_channel_template = {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = AD7173_AIN_TEMP_POS,
+ .channel2 = AD7173_AIN_TEMP_NEG,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+};
+
+static void ad7173_disable_regulators(void *data)
+{
+ struct ad7173_state *st = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators);
+}
+
+static void ad7173_clk_disable_unprepare(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+static unsigned long ad7173_sel_clk(struct ad7173_state *st,
+ unsigned int clk_sel)
+{
+ int ret;
+
+ st->adc_mode &= ~AD7173_ADC_MODE_CLOCKSEL_MASK;
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, clk_sel);
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 0x2, st->adc_mode);
+
+ return ret;
+}
+
+static unsigned long ad7173_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+
+ return st->info->clock / HZ_PER_KHZ;
+}
+
+static int ad7173_clk_output_is_enabled(struct clk_hw *hw)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+ u32 clk_sel;
+
+ clk_sel = FIELD_GET(AD7173_ADC_MODE_CLOCKSEL_MASK, st->adc_mode);
+ return clk_sel == AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT;
+}
+
+static int ad7173_clk_output_prepare(struct clk_hw *hw)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+
+ return ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT);
+}
+
+static void ad7173_clk_output_unprepare(struct clk_hw *hw)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+
+ ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT);
+}
+
+static const struct clk_ops ad7173_int_clk_ops = {
+ .recalc_rate = ad7173_clk_recalc_rate,
+ .is_enabled = ad7173_clk_output_is_enabled,
+ .prepare = ad7173_clk_output_prepare,
+ .unprepare = ad7173_clk_output_unprepare,
+};
+
+static int ad7173_register_clk_provider(struct iio_dev *indio_dev)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct clk_init_data init = {};
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_COMMON_CLK))
+ return 0;
+
+ init.name = fwnode_get_name(fwnode);
+ init.ops = &ad7173_int_clk_ops;
+
+ st->int_clk_hw.init = &init;
+ ret = devm_clk_hw_register(dev, &st->int_clk_hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &st->int_clk_hw);
+}
+
+static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
+{
+ struct ad7173_channel *chans_st_arr, *chan_st_priv;
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_chan_spec *chan_arr, *chan;
+ unsigned int ain[2], chan_index = 0;
+ int ref_sel, ret;
+
+ chan_arr = devm_kcalloc(dev, sizeof(*indio_dev->channels),
+ st->num_channels, GFP_KERNEL);
+ if (!chan_arr)
+ return -ENOMEM;
+
+ chans_st_arr = devm_kcalloc(dev, st->num_channels, sizeof(*st->channels),
+ GFP_KERNEL);
+ if (!chans_st_arr)
+ return -ENOMEM;
+
+ indio_dev->channels = chan_arr;
+ st->channels = chans_st_arr;
+
+ if (st->info->has_temp) {
+ chan_arr[chan_index] = ad7173_temp_iio_channel_template;
+ chan_st_priv = &chans_st_arr[chan_index];
+ chan_st_priv->ain =
+ AD7173_CH_ADDRESS(chan_arr[chan_index].channel,
+ chan_arr[chan_index].channel2);
+ chan_st_priv->cfg.bipolar = false;
+ chan_st_priv->cfg.input_buf = true;
+ chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
+ st->adc_mode |= AD7173_ADC_MODE_REF_EN;
+
+ chan_index++;
+ }
+
+ device_for_each_child_node_scoped(dev, child) {
+ chan = &chan_arr[chan_index];
+ chan_st_priv = &chans_st_arr[chan_index];
+ ret = fwnode_property_read_u32_array(child, "diff-channels",
+ ain, ARRAY_SIZE(ain));
+ if (ret)
+ return ret;
+
+ if (ain[0] >= st->info->num_inputs ||
+ ain[1] >= st->info->num_inputs)
+ return dev_err_probe(dev, -EINVAL,
+ "Input pin number out of range for pair (%d %d).\n",
+ ain[0], ain[1]);
+
+ ret = fwnode_property_match_property_string(child,
+ "adi,reference-select",
+ ad7173_ref_sel_str,
+ ARRAY_SIZE(ad7173_ref_sel_str));
+ if (ret < 0)
+ ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
+ else
+ ref_sel = ret;
+
+ if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF &&
+ !st->info->has_int_ref)
+ return dev_err_probe(dev, -EINVAL,
+ "Internal reference is not available on current model.\n");
+
+ if (ref_sel == AD7173_SETUP_REF_SEL_EXT_REF2 && !st->info->has_ref2)
+ return dev_err_probe(dev, -EINVAL,
+ "External reference 2 is not available on current model.\n");
+
+ ret = ad7173_get_ref_voltage_milli(st, ref_sel);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Cannot use reference %u\n", ref_sel);
+
+ if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF)
+ st->adc_mode |= AD7173_ADC_MODE_REF_EN;
+ chan_st_priv->cfg.ref_sel = ref_sel;
+
+ *chan = ad7173_channel_template;
+ chan->address = chan_index;
+ chan->scan_index = chan_index;
+ chan->channel = ain[0];
+ chan->channel2 = ain[1];
+ chan->differential = true;
+
+ chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]);
+ chan_st_priv->chan_reg = chan_index;
+ chan_st_priv->cfg.input_buf = true;
+ chan_st_priv->cfg.odr = 0;
+
+ chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");
+ if (chan_st_priv->cfg.bipolar)
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET);
+
+ chan_index++;
+ }
+ return 0;
+}
+
+static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ unsigned int num_channels;
+ int ret;
+
+ st->regulators[0].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF];
+ st->regulators[1].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF2];
+ st->regulators[2].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_AVDD1_AVSS];
+
+ /*
+ * If a regulator is not available, it will be set to a dummy regulator.
+ * Each channel reference is checked with regulator_get_voltage() before
+ * setting attributes so if any channel uses a dummy supply the driver
+ * probe will fail.
+ */
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators),
+ st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ ret = devm_add_action_or_reset(dev, ad7173_disable_regulators, st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to add regulators disable action\n");
+
+ ret = device_property_match_property_string(dev, "clock-names",
+ ad7173_clk_sel,
+ ARRAY_SIZE(ad7173_clk_sel));
+ if (ret < 0) {
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK,
+ AD7173_ADC_MODE_CLOCKSEL_INT);
+ ad7173_register_clk_provider(indio_dev);
+ } else {
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK,
+ AD7173_ADC_MODE_CLOCKSEL_EXT + ret);
+ st->ext_clk = devm_clk_get(dev, ad7173_clk_sel[ret]);
+ if (IS_ERR(st->ext_clk))
+ return dev_err_probe(dev, PTR_ERR(st->ext_clk),
+ "Failed to get external clock\n");
+
+ ret = clk_prepare_enable(st->ext_clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable external clock\n");
+
+ ret = devm_add_action_or_reset(dev, ad7173_clk_disable_unprepare,
+ st->ext_clk);
+ if (ret)
+ return ret;
+ }
+
+ ret = fwnode_irq_get_byname(dev_fwnode(dev), "rdy");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Interrupt 'rdy' is required\n");
+
+ ad7173_sigma_delta_info.irq_line = ret;
+
+ num_channels = device_get_child_node_count(dev);
+
+ if (st->info->has_temp)
+ num_channels++;
+
+ if (num_channels == 0)
+ return dev_err_probe(dev, -ENODATA, "No channels specified\n");
+ indio_dev->num_channels = num_channels;
+ st->num_channels = num_channels;
+
+ return ad7173_fw_parse_channel_config(indio_dev);
+}
+
+static int ad7173_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ad7173_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -ENODEV;
+
+ ida_init(&st->cfg_slots_status);
+ ret = devm_add_action_or_reset(dev, ad7173_ida_destroy, st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7173_info;
+
+ spi->mode = SPI_MODE_3;
+ spi_setup(spi);
+
+ ad7173_sigma_delta_info.num_slots = st->info->num_configs;
+ ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7173_sigma_delta_info);
+ if (ret)
+ return ret;
+
+ ret = ad7173_fw_parse_device_config(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7173_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB))
+ return ad7173_gpio_init(st);
+
+ return 0;
+}
+
+static const struct of_device_id ad7173_of_match[] = {
+ { .compatible = "adi,ad7172-2",
+ .data = &ad7173_device_info[ID_AD7172_2]},
+ { .compatible = "adi,ad7172-4",
+ .data = &ad7173_device_info[ID_AD7172_4]},
+ { .compatible = "adi,ad7173-8",
+ .data = &ad7173_device_info[ID_AD7173_8]},
+ { .compatible = "adi,ad7175-2",
+ .data = &ad7173_device_info[ID_AD7175_2]},
+ { .compatible = "adi,ad7175-8",
+ .data = &ad7173_device_info[ID_AD7175_8]},
+ { .compatible = "adi,ad7176-2",
+ .data = &ad7173_device_info[ID_AD7176_2]},
+ { .compatible = "adi,ad7177-2",
+ .data = &ad7173_device_info[ID_AD7177_2]},
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7173_of_match);
+
+static const struct spi_device_id ad7173_id_table[] = {
+ { "ad7172-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_2]},
+ { "ad7172-4", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_4]},
+ { "ad7173-8", (kernel_ulong_t)&ad7173_device_info[ID_AD7173_8]},
+ { "ad7175-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7175_2]},
+ { "ad7175-8", (kernel_ulong_t)&ad7173_device_info[ID_AD7175_8]},
+ { "ad7176-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7176_2]},
+ { "ad7177-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7177_2]},
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7173_id_table);
+
+static struct spi_driver ad7173_driver = {
+ .driver = {
+ .name = "ad7173",
+ .of_match_table = ad7173_of_match,
+ },
+ .probe = ad7173_probe,
+ .id_table = ad7173_id_table,
+};
+module_spi_driver(ad7173_driver);
+
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafo.de>");
+MODULE_AUTHOR("Dumitru Ceclan <dumitru.ceclan@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7172/AD7173/AD7175/AD7176 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
index adc3cbe92d6e..7bcc7e2aa2a2 100644
--- a/drivers/iio/adc/ad7192.c
+++ b/drivers/iio/adc/ad7192.c
@@ -17,7 +17,9 @@
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -364,19 +366,19 @@ static inline bool ad7192_valid_external_frequency(u32 freq)
freq <= AD7192_EXT_FREQ_MHZ_MAX);
}
-static int ad7192_of_clock_select(struct ad7192_state *st)
+static int ad7192_clock_select(struct ad7192_state *st)
{
- struct device_node *np = st->sd.spi->dev.of_node;
+ struct device *dev = &st->sd.spi->dev;
unsigned int clock_sel;
clock_sel = AD7192_CLK_INT;
/* use internal clock */
if (!st->mclk) {
- if (of_property_read_bool(np, "adi,int-clock-output-enable"))
+ if (device_property_read_bool(dev, "adi,int-clock-output-enable"))
clock_sel = AD7192_CLK_INT_CO;
} else {
- if (of_property_read_bool(np, "adi,clock-xtal"))
+ if (device_property_read_bool(dev, "adi,clock-xtal"))
clock_sel = AD7192_CLK_EXT_MCLK1_2;
else
clock_sel = AD7192_CLK_EXT_MCLK2;
@@ -385,7 +387,7 @@ static int ad7192_of_clock_select(struct ad7192_state *st)
return clock_sel;
}
-static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
+static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev)
{
struct ad7192_state *st = iio_priv(indio_dev);
bool rej60_en, refin2_en;
@@ -407,7 +409,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
id = FIELD_GET(AD7192_ID_MASK, id);
if (id != st->chip_info->chip_id)
- dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n",
+ dev_warn(dev, "device ID query failed (0x%X != 0x%X)\n",
id, st->chip_info->chip_id);
st->mode = FIELD_PREP(AD7192_MODE_SEL_MASK, AD7192_MODE_IDLE) |
@@ -416,30 +418,30 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
st->conf = FIELD_PREP(AD7192_CONF_GAIN_MASK, 0);
- rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable");
+ rej60_en = device_property_read_bool(dev, "adi,rejection-60-Hz-enable");
if (rej60_en)
st->mode |= AD7192_MODE_REJ60;
- refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable");
+ refin2_en = device_property_read_bool(dev, "adi,refin2-pins-enable");
if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195)
st->conf |= AD7192_CONF_REFSEL;
st->conf &= ~AD7192_CONF_CHOP;
- buf_en = of_property_read_bool(np, "adi,buffer-enable");
+ buf_en = device_property_read_bool(dev, "adi,buffer-enable");
if (buf_en)
st->conf |= AD7192_CONF_BUF;
- bipolar = of_property_read_bool(np, "bipolar");
+ bipolar = device_property_read_bool(dev, "bipolar");
if (!bipolar)
st->conf |= AD7192_CONF_UNIPOLAR;
- burnout_curr_en = of_property_read_bool(np,
- "adi,burnout-currents-enable");
+ burnout_curr_en = device_property_read_bool(dev,
+ "adi,burnout-currents-enable");
if (burnout_curr_en && buf_en) {
st->conf |= AD7192_CONF_BURN;
} else if (burnout_curr_en) {
- dev_warn(&st->sd.spi->dev,
+ dev_warn(dev,
"Can't enable burnout currents: see CHOP or buffer\n");
}
@@ -1117,9 +1119,7 @@ static int ad7192_probe(struct spi_device *spi)
}
st->int_vref_mv = ret / 1000;
- st->chip_info = of_device_get_match_data(&spi->dev);
- if (!st->chip_info)
- st->chip_info = (void *)spi_get_device_id(spi)->driver_data;
+ st->chip_info = spi_get_device_match_data(spi);
indio_dev->name = st->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channels;
@@ -1140,7 +1140,7 @@ static int ad7192_probe(struct spi_device *spi)
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
- st->clock_sel = ad7192_of_clock_select(st);
+ st->clock_sel = ad7192_clock_select(st);
if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
st->clock_sel == AD7192_CLK_EXT_MCLK2) {
@@ -1152,7 +1152,7 @@ static int ad7192_probe(struct spi_device *spi)
}
}
- ret = ad7192_setup(indio_dev, spi->dev.of_node);
+ ret = ad7192_setup(indio_dev, &spi->dev);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 468c2656d2be..353a97f9c086 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -371,7 +371,6 @@ static void ad7266_init_channels(struct iio_dev *indio_dev)
indio_dev->channels = chan_info->channels;
indio_dev->num_channels = chan_info->num_channels;
indio_dev->available_scan_masks = chan_info->scan_masks;
- indio_dev->masklength = chan_info->num_channels - 1;
}
static const char * const ad7266_gpio_labels[] = {
diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c
index cccacec5db6d..6aadd14f459d 100644
--- a/drivers/iio/adc/ad7292.c
+++ b/drivers/iio/adc/ad7292.c
@@ -8,7 +8,8 @@
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -260,7 +261,6 @@ static int ad7292_probe(struct spi_device *spi)
{
struct ad7292_state *st;
struct iio_dev *indio_dev;
- struct device_node *child;
bool diff_channels = false;
int ret;
@@ -305,12 +305,11 @@ static int ad7292_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ad7292_info;
- for_each_available_child_of_node(spi->dev.of_node, child) {
- diff_channels = of_property_read_bool(child, "diff-channels");
- if (diff_channels) {
- of_node_put(child);
+ device_for_each_child_node_scoped(&spi->dev, child) {
+ diff_channels = fwnode_property_read_bool(child,
+ "diff-channels");
+ if (diff_channels)
break;
- }
}
if (diff_channels) {
diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c
new file mode 100644
index 000000000000..4602ab5ed2a6
--- /dev/null
+++ b/drivers/iio/adc/ad7944.c
@@ -0,0 +1,690 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD7944/85/86 PulSAR ADC family driver.
+ *
+ * Copyright 2024 Analog Devices, Inc.
+ * Copyright 2024 BayLibre, SAS
+ */
+
+#include <linux/align.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/string_helpers.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define AD7944_INTERNAL_REF_MV 4096
+
+struct ad7944_timing_spec {
+ /* Normal mode max conversion time (t_{CONV}). */
+ unsigned int conv_ns;
+ /* TURBO mode max conversion time (t_{CONV}). */
+ unsigned int turbo_conv_ns;
+};
+
+enum ad7944_spi_mode {
+ /* datasheet calls this "4-wire mode" */
+ AD7944_SPI_MODE_DEFAULT,
+ /* datasheet calls this "3-wire mode" (not related to SPI_3WIRE!) */
+ AD7944_SPI_MODE_SINGLE,
+ /* datasheet calls this "chain mode" */
+ AD7944_SPI_MODE_CHAIN,
+};
+
+/* maps adi,spi-mode property value to enum */
+static const char * const ad7944_spi_modes[] = {
+ [AD7944_SPI_MODE_DEFAULT] = "",
+ [AD7944_SPI_MODE_SINGLE] = "single",
+ [AD7944_SPI_MODE_CHAIN] = "chain",
+};
+
+struct ad7944_adc {
+ struct spi_device *spi;
+ enum ad7944_spi_mode spi_mode;
+ struct spi_transfer xfers[3];
+ struct spi_message msg;
+ void *chain_mode_buf;
+ /* Chip-specific timing specifications. */
+ const struct ad7944_timing_spec *timing_spec;
+ /* GPIO connected to CNV pin. */
+ struct gpio_desc *cnv;
+ /* Optional GPIO to enable turbo mode. */
+ struct gpio_desc *turbo;
+ /* Indicates TURBO is hard-wired to be always enabled. */
+ bool always_turbo;
+ /* Reference voltage (millivolts). */
+ unsigned int ref_mv;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ struct {
+ union {
+ u16 u16;
+ u32 u32;
+ } raw;
+ u64 timestamp __aligned(8);
+ } sample __aligned(IIO_DMA_MINALIGN);
+};
+
+/* quite time before CNV rising edge */
+#define T_QUIET_NS 20
+
+static const struct ad7944_timing_spec ad7944_timing_spec = {
+ .conv_ns = 420,
+ .turbo_conv_ns = 320,
+};
+
+static const struct ad7944_timing_spec ad7986_timing_spec = {
+ .conv_ns = 500,
+ .turbo_conv_ns = 400,
+};
+
+struct ad7944_chip_info {
+ const char *name;
+ const struct ad7944_timing_spec *timing_spec;
+ const struct iio_chan_spec channels[2];
+};
+
+/*
+ * AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip
+ * @_name: The name of the chip
+ * @_ts: The timing specification for the chip
+ * @_bits: The number of bits in the conversion result
+ * @_diff: Whether the chip is true differential or not
+ */
+#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _bits, _diff) \
+static const struct ad7944_chip_info _name##_chip_info = { \
+ .name = #_name, \
+ .timing_spec = &_ts##_timing_spec, \
+ .channels = { \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = _diff, \
+ .channel = 0, \
+ .channel2 = _diff ? 1 : 0, \
+ .scan_index = 0, \
+ .scan_type.sign = _diff ? 's' : 'u', \
+ .scan_type.realbits = _bits, \
+ .scan_type.storagebits = _bits > 16 ? 32 : 16, \
+ .scan_type.endianness = IIO_CPU, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE), \
+ }, \
+ IIO_CHAN_SOFT_TIMESTAMP(1), \
+ }, \
+}
+
+/* pseudo-differential with ground sense */
+AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 14, 0);
+AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0);
+/* fully differential */
+AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
+
+static void ad7944_unoptimize_msg(void *msg)
+{
+ spi_unoptimize_message(msg);
+}
+
+static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
+ : adc->timing_spec->conv_ns;
+ struct spi_transfer *xfers = adc->xfers;
+ int ret;
+
+ /*
+ * NB: can get better performance from some SPI controllers if we use
+ * the same bits_per_word in every transfer.
+ */
+ xfers[0].bits_per_word = chan->scan_type.realbits;
+ /*
+ * CS is tied to CNV and we need a low to high transition to start the
+ * conversion, so place CNV low for t_QUIET to prepare for this.
+ */
+ xfers[0].delay.value = T_QUIET_NS;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ /*
+ * CS has to be high for full conversion time to avoid triggering the
+ * busy indication.
+ */
+ xfers[1].cs_off = 1;
+ xfers[1].delay.value = t_conv_ns;
+ xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfers[1].bits_per_word = chan->scan_type.realbits;
+
+ /* Then we can read the data during the acquisition phase */
+ xfers[2].rx_buf = &adc->sample.raw;
+ xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+ xfers[2].bits_per_word = chan->scan_type.realbits;
+
+ spi_message_init_with_transfers(&adc->msg, xfers, 3);
+
+ ret = spi_optimize_message(adc->spi, &adc->msg);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
+}
+
+static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
+ : adc->timing_spec->conv_ns;
+ struct spi_transfer *xfers = adc->xfers;
+ int ret;
+
+ /*
+ * NB: can get better performance from some SPI controllers if we use
+ * the same bits_per_word in every transfer.
+ */
+ xfers[0].bits_per_word = chan->scan_type.realbits;
+ /*
+ * CS has to be high for full conversion time to avoid triggering the
+ * busy indication.
+ */
+ xfers[0].cs_off = 1;
+ xfers[0].delay.value = t_conv_ns;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].rx_buf = &adc->sample.raw;
+ xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+ xfers[1].bits_per_word = chan->scan_type.realbits;
+
+ spi_message_init_with_transfers(&adc->msg, xfers, 2);
+
+ ret = spi_optimize_message(adc->spi, &adc->msg);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
+}
+
+static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan,
+ u32 n_chain_dev)
+{
+ struct spi_transfer *xfers = adc->xfers;
+ int ret;
+
+ /*
+ * NB: SCLK has to be low before we toggle CS to avoid triggering the
+ * busy indication.
+ */
+ if (adc->spi->mode & SPI_CPOL)
+ return dev_err_probe(dev, -EINVAL,
+ "chain mode requires ~SPI_CPOL\n");
+
+ /*
+ * We only support CNV connected to CS in chain mode and we need CNV
+ * to be high during the transfer to trigger the conversion.
+ */
+ if (!(adc->spi->mode & SPI_CS_HIGH))
+ return dev_err_probe(dev, -EINVAL,
+ "chain mode requires SPI_CS_HIGH\n");
+
+ /* CNV has to be high for full conversion time before reading data. */
+ xfers[0].delay.value = adc->timing_spec->conv_ns;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].rx_buf = adc->chain_mode_buf;
+ xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits) * n_chain_dev;
+ xfers[1].bits_per_word = chan->scan_type.realbits;
+
+ spi_message_init_with_transfers(&adc->msg, xfers, 2);
+
+ ret = spi_optimize_message(adc->spi, &adc->msg);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
+}
+
+/**
+ * ad7944_convert_and_acquire - Perform a single conversion and acquisition
+ * @adc: The ADC device structure
+ * @chan: The channel specification
+ * Return: 0 on success, a negative error code on failure
+ *
+ * Perform a conversion and acquisition of a single sample using the
+ * pre-optimized adc->msg.
+ *
+ * Upon successful return adc->sample.raw will contain the conversion result
+ * (or adc->chain_mode_buf if the device is using chain mode).
+ */
+static int ad7944_convert_and_acquire(struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan)
+{
+ int ret;
+
+ /*
+ * In 4-wire mode, the CNV line is held high for the entire conversion
+ * and acquisition process. In other modes adc->cnv is NULL and is
+ * ignored (CS is wired to CNV in those cases).
+ */
+ gpiod_set_value_cansleep(adc->cnv, 1);
+ ret = spi_sync(adc->spi, &adc->msg);
+ gpiod_set_value_cansleep(adc->cnv, 0);
+
+ return ret;
+}
+
+static int ad7944_single_conversion(struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ int ret;
+
+ ret = ad7944_convert_and_acquire(adc, chan);
+ if (ret)
+ return ret;
+
+ if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
+ if (chan->scan_type.storagebits > 16)
+ *val = ((u32 *)adc->chain_mode_buf)[chan->scan_index];
+ else
+ *val = ((u16 *)adc->chain_mode_buf)[chan->scan_index];
+ } else {
+ if (chan->scan_type.storagebits > 16)
+ *val = adc->sample.raw.u32;
+ else
+ *val = adc->sample.raw.u16;
+ }
+
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(*val, chan->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+}
+
+static int ad7944_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7944_single_conversion(adc, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = adc->ref_mv;
+
+ if (chan->scan_type.sign == 's')
+ *val2 = chan->scan_type.realbits - 1;
+ else
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad7944_iio_info = {
+ .read_raw = &ad7944_read_raw,
+};
+
+static irqreturn_t ad7944_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad7944_convert_and_acquire(adc, &indio_dev->channels[0]);
+ if (ret)
+ goto out;
+
+ if (adc->spi_mode == AD7944_SPI_MODE_CHAIN)
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->chain_mode_buf,
+ pf->timestamp);
+ else
+ iio_push_to_buffers_with_timestamp(indio_dev, &adc->sample.raw,
+ pf->timestamp);
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ad7944_chain_mode_alloc - allocate and initialize channel specs and buffers
+ * for daisy-chained devices
+ * @dev: The device for devm_ functions
+ * @chan_template: The channel template for the devices (array of 2 channels
+ * voltage and timestamp)
+ * @n_chain_dev: The number of devices in the chain
+ * @chain_chan: Pointer to receive the allocated channel specs
+ * @chain_mode_buf: Pointer to receive the allocated rx buffer
+ * @chain_scan_masks: Pointer to receive the allocated scan masks
+ * Return: 0 on success, a negative error code on failure
+ */
+static int ad7944_chain_mode_alloc(struct device *dev,
+ const struct iio_chan_spec *chan_template,
+ u32 n_chain_dev,
+ struct iio_chan_spec **chain_chan,
+ void **chain_mode_buf,
+ unsigned long **chain_scan_masks)
+{
+ struct iio_chan_spec *chan;
+ size_t chain_mode_buf_size;
+ unsigned long *scan_masks;
+ void *buf;
+ int i;
+
+ /* 1 channel for each device in chain plus 1 for soft timestamp */
+
+ chan = devm_kcalloc(dev, n_chain_dev + 1, sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ for (i = 0; i < n_chain_dev; i++) {
+ chan[i] = chan_template[0];
+
+ if (chan_template[0].differential) {
+ chan[i].channel = 2 * i;
+ chan[i].channel2 = 2 * i + 1;
+ } else {
+ chan[i].channel = i;
+ }
+
+ chan[i].scan_index = i;
+ }
+
+ /* soft timestamp */
+ chan[i] = chan_template[1];
+ chan[i].scan_index = i;
+
+ *chain_chan = chan;
+
+ /* 1 word for each voltage channel + aligned u64 for timestamp */
+
+ chain_mode_buf_size = ALIGN(n_chain_dev *
+ BITS_TO_BYTES(chan[0].scan_type.storagebits), sizeof(u64))
+ + sizeof(u64);
+ buf = devm_kzalloc(dev, chain_mode_buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ *chain_mode_buf = buf;
+
+ /*
+ * Have to limit n_chain_dev due to current implementation of
+ * available_scan_masks.
+ */
+ if (n_chain_dev > BITS_PER_LONG)
+ return dev_err_probe(dev, -EINVAL,
+ "chain is limited to 32 devices\n");
+
+ scan_masks = devm_kcalloc(dev, 2, sizeof(*scan_masks), GFP_KERNEL);
+ if (!scan_masks)
+ return -ENOMEM;
+
+ /*
+ * Scan mask is needed since we always have to read all devices in the
+ * chain in one SPI transfer.
+ */
+ scan_masks[0] = GENMASK(n_chain_dev - 1, 0);
+
+ *chain_scan_masks = scan_masks;
+
+ return 0;
+}
+
+static const char * const ad7944_power_supplies[] = {
+ "avdd", "dvdd", "bvdd", "vio"
+};
+
+static void ad7944_ref_disable(void *ref)
+{
+ regulator_disable(ref);
+}
+
+static int ad7944_probe(struct spi_device *spi)
+{
+ const struct ad7944_chip_info *chip_info;
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad7944_adc *adc;
+ bool have_refin = false;
+ struct regulator *ref;
+ struct iio_chan_spec *chain_chan;
+ unsigned long *chain_scan_masks;
+ u32 n_chain_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ chip_info = spi_get_device_match_data(spi);
+ if (!chip_info)
+ return dev_err_probe(dev, -EINVAL, "no chip info\n");
+
+ adc->timing_spec = chip_info->timing_spec;
+
+ ret = device_property_match_property_string(dev, "adi,spi-mode",
+ ad7944_spi_modes,
+ ARRAY_SIZE(ad7944_spi_modes));
+ /* absence of adi,spi-mode property means default mode */
+ if (ret == -EINVAL)
+ adc->spi_mode = AD7944_SPI_MODE_DEFAULT;
+ else if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "getting adi,spi-mode property failed\n");
+ else
+ adc->spi_mode = ret;
+
+ /*
+ * Some chips use unusual word sizes, so check now instead of waiting
+ * for the first xfer.
+ */
+ if (!spi_is_bpw_supported(spi, chip_info->channels[0].scan_type.realbits))
+ return dev_err_probe(dev, -EINVAL,
+ "SPI host does not support %d bits per word\n",
+ chip_info->channels[0].scan_type.realbits);
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(ad7944_power_supplies),
+ ad7944_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable supplies\n");
+
+ /*
+ * Sort out what is being used for the reference voltage. Options are:
+ * - internal reference: neither REF or REFIN is connected
+ * - internal reference with external buffer: REF not connected, REFIN
+ * is connected
+ * - external reference: REF is connected, REFIN is not connected
+ */
+
+ ref = devm_regulator_get_optional(dev, "ref");
+ if (IS_ERR(ref)) {
+ if (PTR_ERR(ref) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(ref),
+ "failed to get REF supply\n");
+
+ ref = NULL;
+ }
+
+ ret = devm_regulator_get_enable_optional(dev, "refin");
+ if (ret == 0)
+ have_refin = true;
+ else if (ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable REFIN supply\n");
+
+ if (have_refin && ref)
+ return dev_err_probe(dev, -EINVAL,
+ "cannot have both refin and ref supplies\n");
+
+ if (ref) {
+ ret = regulator_enable(ref);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to enable REF supply\n");
+
+ ret = devm_add_action_or_reset(dev, ad7944_ref_disable, ref);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(ref);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to get REF voltage\n");
+
+ /* external reference */
+ adc->ref_mv = ret / 1000;
+ } else {
+ /* internal reference */
+ adc->ref_mv = AD7944_INTERNAL_REF_MV;
+ }
+
+ adc->cnv = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(adc->cnv))
+ return dev_err_probe(dev, PTR_ERR(adc->cnv),
+ "failed to get CNV GPIO\n");
+
+ if (!adc->cnv && adc->spi_mode == AD7944_SPI_MODE_DEFAULT)
+ return dev_err_probe(&spi->dev, -EINVAL, "CNV GPIO is required\n");
+ if (adc->cnv && adc->spi_mode != AD7944_SPI_MODE_DEFAULT)
+ return dev_err_probe(&spi->dev, -EINVAL,
+ "CNV GPIO in single and chain mode is not currently supported\n");
+
+ adc->turbo = devm_gpiod_get_optional(dev, "turbo", GPIOD_OUT_LOW);
+ if (IS_ERR(adc->turbo))
+ return dev_err_probe(dev, PTR_ERR(adc->turbo),
+ "failed to get TURBO GPIO\n");
+
+ adc->always_turbo = device_property_present(dev, "adi,always-turbo");
+
+ if (adc->turbo && adc->always_turbo)
+ return dev_err_probe(dev, -EINVAL,
+ "cannot have both turbo-gpios and adi,always-turbo\n");
+
+ if (adc->spi_mode == AD7944_SPI_MODE_CHAIN && adc->always_turbo)
+ return dev_err_probe(dev, -EINVAL,
+ "cannot have both chain mode and always turbo\n");
+
+ switch (adc->spi_mode) {
+ case AD7944_SPI_MODE_DEFAULT:
+ ret = ad7944_4wire_mode_init_msg(dev, adc, &chip_info->channels[0]);
+ if (ret)
+ return ret;
+
+ break;
+ case AD7944_SPI_MODE_SINGLE:
+ ret = ad7944_3wire_cs_mode_init_msg(dev, adc, &chip_info->channels[0]);
+ if (ret)
+ return ret;
+
+ break;
+ case AD7944_SPI_MODE_CHAIN:
+ ret = device_property_read_u32(dev, "#daisy-chained-devices",
+ &n_chain_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get #daisy-chained-devices\n");
+
+ ret = ad7944_chain_mode_alloc(dev, chip_info->channels,
+ n_chain_dev, &chain_chan,
+ &adc->chain_mode_buf,
+ &chain_scan_masks);
+ if (ret)
+ return ret;
+
+ ret = ad7944_chain_mode_init_msg(dev, adc, &chain_chan[0],
+ n_chain_dev);
+ if (ret)
+ return ret;
+
+ break;
+ }
+
+ indio_dev->name = chip_info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7944_iio_info;
+
+ if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
+ indio_dev->available_scan_masks = chain_scan_masks;
+ indio_dev->channels = chain_chan;
+ indio_dev->num_channels = n_chain_dev + 1;
+ } else {
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = ARRAY_SIZE(chip_info->channels);
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad7944_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ad7944_of_match[] = {
+ { .compatible = "adi,ad7944", .data = &ad7944_chip_info },
+ { .compatible = "adi,ad7985", .data = &ad7985_chip_info },
+ { .compatible = "adi,ad7986", .data = &ad7986_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7944_of_match);
+
+static const struct spi_device_id ad7944_spi_id[] = {
+ { "ad7944", (kernel_ulong_t)&ad7944_chip_info },
+ { "ad7985", (kernel_ulong_t)&ad7985_chip_info },
+ { "ad7986", (kernel_ulong_t)&ad7986_chip_info },
+ { }
+
+};
+MODULE_DEVICE_TABLE(spi, ad7944_spi_id);
+
+static struct spi_driver ad7944_driver = {
+ .driver = {
+ .name = "ad7944",
+ .of_match_table = ad7944_of_match,
+ },
+ .probe = ad7944_probe,
+ .id_table = ad7944_spi_id,
+};
+module_spi_driver(ad7944_driver);
+
+MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
+MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index b757cc45c4de..0f0dcd9ca6b6 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -128,7 +128,7 @@ struct ad799x_state {
struct regulator *vref;
/* lock to protect against multiple access to the device */
struct mutex lock;
- unsigned id;
+ unsigned int id;
u16 config;
u8 *rx_buf;
@@ -253,7 +253,7 @@ static int ad799x_update_scan_mode(struct iio_dev *indio_dev,
}
}
-static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
+static int ad799x_scan_direct(struct ad799x_state *st, unsigned int ch)
{
u8 cmd;
@@ -335,6 +335,7 @@ static ssize_t ad799x_read_frequency(struct device *dev,
struct ad799x_state *st = iio_priv(indio_dev);
int ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG);
+
if (ret < 0)
return ret;
@@ -523,7 +524,7 @@ done:
return IRQ_HANDLED;
}
-static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+static IIO_DEV_ATTR_SAMP_FREQ(0644,
ad799x_read_frequency,
ad799x_write_frequency);
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0");
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 7475ec2a56c7..e85b763b9ffc 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -4,7 +4,11 @@
*
* Copyright 2012-2020 Analog Devices Inc.
*/
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
#include <linux/cleanup.h>
+#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/device.h>
@@ -100,6 +104,8 @@
#define AD9467_DEF_OUTPUT_MODE 0x08
#define AD9467_REG_VREF_MASK 0x0F
+#define AD9647_MAX_TEST_POINTS 32
+
struct ad9467_chip_info {
const char *name;
unsigned int id;
@@ -110,6 +116,9 @@ struct ad9467_chip_info {
unsigned long max_rate;
unsigned int default_output_mode;
unsigned int vref_mask;
+ unsigned int num_lanes;
+ /* data clock output */
+ bool has_dco;
};
struct ad9467_state {
@@ -119,7 +128,16 @@ struct ad9467_state {
struct clk *clk;
unsigned int output_mode;
unsigned int (*scales)[2];
-
+ /*
+ * Times 2 because we may also invert the signal polarity and run the
+ * calibration again. For some reference on the test points (ad9265) see:
+ * https://www.analog.com/media/en/technical-documentation/data-sheets/ad9265.pdf
+ * at page 38 for the dco output delay. On devices as ad9467, the
+ * calibration is done at the backend level. For the ADI axi-adc:
+ * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
+ * at the io delay control section.
+ */
+ DECLARE_BITMAP(calib_map, AD9647_MAX_TEST_POINTS * 2);
struct gpio_desc *pwrdown_gpio;
/* ensure consistent state obtained on multiple related accesses */
struct mutex lock;
@@ -242,6 +260,7 @@ static const struct ad9467_chip_info ad9467_chip_tbl = {
.num_channels = ARRAY_SIZE(ad9467_channels),
.default_output_mode = AD9467_DEF_OUTPUT_MODE,
.vref_mask = AD9467_REG_VREF_MASK,
+ .num_lanes = 8,
};
static const struct ad9467_chip_info ad9434_chip_tbl = {
@@ -254,6 +273,7 @@ static const struct ad9467_chip_info ad9434_chip_tbl = {
.num_channels = ARRAY_SIZE(ad9434_channels),
.default_output_mode = AD9434_DEF_OUTPUT_MODE,
.vref_mask = AD9434_REG_VREF_MASK,
+ .num_lanes = 6,
};
static const struct ad9467_chip_info ad9265_chip_tbl = {
@@ -266,6 +286,7 @@ static const struct ad9467_chip_info ad9265_chip_tbl = {
.num_channels = ARRAY_SIZE(ad9467_channels),
.default_output_mode = AD9265_DEF_OUTPUT_MODE,
.vref_mask = AD9265_REG_VREF_MASK,
+ .has_dco = true,
};
static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2)
@@ -321,6 +342,246 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
return -EINVAL;
}
+static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode)
+{
+ int ret;
+
+ ret = ad9467_spi_write(spi, AN877_ADC_REG_OUTPUT_MODE, mode);
+ if (ret < 0)
+ return ret;
+
+ return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+}
+
+static int ad9647_calibrate_prepare(const struct ad9467_state *st)
+{
+ struct iio_backend_data_fmt data = {
+ .enable = false,
+ };
+ unsigned int c;
+ int ret;
+
+ ret = ad9467_spi_write(st->spi, AN877_ADC_REG_TEST_IO,
+ AN877_ADC_TESTMODE_PN9_SEQ);
+ if (ret)
+ return ret;
+
+ ret = ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+ if (ret)
+ return ret;
+
+ ret = ad9467_outputmode_set(st->spi, st->info->default_output_mode);
+ if (ret)
+ return ret;
+
+ for (c = 0; c < st->info->num_channels; c++) {
+ ret = iio_backend_data_format_set(st->back, c, &data);
+ if (ret)
+ return ret;
+ }
+
+ ret = iio_backend_test_pattern_set(st->back, 0,
+ IIO_BACKEND_ADI_PRBS_9A);
+ if (ret)
+ return ret;
+
+ return iio_backend_chan_enable(st->back, 0);
+}
+
+static int ad9647_calibrate_polarity_set(const struct ad9467_state *st,
+ bool invert)
+{
+ enum iio_backend_sample_trigger trigger;
+
+ if (st->info->has_dco) {
+ unsigned int phase = AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN;
+
+ if (invert)
+ phase |= AN877_ADC_INVERT_DCO_CLK;
+
+ return ad9467_spi_write(st->spi, AN877_ADC_REG_OUTPUT_PHASE,
+ phase);
+ }
+
+ if (invert)
+ trigger = IIO_BACKEND_SAMPLE_TRIGGER_EDGE_FALLING;
+ else
+ trigger = IIO_BACKEND_SAMPLE_TRIGGER_EDGE_RISING;
+
+ return iio_backend_data_sample_trigger(st->back, trigger);
+}
+
+/*
+ * The idea is pretty simple. Find the max number of successful points in a row
+ * and get the one in the middle.
+ */
+static unsigned int ad9467_find_optimal_point(const unsigned long *calib_map,
+ unsigned int start,
+ unsigned int nbits,
+ unsigned int *val)
+{
+ unsigned int bit = start, end, start_cnt, cnt = 0;
+
+ for_each_clear_bitrange_from(bit, end, calib_map, nbits + start) {
+ if (end - bit > cnt) {
+ cnt = end - bit;
+ start_cnt = bit;
+ }
+ }
+
+ if (cnt)
+ *val = start_cnt + cnt / 2;
+
+ return cnt;
+}
+
+static int ad9467_calibrate_apply(const struct ad9467_state *st,
+ unsigned int val)
+{
+ unsigned int lane;
+ int ret;
+
+ if (st->info->has_dco) {
+ ret = ad9467_spi_write(st->spi, AN877_ADC_REG_OUTPUT_DELAY,
+ val);
+ if (ret)
+ return ret;
+
+ return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+ }
+
+ for (lane = 0; lane < st->info->num_lanes; lane++) {
+ ret = iio_backend_iodelay_set(st->back, lane, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad9647_calibrate_stop(const struct ad9467_state *st)
+{
+ struct iio_backend_data_fmt data = {
+ .sign_extend = true,
+ .enable = true,
+ };
+ unsigned int c, mode;
+ int ret;
+
+ ret = iio_backend_chan_disable(st->back, 0);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_test_pattern_set(st->back, 0,
+ IIO_BACKEND_NO_TEST_PATTERN);
+ if (ret)
+ return ret;
+
+ for (c = 0; c < st->info->num_channels; c++) {
+ ret = iio_backend_data_format_set(st->back, c, &data);
+ if (ret)
+ return ret;
+ }
+
+ mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+ ret = ad9467_outputmode_set(st->spi, mode);
+ if (ret)
+ return ret;
+
+ ret = ad9467_spi_write(st->spi, AN877_ADC_REG_TEST_IO,
+ AN877_ADC_TESTMODE_OFF);
+ if (ret)
+ return ret;
+
+ return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+}
+
+static int ad9467_calibrate(struct ad9467_state *st)
+{
+ unsigned int point, val, inv_val, cnt, inv_cnt = 0;
+ /*
+ * Half of the bitmap is for the inverted signal. The number of test
+ * points is the same though...
+ */
+ unsigned int test_points = AD9647_MAX_TEST_POINTS;
+ unsigned long sample_rate = clk_get_rate(st->clk);
+ struct device *dev = &st->spi->dev;
+ bool invert = false, stat;
+ int ret;
+
+ /* all points invalid */
+ bitmap_fill(st->calib_map, BITS_PER_TYPE(st->calib_map));
+
+ ret = ad9647_calibrate_prepare(st);
+ if (ret)
+ return ret;
+retune:
+ ret = ad9647_calibrate_polarity_set(st, invert);
+ if (ret)
+ return ret;
+
+ for (point = 0; point < test_points; point++) {
+ ret = ad9467_calibrate_apply(st, point);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_chan_status(st->back, 0, &stat);
+ if (ret)
+ return ret;
+
+ __assign_bit(point + invert * test_points, st->calib_map, stat);
+ }
+
+ if (!invert) {
+ cnt = ad9467_find_optimal_point(st->calib_map, 0, test_points,
+ &val);
+ /*
+ * We're happy if we find, at least, three good test points in
+ * a row.
+ */
+ if (cnt < 3) {
+ invert = true;
+ goto retune;
+ }
+ } else {
+ inv_cnt = ad9467_find_optimal_point(st->calib_map, test_points,
+ test_points, &inv_val);
+ if (!inv_cnt && !cnt)
+ return -EIO;
+ }
+
+ if (inv_cnt < cnt) {
+ ret = ad9647_calibrate_polarity_set(st, false);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * polarity inverted is the last test to run. Hence, there's no
+ * need to re-do any configuration. We just need to "normalize"
+ * the selected value.
+ */
+ val = inv_val - test_points;
+ }
+
+ if (st->info->has_dco)
+ dev_dbg(dev, "%sDCO 0x%X CLK %lu Hz\n", inv_cnt >= cnt ? "INVERT " : "",
+ val, sample_rate);
+ else
+ dev_dbg(dev, "%sIDELAY 0x%x\n", inv_cnt >= cnt ? "INVERT " : "",
+ val);
+
+ ret = ad9467_calibrate_apply(st, val);
+ if (ret)
+ return ret;
+
+ /* finally apply the optimal value */
+ return ad9647_calibrate_stop(st);
+}
+
static int ad9467_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long m)
@@ -345,7 +606,9 @@ static int ad9467_write_raw(struct iio_dev *indio_dev,
{
struct ad9467_state *st = iio_priv(indio_dev);
const struct ad9467_chip_info *info = st->info;
+ unsigned long sample_rate;
long r_clk;
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -358,7 +621,23 @@ static int ad9467_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
- return clk_set_rate(st->clk, r_clk);
+ sample_rate = clk_get_rate(st->clk);
+ /*
+ * clk_set_rate() would also do this but since we would still
+ * need it for avoiding an unnecessary calibration, do it now.
+ */
+ if (sample_rate == r_clk)
+ return 0;
+
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ ret = clk_set_rate(st->clk, r_clk);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+ ret = ad9467_calibrate(st);
+ }
+ return ret;
default:
return -EINVAL;
}
@@ -411,18 +690,6 @@ static const struct iio_info ad9467_info = {
.read_avail = ad9467_read_avail,
};
-static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode)
-{
- int ret;
-
- ret = ad9467_spi_write(spi, AN877_ADC_REG_OUTPUT_MODE, mode);
- if (ret < 0)
- return ret;
-
- return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER,
- AN877_ADC_TRANSFER_SYNC);
-}
-
static int ad9467_scale_fill(struct ad9467_state *st)
{
const struct ad9467_chip_info *info = st->info;
@@ -442,29 +709,6 @@ static int ad9467_scale_fill(struct ad9467_state *st)
return 0;
}
-static int ad9467_setup(struct ad9467_state *st)
-{
- struct iio_backend_data_fmt data = {
- .sign_extend = true,
- .enable = true,
- };
- unsigned int c, mode;
- int ret;
-
- mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
- ret = ad9467_outputmode_set(st->spi, mode);
- if (ret)
- return ret;
-
- for (c = 0; c < st->info->num_channels; c++) {
- ret = iio_backend_data_format_set(st->back, c, &data);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
static int ad9467_reset(struct device *dev)
{
struct gpio_desc *gpio;
@@ -521,6 +765,52 @@ static int ad9467_iio_backend_get(struct ad9467_state *st)
return -ENODEV;
}
+static ssize_t ad9467_dump_calib_table(struct file *file,
+ char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ad9467_state *st = file->private_data;
+ unsigned int bit, size = BITS_PER_TYPE(st->calib_map);
+ /* +2 for the newline and +1 for the string termination */
+ unsigned char map[AD9647_MAX_TEST_POINTS * 2 + 3];
+ ssize_t len = 0;
+
+ guard(mutex)(&st->lock);
+ if (*ppos)
+ goto out_read;
+
+ for (bit = 0; bit < size; bit++) {
+ if (bit == size / 2)
+ len += scnprintf(map + len, sizeof(map) - len, "\n");
+
+ len += scnprintf(map + len, sizeof(map) - len, "%c",
+ test_bit(bit, st->calib_map) ? 'x' : 'o');
+ }
+
+ len += scnprintf(map + len, sizeof(map) - len, "\n");
+out_read:
+ return simple_read_from_buffer(userbuf, count, ppos, map, len);
+}
+
+static const struct file_operations ad9467_calib_table_fops = {
+ .open = simple_open,
+ .read = ad9467_dump_calib_table,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+
+static void ad9467_debugfs_init(struct iio_dev *indio_dev)
+{
+ struct dentry *d = iio_get_debugfs_dentry(indio_dev);
+ struct ad9467_state *st = iio_priv(indio_dev);
+
+ if (!IS_ENABLED(CONFIG_DEBUG_FS))
+ return;
+
+ debugfs_create_file("calibration_table_dump", 0400, d, st,
+ &ad9467_calib_table_fops);
+}
+
static int ad9467_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@@ -580,11 +870,17 @@ static int ad9467_probe(struct spi_device *spi)
if (ret)
return ret;
- ret = ad9467_setup(st);
+ ret = ad9467_calibrate(st);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret)
return ret;
- return devm_iio_device_register(&spi->dev, indio_dev);
+ ad9467_debugfs_init(indio_dev);
+
+ return 0;
}
static const struct of_device_id ad9467_of_match[] = {
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index a602429cdde4..a2b87f6b7a07 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -206,7 +206,7 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
unsigned int mode, unsigned int channel)
{
int ret;
- unsigned long timeout;
+ unsigned long time_left;
ret = ad_sigma_delta_set_channel(sigma_delta, channel);
if (ret)
@@ -222,11 +222,11 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
goto out;
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
- timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ);
- if (timeout == 0) {
+ enable_irq(sigma_delta->irq_line);
+ time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ);
+ if (time_left == 0) {
sigma_delta->irq_dis = true;
- disable_irq_nosync(sigma_delta->spi->irq);
+ disable_irq_nosync(sigma_delta->irq_line);
ret = -EIO;
} else {
ret = 0;
@@ -295,7 +295,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
+ enable_irq(sigma_delta->irq_line);
ret = wait_for_completion_interruptible_timeout(
&sigma_delta->completion, HZ);
@@ -315,7 +315,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
out:
if (!sigma_delta->irq_dis) {
- disable_irq_nosync(sigma_delta->spi->irq);
+ disable_irq_nosync(sigma_delta->irq_line);
sigma_delta->irq_dis = true;
}
@@ -396,7 +396,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
goto err_unlock;
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
+ enable_irq(sigma_delta->irq_line);
return 0;
@@ -414,7 +414,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
wait_for_completion_timeout(&sigma_delta->completion, HZ);
if (!sigma_delta->irq_dis) {
- disable_irq_nosync(sigma_delta->spi->irq);
+ disable_irq_nosync(sigma_delta->irq_line);
sigma_delta->irq_dis = true;
}
@@ -516,7 +516,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
irq_handled:
iio_trigger_notify_done(indio_dev->trig);
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
+ enable_irq(sigma_delta->irq_line);
return IRQ_HANDLED;
}
@@ -587,13 +587,13 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de
sigma_delta->irq_dis = true;
/* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */
- irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY);
+ irq_set_status_flags(sigma_delta->irq_line, IRQ_DISABLE_UNLAZY);
/* Allow overwriting the flags from firmware */
if (!irq_flags)
irq_flags = sigma_delta->info->irq_flags;
- ret = devm_request_irq(dev, sigma_delta->spi->irq,
+ ret = devm_request_irq(dev, sigma_delta->irq_line,
ad_sd_data_rdy_trig_poll,
irq_flags | IRQF_NO_AUTOEN,
indio_dev->name,
@@ -673,6 +673,11 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
}
}
+ if (info->irq_line)
+ sigma_delta->irq_line = info->irq_line;
+ else
+ sigma_delta->irq_line = spi->irq;
+
iio_device_set_drvdata(indio_dev, sigma_delta);
return 0;
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 4156639b3c8b..0cf0d81358fd 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -7,11 +7,13 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
@@ -37,6 +39,9 @@
#define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1)
#define ADI_AXI_REG_RSTN_RSTN BIT(0)
+#define ADI_AXI_ADC_REG_CTRL 0x0044
+#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
+
/* ADC Channel controls */
#define ADI_AXI_REG_CHAN_CTRL(c) (0x0400 + (c) * 0x40)
@@ -51,14 +56,28 @@
#define ADI_AXI_REG_CHAN_CTRL_PN_TYPE_OWR BIT(1)
#define ADI_AXI_REG_CHAN_CTRL_ENABLE BIT(0)
+#define ADI_AXI_ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40)
+#define ADI_AXI_ADC_CHAN_STAT_PN_MASK GENMASK(2, 1)
+
+#define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40)
+#define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16)
+
+/* IO Delays */
+#define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4)
+#define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0)
+
+#define ADI_AXI_ADC_MAX_IO_NUM_LANES 15
+
#define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \
(ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT | \
ADI_AXI_REG_CHAN_CTRL_FMT_EN | \
ADI_AXI_REG_CHAN_CTRL_ENABLE)
struct adi_axi_adc_state {
- struct regmap *regmap;
- struct device *dev;
+ struct regmap *regmap;
+ struct device *dev;
+ /* lock to protect multiple accesses to the device registers */
+ struct mutex lock;
};
static int axi_adc_enable(struct iio_backend *back)
@@ -104,6 +123,100 @@ static int axi_adc_data_format_set(struct iio_backend *back, unsigned int chan,
ADI_AXI_REG_CHAN_CTRL_FMT_MASK, val);
}
+static int axi_adc_data_sample_trigger(struct iio_backend *back,
+ enum iio_backend_sample_trigger trigger)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ switch (trigger) {
+ case IIO_BACKEND_SAMPLE_TRIGGER_EDGE_RISING:
+ return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CTRL,
+ ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK);
+ case IIO_BACKEND_SAMPLE_TRIGGER_EDGE_FALLING:
+ return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CTRL,
+ ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axi_adc_iodelays_set(struct iio_backend *back, unsigned int lane,
+ unsigned int tap)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ int ret;
+ u32 val;
+
+ if (tap > FIELD_MAX(AXI_ADC_DELAY_CTRL_MASK))
+ return -EINVAL;
+ if (lane > ADI_AXI_ADC_MAX_IO_NUM_LANES)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+ ret = regmap_write(st->regmap, ADI_AXI_ADC_REG_DELAY(lane), tap);
+ if (ret)
+ return ret;
+ /*
+ * If readback is ~0, that means there are issues with the
+ * delay_clk.
+ */
+ ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_DELAY(lane), &val);
+ if (ret)
+ return ret;
+ if (val == U32_MAX)
+ return -EIO;
+
+ return 0;
+}
+
+static int axi_adc_test_pattern_set(struct iio_backend *back,
+ unsigned int chan,
+ enum iio_backend_test_pattern pattern)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ switch (pattern) {
+ case IIO_BACKEND_NO_TEST_PATTERN:
+ /* nothing to do */
+ return 0;
+ case IIO_BACKEND_ADI_PRBS_9A:
+ return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan),
+ ADI_AXI_ADC_CHAN_PN_SEL_MASK,
+ FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 0));
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
+ bool *error)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ int ret;
+ u32 val;
+
+ guard(mutex)(&st->lock);
+ /* reset test bits by setting them */
+ ret = regmap_write(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan),
+ ADI_AXI_ADC_CHAN_STAT_PN_MASK);
+ if (ret)
+ return ret;
+
+ /* let's give enough time to validate or erroring the incoming pattern */
+ fsleep(1000);
+
+ ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), &val);
+ if (ret)
+ return ret;
+
+ if (ADI_AXI_ADC_CHAN_STAT_PN_MASK & val)
+ *error = true;
+ else
+ *error = false;
+
+ return 0;
+}
+
static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
@@ -124,26 +237,12 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
struct iio_dev *indio_dev)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
- struct iio_buffer *buffer;
const char *dma_name;
- int ret;
if (device_property_read_string(st->dev, "dma-names", &dma_name))
dma_name = "rx";
- buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name);
- if (IS_ERR(buffer)) {
- dev_err(st->dev, "Could not get DMA buffer, %ld\n",
- PTR_ERR(buffer));
- return ERR_CAST(buffer);
- }
-
- indio_dev->modes |= INDIO_BUFFER_HARDWARE;
- ret = iio_device_attach_buffer(indio_dev, buffer);
- if (ret)
- return ERR_PTR(ret);
-
- return buffer;
+ return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name);
}
static void axi_adc_free_buffer(struct iio_backend *back,
@@ -156,7 +255,6 @@ static const struct regmap_config axi_adc_regmap_config = {
.val_bits = 32,
.reg_bits = 32,
.reg_stride = 4,
- .max_register = 0x0800,
};
static const struct iio_backend_ops adi_axi_adc_generic = {
@@ -167,6 +265,10 @@ static const struct iio_backend_ops adi_axi_adc_generic = {
.chan_disable = axi_adc_chan_disable,
.request_buffer = axi_adc_request_buffer,
.free_buffer = axi_adc_free_buffer,
+ .data_sample_trigger = axi_adc_data_sample_trigger,
+ .iodelay_set = axi_adc_iodelays_set,
+ .test_pattern_set = axi_adc_test_pattern_set,
+ .chan_status = axi_adc_chan_status,
};
static int adi_axi_adc_probe(struct platform_device *pdev)
@@ -175,6 +277,7 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
struct adi_axi_adc_state *st;
void __iomem *base;
unsigned int ver;
+ struct clk *clk;
int ret;
st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
@@ -195,6 +298,10 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (!expected_ver)
return -ENODEV;
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
/*
* Force disable the core. Up to the frontend to enable us. And we can
* still read/write registers...
@@ -207,9 +314,9 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (*expected_ver > ver) {
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
dev_err(&pdev->dev,
- "IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
ADI_AXI_PCORE_VER_MINOR(*expected_ver),
ADI_AXI_PCORE_VER_PATCH(*expected_ver),
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index 614de9644800..78fada4b7b1c 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -538,7 +538,7 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct exynos_adc *info = iio_priv(indio_dev);
- unsigned long timeout;
+ unsigned long time_left;
int ret;
if (mask == IIO_CHAN_INFO_SCALE) {
@@ -562,9 +562,9 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
if (info->data->start_conv)
info->data->start_conv(info, chan->address);
- timeout = wait_for_completion_timeout(&info->completion,
- EXYNOS_ADC_TIMEOUT);
- if (timeout == 0) {
+ time_left = wait_for_completion_timeout(&info->completion,
+ EXYNOS_ADC_TIMEOUT);
+ if (time_left == 0) {
dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
if (info->data->init_hw)
info->data->init_hw(info);
@@ -583,7 +583,7 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
{
struct exynos_adc *info = iio_priv(indio_dev);
- unsigned long timeout;
+ unsigned long time_left;
int ret;
mutex_lock(&info->lock);
@@ -597,9 +597,9 @@ static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
/* Select the ts channel to be used and Trigger conversion */
info->data->start_conv(info, ADC_S3C2410_MUX_TS);
- timeout = wait_for_completion_timeout(&info->completion,
- EXYNOS_ADC_TIMEOUT);
- if (timeout == 0) {
+ time_left = wait_for_completion_timeout(&info->completion,
+ EXYNOS_ADC_TIMEOUT);
+ if (time_left == 0) {
dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
if (info->data->init_hw)
info->data->init_hw(info);
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
index 68c813de0605..b680690631db 100644
--- a/drivers/iio/adc/fsl-imx25-gcq.c
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -12,8 +12,9 @@
#include <linux/interrupt.h>
#include <linux/mfd/imx25-tsadc.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -107,7 +108,7 @@ static int mx25_gcq_get_raw_value(struct device *dev,
struct mx25_gcq_priv *priv,
int *val)
{
- long timeout;
+ long time_left;
u32 data;
/* Setup the configuration we want to use */
@@ -120,12 +121,12 @@ static int mx25_gcq_get_raw_value(struct device *dev,
regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS,
MX25_ADCQ_CR_FQS);
- timeout = wait_for_completion_interruptible_timeout(
+ time_left = wait_for_completion_interruptible_timeout(
&priv->completed, MX25_GCQ_TIMEOUT);
- if (timeout < 0) {
+ if (time_left < 0) {
dev_err(dev, "ADC wait for measurement failed\n");
- return timeout;
- } else if (timeout == 0) {
+ return time_left;
+ } else if (time_left == 0) {
dev_err(dev, "ADC timed out\n");
return -ETIMEDOUT;
}
@@ -198,8 +199,6 @@ static int mx25_gcq_ext_regulator_setup(struct device *dev,
static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
struct mx25_gcq_priv *priv)
{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *child;
struct device *dev = &pdev->dev;
int ret, i;
@@ -216,37 +215,30 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
MX25_ADCQ_CFG_IN(i) |
MX25_ADCQ_CFG_REFN_NGND2);
- for_each_child_of_node(np, child) {
+ device_for_each_child_node_scoped(dev, child) {
u32 reg;
u32 refp = MX25_ADCQ_CFG_REFP_INT;
u32 refn = MX25_ADCQ_CFG_REFN_NGND2;
- ret = of_property_read_u32(child, "reg", &reg);
- if (ret) {
- dev_err(dev, "Failed to get reg property\n");
- of_node_put(child);
- return ret;
- }
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get reg property\n");
- if (reg >= MX25_NUM_CFGS) {
- dev_err(dev,
+ if (reg >= MX25_NUM_CFGS)
+ return dev_err_probe(dev, -EINVAL,
"reg value is greater than the number of available configuration registers\n");
- of_node_put(child);
- return -EINVAL;
- }
- of_property_read_u32(child, "fsl,adc-refp", &refp);
- of_property_read_u32(child, "fsl,adc-refn", &refn);
+ fwnode_property_read_u32(child, "fsl,adc-refp", &refp);
+ fwnode_property_read_u32(child, "fsl,adc-refn", &refn);
switch (refp) {
case MX25_ADC_REFP_EXT:
case MX25_ADC_REFP_XP:
case MX25_ADC_REFP_YP:
ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp);
- if (ret) {
- of_node_put(child);
+ if (ret)
return ret;
- }
priv->channel_vref_mv[reg] =
regulator_get_voltage(priv->vref[refp]);
/* Conversion from uV to mV */
@@ -256,9 +248,8 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
priv->channel_vref_mv[reg] = 2500;
break;
default:
- dev_err(dev, "Invalid positive reference %d\n", refp);
- of_node_put(child);
- return -EINVAL;
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid positive reference %d\n", refp);
}
/*
@@ -268,16 +259,13 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
refp = MX25_ADCQ_CFG_REFP(refp);
refn = MX25_ADCQ_CFG_REFN(refn);
- if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
- dev_err(dev, "Invalid fsl,adc-refp property value\n");
- of_node_put(child);
- return -EINVAL;
- }
- if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
- dev_err(dev, "Invalid fsl,adc-refn property value\n");
- of_node_put(child);
- return -EINVAL;
- }
+ if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid fsl,adc-refp property value\n");
+
+ if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid fsl,adc-refn property value\n");
regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),
MX25_ADCQ_CFG_REFP_MASK |
@@ -294,6 +282,17 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
return 0;
}
+static void mx25_gcq_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+/* Custom handling needed as this driver doesn't own the clock */
+static void mx25_gcq_clk_disable(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
static int mx25_gcq_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
@@ -315,10 +314,9 @@ static int mx25_gcq_probe(struct platform_device *pdev)
return PTR_ERR(mem);
priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);
- if (IS_ERR(priv->regs)) {
- dev_err(dev, "Failed to initialize regmap\n");
- return PTR_ERR(priv->regs);
- }
+ if (IS_ERR(priv->regs))
+ return dev_err_probe(dev, PTR_ERR(priv->regs),
+ "Failed to initialize regmap\n");
mutex_init(&priv->lock);
@@ -334,69 +332,44 @@ static int mx25_gcq_probe(struct platform_device *pdev)
ret = regulator_enable(priv->vref[i]);
if (ret)
- goto err_regulator_disable;
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mx25_gcq_reg_disable,
+ priv->vref[i]);
+ if (ret)
+ return ret;
}
priv->clk = tsadc->clk;
ret = clk_prepare_enable(priv->clk);
- if (ret) {
- dev_err(dev, "Failed to enable clock\n");
- goto err_vref_disable;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable clock\n");
+
+ ret = devm_add_action_or_reset(dev, mx25_gcq_clk_disable,
+ priv->clk);
+ if (ret)
+ return ret;
ret = platform_get_irq(pdev, 0);
if (ret < 0)
- goto err_clk_unprepare;
+ return ret;
priv->irq = ret;
- ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv);
- if (ret) {
- dev_err(dev, "Failed requesting IRQ\n");
- goto err_clk_unprepare;
- }
+ ret = devm_request_irq(dev, priv->irq, mx25_gcq_irq, 0, pdev->name,
+ priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed requesting IRQ\n");
indio_dev->channels = mx25_gcq_channels;
indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);
indio_dev->info = &mx25_gcq_iio_info;
indio_dev->name = driver_name;
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(dev, "Failed to register iio device\n");
- goto err_irq_free;
- }
-
- platform_set_drvdata(pdev, indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register iio device\n");
return 0;
-
-err_irq_free:
- free_irq(priv->irq, priv);
-err_clk_unprepare:
- clk_disable_unprepare(priv->clk);
-err_vref_disable:
- i = 4;
-err_regulator_disable:
- for (; i-- > 0;) {
- if (priv->vref[i])
- regulator_disable(priv->vref[i]);
- }
- return ret;
-}
-
-static void mx25_gcq_remove(struct platform_device *pdev)
-{
- struct iio_dev *indio_dev = platform_get_drvdata(pdev);
- struct mx25_gcq_priv *priv = iio_priv(indio_dev);
- int i;
-
- iio_device_unregister(indio_dev);
- free_irq(priv->irq, priv);
- clk_disable_unprepare(priv->clk);
- for (i = 4; i-- > 0;) {
- if (priv->vref[i])
- regulator_disable(priv->vref[i]);
- }
}
static const struct of_device_id mx25_gcq_ids[] = {
@@ -411,7 +384,6 @@ static struct platform_driver mx25_gcq_driver = {
.of_match_table = mx25_gcq_ids,
},
.probe = mx25_gcq_probe,
- .remove_new = mx25_gcq_remove,
};
module_platform_driver(mx25_gcq_driver);
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index c80c55fb8c6c..fef97c1d226a 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -7,7 +7,7 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
@@ -459,7 +459,6 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
static int hx711_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
struct hx711_data *hx711_data;
struct iio_dev *indio_dev;
int ret;
@@ -533,7 +532,7 @@ static int hx711_probe(struct platform_device *pdev)
hx711_data->gain_chan_a = 128;
hx711_data->clock_frequency = 400000;
- ret = of_property_read_u32(np, "clock-frequency",
+ ret = device_property_read_u32(&pdev->dev, "clock-frequency",
&hx711_data->clock_frequency);
/*
diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c
index 7263ad76124d..c7f40ae6e608 100644
--- a/drivers/iio/adc/intel_mrfld_adc.c
+++ b/drivers/iio/adc/intel_mrfld_adc.c
@@ -75,7 +75,7 @@ static int mrfld_adc_single_conv(struct iio_dev *indio_dev,
struct mrfld_adc *adc = iio_priv(indio_dev);
struct regmap *regmap = adc->regmap;
unsigned int req;
- long timeout;
+ long time_left;
__be16 value;
int ret;
@@ -95,13 +95,13 @@ static int mrfld_adc_single_conv(struct iio_dev *indio_dev,
if (ret)
goto done;
- timeout = wait_for_completion_interruptible_timeout(&adc->completion,
- BCOVE_ADC_TIMEOUT);
- if (timeout < 0) {
- ret = timeout;
+ time_left = wait_for_completion_interruptible_timeout(&adc->completion,
+ BCOVE_ADC_TIMEOUT);
+ if (time_left < 0) {
+ ret = time_left;
goto done;
}
- if (timeout == 0) {
+ if (time_left == 0) {
ret = -ETIMEDOUT;
goto done;
}
diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c
index 6af829349b4e..45368850b220 100644
--- a/drivers/iio/adc/max11410.c
+++ b/drivers/iio/adc/max11410.c
@@ -696,7 +696,6 @@ static int max11410_parse_channels(struct max11410_state *st,
struct device *dev = &st->spi_dev->dev;
struct max11410_channel_config *cfg;
struct iio_chan_spec *channels;
- struct fwnode_handle *child;
u32 reference, sig_path;
const char *node_name;
u32 inputs[2], scale;
@@ -720,7 +719,7 @@ static int max11410_parse_channels(struct max11410_state *st,
if (!st->channels)
return -ENOMEM;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
node_name = fwnode_get_name(child);
if (fwnode_property_present(child, "diff-channels")) {
ret = fwnode_property_read_u32_array(child,
@@ -735,47 +734,37 @@ static int max11410_parse_channels(struct max11410_state *st,
inputs[1] = 0;
chanspec.differential = 0;
}
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
if (inputs[0] > MAX11410_CHANNEL_INDEX_MAX ||
- inputs[1] > MAX11410_CHANNEL_INDEX_MAX) {
- fwnode_handle_put(child);
+ inputs[1] > MAX11410_CHANNEL_INDEX_MAX)
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid channel index for %s, should be less than %d\n",
node_name,
MAX11410_CHANNEL_INDEX_MAX + 1);
- }
cfg = &st->channels[chan_idx];
reference = MAX11410_REFSEL_AVDD_AGND;
fwnode_property_read_u32(child, "adi,reference", &reference);
- if (reference > MAX11410_REFSEL_MAX) {
- fwnode_handle_put(child);
+ if (reference > MAX11410_REFSEL_MAX)
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid adi,reference value for %s, should be less than %d.\n",
node_name, MAX11410_REFSEL_MAX + 1);
- }
if (!max11410_get_vrefp(st, reference) ||
- (!max11410_get_vrefn(st, reference) && reference <= 2)) {
- fwnode_handle_put(child);
+ (!max11410_get_vrefn(st, reference) && reference <= 2))
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid VREF configuration for %s, either specify corresponding VREF regulators or change adi,reference property.\n",
node_name);
- }
sig_path = MAX11410_PGA_SIG_PATH_BUFFERED;
fwnode_property_read_u32(child, "adi,input-mode", &sig_path);
- if (sig_path > MAX11410_SIG_PATH_MAX) {
- fwnode_handle_put(child);
+ if (sig_path > MAX11410_SIG_PATH_MAX)
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid adi,input-mode value for %s, should be less than %d.\n",
node_name, MAX11410_SIG_PATH_MAX + 1);
- }
fwnode_property_read_u32(child, "settling-time-us",
&cfg->settling_time_us);
@@ -793,10 +782,8 @@ static int max11410_parse_channels(struct max11410_state *st,
cfg->scale_avail = devm_kcalloc(dev, MAX11410_SCALE_AVAIL_SIZE * 2,
sizeof(*cfg->scale_avail),
GFP_KERNEL);
- if (!cfg->scale_avail) {
- fwnode_handle_put(child);
+ if (!cfg->scale_avail)
return -ENOMEM;
- }
scale = max11410_get_scale(st, *cfg);
for (i = 0; i < MAX11410_SCALE_AVAIL_SIZE; i++) {
diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c
index 311b613b6057..e2ae13f1e842 100644
--- a/drivers/iio/adc/mcp3564.c
+++ b/drivers/iio/adc/mcp3564.c
@@ -998,7 +998,6 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev)
struct mcp3564_state *adc = iio_priv(indio_dev);
struct device *dev = &adc->spi->dev;
struct iio_chan_spec *channels;
- struct fwnode_handle *child;
struct iio_chan_spec chanspec = mcp3564_channel_template;
struct iio_chan_spec temp_chanspec = mcp3564_temp_channel_template;
struct iio_chan_spec burnout_chanspec = mcp3564_burnout_channel_template;
@@ -1025,7 +1024,7 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev)
if (!channels)
return dev_err_probe(dev, -ENOMEM, "Can't allocate memory\n");
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
node_name = fwnode_get_name(child);
if (fwnode_property_present(child, "diff-channels")) {
@@ -1033,26 +1032,25 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev)
"diff-channels",
inputs,
ARRAY_SIZE(inputs));
+ if (ret)
+ return ret;
+
chanspec.differential = 1;
} else {
ret = fwnode_property_read_u32(child, "reg", &inputs[0]);
+ if (ret)
+ return ret;
chanspec.differential = 0;
inputs[1] = MCP3564_AGND;
}
- if (ret) {
- fwnode_handle_put(child);
- return ret;
- }
if (inputs[0] > MCP3564_INTERNAL_VCM ||
- inputs[1] > MCP3564_INTERNAL_VCM) {
- fwnode_handle_put(child);
+ inputs[1] > MCP3564_INTERNAL_VCM)
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Channel index > %d, for %s\n",
MCP3564_INTERNAL_VCM + 1,
node_name);
- }
chanspec.address = (inputs[0] << 4) | inputs[1];
chanspec.channel = inputs[0];
diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
index 2e60c10ee4ff..8c7b64e78dbb 100644
--- a/drivers/iio/adc/mxs-lradc-adc.c
+++ b/drivers/iio/adc/mxs-lradc-adc.c
@@ -724,7 +724,6 @@ static int mxs_lradc_adc_probe(struct platform_device *pdev)
iio->dev.of_node = dev->parent->of_node;
iio->info = &mxs_lradc_adc_iio_info;
iio->modes = INDIO_DIRECT_MODE;
- iio->masklength = LRADC_MAX_TOTAL_CHANS;
if (lradc->soc == IMX23_LRADC) {
iio->channels = mx23_lradc_chan_spec;
diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c
index e0c2742da523..456f12faa348 100644
--- a/drivers/iio/adc/pac1934.c
+++ b/drivers/iio/adc/pac1934.c
@@ -787,6 +787,15 @@ static int pac1934_read_raw(struct iio_dev *indio_dev,
s64 curr_energy;
int ret, channel = chan->channel - 1;
+ /*
+ * For AVG the index should be between 5 to 8.
+ * To calculate PAC1934_CH_VOLTAGE_AVERAGE,
+ * respectively PAC1934_CH_CURRENT real index, we need
+ * to remove the added offset (PAC1934_MAX_NUM_CHANNELS).
+ */
+ if (channel >= PAC1934_MAX_NUM_CHANNELS)
+ channel = channel - PAC1934_MAX_NUM_CHANNELS;
+
ret = pac1934_retrieve_data(info, PAC1934_MIN_UPDATE_WAIT_TIME_US);
if (ret < 0)
return ret;
@@ -1079,8 +1088,8 @@ static int pac1934_chip_identify(struct pac1934_chip_info *info)
* documentation related to the ACPI device definition
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
*/
-static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
- struct pac1934_chip_info *info)
+static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
+ struct pac1934_chip_info *info)
{
acpi_handle handle;
union acpi_object *rez;
@@ -1095,7 +1104,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL);
if (!rez)
- return false;
+ return -EINVAL;
for (i = 0; i < rez->package.count; i += 2) {
idx = i / 2;
@@ -1118,7 +1127,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
* and assign the default sampling rate
*/
info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ;
- return true;
+ return 0;
}
for (i = 0; i < rez->package.count; i++) {
@@ -1131,7 +1140,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL);
if (!rez)
- return false;
+ return -EINVAL;
bi_dir_mask = rez->package.elements[0].integer.value;
info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0;
@@ -1143,19 +1152,18 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL);
if (!rez)
- return false;
+ return -EINVAL;
info->sample_rate_value = rez->package.elements[0].integer.value;
ACPI_FREE(rez);
- return true;
+ return 0;
}
-static bool pac1934_of_parse_channel_config(struct i2c_client *client,
- struct pac1934_chip_info *info)
+static int pac1934_fw_parse_channel_config(struct i2c_client *client,
+ struct pac1934_chip_info *info)
{
- struct fwnode_handle *node, *fwnode;
struct device *dev = &client->dev;
unsigned int current_channel;
int idx, ret;
@@ -1163,46 +1171,38 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client,
info->sample_rate_value = 1024;
current_channel = 1;
- fwnode = dev_fwnode(dev);
- fwnode_for_each_available_child_node(fwnode, node) {
+ device_for_each_child_node_scoped(dev, node) {
ret = fwnode_property_read_u32(node, "reg", &idx);
- if (ret) {
- dev_err_probe(dev, ret,
- "reading invalid channel index\n");
- goto err_fwnode;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reading invalid channel index\n");
+
/* adjust idx to match channel index (1 to 4) from the datasheet */
idx--;
if (current_channel >= (info->phys_channels + 1) ||
- idx >= info->phys_channels || idx < 0) {
- dev_err_probe(dev, -EINVAL,
- "%s: invalid channel_index %d value\n",
- fwnode_get_name(node), idx);
- goto err_fwnode;
- }
+ idx >= info->phys_channels || idx < 0)
+ return dev_err_probe(dev, -EINVAL,
+ "%s: invalid channel_index %d value\n",
+ fwnode_get_name(node), idx);
/* enable channel */
info->active_channels[idx] = true;
ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms",
&info->shunts[idx]);
- if (ret) {
- dev_err_probe(dev, ret,
- "%s: invalid shunt-resistor value: %d\n",
- fwnode_get_name(node), info->shunts[idx]);
- goto err_fwnode;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "%s: invalid shunt-resistor value: %d\n",
+ fwnode_get_name(node), info->shunts[idx]);
if (fwnode_property_present(node, "label")) {
ret = fwnode_property_read_string(node, "label",
(const char **)&info->labels[idx]);
- if (ret) {
- dev_err_probe(dev, ret,
- "%s: invalid rail-name value\n",
- fwnode_get_name(node));
- goto err_fwnode;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "%s: invalid rail-name value\n",
+ fwnode_get_name(node));
}
info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar");
@@ -1210,12 +1210,7 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client,
current_channel++;
}
- return true;
-
-err_fwnode:
- fwnode_handle_put(node);
-
- return false;
+ return 0;
}
static void pac1934_cancel_delayed_work(void *dwork)
@@ -1485,7 +1480,6 @@ static int pac1934_probe(struct i2c_client *client)
const struct pac1934_features *chip;
struct iio_dev *indio_dev;
int cnt, ret;
- bool match = false;
struct device *dev = &client->dev;
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
@@ -1519,16 +1513,16 @@ static int pac1934_probe(struct i2c_client *client)
}
if (acpi_match_device(dev->driver->acpi_match_table, dev))
- match = pac1934_acpi_parse_channel_config(client, info);
+ ret = pac1934_acpi_parse_channel_config(client, info);
else
/*
* This makes it possible to use also ACPI PRP0001 for
* registering the device using device tree properties.
*/
- match = pac1934_of_parse_channel_config(client, info);
+ ret = pac1934_fw_parse_channel_config(client, info);
- if (!match)
- return dev_err_probe(dev, -EINVAL,
+ if (ret)
+ return dev_err_probe(dev, ret,
"parameter parsing returned an error\n");
mutex_init(&info->lock);
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index b6b612d733ff..9b69f40beed8 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -825,7 +825,6 @@ static int adc5_get_fw_data(struct adc5_chip *adc)
const struct adc5_channels *adc_chan;
struct iio_chan_spec *iio_chan;
struct adc5_channel_prop prop, *chan_props;
- struct fwnode_handle *child;
unsigned int index = 0;
int ret;
@@ -849,12 +848,10 @@ static int adc5_get_fw_data(struct adc5_chip *adc)
if (!adc->data)
adc->data = &adc5_data_pmic;
- device_for_each_child_node(adc->dev, child) {
+ device_for_each_child_node_scoped(adc->dev, child) {
ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
prop.scale_fn_type =
adc->data->adc_chans[prop.channel].scale_fn_type;
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index d524f2e8e927..15a21d2860e7 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -318,7 +318,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
struct rcar_gyroadc *priv = iio_priv(indio_dev);
struct device *dev = priv->dev;
struct device_node *np = dev->of_node;
- struct device_node *child;
struct regulator *vref;
unsigned int reg;
unsigned int adcmode = -1, childmode;
@@ -326,7 +325,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
unsigned int num_channels;
int ret, first = 1;
- for_each_child_of_node(np, child) {
+ for_each_available_child_of_node_scoped(np, child) {
of_id = of_match_node(rcar_gyroadc_child_match, child);
if (!of_id) {
dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
@@ -352,7 +351,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
break;
default:
- goto err_e_inval;
+ return -EINVAL;
}
/*
@@ -369,7 +368,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
dev_err(dev,
"Failed to get child reg property of ADC \"%pOFn\".\n",
child);
- goto err_of_node_put;
+ return ret;
}
/* Channel number is too high. */
@@ -377,7 +376,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
dev_err(dev,
"Only %i channels supported with %pOFn, but reg = <%i>.\n",
num_channels, child, reg);
- goto err_e_inval;
+ return -EINVAL;
}
}
@@ -386,7 +385,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
dev_err(dev,
"Channel %i uses different ADC mode than the rest.\n",
reg);
- goto err_e_inval;
+ return -EINVAL;
}
/* Channel is valid, grab the regulator. */
@@ -396,8 +395,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
if (IS_ERR(vref)) {
dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
reg);
- ret = PTR_ERR(vref);
- goto err_of_node_put;
+ return PTR_ERR(vref);
}
priv->vref[reg] = vref;
@@ -422,7 +420,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
* we can stop parsing here.
*/
if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
- of_node_put(child);
break;
}
}
@@ -433,12 +430,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
}
return 0;
-
-err_e_inval:
- ret = -EINVAL;
-err_of_node_put:
- of_node_put(child);
- return ret;
}
static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
index a5464737e527..bcb129840908 100644
--- a/drivers/iio/adc/rtq6056.c
+++ b/drivers/iio/adc/rtq6056.c
@@ -520,32 +520,20 @@ static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
{
struct rtq6056_priv *priv = iio_priv(indio_dev);
const struct richtek_dev_data *devdata = priv->devdata;
- int ret;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
- switch (mask) {
- case IIO_CHAN_INFO_SAMP_FREQ:
- if (devdata->fixed_samp_freq) {
- ret = -EINVAL;
- break;
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (devdata->fixed_samp_freq)
+ return -EINVAL;
+ return rtq6056_adc_set_samp_freq(priv, chan, val);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return devdata->set_average(priv, val);
+ default:
+ return -EINVAL;
}
-
- ret = rtq6056_adc_set_samp_freq(priv, chan, val);
- break;
- case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = devdata->set_average(priv, val);
- break;
- default:
- ret = -EINVAL;
- break;
}
-
- iio_device_release_direct_mode(indio_dev);
-
- return ret;
+ unreachable();
}
static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = {
diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
index 0921ff2d9b3a..cd3a7e46ea53 100644
--- a/drivers/iio/adc/rzg2l_adc.c
+++ b/drivers/iio/adc/rzg2l_adc.c
@@ -302,7 +302,6 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc)
{
struct iio_chan_spec *chan_array;
- struct fwnode_handle *fwnode;
struct rzg2l_adc_data *data;
unsigned int channel;
int num_channels;
@@ -330,17 +329,13 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l
return -ENOMEM;
i = 0;
- device_for_each_child_node(&pdev->dev, fwnode) {
+ device_for_each_child_node_scoped(&pdev->dev, fwnode) {
ret = fwnode_property_read_u32(fwnode, "reg", &channel);
- if (ret) {
- fwnode_handle_put(fwnode);
+ if (ret)
return ret;
- }
- if (channel >= RZG2L_ADC_MAX_CHANNELS) {
- fwnode_handle_put(fwnode);
+ if (channel >= RZG2L_ADC_MAX_CHANNELS)
return -EINVAL;
- }
chan_array[i].type = IIO_VOLTAGE;
chan_array[i].indexed = 1;
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
index 71362c2ddf89..b6dd096391c1 100644
--- a/drivers/iio/adc/spear_adc.c
+++ b/drivers/iio/adc/spear_adc.c
@@ -5,8 +5,10 @@
* Copyright 2012 Stefan Roese <sr@denx.de>
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
@@ -15,8 +17,6 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/completion.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -70,7 +70,7 @@ struct adc_regs_spear6xx {
};
struct spear_adc_state {
- struct device_node *np;
+ struct device *dev;
struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
struct clk *clk;
@@ -123,7 +123,7 @@ static void spear_adc_set_ctrl(struct spear_adc_state *st, int n,
static u32 spear_adc_get_average(struct spear_adc_state *st)
{
- if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ if (device_is_compatible(st->dev, "st,spear600-adc")) {
return __raw_readl(&st->adc_base_spear6xx->average.msb) &
SPEAR_ADC_DATA_MASK;
} else {
@@ -134,7 +134,7 @@ static u32 spear_adc_get_average(struct spear_adc_state *st)
static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate)
{
- if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ if (device_is_compatible(st->dev, "st,spear600-adc")) {
__raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
&st->adc_base_spear6xx->scan_rate_lo);
__raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
@@ -266,7 +266,6 @@ static const struct iio_info spear_adc_info = {
static int spear_adc_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct spear_adc_state *st;
struct iio_dev *indio_dev = NULL;
@@ -279,11 +278,10 @@ static int spear_adc_probe(struct platform_device *pdev)
"failed allocating iio device\n");
st = iio_priv(indio_dev);
+ st->dev = dev;
mutex_init(&st->lock);
- st->np = np;
-
/*
* SPEAr600 has a different register layout than other SPEAr SoC's
* (e.g. SPEAr3xx). Let's provide two register base addresses
@@ -310,8 +308,7 @@ static int spear_adc_probe(struct platform_device *pdev)
if (ret < 0)
return dev_err_probe(dev, ret, "failed requesting interrupt\n");
- if (of_property_read_u32(np, "sampling-frequency",
- &st->sampling_freq))
+ if (device_property_read_u32(dev, "sampling-frequency", &st->sampling_freq))
return dev_err_probe(dev, -EINVAL,
"sampling-frequency missing in DT\n");
@@ -319,13 +316,13 @@ static int spear_adc_probe(struct platform_device *pdev)
* Optional avg_samples defaults to 0, resulting in single data
* conversion
*/
- of_property_read_u32(np, "average-samples", &st->avg_samples);
+ device_property_read_u32(dev, "average-samples", &st->avg_samples);
/*
* Optional vref_external defaults to 0, resulting in internal vref
* selection
*/
- of_property_read_u32(np, "vref-external", &st->vref_external);
+ device_property_read_u32(dev, "vref-external", &st->vref_external);
spear_adc_configure(st);
@@ -346,19 +343,17 @@ static int spear_adc_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_OF
static const struct of_device_id spear_adc_dt_ids[] = {
{ .compatible = "st,spear600-adc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
-#endif
static struct platform_driver spear_adc_driver = {
.probe = spear_adc_probe,
.driver = {
.name = SPEAR_ADC_MOD_NAME,
- .of_match_table = of_match_ptr(spear_adc_dt_ids),
+ .of_match_table = spear_adc_dt_ids,
},
};
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index b5d3c9cea5c4..375aa7720f80 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1408,7 +1408,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
struct stm32_adc *adc = iio_priv(indio_dev);
struct device *dev = indio_dev->dev.parent;
const struct stm32_adc_regspec *regs = adc->cfg->regs;
- long timeout;
+ long time_left;
u32 val;
int ret;
@@ -1440,12 +1440,12 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
adc->cfg->start_conv(indio_dev, false);
- timeout = wait_for_completion_interruptible_timeout(
+ time_left = wait_for_completion_interruptible_timeout(
&adc->completion, STM32_ADC_TIMEOUT);
- if (timeout == 0) {
+ if (time_left == 0) {
ret = -ETIMEDOUT;
- } else if (timeout < 0) {
- ret = timeout;
+ } else if (time_left < 0) {
+ ret = time_left;
} else {
*res = adc->buffer[0];
ret = IIO_VAL_INT;
@@ -2187,58 +2187,52 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
struct iio_chan_spec *channels)
{
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
- struct fwnode_handle *child;
+ struct device *dev = &indio_dev->dev;
const char *name;
int val, scan_index = 0, ret;
bool differential;
u32 vin[2];
- device_for_each_child_node(&indio_dev->dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &val);
- if (ret) {
- dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
- goto err;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Missing channel index\n");
ret = fwnode_property_read_string(child, "label", &name);
/* label is optional */
if (!ret) {
- if (strlen(name) >= STM32_ADC_CH_SZ) {
- dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
- name, STM32_ADC_CH_SZ);
- ret = -EINVAL;
- goto err;
- }
+ if (strlen(name) >= STM32_ADC_CH_SZ)
+ return dev_err_probe(dev, -EINVAL,
+ "Label %s exceeds %d characters\n",
+ name, STM32_ADC_CH_SZ);
+
strscpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
ret = stm32_adc_populate_int_ch(indio_dev, name, val);
if (ret == -ENOENT)
continue;
else if (ret)
- goto err;
+ return ret;
} else if (ret != -EINVAL) {
- dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
- goto err;
+ return dev_err_probe(dev, ret, "Invalid label\n");
}
- if (val >= adc_info->max_channels) {
- dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
- ret = -EINVAL;
- goto err;
- }
+ if (val >= adc_info->max_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid channel %d\n", val);
differential = false;
ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2);
/* diff-channels is optional */
if (!ret) {
differential = true;
- if (vin[0] != val || vin[1] >= adc_info->max_channels) {
- dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
- vin[0], vin[1]);
- goto err;
- }
+ if (vin[0] != val || vin[1] >= adc_info->max_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid channel in%d-in%d\n",
+ vin[0], vin[1]);
} else if (ret != -EINVAL) {
- dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
- goto err;
+ return dev_err_probe(dev, ret,
+ "Invalid diff-channels property\n");
}
stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
@@ -2247,11 +2241,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
val = 0;
ret = fwnode_property_read_u32(child, "st,min-sample-time-ns", &val);
/* st,min-sample-time-ns is optional */
- if (ret && ret != -EINVAL) {
- dev_err(&indio_dev->dev, "Invalid st,min-sample-time-ns property %d\n",
- ret);
- goto err;
- }
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "Invalid st,min-sample-time-ns property\n");
stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
if (differential)
@@ -2261,11 +2253,6 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
}
return scan_index;
-
-err:
- fwnode_handle_put(child);
-
- return ret;
}
static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping)
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index ca08ae3108b2..9a47d2c87f05 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -1116,7 +1116,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *res)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- long timeout;
+ long time_left;
int ret;
reinit_completion(&adc->completion);
@@ -1141,17 +1141,17 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
goto stop_dfsdm;
}
- timeout = wait_for_completion_interruptible_timeout(&adc->completion,
- DFSDM_TIMEOUT);
+ time_left = wait_for_completion_interruptible_timeout(&adc->completion,
+ DFSDM_TIMEOUT);
/* Mask IRQ for regular conversion achievement*/
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
- if (timeout == 0)
+ if (time_left == 0)
ret = -ETIMEDOUT;
- else if (timeout < 0)
- ret = timeout;
+ else if (time_left < 0)
+ ret = time_left;
else
ret = IIO_VAL_INT;
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index 6ae967e4d8fa..d3363d02f292 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -902,10 +902,9 @@ static int ads1015_client_get_channels_config(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ads1015_data *data = iio_priv(indio_dev);
struct device *dev = &client->dev;
- struct fwnode_handle *node;
int i = -1;
- device_for_each_child_node(dev, node) {
+ device_for_each_child_node_scoped(dev, node) {
u32 pval;
unsigned int channel;
unsigned int pga = ADS1015_DEFAULT_PGA;
@@ -927,7 +926,6 @@ static int ads1015_client_get_channels_config(struct i2c_client *client)
pga = pval;
if (pga > 5) {
dev_err(dev, "invalid gain on %pfw\n", node);
- fwnode_handle_put(node);
return -EINVAL;
}
}
@@ -936,7 +934,6 @@ static int ads1015_client_get_channels_config(struct i2c_client *client)
data_rate = pval;
if (data_rate > 7) {
dev_err(dev, "invalid data_rate on %pfw\n", node);
- fwnode_handle_put(node);
return -EINVAL;
}
}
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index fcfc46254313..cb04a29b3dba 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -694,7 +694,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
struct ads131e08_channel_config *channel_config;
struct device *dev = &st->spi->dev;
struct iio_chan_spec *channels;
- struct fwnode_handle *node;
unsigned int channel, tmp;
int num_channels, i, ret;
@@ -736,10 +735,10 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
return -ENOMEM;
i = 0;
- device_for_each_child_node(dev, node) {
+ device_for_each_child_node_scoped(dev, node) {
ret = fwnode_property_read_u32(node, "reg", &channel);
if (ret)
- goto err_child_out;
+ return ret;
ret = fwnode_property_read_u32(node, "ti,gain", &tmp);
if (ret) {
@@ -747,7 +746,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
} else {
ret = ads131e08_pga_gain_to_field_value(st, tmp);
if (ret < 0)
- goto err_child_out;
+ return ret;
channel_config[i].pga_gain = tmp;
}
@@ -758,7 +757,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
} else {
ret = ads131e08_validate_channel_mux(st, tmp);
if (ret)
- goto err_child_out;
+ return ret;
channel_config[i].mux = tmp;
}
@@ -785,9 +784,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
return 0;
-err_child_out:
- fwnode_handle_put(node);
- return ret;
}
static void ads131e08_regulator_disable(void *data)
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 4a247ca25a44..0253064fadec 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -19,10 +19,12 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/mfd/twl.h>
-#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/mutex.h>
#include <linux/bitops.h>
@@ -30,7 +32,6 @@
#include <linux/types.h>
#include <linux/gfp.h>
#include <linux/err.h>
-#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
@@ -744,14 +745,14 @@ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
*/
static int twl4030_madc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct twl4030_madc_platform_data *pdata = dev_get_platdata(dev);
struct twl4030_madc_data *madc;
- struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct device_node *np = pdev->dev.of_node;
int irq, ret;
u8 regval;
struct iio_dev *iio_dev = NULL;
- if (!pdata && !np) {
+ if (!pdata && !dev_fwnode(dev)) {
dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
return -EINVAL;
}
@@ -779,7 +780,7 @@ static int twl4030_madc_probe(struct platform_device *pdev)
if (pdata)
madc->use_second_irq = (pdata->irq_line != 1);
else
- madc->use_second_irq = of_property_read_bool(np,
+ madc->use_second_irq = device_property_read_bool(dev,
"ti,system-uses-second-madc-irq");
madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
@@ -905,20 +906,18 @@ static void twl4030_madc_remove(struct platform_device *pdev)
regulator_disable(madc->usb3v1);
}
-#ifdef CONFIG_OF
static const struct of_device_id twl_madc_of_match[] = {
{ .compatible = "ti,twl4030-madc", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, twl_madc_of_match);
-#endif
static struct platform_driver twl4030_madc_driver = {
.probe = twl4030_madc_probe,
.remove_new = twl4030_madc_remove,
.driver = {
.name = "twl4030_madc",
- .of_match_table = of_match_ptr(twl_madc_of_match),
+ .of_match_table = twl_madc_of_match,
},
};
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
index 78bf55438b2c..6a3db2bce460 100644
--- a/drivers/iio/adc/twl6030-gpadc.c
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -519,7 +519,7 @@ static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev,
{
struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev);
int ret;
- long timeout;
+ long time_left;
mutex_lock(&gpadc->lock);
@@ -529,12 +529,12 @@ static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev,
goto err;
}
/* wait for conversion to complete */
- timeout = wait_for_completion_interruptible_timeout(
+ time_left = wait_for_completion_interruptible_timeout(
&gpadc->irq_complete, msecs_to_jiffies(5000));
- if (timeout == 0) {
+ if (time_left == 0) {
ret = -ETIMEDOUT;
goto err;
- } else if (timeout < 0) {
+ } else if (time_left < 0) {
ret = -EINTR;
goto err;
}
diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c
index 7af3e4b8fe3b..cd26a16dc0ff 100644
--- a/drivers/iio/addac/ad74413r.c
+++ b/drivers/iio/addac/ad74413r.c
@@ -1255,21 +1255,15 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev,
static int ad74413r_parse_channel_configs(struct iio_dev *indio_dev)
{
struct ad74413r_state *st = iio_priv(indio_dev);
- struct fwnode_handle *channel_node = NULL;
int ret;
- fwnode_for_each_available_child_node(dev_fwnode(st->dev), channel_node) {
+ device_for_each_child_node_scoped(st->dev, channel_node) {
ret = ad74413r_parse_channel_config(indio_dev, channel_node);
if (ret)
- goto put_channel_node;
+ return ret;
}
return 0;
-
-put_channel_node:
- fwnode_handle_put(channel_node);
-
- return ret;
}
static int ad74413r_setup_channels(struct iio_dev *indio_dev)
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 5610ba67925e..13b1a858969e 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -195,6 +195,18 @@ static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
block->state = IIO_BLOCK_STATE_DONE;
}
+static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
+{
+ __poll_t flags;
+
+ if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN)
+ flags = EPOLLIN | EPOLLRDNORM;
+ else
+ flags = EPOLLOUT | EPOLLWRNORM;
+
+ wake_up_interruptible_poll(&queue->buffer.pollq, flags);
+}
+
/**
* iio_dma_buffer_block_done() - Indicate that a block has been completed
* @block: The completed block
@@ -212,7 +224,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
spin_unlock_irqrestore(&queue->list_lock, flags);
iio_buffer_block_put_atomic(block);
- wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+ iio_dma_buffer_queue_wake(queue);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
@@ -241,7 +253,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
}
spin_unlock_irqrestore(&queue->list_lock, flags);
- wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+ iio_dma_buffer_queue_wake(queue);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort);
@@ -335,8 +347,24 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
queue->fileio.blocks[i] = block;
}
- block->state = IIO_BLOCK_STATE_QUEUED;
- list_add_tail(&block->head, &queue->incoming);
+ /*
+ * block->bytes_used may have been modified previously, e.g. by
+ * iio_dma_buffer_block_list_abort(). Reset it here to the
+ * block's so that iio_dma_buffer_io() will work.
+ */
+ block->bytes_used = block->size;
+
+ /*
+ * If it's an input buffer, mark the block as queued, and
+ * iio_dma_buffer_enable() will submit it. Otherwise mark it as
+ * done, which means it's ready to be dequeued.
+ */
+ if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
+ block->state = IIO_BLOCK_STATE_QUEUED;
+ list_add_tail(&block->head, &queue->incoming);
+ } else {
+ block->state = IIO_BLOCK_STATE_DONE;
+ }
}
out_unlock:
@@ -488,20 +516,12 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
return block;
}
-/**
- * iio_dma_buffer_read() - DMA buffer read callback
- * @buffer: Buffer to read form
- * @n: Number of bytes to read
- * @user_buffer: Userspace buffer to copy the data to
- *
- * Should be used as the read callback for iio_buffer_access_ops
- * struct for DMA buffers.
- */
-int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
- char __user *user_buffer)
+static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
+ char __user *user_buffer, bool is_from_user)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block;
+ void *addr;
int ret;
if (n < buffer->bytes_per_datum)
@@ -524,8 +544,13 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
n = rounddown(n, buffer->bytes_per_datum);
if (n > block->bytes_used - queue->fileio.pos)
n = block->bytes_used - queue->fileio.pos;
+ addr = block->vaddr + queue->fileio.pos;
- if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) {
+ if (is_from_user)
+ ret = copy_from_user(addr, user_buffer, n);
+ else
+ ret = copy_to_user(user_buffer, addr, n);
+ if (ret) {
ret = -EFAULT;
goto out_unlock;
}
@@ -544,16 +569,49 @@ out_unlock:
return ret;
}
+
+/**
+ * iio_dma_buffer_read() - DMA buffer read callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data to
+ *
+ * Should be used as the read callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
+ char __user *user_buffer)
+{
+ return iio_dma_buffer_io(buffer, n, user_buffer, false);
+}
EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
/**
- * iio_dma_buffer_data_available() - DMA buffer data_available callback
- * @buf: Buffer to check for data availability
+ * iio_dma_buffer_write() - DMA buffer write callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data from
*
- * Should be used as the data_available callback for iio_buffer_access_ops
+ * Should be used as the write callback for iio_buffer_access_ops
* struct for DMA buffers.
*/
-size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+ const char __user *user_buffer)
+{
+ return iio_dma_buffer_io(buffer, n,
+ (__force __user char *)user_buffer, true);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_write);
+
+/**
+ * iio_dma_buffer_usage() - DMA buffer data_available and
+ * space_available callback
+ * @buf: Buffer to check for data availability
+ *
+ * Should be used as the data_available and space_available callbacks for
+ * iio_buffer_access_ops struct for DMA buffers.
+ */
+size_t iio_dma_buffer_usage(struct iio_buffer *buf)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf);
struct iio_dma_buffer_block *block;
@@ -586,7 +644,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
return data_available;
}
-EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available);
+EXPORT_SYMBOL_GPL(iio_dma_buffer_usage);
/**
* iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index a18c1da292af..918f6f8d65b6 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -64,14 +64,25 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(&queue->buffer);
struct dma_async_tx_descriptor *desc;
+ enum dma_transfer_direction dma_dir;
+ size_t max_size;
dma_cookie_t cookie;
- block->bytes_used = min(block->size, dmaengine_buffer->max_size);
- block->bytes_used = round_down(block->bytes_used,
- dmaengine_buffer->align);
+ max_size = min(block->size, dmaengine_buffer->max_size);
+ max_size = round_down(max_size, dmaengine_buffer->align);
+
+ if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
+ block->bytes_used = max_size;
+ dma_dir = DMA_DEV_TO_MEM;
+ } else {
+ dma_dir = DMA_MEM_TO_DEV;
+ }
+
+ if (!block->bytes_used || block->bytes_used > max_size)
+ return -EINVAL;
desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
- block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
+ block->phys_addr, block->bytes_used, dma_dir,
DMA_PREP_INTERRUPT);
if (!desc)
return -ENOMEM;
@@ -112,12 +123,14 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
.read = iio_dma_buffer_read,
+ .write = iio_dma_buffer_write,
.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
.set_length = iio_dma_buffer_set_length,
.request_update = iio_dma_buffer_request_update,
.enable = iio_dma_buffer_enable,
.disable = iio_dma_buffer_disable,
- .data_available = iio_dma_buffer_data_available,
+ .data_available = iio_dma_buffer_usage,
+ .space_available = iio_dma_buffer_usage,
.release = iio_dmaengine_buffer_release,
.modes = INDIO_BUFFER_HARDWARE,
@@ -159,7 +172,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = {
* Once done using the buffer iio_dmaengine_buffer_free() should be used to
* release it.
*/
-struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
+static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
const char *channel)
{
struct dmaengine_buffer *dmaengine_buffer;
@@ -210,7 +223,6 @@ err_free:
kfree(dmaengine_buffer);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER);
/**
* iio_dmaengine_buffer_free() - Free dmaengine buffer
@@ -230,66 +242,64 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
}
EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER);
-static void __devm_iio_dmaengine_buffer_free(void *buffer)
-{
- iio_dmaengine_buffer_free(buffer);
-}
-
-/**
- * devm_iio_dmaengine_buffer_alloc() - Resource-managed iio_dmaengine_buffer_alloc()
- * @dev: Parent device for the buffer
- * @channel: DMA channel name, typically "rx".
- *
- * This allocates a new IIO buffer which internally uses the DMAengine framework
- * to perform its transfers. The parent device will be used to request the DMA
- * channel.
- *
- * The buffer will be automatically de-allocated once the device gets destroyed.
- */
-static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
- const char *channel)
+struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir)
{
struct iio_buffer *buffer;
int ret;
buffer = iio_dmaengine_buffer_alloc(dev, channel);
if (IS_ERR(buffer))
- return buffer;
+ return ERR_CAST(buffer);
- ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
- buffer);
- if (ret)
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ buffer->direction = dir;
+
+ ret = iio_device_attach_buffer(indio_dev, buffer);
+ if (ret) {
+ iio_dmaengine_buffer_free(buffer);
return ERR_PTR(ret);
+ }
return buffer;
}
+EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER);
+
+static void __devm_iio_dmaengine_buffer_free(void *buffer)
+{
+ iio_dmaengine_buffer_free(buffer);
+}
/**
- * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device
+ * devm_iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device
* @dev: Parent device for the buffer
* @indio_dev: IIO device to which to attach this buffer.
* @channel: DMA channel name, typically "rx".
+ * @dir: Direction of buffer (in or out)
*
* This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc()
* and attaches it to an IIO device with iio_device_attach_buffer().
* It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the
* IIO device.
*/
-int devm_iio_dmaengine_buffer_setup(struct device *dev,
- struct iio_dev *indio_dev,
- const char *channel)
+int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir)
{
struct iio_buffer *buffer;
- buffer = devm_iio_dmaengine_buffer_alloc(dev, channel);
+ buffer = iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, dir);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
- indio_dev->modes |= INDIO_BUFFER_HARDWARE;
-
- return iio_device_attach_buffer(indio_dev, buffer);
+ return devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
+ buffer);
}
-EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER);
+EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("DMA buffer for the IIO framework");
diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
index 3b0f9598a7c7..fa205f17bd90 100644
--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
+++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
@@ -70,13 +70,13 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
-static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult)
+static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period)
{
uint32_t period_min, period_max;
/* check that period is acceptable */
- period_min = ts->min_period * mult;
- period_max = ts->max_period * mult;
+ period_min = ts->min_period * ts->mult;
+ period_max = ts->max_period * ts->mult;
if (period > period_min && period < period_max)
return true;
else
@@ -84,15 +84,15 @@ static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t perio
}
static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
- uint32_t mult, uint32_t period)
+ uint32_t period)
{
uint32_t new_chip_period;
- if (!inv_validate_period(ts, period, mult))
+ if (!inv_validate_period(ts, period))
return false;
/* update chip internal period estimation */
- new_chip_period = period / mult;
+ new_chip_period = period / ts->mult;
inv_update_acc(&ts->chip_period, new_chip_period);
ts->period = ts->mult * ts->chip_period.val;
@@ -101,6 +101,9 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts)
{
+ const int64_t period_min = ts->min_period * ts->mult;
+ const int64_t period_max = ts->max_period * ts->mult;
+ int64_t add_max, sub_max;
int64_t delta, jitter;
int64_t adjust;
@@ -108,11 +111,13 @@ static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts)
delta = ts->it.lo - ts->timestamp;
/* adjust timestamp while respecting jitter */
+ add_max = period_max - (int64_t)ts->period;
+ sub_max = period_min - (int64_t)ts->period;
jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter);
if (delta > jitter)
- adjust = jitter;
+ adjust = add_max;
else if (delta < -jitter)
- adjust = -jitter;
+ adjust = sub_max;
else
adjust = 0;
@@ -120,16 +125,14 @@ static void inv_align_timestamp_it(struct inv_sensors_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)
+ size_t sample_nb, int64_t timestamp)
{
struct inv_sensors_timestamp_interval *it;
int64_t delta, interval;
- const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
uint32_t period;
bool valid = false;
- if (fifo_nb == 0)
+ if (sample_nb == 0)
return;
/* update interrupt timestamp and compute chip and sensor periods */
@@ -139,14 +142,14 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
delta = it->up - it->lo;
if (it->lo != 0) {
/* compute period: delta time divided by number of samples */
- period = div_s64(delta, fifo_nb);
- valid = inv_update_chip_period(ts, fifo_mult, period);
+ period = div_s64(delta, sample_nb);
+ valid = inv_update_chip_period(ts, period);
}
/* no previous data, compute theoritical value from interrupt */
if (ts->timestamp == 0) {
/* elapsed time: sensor period * sensor samples number */
- interval = (int64_t)ts->period * (int64_t)sensor_nb;
+ interval = (int64_t)ts->period * (int64_t)sample_nb;
ts->timestamp = it->up - interval;
return;
}
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 34eb40bb9529..3c2bf620f00f 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,43 @@ config AD5624R_SPI
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
+config AD9739A
+ tristate "Analog Devices AD9739A RF DAC spi driver"
+ depends on SPI || COMPILE_TEST
+ select REGMAP_SPI
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices AD9739A Digital-to
+ Analog Converter.
+
+ The driver requires the assistance of the AXI DAC IP core to operate,
+ since SPI is used for configuration only, while data has to be
+ streamed into memory via DMA.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad9739a.
+
+config ADI_AXI_DAC
+ tristate "Analog Devices Generic AXI DAC IP core driver"
+ select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
+ select REGMAP_MMIO
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices Generic
+ AXI DAC IP core. The IP core is used for interfacing with
+ digital-to-analog (DAC) converters that require either a high-speed
+ serial interface (JESD204B/C) or a source synchronous parallel
+ interface (LVDS/CMOS).
+ Typically (for such devices) SPI will be used for configuration only,
+ while this IP core handles the streaming of data into memory via DMA.
+
+ Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called adi-axi-dac.
+
config LTC2688
tristate "Analog Devices LTC2688 DAC spi driver"
depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 55bf89739d14..8432a81a19dc 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
obj-$(CONFIG_AD7293) += ad7293.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_AD9739A) += ad9739a.o
+obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
obj-$(CONFIG_DS4424) += ds4424.o
diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c
index a492e8f2fc0f..8aa942896b5b 100644
--- a/drivers/iio/dac/ad3552r.c
+++ b/drivers/iio/dac/ad3552r.c
@@ -801,51 +801,45 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
u32 ch)
{
struct device *dev = &dac->spi->dev;
- struct fwnode_handle *gain_child;
u32 val;
int err;
u8 addr;
u16 reg = 0, offset;
- gain_child = fwnode_get_named_child_node(child,
- "custom-output-range-config");
- if (!gain_child) {
- dev_err(dev,
- "mandatory custom-output-range-config property missing\n");
- return -EINVAL;
- }
+ struct fwnode_handle *gain_child __free(fwnode_handle)
+ = fwnode_get_named_child_node(child,
+ "custom-output-range-config");
+ if (!gain_child)
+ return dev_err_probe(dev, -EINVAL,
+ "mandatory custom-output-range-config property missing\n");
dac->ch_data[ch].range_override = 1;
reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE);
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
- if (err) {
- dev_err(dev, "mandatory adi,gain-scaling-p property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,gain-scaling-p property missing\n");
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P);
dac->ch_data[ch].p = val;
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
- if (err) {
- dev_err(dev, "mandatory adi,gain-scaling-n property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,gain-scaling-n property missing\n");
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N);
dac->ch_data[ch].n = val;
err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
- if (err) {
- dev_err(dev, "mandatory adi,rfb-ohms property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,rfb-ohms property missing\n");
dac->ch_data[ch].rfb = val;
err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
- if (err) {
- dev_err(dev, "mandatory adi,gain-offset property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,gain-offset property missing\n");
dac->ch_data[ch].gain_offset = val;
offset = abs((s32)val);
@@ -855,21 +849,14 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
addr = AD3552R_REG_ADDR_CH_GAIN(ch);
err = ad3552r_write_reg(dac, addr,
offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
- if (err) {
- dev_err(dev, "Error writing register\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err, "Error writing register\n");
err = ad3552r_write_reg(dac, addr, reg);
- if (err) {
- dev_err(dev, "Error writing register\n");
- goto put_child;
- }
-
-put_child:
- fwnode_handle_put(gain_child);
+ if (err)
+ return dev_err_probe(dev, err, "Error writing register\n");
- return err;
+ return 0;
}
static void ad3552r_reg_disable(void *reg)
@@ -880,7 +867,6 @@ static void ad3552r_reg_disable(void *reg)
static int ad3552r_configure_device(struct ad3552r_desc *dac)
{
struct device *dev = &dac->spi->dev;
- struct fwnode_handle *child;
struct regulator *vref;
int err, cnt = 0, voltage, delta = 100000;
u32 vals[2], val, ch;
@@ -949,53 +935,45 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
return -ENODEV;
}
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
err = fwnode_property_read_u32(child, "reg", &ch);
- if (err) {
- dev_err(dev, "mandatory reg property missing\n");
- goto put_child;
- }
- if (ch >= AD3552R_NUM_CH) {
- dev_err(dev, "reg must be less than %d\n",
- AD3552R_NUM_CH);
- err = -EINVAL;
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory reg property missing\n");
+ if (ch >= AD3552R_NUM_CH)
+ return dev_err_probe(dev, -EINVAL,
+ "reg must be less than %d\n",
+ AD3552R_NUM_CH);
if (fwnode_property_present(child, "adi,output-range-microvolt")) {
err = fwnode_property_read_u32_array(child,
"adi,output-range-microvolt",
vals,
2);
- if (err) {
- dev_err(dev,
+ if (err)
+ return dev_err_probe(dev, err,
"adi,output-range-microvolt property could not be parsed\n");
- goto put_child;
- }
err = ad3552r_find_range(dac->chip_id, vals);
- if (err < 0) {
- dev_err(dev,
- "Invalid adi,output-range-microvolt value\n");
- goto put_child;
- }
+ if (err < 0)
+ return dev_err_probe(dev, err,
+ "Invalid adi,output-range-microvolt value\n");
+
val = err;
err = ad3552r_set_ch_value(dac,
AD3552R_CH_OUTPUT_RANGE_SEL,
ch, val);
if (err)
- goto put_child;
+ return err;
dac->ch_data[ch].range = val;
} else if (dac->chip_id == AD3542R_ID) {
- dev_err(dev,
- "adi,output-range-microvolt is required for ad3542r\n");
- err = -EINVAL;
- goto put_child;
+ return dev_err_probe(dev, -EINVAL,
+ "adi,output-range-microvolt is required for ad3542r\n");
} else {
err = ad3552r_configure_custom_gain(dac, child, ch);
if (err)
- goto put_child;
+ return err;
}
ad3552r_calc_gain_and_offset(dac, ch);
@@ -1003,7 +981,7 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1);
if (err < 0)
- goto put_child;
+ return err;
dac->channels[cnt] = AD3552R_CH_DAC(ch);
++cnt;
@@ -1021,10 +999,6 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
dac->num_ch = cnt;
return 0;
-put_child:
- fwnode_handle_put(child);
-
- return err;
}
static int ad3552r_init(struct ad3552r_desc *dac)
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
index 404865e35460..0b24cb19ac9d 100644
--- a/drivers/iio/dac/ad5755.c
+++ b/drivers/iio/dac/ad5755.c
@@ -809,7 +809,6 @@ static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev)
static int ad5755_probe(struct spi_device *spi)
{
- enum ad5755_type type = spi_get_device_id(spi)->driver_data;
const struct ad5755_platform_data *pdata;
struct iio_dev *indio_dev;
struct ad5755_state *st;
@@ -824,7 +823,7 @@ static int ad5755_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
- st->chip_info = &ad5755_chip_info_tbl[type];
+ st->chip_info = spi_get_device_match_data(spi);
st->spi = spi;
st->pwr_down = 0xf;
@@ -854,21 +853,21 @@ static int ad5755_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5755_id[] = {
- { "ad5755", ID_AD5755 },
- { "ad5755-1", ID_AD5755 },
- { "ad5757", ID_AD5757 },
- { "ad5735", ID_AD5735 },
- { "ad5737", ID_AD5737 },
+ { "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
+ { "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
+ { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] },
+ { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] },
+ { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] },
{}
};
MODULE_DEVICE_TABLE(spi, ad5755_id);
static const struct of_device_id ad5755_of_match[] = {
- { .compatible = "adi,ad5755" },
- { .compatible = "adi,ad5755-1" },
- { .compatible = "adi,ad5757" },
- { .compatible = "adi,ad5735" },
- { .compatible = "adi,ad5737" },
+ { .compatible = "adi,ad5755", &ad5755_chip_info_tbl[ID_AD5755] },
+ { .compatible = "adi,ad5755-1", &ad5755_chip_info_tbl[ID_AD5755] },
+ { .compatible = "adi,ad5757", &ad5755_chip_info_tbl[ID_AD5757] },
+ { .compatible = "adi,ad5735", &ad5755_chip_info_tbl[ID_AD5735] },
+ { .compatible = "adi,ad5737", &ad5755_chip_info_tbl[ID_AD5737] },
{ }
};
MODULE_DEVICE_TABLE(of, ad5755_of_match);
@@ -876,6 +875,7 @@ MODULE_DEVICE_TABLE(of, ad5755_of_match);
static struct spi_driver ad5755_driver = {
.driver = {
.name = "ad5755",
+ .of_match_table = ad5755_of_match,
},
.probe = ad5755_probe,
.id_table = ad5755_id,
diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c
index f66d67402e43..c360ebf5297a 100644
--- a/drivers/iio/dac/ad5770r.c
+++ b/drivers/iio/dac/ad5770r.c
@@ -515,39 +515,32 @@ static int ad5770r_channel_config(struct ad5770r_state *st)
{
int ret, tmp[2], min, max;
unsigned int num;
- struct fwnode_handle *child;
num = device_get_child_node_count(&st->spi->dev);
if (num != AD5770R_MAX_CHANNELS)
return -EINVAL;
- device_for_each_child_node(&st->spi->dev, child) {
+ device_for_each_child_node_scoped(&st->spi->dev, child) {
ret = fwnode_property_read_u32(child, "reg", &num);
if (ret)
- goto err_child_out;
- if (num >= AD5770R_MAX_CHANNELS) {
- ret = -EINVAL;
- goto err_child_out;
- }
+ return ret;
+ if (num >= AD5770R_MAX_CHANNELS)
+ return -EINVAL;
ret = fwnode_property_read_u32_array(child,
"adi,range-microamp",
tmp, 2);
if (ret)
- goto err_child_out;
+ return ret;
min = tmp[0] / 1000;
max = tmp[1] / 1000;
ret = ad5770r_store_output_range(st, min, max, num);
if (ret)
- goto err_child_out;
+ return ret;
}
return 0;
-
-err_child_out:
- fwnode_handle_put(child);
- return ret;
}
static int ad5770r_init(struct ad5770r_state *st)
diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
new file mode 100644
index 000000000000..f56eabe53723
--- /dev/null
+++ b/drivers/iio/dac/ad9739a.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD9739a SPI DAC driver
+ *
+ * Copyright 2015-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define AD9739A_REG_MODE 0
+#define AD9739A_RESET_MASK BIT(5)
+#define AD9739A_REG_FSC_1 0x06
+#define AD9739A_REG_FSC_2 0x07
+#define AD9739A_FSC_MSB GENMASK(1, 0)
+#define AD9739A_REG_DEC_CNT 0x8
+#define AD9739A_NORMAL_MODE 0
+#define AD9739A_MIXED_MODE 2
+#define AD9739A_DAC_DEC GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT1 0x10
+#define AD9739A_RCVR_LOOP_EN_MASK GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT4 0x13
+#define AD9739A_FINE_DEL_SKW_MASK GENMASK(3, 0)
+#define AD9739A_REG_LVDS_REC_STAT9 0x21
+#define AD9739A_RCVR_TRACK_AND_LOCK (BIT(3) | BIT(0))
+#define AD9739A_REG_CROSS_CNT1 0x22
+#define AD9739A_REG_CROSS_CNT2 0x23
+#define AD9739A_REG_PHS_DET 0x24
+#define AD9739A_REG_MU_DUTY 0x25
+#define AD9739A_REG_MU_CNT1 0x26
+#define AD9739A_MU_EN_MASK BIT(0)
+#define AD9739A_MU_GAIN_MASK BIT(1)
+#define AD9739A_REG_MU_CNT2 0x27
+#define AD9739A_REG_MU_CNT3 0x28
+#define AD9739A_REG_MU_CNT4 0x29
+#define AD9739A_MU_CNT4_DEFAULT 0xcb
+#define AD9739A_REG_MU_STAT1 0x2A
+#define AD9739A_MU_LOCK_MASK BIT(0)
+#define AD9739A_REG_ANA_CNT_1 0x32
+#define AD9739A_REG_ID 0x35
+
+#define AD9739A_ID 0x24
+#define AD9739A_REG_IS_RESERVED(reg) \
+ ((reg) == 0x5 || (reg) == 0x9 || (reg) == 0x0E || (reg) == 0x0D || \
+ (reg) == 0x2B || (reg) == 0x2C || (reg) == 0x34)
+
+#define AD9739A_FSC_MIN 8580
+#define AD9739A_FSC_MAX 31700
+#define AD9739A_FSC_RANGE (AD9739A_FSC_MAX - AD9739A_FSC_MIN + 1)
+
+#define AD9739A_MIN_DAC_CLK (1600 * MEGA)
+#define AD9739A_MAX_DAC_CLK (2500 * MEGA)
+#define AD9739A_DAC_CLK_RANGE (AD9739A_MAX_DAC_CLK - AD9739A_MIN_DAC_CLK + 1)
+/* as recommended by the datasheet */
+#define AD9739A_LOCK_N_TRIES 3
+
+struct ad9739a_state {
+ struct iio_backend *back;
+ struct regmap *regmap;
+ unsigned long sample_rate;
+};
+
+static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+ u32 mode;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
+ if (ret)
+ return ret;
+
+ mode = FIELD_GET(AD9739A_DAC_DEC, mode);
+ /* sanity check we get valid values from the HW */
+ if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
+ return -EIO;
+ if (!mode)
+ return AD9739A_NORMAL_MODE;
+
+ /*
+ * We get 2 from the device but for IIO modes, that means 1. Hence the
+ * minus 1.
+ */
+ return AD9739A_MIXED_MODE - 1;
+}
+
+static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, u32 mode)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ /*
+ * On the IIO interface we have 0 and 1 for mode. But for mixed_mode, we
+ * need to write 2 in the device. That's what the below check is about.
+ */
+ if (mode == AD9739A_MIXED_MODE - 1)
+ mode = AD9739A_MIXED_MODE;
+
+ return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
+ AD9739A_DAC_DEC, mode);
+}
+
+static int ad9739a_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->sample_rate;
+ *val2 = 0;
+ return IIO_VAL_INT_64;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad9739a_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ return iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
+}
+
+static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ return iio_backend_data_source_set(st->back, 0,
+ IIO_BACKEND_INTERNAL_CONTINUOS_WAVE);
+}
+
+static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg)
+{
+ if (AD9739A_REG_IS_RESERVED(reg))
+ return false;
+ if (reg > AD9739A_REG_MU_STAT1 && reg < AD9739A_REG_ANA_CNT_1)
+ return false;
+
+ return true;
+}
+
+static int ad9739a_reset(struct device *dev, const struct ad9739a_state *st)
+{
+ struct gpio_desc *gpio;
+ int ret;
+
+ gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ if (gpio) {
+ /* minimum pulse width of 40ns */
+ ndelay(40);
+ gpiod_set_value_cansleep(gpio, 0);
+ return 0;
+ }
+
+ /* bring all registers to their default state */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_MODE, AD9739A_RESET_MASK);
+ if (ret)
+ return ret;
+
+ ndelay(40);
+
+ return regmap_clear_bits(st->regmap, AD9739A_REG_MODE,
+ AD9739A_RESET_MASK);
+}
+
+/*
+ * Recommended values (as per datasheet) for the dac clk common mode voltage
+ * and Mu controller. Look at table 29.
+ */
+static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
+ /* DAC clk common mode voltage */
+ { AD9739A_REG_CROSS_CNT1, 0x0f },
+ { AD9739A_REG_CROSS_CNT2, 0x0f },
+ /* Mu controller configuration */
+ { AD9739A_REG_PHS_DET, 0x30 },
+ { AD9739A_REG_MU_DUTY, 0x80 },
+ { AD9739A_REG_MU_CNT2, 0x44 },
+ { AD9739A_REG_MU_CNT3, 0x6c },
+};
+
+static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
+{
+ unsigned int i = 0, lock, fsc;
+ u32 fsc_raw;
+ int ret;
+
+ ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
+ ARRAY_SIZE(ad9739a_clk_mu_ctrl));
+ if (ret)
+ return ret;
+
+ /*
+ * Try to get the Mu lock. Repeat the below steps AD9739A_LOCK_N_TRIES
+ * (as specified by the datasheet) until we get the lock.
+ */
+ do {
+ ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
+ AD9739A_MU_CNT4_DEFAULT);
+ if (ret)
+ return ret;
+
+ /* Enable the Mu controller search and track mode. */
+ ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT1,
+ AD9739A_MU_EN_MASK | AD9739A_MU_GAIN_MASK);
+ if (ret)
+ return ret;
+
+ /* Ensure the DLL loop is locked */
+ ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
+ lock, lock & AD9739A_MU_LOCK_MASK,
+ 0, 1000);
+ if (ret && ret != -ETIMEDOUT)
+ return ret;
+ } while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+ if (i == AD9739A_LOCK_N_TRIES)
+ return dev_err_probe(dev, ret, "Mu lock timeout\n");
+
+ /* Receiver tracking and lock. Same deal as the Mu controller */
+ i = 0;
+ do {
+ ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
+ AD9739A_FINE_DEL_SKW_MASK,
+ FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
+ if (ret)
+ return ret;
+
+ /* Disable the receiver and the loop. */
+ ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Re-enable the loop so it falls out of lock and begins the
+ * search/track routine again.
+ */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
+ AD9739A_RCVR_LOOP_EN_MASK);
+ if (ret)
+ return ret;
+
+ /* Ensure the DLL loop is locked */
+ ret = regmap_read_poll_timeout(st->regmap,
+ AD9739A_REG_LVDS_REC_STAT9, lock,
+ lock == AD9739A_RCVR_TRACK_AND_LOCK,
+ 0, 1000);
+ if (ret && ret != -ETIMEDOUT)
+ return ret;
+ } while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+ if (i == AD9739A_LOCK_N_TRIES)
+ return dev_err_probe(dev, ret, "Receiver lock timeout\n");
+
+ ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
+ if (ret && ret == -EINVAL)
+ return 0;
+ if (ret)
+ return ret;
+ if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid full scale current(%u) [%u %u]\n",
+ fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
+ /*
+ * IOUTFS is given by
+ * Ioutfs = 0.0226 * FSC + 8.58
+ * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
+ * order to get rid of the fractional.
+ */
+ fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
+
+ ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, AD9739A_REG_FSC_2,
+ AD9739A_FSC_MSB, fsc_raw >> 8);
+}
+
+static const char * const ad9739a_modes_avail[] = { "normal", "mixed-mode" };
+
+static const struct iio_enum ad9739a_modes = {
+ .items = ad9739a_modes_avail,
+ .num_items = ARRAY_SIZE(ad9739a_modes_avail),
+ .get = ad9739a_oper_mode_get,
+ .set = ad9739a_oper_mode_set,
+};
+
+static const struct iio_chan_spec_ext_info ad9739a_ext_info[] = {
+ IIO_ENUM_AVAILABLE("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+ IIO_ENUM("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+ { }
+};
+
+/*
+ * The reason for having two different channels is because we have, in reality,
+ * two sources of data:
+ * ALTVOLTAGE: It's a Continuous Wave that's internally generated by the
+ * backend device.
+ * VOLTAGE: It's the typical data we can have in a DAC device and the source
+ * of it has nothing to do with the backend. The backend will only
+ * forward it into our data interface to be sent out.
+ */
+static struct iio_chan_spec ad9739a_channels[] = {
+ {
+ .type = IIO_ALTVOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .scan_index = -1,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .output = 1,
+ .ext_info = ad9739a_ext_info,
+ .scan_type = {
+ .sign = 's',
+ .storagebits = 16,
+ .realbits = 16,
+ },
+ }
+};
+
+static const struct iio_info ad9739a_info = {
+ .read_raw = ad9739a_read_raw,
+};
+
+static const struct iio_buffer_setup_ops ad9739a_buffer_setup_ops = {
+ .preenable = &ad9739a_buffer_preenable,
+ .postdisable = &ad9739a_buffer_postdisable,
+};
+
+static const struct regmap_config ad9739a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .readable_reg = ad9739a_reg_accessible,
+ .writeable_reg = ad9739a_reg_accessible,
+ .max_register = AD9739A_REG_ID,
+};
+
+static int ad9739a_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad9739a_state *st;
+ unsigned int id;
+ struct clk *clk;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
+
+ st->sample_rate = clk_get_rate(clk);
+ if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
+ AD9739A_DAC_CLK_RANGE))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid dac clk range(%lu) [%lu %lu]\n",
+ st->sample_rate, AD9739A_MIN_DAC_CLK,
+ AD9739A_MAX_DAC_CLK);
+
+ st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
+ if (ret)
+ return ret;
+
+ if (id != AD9739A_ID)
+ dev_warn(dev, "Unrecognized CHIP_ID 0x%X", id);
+
+ ret = ad9739a_reset(dev, st);
+ if (ret)
+ return ret;
+
+ ret = ad9739a_init(dev, st);
+ if (ret)
+ return ret;
+
+ st->back = devm_iio_backend_get(dev, NULL);
+ if (IS_ERR(st->back))
+ return PTR_ERR(st->back);
+
+ ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_extend_chan_spec(indio_dev, st->back,
+ &ad9739a_channels[0]);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_backend_enable(dev, st->back);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "ad9739a";
+ indio_dev->info = &ad9739a_info;
+ indio_dev->channels = ad9739a_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
+ indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ad9739a_of_match[] = {
+ { .compatible = "adi,ad9739a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad9739a_of_match);
+
+static const struct spi_device_id ad9739a_id[] = {
+ {"ad9739a"},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad9739a_id);
+
+static struct spi_driver ad9739a_driver = {
+ .driver = {
+ .name = "ad9739a",
+ .of_match_table = ad9739a_of_match,
+ },
+ .probe = ad9739a_probe,
+ .id_table = ad9739a_id,
+};
+module_spi_driver(ad9739a_driver);
+
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9739 DAC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BACKEND);
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
new file mode 100644
index 000000000000..880d83a014a1
--- /dev/null
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -0,0 +1,635 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices Generic AXI DAC IP core
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ *
+ * Copyright 2016-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/limits.h>
+#include <linux/kstrtox.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/iio/backend.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+
+/*
+ * Register definitions:
+ * https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
+ */
+
+/* Base controls */
+#define AXI_DAC_REG_CONFIG 0x0c
+#define AXI_DDS_DISABLE BIT(6)
+
+ /* DAC controls */
+#define AXI_DAC_REG_RSTN 0x0040
+#define AXI_DAC_RSTN_CE_N BIT(2)
+#define AXI_DAC_RSTN_MMCM_RSTN BIT(1)
+#define AXI_DAC_RSTN_RSTN BIT(0)
+#define AXI_DAC_REG_CNTRL_1 0x0044
+#define AXI_DAC_SYNC BIT(0)
+#define AXI_DAC_REG_CNTRL_2 0x0048
+#define ADI_DAC_R1_MODE BIT(4)
+#define AXI_DAC_DRP_STATUS 0x0074
+#define AXI_DAC_DRP_LOCKED BIT(17)
+/* DAC Channel controls */
+#define AXI_DAC_REG_CHAN_CNTRL_1(c) (0x0400 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_3(c) (0x0408 + (c) * 0x40)
+#define AXI_DAC_SCALE_SIGN BIT(15)
+#define AXI_DAC_SCALE_INT BIT(14)
+#define AXI_DAC_SCALE GENMASK(14, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_2(c) (0x0404 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_4(c) (0x040c + (c) * 0x40)
+#define AXI_DAC_PHASE GENMASK(31, 16)
+#define AXI_DAC_FREQUENCY GENMASK(15, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40)
+#define AXI_DAC_DATA_SEL GENMASK(3, 0)
+
+/* 360 degrees in rad */
+#define AXI_DAC_2_PI_MEGA 6283190
+enum {
+ AXI_DAC_DATA_INTERNAL_TONE,
+ AXI_DAC_DATA_DMA = 2,
+};
+
+struct axi_dac_state {
+ struct regmap *regmap;
+ struct device *dev;
+ /*
+ * lock to protect multiple accesses to the device registers and global
+ * data/variables.
+ */
+ struct mutex lock;
+ u64 dac_clk;
+ u32 reg_config;
+ bool int_tone;
+};
+
+static int axi_dac_enable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int __val;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_MMCM_RSTN);
+ if (ret)
+ return ret;
+ /*
+ * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
+ * designs really use it but if they don't we still get the lock bit
+ * set. So let's do it all the time so the code is generic.
+ */
+ ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val,
+ __val & AXI_DAC_DRP_LOCKED, 100, 1000);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN);
+}
+
+static void axi_dac_disable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ guard(mutex)(&st->lock);
+ regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+}
+
+static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
+ struct iio_dev *indio_dev)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ const char *dma_name;
+
+ if (device_property_read_string(st->dev, "dma-names", &dma_name))
+ dma_name = "tx";
+
+ return iio_dmaengine_buffer_setup_ext(st->dev, indio_dev, dma_name,
+ IIO_BUFFER_DIRECTION_OUT);
+}
+
+static void axi_dac_free_buffer(struct iio_backend *back,
+ struct iio_buffer *buffer)
+{
+ iio_dmaengine_buffer_free(buffer);
+}
+
+enum {
+ AXI_DAC_FREQ_TONE_1,
+ AXI_DAC_FREQ_TONE_2,
+ AXI_DAC_SCALE_TONE_1,
+ AXI_DAC_SCALE_TONE_2,
+ AXI_DAC_PHASE_TONE_1,
+ AXI_DAC_PHASE_TONE_2,
+};
+
+static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
+ unsigned int tone_2, unsigned int *freq)
+{
+ u32 reg, raw;
+ int ret;
+
+ if (!st->dac_clk) {
+ dev_err(st->dev, "Sampling rate is 0...\n");
+ return -EINVAL;
+ }
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
+ *freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
+
+ return 0;
+}
+
+static int axi_dac_frequency_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone_2)
+{
+ unsigned int freq;
+ int ret;
+
+ scoped_guard(mutex, &st->lock) {
+ ret = __axi_dac_frequency_get(st, chan->channel, tone_2, &freq);
+ if (ret)
+ return ret;
+ }
+
+ return sysfs_emit(buf, "%u\n", freq);
+}
+
+static int axi_dac_scale_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone_2)
+{
+ unsigned int scale, sign;
+ int ret, vals[2];
+ u32 reg, raw;
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw);
+ raw = FIELD_GET(AXI_DAC_SCALE, raw);
+ scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT);
+
+ vals[0] = scale / MEGA;
+ vals[1] = scale % MEGA;
+
+ if (sign) {
+ vals[0] *= -1;
+ if (!vals[0])
+ vals[1] *= -1;
+ }
+
+ return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+ vals);
+}
+
+static int axi_dac_phase_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone_2)
+{
+ u32 reg, raw, phase;
+ int ret, vals[2];
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(AXI_DAC_PHASE, raw);
+ phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX);
+
+ vals[0] = phase / MEGA;
+ vals[1] = phase % MEGA;
+
+ return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+ vals);
+}
+
+static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan,
+ u64 sample_rate, unsigned int freq,
+ unsigned int tone_2)
+{
+ u32 reg;
+ u16 raw;
+ int ret;
+
+ if (!sample_rate || freq > sample_rate / 2) {
+ dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n",
+ freq, sample_rate);
+ return -EINVAL;
+ }
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+
+ raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate);
+
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_FREQUENCY, raw);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_frequency_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone_2)
+{
+ unsigned int freq;
+ int ret;
+
+ ret = kstrtou32(buf, 10, &freq);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+ ret = __axi_dac_frequency_set(st, chan->channel, st->dac_clk, freq,
+ tone_2);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_scale_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone_2)
+{
+ int integer, frac, scale;
+ u32 raw = 0, reg;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+ if (ret)
+ return ret;
+
+ scale = integer * MEGA + frac;
+ if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
+ return -EINVAL;
+
+ /* format is 1.1.14 (sign, integer and fractional bits) */
+ if (scale < 0) {
+ raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
+ scale *= -1;
+ }
+
+ raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_write(st->regmap, reg, raw);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_phase_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone_2)
+{
+ int integer, frac, phase;
+ u32 raw, reg;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+ if (ret)
+ return ret;
+
+ phase = integer * MEGA + frac;
+ if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
+ return -EINVAL;
+
+ raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
+ FIELD_PREP(AXI_DAC_PHASE, raw));
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (private) {
+ case AXI_DAC_FREQ_TONE_1:
+ case AXI_DAC_FREQ_TONE_2:
+ return axi_dac_frequency_set(st, chan, buf, len,
+ private == AXI_DAC_FREQ_TONE_2);
+ case AXI_DAC_SCALE_TONE_1:
+ case AXI_DAC_SCALE_TONE_2:
+ return axi_dac_scale_set(st, chan, buf, len,
+ private == AXI_DAC_SCALE_TONE_2);
+ case AXI_DAC_PHASE_TONE_1:
+ case AXI_DAC_PHASE_TONE_2:
+ return axi_dac_phase_set(st, chan, buf, len,
+ private == AXI_DAC_PHASE_TONE_2);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (private) {
+ case AXI_DAC_FREQ_TONE_1:
+ case AXI_DAC_FREQ_TONE_2:
+ return axi_dac_frequency_get(st, chan, buf,
+ private - AXI_DAC_FREQ_TONE_1);
+ case AXI_DAC_SCALE_TONE_1:
+ case AXI_DAC_SCALE_TONE_2:
+ return axi_dac_scale_get(st, chan, buf,
+ private - AXI_DAC_SCALE_TONE_1);
+ case AXI_DAC_PHASE_TONE_1:
+ case AXI_DAC_PHASE_TONE_2:
+ return axi_dac_phase_get(st, chan, buf,
+ private - AXI_DAC_PHASE_TONE_1);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
+ IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
+ IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
+ IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
+ IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
+ IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
+ IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
+ {}
+};
+
+static int axi_dac_extend_chan(struct iio_backend *back,
+ struct iio_chan_spec *chan)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ if (chan->type != IIO_ALTVOLTAGE)
+ return -EINVAL;
+ if (st->reg_config & AXI_DDS_DISABLE)
+ /* nothing to extend */
+ return 0;
+
+ chan->ext_info = axi_dac_ext_info;
+
+ return 0;
+}
+
+static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (data) {
+ case IIO_BACKEND_INTERNAL_CONTINUOS_WAVE:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL,
+ AXI_DAC_DATA_INTERNAL_TONE);
+ case IIO_BACKEND_EXTERNAL:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int freq;
+ int ret, tone;
+
+ if (!sample_rate)
+ return -EINVAL;
+ if (st->reg_config & AXI_DDS_DISABLE)
+ /* sample_rate has no meaning if DDS is disabled */
+ return 0;
+
+ guard(mutex)(&st->lock);
+ /*
+ * If dac_clk is 0 then this must be the first time we're being notified
+ * about the interface sample rate. Hence, just update our internal
+ * variable and bail... If it's not 0, then we get the current DDS
+ * frequency (for the old rate) and update the registers for the new
+ * sample rate.
+ */
+ if (!st->dac_clk) {
+ st->dac_clk = sample_rate;
+ return 0;
+ }
+
+ for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
+ ret = __axi_dac_frequency_get(st, chan, tone, &freq);
+ if (ret)
+ return ret;
+
+ ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
+ if (ret)
+ return ret;
+ }
+
+ st->dac_clk = sample_rate;
+
+ return 0;
+}
+
+static const struct iio_backend_ops axi_dac_generic = {
+ .enable = axi_dac_enable,
+ .disable = axi_dac_disable,
+ .request_buffer = axi_dac_request_buffer,
+ .free_buffer = axi_dac_free_buffer,
+ .extend_chan_spec = axi_dac_extend_chan,
+ .ext_info_set = axi_dac_ext_info_set,
+ .ext_info_get = axi_dac_ext_info_get,
+ .data_source_set = axi_dac_data_source_set,
+ .set_sample_rate = axi_dac_set_sample_rate,
+};
+
+static const struct regmap_config axi_dac_regmap_config = {
+ .val_bits = 32,
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x0800,
+};
+
+static int axi_dac_probe(struct platform_device *pdev)
+{
+ const unsigned int *expected_ver;
+ struct axi_dac_state *st;
+ void __iomem *base;
+ unsigned int ver;
+ struct clk *clk;
+ int ret;
+
+ st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ expected_ver = device_get_match_data(&pdev->dev);
+ if (!expected_ver)
+ return -ENODEV;
+
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ st->dev = &pdev->dev;
+ st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_dac_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ /*
+ * Force disable the core. Up to the frontend to enable us. And we can
+ * still read/write registers...
+ */
+ ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
+ if (ret)
+ return ret;
+
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+ dev_err(&pdev->dev,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
+ ADI_AXI_PCORE_VER_MINOR(*expected_ver),
+ ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+ return -ENODEV;
+ }
+
+ /* Let's get the core read only configuration */
+ ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config);
+ if (ret)
+ return ret;
+
+ /*
+ * In some designs, setting the R1_MODE bit to 0 (which is the default
+ * value) causes all channels of the frontend to be routed to the same
+ * DMA (so they are sampled together). This is for things like
+ * Multiple-Input and Multiple-Output (MIMO). As most of the times we
+ * want independent channels let's override the core's default value and
+ * set the R1_MODE bit.
+ */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE);
+ if (ret)
+ return ret;
+
+ mutex_init(&st->lock);
+ ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+
+ return 0;
+}
+
+static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
+
+static const struct of_device_id axi_dac_of_match[] = {
+ { .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axi_dac_of_match);
+
+static struct platform_driver axi_dac_driver = {
+ .driver = {
+ .name = "adi-axi-dac",
+ .of_match_table = axi_dac_of_match,
+ },
+ .probe = axi_dac_probe,
+};
+module_platform_driver(axi_dac_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
+MODULE_IMPORT_NS(IIO_BACKEND);
diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c
index fc8eb53c65be..c4b1ba30f935 100644
--- a/drivers/iio/dac/ltc2688.c
+++ b/drivers/iio/dac/ltc2688.c
@@ -746,26 +746,21 @@ static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max)
static int ltc2688_channel_config(struct ltc2688_state *st)
{
struct device *dev = &st->spi->dev;
- struct fwnode_handle *child;
u32 reg, clk_input, val, tmp[2];
int ret, span;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
struct ltc2688_chan *chan;
ret = fwnode_property_read_u32(child, "reg", &reg);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return dev_err_probe(dev, ret,
"Failed to get reg property\n");
- }
- if (reg >= LTC2688_DAC_CHANNELS) {
- fwnode_handle_put(child);
+ if (reg >= LTC2688_DAC_CHANNELS)
return dev_err_probe(dev, -EINVAL,
"reg bigger than: %d\n",
LTC2688_DAC_CHANNELS);
- }
val = 0;
chan = &st->channels[reg];
@@ -786,12 +781,10 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
if (!ret) {
span = ltc2688_span_lookup(st, (int)tmp[0] / 1000,
tmp[1] / 1000);
- if (span < 0) {
- fwnode_handle_put(child);
- return dev_err_probe(dev, -EINVAL,
+ if (span < 0)
+ return dev_err_probe(dev, span,
"output range not valid:[%d %d]\n",
tmp[0], tmp[1]);
- }
val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span);
}
@@ -800,17 +793,14 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
&clk_input);
if (!ret) {
if (clk_input >= LTC2688_CH_TGP_MAX) {
- fwnode_handle_put(child);
return dev_err_probe(dev, -EINVAL,
"toggle-dither-input inv value(%d)\n",
clk_input);
}
ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
/*
* 0 means software toggle which is the default mode.
@@ -844,11 +834,9 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg),
val);
- if (ret) {
- fwnode_handle_put(child);
- return dev_err_probe(dev, -EINVAL,
+ if (ret)
+ return dev_err_probe(dev, ret,
"failed to set chan settings\n");
- }
}
return 0;
diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c
index efb1269a77c1..c5162b72951a 100644
--- a/drivers/iio/dac/ti-dac5571.c
+++ b/drivers/iio/dac/ti-dac5571.c
@@ -13,6 +13,7 @@
* https://www.ti.com/lit/ds/symlink/dac5573.pdf
* https://www.ti.com/lit/ds/symlink/dac6573.pdf
* https://www.ti.com/lit/ds/symlink/dac7573.pdf
+ * https://www.ti.com/lit/ds/symlink/dac081c081.pdf
* https://www.ti.com/lit/ds/symlink/dac121c081.pdf
*/
@@ -386,6 +387,7 @@ static void dac5571_remove(struct i2c_client *i2c)
}
static const struct of_device_id dac5571_of_id[] = {
+ {.compatible = "ti,dac081c081", .data = &dac5571_spec[single_8bit] },
{.compatible = "ti,dac121c081", .data = &dac5571_spec[single_12bit] },
{.compatible = "ti,dac5571", .data = &dac5571_spec[single_8bit] },
{.compatible = "ti,dac6571", .data = &dac5571_spec[single_10bit] },
@@ -401,6 +403,7 @@ static const struct of_device_id dac5571_of_id[] = {
MODULE_DEVICE_TABLE(of, dac5571_of_id);
static const struct i2c_device_id dac5571_id[] = {
+ {"dac081c081", (kernel_ulong_t)&dac5571_spec[single_8bit] },
{"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] },
{"dac5571", (kernel_ulong_t)&dac5571_spec[single_8bit] },
{"dac6571", (kernel_ulong_t)&dac5571_spec[single_10bit] },
diff --git a/drivers/iio/frequency/admfm2000.c b/drivers/iio/frequency/admfm2000.c
index c34d79e55a7c..b2263b9afeda 100644
--- a/drivers/iio/frequency/admfm2000.c
+++ b/drivers/iio/frequency/admfm2000.c
@@ -160,26 +160,21 @@ static int admfm2000_channel_config(struct admfm2000_state *st,
{
struct platform_device *pdev = to_platform_device(indio_dev->dev.parent);
struct device *dev = &pdev->dev;
- struct fwnode_handle *child;
struct gpio_desc **dsa;
struct gpio_desc **sw;
int ret, i;
bool mode;
u32 reg;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &reg);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return dev_err_probe(dev, ret,
"Failed to get reg property\n");
- }
- if (reg >= indio_dev->num_channels) {
- fwnode_handle_put(child);
+ if (reg >= indio_dev->num_channels)
return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n",
indio_dev->num_channels);
- }
if (fwnode_property_present(child, "adi,mixer-mode"))
mode = ADMFM2000_MIXER_MODE;
@@ -196,36 +191,29 @@ static int admfm2000_channel_config(struct admfm2000_state *st,
dsa = st->dsa2_gpios;
break;
default:
- fwnode_handle_put(child);
return -EINVAL;
}
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch",
i, GPIOD_OUT_LOW, NULL);
- if (IS_ERR(sw[i])) {
- fwnode_handle_put(child);
+ if (IS_ERR(sw[i]))
return dev_err_probe(dev, PTR_ERR(sw[i]),
"Failed to get gpios\n");
- }
}
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) {
dsa[i] = devm_fwnode_gpiod_get_index(dev, child,
"attenuation", i,
GPIOD_OUT_LOW, NULL);
- if (IS_ERR(dsa[i])) {
- fwnode_handle_put(child);
+ if (IS_ERR(dsa[i]))
return dev_err_probe(dev, PTR_ERR(dsa[i]),
"Failed to get gpios\n");
- }
}
ret = admfm2000_mode(indio_dev, reg, mode);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
}
return 0;
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index 37e619827e8a..6616729af5b7 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -613,6 +613,7 @@ static void max30102_remove(struct i2c_client *client)
}
static const struct i2c_device_id max30102_id[] = {
+ { "max30101", max30105 },
{ "max30102", max30102 },
{ "max30105", max30105 },
{}
@@ -620,6 +621,7 @@ static const struct i2c_device_id max30102_id[] = {
MODULE_DEVICE_TABLE(i2c, max30102_id);
static const struct of_device_id max30102_dt_ids[] = {
+ { .compatible = "maxim,max30101" },
{ .compatible = "maxim,max30102" },
{ .compatible = "maxim,max30105" },
{ }
diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c
index 1e5d0d4797b1..cdc4789213ba 100644
--- a/drivers/iio/humidity/hdc3020.c
+++ b/drivers/iio/humidity/hdc3020.c
@@ -15,11 +15,14 @@
#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
#include <linux/units.h>
#include <asm/unaligned.h>
@@ -68,6 +71,8 @@
struct hdc3020_data {
struct i2c_client *client;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vdd_supply;
/*
* Ensure that the sensor configuration (currently only heater is
* supported) will not be changed during the process of reading
@@ -551,9 +556,53 @@ static const struct iio_info hdc3020_info = {
.write_event_value = hdc3020_write_thresh,
};
-static void hdc3020_stop(void *data)
+static int hdc3020_power_off(struct hdc3020_data *data)
{
- hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO);
+ hdc3020_exec_cmd(data, HDC3020_EXIT_AUTO);
+
+ if (data->reset_gpio)
+ gpiod_set_value_cansleep(data->reset_gpio, 1);
+
+ return regulator_disable(data->vdd_supply);
+}
+
+static int hdc3020_power_on(struct hdc3020_data *data)
+{
+ int ret;
+
+ ret = regulator_enable(data->vdd_supply);
+ if (ret)
+ return ret;
+
+ fsleep(5000);
+
+ if (data->reset_gpio) {
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
+ fsleep(3000);
+ }
+
+ if (data->client->irq) {
+ /*
+ * The alert output is activated by default upon power up,
+ * hardware reset, and soft reset. Clear the status register.
+ */
+ ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
+ if (ret) {
+ hdc3020_power_off(data);
+ return ret;
+ }
+ }
+
+ ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
+ if (ret)
+ hdc3020_power_off(data);
+
+ return ret;
+}
+
+static void hdc3020_exit(void *data)
+{
+ hdc3020_power_off(data);
}
static int hdc3020_probe(struct i2c_client *client)
@@ -569,6 +618,8 @@ static int hdc3020_probe(struct i2c_client *client)
if (!indio_dev)
return -ENOMEM;
+ dev_set_drvdata(&client->dev, indio_dev);
+
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
@@ -580,6 +631,26 @@ static int hdc3020_probe(struct i2c_client *client)
indio_dev->info = &hdc3020_info;
indio_dev->channels = hdc3020_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
+
+ data->vdd_supply = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(data->vdd_supply))
+ return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply),
+ "Unable to get VDD regulator\n");
+
+ data->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(data->reset_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpio),
+ "Cannot get reset GPIO\n");
+
+ ret = hdc3020_power_on(data);
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "Power on failed\n");
+
+ ret = devm_add_action_or_reset(&data->client->dev, hdc3020_exit, data);
+ if (ret)
+ return ret;
+
if (client->irq) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, hdc3020_interrupt_handler,
@@ -588,25 +659,8 @@ static int hdc3020_probe(struct i2c_client *client)
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to request IRQ\n");
-
- /*
- * The alert output is activated by default upon power up,
- * hardware reset, and soft reset. Clear the status register.
- */
- ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
- if (ret)
- return ret;
}
- ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
- if (ret)
- return dev_err_probe(&client->dev, ret,
- "Unable to set up measurement\n");
-
- ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data);
- if (ret)
- return ret;
-
ret = devm_iio_device_register(&data->client->dev, indio_dev);
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to add device");
@@ -614,6 +668,24 @@ static int hdc3020_probe(struct i2c_client *client)
return 0;
}
+static int hdc3020_suspend(struct device *dev)
+{
+ struct iio_dev *iio_dev = dev_get_drvdata(dev);
+ struct hdc3020_data *data = iio_priv(iio_dev);
+
+ return hdc3020_power_off(data);
+}
+
+static int hdc3020_resume(struct device *dev)
+{
+ struct iio_dev *iio_dev = dev_get_drvdata(dev);
+ struct hdc3020_data *data = iio_priv(iio_dev);
+
+ return hdc3020_power_on(data);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(hdc3020_pm_ops, hdc3020_suspend, hdc3020_resume);
+
static const struct i2c_device_id hdc3020_id[] = {
{ "hdc3020" },
{ "hdc3021" },
@@ -633,6 +705,7 @@ MODULE_DEVICE_TABLE(of, hdc3020_dt_ids);
static struct i2c_driver hdc3020_driver = {
.driver = {
.name = "hdc3020",
+ .pm = pm_sleep_ptr(&hdc3020_pm_ops),
.of_match_table = hdc3020_dt_ids,
},
.probe = hdc3020_probe,
diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
index 2a413da87b76..87627d116eff 100644
--- a/drivers/iio/humidity/hts221_core.c
+++ b/drivers/iio/humidity/hts221_core.c
@@ -573,7 +573,7 @@ int hts221_probe(struct device *dev, int irq, const char *name,
if (!iio_dev)
return -ENOMEM;
- dev_set_drvdata(dev, (void *)iio_dev);
+ dev_set_drvdata(dev, iio_dev);
hw = iio_priv(iio_dev);
hw->name = name;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
index 0e290c807b0f..c4ac91f6bafe 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
@@ -13,6 +13,7 @@
#include <linux/regulator/consumer.h>
#include <linux/pm.h>
#include <linux/iio/iio.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
#include "inv_icm42600_buffer.h"
@@ -21,7 +22,9 @@ enum inv_icm42600_chip {
INV_CHIP_ICM42600,
INV_CHIP_ICM42602,
INV_CHIP_ICM42605,
+ INV_CHIP_ICM42686,
INV_CHIP_ICM42622,
+ INV_CHIP_ICM42688,
INV_CHIP_ICM42631,
INV_CHIP_NB,
};
@@ -56,6 +59,17 @@ enum inv_icm42600_gyro_fs {
INV_ICM42600_GYRO_FS_15_625DPS,
INV_ICM42600_GYRO_FS_NB,
};
+enum inv_icm42686_gyro_fs {
+ INV_ICM42686_GYRO_FS_4000DPS,
+ INV_ICM42686_GYRO_FS_2000DPS,
+ INV_ICM42686_GYRO_FS_1000DPS,
+ INV_ICM42686_GYRO_FS_500DPS,
+ INV_ICM42686_GYRO_FS_250DPS,
+ INV_ICM42686_GYRO_FS_125DPS,
+ INV_ICM42686_GYRO_FS_62_5DPS,
+ INV_ICM42686_GYRO_FS_31_25DPS,
+ INV_ICM42686_GYRO_FS_NB,
+};
/* accelerometer fullscale values */
enum inv_icm42600_accel_fs {
@@ -65,6 +79,14 @@ enum inv_icm42600_accel_fs {
INV_ICM42600_ACCEL_FS_2G,
INV_ICM42600_ACCEL_FS_NB,
};
+enum inv_icm42686_accel_fs {
+ INV_ICM42686_ACCEL_FS_32G,
+ INV_ICM42686_ACCEL_FS_16G,
+ INV_ICM42686_ACCEL_FS_8G,
+ INV_ICM42686_ACCEL_FS_4G,
+ INV_ICM42686_ACCEL_FS_2G,
+ INV_ICM42686_ACCEL_FS_NB,
+};
/* ODR suffixed by LN or LP are Low-Noise or Low-Power mode only */
enum inv_icm42600_odr {
@@ -150,6 +172,19 @@ struct inv_icm42600_state {
} timestamp;
};
+
+/**
+ * struct inv_icm42600_sensor_state - sensor state variables
+ * @scales: table of scales.
+ * @scales_len: length (nb of items) of the scales table.
+ * @ts: timestamp module states.
+ */
+struct inv_icm42600_sensor_state {
+ const int *scales;
+ size_t scales_len;
+ struct inv_sensors_timestamp ts;
+};
+
/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
/* Bank selection register, available in all banks */
@@ -303,7 +338,9 @@ struct inv_icm42600_state {
#define INV_ICM42600_WHOAMI_ICM42600 0x40
#define INV_ICM42600_WHOAMI_ICM42602 0x41
#define INV_ICM42600_WHOAMI_ICM42605 0x42
+#define INV_ICM42600_WHOAMI_ICM42686 0x44
#define INV_ICM42600_WHOAMI_ICM42622 0x46
+#define INV_ICM42600_WHOAMI_ICM42688 0x47
#define INV_ICM42600_WHOAMI_ICM42631 0x5C
/* User bank 1 (MSB 0x10) */
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
index f67bd5a39beb..83d8504ebfff 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
@@ -99,7 +99,8 @@ 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_sensors_timestamp *ts = iio_priv(indio_dev);
+ struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
unsigned int fifo_en = 0;
unsigned int sleep_temp = 0;
@@ -210,33 +211,54 @@ static const int inv_icm42600_accel_scale[] = {
[2 * INV_ICM42600_ACCEL_FS_2G] = 0,
[2 * INV_ICM42600_ACCEL_FS_2G + 1] = 598550,
};
+static const int inv_icm42686_accel_scale[] = {
+ /* +/- 32G => 0.009576807 m/s-2 */
+ [2 * INV_ICM42686_ACCEL_FS_32G] = 0,
+ [2 * INV_ICM42686_ACCEL_FS_32G + 1] = 9576807,
+ /* +/- 16G => 0.004788403 m/s-2 */
+ [2 * INV_ICM42686_ACCEL_FS_16G] = 0,
+ [2 * INV_ICM42686_ACCEL_FS_16G + 1] = 4788403,
+ /* +/- 8G => 0.002394202 m/s-2 */
+ [2 * INV_ICM42686_ACCEL_FS_8G] = 0,
+ [2 * INV_ICM42686_ACCEL_FS_8G + 1] = 2394202,
+ /* +/- 4G => 0.001197101 m/s-2 */
+ [2 * INV_ICM42686_ACCEL_FS_4G] = 0,
+ [2 * INV_ICM42686_ACCEL_FS_4G + 1] = 1197101,
+ /* +/- 2G => 0.000598550 m/s-2 */
+ [2 * INV_ICM42686_ACCEL_FS_2G] = 0,
+ [2 * INV_ICM42686_ACCEL_FS_2G + 1] = 598550,
+};
-static int inv_icm42600_accel_read_scale(struct inv_icm42600_state *st,
+static int inv_icm42600_accel_read_scale(struct iio_dev *indio_dev,
int *val, int *val2)
{
+ struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
unsigned int idx;
idx = st->conf.accel.fs;
- *val = inv_icm42600_accel_scale[2 * idx];
- *val2 = inv_icm42600_accel_scale[2 * idx + 1];
+ *val = accel_st->scales[2 * idx];
+ *val2 = accel_st->scales[2 * idx + 1];
return IIO_VAL_INT_PLUS_NANO;
}
-static int inv_icm42600_accel_write_scale(struct inv_icm42600_state *st,
+static int inv_icm42600_accel_write_scale(struct iio_dev *indio_dev,
int val, int val2)
{
+ struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42600_sensor_state *accel_st = 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;
int ret;
- for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_accel_scale); idx += 2) {
- if (val == inv_icm42600_accel_scale[idx] &&
- val2 == inv_icm42600_accel_scale[idx + 1])
+ for (idx = 0; idx < accel_st->scales_len; idx += 2) {
+ if (val == accel_st->scales[idx] &&
+ val2 == accel_st->scales[idx + 1])
break;
}
- if (idx >= ARRAY_SIZE(inv_icm42600_accel_scale))
+ if (idx >= accel_st->scales_len)
return -EINVAL;
conf.fs = idx / 2;
@@ -309,7 +331,8 @@ 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_sensors_timestamp *ts = iio_priv(indio_dev);
+ struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
struct device *dev = regmap_get_device(st->map);
unsigned int idx;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
@@ -565,7 +588,7 @@ static int inv_icm42600_accel_read_raw(struct iio_dev *indio_dev,
*val = data;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- return inv_icm42600_accel_read_scale(st, val, val2);
+ return inv_icm42600_accel_read_scale(indio_dev, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
return inv_icm42600_accel_read_odr(st, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
@@ -580,14 +603,16 @@ static int inv_icm42600_accel_read_avail(struct iio_dev *indio_dev,
const int **vals,
int *type, int *length, long mask)
{
+ struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
+
if (chan->type != IIO_ACCEL)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- *vals = inv_icm42600_accel_scale;
+ *vals = accel_st->scales;
*type = IIO_VAL_INT_PLUS_NANO;
- *length = ARRAY_SIZE(inv_icm42600_accel_scale);
+ *length = accel_st->scales_len;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = inv_icm42600_accel_odr;
@@ -618,7 +643,7 @@ static int inv_icm42600_accel_write_raw(struct iio_dev *indio_dev,
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
- ret = inv_icm42600_accel_write_scale(st, val, val2);
+ ret = inv_icm42600_accel_write_scale(indio_dev, val, val2);
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -705,8 +730,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_sensor_state *accel_st;
struct inv_sensors_timestamp_chip ts_chip;
- struct inv_sensors_timestamp *ts;
struct iio_dev *indio_dev;
int ret;
@@ -714,9 +739,21 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
if (!name)
return ERR_PTR(-ENOMEM);
- indio_dev = devm_iio_device_alloc(dev, sizeof(*ts));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st));
if (!indio_dev)
return ERR_PTR(-ENOMEM);
+ accel_st = iio_priv(indio_dev);
+
+ switch (st->chip) {
+ case INV_CHIP_ICM42686:
+ accel_st->scales = inv_icm42686_accel_scale;
+ accel_st->scales_len = ARRAY_SIZE(inv_icm42686_accel_scale);
+ break;
+ default:
+ accel_st->scales = inv_icm42600_accel_scale;
+ accel_st->scales_len = ARRAY_SIZE(inv_icm42600_accel_scale);
+ break;
+ }
/*
* clock period is 32kHz (31250ns)
@@ -725,8 +762,7 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
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_sensors_timestamp_init(ts, &ts_chip);
+ inv_sensors_timestamp_init(&accel_st->ts, &ts_chip);
iio_device_set_drvdata(indio_dev, st);
indio_dev->name = name;
@@ -751,7 +787,8 @@ 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_sensors_timestamp *ts = iio_priv(indio_dev);
+ struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
ssize_t i, size;
unsigned int no;
const void *accel, *gyro, *timestamp;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
index b52f328fd26c..63b85ec88c13 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
@@ -276,7 +276,8 @@ 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_sensors_timestamp *ts = iio_priv(indio_dev);
+ struct inv_icm42600_sensor_state *sensor_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &sensor_st->ts;
pm_runtime_get_sync(dev);
@@ -502,6 +503,8 @@ 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_sensor_state *gyro_st = iio_priv(st->indio_gyro);
+ struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel);
struct inv_sensors_timestamp *ts;
int ret;
@@ -509,20 +512,20 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
return 0;
/* handle gyroscope timestamp and FIFO data parsing */
- ts = iio_priv(st->indio_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) {
+ ts = &gyro_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro,
+ st->timestamp.gyro);
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
return ret;
}
/* handle accelerometer timestamp and FIFO data parsing */
- ts = iio_priv(st->indio_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) {
+ ts = &accel_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel,
+ st->timestamp.accel);
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
return ret;
@@ -534,6 +537,8 @@ 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_sensor_state *gyro_st = iio_priv(st->indio_gyro);
+ struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel);
struct inv_sensors_timestamp *ts;
int64_t gyro_ts, accel_ts;
int ret;
@@ -549,20 +554,16 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
return 0;
if (st->fifo.nb.gyro > 0) {
- ts = iio_priv(st->indio_gyro);
- inv_sensors_timestamp_interrupt(ts, st->fifo.period,
- st->fifo.nb.total, st->fifo.nb.gyro,
- gyro_ts);
+ ts = &gyro_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
return ret;
}
if (st->fifo.nb.accel > 0) {
- ts = iio_priv(st->indio_accel);
- inv_sensors_timestamp_interrupt(ts, st->fifo.period,
- st->fifo.nb.total, st->fifo.nb.accel,
- accel_ts);
+ ts = &accel_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
return ret;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
index a5e81906e37e..96116a68ab29 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
@@ -66,6 +66,22 @@ static const struct inv_icm42600_conf inv_icm42600_default_conf = {
.temp_en = false,
};
+static const struct inv_icm42600_conf inv_icm42686_default_conf = {
+ .gyro = {
+ .mode = INV_ICM42600_SENSOR_MODE_OFF,
+ .fs = INV_ICM42686_GYRO_FS_4000DPS,
+ .odr = INV_ICM42600_ODR_50HZ,
+ .filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
+ },
+ .accel = {
+ .mode = INV_ICM42600_SENSOR_MODE_OFF,
+ .fs = INV_ICM42686_ACCEL_FS_32G,
+ .odr = INV_ICM42600_ODR_50HZ,
+ .filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
+ },
+ .temp_en = false,
+};
+
static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = {
[INV_CHIP_ICM42600] = {
.whoami = INV_ICM42600_WHOAMI_ICM42600,
@@ -82,11 +98,21 @@ static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = {
.name = "icm42605",
.conf = &inv_icm42600_default_conf,
},
+ [INV_CHIP_ICM42686] = {
+ .whoami = INV_ICM42600_WHOAMI_ICM42686,
+ .name = "icm42686",
+ .conf = &inv_icm42686_default_conf,
+ },
[INV_CHIP_ICM42622] = {
.whoami = INV_ICM42600_WHOAMI_ICM42622,
.name = "icm42622",
.conf = &inv_icm42600_default_conf,
},
+ [INV_CHIP_ICM42688] = {
+ .whoami = INV_ICM42600_WHOAMI_ICM42688,
+ .name = "icm42688",
+ .conf = &inv_icm42600_default_conf,
+ },
[INV_CHIP_ICM42631] = {
.whoami = INV_ICM42600_WHOAMI_ICM42631,
.name = "icm42631",
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
index 3df0a715e885..e6f8de80128c 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
@@ -99,7 +99,8 @@ 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_sensors_timestamp *ts = iio_priv(indio_dev);
+ struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
unsigned int fifo_en = 0;
unsigned int sleep_gyro = 0;
@@ -222,33 +223,63 @@ static const int inv_icm42600_gyro_scale[] = {
[2 * INV_ICM42600_GYRO_FS_15_625DPS] = 0,
[2 * INV_ICM42600_GYRO_FS_15_625DPS + 1] = 8322,
};
+static const int inv_icm42686_gyro_scale[] = {
+ /* +/- 4000dps => 0.002130529 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_4000DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_4000DPS + 1] = 2130529,
+ /* +/- 2000dps => 0.001065264 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_2000DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_2000DPS + 1] = 1065264,
+ /* +/- 1000dps => 0.000532632 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_1000DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_1000DPS + 1] = 532632,
+ /* +/- 500dps => 0.000266316 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_500DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_500DPS + 1] = 266316,
+ /* +/- 250dps => 0.000133158 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_250DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_250DPS + 1] = 133158,
+ /* +/- 125dps => 0.000066579 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_125DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_125DPS + 1] = 66579,
+ /* +/- 62.5dps => 0.000033290 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_62_5DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_62_5DPS + 1] = 33290,
+ /* +/- 31.25dps => 0.000016645 rad/s */
+ [2 * INV_ICM42686_GYRO_FS_31_25DPS] = 0,
+ [2 * INV_ICM42686_GYRO_FS_31_25DPS + 1] = 16645,
+};
-static int inv_icm42600_gyro_read_scale(struct inv_icm42600_state *st,
+static int inv_icm42600_gyro_read_scale(struct iio_dev *indio_dev,
int *val, int *val2)
{
+ struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev);
unsigned int idx;
idx = st->conf.gyro.fs;
- *val = inv_icm42600_gyro_scale[2 * idx];
- *val2 = inv_icm42600_gyro_scale[2 * idx + 1];
+ *val = gyro_st->scales[2 * idx];
+ *val2 = gyro_st->scales[2 * idx + 1];
return IIO_VAL_INT_PLUS_NANO;
}
-static int inv_icm42600_gyro_write_scale(struct inv_icm42600_state *st,
+static int inv_icm42600_gyro_write_scale(struct iio_dev *indio_dev,
int val, int val2)
{
+ struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42600_sensor_state *gyro_st = 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;
int ret;
- for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_scale); idx += 2) {
- if (val == inv_icm42600_gyro_scale[idx] &&
- val2 == inv_icm42600_gyro_scale[idx + 1])
+ for (idx = 0; idx < gyro_st->scales_len; idx += 2) {
+ if (val == gyro_st->scales[idx] &&
+ val2 == gyro_st->scales[idx + 1])
break;
}
- if (idx >= ARRAY_SIZE(inv_icm42600_gyro_scale))
+ if (idx >= gyro_st->scales_len)
return -EINVAL;
conf.fs = idx / 2;
@@ -321,7 +352,8 @@ 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_sensors_timestamp *ts = iio_priv(indio_dev);
+ struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
struct device *dev = regmap_get_device(st->map);
unsigned int idx;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
@@ -576,7 +608,7 @@ static int inv_icm42600_gyro_read_raw(struct iio_dev *indio_dev,
*val = data;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- return inv_icm42600_gyro_read_scale(st, val, val2);
+ return inv_icm42600_gyro_read_scale(indio_dev, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
return inv_icm42600_gyro_read_odr(st, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
@@ -591,14 +623,16 @@ static int inv_icm42600_gyro_read_avail(struct iio_dev *indio_dev,
const int **vals,
int *type, int *length, long mask)
{
+ struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev);
+
if (chan->type != IIO_ANGL_VEL)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- *vals = inv_icm42600_gyro_scale;
+ *vals = gyro_st->scales;
*type = IIO_VAL_INT_PLUS_NANO;
- *length = ARRAY_SIZE(inv_icm42600_gyro_scale);
+ *length = gyro_st->scales_len;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = inv_icm42600_gyro_odr;
@@ -629,7 +663,7 @@ static int inv_icm42600_gyro_write_raw(struct iio_dev *indio_dev,
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
- ret = inv_icm42600_gyro_write_scale(st, val, val2);
+ ret = inv_icm42600_gyro_write_scale(indio_dev, val, val2);
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -716,8 +750,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_sensor_state *gyro_st;
struct inv_sensors_timestamp_chip ts_chip;
- struct inv_sensors_timestamp *ts;
struct iio_dev *indio_dev;
int ret;
@@ -725,9 +759,21 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
if (!name)
return ERR_PTR(-ENOMEM);
- indio_dev = devm_iio_device_alloc(dev, sizeof(*ts));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
if (!indio_dev)
return ERR_PTR(-ENOMEM);
+ gyro_st = iio_priv(indio_dev);
+
+ switch (st->chip) {
+ case INV_CHIP_ICM42686:
+ gyro_st->scales = inv_icm42686_gyro_scale;
+ gyro_st->scales_len = ARRAY_SIZE(inv_icm42686_gyro_scale);
+ break;
+ default:
+ gyro_st->scales = inv_icm42600_gyro_scale;
+ gyro_st->scales_len = ARRAY_SIZE(inv_icm42600_gyro_scale);
+ break;
+ }
/*
* clock period is 32kHz (31250ns)
@@ -736,8 +782,7 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
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_sensors_timestamp_init(ts, &ts_chip);
+ inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip);
iio_device_set_drvdata(indio_dev, st);
indio_dev->name = name;
@@ -763,7 +808,8 @@ 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_sensors_timestamp *ts = iio_priv(indio_dev);
+ struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
ssize_t i, size;
unsigned int no;
const void *accel, *gyro, *timestamp;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
index 1af559403ba6..8d33504d770f 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
@@ -82,9 +82,15 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
.compatible = "invensense,icm42605",
.data = (void *)INV_CHIP_ICM42605,
}, {
+ .compatible = "invensense,icm42686",
+ .data = (void *)INV_CHIP_ICM42686,
+ }, {
.compatible = "invensense,icm42622",
.data = (void *)INV_CHIP_ICM42622,
}, {
+ .compatible = "invensense,icm42688",
+ .data = (void *)INV_CHIP_ICM42688,
+ }, {
.compatible = "invensense,icm42631",
.data = (void *)INV_CHIP_ICM42631,
},
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
index 6be4ac794937..cc2bf1799a46 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
@@ -78,9 +78,15 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
.compatible = "invensense,icm42605",
.data = (void *)INV_CHIP_ICM42605,
}, {
+ .compatible = "invensense,icm42686",
+ .data = (void *)INV_CHIP_ICM42686,
+ }, {
.compatible = "invensense,icm42622",
.data = (void *)INV_CHIP_ICM42622,
}, {
+ .compatible = "invensense,icm42688",
+ .data = (void *)INV_CHIP_ICM42688,
+ }, {
.compatible = "invensense,icm42631",
.data = (void *)INV_CHIP_ICM42631,
},
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
index 0e94e5335e93..14d95f34e981 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -15,6 +15,8 @@
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
@@ -287,7 +289,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
};
static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep,
- int clock, int temp_dis)
+ bool cycle, int clock, int temp_dis)
{
u8 val;
@@ -301,6 +303,8 @@ static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep
val |= INV_MPU6050_BIT_TEMP_DIS;
if (sleep)
val |= INV_MPU6050_BIT_SLEEP;
+ if (cycle)
+ val |= INV_MPU6050_BIT_CYCLE;
dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val);
return regmap_write(st->map, st->reg->pwr_mgmt_1, val);
@@ -316,7 +320,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
case INV_MPU6000:
case INV_MPU9150:
/* old chips: switch clock manually */
- ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1);
+ ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, clock, -1);
if (ret)
return ret;
st->chip_config.clk = clock;
@@ -332,7 +336,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
unsigned int mask)
{
- unsigned int sleep;
+ unsigned int sleep, val;
u8 pwr_mgmt2, user_ctrl;
int ret;
@@ -345,12 +349,20 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
mask &= ~INV_MPU6050_SENSOR_TEMP;
if (mask & INV_MPU6050_SENSOR_MAGN && en == st->chip_config.magn_en)
mask &= ~INV_MPU6050_SENSOR_MAGN;
+ if (mask & INV_MPU6050_SENSOR_WOM && en == st->chip_config.wom_en)
+ mask &= ~INV_MPU6050_SENSOR_WOM;
+
+ /* force accel on if WoM is on and not going off */
+ if (!en && (mask & INV_MPU6050_SENSOR_ACCL) && st->chip_config.wom_en &&
+ !(mask & INV_MPU6050_SENSOR_WOM))
+ mask &= ~INV_MPU6050_SENSOR_ACCL;
+
if (mask == 0)
return 0;
/* turn on/off temperature sensor */
if (mask & INV_MPU6050_SENSOR_TEMP) {
- ret = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, !en);
+ ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, !en);
if (ret)
return ret;
st->chip_config.temp_en = en;
@@ -439,6 +451,16 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
}
}
+ /* enable/disable accel intelligence control */
+ if (mask & INV_MPU6050_SENSOR_WOM) {
+ val = en ? INV_MPU6500_BIT_ACCEL_INTEL_EN |
+ INV_MPU6500_BIT_ACCEL_INTEL_MODE : 0;
+ ret = regmap_write(st->map, INV_MPU6500_REG_ACCEL_INTEL_CTRL, val);
+ if (ret)
+ return ret;
+ st->chip_config.wom_en = en;
+ }
+
return 0;
}
@@ -447,7 +469,7 @@ static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st,
{
int result;
- result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1);
+ result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, false, -1, -1);
if (result)
return result;
@@ -477,22 +499,9 @@ static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st,
return regmap_write(st->map, st->reg->gyro_config, data);
}
-/*
- * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
- *
- * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
- * MPU6500 and above have a dedicated register for accelerometer
- */
-static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
- enum inv_mpu6050_filter_e val)
+static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st,
+ enum inv_mpu6050_filter_e val)
{
- int result;
-
- result = regmap_write(st->map, st->reg->lpf, val);
- if (result)
- return result;
-
- /* set accel lpf */
switch (st->chip_type) {
case INV_MPU6050:
case INV_MPU6000:
@@ -512,6 +521,25 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
}
/*
+ * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
+ *
+ * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
+ * MPU6500 and above have a dedicated register for accelerometer
+ */
+static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
+ enum inv_mpu6050_filter_e val)
+{
+ int result;
+
+ result = regmap_write(st->map, st->reg->lpf, val);
+ if (result)
+ return result;
+
+ /* set accel lpf */
+ return inv_mpu6050_set_accel_lpf_regs(st, val);
+}
+
+/*
* inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
*
* Initial configuration:
@@ -893,6 +921,317 @@ error_write_raw_unlock:
return result;
}
+static u64 inv_mpu6050_convert_wom_to_roc(unsigned int threshold, unsigned int freq_div)
+{
+ /* 4mg per LSB converted in m/s² in micro (1000000) */
+ const unsigned int convert = 4U * 9807U;
+ u64 value;
+
+ value = threshold * convert;
+
+ /* compute the differential by multiplying by the frequency */
+ return div_u64(value * INV_MPU6050_INTERNAL_FREQ_HZ, freq_div);
+}
+
+static unsigned int inv_mpu6050_convert_roc_to_wom(u64 roc, unsigned int freq_div)
+{
+ /* 4mg per LSB converted in m/s² in micro (1000000) */
+ const unsigned int convert = 4U * 9807U;
+ u64 value;
+
+ /* return 0 only if roc is 0 */
+ if (roc == 0)
+ return 0;
+
+ value = div_u64(roc * freq_div, convert * INV_MPU6050_INTERNAL_FREQ_HZ);
+
+ /* limit value to 8 bits and prevent 0 */
+ return min(255, max(1, value));
+}
+
+static int inv_mpu6050_set_wom_int(struct inv_mpu6050_state *st, bool on)
+{
+ unsigned int reg_val, val;
+
+ switch (st->chip_type) {
+ case INV_MPU6050:
+ case INV_MPU6500:
+ case INV_MPU6515:
+ case INV_MPU6880:
+ case INV_MPU6000:
+ case INV_MPU9150:
+ case INV_MPU9250:
+ case INV_MPU9255:
+ reg_val = INV_MPU6500_BIT_WOM_INT_EN;
+ break;
+ default:
+ reg_val = INV_ICM20608_BIT_WOM_INT_EN;
+ break;
+ }
+
+ val = on ? reg_val : 0;
+
+ return regmap_update_bits(st->map, st->reg->int_enable, reg_val, val);
+}
+
+static int inv_mpu6050_set_wom_threshold(struct inv_mpu6050_state *st, u64 value,
+ unsigned int freq_div)
+{
+ unsigned int threshold;
+ int result;
+
+ /* convert roc to wom threshold and convert back to handle clipping */
+ threshold = inv_mpu6050_convert_roc_to_wom(value, freq_div);
+ value = inv_mpu6050_convert_wom_to_roc(threshold, freq_div);
+
+ dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold);
+
+ switch (st->chip_type) {
+ case INV_ICM20609:
+ case INV_ICM20689:
+ case INV_ICM20600:
+ case INV_ICM20602:
+ case INV_ICM20690:
+ st->data[0] = threshold;
+ st->data[1] = threshold;
+ st->data[2] = threshold;
+ result = regmap_bulk_write(st->map, INV_ICM20609_REG_ACCEL_WOM_X_THR,
+ st->data, 3);
+ break;
+ default:
+ result = regmap_write(st->map, INV_MPU6500_REG_WOM_THRESHOLD, threshold);
+ break;
+ }
+ if (result)
+ return result;
+
+ st->chip_config.roc_threshold = value;
+
+ return 0;
+}
+
+static int inv_mpu6050_set_lp_odr(struct inv_mpu6050_state *st, unsigned int freq_div,
+ unsigned int *lp_div)
+{
+ static const unsigned int freq_dividers[] = {2, 4, 8, 16, 32, 64, 128, 256};
+ static const unsigned int reg_values[] = {
+ INV_MPU6050_LPOSC_500HZ, INV_MPU6050_LPOSC_250HZ,
+ INV_MPU6050_LPOSC_125HZ, INV_MPU6050_LPOSC_62HZ,
+ INV_MPU6050_LPOSC_31HZ, INV_MPU6050_LPOSC_16HZ,
+ INV_MPU6050_LPOSC_8HZ, INV_MPU6050_LPOSC_4HZ,
+ };
+ unsigned int val, i;
+
+ switch (st->chip_type) {
+ case INV_ICM20609:
+ case INV_ICM20689:
+ case INV_ICM20600:
+ case INV_ICM20602:
+ case INV_ICM20690:
+ /* nothing to do */
+ *lp_div = INV_MPU6050_FREQ_DIVIDER(st);
+ return 0;
+ default:
+ break;
+ }
+
+ /* found the nearest superior frequency divider */
+ i = ARRAY_SIZE(reg_values) - 1;
+ val = reg_values[i];
+ *lp_div = freq_dividers[i];
+ for (i = 0; i < ARRAY_SIZE(freq_dividers); ++i) {
+ if (freq_div <= freq_dividers[i]) {
+ val = reg_values[i];
+ *lp_div = freq_dividers[i];
+ break;
+ }
+ }
+
+ dev_dbg(regmap_get_device(st->map), "lp_odr: 0x%x\n", val);
+ return regmap_write(st->map, INV_MPU6500_REG_LP_ODR, val);
+}
+
+static int inv_mpu6050_set_wom_lp(struct inv_mpu6050_state *st, bool on)
+{
+ unsigned int lp_div;
+ int result;
+
+ if (on) {
+ /* set low power ODR */
+ result = inv_mpu6050_set_lp_odr(st, INV_MPU6050_FREQ_DIVIDER(st), &lp_div);
+ if (result)
+ return result;
+ /* disable accel low pass filter */
+ result = inv_mpu6050_set_accel_lpf_regs(st, INV_MPU6050_FILTER_NOLPF);
+ if (result)
+ return result;
+ /* update wom threshold with new low-power frequency divider */
+ result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, lp_div);
+ if (result)
+ return result;
+ /* set cycle mode */
+ result = inv_mpu6050_pwr_mgmt_1_write(st, false, true, -1, -1);
+ } else {
+ /* disable cycle mode */
+ result = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, -1);
+ if (result)
+ return result;
+ /* restore wom threshold */
+ result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
+ INV_MPU6050_FREQ_DIVIDER(st));
+ if (result)
+ return result;
+ /* restore accel low pass filter */
+ result = inv_mpu6050_set_accel_lpf_regs(st, st->chip_config.lpf);
+ }
+
+ return result;
+}
+
+static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en)
+{
+ struct device *pdev = regmap_get_device(st->map);
+ unsigned int mask;
+ int result;
+
+ if (en) {
+ result = pm_runtime_resume_and_get(pdev);
+ if (result)
+ return result;
+
+ mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_WOM;
+ result = inv_mpu6050_switch_engine(st, true, mask);
+ if (result)
+ goto error_suspend;
+
+ result = inv_mpu6050_set_wom_int(st, true);
+ if (result)
+ goto error_suspend;
+ } else {
+ result = inv_mpu6050_set_wom_int(st, false);
+ if (result)
+ dev_err(pdev, "error %d disabling WoM interrupt bit", result);
+
+ /* disable only WoM and let accel be disabled by autosuspend */
+ result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_WOM);
+ if (result) {
+ dev_err(pdev, "error %d disabling WoM force off", result);
+ /* force WoM off */
+ st->chip_config.wom_en = false;
+ }
+
+ pm_runtime_mark_last_busy(pdev);
+ pm_runtime_put_autosuspend(pdev);
+ }
+
+ return result;
+
+error_suspend:
+ pm_runtime_mark_last_busy(pdev);
+ pm_runtime_put_autosuspend(pdev);
+ return result;
+}
+
+static int inv_mpu6050_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 inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ /* support only WoM (accel roc rising) event */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ return st->chip_config.wom_en ? 1 : 0;
+}
+
+static int inv_mpu6050_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 inv_mpu6050_state *st = iio_priv(indio_dev);
+ int enable;
+
+ /* support only WoM (accel roc rising) event */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ enable = !!state;
+
+ guard(mutex)(&st->lock);
+
+ if (st->chip_config.wom_en == enable)
+ return 0;
+
+ return inv_mpu6050_enable_wom(st, enable);
+}
+
+static int inv_mpu6050_read_event_value(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 inv_mpu6050_state *st = iio_priv(indio_dev);
+ u32 rem;
+
+ /* support only WoM (accel roc rising) event value */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ /* return value in micro */
+ *val = div_u64_rem(st->chip_config.roc_threshold, 1000000U, &rem);
+ *val2 = rem;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_mpu6050_write_event_value(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 inv_mpu6050_state *st = iio_priv(indio_dev);
+ struct device *pdev = regmap_get_device(st->map);
+ u64 value;
+ int result;
+
+ /* support only WoM (accel roc rising) event value */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ result = pm_runtime_resume_and_get(pdev);
+ if (result)
+ return result;
+
+ value = (u64)val * 1000000ULL + (u64)val2;
+ result = inv_mpu6050_set_wom_threshold(st, value, INV_MPU6050_FREQ_DIVIDER(st));
+
+ pm_runtime_mark_last_busy(pdev);
+ pm_runtime_put_autosuspend(pdev);
+
+ return result;
+}
+
/*
* inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
*
@@ -989,6 +1328,12 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
if (result)
goto fifo_rate_fail_power_off;
+ /* update wom threshold since roc is dependent on sampling frequency */
+ result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
+ INV_MPU6050_FREQ_DIVIDER(st));
+ if (result)
+ goto fifo_rate_fail_power_off;
+
pm_runtime_mark_last_busy(pdev);
fifo_rate_fail_power_off:
pm_runtime_put_autosuspend(pdev);
@@ -1089,6 +1434,15 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
{ }
};
+static const struct iio_event_spec inv_wom_events[] = {
+ {
+ .type = IIO_EV_TYPE_ROC,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
#define INV_MPU6050_CHAN(_type, _channel2, _index) \
{ \
.type = _type, \
@@ -1124,7 +1478,31 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
}, \
}
-static const struct iio_chan_spec inv_mpu_channels[] = {
+#define INV_MPU6050_EVENT_CHAN(_type, _channel2, _events, _events_nb) \
+{ \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .event_spec = _events, \
+ .num_event_specs = _events_nb, \
+ .scan_index = -1, \
+}
+
+static const struct iio_chan_spec inv_mpu6050_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
+
+ INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
+
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
+
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
+};
+
+static const struct iio_chan_spec inv_mpu6500_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
@@ -1136,6 +1514,9 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
+
+ INV_MPU6050_EVENT_CHAN(IIO_ACCEL, IIO_MOD_X_OR_Y_OR_Z,
+ inv_wom_events, ARRAY_SIZE(inv_wom_events)),
};
#define INV_MPU6050_SCAN_MASK_3AXIS_ACCEL \
@@ -1326,6 +1707,10 @@ static const struct iio_info mpu_info = {
.write_raw = &inv_mpu6050_write_raw,
.write_raw_get_fmt = &inv_write_raw_get_fmt,
.attrs = &inv_attribute_group,
+ .read_event_config = inv_mpu6050_read_event_config,
+ .write_event_config = inv_mpu6050_write_event_config,
+ .read_event_value = inv_mpu6050_read_event_value,
+ .write_event_value = inv_mpu6050_write_event_value,
.validate_trigger = inv_mpu6050_validate_trigger,
.debugfs_reg_access = &inv_mpu6050_reg_access,
};
@@ -1537,6 +1922,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
irq_type);
return -EINVAL;
}
+ device_set_wakeup_capable(dev, true);
st->vdd_supply = devm_regulator_get(dev, "vdd");
if (IS_ERR(st->vdd_supply))
@@ -1613,6 +1999,12 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
return result;
switch (chip_type) {
+ case INV_MPU6000:
+ case INV_MPU6050:
+ indio_dev->channels = inv_mpu6050_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ break;
case INV_MPU9150:
indio_dev->channels = inv_mpu9150_channels;
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels);
@@ -1626,13 +2018,13 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
break;
case INV_ICM20600:
case INV_ICM20602:
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+ indio_dev->channels = inv_mpu6500_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
indio_dev->available_scan_masks = inv_icm20602_scan_masks;
break;
default:
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+ indio_dev->channels = inv_mpu6500_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
indio_dev->available_scan_masks = inv_mpu_scan_masks;
break;
}
@@ -1641,9 +2033,18 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
* auxiliary device in use. Otherwise Going back to 6-axis only.
*/
if (st->magn_disabled) {
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
- indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ switch (chip_type) {
+ case INV_MPU9150:
+ indio_dev->channels = inv_mpu6050_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ break;
+ default:
+ indio_dev->channels = inv_mpu6500_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ break;
+ }
}
indio_dev->info = &mpu_info;
@@ -1687,16 +2088,27 @@ static int inv_mpu_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ bool wakeup;
int result;
- mutex_lock(&st->lock);
- result = inv_mpu_core_enable_regulator_vddio(st);
- if (result)
- goto out_unlock;
+ guard(mutex)(&st->lock);
- result = inv_mpu6050_set_power_itg(st, true);
- if (result)
- goto out_unlock;
+ wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
+
+ if (wakeup) {
+ enable_irq(st->irq);
+ disable_irq_wake(st->irq);
+ result = inv_mpu6050_set_wom_lp(st, false);
+ if (result)
+ return result;
+ } else {
+ result = inv_mpu_core_enable_regulator_vddio(st);
+ if (result)
+ return result;
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ return result;
+ }
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
@@ -1704,14 +2116,17 @@ static int inv_mpu_resume(struct device *dev)
result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
if (result)
- goto out_unlock;
+ return result;
+
+ if (st->chip_config.wom_en && !wakeup) {
+ result = inv_mpu6050_set_wom_int(st, true);
+ if (result)
+ return result;
+ }
if (iio_buffer_enabled(indio_dev))
result = inv_mpu6050_prepare_fifo(st, true);
-out_unlock:
- mutex_unlock(&st->lock);
-
return result;
}
@@ -1719,23 +2134,30 @@ static int inv_mpu_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ bool wakeup;
int result;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
st->suspended_sensors = 0;
- if (pm_runtime_suspended(dev)) {
- result = 0;
- goto out_unlock;
- }
+ if (pm_runtime_suspended(dev))
+ return 0;
if (iio_buffer_enabled(indio_dev)) {
result = inv_mpu6050_prepare_fifo(st, false);
if (result)
- goto out_unlock;
+ return result;
+ }
+
+ wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
+
+ if (st->chip_config.wom_en && !wakeup) {
+ result = inv_mpu6050_set_wom_int(st, false);
+ if (result)
+ return result;
}
- if (st->chip_config.accl_en)
+ if (st->chip_config.accl_en && !wakeup)
st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
if (st->chip_config.gyro_en)
st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO;
@@ -1743,19 +2165,26 @@ static int inv_mpu_suspend(struct device *dev)
st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP;
if (st->chip_config.magn_en)
st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN;
+ if (st->chip_config.wom_en && !wakeup)
+ st->suspended_sensors |= INV_MPU6050_SENSOR_WOM;
result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors);
if (result)
- goto out_unlock;
-
- result = inv_mpu6050_set_power_itg(st, false);
- if (result)
- goto out_unlock;
+ return result;
- inv_mpu_core_disable_regulator_vddio(st);
-out_unlock:
- mutex_unlock(&st->lock);
+ if (wakeup) {
+ result = inv_mpu6050_set_wom_lp(st, true);
+ if (result)
+ return result;
+ enable_irq_wake(st->irq);
+ disable_irq(st->irq);
+ } else {
+ result = inv_mpu6050_set_power_itg(st, false);
+ if (result)
+ return result;
+ inv_mpu_core_disable_regulator_vddio(st);
+ }
- return result;
+ return 0;
}
static int inv_mpu_runtime_suspend(struct device *dev)
@@ -1767,7 +2196,8 @@ static int inv_mpu_runtime_suspend(struct device *dev)
mutex_lock(&st->lock);
sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
- INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN;
+ INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN |
+ INV_MPU6050_SENSOR_WOM;
ret = inv_mpu6050_switch_engine(st, false, sensors);
if (ret)
goto out_unlock;
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
index 5950e2419ebb..e1c0c5146876 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
@@ -88,11 +88,12 @@ enum inv_devices {
INV_NUM_PARTS
};
-/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */
+/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer, WoM */
#define INV_MPU6050_SENSOR_ACCL BIT(0)
#define INV_MPU6050_SENSOR_GYRO BIT(1)
#define INV_MPU6050_SENSOR_TEMP BIT(2)
#define INV_MPU6050_SENSOR_MAGN BIT(3)
+#define INV_MPU6050_SENSOR_WOM BIT(4)
/**
* struct inv_mpu6050_chip_config - Cached chip configuration data.
@@ -104,11 +105,13 @@ enum inv_devices {
* @gyro_en: gyro engine enabled
* @temp_en: temperature sensor enabled
* @magn_en: magn engine (i2c master) enabled
+ * @wom_en: Wake-on-Motion enabled
* @accl_fifo_enable: enable accel data output
* @gyro_fifo_enable: enable gyro data output
* @temp_fifo_enable: enable temp data output
* @magn_fifo_enable: enable magn data output
* @divider: chip sample rate divider (sample rate divider - 1)
+ * @roc_threshold: save ROC threshold (WoM) set value
*/
struct inv_mpu6050_chip_config {
unsigned int clk:3;
@@ -119,12 +122,14 @@ struct inv_mpu6050_chip_config {
unsigned int gyro_en:1;
unsigned int temp_en:1;
unsigned int magn_en:1;
+ unsigned int wom_en:1;
unsigned int accl_fifo_enable:1;
unsigned int gyro_fifo_enable:1;
unsigned int temp_fifo_enable:1;
unsigned int magn_fifo_enable:1;
u8 divider;
u8 user_ctrl;
+ u64 roc_threshold;
};
/*
@@ -180,6 +185,7 @@ struct inv_mpu6050_hw {
* @magn_orient: magnetometer sensor chip orientation if available.
* @suspended_sensors: sensors mask of sensors turned off for suspend
* @data: read buffer used for bulk reads.
+ * @it_timestamp: interrupt timestamp.
*/
struct inv_mpu6050_state {
struct mutex lock;
@@ -205,6 +211,7 @@ struct inv_mpu6050_state {
unsigned int suspended_sensors;
bool level_shifter;
u8 *data;
+ s64 it_timestamp;
};
/*register and associated bit definition*/
@@ -256,12 +263,16 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_INT_ENABLE 0x38
#define INV_MPU6050_BIT_DATA_RDY_EN 0x01
#define INV_MPU6050_BIT_DMP_INT_EN 0x02
+#define INV_MPU6500_BIT_WOM_INT_EN BIT(6)
+#define INV_ICM20608_BIT_WOM_INT_EN GENMASK(7, 5)
#define INV_MPU6050_REG_RAW_ACCEL 0x3B
#define INV_MPU6050_REG_TEMPERATURE 0x41
#define INV_MPU6050_REG_RAW_GYRO 0x43
#define INV_MPU6050_REG_INT_STATUS 0x3A
+#define INV_MPU6500_BIT_WOM_INT BIT(6)
+#define INV_ICM20608_BIT_WOM_INT GENMASK(7, 5)
#define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10
#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01
@@ -294,6 +305,7 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_PWR_MGMT_1 0x6B
#define INV_MPU6050_BIT_H_RESET 0x80
#define INV_MPU6050_BIT_SLEEP 0x40
+#define INV_MPU6050_BIT_CYCLE 0x20
#define INV_MPU6050_BIT_TEMP_DIS 0x08
#define INV_MPU6050_BIT_CLK_MASK 0x7
@@ -301,6 +313,11 @@ struct inv_mpu6050_state {
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
+/* ICM20609 registers */
+#define INV_ICM20609_REG_ACCEL_WOM_X_THR 0x20
+#define INV_ICM20609_REG_ACCEL_WOM_Y_THR 0x21
+#define INV_ICM20609_REG_ACCEL_WOM_Z_THR 0x22
+
/* ICM20602 register */
#define INV_ICM20602_REG_I2C_IF 0x70
#define INV_ICM20602_BIT_I2C_IF_DIS 0x40
@@ -320,6 +337,11 @@ struct inv_mpu6050_state {
/* mpu6500 registers */
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
#define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0
+#define INV_MPU6500_REG_LP_ODR 0x1E
+#define INV_MPU6500_REG_WOM_THRESHOLD 0x1F
+#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69
+#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7)
+#define INV_MPU6500_BIT_ACCEL_INTEL_MODE BIT(6)
#define INV_MPU6500_REG_ACCEL_OFFSET 0x77
/* delay time in milliseconds */
@@ -432,6 +454,18 @@ enum inv_mpu6050_filter_e {
NUM_MPU6050_FILTER
};
+enum inv_mpu6050_lposc_e {
+ INV_MPU6050_LPOSC_4HZ = 4,
+ INV_MPU6050_LPOSC_8HZ,
+ INV_MPU6050_LPOSC_16HZ,
+ INV_MPU6050_LPOSC_31HZ,
+ INV_MPU6050_LPOSC_62HZ,
+ INV_MPU6050_LPOSC_125HZ,
+ INV_MPU6050_LPOSC_250HZ,
+ INV_MPU6050_LPOSC_500HZ,
+ NUM_MPU6050_LPOSC,
+};
+
/* IIO attribute address */
enum INV_MPU6050_IIO_ATTR_ADDR {
ATTR_GYRO_MATRIX,
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
index d4f9b5d8d28d..0dc0f22a5582 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
@@ -33,10 +33,8 @@ static int inv_reset_fifo(struct iio_dev *indio_dev)
reset_fifo_fail:
dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result);
- result = regmap_write(st->map, st->reg->int_enable,
- INV_MPU6050_BIT_DATA_RDY_EN);
-
- return result;
+ return regmap_update_bits(st->map, st->reg->int_enable,
+ INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN);
}
/*
@@ -53,21 +51,10 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
u32 fifo_period;
s64 timestamp;
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
- int int_status;
size_t i, nb;
mutex_lock(&st->lock);
- /* ack interrupt and check status */
- result = regmap_read(st->map, st->reg->int_status, &int_status);
- if (result) {
- dev_err(regmap_get_device(st->map),
- "failed to ack interrupt\n");
- goto flush_fifo;
- }
- if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT))
- goto end_session;
-
if (!(st->chip_config.accl_fifo_enable |
st->chip_config.gyro_fifo_enable |
st->chip_config.magn_fifo_enable))
@@ -113,7 +100,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
goto end_session;
/* 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_interrupt(&st->timestamp, nb, pf->timestamp);
inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0);
/* clear internal data buffer for avoiding kernel data leak */
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
index e6e6e94452a3..1b603567ccc8 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
@@ -6,6 +6,7 @@
#include <linux/pm_runtime.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/events.h>
#include "inv_mpu_iio.h"
@@ -135,11 +136,13 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
ret = regmap_write(st->map, st->reg->user_ctrl, d);
if (ret)
return ret;
- /* enable interrupt */
- ret = regmap_write(st->map, st->reg->int_enable,
- INV_MPU6050_BIT_DATA_RDY_EN);
+ /* enable data interrupt */
+ ret = regmap_update_bits(st->map, st->reg->int_enable,
+ INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN);
} else {
- ret = regmap_write(st->map, st->reg->int_enable, 0);
+ /* disable data interrupt */
+ ret = regmap_update_bits(st->map, st->reg->int_enable,
+ INV_MPU6050_BIT_DATA_RDY_EN, 0);
if (ret)
return ret;
ret = regmap_write(st->map, st->reg->fifo_en, 0);
@@ -172,9 +175,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
return result;
/*
* In case autosuspend didn't trigger, turn off first not
- * required sensors.
+ * required sensors excepted WoM
*/
- result = inv_mpu6050_switch_engine(st, false, ~scan);
+ result = inv_mpu6050_switch_engine(st, false, ~scan & ~INV_MPU6050_SENSOR_WOM);
if (result)
goto error_power_off;
result = inv_mpu6050_switch_engine(st, true, scan);
@@ -226,6 +229,65 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = {
.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
};
+static irqreturn_t inv_mpu6050_interrupt_timestamp(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ st->it_timestamp = iio_get_time_ns(indio_dev);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t inv_mpu6050_interrupt_handle(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ unsigned int int_status, wom_bits;
+ u64 ev_code;
+ int result;
+
+ switch (st->chip_type) {
+ case INV_MPU6050:
+ case INV_MPU6500:
+ case INV_MPU6515:
+ case INV_MPU6880:
+ case INV_MPU6000:
+ case INV_MPU9150:
+ case INV_MPU9250:
+ case INV_MPU9255:
+ wom_bits = INV_MPU6500_BIT_WOM_INT;
+ break;
+ default:
+ wom_bits = INV_ICM20608_BIT_WOM_INT;
+ break;
+ }
+
+ scoped_guard(mutex, &st->lock) {
+ /* ack interrupt and check status */
+ result = regmap_read(st->map, st->reg->int_status, &int_status);
+ if (result) {
+ dev_err(regmap_get_device(st->map), "failed to ack interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ /* handle WoM event */
+ if (st->chip_config.wom_en && (int_status & wom_bits)) {
+ ev_code = IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING);
+ iio_push_event(indio_dev, ev_code, st->it_timestamp);
+ }
+ }
+
+ /* handle raw data interrupt */
+ if (int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT) {
+ indio_dev->pollfunc->timestamp = st->it_timestamp;
+ iio_trigger_poll_nested(st->trig);
+ }
+
+ return IRQ_HANDLED;
+}
+
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
{
int ret;
@@ -238,11 +300,10 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
if (!st->trig)
return -ENOMEM;
- ret = devm_request_irq(&indio_dev->dev, st->irq,
- &iio_trigger_generic_data_rdy_poll,
- irq_type,
- "inv_mpu",
- st->trig);
+ ret = devm_request_threaded_irq(&indio_dev->dev, st->irq,
+ &inv_mpu6050_interrupt_timestamp,
+ &inv_mpu6050_interrupt_handle,
+ irq_type, "inv_mpu", indio_dev);
if (ret)
return ret;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 0716986f9812..937ff9c5a74c 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -2726,7 +2726,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
if (!hw)
return -ENOMEM;
- dev_set_drvdata(dev, (void *)hw);
+ dev_set_drvdata(dev, hw);
mutex_init(&hw->fifo_lock);
mutex_init(&hw->conf_lock);
diff --git a/drivers/iio/industrialio-acpi.c b/drivers/iio/industrialio-acpi.c
new file mode 100644
index 000000000000..981b75d40780
--- /dev/null
+++ b/drivers/iio/industrialio-acpi.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* IIO ACPI helper functions */
+
+#include <linux/acpi.h>
+#include <linux/dev_printk.h>
+#include <linux/iio/iio.h>
+#include <linux/sprintf.h>
+
+/**
+ * iio_read_acpi_mount_matrix() - Read accelerometer mount matrix info from ACPI
+ * @dev: Device structure
+ * @orientation: iio_mount_matrix struct to fill
+ * @acpi_method: ACPI method name to read the matrix from, usually "ROTM"
+ *
+ * Try to read the mount-matrix by calling the specified method on the device's
+ * ACPI firmware-node. If the device has no ACPI firmware-node; or the method
+ * does not exist then this will fail silently. This expects the method to
+ * return data in the ACPI "ROTM" format defined by Microsoft:
+ * https://learn.microsoft.com/en-us/windows-hardware/drivers/sensors/sensors-acpi-entries
+ * This is a Microsoft extension and not part of the official ACPI spec.
+ * The method name is configurable because some dual-accel setups define 2 mount
+ * matrices in a single ACPI device using separate "ROMK" and "ROMS" methods.
+ *
+ * Returns: true if the matrix was successfully, false otherwise.
+ */
+bool iio_read_acpi_mount_matrix(struct device *dev,
+ struct iio_mount_matrix *orientation,
+ char *acpi_method)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ char *str;
+ union acpi_object *obj, *elements;
+ acpi_status status;
+ int i, j, val[3];
+ bool ret = false;
+
+ if (!adev || !acpi_has_method(adev->handle, acpi_method))
+ return false;
+
+ status = acpi_evaluate_object(adev->handle, acpi_method, NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status);
+ return false;
+ }
+
+ obj = buffer.pointer;
+ if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) {
+ dev_err(dev, "Unknown ACPI mount matrix package format\n");
+ goto out_free_buffer;
+ }
+
+ elements = obj->package.elements;
+ for (i = 0; i < 3; i++) {
+ if (elements[i].type != ACPI_TYPE_STRING) {
+ dev_err(dev, "Unknown ACPI mount matrix element format\n");
+ goto out_free_buffer;
+ }
+
+ str = elements[i].string.pointer;
+ if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) {
+ dev_err(dev, "Incorrect ACPI mount matrix string format\n");
+ goto out_free_buffer;
+ }
+
+ for (j = 0; j < 3; j++) {
+ switch (val[j]) {
+ case -1: str = "-1"; break;
+ case 0: str = "0"; break;
+ case 1: str = "1"; break;
+ default:
+ dev_err(dev, "Invalid value in ACPI mount matrix: %d\n", val[j]);
+ goto out_free_buffer;
+ }
+ orientation->rotation[i * 3 + j] = str;
+ }
+ }
+
+ ret = true;
+
+out_free_buffer:
+ kfree(buffer.pointer);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_acpi_mount_matrix);
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 2fea2bbbe47f..929aff4040ed 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -43,10 +43,12 @@
#include <linux/types.h>
#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
struct iio_backend {
struct list_head entry;
const struct iio_backend_ops *ops;
+ struct device *frontend_dev;
struct device *dev;
struct module *owner;
void *priv;
@@ -113,8 +115,8 @@ static DEFINE_MUTEX(iio_back_lock);
/**
* iio_backend_chan_enable - Enable a backend channel
- * @back: Backend device
- * @chan: Channel number
+ * @back: Backend device
+ * @chan: Channel number
*
* RETURNS:
* 0 on success, negative error number on failure.
@@ -127,8 +129,8 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_chan_enable, IIO_BACKEND);
/**
* iio_backend_chan_disable - Disable a backend channel
- * @back: Backend device
- * @chan: Channel number
+ * @back: Backend device
+ * @chan: Channel number
*
* RETURNS:
* 0 on success, negative error number on failure.
@@ -146,8 +148,8 @@ static void __iio_backend_disable(void *back)
/**
* devm_iio_backend_enable - Device managed backend enable
- * @dev: Consumer device for the backend
- * @back: Backend device
+ * @dev: Consumer device for the backend
+ * @back: Backend device
*
* RETURNS:
* 0 on success, negative error number on failure.
@@ -166,9 +168,9 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_enable, IIO_BACKEND);
/**
* iio_backend_data_format_set - Configure the channel data format
- * @back: Backend device
- * @chan: Channel number
- * @data: Data format
+ * @back: Backend device
+ * @chan: Channel number
+ * @data: Data format
*
* Properly configure a channel with respect to the expected data format. A
* @struct iio_backend_data_fmt must be passed with the settings.
@@ -186,6 +188,130 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
}
EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
+/**
+ * iio_backend_data_source_set - Select data source
+ * @back: Backend device
+ * @chan: Channel number
+ * @data: Data source
+ *
+ * A given backend may have different sources to stream/sync data. This allows
+ * to choose that source.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data)
+{
+ if (data >= IIO_BACKEND_DATA_SOURCE_MAX)
+ return -EINVAL;
+
+ return iio_backend_op_call(back, data_source_set, chan, data);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND);
+
+/**
+ * iio_backend_set_sampling_freq - Set channel sampling rate
+ * @back: Backend device
+ * @chan: Channel number
+ * @sample_rate_hz: Sample rate
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate_hz)
+{
+ return iio_backend_op_call(back, set_sample_rate, chan, sample_rate_hz);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND);
+
+/**
+ * iio_backend_test_pattern_set - Configure a test pattern
+ * @back: Backend device
+ * @chan: Channel number
+ * @pattern: Test pattern
+ *
+ * Configure a test pattern on the backend. This is typically used for
+ * calibrating the timings on the data digital interface.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_test_pattern_set(struct iio_backend *back,
+ unsigned int chan,
+ enum iio_backend_test_pattern pattern)
+{
+ if (pattern >= IIO_BACKEND_TEST_PATTERN_MAX)
+ return -EINVAL;
+
+ return iio_backend_op_call(back, test_pattern_set, chan, pattern);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_test_pattern_set, IIO_BACKEND);
+
+/**
+ * iio_backend_chan_status - Get the channel status
+ * @back: Backend device
+ * @chan: Channel number
+ * @error: Error indication
+ *
+ * Get the current state of the backend channel. Typically used to check if
+ * there were any errors sending/receiving data.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_chan_status(struct iio_backend *back, unsigned int chan,
+ bool *error)
+{
+ return iio_backend_op_call(back, chan_status, chan, error);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_chan_status, IIO_BACKEND);
+
+/**
+ * iio_backend_iodelay_set - Set digital I/O delay
+ * @back: Backend device
+ * @lane: Lane number
+ * @taps: Number of taps
+ *
+ * Controls delays on sending/receiving data. One usecase for this is to
+ * calibrate the data digital interface so we get the best results when
+ * transferring data. Note that @taps has no unit since the actual delay per tap
+ * is very backend specific. Hence, frontend devices typically should go through
+ * an array of @taps (the size of that array should typically match the size of
+ * calibration points on the frontend device) and call this API.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_iodelay_set(struct iio_backend *back, unsigned int lane,
+ unsigned int taps)
+{
+ return iio_backend_op_call(back, iodelay_set, lane, taps);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_iodelay_set, IIO_BACKEND);
+
+/**
+ * iio_backend_data_sample_trigger - Control when to sample data
+ * @back: Backend device
+ * @trigger: Data trigger
+ *
+ * Mostly useful for input backends. Configures the backend for when to sample
+ * data (eg: rising vs falling edge).
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_sample_trigger(struct iio_backend *back,
+ enum iio_backend_sample_trigger trigger)
+{
+ if (trigger >= IIO_BACKEND_SAMPLE_TRIGGER_MAX)
+ return -EINVAL;
+
+ return iio_backend_op_call(back, data_sample_trigger, trigger);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_sample_trigger, IIO_BACKEND);
+
static void iio_backend_free_buffer(void *arg)
{
struct iio_backend_buffer_pair *pair = arg;
@@ -195,9 +321,9 @@ static void iio_backend_free_buffer(void *arg)
/**
* devm_iio_backend_request_buffer - Device managed buffer request
- * @dev: Consumer device for the backend
- * @back: Backend device
- * @indio_dev: IIO device
+ * @dev: Consumer device for the backend
+ * @back: Backend device
+ * @indio_dev: IIO device
*
* Request an IIO buffer from the backend. The type of the buffer (typically
* INDIO_BUFFER_HARDWARE) is up to the backend to decide. This is because,
@@ -231,6 +357,143 @@ int devm_iio_backend_request_buffer(struct device *dev,
}
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
+static struct iio_backend *iio_backend_from_indio_dev_parent(const struct device *dev)
+{
+ struct iio_backend *back = ERR_PTR(-ENODEV), *iter;
+
+ /*
+ * We deliberately go through all backends even after finding a match.
+ * The reason is that we want to catch frontend devices which have more
+ * than one backend in which case returning the first we find is bogus.
+ * For those cases, frontends need to explicitly define
+ * get_iio_backend() in struct iio_info.
+ */
+ guard(mutex)(&iio_back_lock);
+ list_for_each_entry(iter, &iio_back_list, entry) {
+ if (dev == iter->frontend_dev) {
+ if (!IS_ERR(back)) {
+ dev_warn(dev,
+ "Multiple backends! get_iio_backend() needs to be implemented");
+ return ERR_PTR(-ENODEV);
+ }
+
+ back = iter;
+ }
+ }
+
+ return back;
+}
+
+/**
+ * iio_backend_ext_info_get - IIO ext_info read callback
+ * @indio_dev: IIO device
+ * @private: Data private to the driver
+ * @chan: IIO channel
+ * @buf: Buffer where to place the attribute data
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (through iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not have
+ * a way to get the backend from indio_dev). This is the getter.
+ *
+ * RETURNS:
+ * Number of bytes written to buf, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct iio_backend *back;
+
+ /*
+ * The below should work for the majority of the cases. It will not work
+ * when one frontend has multiple backends in which case we'll need a
+ * new callback in struct iio_info so we can directly request the proper
+ * backend from the frontend. Anyways, let's only introduce new options
+ * when really needed...
+ */
+ back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
+ if (IS_ERR(back))
+ return PTR_ERR(back);
+
+ return iio_backend_op_call(back, ext_info_get, private, chan, buf);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND);
+
+/**
+ * iio_backend_ext_info_set - IIO ext_info write callback
+ * @indio_dev: IIO device
+ * @private: Data private to the driver
+ * @chan: IIO channel
+ * @buf: Buffer holding the sysfs attribute
+ * @len: Buffer length
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not have
+ * a way to get the backend from indio_dev). This is the setter.
+ *
+ * RETURNS:
+ * Buffer length on success, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct iio_backend *back;
+
+ back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
+ if (IS_ERR(back))
+ return PTR_ERR(back);
+
+ return iio_backend_op_call(back, ext_info_set, private, chan, buf, len);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND);
+
+/**
+ * iio_backend_extend_chan_spec - Extend an IIO channel
+ * @indio_dev: IIO device
+ * @back: Backend device
+ * @chan: IIO channel
+ *
+ * Some backends may have their own functionalities and hence capable of
+ * extending a frontend's channel.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+ struct iio_backend *back,
+ struct iio_chan_spec *chan)
+{
+ const struct iio_chan_spec_ext_info *frontend_ext_info = chan->ext_info;
+ const struct iio_chan_spec_ext_info *back_ext_info;
+ int ret;
+
+ ret = iio_backend_op_call(back, extend_chan_spec, chan);
+ if (ret)
+ return ret;
+ /*
+ * Let's keep things simple for now. Don't allow to overwrite the
+ * frontend's extended info. If ever needed, we can support appending
+ * it.
+ */
+ if (frontend_ext_info && chan->ext_info != frontend_ext_info)
+ return -EOPNOTSUPP;
+ if (!chan->ext_info)
+ return 0;
+
+ /* Don't allow backends to get creative and force their own handlers */
+ for (back_ext_info = chan->ext_info; back_ext_info->name; back_ext_info++) {
+ if (back_ext_info->read != iio_backend_ext_info_get)
+ return -EINVAL;
+ if (back_ext_info->write != iio_backend_ext_info_set)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
+
static void iio_backend_release(void *arg)
{
struct iio_backend *back = arg;
@@ -263,6 +526,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back)
"Could not link to supplier(%s)\n",
dev_name(back->dev));
+ back->frontend_dev = dev;
+
dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev));
return 0;
@@ -270,8 +535,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back)
/**
* devm_iio_backend_get - Device managed backend device get
- * @dev: Consumer device for the backend
- * @name: Backend name
+ * @dev: Consumer device for the backend
+ * @name: Backend name
*
* Get's the backend associated with @dev.
*
@@ -322,8 +587,8 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, IIO_BACKEND);
/**
* __devm_iio_backend_get_from_fwnode_lookup - Device managed fwnode backend device get
- * @dev: Consumer device for the backend
- * @fwnode: Firmware node of the backend device
+ * @dev: Consumer device for the backend
+ * @fwnode: Firmware node of the backend device
*
* Search the backend list for a device matching @fwnode.
* This API should not be used and it's only present for preventing the first
@@ -357,7 +622,7 @@ EXPORT_SYMBOL_NS_GPL(__devm_iio_backend_get_from_fwnode_lookup, IIO_BACKEND);
/**
* iio_backend_get_priv - Get driver private data
- * @back: Backend device
+ * @back: Backend device
*/
void *iio_backend_get_priv(const struct iio_backend *back)
{
@@ -375,9 +640,9 @@ static void iio_backend_unregister(void *arg)
/**
* devm_iio_backend_register - Device managed backend device register
- * @dev: Backend device being registered
- * @ops: Backend ops
- * @priv: Device private data
+ * @dev: Backend device being registered
+ * @ops: Backend ops
+ * @priv: Device private data
*
* @ops is mandatory. Not providing it results in -EINVAL.
*
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index b581a7e80566..cec58a604d73 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -10,6 +10,7 @@
* - Alternative access techniques?
*/
#include <linux/anon_inodes.h>
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/device.h>
@@ -533,28 +534,26 @@ static ssize_t iio_scan_el_store(struct device *dev,
ret = kstrtobool(buf, &state);
if (ret < 0)
return ret;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- goto error_ret;
- }
+
+ guard(mutex)(&iio_dev_opaque->mlock);
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
+
ret = iio_scan_mask_query(indio_dev, buffer, this_attr->address);
if (ret < 0)
- goto error_ret;
- if (!state && ret) {
- ret = iio_scan_mask_clear(buffer, this_attr->address);
- if (ret)
- goto error_ret;
- } else if (state && !ret) {
- ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address);
- if (ret)
- goto error_ret;
- }
+ return ret;
-error_ret:
- mutex_unlock(&iio_dev_opaque->mlock);
+ if (state && ret)
+ return len;
- return ret < 0 ? ret : len;
+ if (state)
+ ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address);
+ else
+ ret = iio_scan_mask_clear(buffer, this_attr->address);
+ if (ret)
+ return ret;
+
+ return len;
}
static ssize_t iio_scan_el_ts_show(struct device *dev,
@@ -581,16 +580,13 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
if (ret < 0)
return ret;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- goto error_ret;
- }
+ guard(mutex)(&iio_dev_opaque->mlock);
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
+
buffer->scan_timestamp = state;
-error_ret:
- mutex_unlock(&iio_dev_opaque->mlock);
- return ret ? ret : len;
+ return len;
}
static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
@@ -674,21 +670,16 @@ static ssize_t length_store(struct device *dev, struct device_attribute *attr,
if (val == buffer->length)
return len;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- } else {
- buffer->access->set_length(buffer, val);
- ret = 0;
- }
- if (ret)
- goto out;
+ guard(mutex)(&iio_dev_opaque->mlock);
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
+
+ buffer->access->set_length(buffer, val);
+
if (buffer->length && buffer->length < buffer->watermark)
buffer->watermark = buffer->length;
-out:
- mutex_unlock(&iio_dev_opaque->mlock);
- return ret ? ret : len;
+ return len;
}
static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
@@ -1268,7 +1259,6 @@ int iio_update_buffers(struct iio_dev *indio_dev,
struct iio_buffer *remove_buffer)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
- int ret;
if (insert_buffer == remove_buffer)
return 0;
@@ -1277,8 +1267,8 @@ int iio_update_buffers(struct iio_dev *indio_dev,
insert_buffer->direction == IIO_BUFFER_DIRECTION_OUT)
return -EINVAL;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->mlock);
if (insert_buffer && iio_buffer_is_active(insert_buffer))
insert_buffer = NULL;
@@ -1286,23 +1276,13 @@ int iio_update_buffers(struct iio_dev *indio_dev,
if (remove_buffer && !iio_buffer_is_active(remove_buffer))
remove_buffer = NULL;
- if (!insert_buffer && !remove_buffer) {
- ret = 0;
- goto out_unlock;
- }
-
- if (!indio_dev->info) {
- ret = -ENODEV;
- goto out_unlock;
- }
-
- ret = __iio_update_buffers(indio_dev, insert_buffer, remove_buffer);
+ if (!insert_buffer && !remove_buffer)
+ return 0;
-out_unlock:
- mutex_unlock(&iio_dev_opaque->mlock);
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ if (!indio_dev->info)
+ return -ENODEV;
- return ret;
+ return __iio_update_buffers(indio_dev, insert_buffer, remove_buffer);
}
EXPORT_SYMBOL_GPL(iio_update_buffers);
@@ -1326,22 +1306,22 @@ static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
if (ret < 0)
return ret;
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->mlock);
/* Find out if it is in the list */
inlist = iio_buffer_is_active(buffer);
/* Already in desired state */
if (inlist == requested_state)
- goto done;
+ return len;
if (requested_state)
ret = __iio_update_buffers(indio_dev, buffer, NULL);
else
ret = __iio_update_buffers(indio_dev, NULL, buffer);
+ if (ret)
+ return ret;
-done:
- mutex_unlock(&iio_dev_opaque->mlock);
- return (ret < 0) ? ret : len;
+ return len;
}
static ssize_t watermark_show(struct device *dev, struct device_attribute *attr,
@@ -1368,23 +1348,17 @@ static ssize_t watermark_store(struct device *dev,
if (!val)
return -EINVAL;
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->mlock);
- if (val > buffer->length) {
- ret = -EINVAL;
- goto out;
- }
+ if (val > buffer->length)
+ return -EINVAL;
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- goto out;
- }
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
buffer->watermark = val;
-out:
- mutex_unlock(&iio_dev_opaque->mlock);
- return ret ? ret : len;
+ return len;
}
static ssize_t data_available_show(struct device *dev,
@@ -1770,7 +1744,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
channels = indio_dev->channels;
if (channels) {
- int ml = indio_dev->masklength;
+ int ml = 0;
for (i = 0; i < indio_dev->num_channels; i++)
ml = max(ml, channels[i].scan_index + 1);
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 4302093b92c7..fa7cc051b4c4 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -11,6 +11,7 @@
#include <linux/anon_inodes.h>
#include <linux/cdev.h>
+#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -1643,19 +1644,20 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
struct iio_dev *indio_dev;
size_t alloc_size;
- alloc_size = sizeof(struct iio_dev_opaque);
- if (sizeof_priv) {
- alloc_size = ALIGN(alloc_size, IIO_DMA_MINALIGN);
- alloc_size += sizeof_priv;
- }
+ if (sizeof_priv)
+ alloc_size = ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN) + sizeof_priv;
+ else
+ alloc_size = sizeof(*iio_dev_opaque);
iio_dev_opaque = kzalloc(alloc_size, GFP_KERNEL);
if (!iio_dev_opaque)
return NULL;
indio_dev = &iio_dev_opaque->indio_dev;
- indio_dev->priv = (char *)iio_dev_opaque +
- ALIGN(sizeof(struct iio_dev_opaque), IIO_DMA_MINALIGN);
+
+ if (sizeof_priv)
+ indio_dev->priv = (char *)iio_dev_opaque +
+ ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN);
indio_dev->dev.parent = parent;
indio_dev->dev.type = &iio_device_type;
@@ -1809,31 +1811,24 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct iio_dev *indio_dev = ib->indio_dev;
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_ioctl_handler *h;
- int ret = -ENODEV;
-
- mutex_lock(&iio_dev_opaque->info_exist_lock);
+ int ret;
+ guard(mutex)(&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.
*/
if (!indio_dev->info)
- goto out_unlock;
+ return -ENODEV;
list_for_each_entry(h, &iio_dev_opaque->ioctl_handlers, entry) {
ret = h->ioctl(indio_dev, filp, cmd, arg);
if (ret != IIO_IOCTL_UNHANDLED)
- break;
+ return ret;
}
- if (ret == IIO_IOCTL_UNHANDLED)
- ret = -ENODEV;
-
-out_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
-
- return ret;
+ return -ENODEV;
}
static const struct file_operations iio_buffer_fileops = {
@@ -2061,18 +2056,16 @@ void iio_device_unregister(struct iio_dev *indio_dev)
cdev_device_del(&iio_dev_opaque->chrdev, &indio_dev->dev);
- mutex_lock(&iio_dev_opaque->info_exist_lock);
+ scoped_guard(mutex, &iio_dev_opaque->info_exist_lock) {
+ iio_device_unregister_debugfs(indio_dev);
- iio_device_unregister_debugfs(indio_dev);
+ iio_disable_all_buffers(indio_dev);
- iio_disable_all_buffers(indio_dev);
+ indio_dev->info = NULL;
- indio_dev->info = NULL;
-
- iio_device_wakeup_eventset(indio_dev);
- iio_buffer_wakeup_poll(indio_dev);
-
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ iio_device_wakeup_eventset(indio_dev);
+ iio_buffer_wakeup_poll(indio_dev);
+ }
iio_buffers_free_sysfs_and_mask(indio_dev);
}
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index 18f83158f637..16de57846bd9 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -4,6 +4,7 @@
* Copyright (c) 2008 Jonathan Cameron
*/
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/idr.h>
#include <linux/err.h>
@@ -80,19 +81,18 @@ int iio_trigger_register(struct iio_trigger *trig_info)
goto error_unregister_id;
/* Add to list of available triggers held by the IIO core */
- mutex_lock(&iio_trigger_list_lock);
- if (__iio_trigger_find_by_name(trig_info->name)) {
- pr_err("Duplicate trigger name '%s'\n", trig_info->name);
- ret = -EEXIST;
- goto error_device_del;
+ scoped_guard(mutex, &iio_trigger_list_lock) {
+ if (__iio_trigger_find_by_name(trig_info->name)) {
+ pr_err("Duplicate trigger name '%s'\n", trig_info->name);
+ ret = -EEXIST;
+ goto error_device_del;
+ }
+ list_add_tail(&trig_info->list, &iio_trigger_list);
}
- list_add_tail(&trig_info->list, &iio_trigger_list);
- mutex_unlock(&iio_trigger_list_lock);
return 0;
error_device_del:
- mutex_unlock(&iio_trigger_list_lock);
device_del(&trig_info->dev);
error_unregister_id:
ida_free(&iio_trigger_ida, trig_info->id);
@@ -102,9 +102,8 @@ EXPORT_SYMBOL(iio_trigger_register);
void iio_trigger_unregister(struct iio_trigger *trig_info)
{
- mutex_lock(&iio_trigger_list_lock);
- list_del(&trig_info->list);
- mutex_unlock(&iio_trigger_list_lock);
+ scoped_guard(mutex, &iio_trigger_list_lock)
+ list_del(&trig_info->list);
ida_free(&iio_trigger_ida, trig_info->id);
/* Possible issue in here */
@@ -120,12 +119,11 @@ int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *tri
return -EINVAL;
iio_dev_opaque = to_iio_dev_opaque(indio_dev);
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->mlock);
WARN_ON(iio_dev_opaque->trig_readonly);
indio_dev->trig = iio_trigger_get(trig);
iio_dev_opaque->trig_readonly = true;
- mutex_unlock(&iio_dev_opaque->mlock);
return 0;
}
@@ -145,18 +143,14 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
static struct iio_trigger *iio_trigger_acquire_by_name(const char *name)
{
- struct iio_trigger *trig = NULL, *iter;
+ struct iio_trigger *iter;
- mutex_lock(&iio_trigger_list_lock);
+ guard(mutex)(&iio_trigger_list_lock);
list_for_each_entry(iter, &iio_trigger_list, list)
- if (sysfs_streq(iter->name, name)) {
- trig = iter;
- iio_trigger_get(trig);
- break;
- }
- mutex_unlock(&iio_trigger_list_lock);
+ if (sysfs_streq(iter->name, name))
+ return iio_trigger_get(iter);
- return trig;
+ return NULL;
}
static void iio_reenable_work_fn(struct work_struct *work)
@@ -259,22 +253,21 @@ static int iio_trigger_get_irq(struct iio_trigger *trig)
{
int ret;
- mutex_lock(&trig->pool_lock);
- ret = bitmap_find_free_region(trig->pool,
- CONFIG_IIO_CONSUMERS_PER_TRIGGER,
- ilog2(1));
- mutex_unlock(&trig->pool_lock);
- if (ret >= 0)
- ret += trig->subirq_base;
+ scoped_guard(mutex, &trig->pool_lock) {
+ ret = bitmap_find_free_region(trig->pool,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER,
+ ilog2(1));
+ if (ret < 0)
+ return ret;
+ }
- return ret;
+ return ret + trig->subirq_base;
}
static void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
{
- mutex_lock(&trig->pool_lock);
+ guard(mutex)(&trig->pool_lock);
clear_bit(irq - trig->subirq_base, trig->pool);
- mutex_unlock(&trig->pool_lock);
}
/* Complexity in here. With certain triggers (datardy) an acknowledgement
@@ -451,16 +444,12 @@ static ssize_t current_trigger_store(struct device *dev,
struct iio_trigger *trig;
int ret;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED) {
- mutex_unlock(&iio_dev_opaque->mlock);
- return -EBUSY;
- }
- if (iio_dev_opaque->trig_readonly) {
- mutex_unlock(&iio_dev_opaque->mlock);
- return -EPERM;
+ scoped_guard(mutex, &iio_dev_opaque->mlock) {
+ if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED)
+ return -EBUSY;
+ if (iio_dev_opaque->trig_readonly)
+ return -EPERM;
}
- mutex_unlock(&iio_dev_opaque->mlock);
trig = iio_trigger_acquire_by_name(buf);
if (oldtrig == trig) {
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 7a1f6713318a..52d773261828 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2011 Jonathan Cameron
*/
+#include <linux/cleanup.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/minmax.h>
@@ -43,13 +44,14 @@ static int iio_map_array_unregister_locked(struct iio_dev *indio_dev)
int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
{
- int i = 0, ret = 0;
struct iio_map_internal *mapi;
+ int i = 0;
+ int ret;
if (!maps)
return 0;
- mutex_lock(&iio_map_list_lock);
+ guard(mutex)(&iio_map_list_lock);
while (maps[i].consumer_dev_name) {
mapi = kzalloc(sizeof(*mapi), GFP_KERNEL);
if (!mapi) {
@@ -61,11 +63,10 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
list_add_tail(&mapi->l, &iio_map_list);
i++;
}
-error_ret:
- if (ret)
- iio_map_array_unregister_locked(indio_dev);
- mutex_unlock(&iio_map_list_lock);
+ return 0;
+error_ret:
+ iio_map_array_unregister_locked(indio_dev);
return ret;
}
EXPORT_SYMBOL_GPL(iio_map_array_register);
@@ -75,13 +76,8 @@ EXPORT_SYMBOL_GPL(iio_map_array_register);
*/
int iio_map_array_unregister(struct iio_dev *indio_dev)
{
- int ret;
-
- mutex_lock(&iio_map_list_lock);
- ret = iio_map_array_unregister_locked(indio_dev);
- mutex_unlock(&iio_map_list_lock);
-
- return ret;
+ guard(mutex)(&iio_map_list_lock);
+ return iio_map_array_unregister_locked(indio_dev);
}
EXPORT_SYMBOL_GPL(iio_map_array_unregister);
@@ -183,25 +179,21 @@ err_put:
static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode,
int index)
{
- struct iio_channel *channel;
int err;
if (index < 0)
return ERR_PTR(-EINVAL);
- channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ struct iio_channel *channel __free(kfree) =
+ kzalloc(sizeof(*channel), GFP_KERNEL);
if (!channel)
return ERR_PTR(-ENOMEM);
err = __fwnode_iio_channel_get(channel, fwnode, index);
if (err)
- goto err_free_channel;
+ return ERR_PTR(err);
- return channel;
-
-err_free_channel:
- kfree(channel);
- return ERR_PTR(err);
+ return_ptr(channel);
}
static struct iio_channel *
@@ -291,7 +283,6 @@ EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name);
static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
{
struct fwnode_handle *fwnode = dev_fwnode(dev);
- struct iio_channel *chans;
int i, mapind, nummaps = 0;
int ret;
@@ -307,7 +298,8 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
return ERR_PTR(-ENODEV);
/* NULL terminated array to save passing size */
- chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
+ struct iio_channel *chans __free(kfree) =
+ kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
if (!chans)
return ERR_PTR(-ENOMEM);
@@ -317,12 +309,11 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
if (ret)
goto error_free_chans;
}
- return chans;
+ return_ptr(chans);
error_free_chans:
for (i = 0; i < mapind; i++)
iio_device_put(chans[i].indio_dev);
- kfree(chans);
return ERR_PTR(ret);
}
@@ -330,28 +321,28 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
const char *channel_name)
{
struct iio_map_internal *c_i = NULL, *c = NULL;
- struct iio_channel *channel;
int err;
if (!(name || channel_name))
return ERR_PTR(-ENODEV);
/* first find matching entry the channel map */
- mutex_lock(&iio_map_list_lock);
- list_for_each_entry(c_i, &iio_map_list, l) {
- if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
- (channel_name &&
- strcmp(channel_name, c_i->map->consumer_channel) != 0))
- continue;
- c = c_i;
- iio_device_get(c->indio_dev);
- break;
+ scoped_guard(mutex, &iio_map_list_lock) {
+ list_for_each_entry(c_i, &iio_map_list, l) {
+ if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
+ (channel_name &&
+ strcmp(channel_name, c_i->map->consumer_channel) != 0))
+ continue;
+ c = c_i;
+ iio_device_get(c->indio_dev);
+ break;
+ }
}
- mutex_unlock(&iio_map_list_lock);
if (!c)
return ERR_PTR(-ENODEV);
- channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ struct iio_channel *channel __free(kfree) =
+ kzalloc(sizeof(*channel), GFP_KERNEL);
if (!channel) {
err = -ENOMEM;
goto error_no_mem;
@@ -366,14 +357,12 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
if (!channel->channel) {
err = -EINVAL;
- goto error_no_chan;
+ goto error_no_mem;
}
}
- return channel;
+ return_ptr(channel);
-error_no_chan:
- kfree(channel);
error_no_mem:
iio_device_put(c->indio_dev);
return ERR_PTR(err);
@@ -450,8 +439,8 @@ EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name);
struct iio_channel *iio_channel_get_all(struct device *dev)
{
const char *name;
- struct iio_channel *chans;
struct iio_map_internal *c = NULL;
+ struct iio_channel *fw_chans;
int nummaps = 0;
int mapind = 0;
int i, ret;
@@ -459,17 +448,17 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
if (!dev)
return ERR_PTR(-EINVAL);
- chans = fwnode_iio_channel_get_all(dev);
+ fw_chans = fwnode_iio_channel_get_all(dev);
/*
* We only want to carry on if the error is -ENODEV. Anything else
* should be reported up the stack.
*/
- if (!IS_ERR(chans) || PTR_ERR(chans) != -ENODEV)
- return chans;
+ if (!IS_ERR(fw_chans) || PTR_ERR(fw_chans) != -ENODEV)
+ return fw_chans;
name = dev_name(dev);
- mutex_lock(&iio_map_list_lock);
+ guard(mutex)(&iio_map_list_lock);
/* first count the matching maps */
list_for_each_entry(c, &iio_map_list, l)
if (name && strcmp(name, c->map->consumer_dev_name) != 0)
@@ -477,17 +466,14 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
else
nummaps++;
- if (nummaps == 0) {
- ret = -ENODEV;
- goto error_ret;
- }
+ if (nummaps == 0)
+ return ERR_PTR(-ENODEV);
/* NULL terminated array to save passing size */
- chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
- if (!chans) {
- ret = -ENOMEM;
- goto error_ret;
- }
+ struct iio_channel *chans __free(kfree) =
+ kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
+ if (!chans)
+ return ERR_PTR(-ENOMEM);
/* for each map fill in the chans element */
list_for_each_entry(c, &iio_map_list, l) {
@@ -509,17 +495,12 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
ret = -ENODEV;
goto error_free_chans;
}
- mutex_unlock(&iio_map_list_lock);
- return chans;
+ return_ptr(chans);
error_free_chans:
for (i = 0; i < nummaps; i++)
iio_device_put(chans[i].indio_dev);
- kfree(chans);
-error_ret:
- mutex_unlock(&iio_map_list_lock);
-
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(iio_channel_get_all);
@@ -590,38 +571,24 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
int iio_read_channel_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_channel_raw);
int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
@@ -708,20 +675,13 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
int *processed, unsigned int scale)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
- scale);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_convert_raw_to_processed_unlocked(chan, raw, processed,
+ scale);
}
EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
@@ -729,19 +689,12 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
enum iio_chan_info_enum attribute)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read(chan, val, val2, attribute);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read(chan, val, val2, attribute);
}
EXPORT_SYMBOL_GPL(iio_read_channel_attribute);
@@ -757,30 +710,26 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val,
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
ret = iio_channel_read(chan, val, NULL,
IIO_CHAN_INFO_PROCESSED);
if (ret < 0)
- goto err_unlock;
+ return ret;
*val *= scale;
+
+ return 0;
} else {
ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
if (ret < 0)
- goto err_unlock;
- ret = iio_convert_raw_to_processed_unlocked(chan, *val, val,
- scale);
- }
+ return ret;
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
-
- return ret;
+ return iio_convert_raw_to_processed_unlocked(chan, *val, val,
+ scale);
+ }
}
EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale);
@@ -813,19 +762,12 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan,
enum iio_chan_info_enum attribute)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
-
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
- ret = iio_channel_read_avail(chan, vals, type, length, attribute);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read_avail(chan, vals, type, length, attribute);
}
EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute);
@@ -892,20 +834,13 @@ static int iio_channel_read_max(struct iio_channel *chan,
int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
int type;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);
@@ -955,40 +890,27 @@ static int iio_channel_read_min(struct iio_channel *chan,
int iio_read_min_channel_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
int type;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_min_channel_raw);
int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret = 0;
- /* Need to verify underlying driver has not gone away */
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
*type = chan->channel->type;
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(iio_get_channel_type);
@@ -1003,19 +925,12 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,
enum iio_chan_info_enum attribute)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- ret = iio_channel_write(chan, val, val2, attribute);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
-
- return ret;
+ return iio_channel_write(chan, val, val2, attribute);
}
EXPORT_SYMBOL_GPL(iio_write_channel_attribute);
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index fd5a9879a582..9a587d403118 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -73,6 +73,18 @@ config APDS9300
To compile this driver as a module, choose M here: the
module will be called apds9300.
+config APDS9306
+ tristate "Avago APDS9306 Ambient Light Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_GTS_HELPER
+ help
+ If you say Y or M here, you get support for Avago APDS9306
+ Ambient Light Sensor.
+
+ If built as a dynamically linked module, it will be called
+ apds9306.
+
config APDS9960
tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
select REGMAP_I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 2e5fdb33e0e9..a30f906e91ba 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_ADUX1020) += adux1020.o
obj-$(CONFIG_AL3010) += al3010.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
+obj-$(CONFIG_APDS9306) += apds9306.o
obj-$(CONFIG_APDS9960) += apds9960.o
obj-$(CONFIG_AS73211) += as73211.o
obj-$(CONFIG_BH1750) += bh1750.o
diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c
new file mode 100644
index 000000000000..d6627b3e6000
--- /dev/null
+++ b/drivers/iio/light/apds9306.c
@@ -0,0 +1,1361 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * APDS-9306/APDS-9306-065 Ambient Light Sensor
+ * I2C Address: 0x52
+ * Datasheet: https://docs.broadcom.com/doc/AV02-4755EN
+ *
+ * Copyright (C) 2024 Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/iio-gts-helper.h>
+#include <linux/iio/events.h>
+#include <linux/iio/sysfs.h>
+
+#include <asm/unaligned.h>
+
+#define APDS9306_MAIN_CTRL_REG 0x00
+#define APDS9306_ALS_MEAS_RATE_REG 0x04
+#define APDS9306_ALS_GAIN_REG 0x05
+#define APDS9306_PART_ID_REG 0x06
+#define APDS9306_MAIN_STATUS_REG 0x07
+#define APDS9306_CLEAR_DATA_0_REG 0x0A
+#define APDS9306_CLEAR_DATA_1_REG 0x0B
+#define APDS9306_CLEAR_DATA_2_REG 0x0C
+#define APDS9306_ALS_DATA_0_REG 0x0D
+#define APDS9306_ALS_DATA_1_REG 0x0E
+#define APDS9306_ALS_DATA_2_REG 0x0F
+#define APDS9306_INT_CFG_REG 0x19
+#define APDS9306_INT_PERSISTENCE_REG 0x1A
+#define APDS9306_ALS_THRES_UP_0_REG 0x21
+#define APDS9306_ALS_THRES_UP_1_REG 0x22
+#define APDS9306_ALS_THRES_UP_2_REG 0x23
+#define APDS9306_ALS_THRES_LOW_0_REG 0x24
+#define APDS9306_ALS_THRES_LOW_1_REG 0x25
+#define APDS9306_ALS_THRES_LOW_2_REG 0x26
+#define APDS9306_ALS_THRES_VAR_REG 0x27
+
+#define APDS9306_ALS_INT_STAT_MASK BIT(4)
+#define APDS9306_ALS_DATA_STAT_MASK BIT(3)
+
+#define APDS9306_ALS_THRES_VAL_MAX (BIT(20) - 1)
+#define APDS9306_ALS_THRES_VAR_NUM_VALS 8
+#define APDS9306_ALS_PERSIST_NUM_VALS 16
+#define APDS9306_ALS_READ_DATA_DELAY_US (20 * USEC_PER_MSEC)
+#define APDS9306_NUM_REPEAT_RATES 7
+#define APDS9306_INT_SRC_CLEAR 0
+#define APDS9306_INT_SRC_ALS 1
+#define APDS9306_SAMP_FREQ_10HZ 0
+
+/**
+ * struct part_id_gts_multiplier - Part no. and corresponding gts multiplier
+ *
+ * GTS (Gain Time Scale) are helper functions for Light sensors which along
+ * with hardware gains also has gains associated with Integration times.
+ *
+ * There are two variants of the device with slightly different characteristics,
+ * they have same ADC count for different Lux levels as mentioned in the
+ * datasheet. This multiplier array is used to store the derived Lux per count
+ * value for the two variants to be used by the GTS helper functions.
+ *
+ * @part_id: Part ID of the device
+ * @max_scale_int: Multiplier for iio_init_iio_gts()
+ * @max_scale_nano: Multiplier for iio_init_iio_gts()
+ */
+struct part_id_gts_multiplier {
+ int part_id;
+ int max_scale_int;
+ int max_scale_nano;
+};
+
+/*
+ * As per the datasheet, at HW Gain = 3x, Integration time 100mS (32x),
+ * typical 2000 ADC counts are observed for 49.8 uW per sq cm (340.134 lux)
+ * for apds9306 and 43 uW per sq cm (293.69 lux) for apds9306-065.
+ * Assuming lux per count is linear across all integration time ranges.
+ *
+ * Lux = (raw + offset) * scale; offset can be any value by userspace.
+ * HG = Hardware Gain; ITG = Gain by changing integration time.
+ * Scale table by IIO GTS Helpers = (1 / HG) * (1 / ITG) * Multiplier.
+ *
+ * The Lux values provided in the datasheet are at ITG=32x and HG=3x,
+ * at typical 2000 count for both variants of the device.
+ *
+ * Lux per ADC count at 3x and 32x for apds9306 = 340.134 / 2000
+ * Lux per ADC count at 3x and 32x for apds9306-065 = 293.69 / 2000
+ *
+ * The Multiplier for the scale table provided to userspace:
+ * IIO GTS scale Multiplier for apds9306 = (340.134 / 2000) * 32 * 3 = 16.326432
+ * and for apds9306-065 = (293.69 / 2000) * 32 * 3 = 14.09712
+ */
+static const struct part_id_gts_multiplier apds9306_gts_mul[] = {
+ {
+ .part_id = 0xB1,
+ .max_scale_int = 16,
+ .max_scale_nano = 3264320,
+ }, {
+ .part_id = 0xB3,
+ .max_scale_int = 14,
+ .max_scale_nano = 9712000,
+ },
+};
+
+static const int apds9306_repeat_rate_freq[APDS9306_NUM_REPEAT_RATES][2] = {
+ { 40, 0 },
+ { 20, 0 },
+ { 10, 0 },
+ { 5, 0 },
+ { 2, 0 },
+ { 1, 0 },
+ { 0, 500000 },
+};
+
+static const int apds9306_repeat_rate_period[APDS9306_NUM_REPEAT_RATES] = {
+ 25000, 50000, 100000, 200000, 500000, 1000000, 2000000,
+};
+
+/**
+ * struct apds9306_regfields - apds9306 regmap fields definitions
+ *
+ * @sw_reset: Software reset regfield
+ * @en: Enable regfield
+ * @intg_time: Resolution regfield
+ * @repeat_rate: Measurement Rate regfield
+ * @gain: Hardware gain regfield
+ * @int_src: Interrupt channel regfield
+ * @int_thresh_var_en: Interrupt variance threshold regfield
+ * @int_en: Interrupt enable regfield
+ * @int_persist_val: Interrupt persistence regfield
+ * @int_thresh_var_val: Interrupt threshold variance value regfield
+ */
+struct apds9306_regfields {
+ struct regmap_field *sw_reset;
+ struct regmap_field *en;
+ struct regmap_field *intg_time;
+ struct regmap_field *repeat_rate;
+ struct regmap_field *gain;
+ struct regmap_field *int_src;
+ struct regmap_field *int_thresh_var_en;
+ struct regmap_field *int_en;
+ struct regmap_field *int_persist_val;
+ struct regmap_field *int_thresh_var_val;
+};
+
+/**
+ * struct apds9306_data - apds9306 private data and registers definitions
+ *
+ * @dev: Pointer to the device structure
+ * @gts: IIO Gain Time Scale structure
+ * @mutex: Lock for protecting adc reads, device settings changes where
+ * some calculations are required before or after setting or
+ * getting the raw settings values from regmap writes or reads
+ * respectively.
+ * @regmap: Regmap structure pointer
+ * @rf: Regmap register fields structure
+ * @nlux_per_count: Nano lux per ADC count for a particular model
+ * @read_data_available: Flag set by IRQ handler for ADC data available
+ */
+struct apds9306_data {
+ struct device *dev;
+ struct iio_gts gts;
+
+ struct mutex mutex;
+
+ struct regmap *regmap;
+ struct apds9306_regfields rf;
+
+ int nlux_per_count;
+ int read_data_available;
+};
+
+/*
+ * Available scales with gain 1x - 18x, timings 3.125, 25, 50, 100, 200, 400 ms
+ * Time impacts to gain: 1x, 8x, 16x, 32x, 64x, 128x
+ */
+#define APDS9306_GSEL_1X 0x00
+#define APDS9306_GSEL_3X 0x01
+#define APDS9306_GSEL_6X 0x02
+#define APDS9306_GSEL_9X 0x03
+#define APDS9306_GSEL_18X 0x04
+
+static const struct iio_gain_sel_pair apds9306_gains[] = {
+ GAIN_SCALE_GAIN(1, APDS9306_GSEL_1X),
+ GAIN_SCALE_GAIN(3, APDS9306_GSEL_3X),
+ GAIN_SCALE_GAIN(6, APDS9306_GSEL_6X),
+ GAIN_SCALE_GAIN(9, APDS9306_GSEL_9X),
+ GAIN_SCALE_GAIN(18, APDS9306_GSEL_18X),
+};
+
+#define APDS9306_MEAS_MODE_400MS 0x00
+#define APDS9306_MEAS_MODE_200MS 0x01
+#define APDS9306_MEAS_MODE_100MS 0x02
+#define APDS9306_MEAS_MODE_50MS 0x03
+#define APDS9306_MEAS_MODE_25MS 0x04
+#define APDS9306_MEAS_MODE_3125US 0x05
+
+static const struct iio_itime_sel_mul apds9306_itimes[] = {
+ GAIN_SCALE_ITIME_US(400000, APDS9306_MEAS_MODE_400MS, BIT(7)),
+ GAIN_SCALE_ITIME_US(200000, APDS9306_MEAS_MODE_200MS, BIT(6)),
+ GAIN_SCALE_ITIME_US(100000, APDS9306_MEAS_MODE_100MS, BIT(5)),
+ GAIN_SCALE_ITIME_US(50000, APDS9306_MEAS_MODE_50MS, BIT(4)),
+ GAIN_SCALE_ITIME_US(25000, APDS9306_MEAS_MODE_25MS, BIT(3)),
+ GAIN_SCALE_ITIME_US(3125, APDS9306_MEAS_MODE_3125US, BIT(0)),
+};
+
+static const struct iio_event_spec apds9306_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH_ADAPTIVE,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec apds9306_channels_with_events[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ .event_spec = apds9306_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9306_event_spec),
+ }, {
+ .type = IIO_INTENSITY,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .modified = 1,
+ .event_spec = apds9306_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9306_event_spec),
+ },
+};
+
+static const struct iio_chan_spec apds9306_channels_without_events[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_INTENSITY,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ },
+};
+
+/* INT_PERSISTENCE available */
+static IIO_CONST_ATTR(thresh_either_period_available, "[0 1 15]");
+
+/* ALS_THRESH_VAR available */
+static IIO_CONST_ATTR(thresh_adaptive_either_values_available, "[0 1 7]");
+
+static struct attribute *apds9306_event_attributes[] = {
+ &iio_const_attr_thresh_either_period_available.dev_attr.attr,
+ &iio_const_attr_thresh_adaptive_either_values_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group apds9306_event_attr_group = {
+ .attrs = apds9306_event_attributes,
+};
+
+static const struct regmap_range apds9306_readable_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_THRES_VAR_REG)
+};
+
+static const struct regmap_range apds9306_writable_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_GAIN_REG),
+ regmap_reg_range(APDS9306_INT_CFG_REG, APDS9306_ALS_THRES_VAR_REG)
+};
+
+static const struct regmap_range apds9306_volatile_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG),
+ regmap_reg_range(APDS9306_CLEAR_DATA_0_REG, APDS9306_ALS_DATA_2_REG)
+};
+
+static const struct regmap_range apds9306_precious_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG)
+};
+
+static const struct regmap_access_table apds9306_readable_table = {
+ .yes_ranges = apds9306_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_readable_ranges)
+};
+
+static const struct regmap_access_table apds9306_writable_table = {
+ .yes_ranges = apds9306_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_writable_ranges)
+};
+
+static const struct regmap_access_table apds9306_volatile_table = {
+ .yes_ranges = apds9306_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_volatile_ranges)
+};
+
+static const struct regmap_access_table apds9306_precious_table = {
+ .yes_ranges = apds9306_precious_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_precious_ranges)
+};
+
+static const struct regmap_config apds9306_regmap = {
+ .name = "apds9306_regmap",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &apds9306_readable_table,
+ .wr_table = &apds9306_writable_table,
+ .volatile_table = &apds9306_volatile_table,
+ .precious_table = &apds9306_precious_table,
+ .max_register = APDS9306_ALS_THRES_VAR_REG,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct reg_field apds9306_rf_sw_reset =
+ REG_FIELD(APDS9306_MAIN_CTRL_REG, 4, 4);
+
+static const struct reg_field apds9306_rf_en =
+ REG_FIELD(APDS9306_MAIN_CTRL_REG, 1, 1);
+
+static const struct reg_field apds9306_rf_intg_time =
+ REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 4, 6);
+
+static const struct reg_field apds9306_rf_repeat_rate =
+ REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 0, 2);
+
+static const struct reg_field apds9306_rf_gain =
+ REG_FIELD(APDS9306_ALS_GAIN_REG, 0, 2);
+
+static const struct reg_field apds9306_rf_int_src =
+ REG_FIELD(APDS9306_INT_CFG_REG, 4, 5);
+
+static const struct reg_field apds9306_rf_int_thresh_var_en =
+ REG_FIELD(APDS9306_INT_CFG_REG, 3, 3);
+
+static const struct reg_field apds9306_rf_int_en =
+ REG_FIELD(APDS9306_INT_CFG_REG, 2, 2);
+
+static const struct reg_field apds9306_rf_int_persist_val =
+ REG_FIELD(APDS9306_INT_PERSISTENCE_REG, 4, 7);
+
+static const struct reg_field apds9306_rf_int_thresh_var_val =
+ REG_FIELD(APDS9306_ALS_THRES_VAR_REG, 0, 2);
+
+static int apds9306_regfield_init(struct apds9306_data *data)
+{
+ struct device *dev = data->dev;
+ struct regmap *regmap = data->regmap;
+ struct regmap_field *tmp;
+ struct apds9306_regfields *rf = &data->rf;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_sw_reset);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->sw_reset = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_intg_time);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->intg_time = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_repeat_rate);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->repeat_rate = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_gain);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->gain = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_src);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_src = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_thresh_var_en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_persist_val);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_persist_val = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_val);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_thresh_var_val = tmp;
+
+ return 0;
+}
+
+static int apds9306_power_state(struct apds9306_data *data, bool state)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ /* Reset not included as it causes ugly I2C bus error */
+ if (state) {
+ ret = regmap_field_write(rf->en, 1);
+ if (ret)
+ return ret;
+ /* 5ms wake up time */
+ fsleep(5000);
+ return 0;
+ }
+
+ return regmap_field_write(rf->en, 0);
+}
+
+static int apds9306_read_data(struct apds9306_data *data, int *val, int reg)
+{
+ struct device *dev = data->dev;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct apds9306_regfields *rf = &data->rf;
+ u64 ev_code;
+ int ret, delay, intg_time, intg_time_idx, repeat_rate_idx, int_src;
+ int status = 0;
+ u8 buff[3];
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ intg_time = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (intg_time < 0)
+ return intg_time;
+
+ /* Whichever is greater - integration time period or sampling period. */
+ delay = max(intg_time, apds9306_repeat_rate_period[repeat_rate_idx]);
+
+ /*
+ * Clear stale data flag that might have been set by the interrupt
+ * handler if it got data available flag set in the status reg.
+ */
+ data->read_data_available = 0;
+
+ /*
+ * If this function runs parallel with the interrupt handler, either
+ * this reads and clears the status registers or the interrupt handler
+ * does. The interrupt handler sets a flag for read data available
+ * in our private structure which we read here.
+ */
+ ret = regmap_read_poll_timeout(data->regmap, APDS9306_MAIN_STATUS_REG,
+ status, data->read_data_available ||
+ (status & (APDS9306_ALS_DATA_STAT_MASK |
+ APDS9306_ALS_INT_STAT_MASK)),
+ APDS9306_ALS_READ_DATA_DELAY_US, delay * 2);
+ if (ret)
+ return ret;
+
+ /* If we reach here before the interrupt handler we push an event */
+ if ((status & APDS9306_ALS_INT_STAT_MASK)) {
+ if (int_src == APDS9306_INT_SRC_ALS)
+ ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ else
+ ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_MOD_LIGHT_CLEAR,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+
+ iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev));
+ }
+
+ ret = regmap_bulk_read(data->regmap, reg, buff, sizeof(buff));
+ if (ret) {
+ dev_err_ratelimited(dev, "read data failed\n");
+ return ret;
+ }
+
+ *val = get_unaligned_le24(&buff);
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return 0;
+}
+
+static int apds9306_intg_time_get(struct apds9306_data *data, int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, intg_time_idx;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (ret < 0)
+ return ret;
+
+ *val2 = ret;
+
+ return 0;
+}
+
+static int apds9306_intg_time_set(struct apds9306_data *data, int val2)
+{
+ struct device *dev = data->dev;
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, intg_old, gain_old, gain_new, gain_new_closest, intg_time_idx;
+ int gain_idx;
+ bool ok;
+
+ if (!iio_gts_valid_time(&data->gts, val2)) {
+ dev_err_ratelimited(dev, "Unsupported integration time %u\n", val2);
+ return -EINVAL;
+ }
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->gain, &gain_idx);
+ if (ret)
+ return ret;
+
+ intg_old = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (ret < 0)
+ return ret;
+
+ if (intg_old == val2)
+ return 0;
+
+ gain_old = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (gain_old < 0)
+ return gain_old;
+
+ iio_gts_find_new_gain_by_old_gain_time(&data->gts, gain_old, intg_old,
+ val2, &gain_new);
+
+ if (gain_new < 0) {
+ dev_err_ratelimited(dev, "Unsupported gain with time\n");
+ return gain_new;
+ }
+
+ gain_new_closest = iio_find_closest_gain_low(&data->gts, gain_new, &ok);
+ if (gain_new_closest < 0) {
+ gain_new_closest = iio_gts_get_min_gain(&data->gts);
+ if (gain_new_closest < 0)
+ return gain_new_closest;
+ }
+ if (!ok)
+ dev_dbg(dev, "Unable to find optimum gain, setting minimum");
+
+ ret = iio_gts_find_sel_by_int_time(&data->gts, val2);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_field_write(rf->intg_time, ret);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_sel_by_gain(&data->gts, gain_new_closest);
+ if (ret < 0)
+ return ret;
+
+ return regmap_field_write(rf->gain, ret);
+}
+
+static int apds9306_sampling_freq_get(struct apds9306_data *data, int *val,
+ int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, repeat_rate_idx;
+
+ ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx);
+ if (ret)
+ return ret;
+
+ if (repeat_rate_idx >= ARRAY_SIZE(apds9306_repeat_rate_freq))
+ return -EINVAL;
+
+ *val = apds9306_repeat_rate_freq[repeat_rate_idx][0];
+ *val2 = apds9306_repeat_rate_freq[repeat_rate_idx][1];
+
+ return 0;
+}
+
+static int apds9306_sampling_freq_set(struct apds9306_data *data, int val,
+ int val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(apds9306_repeat_rate_freq); i++) {
+ if (apds9306_repeat_rate_freq[i][0] == val &&
+ apds9306_repeat_rate_freq[i][1] == val2)
+ return regmap_field_write(rf->repeat_rate, i);
+ }
+
+ return -EINVAL;
+}
+
+static int apds9306_scale_get(struct apds9306_data *data, int *val, int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int gain, intg, ret, intg_time_idx, gain_idx;
+
+ ret = regmap_field_read(rf->gain, &gain_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (gain < 0)
+ return gain;
+
+ intg = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (intg < 0)
+ return intg;
+
+ return iio_gts_get_scale(&data->gts, gain, intg, val, val2);
+}
+
+static int apds9306_scale_set(struct apds9306_data *data, int val, int val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int i, ret, time_sel, gain_sel, intg_time_idx;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ intg_time_idx, val, val2, &gain_sel);
+ if (ret) {
+ for (i = 0; i < data->gts.num_itime; i++) {
+ time_sel = data->gts.itime_table[i].sel;
+
+ if (time_sel == intg_time_idx)
+ continue;
+
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ time_sel, val, val2, &gain_sel);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return -EINVAL;
+
+ ret = regmap_field_write(rf->intg_time, time_sel);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_field_write(rf->gain, gain_sel);
+}
+
+static int apds9306_event_period_get(struct apds9306_data *data, int *val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int period, ret;
+
+ ret = regmap_field_read(rf->int_persist_val, &period);
+ if (ret)
+ return ret;
+
+ if (!in_range(period, 0, APDS9306_ALS_PERSIST_NUM_VALS))
+ return -EINVAL;
+
+ *val = period;
+
+ return ret;
+}
+
+static int apds9306_event_period_set(struct apds9306_data *data, int val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+
+ if (!in_range(val, 0, APDS9306_ALS_PERSIST_NUM_VALS))
+ return -EINVAL;
+
+ return regmap_field_write(rf->int_persist_val, val);
+}
+
+static int apds9306_event_thresh_get(struct apds9306_data *data, int dir,
+ int *val)
+{
+ int var, ret;
+ u8 buff[3];
+
+ if (dir == IIO_EV_DIR_RISING)
+ var = APDS9306_ALS_THRES_UP_0_REG;
+ else if (dir == IIO_EV_DIR_FALLING)
+ var = APDS9306_ALS_THRES_LOW_0_REG;
+ else
+ return -EINVAL;
+
+ ret = regmap_bulk_read(data->regmap, var, buff, sizeof(buff));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_le24(&buff);
+
+ return 0;
+}
+
+static int apds9306_event_thresh_set(struct apds9306_data *data, int dir,
+ int val)
+{
+ int var;
+ u8 buff[3];
+
+ if (dir == IIO_EV_DIR_RISING)
+ var = APDS9306_ALS_THRES_UP_0_REG;
+ else if (dir == IIO_EV_DIR_FALLING)
+ var = APDS9306_ALS_THRES_LOW_0_REG;
+ else
+ return -EINVAL;
+
+ if (!in_range(val, 0, APDS9306_ALS_THRES_VAL_MAX))
+ return -EINVAL;
+
+ put_unaligned_le24(val, buff);
+
+ return regmap_bulk_write(data->regmap, var, buff, sizeof(buff));
+}
+
+static int apds9306_event_thresh_adaptive_get(struct apds9306_data *data, int *val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int thr_adpt, ret;
+
+ ret = regmap_field_read(rf->int_thresh_var_val, &thr_adpt);
+ if (ret)
+ return ret;
+
+ if (!in_range(thr_adpt, 0, APDS9306_ALS_THRES_VAR_NUM_VALS))
+ return -EINVAL;
+
+ *val = thr_adpt;
+
+ return ret;
+}
+
+static int apds9306_event_thresh_adaptive_set(struct apds9306_data *data, int val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+
+ if (!in_range(val, 0, APDS9306_ALS_THRES_VAR_NUM_VALS))
+ return -EINVAL;
+
+ return regmap_field_write(rf->int_thresh_var_val, val);
+}
+
+static int apds9306_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ int ret, reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->channel2 == IIO_MOD_LIGHT_CLEAR)
+ reg = APDS9306_CLEAR_DATA_0_REG;
+ else
+ reg = APDS9306_ALS_DATA_0_REG;
+ /*
+ * Changing device parameters during adc operation, resets
+ * the ADC which has to avoided.
+ */
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = apds9306_read_data(data, val, reg);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ ret = apds9306_intg_time_get(data, val2);
+ if (ret)
+ return ret;
+ *val = 0;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = apds9306_sampling_freq_get(data, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ ret = apds9306_scale_get(data, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+};
+
+static int apds9306_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return iio_gts_avail_times(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SCALE:
+ return iio_gts_all_avail_scales(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *length = ARRAY_SIZE(apds9306_repeat_rate_freq) * 2;
+ *vals = (const int *)apds9306_repeat_rate_freq;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->mutex);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val)
+ return -EINVAL;
+ return apds9306_intg_time_set(data, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return apds9306_scale_set(data, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return apds9306_sampling_freq_set(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t apds9306_irq_handler(int irq, void *priv)
+{
+ struct iio_dev *indio_dev = priv;
+ struct apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ u64 ev_code;
+ int ret, status, int_src;
+
+ /*
+ * The interrupt line is released and the interrupt flag is
+ * cleared as a result of reading the status register. All the
+ * status flags are cleared as a result of this read.
+ */
+ ret = regmap_read(data->regmap, APDS9306_MAIN_STATUS_REG, &status);
+ if (ret < 0) {
+ dev_err_ratelimited(data->dev, "status reg read failed\n");
+ return IRQ_HANDLED;
+ }
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ if ((status & APDS9306_ALS_INT_STAT_MASK)) {
+ if (int_src == APDS9306_INT_SRC_ALS)
+ ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ else
+ ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_MOD_LIGHT_CLEAR,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+
+ iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev));
+ }
+
+ /*
+ * If a one-shot read through sysfs is underway at the same time
+ * as this interrupt handler is executing and a read data available
+ * flag was set, this flag is set to inform read_poll_timeout()
+ * to exit.
+ */
+ if ((status & APDS9306_ALS_DATA_STAT_MASK))
+ data->read_data_available = 1;
+
+ return IRQ_HANDLED;
+}
+
+static int apds9306_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 apds9306_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD)
+ ret = apds9306_event_period_get(data, val);
+ else
+ ret = apds9306_event_thresh_get(data, dir, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ ret = apds9306_event_thresh_adaptive_get(data, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_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 apds9306_data *data = iio_priv(indio_dev);
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD)
+ return apds9306_event_period_set(data, val);
+
+ return apds9306_event_thresh_set(data, dir, val);
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ return apds9306_event_thresh_adaptive_set(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_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 apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ int int_en, int_src, ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH: {
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->int_en, &int_en);
+ if (ret)
+ return ret;
+
+ if (chan->type == IIO_LIGHT)
+ return int_en && (int_src == APDS9306_INT_SRC_ALS);
+
+ if (chan->type == IIO_INTENSITY)
+ return int_en && (int_src == APDS9306_INT_SRC_CLEAR);
+
+ return -EINVAL;
+ }
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ ret = regmap_field_read(rf->int_thresh_var_en, &int_en);
+ if (ret)
+ return ret;
+
+ return int_en;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_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 apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, enabled;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH: {
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_field_read(rf->int_en, &enabled);
+ if (ret)
+ return ret;
+
+ /*
+ * If interrupt is enabled, the channel is set before enabling
+ * the interrupt. In case of disable, no need to switch
+ * channels. In case of different channel is selected while
+ * interrupt in on, just change the channel.
+ */
+ if (state) {
+ if (chan->type == IIO_LIGHT)
+ ret = regmap_field_write(rf->int_src, 1);
+ else if (chan->type == IIO_INTENSITY)
+ ret = regmap_field_write(rf->int_src, 0);
+ else
+ return -EINVAL;
+
+ if (ret)
+ return ret;
+
+ if (enabled)
+ return 0;
+
+ ret = regmap_field_write(rf->int_en, 1);
+ if (ret)
+ return ret;
+
+ return pm_runtime_resume_and_get(data->dev);
+ } else {
+ if (!enabled)
+ return 0;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return ret;
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return 0;
+ }
+ }
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ if (state)
+ return regmap_field_write(rf->int_thresh_var_en, 1);
+ else
+ return regmap_field_write(rf->int_thresh_var_en, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info apds9306_info_no_events = {
+ .read_avail = apds9306_read_avail,
+ .read_raw = apds9306_read_raw,
+ .write_raw = apds9306_write_raw,
+ .write_raw_get_fmt = apds9306_write_raw_get_fmt,
+};
+
+static const struct iio_info apds9306_info = {
+ .read_avail = apds9306_read_avail,
+ .read_raw = apds9306_read_raw,
+ .write_raw = apds9306_write_raw,
+ .write_raw_get_fmt = apds9306_write_raw_get_fmt,
+ .read_event_value = apds9306_read_event,
+ .write_event_value = apds9306_write_event,
+ .read_event_config = apds9306_read_event_config,
+ .write_event_config = apds9306_write_event_config,
+ .event_attrs = &apds9306_event_attr_group,
+};
+
+static int apds9306_init_iio_gts(struct apds9306_data *data)
+{
+ int i, ret, part_id;
+
+ ret = regmap_read(data->regmap, APDS9306_PART_ID_REG, &part_id);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(apds9306_gts_mul); i++)
+ if (part_id == apds9306_gts_mul[i].part_id)
+ break;
+
+ if (i == ARRAY_SIZE(apds9306_gts_mul))
+ return -ENOENT;
+
+ return devm_iio_init_iio_gts(data->dev,
+ apds9306_gts_mul[i].max_scale_int,
+ apds9306_gts_mul[i].max_scale_nano,
+ apds9306_gains, ARRAY_SIZE(apds9306_gains),
+ apds9306_itimes, ARRAY_SIZE(apds9306_itimes),
+ &data->gts);
+}
+
+static void apds9306_powerdown(void *ptr)
+{
+ struct apds9306_data *data = (struct apds9306_data *)ptr;
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ ret = regmap_field_write(rf->int_thresh_var_en, 0);
+ if (ret)
+ return;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return;
+
+ apds9306_power_state(data, false);
+}
+
+static int apds9306_device_init(struct apds9306_data *data)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ ret = apds9306_init_iio_gts(data);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->intg_time, APDS9306_MEAS_MODE_100MS);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->repeat_rate, APDS9306_SAMP_FREQ_10HZ);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->gain, APDS9306_GSEL_3X);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->int_src, APDS9306_INT_SRC_ALS);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return ret;
+
+ return regmap_field_write(rf->int_thresh_var_en, 0);
+}
+
+static int apds9306_pm_init(struct apds9306_data *data)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = apds9306_power_state(data, true);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 5000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_get(dev);
+
+ return 0;
+}
+
+static int apds9306_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct apds9306_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+
+ mutex_init(&data->mutex);
+
+ data->regmap = devm_regmap_init_i2c(client, &apds9306_regmap);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "regmap initialization failed\n");
+
+ data->dev = dev;
+ i2c_set_clientdata(client, indio_dev);
+
+ ret = apds9306_regfield_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "regfield initialization failed\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+ indio_dev->name = "apds9306";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ if (client->irq) {
+ indio_dev->info = &apds9306_info;
+ indio_dev->channels = apds9306_channels_with_events;
+ indio_dev->num_channels = ARRAY_SIZE(apds9306_channels_with_events);
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ apds9306_irq_handler, IRQF_ONESHOT,
+ "apds9306_event", indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to assign interrupt.\n");
+ } else {
+ indio_dev->info = &apds9306_info_no_events;
+ indio_dev->channels = apds9306_channels_without_events;
+ indio_dev->num_channels =
+ ARRAY_SIZE(apds9306_channels_without_events);
+ }
+
+ ret = apds9306_pm_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed pm init\n");
+
+ ret = apds9306_device_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init device\n");
+
+ ret = devm_add_action_or_reset(dev, apds9306_powerdown, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add action or reset\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed iio device registration\n");
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int apds9306_runtime_suspend(struct device *dev)
+{
+ struct apds9306_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return apds9306_power_state(data, false);
+}
+
+static int apds9306_runtime_resume(struct device *dev)
+{
+ struct apds9306_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return apds9306_power_state(data, true);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(apds9306_pm_ops,
+ apds9306_runtime_suspend,
+ apds9306_runtime_resume,
+ NULL);
+
+static const struct of_device_id apds9306_of_match[] = {
+ { .compatible = "avago,apds9306" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apds9306_of_match);
+
+static struct i2c_driver apds9306_driver = {
+ .driver = {
+ .name = "apds9306",
+ .pm = pm_ptr(&apds9306_pm_ops),
+ .of_match_table = apds9306_of_match,
+ },
+ .probe = apds9306_probe,
+};
+module_i2c_driver(apds9306_driver);
+
+MODULE_AUTHOR("Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>");
+MODULE_DESCRIPTION("APDS9306 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_GTS_HELPER);
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index 50f95c5d2060..d4e17079b2f4 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -291,7 +291,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
if (!iio_dev)
return -ENOMEM;
- dev_set_drvdata(dev, (void *)iio_dev);
+ dev_set_drvdata(dev, iio_dev);
hw = iio_priv(iio_dev);
hw->irq = irq;
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index 7b71ad71d78d..08d471438175 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -693,7 +693,6 @@ MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id);
static const struct acpi_device_id stk3310_acpi_id[] = {
{"STK3310", 0},
{"STK3311", 0},
- {"STK3335", 0},
{}
};
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index 62e9e93d915d..09f53d987c7d 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -27,20 +27,20 @@
#include <linux/bitops.h>
#include <linux/bitfield.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/nvmem-provider.h>
-#include <linux/regmap.h>
+#include <linux/completion.h>
#include <linux/delay.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
+#include <linux/device.h>
#include <linux/gpio/consumer.h>
-#include <linux/regulator/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h> /* For irq_get_irq_data() */
-#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
#include <asm/unaligned.h>
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
index 1ff091b2f764..7d882e15e556 100644
--- a/drivers/iio/pressure/dps310.c
+++ b/drivers/iio/pressure/dps310.c
@@ -171,7 +171,7 @@ static int dps310_temp_workaround(struct dps310_data *data)
int reg;
rc = regmap_read(data->regmap, 0x32, &reg);
- if (rc)
+ if (rc < 0)
return rc;
/*
@@ -256,24 +256,24 @@ static int dps310_startup(struct dps310_data *data)
return dps310_temp_workaround(data);
}
-static int dps310_get_pres_precision(struct dps310_data *data)
+static int dps310_get_pres_precision(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
if (rc < 0)
return rc;
- return BIT(val & GENMASK(2, 0));
+ *val = BIT(reg_val & GENMASK(2, 0));
+
+ return 0;
}
-static int dps310_get_temp_precision(struct dps310_data *data)
+static int dps310_get_temp_precision(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
if (rc < 0)
return rc;
@@ -281,7 +281,9 @@ static int dps310_get_temp_precision(struct dps310_data *data)
* Scale factor is bottom 4 bits of the register, but 1111 is
* reserved so just grab bottom three
*/
- return BIT(val & GENMASK(2, 0));
+ *val = BIT(reg_val & GENMASK(2, 0));
+
+ return 0;
}
/* Called with lock held */
@@ -350,48 +352,56 @@ static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq)
DPS310_TMP_RATE_BITS, val);
}
-static int dps310_get_pres_samp_freq(struct dps310_data *data)
+static int dps310_get_pres_samp_freq(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
if (rc < 0)
return rc;
- return BIT((val & DPS310_PRS_RATE_BITS) >> 4);
+ *val = BIT((reg_val & DPS310_PRS_RATE_BITS) >> 4);
+
+ return 0;
}
-static int dps310_get_temp_samp_freq(struct dps310_data *data)
+static int dps310_get_temp_samp_freq(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
if (rc < 0)
return rc;
- return BIT((val & DPS310_TMP_RATE_BITS) >> 4);
+ *val = BIT((reg_val & DPS310_TMP_RATE_BITS) >> 4);
+
+ return 0;
}
-static int dps310_get_pres_k(struct dps310_data *data)
+static int dps310_get_pres_k(struct dps310_data *data, int *val)
{
- int rc = dps310_get_pres_precision(data);
+ int reg_val, rc;
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
if (rc < 0)
return rc;
- return scale_factors[ilog2(rc)];
+ *val = scale_factors[reg_val & GENMASK(2, 0)];
+
+ return 0;
}
-static int dps310_get_temp_k(struct dps310_data *data)
+static int dps310_get_temp_k(struct dps310_data *data, int *val)
{
- int rc = dps310_get_temp_precision(data);
+ int reg_val, rc;
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
if (rc < 0)
return rc;
- return scale_factors[ilog2(rc)];
+ *val = scale_factors[reg_val & GENMASK(2, 0)];
+
+ return 0;
}
static int dps310_reset_wait(struct dps310_data *data)
@@ -464,7 +474,10 @@ static int dps310_read_pres_raw(struct dps310_data *data)
if (mutex_lock_interruptible(&data->lock))
return -EINTR;
- rate = dps310_get_pres_samp_freq(data);
+ rc = dps310_get_pres_samp_freq(data, &rate);
+ if (rc)
+ goto done;
+
timeout = DPS310_POLL_TIMEOUT_US(rate);
/* Poll for sensor readiness; base the timeout upon the sample rate. */
@@ -510,7 +523,10 @@ static int dps310_read_temp_raw(struct dps310_data *data)
if (mutex_lock_interruptible(&data->lock))
return -EINTR;
- rate = dps310_get_temp_samp_freq(data);
+ rc = dps310_get_temp_samp_freq(data, &rate);
+ if (rc)
+ goto done;
+
timeout = DPS310_POLL_TIMEOUT_US(rate);
/* Poll for sensor readiness; base the timeout upon the sample rate. */
@@ -612,13 +628,13 @@ static int dps310_write_raw(struct iio_dev *iio,
return rc;
}
-static int dps310_calculate_pressure(struct dps310_data *data)
+static int dps310_calculate_pressure(struct dps310_data *data, int *val)
{
int i;
int rc;
int t_ready;
- int kpi = dps310_get_pres_k(data);
- int kti = dps310_get_temp_k(data);
+ int kpi;
+ int kti;
s64 rem = 0ULL;
s64 pressure = 0ULL;
s64 p;
@@ -629,11 +645,13 @@ static int dps310_calculate_pressure(struct dps310_data *data)
s64 kp;
s64 kt;
- if (kpi < 0)
- return kpi;
+ rc = dps310_get_pres_k(data, &kpi);
+ if (rc)
+ return rc;
- if (kti < 0)
- return kti;
+ rc = dps310_get_temp_k(data, &kti);
+ if (rc)
+ return rc;
kp = (s64)kpi;
kt = (s64)kti;
@@ -687,7 +705,9 @@ static int dps310_calculate_pressure(struct dps310_data *data)
if (pressure < 0LL)
return -ERANGE;
- return (int)min_t(s64, pressure, INT_MAX);
+ *val = (int)min_t(s64, pressure, INT_MAX);
+
+ return 0;
}
static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
@@ -697,11 +717,10 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- rc = dps310_get_pres_samp_freq(data);
- if (rc < 0)
+ rc = dps310_get_pres_samp_freq(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
@@ -709,20 +728,17 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
if (rc)
return rc;
- rc = dps310_calculate_pressure(data);
- if (rc < 0)
+ rc = dps310_calculate_pressure(data, val);
+ if (rc)
return rc;
- *val = rc;
*val2 = 1000; /* Convert Pa to KPa per IIO ABI */
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- rc = dps310_get_pres_precision(data);
- if (rc < 0)
+ rc = dps310_get_pres_precision(data, val);
+ if (rc)
return rc;
-
- *val = rc;
return IIO_VAL_INT;
default:
@@ -730,14 +746,15 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
}
}
-static int dps310_calculate_temp(struct dps310_data *data)
+static int dps310_calculate_temp(struct dps310_data *data, int *val)
{
s64 c0;
s64 t;
- int kt = dps310_get_temp_k(data);
+ int kt, rc;
- if (kt < 0)
- return kt;
+ rc = dps310_get_temp_k(data, &kt);
+ if (rc)
+ return rc;
/* Obtain inverse-scaled offset */
c0 = div_s64((s64)kt * (s64)data->c0, 2);
@@ -746,7 +763,9 @@ static int dps310_calculate_temp(struct dps310_data *data)
t = c0 + ((s64)data->temp_raw * (s64)data->c1);
/* Convert to milliCelsius and scale the temperature */
- return (int)div_s64(t * 1000LL, kt);
+ *val = (int)div_s64(t * 1000LL, kt);
+
+ return 0;
}
static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
@@ -756,11 +775,10 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- rc = dps310_get_temp_samp_freq(data);
- if (rc < 0)
+ rc = dps310_get_temp_samp_freq(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
@@ -768,19 +786,17 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
if (rc)
return rc;
- rc = dps310_calculate_temp(data);
- if (rc < 0)
+ rc = dps310_calculate_temp(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- rc = dps310_get_temp_precision(data);
- if (rc < 0)
+ rc = dps310_get_temp_precision(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
default:
diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c
index 818fa6303454..337eecc577d2 100644
--- a/drivers/iio/pressure/hsc030pa_spi.c
+++ b/drivers/iio/pressure/hsc030pa_spi.c
@@ -23,14 +23,9 @@
static int hsc_spi_recv(struct hsc_data *data)
{
struct spi_device *spi = to_spi_device(data->dev);
- struct spi_transfer xfer = {
- .tx_buf = NULL,
- .rx_buf = data->buffer,
- .len = HSC_REG_MEASUREMENT_RD_SIZE,
- };
msleep_interruptible(HSC_RESP_TIME_MS);
- return spi_sync_transfer(spi, &xfer, 1);
+ return spi_read(spi, data->buffer, HSC_REG_MEASUREMENT_RD_SIZE);
}
static int hsc_spi_probe(struct spi_device *spi)
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
index 421e059d1f19..dcc87a9015e8 100644
--- a/drivers/iio/pressure/zpa2326.c
+++ b/drivers/iio/pressure/zpa2326.c
@@ -861,13 +861,13 @@ static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev,
struct zpa2326_private *private)
{
unsigned int val;
- long timeout;
+ long time_left;
zpa2326_dbg(indio_dev, "waiting for one shot completion interrupt");
- timeout = wait_for_completion_interruptible_timeout(
+ time_left = wait_for_completion_interruptible_timeout(
&private->data_ready, ZPA2326_CONVERSION_JIFFIES);
- if (timeout > 0)
+ if (time_left > 0)
/*
* Interrupt handler completed before timeout: return operation
* status.
@@ -877,10 +877,10 @@ static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev,
/* Clear all interrupts just to be sure. */
regmap_read(private->regmap, ZPA2326_INT_SOURCE_REG, &val);
- if (!timeout) {
+ if (!time_left) {
/* Timed out. */
zpa2326_warn(indio_dev, "no one shot interrupt occurred (%ld)",
- timeout);
+ time_left);
return -ETIME;
}
diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c
index 39447c786af3..24d19f3c7292 100644
--- a/drivers/iio/temperature/ltc2983.c
+++ b/drivers/iio/temperature/ltc2983.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <asm/byteorder.h>
@@ -657,7 +658,6 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
const struct ltc2983_sensor *sensor)
{
struct ltc2983_thermocouple *thermo;
- struct fwnode_handle *ref;
u32 oc_current;
int ret;
@@ -704,7 +704,8 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
return ERR_PTR(-EINVAL);
}
- ref = fwnode_find_reference(child, "adi,cold-junction-handle", 0);
+ struct fwnode_handle *ref __free(fwnode_handle) =
+ fwnode_find_reference(child, "adi,cold-junction-handle", 0);
if (IS_ERR(ref)) {
ref = NULL;
} else {
@@ -715,7 +716,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
* the error right away.
*/
dev_err(&st->spi->dev, "Property reg must be given\n");
- goto fail;
+ return ERR_PTR(ret);
}
}
@@ -726,22 +727,15 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
thermo->custom = __ltc2983_custom_sensor_new(st, child,
propname, false,
16384, true);
- if (IS_ERR(thermo->custom)) {
- ret = PTR_ERR(thermo->custom);
- goto fail;
- }
+ if (IS_ERR(thermo->custom))
+ return ERR_CAST(thermo->custom);
}
/* set common parameters */
thermo->sensor.fault_handler = ltc2983_thermocouple_fault_handler;
thermo->sensor.assign_chan = ltc2983_thermocouple_assign_chan;
- fwnode_handle_put(ref);
return &thermo->sensor;
-
-fail:
- fwnode_handle_put(ref);
- return ERR_PTR(ret);
}
static struct ltc2983_sensor *
@@ -751,14 +745,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
struct ltc2983_rtd *rtd;
int ret = 0;
struct device *dev = &st->spi->dev;
- struct fwnode_handle *ref;
u32 excitation_current = 0, n_wires = 0;
rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
if (!rtd)
return ERR_PTR(-ENOMEM);
- ref = fwnode_find_reference(child, "adi,rsense-handle", 0);
+ struct fwnode_handle *ref __free(fwnode_handle) =
+ fwnode_find_reference(child, "adi,rsense-handle", 0);
if (IS_ERR(ref)) {
dev_err(dev, "Property adi,rsense-handle missing or invalid");
return ERR_CAST(ref);
@@ -767,7 +761,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
ret = fwnode_property_read_u32(ref, "reg", &rtd->r_sense_chan);
if (ret) {
dev_err(dev, "Property reg must be given\n");
- goto fail;
+ return ERR_PTR(ret);
}
ret = fwnode_property_read_u32(child, "adi,number-of-wires", &n_wires);
@@ -788,8 +782,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
break;
default:
dev_err(dev, "Invalid number of wires:%u\n", n_wires);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
@@ -799,8 +792,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
if (n_wires == 2 || n_wires == 3) {
dev_err(dev,
"Rotation not allowed for 2/3 Wire RTDs");
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
rtd->sensor_config |= LTC2983_RTD_C_ROTATE(1);
} else {
@@ -830,16 +822,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
"Invalid rsense chann:%d to use in kelvin rsense",
rtd->r_sense_chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
if (sensor->chan < min || sensor->chan > max) {
dev_err(dev, "Invalid chann:%d for the rtd config",
sensor->chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
} else {
/* same as differential case */
@@ -847,8 +837,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
dev_err(&st->spi->dev,
"Invalid chann:%d for RTD", sensor->chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
@@ -857,10 +846,8 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
rtd->custom = __ltc2983_custom_sensor_new(st, child,
"adi,custom-rtd",
false, 2048, false);
- if (IS_ERR(rtd->custom)) {
- ret = PTR_ERR(rtd->custom);
- goto fail;
- }
+ if (IS_ERR(rtd->custom))
+ return ERR_CAST(rtd->custom);
}
/* set common parameters */
@@ -902,18 +889,13 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
dev_err(&st->spi->dev,
"Invalid value for excitation current(%u)",
excitation_current);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
fwnode_property_read_u32(child, "adi,rtd-curve", &rtd->rtd_curve);
- fwnode_handle_put(ref);
return &rtd->sensor;
-fail:
- fwnode_handle_put(ref);
- return ERR_PTR(ret);
}
static struct ltc2983_sensor *
@@ -922,7 +904,6 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
{
struct ltc2983_thermistor *thermistor;
struct device *dev = &st->spi->dev;
- struct fwnode_handle *ref;
u32 excitation_current = 0;
int ret = 0;
@@ -930,7 +911,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
if (!thermistor)
return ERR_PTR(-ENOMEM);
- ref = fwnode_find_reference(child, "adi,rsense-handle", 0);
+ struct fwnode_handle *ref __free(fwnode_handle) =
+ fwnode_find_reference(child, "adi,rsense-handle", 0);
if (IS_ERR(ref)) {
dev_err(dev, "Property adi,rsense-handle missing or invalid");
return ERR_CAST(ref);
@@ -939,7 +921,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
ret = fwnode_property_read_u32(ref, "reg", &thermistor->r_sense_chan);
if (ret) {
dev_err(dev, "rsense channel must be configured...\n");
- goto fail;
+ return ERR_PTR(ret);
}
if (fwnode_property_read_bool(child, "adi,single-ended")) {
@@ -959,8 +941,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
dev_err(&st->spi->dev,
"Invalid chann:%d for differential thermistor",
sensor->chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
/* check custom sensor */
@@ -979,10 +960,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
propname,
steinhart,
64, false);
- if (IS_ERR(thermistor->custom)) {
- ret = PTR_ERR(thermistor->custom);
- goto fail;
- }
+ if (IS_ERR(thermistor->custom))
+ return ERR_CAST(thermistor->custom);
}
/* set common parameters */
thermistor->sensor.fault_handler = ltc2983_common_fault_handler;
@@ -1006,8 +985,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
LTC2983_SENSOR_THERMISTOR_STEINHART) {
dev_err(&st->spi->dev,
"Auto Range not allowed for custom sensors\n");
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
thermistor->excitation_current = 0x0c;
break;
@@ -1048,16 +1026,11 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
dev_err(&st->spi->dev,
"Invalid value for excitation current(%u)",
excitation_current);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
- fwnode_handle_put(ref);
return &thermistor->sensor;
-fail:
- fwnode_handle_put(ref);
- return ERR_PTR(ret);
}
static struct ltc2983_sensor *
@@ -1350,8 +1323,7 @@ static irqreturn_t ltc2983_irq_handler(int irq, void *data)
static int ltc2983_parse_fw(struct ltc2983_data *st)
{
struct device *dev = &st->spi->dev;
- struct fwnode_handle *child;
- int ret = 0, chan = 0, channel_avail_mask = 0;
+ int ret, chan = 0, channel_avail_mask = 0;
device_property_read_u32(dev, "adi,mux-delay-config-us", &st->mux_delay_config);
@@ -1369,38 +1341,35 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
return -ENOMEM;
st->iio_channels = st->num_channels;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
struct ltc2983_sensor sensor;
ret = fwnode_property_read_u32(child, "reg", &sensor.chan);
- if (ret) {
- dev_err(dev, "reg property must given for child nodes\n");
- goto put_child;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reg property must given for child nodes\n");
/* check if we have a valid channel */
if (sensor.chan < LTC2983_MIN_CHANNELS_NR ||
- sensor.chan > st->info->max_channels_nr) {
- ret = -EINVAL;
- dev_err(dev, "chan:%d must be from %u to %u\n", sensor.chan,
- LTC2983_MIN_CHANNELS_NR, st->info->max_channels_nr);
- goto put_child;
- } else if (channel_avail_mask & BIT(sensor.chan)) {
- ret = -EINVAL;
- dev_err(dev, "chan:%d already in use\n", sensor.chan);
- goto put_child;
- }
+ sensor.chan > st->info->max_channels_nr)
+ return dev_err_probe(dev, -EINVAL,
+ "chan:%d must be from %u to %u\n",
+ sensor.chan,
+ LTC2983_MIN_CHANNELS_NR,
+ st->info->max_channels_nr);
+
+ if (channel_avail_mask & BIT(sensor.chan))
+ return dev_err_probe(dev, -EINVAL,
+ "chan:%d already in use\n",
+ sensor.chan);
ret = fwnode_property_read_u32(child, "adi,sensor-type", &sensor.type);
- if (ret) {
- dev_err(dev,
+ if (ret)
+ return dev_err_probe(dev, ret,
"adi,sensor-type property must given for child nodes\n");
- goto put_child;
- }
dev_dbg(dev, "Create new sensor, type %u, chann %u",
- sensor.type,
- sensor.chan);
+ sensor.type, sensor.chan);
if (sensor.type >= LTC2983_SENSOR_THERMOCOUPLE &&
sensor.type <= LTC2983_SENSOR_THERMOCOUPLE_CUSTOM) {
@@ -1427,17 +1396,15 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
sensor.type == LTC2983_SENSOR_ACTIVE_TEMP) {
st->sensors[chan] = ltc2983_temp_new(child, st, &sensor);
} else {
- dev_err(dev, "Unknown sensor type %d\n", sensor.type);
- ret = -EINVAL;
- goto put_child;
+ return dev_err_probe(dev, -EINVAL,
+ "Unknown sensor type %d\n",
+ sensor.type);
}
- if (IS_ERR(st->sensors[chan])) {
- dev_err(dev, "Failed to create sensor %ld",
- PTR_ERR(st->sensors[chan]));
- ret = PTR_ERR(st->sensors[chan]);
- goto put_child;
- }
+ if (IS_ERR(st->sensors[chan]))
+ return dev_err_probe(dev, PTR_ERR(st->sensors[chan]),
+ "Failed to create sensor\n");
+
/* set generic sensor parameters */
st->sensors[chan]->chan = sensor.chan;
st->sensors[chan]->type = sensor.type;
@@ -1447,9 +1414,6 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
}
return 0;
-put_child:
- fwnode_handle_put(child);
- return ret;
}
static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd,
@@ -1634,6 +1598,10 @@ static int ltc2983_probe(struct spi_device *spi)
if (ret)
return ret;
+ ret = devm_regulator_get_enable(&spi->dev, "vdd");
+ if (ret)
+ return ret;
+
gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
diff --git a/drivers/iio/temperature/mcp9600.c b/drivers/iio/temperature/mcp9600.c
index 46845804292b..7a3eef5d5e75 100644
--- a/drivers/iio/temperature/mcp9600.c
+++ b/drivers/iio/temperature/mcp9600.c
@@ -52,7 +52,8 @@ static int mcp9600_read(struct mcp9600_data *data,
if (ret < 0)
return ret;
- *val = ret;
+
+ *val = sign_extend32(ret, 15);
return 0;
}
diff --git a/drivers/interconnect/qcom/qcm2290.c b/drivers/interconnect/qcom/qcm2290.c
index 96735800b13c..ba4cc08684d6 100644
--- a/drivers/interconnect/qcom/qcm2290.c
+++ b/drivers/interconnect/qcom/qcm2290.c
@@ -164,7 +164,7 @@ static struct qcom_icc_node mas_snoc_bimc = {
.name = "mas_snoc_bimc",
.buswidth = 16,
.qos.ap_owned = true,
- .qos.qos_port = 2,
+ .qos.qos_port = 6,
.qos.qos_mode = NOC_QOS_MODE_BYPASS,
.mas_rpm_id = 164,
.slv_rpm_id = -1,
diff --git a/drivers/interconnect/qcom/sm6115.c b/drivers/interconnect/qcom/sm6115.c
index 7e15ddf0a80a..271b07c74862 100644
--- a/drivers/interconnect/qcom/sm6115.c
+++ b/drivers/interconnect/qcom/sm6115.c
@@ -242,7 +242,7 @@ static struct qcom_icc_node crypto_c0 = {
.id = SM6115_MASTER_CRYPTO_CORE0,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 43,
+ .qos.qos_port = 22,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = 23,
@@ -332,7 +332,7 @@ static struct qcom_icc_node qnm_camera_nrt = {
.id = SM6115_MASTER_CAMNOC_SF,
.channels = 1,
.buswidth = 32,
- .qos.qos_port = 25,
+ .qos.qos_port = 4,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 3,
.mas_rpm_id = -1,
@@ -346,7 +346,7 @@ static struct qcom_icc_node qxm_venus0 = {
.id = SM6115_MASTER_VIDEO_P0,
.channels = 1,
.buswidth = 16,
- .qos.qos_port = 30,
+ .qos.qos_port = 9,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 3,
.qos.urg_fwd_en = true,
@@ -361,7 +361,7 @@ static struct qcom_icc_node qxm_venus_cpu = {
.id = SM6115_MASTER_VIDEO_PROC,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 34,
+ .qos.qos_port = 13,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 4,
.mas_rpm_id = -1,
@@ -379,7 +379,7 @@ static struct qcom_icc_node qnm_camera_rt = {
.id = SM6115_MASTER_CAMNOC_HF,
.channels = 1,
.buswidth = 32,
- .qos.qos_port = 31,
+ .qos.qos_port = 10,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 3,
.qos.urg_fwd_en = true,
@@ -394,7 +394,7 @@ static struct qcom_icc_node qxm_mdp0 = {
.id = SM6115_MASTER_MDP_PORT0,
.channels = 1,
.buswidth = 16,
- .qos.qos_port = 26,
+ .qos.qos_port = 5,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 3,
.qos.urg_fwd_en = true,
@@ -434,7 +434,7 @@ static struct qcom_icc_node qhm_tic = {
.id = SM6115_MASTER_TIC,
.channels = 1,
.buswidth = 4,
- .qos.qos_port = 29,
+ .qos.qos_port = 8,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = -1,
@@ -484,7 +484,7 @@ static struct qcom_icc_node qxm_pimem = {
.id = SM6115_MASTER_PIMEM,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 41,
+ .qos.qos_port = 20,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = -1,
@@ -498,7 +498,7 @@ static struct qcom_icc_node qhm_qdss_bam = {
.id = SM6115_MASTER_QDSS_BAM,
.channels = 1,
.buswidth = 4,
- .qos.qos_port = 23,
+ .qos.qos_port = 2,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = -1,
@@ -523,7 +523,7 @@ static struct qcom_icc_node qhm_qup0 = {
.id = SM6115_MASTER_QUP_0,
.channels = 1,
.buswidth = 4,
- .qos.qos_port = 21,
+ .qos.qos_port = 0,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = 166,
@@ -537,7 +537,7 @@ static struct qcom_icc_node qxm_ipa = {
.id = SM6115_MASTER_IPA,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 24,
+ .qos.qos_port = 3,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = 59,
@@ -551,7 +551,7 @@ static struct qcom_icc_node xm_qdss_etr = {
.id = SM6115_MASTER_QDSS_ETR,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 33,
+ .qos.qos_port = 12,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = -1,
@@ -565,7 +565,7 @@ static struct qcom_icc_node xm_sdc1 = {
.id = SM6115_MASTER_SDCC_1,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 38,
+ .qos.qos_port = 17,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = 33,
@@ -579,7 +579,7 @@ static struct qcom_icc_node xm_sdc2 = {
.id = SM6115_MASTER_SDCC_2,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 44,
+ .qos.qos_port = 23,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = 35,
@@ -593,7 +593,7 @@ static struct qcom_icc_node xm_usb3_0 = {
.id = SM6115_MASTER_USB3,
.channels = 1,
.buswidth = 8,
- .qos.qos_port = 45,
+ .qos.qos_port = 24,
.qos.qos_mode = NOC_QOS_MODE_FIXED,
.qos.areq_prio = 2,
.mas_rpm_id = -1,
@@ -1336,6 +1336,7 @@ static const struct qcom_icc_desc sm6115_sys_noc = {
.intf_clocks = snoc_intf_clocks,
.num_intf_clocks = ARRAY_SIZE(snoc_intf_clocks),
.bus_clk_desc = &bus_2_clk,
+ .qos_offset = 0x15000,
.keep_alive = true,
};
@@ -1367,6 +1368,7 @@ static const struct qcom_icc_desc sm6115_mmnrt_virt = {
.regmap_cfg = &sys_noc_regmap_config,
.bus_clk_desc = &mmaxi_0_clk,
.keep_alive = true,
+ .qos_offset = 0x15000,
.ab_coeff = 142,
};
@@ -1383,6 +1385,7 @@ static const struct qcom_icc_desc sm6115_mmrt_virt = {
.regmap_cfg = &sys_noc_regmap_config,
.bus_clk_desc = &mmaxi_1_clk,
.keep_alive = true,
+ .qos_offset = 0x15000,
.ab_coeff = 139,
};
diff --git a/drivers/mcb/mcb-lpc.c b/drivers/mcb/mcb-lpc.c
index a851e0236464..2bec2086ee17 100644
--- a/drivers/mcb/mcb-lpc.c
+++ b/drivers/mcb/mcb-lpc.c
@@ -97,13 +97,11 @@ out_mcb_bus:
return ret;
}
-static int mcb_lpc_remove(struct platform_device *pdev)
+static void mcb_lpc_remove(struct platform_device *pdev)
{
struct priv *priv = platform_get_drvdata(pdev);
mcb_release_bus(priv->bus);
-
- return 0;
}
static struct platform_device *mcb_lpc_pdev;
@@ -140,7 +138,7 @@ static struct platform_driver mcb_lpc_driver = {
.name = "mcb-lpc",
},
.probe = mcb_lpc_probe,
- .remove = mcb_lpc_remove,
+ .remove_new = mcb_lpc_remove,
};
static const struct dmi_system_id mcb_lpc_dmi_table[] = {
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4fb291f0bf7c..faf983680040 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -293,21 +293,21 @@ config SGI_GRU
depends on X86_UV && SMP
select MMU_NOTIFIER
help
- The GRU is a hardware resource located in the system chipset. The GRU
- contains memory that can be mmapped into the user address space. This memory is
- used to communicate with the GRU to perform functions such as load/store,
- scatter/gather, bcopy, AMOs, etc. The GRU is directly accessed by user
- instructions using user virtual addresses. GRU instructions (ex., bcopy) use
- user virtual addresses for operands.
+ The GRU is a hardware resource located in the system chipset. The GRU
+ contains memory that can be mmapped into the user address space.
+ This memory is used to communicate with the GRU to perform functions
+ such as load/store, scatter/gather, bcopy, AMOs, etc. The GRU is
+ directly accessed by user instructions using user virtual addresses.
+ GRU instructions (ex., bcopy) use user virtual addresses for operands.
- If you are not running on a SGI UV system, say N.
+ If you are not running on a SGI UV system, say N.
config SGI_GRU_DEBUG
bool "SGI GRU driver debug"
depends on SGI_GRU
help
- This option enables additional debugging code for the SGI GRU driver.
- If you are unsure, say N.
+ This option enables additional debugging code for the SGI GRU driver.
+ If you are unsure, say N.
config APDS9802ALS
tristate "Medfield Avago APDS9802 ALS Sensor module"
@@ -428,7 +428,6 @@ config LATTICE_ECP3_CONFIG
tristate "Lattice ECP3 FPGA bitstream configuration via SPI"
depends on SPI && SYSFS
select FW_LOADER
- default n
help
This option enables support for bitstream configuration (programming
or loading) of the Lattice ECP3 FPGA family via SPI.
@@ -506,6 +505,18 @@ config OPEN_DICE
If unsure, say N.
+config NTSYNC
+ tristate "NT synchronization primitive emulation"
+ depends on BROKEN
+ help
+ This module provides kernel support for emulation of Windows NT
+ synchronization primitives. It is not a hardware driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ntsync.
+
+ If unsure, say N.
+
config VCPU_STALL_DETECTOR
tristate "Guest vCPU stall detector"
depends on OF && HAS_IOMEM
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ea6ea5bbbc9c..153a3f4837e8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/
obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
+obj-$(CONFIG_NTSYNC) += ntsync.o
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
obj-$(CONFIG_OPEN_DICE) += open-dice.o
obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c
index 0ad2ff9065aa..117b3c24f910 100644
--- a/drivers/misc/cardreader/rtsx_pcr.c
+++ b/drivers/misc/cardreader/rtsx_pcr.c
@@ -1002,12 +1002,14 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
} else {
pcr->card_removed |= SD_EXIST;
pcr->card_inserted &= ~SD_EXIST;
- if ((PCI_PID(pcr) == PID_5261) || (PCI_PID(pcr) == PID_5264)) {
- rtsx_pci_write_register(pcr, RTS5261_FW_STATUS,
- RTS5261_EXPRESS_LINK_FAIL_MASK, 0);
- pcr->extra_caps |= EXTRA_CAPS_SD_EXPRESS;
- }
}
+
+ if ((PCI_PID(pcr) == PID_5261) || (PCI_PID(pcr) == PID_5264)) {
+ rtsx_pci_write_register(pcr, RTS5261_FW_STATUS,
+ RTS5261_EXPRESS_LINK_FAIL_MASK, 0);
+ pcr->extra_caps |= EXTRA_CAPS_SD_EXPRESS;
+ }
+
pcr->dma_error_count = 0;
}
diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c
index 21fc5bc85c5c..5f8dcd0e3848 100644
--- a/drivers/misc/ds1682.c
+++ b/drivers/misc/ds1682.c
@@ -32,6 +32,7 @@
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/list.h>
+#include <linux/nvmem-provider.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/hwmon-sysfs.h>
@@ -197,11 +198,43 @@ static const struct bin_attribute ds1682_eeprom_attr = {
.write = ds1682_eeprom_write,
};
+static int ds1682_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct i2c_client *client = priv;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + offset,
+ bytes, val);
+ return ret < 0 ? ret : 0;
+}
+
+static int ds1682_nvmem_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct i2c_client *client = priv;
+ int ret;
+
+ ret = i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + offset,
+ bytes, val);
+ return ret < 0 ? ret : 0;
+}
+
/*
* Called when a ds1682 device is matched with this driver
*/
static int ds1682_probe(struct i2c_client *client)
{
+ struct nvmem_config config = {
+ .dev = &client->dev,
+ .owner = THIS_MODULE,
+ .type = NVMEM_TYPE_EEPROM,
+ .reg_read = ds1682_nvmem_read,
+ .reg_write = ds1682_nvmem_write,
+ .size = DS1682_EEPROM_SIZE,
+ .priv = client,
+ };
+ struct nvmem_device *nvmem;
int rc;
if (!i2c_check_functionality(client->adapter,
@@ -211,6 +244,10 @@ static int ds1682_probe(struct i2c_client *client)
goto exit;
}
+ nvmem = devm_nvmem_register(&client->dev, &config);
+ if (IS_ENABLED(CONFIG_NVMEM) && IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
+
rc = sysfs_create_group(&client->dev.kobj, &ds1682_group);
if (rc)
goto exit;
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 65d49a6de1a7..595ceb9a7126 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -529,4 +529,3 @@ module_spi_driver(at25_driver);
MODULE_DESCRIPTION("Driver for most SPI EEPROMs");
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("spi:at25");
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c
index e78a76d74ff4..45c8ae0db8f9 100644
--- a/drivers/misc/eeprom/eeprom_93xx46.c
+++ b/drivers/misc/eeprom/eeprom_93xx46.c
@@ -578,5 +578,3 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
MODULE_ALIAS("spi:93xx46");
-MODULE_ALIAS("spi:eeprom-93xx46");
-MODULE_ALIAS("spi:93lc46b");
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index f9bcff197615..99393f610cdf 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -1327,7 +1327,7 @@ static int mei_cl_device_uevent(const struct device *dev, struct kobj_uevent_env
return 0;
}
-static struct bus_type mei_cl_bus_type = {
+static const struct bus_type mei_cl_bus_type = {
.name = "mei",
.dev_groups = mei_cldev_groups,
.match = mei_cl_device_match,
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index eb800a07a84b..2e9cf6f4efb6 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -247,12 +247,10 @@ enum mei_ext_hdr_type {
* struct mei_ext_hdr - extend header descriptor (TLV)
* @type: enum mei_ext_hdr_type
* @length: length excluding descriptor
- * @data: the extended header payload
*/
struct mei_ext_hdr {
u8 type;
u8 length;
- u8 data[];
} __packed;
/**
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
new file mode 100644
index 000000000000..3c2f743c58b0
--- /dev/null
+++ b/drivers/misc/ntsync.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ntsync.c - Kernel driver for NT synchronization primitives
+ *
+ * Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
+ */
+
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <uapi/linux/ntsync.h>
+
+#define NTSYNC_NAME "ntsync"
+
+enum ntsync_type {
+ NTSYNC_TYPE_SEM,
+};
+
+/*
+ * Individual synchronization primitives are represented by
+ * struct ntsync_obj, and each primitive is backed by a file.
+ *
+ * The whole namespace is represented by a struct ntsync_device also
+ * backed by a file.
+ *
+ * Both rely on struct file for reference counting. Individual
+ * ntsync_obj objects take a reference to the device when created.
+ */
+
+struct ntsync_obj {
+ spinlock_t lock;
+
+ enum ntsync_type type;
+
+ struct file *file;
+ struct ntsync_device *dev;
+
+ /* The following fields are protected by the object lock. */
+ union {
+ struct {
+ __u32 count;
+ __u32 max;
+ } sem;
+ } u;
+};
+
+struct ntsync_device {
+ struct file *file;
+};
+
+/*
+ * Actually change the semaphore state, returning -EOVERFLOW if it is made
+ * invalid.
+ */
+static int post_sem_state(struct ntsync_obj *sem, __u32 count)
+{
+ __u32 sum;
+
+ lockdep_assert_held(&sem->lock);
+
+ if (check_add_overflow(sem->u.sem.count, count, &sum) ||
+ sum > sem->u.sem.max)
+ return -EOVERFLOW;
+
+ sem->u.sem.count = sum;
+ return 0;
+}
+
+static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
+{
+ __u32 __user *user_args = argp;
+ __u32 prev_count;
+ __u32 args;
+ int ret;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ if (sem->type != NTSYNC_TYPE_SEM)
+ return -EINVAL;
+
+ spin_lock(&sem->lock);
+
+ prev_count = sem->u.sem.count;
+ ret = post_sem_state(sem, args);
+
+ spin_unlock(&sem->lock);
+
+ if (!ret && put_user(prev_count, user_args))
+ ret = -EFAULT;
+
+ return ret;
+}
+
+static int ntsync_obj_release(struct inode *inode, struct file *file)
+{
+ struct ntsync_obj *obj = file->private_data;
+
+ fput(obj->dev->file);
+ kfree(obj);
+
+ return 0;
+}
+
+static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+{
+ struct ntsync_obj *obj = file->private_data;
+ void __user *argp = (void __user *)parm;
+
+ switch (cmd) {
+ case NTSYNC_IOC_SEM_POST:
+ return ntsync_sem_post(obj, argp);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static const struct file_operations ntsync_obj_fops = {
+ .owner = THIS_MODULE,
+ .release = ntsync_obj_release,
+ .unlocked_ioctl = ntsync_obj_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
+ enum ntsync_type type)
+{
+ struct ntsync_obj *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return NULL;
+ obj->type = type;
+ obj->dev = dev;
+ get_file(dev->file);
+ spin_lock_init(&obj->lock);
+
+ return obj;
+}
+
+static int ntsync_obj_get_fd(struct ntsync_obj *obj)
+{
+ struct file *file;
+ int fd;
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+ file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
+ if (IS_ERR(file)) {
+ put_unused_fd(fd);
+ return PTR_ERR(file);
+ }
+ obj->file = file;
+ fd_install(fd, file);
+
+ return fd;
+}
+
+static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
+{
+ struct ntsync_sem_args __user *user_args = argp;
+ struct ntsync_sem_args args;
+ struct ntsync_obj *sem;
+ int fd;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ if (args.count > args.max)
+ return -EINVAL;
+
+ sem = ntsync_alloc_obj(dev, NTSYNC_TYPE_SEM);
+ if (!sem)
+ return -ENOMEM;
+ sem->u.sem.count = args.count;
+ sem->u.sem.max = args.max;
+ fd = ntsync_obj_get_fd(sem);
+ if (fd < 0) {
+ kfree(sem);
+ return fd;
+ }
+
+ return put_user(fd, &user_args->sem);
+}
+
+static int ntsync_char_open(struct inode *inode, struct file *file)
+{
+ struct ntsync_device *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ file->private_data = dev;
+ dev->file = file;
+ return nonseekable_open(inode, file);
+}
+
+static int ntsync_char_release(struct inode *inode, struct file *file)
+{
+ struct ntsync_device *dev = file->private_data;
+
+ kfree(dev);
+
+ return 0;
+}
+
+static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+{
+ struct ntsync_device *dev = file->private_data;
+ void __user *argp = (void __user *)parm;
+
+ switch (cmd) {
+ case NTSYNC_IOC_CREATE_SEM:
+ return ntsync_create_sem(dev, argp);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static const struct file_operations ntsync_fops = {
+ .owner = THIS_MODULE,
+ .open = ntsync_char_open,
+ .release = ntsync_char_release,
+ .unlocked_ioctl = ntsync_char_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ntsync_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = NTSYNC_NAME,
+ .fops = &ntsync_fops,
+};
+
+module_misc_device(ntsync_misc);
+
+MODULE_AUTHOR("Elizabeth Figura <zfigura@codeweavers.com>");
+MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/pvpanic/pvpanic.c b/drivers/misc/pvpanic/pvpanic.c
index df3457ce1cb1..17c0eb549463 100644
--- a/drivers/misc/pvpanic/pvpanic.c
+++ b/drivers/misc/pvpanic/pvpanic.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/panic_notifier.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/sysfs.h>
#include <linux/types.h>
@@ -35,6 +36,7 @@ struct pvpanic_instance {
void __iomem *base;
unsigned int capability;
unsigned int events;
+ struct sys_off_handler *sys_off;
struct list_head list;
};
@@ -78,6 +80,39 @@ static struct notifier_block pvpanic_panic_nb = {
.priority = INT_MAX,
};
+static int pvpanic_sys_off(struct sys_off_data *data)
+{
+ pvpanic_send_event(PVPANIC_SHUTDOWN);
+
+ return NOTIFY_DONE;
+}
+
+static void pvpanic_synchronize_sys_off_handler(struct device *dev, struct pvpanic_instance *pi)
+{
+ /* The kernel core has logic to fall back to system halt if no
+ * sys_off_handler is registered.
+ * When the pvpanic sys_off_handler is disabled via sysfs the kernel
+ * should use that fallback logic, so the handler needs to be unregistered.
+ */
+
+ struct sys_off_handler *sys_off;
+
+ if (!(pi->events & PVPANIC_SHUTDOWN) == !pi->sys_off)
+ return;
+
+ if (!pi->sys_off) {
+ sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_LOW,
+ pvpanic_sys_off, NULL);
+ if (IS_ERR(sys_off))
+ dev_warn(dev, "Could not register sys_off_handler: %pe\n", sys_off);
+ else
+ pi->sys_off = sys_off;
+ } else {
+ unregister_sys_off_handler(pi->sys_off);
+ pi->sys_off = NULL;
+ }
+}
+
static void pvpanic_remove(void *param)
{
struct pvpanic_instance *pi_cur, *pi_next;
@@ -91,6 +126,8 @@ static void pvpanic_remove(void *param)
}
}
spin_unlock(&pvpanic_lock);
+
+ unregister_sys_off_handler(pi->sys_off);
}
static ssize_t capability_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -123,6 +160,7 @@ static ssize_t events_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
pi->events = tmp;
+ pvpanic_synchronize_sys_off_handler(dev, pi);
return count;
}
@@ -156,12 +194,15 @@ int devm_pvpanic_probe(struct device *dev, void __iomem *base)
return -ENOMEM;
pi->base = base;
- pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED;
+ pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED | PVPANIC_SHUTDOWN;
/* initlize capability by RDPT */
pi->capability &= ioread8(base);
pi->events = pi->capability;
+ pi->sys_off = NULL;
+ pvpanic_synchronize_sys_off_handler(dev, pi);
+
spin_lock(&pvpanic_lock);
list_add(&pi->list, &pvpanic_list);
spin_unlock(&pvpanic_lock);
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 47ebe80bf849..c4f963cf96f2 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -563,7 +563,7 @@ long st_kim_stop(void *kim_data)
static int version_show(struct seq_file *s, void *unused)
{
- struct kim_data_s *kim_gdata = (struct kim_data_s *)s->private;
+ struct kim_data_s *kim_gdata = s->private;
seq_printf(s, "%04X %d.%d.%d\n", kim_gdata->version.full,
kim_gdata->version.chip, kim_gdata->version.maj_ver,
kim_gdata->version.min_ver);
@@ -572,7 +572,7 @@ static int version_show(struct seq_file *s, void *unused)
static int list_show(struct seq_file *s, void *unused)
{
- struct kim_data_s *kim_gdata = (struct kim_data_s *)s->private;
+ struct kim_data_s *kim_gdata = s->private;
kim_st_list_protocols(kim_gdata->core_data, s);
return 0;
}
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c
index d2eb31f39aa7..fd9c3cbbc51e 100644
--- a/drivers/misc/tifm_core.c
+++ b/drivers/misc/tifm_core.c
@@ -148,7 +148,7 @@ static struct attribute *tifm_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(tifm_dev);
-static struct bus_type tifm_bus_type = {
+static const struct bus_type tifm_bus_type = {
.name = "tifm",
.dev_groups = tifm_dev_groups,
.match = tifm_bus_match,
diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c
index 5d7ac07623c2..9a41ab65378d 100644
--- a/drivers/misc/vmw_vmci/vmci_event.c
+++ b/drivers/misc/vmw_vmci/vmci_event.c
@@ -9,6 +9,7 @@
#include <linux/vmw_vmci_api.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/nospec.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/rculist.h>
@@ -86,9 +87,12 @@ static void event_deliver(struct vmci_event_msg *event_msg)
{
struct vmci_subscription *cur;
struct list_head *subscriber_list;
+ u32 sanitized_event, max_vmci_event;
rcu_read_lock();
- subscriber_list = &subscriber_array[event_msg->event_data.event];
+ max_vmci_event = ARRAY_SIZE(subscriber_array);
+ sanitized_event = array_index_nospec(event_msg->event_data.event, max_vmci_event);
+ subscriber_list = &subscriber_array[sanitized_event];
list_for_each_entry_rcu(cur, subscriber_list, node) {
cur->callback(cur->id, &event_msg->event_data,
cur->callback_data);
diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c
index c61e8953511d..476af89e751b 100644
--- a/drivers/misc/vmw_vmci/vmci_guest.c
+++ b/drivers/misc/vmw_vmci/vmci_guest.c
@@ -625,7 +625,8 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
if (!vmci_dev) {
dev_err(&pdev->dev,
"Can't allocate memory for VMCI device\n");
- return -ENOMEM;
+ error = -ENOMEM;
+ goto err_unmap_mmio_base;
}
vmci_dev->dev = &pdev->dev;
@@ -642,7 +643,8 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
if (!vmci_dev->tx_buffer) {
dev_err(&pdev->dev,
"Can't allocate memory for datagram tx buffer\n");
- return -ENOMEM;
+ error = -ENOMEM;
+ goto err_unmap_mmio_base;
}
vmci_dev->data_buffer = dma_alloc_coherent(&pdev->dev, VMCI_DMA_DG_BUFFER_SIZE,
@@ -892,6 +894,10 @@ err_free_notification_bitmap:
err_free_data_buffers:
vmci_free_dg_buffers(vmci_dev);
+err_unmap_mmio_base:
+ if (mmio_base != NULL)
+ pci_iounmap(pdev, mmio_base);
+
/* The rest are managed resources and will be freed by PCI core */
return error;
}
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 2c6b99402df8..e1ec3b7200d7 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -478,7 +478,7 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
nvmem_cells_group.bin_attrs = cells_attrs;
- ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
+ ret = device_add_groups(&nvmem->dev, nvmem_cells_groups);
if (ret)
goto unlock_mutex;
diff --git a/drivers/nvmem/layouts.c b/drivers/nvmem/layouts.c
index 8b5e2de138eb..64dc7013a098 100644
--- a/drivers/nvmem/layouts.c
+++ b/drivers/nvmem/layouts.c
@@ -52,13 +52,15 @@ static const struct bus_type nvmem_layout_bus_type = {
.remove = nvmem_layout_bus_remove,
};
-int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
+int __nvmem_layout_driver_register(struct nvmem_layout_driver *drv,
+ struct module *owner)
{
drv->driver.bus = &nvmem_layout_bus_type;
+ drv->driver.owner = owner;
return driver_register(&drv->driver);
}
-EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
+EXPORT_SYMBOL_GPL(__nvmem_layout_driver_register);
void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
{
diff --git a/drivers/nvmem/layouts/onie-tlv.c b/drivers/nvmem/layouts/onie-tlv.c
index 9d2ad5f2dc10..0967a32319a2 100644
--- a/drivers/nvmem/layouts/onie-tlv.c
+++ b/drivers/nvmem/layouts/onie-tlv.c
@@ -247,7 +247,6 @@ MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
static struct nvmem_layout_driver onie_tlv_layout = {
.driver = {
- .owner = THIS_MODULE,
.name = "onie-tlv-layout",
.of_match_table = onie_tlv_of_match_table,
},
diff --git a/drivers/nvmem/layouts/sl28vpd.c b/drivers/nvmem/layouts/sl28vpd.c
index 53fa50f17dca..e93b020b0836 100644
--- a/drivers/nvmem/layouts/sl28vpd.c
+++ b/drivers/nvmem/layouts/sl28vpd.c
@@ -156,7 +156,6 @@ MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
static struct nvmem_layout_driver sl28vpd_layout = {
.driver = {
- .owner = THIS_MODULE,
.name = "kontron-sl28vpd-layout",
.of_match_table = sl28vpd_of_match_table,
},
diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c
index a0275b29afd5..a73acc7377d2 100644
--- a/drivers/nvmem/lpc18xx_eeprom.c
+++ b/drivers/nvmem/lpc18xx_eeprom.c
@@ -249,13 +249,11 @@ err_clk:
return ret;
}
-static int lpc18xx_eeprom_remove(struct platform_device *pdev)
+static void lpc18xx_eeprom_remove(struct platform_device *pdev)
{
struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev);
clk_disable_unprepare(eeprom->clk);
-
- return 0;
}
static const struct of_device_id lpc18xx_eeprom_of_match[] = {
@@ -266,7 +264,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_of_match);
static struct platform_driver lpc18xx_eeprom_driver = {
.probe = lpc18xx_eeprom_probe,
- .remove = lpc18xx_eeprom_remove,
+ .remove_new = lpc18xx_eeprom_remove,
.driver = {
.name = "lpc18xx-eeprom",
.of_match_table = lpc18xx_eeprom_of_match,
diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c
index 3ff04d5ca8f8..8a16f5f02657 100644
--- a/drivers/nvmem/meson-mx-efuse.c
+++ b/drivers/nvmem/meson-mx-efuse.c
@@ -43,7 +43,6 @@ struct meson_mx_efuse_platform_data {
struct meson_mx_efuse {
void __iomem *base;
struct clk *core_clk;
- struct nvmem_device *nvmem;
struct nvmem_config config;
};
@@ -193,6 +192,7 @@ static int meson_mx_efuse_probe(struct platform_device *pdev)
{
const struct meson_mx_efuse_platform_data *drvdata;
struct meson_mx_efuse *efuse;
+ struct nvmem_device *nvmem;
drvdata = of_device_get_match_data(&pdev->dev);
if (!drvdata)
@@ -223,9 +223,9 @@ static int meson_mx_efuse_probe(struct platform_device *pdev)
return PTR_ERR(efuse->core_clk);
}
- efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
+ nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
- return PTR_ERR_OR_ZERO(efuse->nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver meson_mx_efuse_driver = {
diff --git a/drivers/nvmem/sc27xx-efuse.c b/drivers/nvmem/sc27xx-efuse.c
index bff27011f4ff..4e2ffefac96c 100644
--- a/drivers/nvmem/sc27xx-efuse.c
+++ b/drivers/nvmem/sc27xx-efuse.c
@@ -262,6 +262,7 @@ static const struct of_device_id sc27xx_efuse_of_match[] = {
{ .compatible = "sprd,sc2730-efuse", .data = &sc2730_edata},
{ }
};
+MODULE_DEVICE_TABLE(of, sc27xx_efuse_of_match);
static struct platform_driver sc27xx_efuse_driver = {
.probe = sc27xx_efuse_probe,
diff --git a/drivers/nvmem/sprd-efuse.c b/drivers/nvmem/sprd-efuse.c
index bb3105f3291f..1a7e4e5d8b86 100644
--- a/drivers/nvmem/sprd-efuse.c
+++ b/drivers/nvmem/sprd-efuse.c
@@ -426,6 +426,7 @@ static const struct of_device_id sprd_efuse_of_match[] = {
{ .compatible = "sprd,ums312-efuse", .data = &ums312_data },
{ }
};
+MODULE_DEVICE_TABLE(of, sprd_efuse_of_match);
static struct platform_driver sprd_efuse_driver = {
.probe = sprd_efuse_probe,
diff --git a/drivers/parport/parport_mfc3.c b/drivers/parport/parport_mfc3.c
index f4d0da741e85..bb1817218d7b 100644
--- a/drivers/parport/parport_mfc3.c
+++ b/drivers/parport/parport_mfc3.c
@@ -102,8 +102,7 @@ static unsigned char control_pc_to_mfc3(unsigned char control)
ret |= 128;
if (control & PARPORT_CONTROL_AUTOFD) /* AUTOLF */
ret &= ~64;
- if (control & PARPORT_CONTROL_STROBE) /* Strobe */
- /* Handled directly by hardware */;
+ /* PARPORT_CONTROL_STROBE handled directly by hardware */
return ret;
}
diff --git a/drivers/peci/core.c b/drivers/peci/core.c
index 0f83a9c6093b..8f8bda2f2a62 100644
--- a/drivers/peci/core.c
+++ b/drivers/peci/core.c
@@ -25,7 +25,7 @@ static void peci_controller_dev_release(struct device *dev)
kfree(controller);
}
-struct device_type peci_controller_type = {
+const struct device_type peci_controller_type = {
.release = peci_controller_dev_release,
};
@@ -201,7 +201,7 @@ static void peci_bus_device_remove(struct device *dev)
driver->remove(device);
}
-struct bus_type peci_bus_type = {
+const struct bus_type peci_bus_type = {
.name = "peci",
.match = peci_bus_device_match,
.probe = peci_bus_device_probe,
diff --git a/drivers/peci/device.c b/drivers/peci/device.c
index e6b0bffb14f4..ee01f03c29b7 100644
--- a/drivers/peci/device.c
+++ b/drivers/peci/device.c
@@ -246,7 +246,7 @@ static void peci_device_release(struct device *dev)
kfree(device);
}
-struct device_type peci_device_type = {
+const struct device_type peci_device_type = {
.groups = peci_device_groups,
.release = peci_device_release,
};
diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h
index 9d75ea54504c..506bafcccbbf 100644
--- a/drivers/peci/internal.h
+++ b/drivers/peci/internal.h
@@ -75,13 +75,13 @@ struct peci_device_id {
u8 model;
};
-extern struct device_type peci_device_type;
+extern const struct device_type peci_device_type;
extern const struct attribute_group *peci_device_groups[];
int peci_device_create(struct peci_controller *controller, u8 addr);
void peci_device_destroy(struct peci_device *device);
-extern struct bus_type peci_bus_type;
+extern const struct bus_type peci_bus_type;
extern const struct attribute_group *peci_bus_groups[];
/**
@@ -129,7 +129,7 @@ void peci_driver_unregister(struct peci_driver *driver);
#define module_peci_driver(__peci_driver) \
module_driver(__peci_driver, peci_driver_register, peci_driver_unregister)
-extern struct device_type peci_controller_type;
+extern const struct device_type peci_controller_type;
int peci_controller_scan_devices(struct peci_controller *controller);
diff --git a/drivers/slimbus/qcom-ctrl.c b/drivers/slimbus/qcom-ctrl.c
index 400b7b385a44..0274bc285b60 100644
--- a/drivers/slimbus/qcom-ctrl.c
+++ b/drivers/slimbus/qcom-ctrl.c
@@ -626,7 +626,7 @@ err_request_irq_failed:
return ret;
}
-static int qcom_slim_remove(struct platform_device *pdev)
+static void qcom_slim_remove(struct platform_device *pdev)
{
struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev);
@@ -635,7 +635,6 @@ static int qcom_slim_remove(struct platform_device *pdev)
clk_disable_unprepare(ctrl->rclk);
clk_disable_unprepare(ctrl->hclk);
destroy_workqueue(ctrl->rxwq);
- return 0;
}
/*
@@ -718,10 +717,11 @@ static const struct of_device_id qcom_slim_dt_match[] = {
{ .compatible = "qcom,slim", },
{}
};
+MODULE_DEVICE_TABLE(of, qcom_slim_dt_match);
static struct platform_driver qcom_slim_driver = {
.probe = qcom_slim_probe,
- .remove = qcom_slim_remove,
+ .remove_new = qcom_slim_remove,
.driver = {
.name = "qcom_slim_ctrl",
.of_match_table = qcom_slim_dt_match,
diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index a09a26bf4988..e0b21f0f79c1 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -81,7 +81,6 @@
#define SLIM_USR_MC_DISCONNECT_PORT 0x2E
#define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0
-#define QCOM_SLIM_NGD_AUTOSUSPEND MSEC_PER_SEC
#define SLIM_RX_MSGQ_TIMEOUT_VAL 0x10000
#define SLIM_LA_MGR 0xFF
@@ -1575,7 +1574,7 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctrl);
pm_runtime_use_autosuspend(dev);
- pm_runtime_set_autosuspend_delay(dev, QCOM_SLIM_NGD_AUTOSUSPEND);
+ pm_runtime_set_autosuspend_delay(dev, 100);
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
pm_runtime_get_noresume(dev);
@@ -1679,14 +1678,12 @@ err_pdr_lookup:
return ret;
}
-static int qcom_slim_ngd_ctrl_remove(struct platform_device *pdev)
+static void qcom_slim_ngd_ctrl_remove(struct platform_device *pdev)
{
platform_driver_unregister(&qcom_slim_ngd_driver);
-
- return 0;
}
-static int qcom_slim_ngd_remove(struct platform_device *pdev)
+static void qcom_slim_ngd_remove(struct platform_device *pdev)
{
struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
@@ -1701,7 +1698,6 @@ static int qcom_slim_ngd_remove(struct platform_device *pdev)
kfree(ctrl->ngd);
ctrl->ngd = NULL;
- return 0;
}
static int __maybe_unused qcom_slim_ngd_runtime_idle(struct device *dev)
@@ -1744,7 +1740,7 @@ static const struct dev_pm_ops qcom_slim_ngd_dev_pm_ops = {
static struct platform_driver qcom_slim_ngd_ctrl_driver = {
.probe = qcom_slim_ngd_ctrl_probe,
- .remove = qcom_slim_ngd_ctrl_remove,
+ .remove_new = qcom_slim_ngd_ctrl_remove,
.driver = {
.name = "qcom,slim-ngd-ctrl",
.of_match_table = qcom_slim_ngd_dt_match,
@@ -1753,7 +1749,7 @@ static struct platform_driver qcom_slim_ngd_ctrl_driver = {
static struct platform_driver qcom_slim_ngd_driver = {
.probe = qcom_slim_ngd_probe,
- .remove = qcom_slim_ngd_remove,
+ .remove_new = qcom_slim_ngd_remove,
.driver = {
.name = QCOM_SLIM_NGD_DRV_NAME,
.pm = &qcom_slim_ngd_dev_pm_ops,
diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c
index 674a350cc676..fa068b34b040 100644
--- a/drivers/spmi/hisi-spmi-controller.c
+++ b/drivers/spmi/hisi-spmi-controller.c
@@ -300,7 +300,6 @@ static int spmi_controller_probe(struct platform_device *pdev)
spin_lock_init(&spmi_controller->lock);
- ctrl->nr = spmi_controller->channel;
ctrl->dev.parent = pdev->dev.parent;
ctrl->dev.of_node = of_node_get(pdev->dev.of_node);
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 9ed1180fe31f..791cdc160c51 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -13,6 +13,8 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spmi.h>
@@ -94,6 +96,8 @@ enum pmic_arb_channel {
PMIC_ARB_CHANNEL_OBS,
};
+#define PMIC_ARB_MAX_BUSES 2
+
/* Maximum number of support PMIC peripherals */
#define PMIC_ARB_MAX_PERIPHS 512
#define PMIC_ARB_MAX_PERIPHS_V7 1024
@@ -125,64 +129,84 @@ struct apid_data {
u8 irq_ee;
};
+struct spmi_pmic_arb;
+
/**
- * struct spmi_pmic_arb - SPMI PMIC Arbiter object
+ * struct spmi_pmic_arb_bus - SPMI PMIC Arbiter Bus object
*
- * @rd_base: on v1 "core", on v2 "observer" register base off DT.
- * @wr_base: on v1 "core", on v2 "chnls" register base off DT.
+ * @pmic_arb: the SPMI PMIC Arbiter the bus belongs to.
+ * @domain: irq domain object for PMIC IRQ domain
* @intr: address of the SPMI interrupt control registers.
* @cnfg: address of the PMIC Arbiter configuration registers.
+ * @spmic: spmi controller registered for this bus
* @lock: lock to synchronize accesses.
- * @channel: execution environment channel to use for accesses.
- * @irq: PMIC ARB interrupt.
- * @ee: the current Execution Environment
- * @bus_instance: on v7: 0 = primary SPMI bus, 1 = secondary SPMI bus
- * @min_apid: minimum APID (used for bounding IRQ search)
- * @max_apid: maximum APID
* @base_apid: on v7: minimum APID associated with the particular SPMI
* bus instance
* @apid_count: on v5 and v7: number of APIDs associated with the
* particular SPMI bus instance
* @mapping_table: in-memory copy of PPID -> APID mapping table.
- * @domain: irq domain object for PMIC IRQ domain
- * @spmic: SPMI controller object
- * @ver_ops: version dependent operations.
+ * @mapping_table_valid:bitmap containing valid-only periphs
* @ppid_to_apid: in-memory copy of PPID -> APID mapping table.
* @last_apid: Highest value APID in use
* @apid_data: Table of data for all APIDs
- * @max_periphs: Number of elements in apid_data[]
+ * @min_apid: minimum APID (used for bounding IRQ search)
+ * @max_apid: maximum APID
+ * @irq: PMIC ARB interrupt.
+ * @id: unique ID of the bus
*/
-struct spmi_pmic_arb {
- void __iomem *rd_base;
- void __iomem *wr_base;
+struct spmi_pmic_arb_bus {
+ struct spmi_pmic_arb *pmic_arb;
+ struct irq_domain *domain;
void __iomem *intr;
void __iomem *cnfg;
- void __iomem *core;
- resource_size_t core_size;
+ struct spmi_controller *spmic;
raw_spinlock_t lock;
- u8 channel;
- int irq;
- u8 ee;
- u32 bus_instance;
- u16 min_apid;
- u16 max_apid;
u16 base_apid;
int apid_count;
u32 *mapping_table;
DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
- struct irq_domain *domain;
- struct spmi_controller *spmic;
- const struct pmic_arb_ver_ops *ver_ops;
u16 *ppid_to_apid;
u16 last_apid;
struct apid_data *apid_data;
+ u16 min_apid;
+ u16 max_apid;
+ int irq;
+ u8 id;
+};
+
+/**
+ * struct spmi_pmic_arb - SPMI PMIC Arbiter object
+ *
+ * @rd_base: on v1 "core", on v2 "observer" register base off DT.
+ * @wr_base: on v1 "core", on v2 "chnls" register base off DT.
+ * @core: core register base for v2 and above only (see above)
+ * @core_size: core register base size
+ * @channel: execution environment channel to use for accesses.
+ * @ee: the current Execution Environment
+ * @ver_ops: version dependent operations.
+ * @max_periphs: Number of elements in apid_data[]
+ * @buses: per arbiter buses instances
+ * @buses_available: number of buses registered
+ */
+struct spmi_pmic_arb {
+ void __iomem *rd_base;
+ void __iomem *wr_base;
+ void __iomem *core;
+ resource_size_t core_size;
+ u8 channel;
+ u8 ee;
+ const struct pmic_arb_ver_ops *ver_ops;
int max_periphs;
+ struct spmi_pmic_arb_bus *buses[PMIC_ARB_MAX_BUSES];
+ int buses_available;
};
/**
* struct pmic_arb_ver_ops - version dependent functionality.
*
* @ver_str: version string.
+ * @get_core_resources: initializes the core, observer and channels
+ * @init_apid: finds the apid base and count
* @ppid_to_apid: finds the apid for a given ppid.
* @non_data_cmd: on v1 issues an spmi non-data command.
* on v2 no HW support, returns -EOPNOTSUPP.
@@ -202,20 +226,22 @@ struct spmi_pmic_arb {
*/
struct pmic_arb_ver_ops {
const char *ver_str;
- int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u16 ppid);
+ int (*get_core_resources)(struct platform_device *pdev, void __iomem *core);
+ int (*init_apid)(struct spmi_pmic_arb_bus *bus, int index);
+ int (*ppid_to_apid)(struct spmi_pmic_arb_bus *bus, u16 ppid);
/* spmi commands (read_cmd, write_cmd, cmd) functionality */
- int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
- enum pmic_arb_channel ch_type);
+ int (*offset)(struct spmi_pmic_arb_bus *bus, u8 sid, u16 addr,
+ enum pmic_arb_channel ch_type);
u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
/* Interrupts controller functionality (offset of PIC registers) */
- void __iomem *(*owner_acc_status)(struct spmi_pmic_arb *pmic_arb, u8 m,
+ void __iomem *(*owner_acc_status)(struct spmi_pmic_arb_bus *bus, u8 m,
u16 n);
- void __iomem *(*acc_enable)(struct spmi_pmic_arb *pmic_arb, u16 n);
- void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n);
- void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n);
+ void __iomem *(*acc_enable)(struct spmi_pmic_arb_bus *bus, u16 n);
+ void __iomem *(*irq_status)(struct spmi_pmic_arb_bus *bus, u16 n);
+ void __iomem *(*irq_clear)(struct spmi_pmic_arb_bus *bus, u16 n);
u32 (*apid_map_offset)(u16 n);
- void __iomem *(*apid_owner)(struct spmi_pmic_arb *pmic_arb, u16 n);
+ void __iomem *(*apid_owner)(struct spmi_pmic_arb_bus *bus, u16 n);
};
static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb,
@@ -232,6 +258,7 @@ static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb,
/**
* pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
+ * @pmic_arb: the SPMI PMIC arbiter
* @bc: byte count -1. range: 0..3
* @reg: register's address
* @buf: output parameter, length must be bc + 1
@@ -246,6 +273,7 @@ pmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc)
/**
* pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register
+ * @pmic_arb: the SPMI PMIC arbiter
* @bc: byte-count -1. range: 0..3.
* @reg: register's address.
* @buf: buffer to write. length must be bc + 1.
@@ -263,13 +291,14 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
void __iomem *base, u8 sid, u16 addr,
enum pmic_arb_channel ch_type)
{
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb_bus *bus = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u32 status = 0;
u32 timeout = PMIC_ARB_TIMEOUT_US;
u32 offset;
int rc;
- rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, ch_type);
+ rc = pmic_arb->ver_ops->offset(bus, sid, addr, ch_type);
if (rc < 0)
return rc;
@@ -287,8 +316,8 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
}
if (status & PMIC_ARB_STATUS_FAILURE) {
- dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x)\n",
- __func__, sid, addr, status);
+ dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x) reg: 0x%x\n",
+ __func__, sid, addr, status, offset);
WARN_ON(1);
return -EIO;
}
@@ -304,32 +333,33 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
udelay(1);
}
- dev_err(&ctrl->dev, "%s: %#x %#x: timeout, status %#x\n",
- __func__, sid, addr, status);
+ dev_err(&ctrl->dev, "%s: %#x %#x %#x: timeout, status %#x\n",
+ __func__, bus->id, sid, addr, status);
return -ETIMEDOUT;
}
static int
pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid)
{
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb_bus *bus = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
unsigned long flags;
u32 cmd;
int rc;
u32 offset;
- rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, PMIC_ARB_CHANNEL_RW);
+ rc = pmic_arb->ver_ops->offset(bus, sid, 0, PMIC_ARB_CHANNEL_RW);
if (rc < 0)
return rc;
offset = rc;
cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
- raw_spin_lock_irqsave(&pmic_arb->lock, flags);
+ raw_spin_lock_irqsave(&bus->lock, flags);
pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0,
PMIC_ARB_CHANNEL_RW);
- raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
+ raw_spin_unlock_irqrestore(&bus->lock, flags);
return rc;
}
@@ -354,20 +384,21 @@ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid);
}
-static int pmic_arb_fmt_read_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc, u8 sid,
+static int pmic_arb_fmt_read_cmd(struct spmi_pmic_arb_bus *bus, u8 opc, u8 sid,
u16 addr, size_t len, u32 *cmd, u32 *offset)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u8 bc = len - 1;
int rc;
- rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
+ rc = pmic_arb->ver_ops->offset(bus, sid, addr,
PMIC_ARB_CHANNEL_OBS);
if (rc < 0)
return rc;
*offset = rc;
if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
- dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
+ dev_err(&bus->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
PMIC_ARB_MAX_TRANS_BYTES, len);
return -EINVAL;
}
@@ -391,7 +422,8 @@ static int pmic_arb_read_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
u32 offset, u8 sid, u16 addr, u8 *buf,
size_t len)
{
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb_bus *bus = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u8 bc = len - 1;
int rc;
@@ -413,38 +445,39 @@ static int pmic_arb_read_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
u16 addr, u8 *buf, size_t len)
{
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb_bus *bus = spmi_controller_get_drvdata(ctrl);
unsigned long flags;
u32 cmd, offset;
int rc;
- rc = pmic_arb_fmt_read_cmd(pmic_arb, opc, sid, addr, len, &cmd,
+ rc = pmic_arb_fmt_read_cmd(bus, opc, sid, addr, len, &cmd,
&offset);
if (rc)
return rc;
- raw_spin_lock_irqsave(&pmic_arb->lock, flags);
+ raw_spin_lock_irqsave(&bus->lock, flags);
rc = pmic_arb_read_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf, len);
- raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
+ raw_spin_unlock_irqrestore(&bus->lock, flags);
return rc;
}
-static int pmic_arb_fmt_write_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc,
+static int pmic_arb_fmt_write_cmd(struct spmi_pmic_arb_bus *bus, u8 opc,
u8 sid, u16 addr, size_t len, u32 *cmd,
u32 *offset)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u8 bc = len - 1;
int rc;
- rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr,
+ rc = pmic_arb->ver_ops->offset(bus, sid, addr,
PMIC_ARB_CHANNEL_RW);
if (rc < 0)
return rc;
*offset = rc;
if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
- dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
+ dev_err(&bus->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
PMIC_ARB_MAX_TRANS_BYTES, len);
return -EINVAL;
}
@@ -470,7 +503,8 @@ static int pmic_arb_write_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
u32 offset, u8 sid, u16 addr,
const u8 *buf, size_t len)
{
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb_bus *bus = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u8 bc = len - 1;
/* Write data to FIFOs */
@@ -489,20 +523,20 @@ static int pmic_arb_write_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd,
static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
u16 addr, const u8 *buf, size_t len)
{
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb_bus *bus = spmi_controller_get_drvdata(ctrl);
unsigned long flags;
u32 cmd, offset;
int rc;
- rc = pmic_arb_fmt_write_cmd(pmic_arb, opc, sid, addr, len, &cmd,
+ rc = pmic_arb_fmt_write_cmd(bus, opc, sid, addr, len, &cmd,
&offset);
if (rc)
return rc;
- raw_spin_lock_irqsave(&pmic_arb->lock, flags);
+ raw_spin_lock_irqsave(&bus->lock, flags);
rc = pmic_arb_write_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf,
len);
- raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
+ raw_spin_unlock_irqrestore(&bus->lock, flags);
return rc;
}
@@ -510,23 +544,23 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
static int pmic_arb_masked_write(struct spmi_controller *ctrl, u8 sid, u16 addr,
const u8 *buf, const u8 *mask, size_t len)
{
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
+ struct spmi_pmic_arb_bus *bus = spmi_controller_get_drvdata(ctrl);
u32 read_cmd, read_offset, write_cmd, write_offset;
u8 temp[PMIC_ARB_MAX_TRANS_BYTES];
unsigned long flags;
int rc, i;
- rc = pmic_arb_fmt_read_cmd(pmic_arb, SPMI_CMD_EXT_READL, sid, addr, len,
+ rc = pmic_arb_fmt_read_cmd(bus, SPMI_CMD_EXT_READL, sid, addr, len,
&read_cmd, &read_offset);
if (rc)
return rc;
- rc = pmic_arb_fmt_write_cmd(pmic_arb, SPMI_CMD_EXT_WRITEL, sid, addr,
+ rc = pmic_arb_fmt_write_cmd(bus, SPMI_CMD_EXT_WRITEL, sid, addr,
len, &write_cmd, &write_offset);
if (rc)
return rc;
- raw_spin_lock_irqsave(&pmic_arb->lock, flags);
+ raw_spin_lock_irqsave(&bus->lock, flags);
rc = pmic_arb_read_cmd_unlocked(ctrl, read_cmd, read_offset, sid, addr,
temp, len);
if (rc)
@@ -538,7 +572,7 @@ static int pmic_arb_masked_write(struct spmi_controller *ctrl, u8 sid, u16 addr,
rc = pmic_arb_write_cmd_unlocked(ctrl, write_cmd, write_offset, sid,
addr, temp, len);
done:
- raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
+ raw_spin_unlock_irqrestore(&bus->lock, flags);
return rc;
}
@@ -564,25 +598,25 @@ struct spmi_pmic_arb_qpnpint_type {
static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
size_t len)
{
- struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb_bus *bus = irq_data_get_irq_chip_data(d);
u8 sid = hwirq_to_sid(d->hwirq);
u8 per = hwirq_to_per(d->hwirq);
- if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid,
+ if (pmic_arb_write_cmd(bus->spmic, SPMI_CMD_EXT_WRITEL, sid,
(per << 8) + reg, buf, len))
- dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
+ dev_err_ratelimited(&bus->spmic->dev, "failed irqchip transaction on %x\n",
d->irq);
}
static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
{
- struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb_bus *bus = irq_data_get_irq_chip_data(d);
u8 sid = hwirq_to_sid(d->hwirq);
u8 per = hwirq_to_per(d->hwirq);
- if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid,
+ if (pmic_arb_read_cmd(bus->spmic, SPMI_CMD_EXT_READL, sid,
(per << 8) + reg, buf, len))
- dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n",
+ dev_err_ratelimited(&bus->spmic->dev, "failed irqchip transaction on %x\n",
d->irq);
}
@@ -590,47 +624,49 @@ static int qpnpint_spmi_masked_write(struct irq_data *d, u8 reg,
const void *buf, const void *mask,
size_t len)
{
- struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb_bus *bus = irq_data_get_irq_chip_data(d);
u8 sid = hwirq_to_sid(d->hwirq);
u8 per = hwirq_to_per(d->hwirq);
int rc;
- rc = pmic_arb_masked_write(pmic_arb->spmic, sid, (per << 8) + reg, buf,
+ rc = pmic_arb_masked_write(bus->spmic, sid, (per << 8) + reg, buf,
mask, len);
if (rc)
- dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x rc=%d\n",
+ dev_err_ratelimited(&bus->spmic->dev, "failed irqchip transaction on %x rc=%d\n",
d->irq, rc);
return rc;
}
-static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id)
+static void cleanup_irq(struct spmi_pmic_arb_bus *bus, u16 apid, int id)
{
- u16 ppid = pmic_arb->apid_data[apid].ppid;
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+ u16 ppid = bus->apid_data[apid].ppid;
u8 sid = ppid >> 8;
u8 per = ppid & 0xFF;
u8 irq_mask = BIT(id);
- dev_err_ratelimited(&pmic_arb->spmic->dev, "%s apid=%d sid=0x%x per=0x%x irq=%d\n",
- __func__, apid, sid, per, id);
- writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
+ dev_err_ratelimited(&bus->spmic->dev, "%s apid=%d sid=0x%x per=0x%x irq=%d\n",
+ __func__, apid, sid, per, id);
+ writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(bus, apid));
}
-static int periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid)
+static int periph_interrupt(struct spmi_pmic_arb_bus *bus, u16 apid)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
unsigned int irq;
u32 status, id;
int handled = 0;
- u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF;
- u8 per = pmic_arb->apid_data[apid].ppid & 0xFF;
+ u8 sid = (bus->apid_data[apid].ppid >> 8) & 0xF;
+ u8 per = bus->apid_data[apid].ppid & 0xFF;
- status = readl_relaxed(pmic_arb->ver_ops->irq_status(pmic_arb, apid));
+ status = readl_relaxed(pmic_arb->ver_ops->irq_status(bus, apid));
while (status) {
id = ffs(status) - 1;
status &= ~BIT(id);
- irq = irq_find_mapping(pmic_arb->domain,
- spec_to_hwirq(sid, per, id, apid));
+ irq = irq_find_mapping(bus->domain,
+ spec_to_hwirq(sid, per, id, apid));
if (irq == 0) {
- cleanup_irq(pmic_arb, apid, id);
+ cleanup_irq(bus, apid, id);
continue;
}
generic_handle_irq(irq);
@@ -642,16 +678,17 @@ static int periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid)
static void pmic_arb_chained_irq(struct irq_desc *desc)
{
- struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc);
+ struct spmi_pmic_arb_bus *bus = irq_desc_get_handler_data(desc);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
struct irq_chip *chip = irq_desc_get_chip(desc);
- int first = pmic_arb->min_apid;
- int last = pmic_arb->max_apid;
+ int first = bus->min_apid;
+ int last = bus->max_apid;
/*
* acc_offset will be non-zero for the secondary SPMI bus instance on
* v7 controllers.
*/
- int acc_offset = pmic_arb->base_apid >> 5;
+ int acc_offset = bus->base_apid >> 5;
u8 ee = pmic_arb->ee;
u32 status, enable, handled = 0;
int i, id, apid;
@@ -662,7 +699,7 @@ static void pmic_arb_chained_irq(struct irq_desc *desc)
chained_irq_enter(chip, desc);
for (i = first >> 5; i <= last >> 5; ++i) {
- status = readl_relaxed(ver_ops->owner_acc_status(pmic_arb, ee, i - acc_offset));
+ status = readl_relaxed(ver_ops->owner_acc_status(bus, ee, i - acc_offset));
if (status)
acc_valid = true;
@@ -676,9 +713,9 @@ static void pmic_arb_chained_irq(struct irq_desc *desc)
continue;
}
enable = readl_relaxed(
- ver_ops->acc_enable(pmic_arb, apid));
+ ver_ops->acc_enable(bus, apid));
if (enable & SPMI_PIC_ACC_ENABLE_BIT)
- if (periph_interrupt(pmic_arb, apid) != 0)
+ if (periph_interrupt(bus, apid) != 0)
handled++;
}
}
@@ -687,19 +724,19 @@ static void pmic_arb_chained_irq(struct irq_desc *desc)
if (!acc_valid) {
for (i = first; i <= last; i++) {
/* skip if APPS is not irq owner */
- if (pmic_arb->apid_data[i].irq_ee != pmic_arb->ee)
+ if (bus->apid_data[i].irq_ee != pmic_arb->ee)
continue;
irq_status = readl_relaxed(
- ver_ops->irq_status(pmic_arb, i));
+ ver_ops->irq_status(bus, i));
if (irq_status) {
enable = readl_relaxed(
- ver_ops->acc_enable(pmic_arb, i));
+ ver_ops->acc_enable(bus, i));
if (enable & SPMI_PIC_ACC_ENABLE_BIT) {
- dev_dbg(&pmic_arb->spmic->dev,
+ dev_dbg(&bus->spmic->dev,
"Dispatching IRQ for apid=%d status=%x\n",
i, irq_status);
- if (periph_interrupt(pmic_arb, i) != 0)
+ if (periph_interrupt(bus, i) != 0)
handled++;
}
}
@@ -714,12 +751,13 @@ static void pmic_arb_chained_irq(struct irq_desc *desc)
static void qpnpint_irq_ack(struct irq_data *d)
{
- struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb_bus *bus = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u8 irq = hwirq_to_irq(d->hwirq);
u16 apid = hwirq_to_apid(d->hwirq);
u8 data;
- writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(pmic_arb, apid));
+ writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(bus, apid));
data = BIT(irq);
qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
@@ -735,14 +773,15 @@ static void qpnpint_irq_mask(struct irq_data *d)
static void qpnpint_irq_unmask(struct irq_data *d)
{
- struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb_bus *bus = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops;
u8 irq = hwirq_to_irq(d->hwirq);
u16 apid = hwirq_to_apid(d->hwirq);
u8 buf[2];
writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT,
- ver_ops->acc_enable(pmic_arb, apid));
+ ver_ops->acc_enable(bus, apid));
qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1);
if (!(buf[0] & BIT(irq))) {
@@ -799,9 +838,9 @@ static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
static int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on)
{
- struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb_bus *bus = irq_data_get_irq_chip_data(d);
- return irq_set_irq_wake(pmic_arb->irq, on);
+ return irq_set_irq_wake(bus->irq, on);
}
static int qpnpint_get_irqchip_state(struct irq_data *d,
@@ -823,17 +862,18 @@ static int qpnpint_get_irqchip_state(struct irq_data *d,
static int qpnpint_irq_domain_activate(struct irq_domain *domain,
struct irq_data *d, bool reserve)
{
- struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb_bus *bus = irq_data_get_irq_chip_data(d);
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u16 periph = hwirq_to_per(d->hwirq);
u16 apid = hwirq_to_apid(d->hwirq);
u16 sid = hwirq_to_sid(d->hwirq);
u16 irq = hwirq_to_irq(d->hwirq);
u8 buf;
- if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) {
- dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n",
+ if (bus->apid_data[apid].irq_ee != pmic_arb->ee) {
+ dev_err(&bus->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n",
sid, periph, irq, pmic_arb->ee,
- pmic_arb->apid_data[apid].irq_ee);
+ bus->apid_data[apid].irq_ee);
return -ENODEV;
}
@@ -860,15 +900,16 @@ static int qpnpint_irq_domain_translate(struct irq_domain *d,
unsigned long *out_hwirq,
unsigned int *out_type)
{
- struct spmi_pmic_arb *pmic_arb = d->host_data;
+ struct spmi_pmic_arb_bus *bus = d->host_data;
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u32 *intspec = fwspec->param;
u16 apid, ppid;
int rc;
- dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
+ dev_dbg(&bus->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
intspec[0], intspec[1], intspec[2]);
- if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node)
+ if (irq_domain_get_of_node(d) != bus->spmic->dev.of_node)
return -EINVAL;
if (fwspec->param_count != 4)
return -EINVAL;
@@ -876,37 +917,37 @@ static int qpnpint_irq_domain_translate(struct irq_domain *d,
return -EINVAL;
ppid = intspec[0] << 8 | intspec[1];
- rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid);
+ rc = pmic_arb->ver_ops->ppid_to_apid(bus, ppid);
if (rc < 0) {
- dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n",
- intspec[0], intspec[1], intspec[2], rc);
+ dev_err(&bus->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n",
+ intspec[0], intspec[1], intspec[2], rc);
return rc;
}
apid = rc;
/* Keep track of {max,min}_apid for bounding search during interrupt */
- if (apid > pmic_arb->max_apid)
- pmic_arb->max_apid = apid;
- if (apid < pmic_arb->min_apid)
- pmic_arb->min_apid = apid;
+ if (apid > bus->max_apid)
+ bus->max_apid = apid;
+ if (apid < bus->min_apid)
+ bus->min_apid = apid;
*out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid);
*out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
- dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
+ dev_dbg(&bus->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
return 0;
}
static struct lock_class_key qpnpint_irq_lock_class, qpnpint_irq_request_class;
-static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb,
+static void qpnpint_irq_domain_map(struct spmi_pmic_arb_bus *bus,
struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq, unsigned int type)
{
irq_flow_handler_t handler;
- dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n",
+ dev_dbg(&bus->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n",
virq, hwirq, type);
if (type & IRQ_TYPE_EDGE_BOTH)
@@ -917,7 +958,7 @@ static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb,
irq_set_lockdep_class(virq, &qpnpint_irq_lock_class,
&qpnpint_irq_request_class);
- irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb,
+ irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, bus,
handler, NULL, NULL);
}
@@ -925,7 +966,7 @@ static int qpnpint_irq_domain_alloc(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs,
void *data)
{
- struct spmi_pmic_arb *pmic_arb = domain->host_data;
+ struct spmi_pmic_arb_bus *bus = domain->host_data;
struct irq_fwspec *fwspec = data;
irq_hw_number_t hwirq;
unsigned int type;
@@ -936,29 +977,77 @@ static int qpnpint_irq_domain_alloc(struct irq_domain *domain,
return ret;
for (i = 0; i < nr_irqs; i++)
- qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i,
+ qpnpint_irq_domain_map(bus, domain, virq + i, hwirq + i,
type);
return 0;
}
-static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid)
+static int pmic_arb_init_apid_min_max(struct spmi_pmic_arb_bus *bus)
{
- u32 *mapping_table = pmic_arb->mapping_table;
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+
+ /*
+ * Initialize max_apid/min_apid to the opposite bounds, during
+ * the irq domain translation, we are sure to update these
+ */
+ bus->max_apid = 0;
+ bus->min_apid = pmic_arb->max_periphs - 1;
+
+ return 0;
+}
+
+static int pmic_arb_get_core_resources_v1(struct platform_device *pdev,
+ void __iomem *core)
+{
+ struct spmi_pmic_arb *pmic_arb = platform_get_drvdata(pdev);
+
+ pmic_arb->wr_base = core;
+ pmic_arb->rd_base = core;
+
+ pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS;
+
+ return 0;
+}
+
+static int pmic_arb_init_apid_v1(struct spmi_pmic_arb_bus *bus, int index)
+{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+ u32 *mapping_table;
+
+ if (index) {
+ dev_err(&bus->spmic->dev, "Unsupported buses count %d detected\n",
+ index);
+ return -EINVAL;
+ }
+
+ mapping_table = devm_kcalloc(&bus->spmic->dev, pmic_arb->max_periphs,
+ sizeof(*mapping_table), GFP_KERNEL);
+ if (!mapping_table)
+ return -ENOMEM;
+
+ bus->mapping_table = mapping_table;
+
+ return pmic_arb_init_apid_min_max(bus);
+}
+
+static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb_bus *bus, u16 ppid)
+{
+ u32 *mapping_table = bus->mapping_table;
int index = 0, i;
u16 apid_valid;
u16 apid;
u32 data;
- apid_valid = pmic_arb->ppid_to_apid[ppid];
+ apid_valid = bus->ppid_to_apid[ppid];
if (apid_valid & PMIC_ARB_APID_VALID) {
apid = apid_valid & ~PMIC_ARB_APID_VALID;
return apid;
}
for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
- if (!test_and_set_bit(index, pmic_arb->mapping_table_valid))
- mapping_table[index] = readl_relaxed(pmic_arb->cnfg +
+ if (!test_and_set_bit(index, bus->mapping_table_valid))
+ mapping_table[index] = readl_relaxed(bus->cnfg +
SPMI_MAPPING_TABLE_REG(index));
data = mapping_table[index];
@@ -968,9 +1057,9 @@ static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid)
index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
} else {
apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
- pmic_arb->ppid_to_apid[ppid]
+ bus->ppid_to_apid[ppid]
= apid | PMIC_ARB_APID_VALID;
- pmic_arb->apid_data[apid].ppid = ppid;
+ bus->apid_data[apid].ppid = ppid;
return apid;
}
} else {
@@ -978,9 +1067,9 @@ static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid)
index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
} else {
apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
- pmic_arb->ppid_to_apid[ppid]
+ bus->ppid_to_apid[ppid]
= apid | PMIC_ARB_APID_VALID;
- pmic_arb->apid_data[apid].ppid = ppid;
+ bus->apid_data[apid].ppid = ppid;
return apid;
}
}
@@ -990,24 +1079,26 @@ static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid)
}
/* v1 offset per ee */
-static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
- enum pmic_arb_channel ch_type)
+static int pmic_arb_offset_v1(struct spmi_pmic_arb_bus *bus, u8 sid, u16 addr,
+ enum pmic_arb_channel ch_type)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
return 0x800 + 0x80 * pmic_arb->channel;
}
-static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
+static u16 pmic_arb_find_apid(struct spmi_pmic_arb_bus *bus, u16 ppid)
{
- struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid];
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+ struct apid_data *apidd = &bus->apid_data[bus->last_apid];
u32 regval, offset;
u16 id, apid;
- for (apid = pmic_arb->last_apid; ; apid++, apidd++) {
+ for (apid = bus->last_apid; ; apid++, apidd++) {
offset = pmic_arb->ver_ops->apid_map_offset(apid);
if (offset >= pmic_arb->core_size)
break;
- regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(pmic_arb,
+ regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(bus,
apid));
apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
apidd->write_ee = apidd->irq_ee;
@@ -1017,33 +1108,61 @@ static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
continue;
id = (regval >> 8) & PMIC_ARB_PPID_MASK;
- pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID;
+ bus->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID;
apidd->ppid = id;
if (id == ppid) {
apid |= PMIC_ARB_APID_VALID;
break;
}
}
- pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID;
+ bus->last_apid = apid & ~PMIC_ARB_APID_VALID;
return apid;
}
-static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid)
+static int pmic_arb_get_obsrvr_chnls_v2(struct platform_device *pdev)
+{
+ struct spmi_pmic_arb *pmic_arb = platform_get_drvdata(pdev);
+
+ pmic_arb->rd_base = devm_platform_ioremap_resource_byname(pdev, "obsrvr");
+ if (IS_ERR(pmic_arb->rd_base))
+ return PTR_ERR(pmic_arb->rd_base);
+
+ pmic_arb->wr_base = devm_platform_ioremap_resource_byname(pdev, "chnls");
+ if (IS_ERR(pmic_arb->wr_base))
+ return PTR_ERR(pmic_arb->wr_base);
+
+ return 0;
+}
+
+static int pmic_arb_get_core_resources_v2(struct platform_device *pdev,
+ void __iomem *core)
+{
+ struct spmi_pmic_arb *pmic_arb = platform_get_drvdata(pdev);
+
+ pmic_arb->core = core;
+
+ pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS;
+
+ return pmic_arb_get_obsrvr_chnls_v2(pdev);
+}
+
+static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb_bus *bus, u16 ppid)
{
u16 apid_valid;
- apid_valid = pmic_arb->ppid_to_apid[ppid];
+ apid_valid = bus->ppid_to_apid[ppid];
if (!(apid_valid & PMIC_ARB_APID_VALID))
- apid_valid = pmic_arb_find_apid(pmic_arb, ppid);
+ apid_valid = pmic_arb_find_apid(bus, ppid);
if (!(apid_valid & PMIC_ARB_APID_VALID))
return -ENODEV;
return apid_valid & ~PMIC_ARB_APID_VALID;
}
-static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb)
+static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb_bus *bus)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
struct apid_data *apidd;
struct apid_data *prev_apidd;
u16 i, apid, ppid, apid_max;
@@ -1065,9 +1184,9 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb)
* where N = number of APIDs supported by the primary bus and
* M = number of APIDs supported by the secondary bus
*/
- apidd = &pmic_arb->apid_data[pmic_arb->base_apid];
- apid_max = pmic_arb->base_apid + pmic_arb->apid_count;
- for (i = pmic_arb->base_apid; i < apid_max; i++, apidd++) {
+ apidd = &bus->apid_data[bus->base_apid];
+ apid_max = bus->base_apid + bus->apid_count;
+ for (i = bus->base_apid; i < apid_max; i++, apidd++) {
offset = pmic_arb->ver_ops->apid_map_offset(i);
if (offset >= pmic_arb->core_size)
break;
@@ -1078,19 +1197,18 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb)
ppid = (regval >> 8) & PMIC_ARB_PPID_MASK;
is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval);
- regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(pmic_arb,
- i));
+ regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(bus, i));
apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE;
- valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID;
- apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
- prev_apidd = &pmic_arb->apid_data[apid];
+ valid = bus->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID;
+ apid = bus->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
+ prev_apidd = &bus->apid_data[apid];
if (!valid || apidd->write_ee == pmic_arb->ee) {
/* First PPID mapping or one for this EE */
- pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID;
+ bus->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID;
} else if (valid && is_irq_ee &&
prev_apidd->write_ee == pmic_arb->ee) {
/*
@@ -1101,42 +1219,43 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb)
}
apidd->ppid = ppid;
- pmic_arb->last_apid = i;
+ bus->last_apid = i;
}
/* Dump the mapping table for debug purposes. */
- dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n");
+ dev_dbg(&bus->spmic->dev, "PPID APID Write-EE IRQ-EE\n");
for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) {
- apid = pmic_arb->ppid_to_apid[ppid];
+ apid = bus->ppid_to_apid[ppid];
if (apid & PMIC_ARB_APID_VALID) {
apid &= ~PMIC_ARB_APID_VALID;
- apidd = &pmic_arb->apid_data[apid];
- dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n",
- ppid, apid, apidd->write_ee, apidd->irq_ee);
+ apidd = &bus->apid_data[apid];
+ dev_dbg(&bus->spmic->dev, "%#03X %3u %2u %2u\n",
+ ppid, apid, apidd->write_ee, apidd->irq_ee);
}
}
return 0;
}
-static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid)
+static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb_bus *bus, u16 ppid)
{
- if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID))
+ if (!(bus->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID))
return -ENODEV;
- return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
+ return bus->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID;
}
/* v2 offset per ppid and per ee */
-static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
- enum pmic_arb_channel ch_type)
+static int pmic_arb_offset_v2(struct spmi_pmic_arb_bus *bus, u8 sid, u16 addr,
+ enum pmic_arb_channel ch_type)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u16 apid;
u16 ppid;
int rc;
ppid = sid << 8 | ((addr >> 8) & 0xFF);
- rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid);
+ rc = pmic_arb_ppid_to_apid_v2(bus, ppid);
if (rc < 0)
return rc;
@@ -1144,19 +1263,55 @@ static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
return 0x1000 * pmic_arb->ee + 0x8000 * apid;
}
+static int pmic_arb_init_apid_v5(struct spmi_pmic_arb_bus *bus, int index)
+{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+ int ret;
+
+ if (index) {
+ dev_err(&bus->spmic->dev, "Unsupported buses count %d detected\n",
+ index);
+ return -EINVAL;
+ }
+
+ bus->base_apid = 0;
+ bus->apid_count = readl_relaxed(pmic_arb->core + PMIC_ARB_FEATURES) &
+ PMIC_ARB_FEATURES_PERIPH_MASK;
+
+ if (bus->base_apid + bus->apid_count > pmic_arb->max_periphs) {
+ dev_err(&bus->spmic->dev, "Unsupported APID count %d detected\n",
+ bus->base_apid + bus->apid_count);
+ return -EINVAL;
+ }
+
+ ret = pmic_arb_init_apid_min_max(bus);
+ if (ret)
+ return ret;
+
+ ret = pmic_arb_read_apid_map_v5(bus);
+ if (ret) {
+ dev_err(&bus->spmic->dev, "could not read APID->PPID mapping table, rc= %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
/*
* v5 offset per ee and per apid for observer channels and per apid for
* read/write channels.
*/
-static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
- enum pmic_arb_channel ch_type)
+static int pmic_arb_offset_v5(struct spmi_pmic_arb_bus *bus, u8 sid, u16 addr,
+ enum pmic_arb_channel ch_type)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u16 apid;
int rc;
u32 offset = 0;
u16 ppid = (sid << 8) | (addr >> 8);
- rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid);
+ rc = pmic_arb_ppid_to_apid_v5(bus, ppid);
if (rc < 0)
return rc;
@@ -1166,8 +1321,8 @@ static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
offset = 0x10000 * pmic_arb->ee + 0x80 * apid;
break;
case PMIC_ARB_CHANNEL_RW:
- if (pmic_arb->apid_data[apid].write_ee != pmic_arb->ee) {
- dev_err(&pmic_arb->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n",
+ if (bus->apid_data[apid].write_ee != pmic_arb->ee) {
+ dev_err(&bus->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n",
sid, addr);
return -EPERM;
}
@@ -1178,19 +1333,76 @@ static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
return offset;
}
+static int pmic_arb_get_core_resources_v7(struct platform_device *pdev,
+ void __iomem *core)
+{
+ struct spmi_pmic_arb *pmic_arb = platform_get_drvdata(pdev);
+
+ pmic_arb->core = core;
+
+ pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS_V7;
+
+ return pmic_arb_get_obsrvr_chnls_v2(pdev);
+}
+
+/*
+ * Only v7 supports 2 buses. Each bus will get a different apid count, read
+ * from different registers.
+ */
+static int pmic_arb_init_apid_v7(struct spmi_pmic_arb_bus *bus, int index)
+{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+ int ret;
+
+ if (index == 0) {
+ bus->base_apid = 0;
+ bus->apid_count = readl_relaxed(pmic_arb->core + PMIC_ARB_FEATURES) &
+ PMIC_ARB_FEATURES_PERIPH_MASK;
+ } else if (index == 1) {
+ bus->base_apid = readl_relaxed(pmic_arb->core + PMIC_ARB_FEATURES) &
+ PMIC_ARB_FEATURES_PERIPH_MASK;
+ bus->apid_count = readl_relaxed(pmic_arb->core + PMIC_ARB_FEATURES1) &
+ PMIC_ARB_FEATURES_PERIPH_MASK;
+ } else {
+ dev_err(&bus->spmic->dev, "Unsupported buses count %d detected\n",
+ bus->id);
+ return -EINVAL;
+ }
+
+ if (bus->base_apid + bus->apid_count > pmic_arb->max_periphs) {
+ dev_err(&bus->spmic->dev, "Unsupported APID count %d detected\n",
+ bus->base_apid + bus->apid_count);
+ return -EINVAL;
+ }
+
+ ret = pmic_arb_init_apid_min_max(bus);
+ if (ret)
+ return ret;
+
+ ret = pmic_arb_read_apid_map_v5(bus);
+ if (ret) {
+ dev_err(&bus->spmic->dev, "could not read APID->PPID mapping table, rc= %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
/*
* v7 offset per ee and per apid for observer channels and per apid for
* read/write channels.
*/
-static int pmic_arb_offset_v7(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
- enum pmic_arb_channel ch_type)
+static int pmic_arb_offset_v7(struct spmi_pmic_arb_bus *bus, u8 sid, u16 addr,
+ enum pmic_arb_channel ch_type)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
u16 apid;
int rc;
u32 offset = 0;
u16 ppid = (sid << 8) | (addr >> 8);
- rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid);
+ rc = pmic_arb->ver_ops->ppid_to_apid(bus, ppid);
if (rc < 0)
return rc;
@@ -1200,8 +1412,8 @@ static int pmic_arb_offset_v7(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
offset = 0x8000 * pmic_arb->ee + 0x20 * apid;
break;
case PMIC_ARB_CHANNEL_RW:
- if (pmic_arb->apid_data[apid].write_ee != pmic_arb->ee) {
- dev_err(&pmic_arb->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n",
+ if (bus->apid_data[apid].write_ee != pmic_arb->ee) {
+ dev_err(&bus->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n",
sid, addr);
return -EPERM;
}
@@ -1223,104 +1435,110 @@ static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
}
static void __iomem *
-pmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
+pmic_arb_owner_acc_status_v1(struct spmi_pmic_arb_bus *bus, u8 m, u16 n)
{
- return pmic_arb->intr + 0x20 * m + 0x4 * n;
+ return bus->intr + 0x20 * m + 0x4 * n;
}
static void __iomem *
-pmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
+pmic_arb_owner_acc_status_v2(struct spmi_pmic_arb_bus *bus, u8 m, u16 n)
{
- return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n;
+ return bus->intr + 0x100000 + 0x1000 * m + 0x4 * n;
}
static void __iomem *
-pmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
+pmic_arb_owner_acc_status_v3(struct spmi_pmic_arb_bus *bus, u8 m, u16 n)
{
- return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n;
+ return bus->intr + 0x200000 + 0x1000 * m + 0x4 * n;
}
static void __iomem *
-pmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
+pmic_arb_owner_acc_status_v5(struct spmi_pmic_arb_bus *bus, u8 m, u16 n)
{
- return pmic_arb->intr + 0x10000 * m + 0x4 * n;
+ return bus->intr + 0x10000 * m + 0x4 * n;
}
static void __iomem *
-pmic_arb_owner_acc_status_v7(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n)
+pmic_arb_owner_acc_status_v7(struct spmi_pmic_arb_bus *bus, u8 m, u16 n)
{
- return pmic_arb->intr + 0x1000 * m + 0x4 * n;
+ return bus->intr + 0x1000 * m + 0x4 * n;
}
static void __iomem *
-pmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_acc_enable_v1(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->intr + 0x200 + 0x4 * n;
+ return bus->intr + 0x200 + 0x4 * n;
}
static void __iomem *
-pmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_acc_enable_v2(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->intr + 0x1000 * n;
+ return bus->intr + 0x1000 * n;
}
static void __iomem *
-pmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_acc_enable_v5(struct spmi_pmic_arb_bus *bus, u16 n)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
return pmic_arb->wr_base + 0x100 + 0x10000 * n;
}
static void __iomem *
-pmic_arb_acc_enable_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_acc_enable_v7(struct spmi_pmic_arb_bus *bus, u16 n)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
return pmic_arb->wr_base + 0x100 + 0x1000 * n;
}
static void __iomem *
-pmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_status_v1(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->intr + 0x600 + 0x4 * n;
+ return bus->intr + 0x600 + 0x4 * n;
}
static void __iomem *
-pmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_status_v2(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->intr + 0x4 + 0x1000 * n;
+ return bus->intr + 0x4 + 0x1000 * n;
}
static void __iomem *
-pmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_status_v5(struct spmi_pmic_arb_bus *bus, u16 n)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
return pmic_arb->wr_base + 0x104 + 0x10000 * n;
}
static void __iomem *
-pmic_arb_irq_status_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_status_v7(struct spmi_pmic_arb_bus *bus, u16 n)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
return pmic_arb->wr_base + 0x104 + 0x1000 * n;
}
static void __iomem *
-pmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_clear_v1(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->intr + 0xA00 + 0x4 * n;
+ return bus->intr + 0xA00 + 0x4 * n;
}
static void __iomem *
-pmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_clear_v2(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->intr + 0x8 + 0x1000 * n;
+ return bus->intr + 0x8 + 0x1000 * n;
}
static void __iomem *
-pmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_clear_v5(struct spmi_pmic_arb_bus *bus, u16 n)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
return pmic_arb->wr_base + 0x108 + 0x10000 * n;
}
static void __iomem *
-pmic_arb_irq_clear_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_irq_clear_v7(struct spmi_pmic_arb_bus *bus, u16 n)
{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
return pmic_arb->wr_base + 0x108 + 0x1000 * n;
}
@@ -1340,9 +1558,9 @@ static u32 pmic_arb_apid_map_offset_v7(u16 n)
}
static void __iomem *
-pmic_arb_apid_owner_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_apid_owner_v2(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->cnfg + 0x700 + 0x4 * n;
+ return bus->cnfg + 0x700 + 0x4 * n;
}
/*
@@ -1351,13 +1569,15 @@ pmic_arb_apid_owner_v2(struct spmi_pmic_arb *pmic_arb, u16 n)
* 0.
*/
static void __iomem *
-pmic_arb_apid_owner_v7(struct spmi_pmic_arb *pmic_arb, u16 n)
+pmic_arb_apid_owner_v7(struct spmi_pmic_arb_bus *bus, u16 n)
{
- return pmic_arb->cnfg + 0x4 * (n - pmic_arb->base_apid);
+ return bus->cnfg + 0x4 * (n - bus->base_apid);
}
static const struct pmic_arb_ver_ops pmic_arb_v1 = {
.ver_str = "v1",
+ .get_core_resources = pmic_arb_get_core_resources_v1,
+ .init_apid = pmic_arb_init_apid_v1,
.ppid_to_apid = pmic_arb_ppid_to_apid_v1,
.non_data_cmd = pmic_arb_non_data_cmd_v1,
.offset = pmic_arb_offset_v1,
@@ -1372,6 +1592,8 @@ static const struct pmic_arb_ver_ops pmic_arb_v1 = {
static const struct pmic_arb_ver_ops pmic_arb_v2 = {
.ver_str = "v2",
+ .get_core_resources = pmic_arb_get_core_resources_v2,
+ .init_apid = pmic_arb_init_apid_v1,
.ppid_to_apid = pmic_arb_ppid_to_apid_v2,
.non_data_cmd = pmic_arb_non_data_cmd_v2,
.offset = pmic_arb_offset_v2,
@@ -1386,6 +1608,8 @@ static const struct pmic_arb_ver_ops pmic_arb_v2 = {
static const struct pmic_arb_ver_ops pmic_arb_v3 = {
.ver_str = "v3",
+ .get_core_resources = pmic_arb_get_core_resources_v2,
+ .init_apid = pmic_arb_init_apid_v1,
.ppid_to_apid = pmic_arb_ppid_to_apid_v2,
.non_data_cmd = pmic_arb_non_data_cmd_v2,
.offset = pmic_arb_offset_v2,
@@ -1400,6 +1624,8 @@ static const struct pmic_arb_ver_ops pmic_arb_v3 = {
static const struct pmic_arb_ver_ops pmic_arb_v5 = {
.ver_str = "v5",
+ .get_core_resources = pmic_arb_get_core_resources_v2,
+ .init_apid = pmic_arb_init_apid_v5,
.ppid_to_apid = pmic_arb_ppid_to_apid_v5,
.non_data_cmd = pmic_arb_non_data_cmd_v2,
.offset = pmic_arb_offset_v5,
@@ -1414,6 +1640,8 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = {
static const struct pmic_arb_ver_ops pmic_arb_v7 = {
.ver_str = "v7",
+ .get_core_resources = pmic_arb_get_core_resources_v7,
+ .init_apid = pmic_arb_init_apid_v7,
.ppid_to_apid = pmic_arb_ppid_to_apid_v5,
.non_data_cmd = pmic_arb_non_data_cmd_v2,
.offset = pmic_arb_offset_v7,
@@ -1433,145 +1661,181 @@ static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
.translate = qpnpint_irq_domain_translate,
};
-static int spmi_pmic_arb_probe(struct platform_device *pdev)
+static int spmi_pmic_arb_bus_init(struct platform_device *pdev,
+ struct device_node *node,
+ struct spmi_pmic_arb *pmic_arb)
{
- struct spmi_pmic_arb *pmic_arb;
+ int bus_index = pmic_arb->buses_available;
+ struct spmi_pmic_arb_bus *bus;
+ struct device *dev = &pdev->dev;
struct spmi_controller *ctrl;
- struct resource *res;
- void __iomem *core;
- u32 *mapping_table;
- u32 channel, ee, hw_ver;
- int err;
+ void __iomem *intr;
+ void __iomem *cnfg;
+ int index, ret;
+ int irq;
- ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb));
+ ctrl = devm_spmi_controller_alloc(dev, sizeof(*bus));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
- pmic_arb = spmi_controller_get_drvdata(ctrl);
- pmic_arb->spmic = ctrl;
+ ctrl->cmd = pmic_arb_cmd;
+ ctrl->read_cmd = pmic_arb_read_cmd;
+ ctrl->write_cmd = pmic_arb_write_cmd;
- /*
- * Please don't replace this with devm_platform_ioremap_resource() or
- * devm_ioremap_resource(). These both result in a call to
- * devm_request_mem_region() which prevents multiple mappings of this
- * register address range. SoCs with PMIC arbiter v7 may define two
- * arbiter devices, for the two physical SPMI interfaces, which share
- * some register address ranges (i.e. "core", "obsrvr", and "chnls").
- * Ensure that both devices probe successfully by calling devm_ioremap()
- * which does not result in a devm_request_mem_region() call.
- */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
- core = devm_ioremap(&ctrl->dev, res->start, resource_size(res));
- if (IS_ERR(core))
- return PTR_ERR(core);
+ bus = spmi_controller_get_drvdata(ctrl);
- pmic_arb->core_size = resource_size(res);
+ pmic_arb->buses[bus_index] = bus;
- pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID,
- sizeof(*pmic_arb->ppid_to_apid),
- GFP_KERNEL);
- if (!pmic_arb->ppid_to_apid)
+ raw_spin_lock_init(&bus->lock);
+
+ bus->ppid_to_apid = devm_kcalloc(dev, PMIC_ARB_MAX_PPID,
+ sizeof(*bus->ppid_to_apid),
+ GFP_KERNEL);
+ if (!bus->ppid_to_apid)
return -ENOMEM;
- hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
+ bus->apid_data = devm_kcalloc(dev, pmic_arb->max_periphs,
+ sizeof(*bus->apid_data),
+ GFP_KERNEL);
+ if (!bus->apid_data)
+ return -ENOMEM;
- if (hw_ver < PMIC_ARB_VERSION_V2_MIN) {
- pmic_arb->ver_ops = &pmic_arb_v1;
- pmic_arb->wr_base = core;
- pmic_arb->rd_base = core;
- } else {
- pmic_arb->core = core;
-
- if (hw_ver < PMIC_ARB_VERSION_V3_MIN)
- pmic_arb->ver_ops = &pmic_arb_v2;
- else if (hw_ver < PMIC_ARB_VERSION_V5_MIN)
- pmic_arb->ver_ops = &pmic_arb_v3;
- else if (hw_ver < PMIC_ARB_VERSION_V7_MIN)
- pmic_arb->ver_ops = &pmic_arb_v5;
- else
- pmic_arb->ver_ops = &pmic_arb_v7;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "obsrvr");
- pmic_arb->rd_base = devm_ioremap(&ctrl->dev, res->start,
- resource_size(res));
- if (IS_ERR(pmic_arb->rd_base))
- return PTR_ERR(pmic_arb->rd_base);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "chnls");
- pmic_arb->wr_base = devm_ioremap(&ctrl->dev, res->start,
- resource_size(res));
- if (IS_ERR(pmic_arb->wr_base))
- return PTR_ERR(pmic_arb->wr_base);
+ index = of_property_match_string(node, "reg-names", "cnfg");
+ if (index < 0) {
+ dev_err(dev, "cnfg reg region missing");
+ return -EINVAL;
}
- pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS;
+ cnfg = devm_of_iomap(dev, node, index, NULL);
+ if (IS_ERR(cnfg))
+ return PTR_ERR(cnfg);
- if (hw_ver >= PMIC_ARB_VERSION_V7_MIN) {
- pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS_V7;
- /* Optional property for v7: */
- of_property_read_u32(pdev->dev.of_node, "qcom,bus-id",
- &pmic_arb->bus_instance);
- if (pmic_arb->bus_instance > 1) {
- dev_err(&pdev->dev, "invalid bus instance (%u) specified\n",
- pmic_arb->bus_instance);
- return -EINVAL;
- }
+ index = of_property_match_string(node, "reg-names", "intr");
+ if (index < 0) {
+ dev_err(dev, "intr reg region missing");
+ return -EINVAL;
+ }
- if (pmic_arb->bus_instance == 0) {
- pmic_arb->base_apid = 0;
- pmic_arb->apid_count =
- readl_relaxed(core + PMIC_ARB_FEATURES) &
- PMIC_ARB_FEATURES_PERIPH_MASK;
- } else {
- pmic_arb->base_apid =
- readl_relaxed(core + PMIC_ARB_FEATURES) &
- PMIC_ARB_FEATURES_PERIPH_MASK;
- pmic_arb->apid_count =
- readl_relaxed(core + PMIC_ARB_FEATURES1) &
- PMIC_ARB_FEATURES_PERIPH_MASK;
- }
+ intr = devm_of_iomap(dev, node, index, NULL);
+ if (IS_ERR(intr))
+ return PTR_ERR(intr);
- if (pmic_arb->base_apid + pmic_arb->apid_count > pmic_arb->max_periphs) {
- dev_err(&pdev->dev, "Unsupported APID count %d detected\n",
- pmic_arb->base_apid + pmic_arb->apid_count);
- return -EINVAL;
- }
- } else if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) {
- pmic_arb->base_apid = 0;
- pmic_arb->apid_count = readl_relaxed(core + PMIC_ARB_FEATURES) &
- PMIC_ARB_FEATURES_PERIPH_MASK;
+ irq = of_irq_get_byname(node, "periph_irq");
+ if (irq <= 0)
+ return irq ?: -ENXIO;
- if (pmic_arb->apid_count > pmic_arb->max_periphs) {
- dev_err(&pdev->dev, "Unsupported APID count %d detected\n",
- pmic_arb->apid_count);
- return -EINVAL;
+ bus->pmic_arb = pmic_arb;
+ bus->intr = intr;
+ bus->cnfg = cnfg;
+ bus->irq = irq;
+ bus->spmic = ctrl;
+ bus->id = bus_index;
+
+ ret = pmic_arb->ver_ops->init_apid(bus, bus_index);
+ if (ret)
+ return ret;
+
+ dev_dbg(&pdev->dev, "adding irq domain for bus %d\n", bus_index);
+
+ bus->domain = irq_domain_add_tree(dev->of_node,
+ &pmic_arb_irq_domain_ops, bus);
+ if (!bus->domain) {
+ dev_err(&pdev->dev, "unable to create irq_domain\n");
+ return -ENOMEM;
+ }
+
+ irq_set_chained_handler_and_data(bus->irq,
+ pmic_arb_chained_irq, bus);
+
+ ctrl->dev.of_node = node;
+ dev_set_name(&ctrl->dev, "spmi-%d", bus_index);
+
+ ret = devm_spmi_controller_add(dev, ctrl);
+ if (ret)
+ return ret;
+
+ pmic_arb->buses_available++;
+
+ return 0;
+}
+
+static int spmi_pmic_arb_register_buses(struct spmi_pmic_arb *pmic_arb,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct device_node *child;
+ int ret;
+
+ /* legacy mode doesn't provide child node for the bus */
+ if (of_device_is_compatible(node, "qcom,spmi-pmic-arb"))
+ return spmi_pmic_arb_bus_init(pdev, node, pmic_arb);
+
+ for_each_available_child_of_node(node, child) {
+ if (of_node_name_eq(child, "spmi")) {
+ ret = spmi_pmic_arb_bus_init(pdev, child, pmic_arb);
+ if (ret)
+ return ret;
}
}
- pmic_arb->apid_data = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs,
- sizeof(*pmic_arb->apid_data),
- GFP_KERNEL);
- if (!pmic_arb->apid_data)
+ return ret;
+}
+
+static void spmi_pmic_arb_deregister_buses(struct spmi_pmic_arb *pmic_arb)
+{
+ int i;
+
+ for (i = 0; i < pmic_arb->buses_available; i++) {
+ struct spmi_pmic_arb_bus *bus = pmic_arb->buses[i];
+
+ irq_set_chained_handler_and_data(bus->irq,
+ NULL, NULL);
+ irq_domain_remove(bus->domain);
+ }
+}
+
+static int spmi_pmic_arb_probe(struct platform_device *pdev)
+{
+ struct spmi_pmic_arb *pmic_arb;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *core;
+ u32 channel, ee, hw_ver;
+ int err;
+
+ pmic_arb = devm_kzalloc(dev, sizeof(*pmic_arb), GFP_KERNEL);
+ if (!pmic_arb)
return -ENOMEM;
- dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
- pmic_arb->ver_ops->ver_str, hw_ver);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ core = devm_ioremap(dev, res->start, resource_size(res));
+ if (!core)
+ return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
- pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res);
- if (IS_ERR(pmic_arb->intr))
- return PTR_ERR(pmic_arb->intr);
+ pmic_arb->core_size = resource_size(res);
+
+ platform_set_drvdata(pdev, pmic_arb);
+
+ hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
- pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res);
- if (IS_ERR(pmic_arb->cnfg))
- return PTR_ERR(pmic_arb->cnfg);
+ if (hw_ver < PMIC_ARB_VERSION_V2_MIN)
+ pmic_arb->ver_ops = &pmic_arb_v1;
+ else if (hw_ver < PMIC_ARB_VERSION_V3_MIN)
+ pmic_arb->ver_ops = &pmic_arb_v2;
+ else if (hw_ver < PMIC_ARB_VERSION_V5_MIN)
+ pmic_arb->ver_ops = &pmic_arb_v3;
+ else if (hw_ver < PMIC_ARB_VERSION_V7_MIN)
+ pmic_arb->ver_ops = &pmic_arb_v5;
+ else
+ pmic_arb->ver_ops = &pmic_arb_v7;
+
+ err = pmic_arb->ver_ops->get_core_resources(pdev, core);
+ if (err)
+ return err;
- pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq");
- if (pmic_arb->irq < 0)
- return pmic_arb->irq;
+ dev_info(dev, "PMIC arbiter version %s (0x%x)\n",
+ pmic_arb->ver_ops->ver_str, hw_ver);
err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
if (err) {
@@ -1599,66 +1863,20 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
}
pmic_arb->ee = ee;
- mapping_table = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs,
- sizeof(*mapping_table), GFP_KERNEL);
- if (!mapping_table)
- return -ENOMEM;
- pmic_arb->mapping_table = mapping_table;
- /* Initialize max_apid/min_apid to the opposite bounds, during
- * the irq domain translation, we are sure to update these */
- pmic_arb->max_apid = 0;
- pmic_arb->min_apid = pmic_arb->max_periphs - 1;
-
- platform_set_drvdata(pdev, ctrl);
- raw_spin_lock_init(&pmic_arb->lock);
-
- ctrl->cmd = pmic_arb_cmd;
- ctrl->read_cmd = pmic_arb_read_cmd;
- ctrl->write_cmd = pmic_arb_write_cmd;
-
- if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) {
- err = pmic_arb_read_apid_map_v5(pmic_arb);
- if (err) {
- dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n",
- err);
- return err;
- }
- }
-
- dev_dbg(&pdev->dev, "adding irq domain\n");
- pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node,
- &pmic_arb_irq_domain_ops, pmic_arb);
- if (!pmic_arb->domain) {
- dev_err(&pdev->dev, "unable to create irq_domain\n");
- return -ENOMEM;
- }
-
- irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq,
- pmic_arb);
- err = spmi_controller_add(ctrl);
- if (err)
- goto err_domain_remove;
-
- return 0;
-
-err_domain_remove:
- irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
- irq_domain_remove(pmic_arb->domain);
- return err;
+ return spmi_pmic_arb_register_buses(pmic_arb, pdev);
}
static void spmi_pmic_arb_remove(struct platform_device *pdev)
{
- struct spmi_controller *ctrl = platform_get_drvdata(pdev);
- struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
- spmi_controller_remove(ctrl);
- irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
- irq_domain_remove(pmic_arb->domain);
+ struct spmi_pmic_arb *pmic_arb = platform_get_drvdata(pdev);
+
+ spmi_pmic_arb_deregister_buses(pmic_arb);
}
static const struct of_device_id spmi_pmic_arb_match_table[] = {
{ .compatible = "qcom,spmi-pmic-arb", },
+ { .compatible = "qcom,x1e80100-spmi-pmic-arb", },
{},
};
MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
index 3a60fd2e09e1..667085cb199d 100644
--- a/drivers/spmi/spmi.c
+++ b/drivers/spmi/spmi.c
@@ -378,7 +378,7 @@ static int spmi_drv_uevent(const struct device *dev, struct kobj_uevent_env *env
return 0;
}
-static struct bus_type spmi_bus_type = {
+static const struct bus_type spmi_bus_type = {
.name = "spmi",
.match = spmi_device_match,
.probe = spmi_drv_probe,
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index 2e16c5338e5b..b060dcd7c635 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -125,24 +125,6 @@ config UIO_FSL_ELBC_GPCM_NETX5152
Information about this hardware can be found at:
http://www.hilscher.com/netx
-config UIO_PRUSS
- tristate "Texas Instruments PRUSS driver"
- select GENERIC_ALLOCATOR
- depends on HAS_IOMEM && HAS_DMA
- help
- PRUSS driver for OMAPL138/DA850/AM18XX devices
- PRUSS driver requires user space components, examples and user space
- driver is available from below SVN repo - you may use anonymous login
-
- https://gforge.ti.com/gf/project/pru_sw/
-
- More info on API is available at below wiki
-
- http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader
-
- To compile this driver as a module, choose M here: the module
- will be called uio_pruss.
-
config UIO_MF624
tristate "Humusoft MF624 DAQ PCI card driver"
depends on PCI
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index f2f416a14228..1c5f3b5a95cf 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -7,7 +7,6 @@ obj-$(CONFIG_UIO_AEC) += uio_aec.o
obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
obj-$(CONFIG_UIO_NETX) += uio_netx.o
-obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
obj-$(CONFIG_UIO_MF624) += uio_mf624.o
obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o
obj-$(CONFIG_UIO_HV_GENERIC) += uio_hv_generic.o
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 009158fef2a8..5ce429286ab0 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -438,22 +438,36 @@ void uio_event_notify(struct uio_info *info)
EXPORT_SYMBOL_GPL(uio_event_notify);
/**
- * uio_interrupt - hardware interrupt handler
+ * uio_interrupt_handler - hardware interrupt handler
* @irq: IRQ number, can be UIO_IRQ_CYCLIC for cyclic timer
* @dev_id: Pointer to the devices uio_device structure
*/
-static irqreturn_t uio_interrupt(int irq, void *dev_id)
+static irqreturn_t uio_interrupt_handler(int irq, void *dev_id)
{
struct uio_device *idev = (struct uio_device *)dev_id;
irqreturn_t ret;
ret = idev->info->handler(irq, idev->info);
if (ret == IRQ_HANDLED)
- uio_event_notify(idev->info);
+ ret = IRQ_WAKE_THREAD;
return ret;
}
+/**
+ * uio_interrupt_thread - irq thread handler
+ * @irq: IRQ number
+ * @dev_id: Pointer to the devices uio_device structure
+ */
+static irqreturn_t uio_interrupt_thread(int irq, void *dev_id)
+{
+ struct uio_device *idev = (struct uio_device *)dev_id;
+
+ uio_event_notify(idev->info);
+
+ return IRQ_HANDLED;
+}
+
struct uio_listener {
struct uio_device *dev;
s32 event_count;
@@ -1024,8 +1038,8 @@ int __uio_register_device(struct module *owner,
* FDs at the time of unregister and therefore may not be
* freed until they are released.
*/
- ret = request_irq(info->irq, uio_interrupt,
- info->irq_flags, info->name, idev);
+ ret = request_threaded_irq(info->irq, uio_interrupt_handler, uio_interrupt_thread,
+ info->irq_flags, info->name, idev);
if (ret) {
info->uio_dev = NULL;
goto err_request_irq;
diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c
index 82dda799f327..496caff66e7e 100644
--- a/drivers/uio/uio_fsl_elbc_gpcm.c
+++ b/drivers/uio/uio_fsl_elbc_gpcm.c
@@ -427,7 +427,7 @@ out_err2:
return ret;
}
-static int uio_fsl_elbc_gpcm_remove(struct platform_device *pdev)
+static void uio_fsl_elbc_gpcm_remove(struct platform_device *pdev)
{
struct uio_info *info = platform_get_drvdata(pdev);
struct fsl_elbc_gpcm *priv = info->priv;
@@ -438,8 +438,6 @@ static int uio_fsl_elbc_gpcm_remove(struct platform_device *pdev)
priv->shutdown(info, false);
iounmap(info->mem[0].internal_addr);
- return 0;
-
}
static const struct of_device_id uio_fsl_elbc_gpcm_match[] = {
@@ -455,7 +453,7 @@ static struct platform_driver uio_fsl_elbc_gpcm_driver = {
.dev_groups = uio_fsl_elbc_gpcm_groups,
},
.probe = uio_fsl_elbc_gpcm_probe,
- .remove = uio_fsl_elbc_gpcm_remove,
+ .remove_new = uio_fsl_elbc_gpcm_remove,
};
module_platform_driver(uio_fsl_elbc_gpcm_driver);
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
index 6be3462b109f..b45653752301 100644
--- a/drivers/uio/uio_hv_generic.c
+++ b/drivers/uio/uio_hv_generic.c
@@ -36,7 +36,6 @@
#define DRIVER_AUTHOR "Stephen Hemminger <sthemmin at microsoft.com>"
#define DRIVER_DESC "Generic UIO driver for VMBus devices"
-#define HV_RING_SIZE 512 /* pages */
#define SEND_BUFFER_SIZE (16 * 1024 * 1024)
#define RECV_BUFFER_SIZE (31 * 1024 * 1024)
@@ -84,6 +83,9 @@ hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
dev->channel->inbound.ring_buffer->interrupt_mask = !irq_state;
virt_mb();
+ if (!dev->channel->offermsg.monitor_allocated && irq_state)
+ vmbus_setevent(dev->channel);
+
return 0;
}
@@ -143,7 +145,7 @@ static const struct bin_attribute ring_buffer_bin_attr = {
.name = "ring",
.mode = 0600,
},
- .size = 2 * HV_RING_SIZE * PAGE_SIZE,
+ .size = 2 * SZ_2M,
.mmap = hv_uio_ring_mmap,
};
@@ -153,7 +155,7 @@ hv_uio_new_channel(struct vmbus_channel *new_sc)
{
struct hv_device *hv_dev = new_sc->primary_channel->device_obj;
struct device *device = &hv_dev->device;
- const size_t ring_bytes = HV_RING_SIZE * PAGE_SIZE;
+ const size_t ring_bytes = SZ_2M;
int ret;
/* Create host communication ring */
@@ -240,19 +242,16 @@ hv_uio_probe(struct hv_device *dev,
struct hv_uio_private_data *pdata;
void *ring_buffer;
int ret;
+ size_t ring_size = hv_dev_ring_size(channel);
- /* Communicating with host has to be via shared memory not hypercall */
- if (!channel->offermsg.monitor_allocated) {
- dev_err(&dev->device, "vmbus channel requires hypercall\n");
- return -ENOTSUPP;
- }
+ if (!ring_size)
+ ring_size = SZ_2M;
pdata = devm_kzalloc(&dev->device, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- ret = vmbus_alloc_ring(channel, HV_RING_SIZE * PAGE_SIZE,
- HV_RING_SIZE * PAGE_SIZE);
+ ret = vmbus_alloc_ring(channel, ring_size, ring_size);
if (ret)
return ret;
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index 63258b6accc4..796f5be0a086 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -23,8 +23,8 @@
#include <linux/irq.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#define DRIVER_NAME "uio_pdrv_genirq"
@@ -110,7 +110,7 @@ static void uio_pdrv_genirq_cleanup(void *data)
static int uio_pdrv_genirq_probe(struct platform_device *pdev)
{
struct uio_info *uioinfo = dev_get_platdata(&pdev->dev);
- struct device_node *node = pdev->dev.of_node;
+ struct fwnode_handle *node = dev_fwnode(&pdev->dev);
struct uio_pdrv_genirq_platdata *priv;
struct uio_mem *uiomem;
int ret = -EINVAL;
@@ -127,11 +127,11 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
return -ENOMEM;
}
- if (!of_property_read_string(node, "linux,uio-name", &name))
+ if (!device_property_read_string(&pdev->dev, "linux,uio-name", &name))
uioinfo->name = devm_kstrdup(&pdev->dev, name, GFP_KERNEL);
else
uioinfo->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
- "%pOFn", node);
+ "%pfwP", node);
uioinfo->version = "devicetree";
/* Multiple IRQs are not supported */
diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c
deleted file mode 100644
index f67881cba645..000000000000
--- a/drivers/uio/uio_pruss.c
+++ /dev/null
@@ -1,255 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Programmable Real-Time Unit Sub System (PRUSS) UIO driver (uio_pruss)
- *
- * This driver exports PRUSS host event out interrupts and PRUSS, L3 RAM,
- * and DDR RAM to user space for applications interacting with PRUSS firmware
- *
- * Copyright (C) 2010-11 Texas Instruments Incorporated - http://www.ti.com/
- */
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/uio_driver.h>
-#include <linux/platform_data/uio_pruss.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/sizes.h>
-#include <linux/slab.h>
-#include <linux/genalloc.h>
-
-#define DRV_NAME "pruss_uio"
-#define DRV_VERSION "1.0"
-
-static int sram_pool_sz = SZ_16K;
-module_param(sram_pool_sz, int, 0);
-MODULE_PARM_DESC(sram_pool_sz, "sram pool size to allocate ");
-
-static int extram_pool_sz = SZ_256K;
-module_param(extram_pool_sz, int, 0);
-MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate");
-
-/*
- * Host event IRQ numbers from PRUSS - PRUSS can generate up to 8 interrupt
- * events to AINTC of ARM host processor - which can be used for IPC b/w PRUSS
- * firmware and user space application, async notification from PRU firmware
- * to user space application
- * 3 PRU_EVTOUT0
- * 4 PRU_EVTOUT1
- * 5 PRU_EVTOUT2
- * 6 PRU_EVTOUT3
- * 7 PRU_EVTOUT4
- * 8 PRU_EVTOUT5
- * 9 PRU_EVTOUT6
- * 10 PRU_EVTOUT7
-*/
-#define MAX_PRUSS_EVT 8
-
-#define PINTC_HIDISR 0x0038
-#define PINTC_HIPIR 0x0900
-#define HIPIR_NOPEND 0x80000000
-#define PINTC_HIER 0x1500
-
-struct uio_pruss_dev {
- struct uio_info *info;
- struct clk *pruss_clk;
- dma_addr_t sram_paddr;
- dma_addr_t ddr_paddr;
- void __iomem *prussio_vaddr;
- unsigned long sram_vaddr;
- void *ddr_vaddr;
- unsigned int hostirq_start;
- unsigned int pintc_base;
- struct gen_pool *sram_pool;
-};
-
-static irqreturn_t pruss_handler(int irq, struct uio_info *info)
-{
- struct uio_pruss_dev *gdev = info->priv;
- int intr_bit = (irq - gdev->hostirq_start + 2);
- int val, intr_mask = (1 << intr_bit);
- void __iomem *base = gdev->prussio_vaddr + gdev->pintc_base;
- void __iomem *intren_reg = base + PINTC_HIER;
- void __iomem *intrdis_reg = base + PINTC_HIDISR;
- void __iomem *intrstat_reg = base + PINTC_HIPIR + (intr_bit << 2);
-
- val = ioread32(intren_reg);
- /* Is interrupt enabled and active ? */
- if (!(val & intr_mask) && (ioread32(intrstat_reg) & HIPIR_NOPEND))
- return IRQ_NONE;
- /* Disable interrupt */
- iowrite32(intr_bit, intrdis_reg);
- return IRQ_HANDLED;
-}
-
-static void pruss_cleanup(struct device *dev, struct uio_pruss_dev *gdev)
-{
- int cnt;
- struct uio_info *p = gdev->info;
-
- for (cnt = 0; cnt < MAX_PRUSS_EVT; cnt++, p++) {
- uio_unregister_device(p);
- }
- iounmap(gdev->prussio_vaddr);
- if (gdev->ddr_vaddr) {
- dma_free_coherent(dev, extram_pool_sz, gdev->ddr_vaddr,
- gdev->ddr_paddr);
- }
- if (gdev->sram_vaddr)
- gen_pool_free(gdev->sram_pool,
- gdev->sram_vaddr,
- sram_pool_sz);
- clk_disable(gdev->pruss_clk);
-}
-
-static int pruss_probe(struct platform_device *pdev)
-{
- struct uio_info *p;
- struct uio_pruss_dev *gdev;
- struct resource *regs_prussio;
- struct device *dev = &pdev->dev;
- int ret, cnt, i, len;
- struct uio_pruss_pdata *pdata = dev_get_platdata(dev);
-
- gdev = devm_kzalloc(dev, sizeof(struct uio_pruss_dev), GFP_KERNEL);
- if (!gdev)
- return -ENOMEM;
-
- gdev->info = devm_kcalloc(dev, MAX_PRUSS_EVT, sizeof(*p), GFP_KERNEL);
- if (!gdev->info)
- return -ENOMEM;
-
- /* Power on PRU in case its not done as part of boot-loader */
- gdev->pruss_clk = devm_clk_get(dev, "pruss");
- if (IS_ERR(gdev->pruss_clk)) {
- dev_err(dev, "Failed to get clock\n");
- return PTR_ERR(gdev->pruss_clk);
- }
-
- ret = clk_enable(gdev->pruss_clk);
- if (ret) {
- dev_err(dev, "Failed to enable clock\n");
- return ret;
- }
-
- regs_prussio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs_prussio) {
- dev_err(dev, "No PRUSS I/O resource specified\n");
- ret = -EIO;
- goto err_clk_disable;
- }
-
- if (!regs_prussio->start) {
- dev_err(dev, "Invalid memory resource\n");
- ret = -EIO;
- goto err_clk_disable;
- }
-
- if (pdata->sram_pool) {
- gdev->sram_pool = pdata->sram_pool;
- gdev->sram_vaddr =
- (unsigned long)gen_pool_dma_alloc(gdev->sram_pool,
- sram_pool_sz, &gdev->sram_paddr);
- if (!gdev->sram_vaddr) {
- dev_err(dev, "Could not allocate SRAM pool\n");
- ret = -ENOMEM;
- goto err_clk_disable;
- }
- }
-
- gdev->ddr_vaddr = dma_alloc_coherent(dev, extram_pool_sz,
- &(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA);
- if (!gdev->ddr_vaddr) {
- dev_err(dev, "Could not allocate external memory\n");
- ret = -ENOMEM;
- goto err_free_sram;
- }
-
- len = resource_size(regs_prussio);
- gdev->prussio_vaddr = ioremap(regs_prussio->start, len);
- if (!gdev->prussio_vaddr) {
- dev_err(dev, "Can't remap PRUSS I/O address range\n");
- ret = -ENOMEM;
- goto err_free_ddr_vaddr;
- }
-
- ret = platform_get_irq(pdev, 0);
- if (ret < 0)
- goto err_unmap;
-
- gdev->hostirq_start = ret;
- gdev->pintc_base = pdata->pintc_base;
-
- for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) {
- p->mem[0].addr = regs_prussio->start;
- p->mem[0].size = resource_size(regs_prussio);
- p->mem[0].memtype = UIO_MEM_PHYS;
-
- p->mem[1].addr = gdev->sram_paddr;
- p->mem[1].size = sram_pool_sz;
- p->mem[1].memtype = UIO_MEM_PHYS;
-
- p->mem[2].addr = (uintptr_t) gdev->ddr_vaddr;
- p->mem[2].dma_addr = gdev->ddr_paddr;
- p->mem[2].size = extram_pool_sz;
- p->mem[2].memtype = UIO_MEM_DMA_COHERENT;
- p->mem[2].dma_device = dev;
-
- p->name = devm_kasprintf(dev, GFP_KERNEL, "pruss_evt%d", cnt);
- p->version = DRV_VERSION;
-
- /* Register PRUSS IRQ lines */
- p->irq = gdev->hostirq_start + cnt;
- p->handler = pruss_handler;
- p->priv = gdev;
-
- ret = uio_register_device(dev, p);
- if (ret < 0)
- goto err_unloop;
- }
-
- platform_set_drvdata(pdev, gdev);
- return 0;
-
-err_unloop:
- for (i = 0, p = gdev->info; i < cnt; i++, p++) {
- uio_unregister_device(p);
- }
-err_unmap:
- iounmap(gdev->prussio_vaddr);
-err_free_ddr_vaddr:
- dma_free_coherent(dev, extram_pool_sz, gdev->ddr_vaddr,
- gdev->ddr_paddr);
-err_free_sram:
- if (pdata->sram_pool)
- gen_pool_free(gdev->sram_pool, gdev->sram_vaddr, sram_pool_sz);
-err_clk_disable:
- clk_disable(gdev->pruss_clk);
-
- return ret;
-}
-
-static int pruss_remove(struct platform_device *dev)
-{
- struct uio_pruss_dev *gdev = platform_get_drvdata(dev);
-
- pruss_cleanup(&dev->dev, gdev);
- return 0;
-}
-
-static struct platform_driver pruss_driver = {
- .probe = pruss_probe,
- .remove = pruss_remove,
- .driver = {
- .name = DRV_NAME,
- },
-};
-
-module_platform_driver(pruss_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR("Amit Chatterjee <amit.chatterjee@ti.com>");
-MODULE_AUTHOR("Pratheesh Gangadhar <pratheesh@ti.com>");
diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
index 34128e6bbbfa..a39fa8bf866a 100644
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -5,15 +5,15 @@
* Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi>
*/
-#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of_platform.h>
-#include <linux/err.h>
-#include <linux/of.h>
-#include <linux/delay.h>
+#include <linux/property.h>
+#include <linux/types.h>
#include <linux/w1.h>
@@ -63,20 +63,11 @@ static u8 w1_gpio_read_bit(void *data)
return gpiod_get_value(ddata->gpiod) ? 1 : 0;
}
-#if defined(CONFIG_OF)
-static const struct of_device_id w1_gpio_dt_ids[] = {
- { .compatible = "w1-gpio" },
- {}
-};
-MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids);
-#endif
-
static int w1_gpio_probe(struct platform_device *pdev)
{
struct w1_bus_master *master;
struct w1_gpio_ddata *ddata;
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
/* Enforce open drain mode by default */
enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
int err;
@@ -91,27 +82,22 @@ static int w1_gpio_probe(struct platform_device *pdev)
* driver it high/low like we are in full control of the line and
* open drain will happen transparently.
*/
- if (of_property_present(np, "linux,open-drain"))
+ if (device_property_present(dev, "linux,open-drain"))
gflags = GPIOD_OUT_LOW;
- master = devm_kzalloc(dev, sizeof(struct w1_bus_master),
- GFP_KERNEL);
+ master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
ddata->gpiod = devm_gpiod_get_index(dev, NULL, 0, gflags);
- if (IS_ERR(ddata->gpiod)) {
- dev_err(dev, "gpio_request (pin) failed\n");
- return PTR_ERR(ddata->gpiod);
- }
+ if (IS_ERR(ddata->gpiod))
+ return dev_err_probe(dev, PTR_ERR(ddata->gpiod), "gpio_request (pin) failed\n");
ddata->pullup_gpiod =
devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_LOW);
- if (IS_ERR(ddata->pullup_gpiod)) {
- dev_err(dev, "gpio_request_one "
- "(ext_pullup_enable_pin) failed\n");
- return PTR_ERR(ddata->pullup_gpiod);
- }
+ if (IS_ERR(ddata->pullup_gpiod))
+ return dev_err_probe(dev, PTR_ERR(ddata->pullup_gpiod),
+ "gpio_request (ext_pullup_enable_pin) failed\n");
master->data = ddata;
master->read_bit = w1_gpio_read_bit;
@@ -128,13 +114,10 @@ static int w1_gpio_probe(struct platform_device *pdev)
master->set_pullup = w1_gpio_set_pullup;
err = w1_add_master_device(master);
- if (err) {
- dev_err(dev, "w1_add_master device failed\n");
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "w1_add_master device failed\n");
- if (ddata->pullup_gpiod)
- gpiod_set_value(ddata->pullup_gpiod, 1);
+ gpiod_set_value(ddata->pullup_gpiod, 1);
platform_set_drvdata(pdev, master);
@@ -146,16 +129,21 @@ static void w1_gpio_remove(struct platform_device *pdev)
struct w1_bus_master *master = platform_get_drvdata(pdev);
struct w1_gpio_ddata *ddata = master->data;
- if (ddata->pullup_gpiod)
- gpiod_set_value(ddata->pullup_gpiod, 0);
+ gpiod_set_value(ddata->pullup_gpiod, 0);
w1_remove_master_device(master);
}
+static const struct of_device_id w1_gpio_dt_ids[] = {
+ { .compatible = "w1-gpio" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids);
+
static struct platform_driver w1_gpio_driver = {
.driver = {
.name = "w1-gpio",
- .of_match_table = of_match_ptr(w1_gpio_dt_ids),
+ .of_match_table = w1_gpio_dt_ids,
},
.probe = w1_gpio_probe,
.remove_new = w1_gpio_remove,
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 5f288d475490..f09ace92176e 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -12,6 +12,7 @@
#include <linux/io.h>
#include <linux/perf_event.h>
#include <linux/sched.h>
+#include <linux/platform_device.h>
/* Peripheral id registers (0xFD0-0xFEC) */
#define CORESIGHT_PERIPHIDR4 0xfd0
@@ -658,4 +659,9 @@ coresight_find_output_type(struct coresight_platform_data *pdata,
enum coresight_dev_type type,
union coresight_dev_subtype subtype);
+int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
+ struct platform_driver *pdev_drv);
+
+void coresight_remove_driver(struct amba_driver *amba_drv,
+ struct platform_driver *pdev_drv);
#endif /* _LINUX_COREISGHT_H */
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 702e9108bbb4..426b7d58a438 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -6,14 +6,15 @@
#ifndef _COUNTER_H_
#define _COUNTER_H_
+#include <linux/array_size.h>
#include <linux/cdev.h>
#include <linux/device.h>
-#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/mutex.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>
#include <linux/wait.h>
+
#include <uapi/linux/counter.h>
struct counter_device;
@@ -359,7 +360,6 @@ struct counter_ops {
* @num_counts: number of Counts specified in @counts
* @ext: optional array of Counter device extensions
* @num_ext: number of Counter device extensions specified in @ext
- * @priv: optional private data supplied by driver
* @dev: internal device structure
* @chrdev: internal character device structure
* @events_list: list of current watching Counter events
@@ -602,6 +602,9 @@ struct counter_array {
#define COUNTER_COMP_FLOOR(_read, _write) \
COUNTER_COMP_COUNT_U64("floor", _read, _write)
+#define COUNTER_COMP_FREQUENCY(_read) \
+ COUNTER_COMP_SIGNAL_U64("frequency", _read, NULL)
+
#define COUNTER_COMP_POLARITY(_read, _write, _available) \
{ \
.type = COUNTER_COMP_SIGNAL_POLARITY, \
diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
index 223da48a6d18..94c4edd047e5 100644
--- a/include/linux/fpga/fpga-bridge.h
+++ b/include/linux/fpga/fpga-bridge.h
@@ -45,6 +45,7 @@ struct fpga_bridge_info {
* @dev: FPGA bridge device
* @mutex: enforces exclusive reference to bridge
* @br_ops: pointer to struct of FPGA bridge ops
+ * @br_ops_owner: module containing the br_ops
* @info: fpga image specific information
* @node: FPGA bridge list node
* @priv: low level driver private date
@@ -54,6 +55,7 @@ struct fpga_bridge {
struct device dev;
struct mutex mutex; /* for exclusive reference to bridge */
const struct fpga_bridge_ops *br_ops;
+ struct module *br_ops_owner;
struct fpga_image_info *info;
struct list_head node;
void *priv;
@@ -79,10 +81,12 @@ int of_fpga_bridge_get_to_list(struct device_node *np,
struct fpga_image_info *info,
struct list_head *bridge_list);
+#define fpga_bridge_register(parent, name, br_ops, priv) \
+ __fpga_bridge_register(parent, name, br_ops, priv, THIS_MODULE)
struct fpga_bridge *
-fpga_bridge_register(struct device *parent, const char *name,
- const struct fpga_bridge_ops *br_ops,
- void *priv);
+__fpga_bridge_register(struct device *parent, const char *name,
+ const struct fpga_bridge_ops *br_ops, void *priv,
+ struct module *owner);
void fpga_bridge_unregister(struct fpga_bridge *br);
#endif /* _LINUX_FPGA_BRIDGE_H */
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 54f63459efd6..0d4fe068f3d8 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -201,6 +201,7 @@ struct fpga_manager_ops {
* @state: state of fpga manager
* @compat_id: FPGA manager id for compatibility check.
* @mops: pointer to struct of fpga manager ops
+ * @mops_owner: module containing the mops
* @priv: low level driver private date
*/
struct fpga_manager {
@@ -210,6 +211,7 @@ struct fpga_manager {
enum fpga_mgr_states state;
struct fpga_compat_id *compat_id;
const struct fpga_manager_ops *mops;
+ struct module *mops_owner;
void *priv;
};
@@ -230,18 +232,30 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
void fpga_mgr_put(struct fpga_manager *mgr);
+#define fpga_mgr_register_full(parent, info) \
+ __fpga_mgr_register_full(parent, info, THIS_MODULE)
struct fpga_manager *
-fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info);
+__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
+ struct module *owner);
+#define fpga_mgr_register(parent, name, mops, priv) \
+ __fpga_mgr_register(parent, name, mops, priv, THIS_MODULE)
struct fpga_manager *
-fpga_mgr_register(struct device *parent, const char *name,
- const struct fpga_manager_ops *mops, void *priv);
+__fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv, struct module *owner);
+
void fpga_mgr_unregister(struct fpga_manager *mgr);
+#define devm_fpga_mgr_register_full(parent, info) \
+ __devm_fpga_mgr_register_full(parent, info, THIS_MODULE)
struct fpga_manager *
-devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info);
+__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
+ struct module *owner);
+#define devm_fpga_mgr_register(parent, name, mops, priv) \
+ __devm_fpga_mgr_register(parent, name, mops, priv, THIS_MODULE)
struct fpga_manager *
-devm_fpga_mgr_register(struct device *parent, const char *name,
- const struct fpga_manager_ops *mops, void *priv);
+__devm_fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv,
+ struct module *owner);
#endif /*_LINUX_FPGA_MGR_H */
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index 9d4d32909340..5fbc05fe70a6 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -36,6 +36,7 @@ struct fpga_region_info {
* @mgr: FPGA manager
* @info: FPGA image info
* @compat_id: FPGA region id for compatibility check.
+ * @ops_owner: module containing the get_bridges function
* @priv: private data
* @get_bridges: optional function to get bridges to a list
*/
@@ -46,6 +47,7 @@ struct fpga_region {
struct fpga_manager *mgr;
struct fpga_image_info *info;
struct fpga_compat_id *compat_id;
+ struct module *ops_owner;
void *priv;
int (*get_bridges)(struct fpga_region *region);
};
@@ -58,12 +60,17 @@ fpga_region_class_find(struct device *start, const void *data,
int fpga_region_program_fpga(struct fpga_region *region);
+#define fpga_region_register_full(parent, info) \
+ __fpga_region_register_full(parent, info, THIS_MODULE)
struct fpga_region *
-fpga_region_register_full(struct device *parent, const struct fpga_region_info *info);
+__fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
+ struct module *owner);
+#define fpga_region_register(parent, mgr, get_bridges) \
+ __fpga_region_register(parent, mgr, get_bridges, THIS_MODULE)
struct fpga_region *
-fpga_region_register(struct device *parent, struct fpga_manager *mgr,
- int (*get_bridges)(struct fpga_region *));
+__fpga_region_register(struct device *parent, struct fpga_manager *mgr,
+ int (*get_bridges)(struct fpga_region *), struct module *owner);
void fpga_region_unregister(struct fpga_region *region);
#endif /* _FPGA_REGION_H */
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 96ceb4095425..5e39baa7f6cb 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -820,6 +820,8 @@ struct vmbus_requestor {
#define VMBUS_RQST_RESET (U64_MAX - 3)
struct vmbus_device {
+ /* preferred ring buffer size in KB, 0 means no preferred size for this device */
+ size_t pref_ring_size;
u16 dev_type;
guid_t guid;
bool perf_device;
diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h
index 719cf9cc6e1a..383614ebd760 100644
--- a/include/linux/iio/adc/ad_sigma_delta.h
+++ b/include/linux/iio/adc/ad_sigma_delta.h
@@ -48,6 +48,7 @@ struct iio_dev;
* be used.
* @irq_flags: flags for the interrupt used by the triggered buffer
* @num_slots: Number of sequencer slots
+ * @irq_line: IRQ for reading conversions. If 0, spi->irq will be used
*/
struct ad_sigma_delta_info {
int (*set_channel)(struct ad_sigma_delta *, unsigned int channel);
@@ -62,6 +63,7 @@ struct ad_sigma_delta_info {
unsigned int data_reg;
unsigned long irq_flags;
unsigned int num_slots;
+ int irq_line;
};
/**
@@ -89,6 +91,7 @@ struct ad_sigma_delta {
unsigned int active_slots;
unsigned int current_slot;
unsigned int num_slots;
+ int irq_line;
bool status_appended;
/* map slots to channels in order to know what to expect from devices */
unsigned int *slots;
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index a6d79381866e..8099759d7242 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
+struct iio_chan_spec;
struct fwnode_handle;
struct iio_backend;
struct device;
@@ -15,12 +16,32 @@ enum iio_backend_data_type {
IIO_BACKEND_DATA_TYPE_MAX
};
+enum iio_backend_data_source {
+ IIO_BACKEND_INTERNAL_CONTINUOS_WAVE,
+ IIO_BACKEND_EXTERNAL,
+ IIO_BACKEND_DATA_SOURCE_MAX
+};
+
+/**
+ * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
+ * @_name: Attribute name
+ * @_shared: Whether the attribute is shared between all channels
+ * @_what: Data private to the driver
+ */
+#define IIO_BACKEND_EX_INFO(_name, _shared, _what) { \
+ .name = (_name), \
+ .shared = (_shared), \
+ .read = iio_backend_ext_info_get, \
+ .write = iio_backend_ext_info_set, \
+ .private = (_what), \
+}
+
/**
* struct iio_backend_data_fmt - Backend data format
- * @type: Data type.
- * @sign_extend: Bool to tell if the data is sign extended.
- * @enable: Enable/Disable the data format module. If disabled,
- * not formatting will happen.
+ * @type: Data type.
+ * @sign_extend: Bool to tell if the data is sign extended.
+ * @enable: Enable/Disable the data format module. If disabled,
+ * not formatting will happen.
*/
struct iio_backend_data_fmt {
enum iio_backend_data_type type;
@@ -28,15 +49,38 @@ struct iio_backend_data_fmt {
bool enable;
};
+/* vendor specific from 32 */
+enum iio_backend_test_pattern {
+ IIO_BACKEND_NO_TEST_PATTERN,
+ /* modified prbs9 */
+ IIO_BACKEND_ADI_PRBS_9A = 32,
+ IIO_BACKEND_TEST_PATTERN_MAX
+};
+
+enum iio_backend_sample_trigger {
+ IIO_BACKEND_SAMPLE_TRIGGER_EDGE_FALLING,
+ IIO_BACKEND_SAMPLE_TRIGGER_EDGE_RISING,
+ IIO_BACKEND_SAMPLE_TRIGGER_MAX
+};
+
/**
* struct iio_backend_ops - operations structure for an iio_backend
- * @enable: Enable backend.
- * @disable: Disable backend.
- * @chan_enable: Enable one channel.
- * @chan_disable: Disable one channel.
- * @data_format_set: Configure the data format for a specific channel.
- * @request_buffer: Request an IIO buffer.
- * @free_buffer: Free an IIO buffer.
+ * @enable: Enable backend.
+ * @disable: Disable backend.
+ * @chan_enable: Enable one channel.
+ * @chan_disable: Disable one channel.
+ * @data_format_set: Configure the data format for a specific channel.
+ * @data_source_set: Configure the data source for a specific channel.
+ * @set_sample_rate: Configure the sampling rate for a specific channel.
+ * @test_pattern_set: Configure a test pattern.
+ * @chan_status: Get the channel status.
+ * @iodelay_set: Set digital I/O delay.
+ * @data_sample_trigger: Control when to sample data.
+ * @request_buffer: Request an IIO buffer.
+ * @free_buffer: Free an IIO buffer.
+ * @extend_chan_spec: Extend an IIO channel.
+ * @ext_info_set: Extended info setter.
+ * @ext_info_get: Extended info getter.
**/
struct iio_backend_ops {
int (*enable)(struct iio_backend *back);
@@ -45,10 +89,30 @@ struct iio_backend_ops {
int (*chan_disable)(struct iio_backend *back, unsigned int chan);
int (*data_format_set)(struct iio_backend *back, unsigned int chan,
const struct iio_backend_data_fmt *data);
+ int (*data_source_set)(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data);
+ int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate_hz);
+ int (*test_pattern_set)(struct iio_backend *back,
+ unsigned int chan,
+ enum iio_backend_test_pattern pattern);
+ int (*chan_status)(struct iio_backend *back, unsigned int chan,
+ bool *error);
+ int (*iodelay_set)(struct iio_backend *back, unsigned int chan,
+ unsigned int taps);
+ int (*data_sample_trigger)(struct iio_backend *back,
+ enum iio_backend_sample_trigger trigger);
struct iio_buffer *(*request_buffer)(struct iio_backend *back,
struct iio_dev *indio_dev);
void (*free_buffer)(struct iio_backend *back,
struct iio_buffer *buffer);
+ int (*extend_chan_spec)(struct iio_backend *back,
+ struct iio_chan_spec *chan);
+ int (*ext_info_set)(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len);
+ int (*ext_info_get)(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf);
};
int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan);
@@ -56,10 +120,31 @@ int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan);
int devm_iio_backend_enable(struct device *dev, struct iio_backend *back);
int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
const struct iio_backend_data_fmt *data);
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data);
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate_hz);
+int iio_backend_test_pattern_set(struct iio_backend *back,
+ unsigned int chan,
+ enum iio_backend_test_pattern pattern);
+int iio_backend_chan_status(struct iio_backend *back, unsigned int chan,
+ bool *error);
+int iio_backend_iodelay_set(struct iio_backend *back, unsigned int lane,
+ unsigned int taps);
+int iio_backend_data_sample_trigger(struct iio_backend *back,
+ enum iio_backend_sample_trigger trigger);
int devm_iio_backend_request_buffer(struct device *dev,
struct iio_backend *back,
struct iio_dev *indio_dev);
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len);
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf);
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+ struct iio_backend *back,
+ struct iio_chan_spec *chan);
void *iio_backend_get_priv(const struct iio_backend *conv);
struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name);
struct iio_backend *
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
index 18d3702fa95d..6e27e47077d5 100644
--- a/include/linux/iio/buffer-dma.h
+++ b/include/linux/iio/buffer-dma.h
@@ -132,7 +132,9 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer,
struct iio_dev *indio_dev);
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
char __user *user_buffer);
-size_t iio_dma_buffer_data_available(struct iio_buffer *buffer);
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+ const char __user *user_buffer);
+size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);
int iio_dma_buffer_request_update(struct iio_buffer *buffer);
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
index cbb8ba957fad..81d9a19aeb91 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -7,14 +7,28 @@
#ifndef __IIO_DMAENGINE_H__
#define __IIO_DMAENGINE_H__
+#include <linux/iio/buffer.h>
+
struct iio_dev;
struct device;
-struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
- const char *channel);
void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
-int devm_iio_dmaengine_buffer_setup(struct device *dev,
- struct iio_dev *indio_dev,
- const char *channel);
+struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir);
+
+#define iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
+ iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
+ IIO_BUFFER_DIRECTION_IN)
+
+int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir);
+
+#define devm_iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
+ devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
+ IIO_BUFFER_DIRECTION_IN)
#endif
diff --git a/include/linux/iio/common/inv_sensors_timestamp.h b/include/linux/iio/common/inv_sensors_timestamp.h
index a47d304d1ba7..8d506f1e9df2 100644
--- a/include/linux/iio/common/inv_sensors_timestamp.h
+++ b/include/linux/iio/common/inv_sensors_timestamp.h
@@ -71,8 +71,7 @@ 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);
+ size_t sample_nb, int64_t timestamp);
static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts)
{
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index e370a7bb3300..55e2b22086a1 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -788,6 +788,19 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev)
}
#endif
+#ifdef CONFIG_ACPI
+bool iio_read_acpi_mount_matrix(struct device *dev,
+ struct iio_mount_matrix *orientation,
+ char *acpi_method);
+#else
+static inline bool iio_read_acpi_mount_matrix(struct device *dev,
+ struct iio_mount_matrix *orientation,
+ char *acpi_method)
+{
+ return false;
+}
+#endif
+
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
index ca35af30745f..9eb17481b07f 100644
--- a/include/linux/mfd/stm32-timers.h
+++ b/include/linux/mfd/stm32-timers.h
@@ -41,6 +41,11 @@
#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */
#define TIM_DIER_UIE BIT(0) /* Update interrupt */
+#define TIM_DIER_CC1IE BIT(1) /* CC1 Interrupt Enable */
+#define TIM_DIER_CC2IE BIT(2) /* CC2 Interrupt Enable */
+#define TIM_DIER_CC3IE BIT(3) /* CC3 Interrupt Enable */
+#define TIM_DIER_CC4IE BIT(4) /* CC4 Interrupt Enable */
+#define TIM_DIER_CC_IE(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt enable */
#define TIM_DIER_UDE BIT(8) /* Update DMA request Enable */
#define TIM_DIER_CC1DE BIT(9) /* CC1 DMA request Enable */
#define TIM_DIER_CC2DE BIT(10) /* CC2 DMA request Enable */
@@ -49,6 +54,7 @@
#define TIM_DIER_COMDE BIT(13) /* COM DMA request Enable */
#define TIM_DIER_TDE BIT(14) /* Trigger DMA request Enable */
#define TIM_SR_UIF BIT(0) /* Update interrupt flag */
+#define TIM_SR_CC_IF(x) BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt flag */
#define TIM_EGR_UG BIT(0) /* Update Generation */
#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */
#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */
@@ -60,16 +66,23 @@
#define TIM_CCMR_CC1S_TI2 BIT(1) /* IC1/IC3 selects TI2/TI4 */
#define TIM_CCMR_CC2S_TI2 BIT(8) /* IC2/IC4 selects TI2/TI4 */
#define TIM_CCMR_CC2S_TI1 BIT(9) /* IC2/IC4 selects TI1/TI3 */
+#define TIM_CCMR_CC3S (BIT(0) | BIT(1)) /* Capture/compare 3 sel */
+#define TIM_CCMR_CC4S (BIT(8) | BIT(9)) /* Capture/compare 4 sel */
+#define TIM_CCMR_CC3S_TI3 BIT(0) /* IC3 selects TI3 */
+#define TIM_CCMR_CC4S_TI4 BIT(8) /* IC4 selects TI4 */
#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */
#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */
#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */
#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */
#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */
#define TIM_CCER_CC2P BIT(5) /* Capt/Comp 2 Polarity */
+#define TIM_CCER_CC2NP BIT(7) /* Capt/Comp 2N Polarity */
#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */
#define TIM_CCER_CC3P BIT(9) /* Capt/Comp 3 Polarity */
+#define TIM_CCER_CC3NP BIT(11) /* Capt/Comp 3N Polarity */
#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */
#define TIM_CCER_CC4P BIT(13) /* Capt/Comp 4 Polarity */
+#define TIM_CCER_CC4NP BIT(15) /* Capt/Comp 4N Polarity */
#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12))
#define TIM_BDTR_BKE(x) BIT(12 + (x) * 12) /* Break input enable */
#define TIM_BDTR_BKP(x) BIT(13 + (x) * 12) /* Break input polarity */
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index cde01e133a1b..b573f15762f8 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -353,6 +353,7 @@ struct mhi_controller_config {
* @read_reg: Read a MHI register via the physical link (required)
* @write_reg: Write a MHI register via the physical link (required)
* @reset: Controller specific reset function (optional)
+ * @edl_trigger: CB function to trigger EDL mode (optional)
* @buffer_len: Bounce buffer length
* @index: Index of the MHI controller instance
* @bounce_buf: Use of bounce buffer
@@ -435,6 +436,7 @@ struct mhi_controller {
void (*write_reg)(struct mhi_controller *mhi_cntrl, void __iomem *addr,
u32 val);
void (*reset)(struct mhi_controller *mhi_cntrl);
+ int (*edl_trigger)(struct mhi_controller *mhi_cntrl);
size_t buffer_len;
int index;
@@ -814,4 +816,13 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
*/
bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
+/**
+ * mhi_get_channel_doorbell_offset - Get the channel doorbell offset
+ * @mhi_cntrl: MHI controller
+ * @chdb_offset: Read channel doorbell offset
+ *
+ * Return: 0 if the read succeeds, a negative error code otherwise
+ */
+int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset);
+
#endif /* _MHI_H_ */
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
index f0ba0e03218f..3ebeaa0ded00 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -199,7 +199,10 @@ int nvmem_add_one_cell(struct nvmem_device *nvmem,
int nvmem_layout_register(struct nvmem_layout *layout);
void nvmem_layout_unregister(struct nvmem_layout *layout);
-int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
+#define nvmem_layout_driver_register(drv) \
+ __nvmem_layout_driver_register(drv, THIS_MODULE)
+int __nvmem_layout_driver_register(struct nvmem_layout_driver *drv,
+ struct module *owner);
void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
#define module_nvmem_layout_driver(__nvmem_layout_driver) \
module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
diff --git a/include/linux/platform_data/uio_pruss.h b/include/linux/platform_data/uio_pruss.h
deleted file mode 100644
index f76fa393b802..000000000000
--- a/include/linux/platform_data/uio_pruss.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * include/linux/platform_data/uio_pruss.h
- *
- * Platform data for uio_pruss driver
- *
- * Copyright (C) 2010-11 Texas Instruments Incorporated - https://www.ti.com/
- */
-
-#ifndef _UIO_PRUSS_H_
-#define _UIO_PRUSS_H_
-
-/* To configure the PRUSS INTC base offset for UIO driver */
-struct uio_pruss_pdata {
- u32 pintc_base;
- struct gen_pool *sram_pool;
-};
-#endif /* _UIO_PRUSS_H_ */
diff --git a/include/linux/property.h b/include/linux/property.h
index 3a1045eb786c..61fc20e5f81f 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -13,6 +13,7 @@
#include <linux/args.h>
#include <linux/array_size.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/fwnode.h>
#include <linux/stddef.h>
#include <linux/types.h>
@@ -174,13 +175,32 @@ struct fwnode_handle *device_get_next_child_node(const struct device *dev,
for (child = device_get_next_child_node(dev, NULL); child; \
child = device_get_next_child_node(dev, child))
+#define device_for_each_child_node_scoped(dev, child) \
+ for (struct fwnode_handle *child __free(fwnode_handle) = \
+ device_get_next_child_node(dev, NULL); \
+ child; child = device_get_next_child_node(dev, child))
+
struct fwnode_handle *fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
const char *childname);
struct fwnode_handle *device_get_named_child_node(const struct device *dev,
const char *childname);
struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode);
-void fwnode_handle_put(struct fwnode_handle *fwnode);
+
+/**
+ * fwnode_handle_put - Drop reference to a device node
+ * @fwnode: Pointer to the device node to drop the reference to.
+ *
+ * This has to be used when terminating device_for_each_child_node() iteration
+ * with break or return to prevent stale device node references from being left
+ * behind.
+ */
+static inline void fwnode_handle_put(struct fwnode_handle *fwnode)
+{
+ fwnode_call_void_op(fwnode, put);
+}
+
+DEFINE_FREE(fwnode_handle, struct fwnode_handle *, fwnode_handle_put(_T))
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index);
int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name);
diff --git a/include/linux/stm.h b/include/linux/stm.h
index 3b22689512be..2fcbef9608f6 100644
--- a/include/linux/stm.h
+++ b/include/linux/stm.h
@@ -30,6 +30,16 @@ enum stp_packet_flags {
STP_PACKET_TIMESTAMPED = 0x2,
};
+/**
+ * enum stm_source_type - STM source driver
+ * @STM_USER: any STM trace source
+ * @STM_FTRACE: ftrace STM source
+ */
+enum stm_source_type {
+ STM_USER,
+ STM_FTRACE,
+};
+
struct stp_policy;
struct stm_device;
@@ -106,6 +116,7 @@ struct stm_source_device;
* @name: device name, will be used for policy lookup
* @src: internal structure, only used by stm class code
* @nr_chans: number of channels to allocate
+ * @type: type of STM source driver represented by stm_source_type
* @link: called when this source gets linked to an STM device
* @unlink: called when this source is about to get unlinked from its STM
*
@@ -117,6 +128,7 @@ struct stm_source_data {
struct stm_source_device *src;
unsigned int percpu;
unsigned int nr_chans;
+ unsigned int type;
int (*link)(struct stm_source_data *data);
void (*unlink)(struct stm_source_data *data);
};
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
new file mode 100644
index 000000000000..dcfa38fdc93c
--- /dev/null
+++ b/include/uapi/linux/ntsync.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Kernel support for NT synchronization primitive emulation
+ *
+ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
+ */
+
+#ifndef __LINUX_NTSYNC_H
+#define __LINUX_NTSYNC_H
+
+#include <linux/types.h>
+
+struct ntsync_sem_args {
+ __u32 sem;
+ __u32 count;
+ __u32 max;
+};
+
+#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
+
+#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
+
+#endif
diff --git a/include/uapi/misc/pvpanic.h b/include/uapi/misc/pvpanic.h
index 54b7485390d3..3f1745cd1b52 100644
--- a/include/uapi/misc/pvpanic.h
+++ b/include/uapi/misc/pvpanic.h
@@ -3,7 +3,10 @@
#ifndef __PVPANIC_H__
#define __PVPANIC_H__
-#define PVPANIC_PANICKED (1 << 0)
-#define PVPANIC_CRASH_LOADED (1 << 1)
+#include <linux/const.h>
+
+#define PVPANIC_PANICKED _BITUL(0)
+#define PVPANIC_CRASH_LOADED _BITUL(1)
+#define PVPANIC_SHUTDOWN _BITUL(2)
#endif /* __PVPANIC_H__ */
diff --git a/samples/acrn/vm-sample.c b/samples/acrn/vm-sample.c
index 704402c64ea3..c61e0f91456e 100644
--- a/samples/acrn/vm-sample.c
+++ b/samples/acrn/vm-sample.c
@@ -3,7 +3,7 @@
* A sample program to run a User VM on the ACRN hypervisor
*
* This sample runs in a Service VM, which is a privileged VM of ACRN.
- * CONFIG_ACRN_HSM need to be enabled in the Service VM.
+ * CONFIG_ACRN_HSM needs to be enabled in the Service VM.
*
* Guest VM code in guest16.s will be executed after the VM launched.
*
@@ -55,7 +55,7 @@ int main(int argc, char **argv)
ret = posix_memalign(&guest_memory, 4096, GUEST_MEMORY_SIZE);
if (ret < 0) {
- printf("No enough memory!\n");
+ printf("Not enough memory!\n");
return -1;
}
hsm_fd = open("/dev/acrn_hsm", O_RDWR|O_CLOEXEC);
diff --git a/scripts/spdxcheck.py b/scripts/spdxcheck.py
index 18cb9f5b3d3d..8b8fb115fc81 100755
--- a/scripts/spdxcheck.py
+++ b/scripts/spdxcheck.py
@@ -412,6 +412,9 @@ if __name__ == '__main__':
if parser.checked:
pc = int(100 * parser.spdx_valid / parser.checked)
sys.stderr.write('Files with SPDX: %12d %3d%%\n' %(parser.spdx_valid, pc))
+ missing = parser.checked - parser.spdx_valid
+ mpc = int(100 * missing / parser.checked)
+ sys.stderr.write('Files without SPDX:%12d %3d%%\n' %(missing, mpc))
sys.stderr.write('Files with errors: %12d\n' %parser.spdx_errors)
if ndirs:
sys.stderr.write('\n')
diff --git a/tools/hv/Build b/tools/hv/Build
index 6cf51fa4b306..7d1f1698069b 100644
--- a/tools/hv/Build
+++ b/tools/hv/Build
@@ -1,3 +1,4 @@
hv_kvp_daemon-y += hv_kvp_daemon.o
hv_vss_daemon-y += hv_vss_daemon.o
-hv_fcopy_daemon-y += hv_fcopy_daemon.o
+hv_fcopy_uio_daemon-y += hv_fcopy_uio_daemon.o
+hv_fcopy_uio_daemon-y += vmbus_bufring.o
diff --git a/tools/hv/Makefile b/tools/hv/Makefile
index fe770e679ae8..bb52871da341 100644
--- a/tools/hv/Makefile
+++ b/tools/hv/Makefile
@@ -2,6 +2,7 @@
# Makefile for Hyper-V tools
include ../scripts/Makefile.include
+ARCH := $(shell uname -m 2>/dev/null)
sbindir ?= /usr/sbin
libexecdir ?= /usr/libexec
sharedstatedir ?= /var/lib
@@ -17,7 +18,10 @@ MAKEFLAGS += -r
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
-ALL_TARGETS := hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
+ALL_TARGETS := hv_kvp_daemon hv_vss_daemon
+ifneq ($(ARCH), aarch64)
+ALL_TARGETS += hv_fcopy_uio_daemon
+endif
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
ALL_SCRIPTS := hv_get_dhcp_info.sh hv_get_dns_info.sh hv_set_ifconfig.sh
@@ -39,10 +43,10 @@ $(HV_VSS_DAEMON_IN): FORCE
$(OUTPUT)hv_vss_daemon: $(HV_VSS_DAEMON_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
-HV_FCOPY_DAEMON_IN := $(OUTPUT)hv_fcopy_daemon-in.o
-$(HV_FCOPY_DAEMON_IN): FORCE
- $(Q)$(MAKE) $(build)=hv_fcopy_daemon
-$(OUTPUT)hv_fcopy_daemon: $(HV_FCOPY_DAEMON_IN)
+HV_FCOPY_UIO_DAEMON_IN := $(OUTPUT)hv_fcopy_uio_daemon-in.o
+$(HV_FCOPY_UIO_DAEMON_IN): FORCE
+ $(Q)$(MAKE) $(build)=hv_fcopy_uio_daemon
+$(OUTPUT)hv_fcopy_uio_daemon: $(HV_FCOPY_UIO_DAEMON_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
clean:
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c
deleted file mode 100644
index 16d629b22c25..000000000000
--- a/tools/hv/hv_fcopy_daemon.c
+++ /dev/null
@@ -1,266 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * An implementation of host to guest copy functionality for Linux.
- *
- * Copyright (C) 2014, Microsoft, Inc.
- *
- * Author : K. Y. Srinivasan <kys@microsoft.com>
- */
-
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <linux/hyperv.h>
-#include <linux/limits.h>
-#include <syslog.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <getopt.h>
-
-static int target_fd;
-static char target_fname[PATH_MAX];
-static unsigned long long filesize;
-
-static int hv_start_fcopy(struct hv_start_fcopy *smsg)
-{
- int error = HV_E_FAIL;
- char *q, *p;
-
- filesize = 0;
- p = (char *)smsg->path_name;
- snprintf(target_fname, sizeof(target_fname), "%s/%s",
- (char *)smsg->path_name, (char *)smsg->file_name);
-
- syslog(LOG_INFO, "Target file name: %s", target_fname);
- /*
- * Check to see if the path is already in place; if not,
- * create if required.
- */
- while ((q = strchr(p, '/')) != NULL) {
- if (q == p) {
- p++;
- continue;
- }
- *q = '\0';
- if (access((char *)smsg->path_name, F_OK)) {
- if (smsg->copy_flags & CREATE_PATH) {
- if (mkdir((char *)smsg->path_name, 0755)) {
- syslog(LOG_ERR, "Failed to create %s",
- (char *)smsg->path_name);
- goto done;
- }
- } else {
- syslog(LOG_ERR, "Invalid path: %s",
- (char *)smsg->path_name);
- goto done;
- }
- }
- p = q + 1;
- *q = '/';
- }
-
- if (!access(target_fname, F_OK)) {
- syslog(LOG_INFO, "File: %s exists", target_fname);
- if (!(smsg->copy_flags & OVER_WRITE)) {
- error = HV_ERROR_ALREADY_EXISTS;
- goto done;
- }
- }
-
- target_fd = open(target_fname,
- O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
- if (target_fd == -1) {
- syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
- goto done;
- }
-
- error = 0;
-done:
- if (error)
- target_fname[0] = '\0';
- return error;
-}
-
-static int hv_copy_data(struct hv_do_fcopy *cpmsg)
-{
- ssize_t bytes_written;
- int ret = 0;
-
- bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
- cpmsg->offset);
-
- filesize += cpmsg->size;
- if (bytes_written != cpmsg->size) {
- switch (errno) {
- case ENOSPC:
- ret = HV_ERROR_DISK_FULL;
- break;
- default:
- ret = HV_E_FAIL;
- break;
- }
- syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
- filesize, (long)bytes_written, strerror(errno));
- }
-
- return ret;
-}
-
-/*
- * Reset target_fname to "" in the two below functions for hibernation: if
- * the fcopy operation is aborted by hibernation, the daemon should remove the
- * partially-copied file; to achieve this, the hv_utils driver always fakes a
- * CANCEL_FCOPY message upon suspend, and later when the VM resumes back,
- * the daemon calls hv_copy_cancel() to remove the file; if a file is copied
- * successfully before suspend, hv_copy_finished() must reset target_fname to
- * avoid that the file can be incorrectly removed upon resume, since the faked
- * CANCEL_FCOPY message is spurious in this case.
- */
-static int hv_copy_finished(void)
-{
- close(target_fd);
- target_fname[0] = '\0';
- return 0;
-}
-static int hv_copy_cancel(void)
-{
- close(target_fd);
- if (strlen(target_fname) > 0) {
- unlink(target_fname);
- target_fname[0] = '\0';
- }
- return 0;
-
-}
-
-void print_usage(char *argv[])
-{
- fprintf(stderr, "Usage: %s [options]\n"
- "Options are:\n"
- " -n, --no-daemon stay in foreground, don't daemonize\n"
- " -h, --help print this help\n", argv[0]);
-}
-
-int main(int argc, char *argv[])
-{
- int fcopy_fd = -1;
- int error;
- int daemonize = 1, long_index = 0, opt;
- int version = FCOPY_CURRENT_VERSION;
- union {
- struct hv_fcopy_hdr hdr;
- struct hv_start_fcopy start;
- struct hv_do_fcopy copy;
- __u32 kernel_modver;
- } buffer = { };
- int in_handshake;
-
- static struct option long_options[] = {
- {"help", no_argument, 0, 'h' },
- {"no-daemon", no_argument, 0, 'n' },
- {0, 0, 0, 0 }
- };
-
- while ((opt = getopt_long(argc, argv, "hn", long_options,
- &long_index)) != -1) {
- switch (opt) {
- case 'n':
- daemonize = 0;
- break;
- case 'h':
- default:
- print_usage(argv);
- exit(EXIT_FAILURE);
- }
- }
-
- if (daemonize && daemon(1, 0)) {
- syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- openlog("HV_FCOPY", 0, LOG_USER);
- syslog(LOG_INFO, "starting; pid is:%d", getpid());
-
-reopen_fcopy_fd:
- if (fcopy_fd != -1)
- close(fcopy_fd);
- /* Remove any possible partially-copied file on error */
- hv_copy_cancel();
- in_handshake = 1;
- fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
-
- if (fcopy_fd < 0) {
- syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s",
- errno, strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- /*
- * Register with the kernel.
- */
- if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
- syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- while (1) {
- /*
- * In this loop we process fcopy messages after the
- * handshake is complete.
- */
- ssize_t len;
-
- len = pread(fcopy_fd, &buffer, sizeof(buffer), 0);
- if (len < 0) {
- syslog(LOG_ERR, "pread failed: %s", strerror(errno));
- goto reopen_fcopy_fd;
- }
-
- if (in_handshake) {
- if (len != sizeof(buffer.kernel_modver)) {
- syslog(LOG_ERR, "invalid version negotiation");
- exit(EXIT_FAILURE);
- }
- in_handshake = 0;
- syslog(LOG_INFO, "kernel module version: %u",
- buffer.kernel_modver);
- continue;
- }
-
- switch (buffer.hdr.operation) {
- case START_FILE_COPY:
- error = hv_start_fcopy(&buffer.start);
- break;
- case WRITE_TO_FILE:
- error = hv_copy_data(&buffer.copy);
- break;
- case COMPLETE_FCOPY:
- error = hv_copy_finished();
- break;
- case CANCEL_FCOPY:
- error = hv_copy_cancel();
- break;
-
- default:
- error = HV_E_FAIL;
- syslog(LOG_ERR, "Unknown operation: %d",
- buffer.hdr.operation);
-
- }
-
- /*
- * pwrite() may return an error due to the faked CANCEL_FCOPY
- * message upon hibernation. Ignore the error by resetting the
- * dev file, i.e. closing and re-opening it.
- */
- if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
- syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
- goto reopen_fcopy_fd;
- }
- }
-}
diff --git a/tools/hv/hv_fcopy_uio_daemon.c b/tools/hv/hv_fcopy_uio_daemon.c
new file mode 100644
index 000000000000..3ce316cc9f97
--- /dev/null
+++ b/tools/hv/hv_fcopy_uio_daemon.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * An implementation of host to guest copy functionality for Linux.
+ *
+ * Copyright (C) 2023, Microsoft, Inc.
+ *
+ * Author : K. Y. Srinivasan <kys@microsoft.com>
+ * Author : Saurabh Sengar <ssengar@microsoft.com>
+ *
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <sys/stat.h>
+#include <linux/hyperv.h>
+#include <linux/limits.h>
+#include "vmbus_bufring.h"
+
+#define ICMSGTYPE_NEGOTIATE 0
+#define ICMSGTYPE_FCOPY 7
+
+#define WIN8_SRV_MAJOR 1
+#define WIN8_SRV_MINOR 1
+#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
+
+#define MAX_FOLDER_NAME 15
+#define MAX_PATH_LEN 15
+#define FCOPY_UIO "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio"
+
+#define FCOPY_VER_COUNT 1
+static const int fcopy_versions[] = {
+ WIN8_SRV_VERSION
+};
+
+#define FW_VER_COUNT 1
+static const int fw_versions[] = {
+ UTIL_FW_VERSION
+};
+
+#define HV_RING_SIZE 0x4000 /* 16KB ring buffer size */
+
+unsigned char desc[HV_RING_SIZE];
+
+static int target_fd;
+static char target_fname[PATH_MAX];
+static unsigned long long filesize;
+
+static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags)
+{
+ int error = HV_E_FAIL;
+ char *q, *p;
+
+ filesize = 0;
+ p = path_name;
+ snprintf(target_fname, sizeof(target_fname), "%s/%s",
+ path_name, file_name);
+
+ /*
+ * Check to see if the path is already in place; if not,
+ * create if required.
+ */
+ while ((q = strchr(p, '/')) != NULL) {
+ if (q == p) {
+ p++;
+ continue;
+ }
+ *q = '\0';
+ if (access(path_name, F_OK)) {
+ if (flags & CREATE_PATH) {
+ if (mkdir(path_name, 0755)) {
+ syslog(LOG_ERR, "Failed to create %s",
+ path_name);
+ goto done;
+ }
+ } else {
+ syslog(LOG_ERR, "Invalid path: %s", path_name);
+ goto done;
+ }
+ }
+ p = q + 1;
+ *q = '/';
+ }
+
+ if (!access(target_fname, F_OK)) {
+ syslog(LOG_INFO, "File: %s exists", target_fname);
+ if (!(flags & OVER_WRITE)) {
+ error = HV_ERROR_ALREADY_EXISTS;
+ goto done;
+ }
+ }
+
+ target_fd = open(target_fname,
+ O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
+ if (target_fd == -1) {
+ syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
+ goto done;
+ }
+
+ error = 0;
+done:
+ if (error)
+ target_fname[0] = '\0';
+ return error;
+}
+
+/* copy the data into the file */
+static int hv_copy_data(struct hv_do_fcopy *cpmsg)
+{
+ ssize_t len;
+ int ret = 0;
+
+ len = pwrite(target_fd, cpmsg->data, cpmsg->size, cpmsg->offset);
+
+ filesize += cpmsg->size;
+ if (len != cpmsg->size) {
+ switch (errno) {
+ case ENOSPC:
+ ret = HV_ERROR_DISK_FULL;
+ break;
+ default:
+ ret = HV_E_FAIL;
+ break;
+ }
+ syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
+ filesize, (long)len, strerror(errno));
+ }
+
+ return ret;
+}
+
+static int hv_copy_finished(void)
+{
+ close(target_fd);
+ target_fname[0] = '\0';
+
+ return 0;
+}
+
+static void print_usage(char *argv[])
+{
+ fprintf(stderr, "Usage: %s [options]\n"
+ "Options are:\n"
+ " -n, --no-daemon stay in foreground, don't daemonize\n"
+ " -h, --help print this help\n", argv[0]);
+}
+
+static bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, unsigned char *buf,
+ unsigned int buflen, const int *fw_version, int fw_vercnt,
+ const int *srv_version, int srv_vercnt,
+ int *nego_fw_version, int *nego_srv_version)
+{
+ int icframe_major, icframe_minor;
+ int icmsg_major, icmsg_minor;
+ int fw_major, fw_minor;
+ int srv_major, srv_minor;
+ int i, j;
+ bool found_match = false;
+ struct icmsg_negotiate *negop;
+
+ /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */
+ if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) {
+ syslog(LOG_ERR, "Invalid icmsg negotiate");
+ return false;
+ }
+
+ icmsghdrp->icmsgsize = 0x10;
+ negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR];
+
+ icframe_major = negop->icframe_vercnt;
+ icframe_minor = 0;
+
+ icmsg_major = negop->icmsg_vercnt;
+ icmsg_minor = 0;
+
+ /* Validate negop packet */
+ if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
+ icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
+ ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) {
+ syslog(LOG_ERR, "Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n",
+ icframe_major, icmsg_major);
+ goto fw_error;
+ }
+
+ /*
+ * Select the framework version number we will
+ * support.
+ */
+
+ for (i = 0; i < fw_vercnt; i++) {
+ fw_major = (fw_version[i] >> 16);
+ fw_minor = (fw_version[i] & 0xFFFF);
+
+ for (j = 0; j < negop->icframe_vercnt; j++) {
+ if (negop->icversion_data[j].major == fw_major &&
+ negop->icversion_data[j].minor == fw_minor) {
+ icframe_major = negop->icversion_data[j].major;
+ icframe_minor = negop->icversion_data[j].minor;
+ found_match = true;
+ break;
+ }
+ }
+
+ if (found_match)
+ break;
+ }
+
+ if (!found_match)
+ goto fw_error;
+
+ found_match = false;
+
+ for (i = 0; i < srv_vercnt; i++) {
+ srv_major = (srv_version[i] >> 16);
+ srv_minor = (srv_version[i] & 0xFFFF);
+
+ for (j = negop->icframe_vercnt;
+ (j < negop->icframe_vercnt + negop->icmsg_vercnt);
+ j++) {
+ if (negop->icversion_data[j].major == srv_major &&
+ negop->icversion_data[j].minor == srv_minor) {
+ icmsg_major = negop->icversion_data[j].major;
+ icmsg_minor = negop->icversion_data[j].minor;
+ found_match = true;
+ break;
+ }
+ }
+
+ if (found_match)
+ break;
+ }
+
+ /*
+ * Respond with the framework and service
+ * version numbers we can support.
+ */
+fw_error:
+ if (!found_match) {
+ negop->icframe_vercnt = 0;
+ negop->icmsg_vercnt = 0;
+ } else {
+ negop->icframe_vercnt = 1;
+ negop->icmsg_vercnt = 1;
+ }
+
+ if (nego_fw_version)
+ *nego_fw_version = (icframe_major << 16) | icframe_minor;
+
+ if (nego_srv_version)
+ *nego_srv_version = (icmsg_major << 16) | icmsg_minor;
+
+ negop->icversion_data[0].major = icframe_major;
+ negop->icversion_data[0].minor = icframe_minor;
+ negop->icversion_data[1].major = icmsg_major;
+ negop->icversion_data[1].minor = icmsg_minor;
+
+ return found_match;
+}
+
+static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size)
+{
+ size_t len = 0;
+
+ while (len < dest_size) {
+ if (src[len] < 0x80)
+ dest[len++] = (char)(*src++);
+ else
+ dest[len++] = 'X';
+ }
+
+ dest[len] = '\0';
+}
+
+static int hv_fcopy_start(struct hv_start_fcopy *smsg_in)
+{
+ setlocale(LC_ALL, "en_US.utf8");
+ size_t file_size, path_size;
+ char *file_name, *path_name;
+ char *in_file_name = (char *)smsg_in->file_name;
+ char *in_path_name = (char *)smsg_in->path_name;
+
+ file_size = wcstombs(NULL, (const wchar_t *restrict)in_file_name, 0) + 1;
+ path_size = wcstombs(NULL, (const wchar_t *restrict)in_path_name, 0) + 1;
+
+ file_name = (char *)malloc(file_size * sizeof(char));
+ path_name = (char *)malloc(path_size * sizeof(char));
+
+ wcstoutf8(file_name, (__u16 *)in_file_name, file_size);
+ wcstoutf8(path_name, (__u16 *)in_path_name, path_size);
+
+ return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags);
+}
+
+static int hv_fcopy_send_data(struct hv_fcopy_hdr *fcopy_msg, int recvlen)
+{
+ int operation = fcopy_msg->operation;
+
+ /*
+ * The strings sent from the host are encoded in
+ * utf16; convert it to utf8 strings.
+ * The host assures us that the utf16 strings will not exceed
+ * the max lengths specified. We will however, reserve room
+ * for the string terminating character - in the utf16s_utf8s()
+ * function we limit the size of the buffer where the converted
+ * string is placed to W_MAX_PATH -1 to guarantee
+ * that the strings can be properly terminated!
+ */
+
+ switch (operation) {
+ case START_FILE_COPY:
+ return hv_fcopy_start((struct hv_start_fcopy *)fcopy_msg);
+ case WRITE_TO_FILE:
+ return hv_copy_data((struct hv_do_fcopy *)fcopy_msg);
+ case COMPLETE_FCOPY:
+ return hv_copy_finished();
+ }
+
+ return HV_E_FAIL;
+}
+
+/* process the packet recv from host */
+static int fcopy_pkt_process(struct vmbus_br *txbr)
+{
+ int ret, offset, pktlen;
+ int fcopy_srv_version;
+ const struct vmbus_chanpkt_hdr *pkt;
+ struct hv_fcopy_hdr *fcopy_msg;
+ struct icmsg_hdr *icmsghdr;
+
+ pkt = (const struct vmbus_chanpkt_hdr *)desc;
+ offset = pkt->hlen << 3;
+ pktlen = (pkt->tlen << 3) - offset;
+ icmsghdr = (struct icmsg_hdr *)&desc[offset + sizeof(struct vmbuspipe_hdr)];
+ icmsghdr->status = HV_E_FAIL;
+
+ if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+ if (vmbus_prep_negotiate_resp(icmsghdr, desc + offset, pktlen, fw_versions,
+ FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT,
+ NULL, &fcopy_srv_version)) {
+ syslog(LOG_INFO, "FCopy IC version %d.%d",
+ fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF);
+ icmsghdr->status = 0;
+ }
+ } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) {
+ /* Ensure recvlen is big enough to contain hv_fcopy_hdr */
+ if (pktlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) {
+ syslog(LOG_ERR, "Invalid Fcopy hdr. Packet length too small: %u",
+ pktlen);
+ return -ENOBUFS;
+ }
+
+ fcopy_msg = (struct hv_fcopy_hdr *)&desc[offset + ICMSG_HDR];
+ icmsghdr->status = hv_fcopy_send_data(fcopy_msg, pktlen);
+ }
+
+ icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
+ ret = rte_vmbus_chan_send(txbr, 0x6, desc + offset, pktlen, 0);
+ if (ret) {
+ syslog(LOG_ERR, "Write to ringbuffer failed err: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fcopy_get_first_folder(char *path, char *chan_no)
+{
+ DIR *dir = opendir(path);
+ struct dirent *entry;
+
+ if (!dir) {
+ syslog(LOG_ERR, "Failed to open directory (errno=%s).\n", strerror(errno));
+ return;
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 &&
+ strcmp(entry->d_name, "..") != 0) {
+ strcpy(chan_no, entry->d_name);
+ break;
+ }
+ }
+
+ closedir(dir);
+}
+
+int main(int argc, char *argv[])
+{
+ int fcopy_fd = -1, tmp = 1;
+ int daemonize = 1, long_index = 0, opt, ret = -EINVAL;
+ struct vmbus_br txbr, rxbr;
+ void *ring;
+ uint32_t len = HV_RING_SIZE;
+ char uio_name[MAX_FOLDER_NAME] = {0};
+ char uio_dev_path[MAX_PATH_LEN] = {0};
+
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h' },
+ {"no-daemon", no_argument, 0, 'n' },
+ {0, 0, 0, 0 }
+ };
+
+ while ((opt = getopt_long(argc, argv, "hn", long_options,
+ &long_index)) != -1) {
+ switch (opt) {
+ case 'n':
+ daemonize = 0;
+ break;
+ case 'h':
+ default:
+ print_usage(argv);
+ goto exit;
+ }
+ }
+
+ if (daemonize && daemon(1, 0)) {
+ syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
+ goto exit;
+ }
+
+ openlog("HV_UIO_FCOPY", 0, LOG_USER);
+ syslog(LOG_INFO, "starting; pid is:%d", getpid());
+
+ fcopy_get_first_folder(FCOPY_UIO, uio_name);
+ snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name);
+ fcopy_fd = open(uio_dev_path, O_RDWR);
+
+ if (fcopy_fd < 0) {
+ syslog(LOG_ERR, "open %s failed; error: %d %s",
+ uio_dev_path, errno, strerror(errno));
+ ret = fcopy_fd;
+ goto exit;
+ }
+
+ ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE);
+ if (!ring) {
+ ret = errno;
+ syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret));
+ goto close;
+ }
+ vmbus_br_setup(&txbr, ring, HV_RING_SIZE);
+ vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE);
+
+ rxbr.vbr->imask = 0;
+
+ while (1) {
+ /*
+ * In this loop we process fcopy messages after the
+ * handshake is complete.
+ */
+ ret = pread(fcopy_fd, &tmp, sizeof(int), 0);
+ if (ret < 0) {
+ syslog(LOG_ERR, "pread failed: %s", strerror(errno));
+ continue;
+ }
+
+ len = HV_RING_SIZE;
+ ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len);
+ if (unlikely(ret <= 0)) {
+ /* This indicates a failure to communicate (or worse) */
+ syslog(LOG_ERR, "VMBus channel recv error: %d", ret);
+ } else {
+ ret = fcopy_pkt_process(&txbr);
+ if (ret < 0)
+ goto close;
+
+ /* Signal host */
+ if ((write(fcopy_fd, &tmp, sizeof(int))) != sizeof(int)) {
+ ret = errno;
+ syslog(LOG_ERR, "Signal to host failed: %s\n", strerror(ret));
+ goto close;
+ }
+ }
+ }
+close:
+ close(fcopy_fd);
+exit:
+ return ret;
+}
diff --git a/tools/hv/vmbus_bufring.c b/tools/hv/vmbus_bufring.c
new file mode 100644
index 000000000000..bac32c1109df
--- /dev/null
+++ b/tools/hv/vmbus_bufring.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2009-2012,2016,2023 Microsoft Corp.
+ * Copyright (c) 2012 NetApp Inc.
+ * Copyright (c) 2012 Citrix Inc.
+ * All rights reserved.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <emmintrin.h>
+#include <linux/limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include "vmbus_bufring.h"
+
+/**
+ * Compiler barrier.
+ *
+ * Guarantees that operation reordering does not occur at compile time
+ * for operations directly before and after the barrier.
+ */
+#define rte_compiler_barrier() ({ asm volatile ("" : : : "memory"); })
+
+#define VMBUS_RQST_ERROR 0xFFFFFFFFFFFFFFFF
+#define ALIGN(val, align) ((typeof(val))((val) & (~((typeof(val))((align) - 1)))))
+
+void *vmbus_uio_map(int *fd, int size)
+{
+ void *map;
+
+ map = mmap(NULL, 2 * size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+ if (map == MAP_FAILED)
+ return NULL;
+
+ return map;
+}
+
+/* Increase bufring index by inc with wraparound */
+static inline uint32_t vmbus_br_idxinc(uint32_t idx, uint32_t inc, uint32_t sz)
+{
+ idx += inc;
+ if (idx >= sz)
+ idx -= sz;
+
+ return idx;
+}
+
+void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen)
+{
+ br->vbr = buf;
+ br->windex = br->vbr->windex;
+ br->dsize = blen - sizeof(struct vmbus_bufring);
+}
+
+static inline __always_inline void
+rte_smp_mb(void)
+{
+ asm volatile("lock addl $0, -128(%%rsp); " ::: "memory");
+}
+
+static inline int
+rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
+{
+ uint8_t res;
+
+ asm volatile("lock ; "
+ "cmpxchgl %[src], %[dst];"
+ "sete %[res];"
+ : [res] "=a" (res), /* output */
+ [dst] "=m" (*dst)
+ : [src] "r" (src), /* input */
+ "a" (exp),
+ "m" (*dst)
+ : "memory"); /* no-clobber list */
+ return res;
+}
+
+static inline uint32_t
+vmbus_txbr_copyto(const struct vmbus_br *tbr, uint32_t windex,
+ const void *src0, uint32_t cplen)
+{
+ uint8_t *br_data = tbr->vbr->data;
+ uint32_t br_dsize = tbr->dsize;
+ const uint8_t *src = src0;
+
+ /* XXX use double mapping like Linux kernel? */
+ if (cplen > br_dsize - windex) {
+ uint32_t fraglen = br_dsize - windex;
+
+ /* Wrap-around detected */
+ memcpy(br_data + windex, src, fraglen);
+ memcpy(br_data, src + fraglen, cplen - fraglen);
+ } else {
+ memcpy(br_data + windex, src, cplen);
+ }
+
+ return vmbus_br_idxinc(windex, cplen, br_dsize);
+}
+
+/*
+ * Write scattered channel packet to TX bufring.
+ *
+ * The offset of this channel packet is written as a 64bits value
+ * immediately after this channel packet.
+ *
+ * The write goes through three stages:
+ * 1. Reserve space in ring buffer for the new data.
+ * Writer atomically moves priv_write_index.
+ * 2. Copy the new data into the ring.
+ * 3. Update the tail of the ring (visible to host) that indicates
+ * next read location. Writer updates write_index
+ */
+static int
+vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen)
+{
+ struct vmbus_bufring *vbr = tbr->vbr;
+ uint32_t ring_size = tbr->dsize;
+ uint32_t old_windex, next_windex, windex, total;
+ uint64_t save_windex;
+ int i;
+
+ total = 0;
+ for (i = 0; i < iovlen; i++)
+ total += iov[i].iov_len;
+ total += sizeof(save_windex);
+
+ /* Reserve space in ring */
+ do {
+ uint32_t avail;
+
+ /* Get current free location */
+ old_windex = tbr->windex;
+
+ /* Prevent compiler reordering this with calculation */
+ rte_compiler_barrier();
+
+ avail = vmbus_br_availwrite(tbr, old_windex);
+
+ /* If not enough space in ring, then tell caller. */
+ if (avail <= total)
+ return -EAGAIN;
+
+ next_windex = vmbus_br_idxinc(old_windex, total, ring_size);
+
+ /* Atomic update of next write_index for other threads */
+ } while (!rte_atomic32_cmpset(&tbr->windex, old_windex, next_windex));
+
+ /* Space from old..new is now reserved */
+ windex = old_windex;
+ for (i = 0; i < iovlen; i++)
+ windex = vmbus_txbr_copyto(tbr, windex, iov[i].iov_base, iov[i].iov_len);
+
+ /* Set the offset of the current channel packet. */
+ save_windex = ((uint64_t)old_windex) << 32;
+ windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
+ sizeof(save_windex));
+
+ /* The region reserved should match region used */
+ if (windex != next_windex)
+ return -EINVAL;
+
+ /* Ensure that data is available before updating host index */
+ rte_compiler_barrier();
+
+ /* Checkin for our reservation. wait for our turn to update host */
+ while (!rte_atomic32_cmpset(&vbr->windex, old_windex, next_windex))
+ _mm_pause();
+
+ return 0;
+}
+
+int rte_vmbus_chan_send(struct vmbus_br *txbr, uint16_t type, void *data,
+ uint32_t dlen, uint32_t flags)
+{
+ struct vmbus_chanpkt pkt;
+ unsigned int pktlen, pad_pktlen;
+ const uint32_t hlen = sizeof(pkt);
+ uint64_t pad = 0;
+ struct iovec iov[3];
+ int error;
+
+ pktlen = hlen + dlen;
+ pad_pktlen = ALIGN(pktlen, sizeof(uint64_t));
+
+ pkt.hdr.type = type;
+ pkt.hdr.flags = flags;
+ pkt.hdr.hlen = hlen >> VMBUS_CHANPKT_SIZE_SHIFT;
+ pkt.hdr.tlen = pad_pktlen >> VMBUS_CHANPKT_SIZE_SHIFT;
+ pkt.hdr.xactid = VMBUS_RQST_ERROR;
+
+ iov[0].iov_base = &pkt;
+ iov[0].iov_len = hlen;
+ iov[1].iov_base = data;
+ iov[1].iov_len = dlen;
+ iov[2].iov_base = &pad;
+ iov[2].iov_len = pad_pktlen - pktlen;
+
+ error = vmbus_txbr_write(txbr, iov, 3);
+
+ return error;
+}
+
+static inline uint32_t
+vmbus_rxbr_copyfrom(const struct vmbus_br *rbr, uint32_t rindex,
+ void *dst0, size_t cplen)
+{
+ const uint8_t *br_data = rbr->vbr->data;
+ uint32_t br_dsize = rbr->dsize;
+ uint8_t *dst = dst0;
+
+ if (cplen > br_dsize - rindex) {
+ uint32_t fraglen = br_dsize - rindex;
+
+ /* Wrap-around detected. */
+ memcpy(dst, br_data + rindex, fraglen);
+ memcpy(dst + fraglen, br_data, cplen - fraglen);
+ } else {
+ memcpy(dst, br_data + rindex, cplen);
+ }
+
+ return vmbus_br_idxinc(rindex, cplen, br_dsize);
+}
+
+/* Copy data from receive ring but don't change index */
+static int
+vmbus_rxbr_peek(const struct vmbus_br *rbr, void *data, size_t dlen)
+{
+ uint32_t avail;
+
+ /*
+ * The requested data and the 64bits channel packet
+ * offset should be there at least.
+ */
+ avail = vmbus_br_availread(rbr);
+ if (avail < dlen + sizeof(uint64_t))
+ return -EAGAIN;
+
+ vmbus_rxbr_copyfrom(rbr, rbr->vbr->rindex, data, dlen);
+ return 0;
+}
+
+/*
+ * Copy data from receive ring and change index
+ * NOTE:
+ * We assume (dlen + skip) == sizeof(channel packet).
+ */
+static int
+vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t skip)
+{
+ struct vmbus_bufring *vbr = rbr->vbr;
+ uint32_t br_dsize = rbr->dsize;
+ uint32_t rindex;
+
+ if (vmbus_br_availread(rbr) < dlen + skip + sizeof(uint64_t))
+ return -EAGAIN;
+
+ /* Record where host was when we started read (for debug) */
+ rbr->windex = rbr->vbr->windex;
+
+ /*
+ * Copy channel packet from RX bufring.
+ */
+ rindex = vmbus_br_idxinc(rbr->vbr->rindex, skip, br_dsize);
+ rindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen);
+
+ /*
+ * Discard this channel packet's 64bits offset, which is useless to us.
+ */
+ rindex = vmbus_br_idxinc(rindex, sizeof(uint64_t), br_dsize);
+
+ /* Update the read index _after_ the channel packet is fetched. */
+ rte_compiler_barrier();
+
+ vbr->rindex = rindex;
+
+ return 0;
+}
+
+int rte_vmbus_chan_recv_raw(struct vmbus_br *rxbr,
+ void *data, uint32_t *len)
+{
+ struct vmbus_chanpkt_hdr pkt;
+ uint32_t dlen, bufferlen = *len;
+ int error;
+
+ error = vmbus_rxbr_peek(rxbr, &pkt, sizeof(pkt));
+ if (error)
+ return error;
+
+ if (unlikely(pkt.hlen < VMBUS_CHANPKT_HLEN_MIN))
+ /* XXX this channel is dead actually. */
+ return -EIO;
+
+ if (unlikely(pkt.hlen > pkt.tlen))
+ return -EIO;
+
+ /* Length are in quad words */
+ dlen = pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT;
+ *len = dlen;
+
+ /* If caller buffer is not large enough */
+ if (unlikely(dlen > bufferlen))
+ return -ENOBUFS;
+
+ /* Read data and skip packet header */
+ error = vmbus_rxbr_read(rxbr, data, dlen, 0);
+ if (error)
+ return error;
+
+ /* Return the number of bytes read */
+ return dlen + sizeof(uint64_t);
+}
diff --git a/tools/hv/vmbus_bufring.h b/tools/hv/vmbus_bufring.h
new file mode 100644
index 000000000000..6e7caacfff57
--- /dev/null
+++ b/tools/hv/vmbus_bufring.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+
+#ifndef _VMBUS_BUF_H_
+#define _VMBUS_BUF_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define __packed __attribute__((__packed__))
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+#define ICMSGHDRFLAG_TRANSACTION 1
+#define ICMSGHDRFLAG_REQUEST 2
+#define ICMSGHDRFLAG_RESPONSE 4
+
+#define IC_VERSION_NEGOTIATION_MAX_VER_COUNT 100
+#define ICMSG_HDR (sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr))
+#define ICMSG_NEGOTIATE_PKT_SIZE(icframe_vercnt, icmsg_vercnt) \
+ (ICMSG_HDR + sizeof(struct icmsg_negotiate) + \
+ (((icframe_vercnt) + (icmsg_vercnt)) * sizeof(struct ic_version)))
+
+/*
+ * Channel packets
+ */
+
+/* Channel packet flags */
+#define VMBUS_CHANPKT_TYPE_INBAND 0x0006
+#define VMBUS_CHANPKT_TYPE_RXBUF 0x0007
+#define VMBUS_CHANPKT_TYPE_GPA 0x0009
+#define VMBUS_CHANPKT_TYPE_COMP 0x000b
+
+#define VMBUS_CHANPKT_FLAG_NONE 0
+#define VMBUS_CHANPKT_FLAG_RC 0x0001 /* report completion */
+
+#define VMBUS_CHANPKT_SIZE_SHIFT 3
+#define VMBUS_CHANPKT_SIZE_ALIGN BIT(VMBUS_CHANPKT_SIZE_SHIFT)
+#define VMBUS_CHANPKT_HLEN_MIN \
+ (sizeof(struct vmbus_chanpkt_hdr) >> VMBUS_CHANPKT_SIZE_SHIFT)
+
+/*
+ * Buffer ring
+ */
+struct vmbus_bufring {
+ volatile uint32_t windex;
+ volatile uint32_t rindex;
+
+ /*
+ * Interrupt mask {0,1}
+ *
+ * For TX bufring, host set this to 1, when it is processing
+ * the TX bufring, so that we can safely skip the TX event
+ * notification to host.
+ *
+ * For RX bufring, once this is set to 1 by us, host will not
+ * further dispatch interrupts to us, even if there are data
+ * pending on the RX bufring. This effectively disables the
+ * interrupt of the channel to which this RX bufring is attached.
+ */
+ volatile uint32_t imask;
+
+ /*
+ * Win8 uses some of the reserved bits to implement
+ * interrupt driven flow management. On the send side
+ * we can request that the receiver interrupt the sender
+ * when the ring transitions from being full to being able
+ * to handle a message of size "pending_send_sz".
+ *
+ * Add necessary state for this enhancement.
+ */
+ volatile uint32_t pending_send;
+ uint32_t reserved1[12];
+
+ union {
+ struct {
+ uint32_t feat_pending_send_sz:1;
+ };
+ uint32_t value;
+ } feature_bits;
+
+ /* Pad it to rte_mem_page_size() so that data starts on page boundary */
+ uint8_t reserved2[4028];
+
+ /*
+ * Ring data starts here + RingDataStartOffset
+ * !!! DO NOT place any fields below this !!!
+ */
+ uint8_t data[];
+} __packed;
+
+struct vmbus_br {
+ struct vmbus_bufring *vbr;
+ uint32_t dsize;
+ uint32_t windex; /* next available location */
+};
+
+struct vmbus_chanpkt_hdr {
+ uint16_t type; /* VMBUS_CHANPKT_TYPE_ */
+ uint16_t hlen; /* header len, in 8 bytes */
+ uint16_t tlen; /* total len, in 8 bytes */
+ uint16_t flags; /* VMBUS_CHANPKT_FLAG_ */
+ uint64_t xactid;
+} __packed;
+
+struct vmbus_chanpkt {
+ struct vmbus_chanpkt_hdr hdr;
+} __packed;
+
+struct vmbuspipe_hdr {
+ unsigned int flags;
+ unsigned int msgsize;
+} __packed;
+
+struct ic_version {
+ unsigned short major;
+ unsigned short minor;
+} __packed;
+
+struct icmsg_negotiate {
+ unsigned short icframe_vercnt;
+ unsigned short icmsg_vercnt;
+ unsigned int reserved;
+ struct ic_version icversion_data[]; /* any size array */
+} __packed;
+
+struct icmsg_hdr {
+ struct ic_version icverframe;
+ unsigned short icmsgtype;
+ struct ic_version icvermsg;
+ unsigned short icmsgsize;
+ unsigned int status;
+ unsigned char ictransaction_id;
+ unsigned char icflags;
+ unsigned char reserved[2];
+} __packed;
+
+int rte_vmbus_chan_recv_raw(struct vmbus_br *rxbr, void *data, uint32_t *len);
+int rte_vmbus_chan_send(struct vmbus_br *txbr, uint16_t type, void *data,
+ uint32_t dlen, uint32_t flags);
+void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen);
+void *vmbus_uio_map(int *fd, int size);
+
+/* Amount of space available for write */
+static inline uint32_t vmbus_br_availwrite(const struct vmbus_br *br, uint32_t windex)
+{
+ uint32_t rindex = br->vbr->rindex;
+
+ if (windex >= rindex)
+ return br->dsize - (windex - rindex);
+ else
+ return rindex - windex;
+}
+
+static inline uint32_t vmbus_br_availread(const struct vmbus_br *br)
+{
+ return br->dsize - vmbus_br_availwrite(br, br->vbr->windex);
+}
+
+#endif /* !_VMBUS_BUF_H_ */