summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/accel/qaic/mhi_controller.c44
-rw-r--r--drivers/android/binder.c2
-rw-r--r--drivers/android/binder/context.rs86
-rw-r--r--drivers/android/binder/node.rs8
-rw-r--r--drivers/android/binder/process.rs14
-rw-r--r--drivers/android/binder/rust_binder.h79
-rw-r--r--drivers/android/binder/rust_binder_events.h30
-rw-r--r--drivers/android/binder/rust_binder_main.rs12
-rw-r--r--drivers/android/binder/thread.rs4
-rw-r--r--drivers/android/binder/trace.rs21
-rw-r--r--drivers/android/binder/transaction.rs14
-rw-r--r--drivers/android/binder_alloc.c6
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c3
-rw-r--r--drivers/bus/mhi/ep/main.c12
-rw-r--r--drivers/bus/mhi/host/boot.c10
-rw-r--r--drivers/bus/mhi/host/init.c22
-rw-r--r--drivers/bus/mhi/host/internal.h3
-rw-r--r--drivers/bus/mhi/host/main.c81
-rw-r--r--drivers/bus/mhi/host/pci_generic.c20
-rw-r--r--drivers/char/Kconfig26
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/misc_minor_kunit.c2
-rw-r--r--drivers/char/mwave/3780i.c536
-rw-r--r--drivers/char/mwave/3780i.h358
-rw-r--r--drivers/char/mwave/Makefile10
-rw-r--r--drivers/char/mwave/README37
-rw-r--r--drivers/char/mwave/mwavedd.c432
-rw-r--r--drivers/char/mwave/mwavedd.h90
-rw-r--r--drivers/char/mwave/mwavepub.h89
-rw-r--r--drivers/char/mwave/smapi.c404
-rw-r--r--drivers/char/mwave/smapi.h76
-rw-r--r--drivers/char/mwave/tp3780i.c477
-rw-r--r--drivers/char/mwave/tp3780i.h103
-rw-r--r--drivers/char/xilinx_hwicap/fifo_icap.c27
-rw-r--r--drivers/comedi/comedi_fops.c81
-rw-r--r--drivers/comedi/drivers/comedi_test.c50
-rw-r--r--drivers/firmware/stratix10-svc.c14
-rw-r--r--drivers/fpga/dfl.c2
-rw-r--r--drivers/fpga/dfl.h2
-rw-r--r--drivers/fpga/of-fpga-region.c8
-rw-r--r--drivers/fpga/xilinx-pr-decoupler.c2
-rw-r--r--drivers/fpga/zynq-fpga.c2
-rw-r--r--drivers/fsi/fsi-core.c107
-rw-r--r--drivers/fsi/fsi-master-hub.c17
-rw-r--r--drivers/fsi/fsi-sbefifo.c31
-rw-r--r--drivers/fsi/fsi-scom.c30
-rw-r--r--drivers/fsi/i2cr-scom.c15
-rw-r--r--drivers/gpib/agilent_82350b/agilent_82350b.c5
-rw-r--r--drivers/gpib/agilent_82357a/agilent_82357a.c2
-rw-r--r--drivers/gpib/cb7210/cb7210.c9
-rw-r--r--drivers/gpib/cec/cec_gpib.c11
-rw-r--r--drivers/gpib/common/iblib.c5
-rw-r--r--drivers/gpib/eastwood/fluke_gpib.c5
-rw-r--r--drivers/gpib/fmh_gpib/fmh_gpib.c5
-rw-r--r--drivers/gpib/gpio/gpib_bitbang.c9
-rw-r--r--drivers/gpib/hp_82335/hp82335.c7
-rw-r--r--drivers/gpib/hp_82341/hp_82341.c5
-rw-r--r--drivers/gpib/ines/ines_gpib.c11
-rw-r--r--drivers/gpib/ni_usb/ni_usb_gpib.c19
-rw-r--r--drivers/gpib/pc2/pc2_gpib.c11
-rw-r--r--drivers/gpib/tnt4882/tnt4882_gpib.c20
-rw-r--r--drivers/greybus/core.c22
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c68
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.h38
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-core.c51
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c175
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.h92
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-etr.c43
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.h2
-rw-r--r--drivers/hwtracing/coresight/coresight-tnoc.c136
-rw-r--r--drivers/hwtracing/coresight/coresight-tpda.c284
-rw-r--r--drivers/hwtracing/coresight/coresight-tpda.h76
-rw-r--r--drivers/hwtracing/intel_th/pci.c269
-rw-r--r--drivers/hwtracing/intel_th/pci_ids.h60
-rw-r--r--drivers/hwtracing/stm/Kconfig4
-rw-r--r--drivers/i2c/busses/i2c-fsi.c16
-rw-r--r--drivers/iio/accel/Kconfig10
-rw-r--r--drivers/iio/accel/adxl355_core.c5
-rw-r--r--drivers/iio/accel/adxl372.c10
-rw-r--r--drivers/iio/accel/adxl380.c138
-rw-r--r--drivers/iio/accel/adxl380.h10
-rw-r--r--drivers/iio/accel/bma180.c5
-rw-r--r--drivers/iio/accel/mxc4005.c11
-rw-r--r--drivers/iio/accel/sca3000.c8
-rw-r--r--drivers/iio/accel/stk8ba50.c11
-rw-r--r--drivers/iio/adc/Kconfig60
-rw-r--r--drivers/iio/adc/Makefile5
-rw-r--r--drivers/iio/adc/ad4062.c1609
-rw-r--r--drivers/iio/adc/ad4134.c500
-rw-r--r--drivers/iio/adc/ad4170-4.c2
-rw-r--r--drivers/iio/adc/ad7476.c1
-rw-r--r--drivers/iio/adc/ad7606_spi.c2
-rw-r--r--drivers/iio/adc/ad7766.c10
-rw-r--r--drivers/iio/adc/ad7768-1.c428
-rw-r--r--drivers/iio/adc/ad7779.c2
-rw-r--r--drivers/iio/adc/ad9467.c152
-rw-r--r--drivers/iio/adc/ade9000.c2
-rw-r--r--drivers/iio/adc/adi-axi-adc.c64
-rw-r--r--drivers/iio/adc/aspeed_adc.c49
-rw-r--r--drivers/iio/adc/exynos_adc.c57
-rw-r--r--drivers/iio/adc/mcp3911.c2
-rw-r--r--drivers/iio/adc/men_z188_adc.c1
-rw-r--r--drivers/iio/adc/nxp-sar-adc.c1016
-rw-r--r--drivers/iio/adc/qcom-spmi-rradc.c20
-rw-r--r--drivers/iio/adc/rockchip_saradc.c59
-rw-r--r--drivers/iio/adc/sc27xx_adc.c49
-rw-r--r--drivers/iio/adc/ti-ads1018.c739
-rw-r--r--drivers/iio/adc/ti-ads131e08.c2
-rw-r--r--drivers/iio/adc/ti-ads131m02.c968
-rw-r--r--drivers/iio/amplifiers/Kconfig12
-rw-r--r--drivers/iio/amplifiers/Makefile1
-rw-r--r--drivers/iio/amplifiers/adl8113.c269
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dma.c188
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c23
-rw-r--r--drivers/iio/chemical/ens160_core.c9
-rw-r--r--drivers/iio/chemical/scd4x.c3
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c11
-rw-r--r--drivers/iio/dac/Kconfig33
-rw-r--r--drivers/iio/dac/Makefile2
-rw-r--r--drivers/iio/dac/adi-axi-dac.c66
-rw-r--r--drivers/iio/dac/ds4424.c1
-rw-r--r--drivers/iio/dac/max22007.c491
-rw-r--r--drivers/iio/dac/mcp47feb02.c1250
-rw-r--r--drivers/iio/frequency/adf4377.c122
-rw-r--r--drivers/iio/gyro/adxrs290.c2
-rw-r--r--drivers/iio/gyro/itg3200_buffer.c8
-rw-r--r--drivers/iio/gyro/itg3200_core.c2
-rw-r--r--drivers/iio/gyro/mpu3050-core.c6
-rw-r--r--drivers/iio/health/afe4403.c9
-rw-r--r--drivers/iio/health/afe4404.c9
-rw-r--r--drivers/iio/health/max30100.c8
-rw-r--r--drivers/iio/health/max30102.c35
-rw-r--r--drivers/iio/imu/bmi270/bmi270_i2c.c3
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c3
-rw-r--r--drivers/iio/imu/smi330/smi330_core.c9
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h55
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c555
-rw-r--r--drivers/iio/industrialio-core.c86
-rw-r--r--drivers/iio/industrialio-sw-device.c2
-rw-r--r--drivers/iio/industrialio-sw-trigger.c2
-rw-r--r--drivers/iio/light/isl29018.c12
-rw-r--r--drivers/iio/light/opt4060.c52
-rw-r--r--drivers/iio/light/si1145.c2
-rw-r--r--drivers/iio/light/vcnl4000.c49
-rw-r--r--drivers/iio/magnetometer/Kconfig13
-rw-r--r--drivers/iio/magnetometer/Makefile1
-rw-r--r--drivers/iio/magnetometer/ak8975.c2
-rw-r--r--drivers/iio/magnetometer/bmc150_magn.c9
-rw-r--r--drivers/iio/magnetometer/mmc5633.c586
-rw-r--r--drivers/iio/pressure/Kconfig63
-rw-r--r--drivers/iio/pressure/Makefile3
-rw-r--r--drivers/iio/pressure/abp2030pa.c544
-rw-r--r--drivers/iio/pressure/abp2030pa.h73
-rw-r--r--drivers/iio/pressure/abp2030pa_i2c.c90
-rw-r--r--drivers/iio/pressure/abp2030pa_spi.c67
-rw-r--r--drivers/iio/pressure/dlhl60d.c7
-rw-r--r--drivers/iio/pressure/mprls0025pa.c117
-rw-r--r--drivers/iio/pressure/mprls0025pa.h15
-rw-r--r--drivers/iio/pressure/mprls0025pa_i2c.c13
-rw-r--r--drivers/iio/pressure/mprls0025pa_spi.c41
-rw-r--r--drivers/iio/proximity/rfd77402.c185
-rw-r--r--drivers/iio/temperature/tmp006.c10
-rw-r--r--drivers/iio/test/Kconfig1
-rw-r--r--drivers/interconnect/Kconfig14
-rw-r--r--drivers/interconnect/Makefile2
-rw-r--r--drivers/interconnect/icc-kunit.c324
-rw-r--r--drivers/interconnect/mediatek/Kconfig7
-rw-r--r--drivers/interconnect/mediatek/Makefile1
-rw-r--r--drivers/interconnect/mediatek/icc-emi.c9
-rw-r--r--drivers/interconnect/mediatek/mt8196.c383
-rw-r--r--drivers/interconnect/qcom/msm8974.c3
-rw-r--r--drivers/interconnect/qcom/qcs8300.c2
-rw-r--r--drivers/interconnect/qcom/smd-rpm.c1
-rw-r--r--drivers/mcb/mcb-core.c9
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_msg.c12
-rw-r--r--drivers/misc/eeprom/at25.c86
-rw-r--r--drivers/misc/eeprom/eeprom_93xx46.c11
-rw-r--r--drivers/misc/fastrpc.c142
-rw-r--r--drivers/misc/kgdbts.c2
-rw-r--r--drivers/misc/mei/Kconfig6
-rw-r--r--drivers/misc/mei/gsc_proxy/Kconfig2
-rw-r--r--drivers/misc/mei/hdcp/Kconfig2
-rw-r--r--drivers/misc/mei/pxp/Kconfig2
-rw-r--r--drivers/misc/ti_fpc202.c3
-rw-r--r--drivers/most/core.c15
-rw-r--r--drivers/mux/mmio.c2
-rw-r--r--drivers/net/wireless/ath/ath11k/mhi.c4
-rw-r--r--drivers/net/wireless/ath/ath12k/wifi7/mhi.c4
-rw-r--r--drivers/nvmem/Kconfig2
-rw-r--r--drivers/nvmem/core.c7
-rw-r--r--drivers/pps/generators/Kconfig8
-rw-r--r--drivers/pps/generators/Makefile1
-rw-r--r--drivers/pps/generators/pps_gen_parport.c238
-rw-r--r--drivers/slimbus/qcom-ngd-ctrl.c10
-rw-r--r--drivers/spi/spi-fsi.c7
-rw-r--r--drivers/spmi/spmi-apple-controller.c1
-rw-r--r--drivers/spmi/spmi-mtk-pmif.c472
-rw-r--r--drivers/spmi/spmi-pmic-arb.c304
-rw-r--r--drivers/staging/iio/addac/adt7316-i2c.c2
-rw-r--r--drivers/staging/iio/addac/adt7316-spi.c2
-rw-r--r--drivers/staging/iio/addac/adt7316.c6
-rw-r--r--drivers/staging/iio/addac/adt7316.h6
-rw-r--r--drivers/staging/iio/frequency/ad9832.c37
-rw-r--r--drivers/staging/iio/frequency/ad9832.h33
204 files changed, 13208 insertions, 5173 deletions
diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c
index 13a14c6c6168..4d787f77ce41 100644
--- a/drivers/accel/qaic/mhi_controller.c
+++ b/drivers/accel/qaic/mhi_controller.c
@@ -39,7 +39,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -55,7 +54,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -71,7 +69,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -87,7 +84,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -103,7 +99,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -119,7 +114,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -135,7 +129,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -151,7 +144,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -167,7 +159,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -183,7 +174,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -199,7 +189,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -215,7 +204,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -231,7 +219,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -247,7 +234,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -263,7 +249,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -279,7 +264,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -295,7 +279,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -311,7 +294,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -327,7 +309,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -343,7 +324,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -359,7 +339,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -375,7 +354,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -391,7 +369,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -407,7 +384,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -423,7 +399,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -439,7 +414,6 @@ static const struct mhi_channel_config aic100_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = true,
.wake_capable = false,
},
};
@@ -458,7 +432,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -474,7 +447,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -490,7 +462,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -506,7 +477,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -522,7 +492,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -538,7 +507,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -554,7 +522,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -570,7 +537,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -586,7 +552,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -602,7 +567,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -618,7 +582,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -634,7 +597,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -650,7 +612,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -666,7 +627,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -682,7 +642,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -698,7 +657,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -714,7 +672,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
.wake_capable = false,
},
{
@@ -730,7 +687,6 @@ static const struct mhi_channel_config aic200_channels[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = true,
.wake_capable = false,
},
};
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index bd780d88b468..f43edb11bef4 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -4523,7 +4523,7 @@ static int binder_thread_write(struct binder_proc *proc,
}
}
binder_debug(BINDER_DEBUG_DEAD_BINDER,
- "%d:%d BC_DEAD_BINDER_DONE %016llx found %pK\n",
+ "%d:%d BC_DEAD_BINDER_DONE %016llx found %p\n",
proc->pid, thread->pid, (u64)cookie,
death);
if (death == NULL) {
diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/context.rs
index 3d135ec03ca7..9cf437c025a2 100644
--- a/drivers/android/binder/context.rs
+++ b/drivers/android/binder/context.rs
@@ -3,8 +3,8 @@
// Copyright (C) 2025 Google LLC.
use kernel::{
- error::Error,
- list::{List, ListArc, ListLinks},
+ alloc::kvec::KVVec,
+ error::code::*,
prelude::*,
security,
str::{CStr, CString},
@@ -17,22 +17,19 @@ use crate::{error::BinderError, node::NodeRef, process::Process};
kernel::sync::global_lock! {
// SAFETY: We call `init` in the module initializer, so it's initialized before first use.
pub(crate) unsafe(uninit) static CONTEXTS: Mutex<ContextList> = ContextList {
- list: List::new(),
+ contexts: KVVec::new(),
};
}
pub(crate) struct ContextList {
- list: List<Context>,
+ contexts: KVVec<Arc<Context>>,
}
-pub(crate) fn get_all_contexts() -> Result<KVec<Arc<Context>>> {
+pub(crate) fn get_all_contexts() -> Result<KVVec<Arc<Context>>> {
let lock = CONTEXTS.lock();
-
- let count = lock.list.iter().count();
-
- let mut ctxs = KVec::with_capacity(count, GFP_KERNEL)?;
- for ctx in &lock.list {
- ctxs.push(Arc::from(ctx), GFP_KERNEL)?;
+ let mut ctxs = KVVec::with_capacity(lock.contexts.len(), GFP_KERNEL)?;
+ for ctx in lock.contexts.iter() {
+ ctxs.push(ctx.clone(), GFP_KERNEL)?;
}
Ok(ctxs)
}
@@ -42,7 +39,7 @@ pub(crate) fn get_all_contexts() -> Result<KVec<Arc<Context>>> {
struct Manager {
node: Option<NodeRef>,
uid: Option<Kuid>,
- all_procs: List<Process>,
+ all_procs: KVVec<Arc<Process>>,
}
/// There is one context per binder file (/dev/binder, /dev/hwbinder, etc)
@@ -51,28 +48,16 @@ pub(crate) struct Context {
#[pin]
manager: Mutex<Manager>,
pub(crate) name: CString,
- #[pin]
- links: ListLinks,
-}
-
-kernel::list::impl_list_arc_safe! {
- impl ListArcSafe<0> for Context { untracked; }
-}
-kernel::list::impl_list_item! {
- impl ListItem<0> for Context {
- using ListLinks { self.links };
- }
}
impl Context {
pub(crate) fn new(name: &CStr) -> Result<Arc<Self>> {
let name = CString::try_from(name)?;
- let list_ctx = ListArc::pin_init::<Error>(
+ let ctx = Arc::pin_init(
try_pin_init!(Context {
name,
- links <- ListLinks::new(),
manager <- kernel::new_mutex!(Manager {
- all_procs: List::new(),
+ all_procs: KVVec::new(),
node: None,
uid: None,
}, "Context::manager"),
@@ -80,8 +65,7 @@ impl Context {
GFP_KERNEL,
)?;
- let ctx = list_ctx.clone_arc();
- CONTEXTS.lock().list.push_back(list_ctx);
+ CONTEXTS.lock().contexts.push(ctx.clone(), GFP_KERNEL)?;
Ok(ctx)
}
@@ -89,27 +73,27 @@ impl Context {
/// Called when the file for this context is unlinked.
///
/// No-op if called twice.
- pub(crate) fn deregister(&self) {
- // SAFETY: We never add the context to any other linked list than this one, so it is either
- // in this list, or not in any list.
- unsafe { CONTEXTS.lock().list.remove(self) };
+ pub(crate) fn deregister(self: &Arc<Self>) {
+ // Safe removal using retain
+ CONTEXTS.lock().contexts.retain(|c| !Arc::ptr_eq(c, self));
}
- pub(crate) fn register_process(self: &Arc<Self>, proc: ListArc<Process>) {
+ pub(crate) fn register_process(self: &Arc<Self>, proc: Arc<Process>) -> Result {
if !Arc::ptr_eq(self, &proc.ctx) {
pr_err!("Context::register_process called on the wrong context.");
- return;
+ return Err(EINVAL);
}
- self.manager.lock().all_procs.push_back(proc);
+ self.manager.lock().all_procs.push(proc, GFP_KERNEL)?;
+ Ok(())
}
- pub(crate) fn deregister_process(self: &Arc<Self>, proc: &Process) {
+ pub(crate) fn deregister_process(self: &Arc<Self>, proc: &Arc<Process>) {
if !Arc::ptr_eq(self, &proc.ctx) {
pr_err!("Context::deregister_process called on the wrong context.");
return;
}
- // SAFETY: We just checked that this is the right list.
- unsafe { self.manager.lock().all_procs.remove(proc) };
+ let mut manager = self.manager.lock();
+ manager.all_procs.retain(|p| !Arc::ptr_eq(p, proc));
}
pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result {
@@ -154,27 +138,27 @@ impl Context {
{
let lock = self.manager.lock();
for proc in &lock.all_procs {
- func(&proc);
+ func(proc);
}
}
- pub(crate) fn get_all_procs(&self) -> Result<KVec<Arc<Process>>> {
+ pub(crate) fn get_all_procs(&self) -> Result<KVVec<Arc<Process>>> {
let lock = self.manager.lock();
- let count = lock.all_procs.iter().count();
-
- let mut procs = KVec::with_capacity(count, GFP_KERNEL)?;
- for proc in &lock.all_procs {
- procs.push(Arc::from(proc), GFP_KERNEL)?;
+ let mut procs = KVVec::with_capacity(lock.all_procs.len(), GFP_KERNEL)?;
+ for proc in lock.all_procs.iter() {
+ procs.push(Arc::clone(proc), GFP_KERNEL)?;
}
Ok(procs)
}
- pub(crate) fn get_procs_with_pid(&self, pid: i32) -> Result<KVec<Arc<Process>>> {
- let orig = self.get_all_procs()?;
- let mut backing = KVec::with_capacity(orig.len(), GFP_KERNEL)?;
- for proc in orig.into_iter().filter(|proc| proc.task.pid() == pid) {
- backing.push(proc, GFP_KERNEL)?;
+ pub(crate) fn get_procs_with_pid(&self, pid: i32) -> Result<KVVec<Arc<Process>>> {
+ let lock = self.manager.lock();
+ let mut matching_procs = KVVec::new();
+ for proc in lock.all_procs.iter() {
+ if proc.task.pid() == pid {
+ matching_procs.push(Arc::clone(proc), GFP_KERNEL)?;
+ }
}
- Ok(backing)
+ Ok(matching_procs)
}
}
diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
index c26d113ede96..69f757ff7461 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -178,6 +178,14 @@ struct NodeInner {
refs: List<NodeRefInfo, { NodeRefInfo::LIST_NODE }>,
}
+use kernel::bindings::rb_node_layout;
+use mem::offset_of;
+pub(crate) const NODE_LAYOUT: rb_node_layout = rb_node_layout {
+ arc_offset: Arc::<Node>::DATA_OFFSET + offset_of!(DTRWrap<Node>, wrapped),
+ debug_id: offset_of!(Node, debug_id),
+ ptr: offset_of!(Node, ptr),
+};
+
#[pin_data]
pub(crate) struct Node {
pub(crate) debug_id: usize,
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index 132055b4790f..41de5593197c 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -28,11 +28,11 @@ use kernel::{
seq_print,
sync::poll::PollTable,
sync::{
+ aref::ARef,
lock::{spinlock::SpinLockBackend, Guard},
Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc,
},
task::Task,
- types::ARef,
uaccess::{UserSlice, UserSliceReader},
uapi,
workqueue::{self, Work},
@@ -418,6 +418,13 @@ impl ProcessNodeRefs {
}
}
+use core::mem::offset_of;
+use kernel::bindings::rb_process_layout;
+pub(crate) const PROCESS_LAYOUT: rb_process_layout = rb_process_layout {
+ arc_offset: Arc::<Process>::DATA_OFFSET,
+ task: offset_of!(Process, task),
+};
+
/// A process using binder.
///
/// Strictly speaking, there can be multiple of these per process. There is one for each binder fd
@@ -496,7 +503,7 @@ impl workqueue::WorkItem for Process {
impl Process {
fn new(ctx: Arc<Context>, cred: ARef<Credential>) -> Result<Arc<Self>> {
let current = kernel::current!();
- let list_process = ListArc::pin_init::<Error>(
+ let process = Arc::pin_init::<Error>(
try_pin_init!(Process {
ctx,
cred,
@@ -512,8 +519,7 @@ impl Process {
GFP_KERNEL,
)?;
- let process = list_process.clone_arc();
- process.ctx.register_process(list_process);
+ process.ctx.register_process(process.clone())?;
Ok(process)
}
diff --git a/drivers/android/binder/rust_binder.h b/drivers/android/binder/rust_binder.h
index 31806890ed1a..d2284726c025 100644
--- a/drivers/android/binder/rust_binder.h
+++ b/drivers/android/binder/rust_binder.h
@@ -20,4 +20,83 @@ struct inode;
struct dentry *rust_binderfs_create_proc_file(struct inode *nodp, int pid);
void rust_binderfs_remove_file(struct dentry *dentry);
+/*
+ * The internal data types in the Rust Binder driver are opaque to C, so we use
+ * void pointer typedefs for these types.
+ */
+
+typedef void *rust_binder_transaction;
+typedef void *rust_binder_process;
+typedef void *rust_binder_node;
+
+struct rb_process_layout {
+ size_t arc_offset;
+ size_t task;
+};
+
+struct rb_transaction_layout {
+ size_t debug_id;
+ size_t code;
+ size_t flags;
+ size_t from_thread;
+ size_t to_proc;
+ size_t target_node;
+};
+
+struct rb_node_layout {
+ size_t arc_offset;
+ size_t debug_id;
+ size_t ptr;
+};
+
+struct rust_binder_layout {
+ struct rb_transaction_layout t;
+ struct rb_process_layout p;
+ struct rb_node_layout n;
+};
+
+extern const struct rust_binder_layout RUST_BINDER_LAYOUT;
+
+static inline size_t rust_binder_transaction_debug_id(rust_binder_transaction t)
+{
+ return *(size_t *) (t + RUST_BINDER_LAYOUT.t.debug_id);
+}
+
+static inline u32 rust_binder_transaction_code(rust_binder_transaction t)
+{
+ return *(u32 *) (t + RUST_BINDER_LAYOUT.t.code);
+}
+
+static inline u32 rust_binder_transaction_flags(rust_binder_transaction t)
+{
+ return *(u32 *) (t + RUST_BINDER_LAYOUT.t.flags);
+}
+
+// Nullable!
+static inline rust_binder_node rust_binder_transaction_target_node(rust_binder_transaction t)
+{
+ void *p = *(void **) (t + RUST_BINDER_LAYOUT.t.target_node);
+
+ if (p)
+ p = p + RUST_BINDER_LAYOUT.n.arc_offset;
+ return p;
+}
+
+static inline rust_binder_process rust_binder_transaction_to_proc(rust_binder_transaction t)
+{
+ void *p = *(void **) (t + RUST_BINDER_LAYOUT.t.to_proc);
+
+ return p + RUST_BINDER_LAYOUT.p.arc_offset;
+}
+
+static inline struct task_struct *rust_binder_process_task(rust_binder_process t)
+{
+ return *(struct task_struct **) (t + RUST_BINDER_LAYOUT.p.task);
+}
+
+static inline size_t rust_binder_node_debug_id(rust_binder_node t)
+{
+ return *(size_t *) (t + RUST_BINDER_LAYOUT.n.debug_id);
+}
+
#endif
diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h
index 2f3efbf9dba6..8ad785c6bd0f 100644
--- a/drivers/android/binder/rust_binder_events.h
+++ b/drivers/android/binder/rust_binder_events.h
@@ -30,6 +30,36 @@ TRACE_EVENT(rust_binder_ioctl,
TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
);
+TRACE_EVENT(rust_binder_transaction,
+ TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread),
+ TP_ARGS(reply, t, thread),
+ TP_STRUCT__entry(
+ __field(int, debug_id)
+ __field(int, target_node)
+ __field(int, to_proc)
+ __field(int, to_thread)
+ __field(int, reply)
+ __field(unsigned int, code)
+ __field(unsigned int, flags)
+ ),
+ TP_fast_assign(
+ rust_binder_process to = rust_binder_transaction_to_proc(t);
+ rust_binder_node target_node = rust_binder_transaction_target_node(t);
+
+ __entry->debug_id = rust_binder_transaction_debug_id(t);
+ __entry->target_node = target_node ? rust_binder_node_debug_id(target_node) : 0;
+ __entry->to_proc = rust_binder_process_task(to)->pid;
+ __entry->to_thread = thread ? thread->pid : 0;
+ __entry->reply = reply;
+ __entry->code = rust_binder_transaction_code(t);
+ __entry->flags = rust_binder_transaction_flags(t);
+ ),
+ TP_printk("transaction=%d dest_node=%d dest_proc=%d dest_thread=%d reply=%d flags=0x%x code=0x%x",
+ __entry->debug_id, __entry->target_node,
+ __entry->to_proc, __entry->to_thread,
+ __entry->reply, __entry->flags, __entry->code)
+);
+
#endif /* _RUST_BINDER_TRACE_H */
/* This part must be outside protection */
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index 47bfb114cabb..aa5f2a75adb4 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -87,6 +87,14 @@ module! {
license: "GPL",
}
+use kernel::bindings::rust_binder_layout;
+#[no_mangle]
+static RUST_BINDER_LAYOUT: rust_binder_layout = rust_binder_layout {
+ t: transaction::TRANSACTION_LAYOUT,
+ p: process::PROCESS_LAYOUT,
+ n: node::NODE_LAYOUT,
+};
+
fn next_debug_id() -> usize {
static NEXT_DEBUG_ID: Atomic<usize> = Atomic::new(0);
@@ -286,7 +294,7 @@ impl kernel::Module for BinderModule {
pr_warn!("Loaded Rust Binder.");
- BINDER_SHRINKER.register(kernel::c_str!("android-binder"))?;
+ BINDER_SHRINKER.register(c"android-binder")?;
// SAFETY: The module is being loaded, so we can initialize binderfs.
unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? };
@@ -312,7 +320,7 @@ pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
owner: THIS_MODULE.as_ptr(),
poll: Some(rust_binder_poll),
unlocked_ioctl: Some(rust_binder_ioctl),
- compat_ioctl: Some(bindings::compat_ptr_ioctl),
+ compat_ioctl: bindings::compat_ptr_ioctl,
mmap: Some(rust_binder_mmap),
open: Some(rust_binder_open),
release: Some(rust_binder_release),
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index 1f1709a6a77a..0b62d24b2118 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -17,9 +17,8 @@ use kernel::{
seq_print,
sync::atomic::{ordering::Relaxed, Atomic},
sync::poll::{PollCondVar, PollTable},
- sync::{Arc, SpinLock},
+ sync::{aref::ARef, Arc, SpinLock},
task::Task,
- types::ARef,
uaccess::UserSlice,
uapi,
};
@@ -1146,6 +1145,7 @@ impl Thread {
transaction: &DArc<Transaction>,
) -> bool {
if let Ok(transaction) = &reply {
+ crate::trace::trace_transaction(true, transaction, Some(&self.task));
transaction.set_outstanding(&mut self.process.inner.lock());
}
diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs
index af0e4392805e..9839901c7151 100644
--- a/drivers/android/binder/trace.rs
+++ b/drivers/android/binder/trace.rs
@@ -2,11 +2,21 @@
// Copyright (C) 2025 Google LLC.
+use crate::transaction::Transaction;
+
+use kernel::bindings::{rust_binder_transaction, task_struct};
use kernel::ffi::{c_uint, c_ulong};
+use kernel::task::Task;
use kernel::tracepoint::declare_trace;
declare_trace! {
unsafe fn rust_binder_ioctl(cmd: c_uint, arg: c_ulong);
+ unsafe fn rust_binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct);
+}
+
+#[inline]
+fn raw_transaction(t: &Transaction) -> rust_binder_transaction {
+ t as *const Transaction as rust_binder_transaction
}
#[inline]
@@ -14,3 +24,14 @@ pub(crate) fn trace_ioctl(cmd: u32, arg: usize) {
// SAFETY: Always safe to call.
unsafe { rust_binder_ioctl(cmd, arg as c_ulong) }
}
+
+#[inline]
+pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Task>) {
+ let thread = match thread {
+ Some(thread) => thread.as_ptr(),
+ None => core::ptr::null_mut(),
+ };
+ // SAFETY: The raw transaction is valid for the duration of this call. The thread pointer is
+ // valid or null.
+ unsafe { rust_binder_transaction(reply, raw_transaction(t), thread) }
+}
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 2273a8e9d01c..75e6f5fbaaae 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -24,6 +24,17 @@ use crate::{
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
};
+use core::mem::offset_of;
+use kernel::bindings::rb_transaction_layout;
+pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout = rb_transaction_layout {
+ debug_id: offset_of!(Transaction, debug_id),
+ code: offset_of!(Transaction, code),
+ flags: offset_of!(Transaction, flags),
+ from_thread: offset_of!(Transaction, from),
+ to_proc: offset_of!(Transaction, to),
+ target_node: offset_of!(Transaction, target_node),
+};
+
#[pin_data(PinnedDrop)]
pub(crate) struct Transaction {
pub(crate) debug_id: usize,
@@ -249,6 +260,7 @@ impl Transaction {
if oneway {
if let Some(target_node) = self.target_node.clone() {
+ crate::trace::trace_transaction(false, &self, None);
if process_inner.is_frozen.is_frozen() {
process_inner.async_recv = true;
if self.flags & TF_UPDATE_TXN != 0 {
@@ -286,11 +298,13 @@ impl Transaction {
}
let res = if let Some(thread) = self.find_target_thread() {
+ crate::trace::trace_transaction(false, &self, Some(&thread.task));
match thread.push_work(self) {
PushWorkRes::Ok => Ok(()),
PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
}
} else {
+ crate::trace::trace_transaction(false, &self, None);
process_inner.push_work(self)
};
drop(process_inner);
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 145ed5f14cdb..67a068c075c0 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -81,7 +81,7 @@ static void binder_insert_free_buffer(struct binder_alloc *alloc,
new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer);
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: add free buffer, size %zd, at %pK\n",
+ "%d: add free buffer, size %zd, at %p\n",
alloc->pid, new_buffer_size, new_buffer);
while (*p) {
@@ -572,7 +572,7 @@ static struct binder_buffer *binder_alloc_new_buf_locked(
}
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n",
+ "%d: binder_alloc_buf size %zd got buffer %p size %zd\n",
alloc->pid, size, buffer, buffer_size);
/*
@@ -748,7 +748,7 @@ static void binder_free_buf_locked(struct binder_alloc *alloc,
ALIGN(buffer->extra_buffers_size, sizeof(void *));
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_free_buf %pK size %zd buffer_size %zd\n",
+ "%d: binder_free_buf %p size %zd buffer_size %zd\n",
alloc->pid, buffer, size, buffer_size);
BUG_ON(buffer->free);
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index 007223549887..290ce13bcb09 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -432,10 +432,9 @@ const struct device_type fsl_mc_bus_dpdmai_type = {
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmai_type);
-const struct device_type fsl_mc_bus_dpdbg_type = {
+static const struct device_type fsl_mc_bus_dpdbg_type = {
.name = "fsl_mc_bus_dpdbg"
};
-EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdbg_type);
static const struct device_type *fsl_mc_get_device_type(const char *type)
{
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 3c208b5c8446..35922e7a24c1 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -1596,7 +1596,7 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
}
EXPORT_SYMBOL_GPL(mhi_ep_unregister_controller);
-static int mhi_ep_driver_probe(struct device *dev)
+static int mhi_ep_probe(struct device *dev)
{
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
@@ -1609,7 +1609,7 @@ static int mhi_ep_driver_probe(struct device *dev)
return mhi_drv->probe(mhi_dev, mhi_dev->id);
}
-static int mhi_ep_driver_remove(struct device *dev)
+static void mhi_ep_remove(struct device *dev)
{
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
@@ -1619,7 +1619,7 @@ static int mhi_ep_driver_remove(struct device *dev)
/* Skip if it is a controller device */
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
- return 0;
+ return;
/* Disconnect the channels associated with the driver */
for (dir = 0; dir < 2; dir++) {
@@ -1643,8 +1643,6 @@ static int mhi_ep_driver_remove(struct device *dev)
/* Remove the client driver now */
mhi_drv->remove(mhi_dev);
-
- return 0;
}
int __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner)
@@ -1660,8 +1658,6 @@ int __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner
driver->bus = &mhi_ep_bus_type;
driver->owner = owner;
- driver->probe = mhi_ep_driver_probe;
- driver->remove = mhi_ep_driver_remove;
return driver_register(driver);
}
@@ -1708,6 +1704,8 @@ const struct bus_type mhi_ep_bus_type = {
.dev_name = "mhi_ep",
.match = mhi_ep_match,
.uevent = mhi_ep_uevent,
+ .probe = mhi_ep_probe,
+ .remove = mhi_ep_remove,
};
static int __init mhi_ep_init(void)
diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c
index 205d83ac069f..c76db35bc29a 100644
--- a/drivers/bus/mhi/host/boot.c
+++ b/drivers/bus/mhi/host/boot.c
@@ -584,6 +584,16 @@ skip_req_fw:
* device transitioning into MHI READY state
*/
if (fw_load_type == MHI_FW_LOAD_FBC) {
+ /*
+ * Some FW combine two separate ELF images (SBL + WLAN FW) in a single
+ * file. Hence, check for the existence of the second ELF header after
+ * SBL. If present, load the second image separately.
+ */
+ if (!memcmp(fw_data + mhi_cntrl->sbl_size, ELFMAG, SELFMAG)) {
+ fw_data += mhi_cntrl->sbl_size;
+ fw_sz -= mhi_cntrl->sbl_size;
+ }
+
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
if (ret) {
release_firmware(firmware);
diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c
index 099be8dd1900..c47f66833cda 100644
--- a/drivers/bus/mhi/host/init.c
+++ b/drivers/bus/mhi/host/init.c
@@ -841,19 +841,9 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
mhi_chan->lpm_notify = ch_cfg->lpm_notify;
mhi_chan->offload_ch = ch_cfg->offload_channel;
mhi_chan->db_cfg.reset_req = ch_cfg->doorbell_mode_switch;
- mhi_chan->pre_alloc = ch_cfg->auto_queue;
mhi_chan->wake_capable = ch_cfg->wake_capable;
/*
- * If MHI host allocates buffers, then the channel direction
- * should be DMA_FROM_DEVICE
- */
- if (mhi_chan->pre_alloc && mhi_chan->dir != DMA_FROM_DEVICE) {
- dev_err(dev, "Invalid channel configuration\n");
- goto error_chan_cfg;
- }
-
- /*
* Bi-directional and direction less channel must be an
* offload channel
*/
@@ -1265,7 +1255,7 @@ struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl)
return mhi_dev;
}
-static int mhi_driver_probe(struct device *dev)
+static int mhi_probe(struct device *dev)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
@@ -1341,7 +1331,7 @@ exit_probe:
return ret;
}
-static int mhi_driver_remove(struct device *dev)
+static void mhi_remove(struct device *dev)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_driver *mhi_drv = to_mhi_driver(dev->driver);
@@ -1355,7 +1345,7 @@ static int mhi_driver_remove(struct device *dev)
/* Skip if it is a controller device */
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
- return 0;
+ return;
/* Reset both channels */
for (dir = 0; dir < 2; dir++) {
@@ -1407,8 +1397,6 @@ static int mhi_driver_remove(struct device *dev)
while (mhi_dev->dev_wake)
mhi_device_put(mhi_dev);
-
- return 0;
}
int __mhi_driver_register(struct mhi_driver *mhi_drv, struct module *owner)
@@ -1420,8 +1408,6 @@ int __mhi_driver_register(struct mhi_driver *mhi_drv, struct module *owner)
driver->bus = &mhi_bus_type;
driver->owner = owner;
- driver->probe = mhi_driver_probe;
- driver->remove = mhi_driver_remove;
return driver_register(driver);
}
@@ -1468,6 +1454,8 @@ const struct bus_type mhi_bus_type = {
.dev_name = "mhi",
.match = mhi_match,
.uevent = mhi_uevent,
+ .probe = mhi_probe,
+ .remove = mhi_remove,
.dev_groups = mhi_dev_groups,
};
diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
index 7937bb1f742c..7b0ee5e3a12d 100644
--- a/drivers/bus/mhi/host/internal.h
+++ b/drivers/bus/mhi/host/internal.h
@@ -286,7 +286,6 @@ struct mhi_chan {
bool lpm_notify;
bool configured;
bool offload_ch;
- bool pre_alloc;
bool wake_capable;
};
@@ -389,8 +388,6 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
-/* Automatically allocate and queue inbound buffers */
-#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0)
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index 861551274319..53c0ffe30070 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -664,23 +664,6 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
mhi_cntrl->runtime_put(mhi_cntrl);
}
- /*
- * Recycle the buffer if buffer is pre-allocated,
- * if there is an error, not much we can do apart
- * from dropping the packet
- */
- if (mhi_chan->pre_alloc) {
- if (mhi_queue_buf(mhi_chan->mhi_dev,
- mhi_chan->dir,
- buf_info->cb_buf,
- buf_info->len, MHI_EOT)) {
- dev_err(dev,
- "Error recycling buffer for chan:%d\n",
- mhi_chan->chan);
- kfree(buf_info->cb_buf);
- }
- }
-
read_lock_bh(&mhi_chan->lock);
}
break;
@@ -1177,17 +1160,12 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info,
int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
struct sk_buff *skb, size_t len, enum mhi_flags mflags)
{
- struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
- mhi_dev->dl_chan;
struct mhi_buf_info buf_info = { };
buf_info.v_addr = skb->data;
buf_info.cb_buf = skb;
buf_info.len = len;
- if (unlikely(mhi_chan->pre_alloc))
- return -EINVAL;
-
return mhi_queue(mhi_dev, &buf_info, dir, mflags);
}
EXPORT_SYMBOL_GPL(mhi_queue_skb);
@@ -1472,45 +1450,6 @@ static int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
if (ret)
goto error_pm_state;
- if (mhi_chan->dir == DMA_FROM_DEVICE)
- mhi_chan->pre_alloc = !!(flags & MHI_CH_INBOUND_ALLOC_BUFS);
-
- /* Pre-allocate buffer for xfer ring */
- if (mhi_chan->pre_alloc) {
- int nr_el = get_nr_avail_ring_elements(mhi_cntrl,
- &mhi_chan->tre_ring);
- size_t len = mhi_cntrl->buffer_len;
-
- while (nr_el--) {
- void *buf;
- struct mhi_buf_info info = { };
-
- buf = kmalloc(len, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto error_pre_alloc;
- }
-
- /* Prepare transfer descriptors */
- info.v_addr = buf;
- info.cb_buf = buf;
- info.len = len;
- ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &info, MHI_EOT);
- if (ret) {
- kfree(buf);
- goto error_pre_alloc;
- }
- }
-
- read_lock_bh(&mhi_cntrl->pm_lock);
- if (MHI_DB_ACCESS_VALID(mhi_cntrl)) {
- read_lock_irq(&mhi_chan->lock);
- mhi_ring_chan_db(mhi_cntrl, mhi_chan);
- read_unlock_irq(&mhi_chan->lock);
- }
- read_unlock_bh(&mhi_cntrl->pm_lock);
- }
-
mutex_unlock(&mhi_chan->mutex);
return 0;
@@ -1523,12 +1462,6 @@ error_init_chan:
mutex_unlock(&mhi_chan->mutex);
return ret;
-
-error_pre_alloc:
- mutex_unlock(&mhi_chan->mutex);
- mhi_unprepare_channel(mhi_cntrl, mhi_chan);
-
- return ret;
}
static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl,
@@ -1600,12 +1533,8 @@ static void mhi_reset_data_chan(struct mhi_controller *mhi_cntrl,
mhi_del_ring_element(mhi_cntrl, buf_ring);
mhi_del_ring_element(mhi_cntrl, tre_ring);
- if (mhi_chan->pre_alloc) {
- kfree(buf_info->cb_buf);
- } else {
- result.buf_addr = buf_info->cb_buf;
- mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
- }
+ result.buf_addr = buf_info->cb_buf;
+ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
}
}
@@ -1666,12 +1595,6 @@ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev)
}
EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer);
-int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
-{
- return __mhi_prepare_for_transfer(mhi_dev, MHI_CH_INBOUND_ALLOC_BUFS);
-}
-EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
-
void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index e3bc737313a2..0884a384b77f 100644
--- a/drivers/bus/mhi/host/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -94,22 +94,6 @@ struct mhi_pci_dev_info {
.doorbell_mode_switch = false, \
}
-#define MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(ch_num, ch_name, el_count, ev_ring) \
- { \
- .num = ch_num, \
- .name = ch_name, \
- .num_elements = el_count, \
- .event_ring = ev_ring, \
- .dir = DMA_FROM_DEVICE, \
- .ee_mask = BIT(MHI_EE_AMSS), \
- .pollcfg = 0, \
- .doorbell = MHI_DB_BRST_DISABLE, \
- .lpm_notify = false, \
- .offload_channel = false, \
- .doorbell_mode_switch = false, \
- .auto_queue = true, \
- }
-
#define MHI_EVENT_CONFIG_CTRL(ev_ring, el_count) \
{ \
.num_elements = el_count, \
@@ -329,7 +313,7 @@ static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
MHI_CHANNEL_CONFIG_UL(14, "QMI", 4, 0),
MHI_CHANNEL_CONFIG_DL(15, "QMI", 4, 0),
MHI_CHANNEL_CONFIG_UL(20, "IPCR", 8, 0),
- MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(21, "IPCR", 8, 0),
+ MHI_CHANNEL_CONFIG_DL(21, "IPCR", 8, 0),
MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 64, 2),
@@ -762,7 +746,7 @@ static const struct mhi_channel_config mhi_telit_fn980_hw_v1_channels[] = {
MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0),
MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0),
MHI_CHANNEL_CONFIG_UL(20, "IPCR", 16, 0),
- MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(21, "IPCR", 16, 0),
+ MHI_CHANNEL_CONFIG_DL(21, "IPCR", 16, 0),
MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 1),
MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 2),
};
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index d2cfc584e202..2a3a37b2cf3c 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -249,32 +249,6 @@ config SONYPI
To compile this driver as a module, choose M here: the
module will be called sonypi.
-config MWAVE
- tristate "ACP Modem (Mwave) support"
- depends on X86 && TTY
- select SERIAL_8250
- help
- The ACP modem (Mwave) for Linux is a WinModem. It is composed of a
- kernel driver and a user level application. Together these components
- support direct attachment to public switched telephone networks (PSTNs)
- and support selected world wide countries.
-
- This version of the ACP Modem driver supports the IBM Thinkpad 600E,
- 600, and 770 that include on board ACP modem hardware.
-
- The modem also supports the standard communications port interface
- (ttySx) and is compatible with the Hayes AT Command Set.
-
- The user level application needed to use this driver can be found at
- the IBM Linux Technology Center (LTC) web site:
- <http://www.ibm.com/linux/ltc/>.
-
- If you own one of the above IBM Thinkpads which has the Mwave chipset
- in it, say Y.
-
- To compile this driver as a module, choose M here: the
- module will be called mwave.
-
config SCx200_GPIO
tristate "NatSemi SCx200 GPIO Support"
depends on SCx200
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 1291369b9126..47bdc882797a 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -33,7 +33,6 @@ obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o
obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
obj-$(CONFIG_TELCLOCK) += tlclk.o
-obj-$(CONFIG_MWAVE) += mwave/
obj-y += agp/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
diff --git a/drivers/char/misc_minor_kunit.c b/drivers/char/misc_minor_kunit.c
index 6fc8b05169c5..e930c78e1ef9 100644
--- a/drivers/char/misc_minor_kunit.c
+++ b/drivers/char/misc_minor_kunit.c
@@ -166,7 +166,7 @@ static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *
KUNIT_FAIL(test, "failed to create node\n");
filp = filp_open(devname, O_RDONLY, 0);
- if (IS_ERR_OR_NULL(filp))
+ if (IS_ERR(filp))
KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp));
else
fput(filp);
diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c
deleted file mode 100644
index 90f93cefb21c..000000000000
--- a/drivers/char/mwave/3780i.c
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
-*
-* 3780i.c -- helper routines for the 3780i DSP
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#define pr_fmt(fmt) "3780i: " fmt
-
-#include <linux/kernel.h>
-#include <linux/unistd.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-#include <linux/sched.h> /* cond_resched() */
-
-#include <asm/io.h>
-#include <linux/uaccess.h>
-#include <asm/irq.h>
-#include "smapi.h"
-#include "mwavedd.h"
-#include "3780i.h"
-
-static DEFINE_SPINLOCK(dsp_lock);
-
-static void PaceMsaAccess(unsigned short usDspBaseIO)
-{
- cond_resched();
- udelay(100);
- cond_resched();
-}
-
-unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO,
- unsigned long ulMsaAddr)
-{
- unsigned long flags;
- unsigned short val;
-
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr);
- OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16));
- val = InWordDsp(DSP_MsaDataDSISHigh);
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- return val;
-}
-
-void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO,
- unsigned long ulMsaAddr, unsigned short usValue)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr);
- OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16));
- OutWordDsp(DSP_MsaDataDSISHigh, usValue);
- spin_unlock_irqrestore(&dsp_lock, flags);
-}
-
-static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex,
- unsigned char ucValue)
-{
- DSP_ISA_SLAVE_CONTROL rSlaveControl;
- DSP_ISA_SLAVE_CONTROL rSlaveControl_Save;
-
- MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl);
-
- rSlaveControl_Save = rSlaveControl;
- rSlaveControl.ConfigMode = true;
-
- OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl));
- OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex);
- OutByteDsp(DSP_ConfigData, ucValue);
- OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl_Save));
-}
-
-int dsp3780I_EnableDSP(struct dsp_3780i_config_settings *pSettings,
- unsigned short *pIrqMap,
- unsigned short *pDmaMap)
-{
- unsigned long flags;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- int i;
- DSP_UART_CFG_1 rUartCfg1;
- DSP_UART_CFG_2 rUartCfg2;
- DSP_HBRIDGE_CFG_1 rHBridgeCfg1;
- DSP_HBRIDGE_CFG_2 rHBridgeCfg2;
- DSP_BUSMASTER_CFG_1 rBusmasterCfg1;
- DSP_BUSMASTER_CFG_2 rBusmasterCfg2;
- DSP_ISA_PROT_CFG rIsaProtCfg;
- DSP_POWER_MGMT_CFG rPowerMgmtCfg;
- DSP_HBUS_TIMER_CFG rHBusTimerCfg;
- DSP_LBUS_TIMEOUT_DISABLE rLBusTimeoutDisable;
- DSP_CHIP_RESET rChipReset;
- DSP_CLOCK_CONTROL_1 rClockControl1;
- DSP_CLOCK_CONTROL_2 rClockControl2;
- DSP_ISA_SLAVE_CONTROL rSlaveControl;
- DSP_HBRIDGE_CONTROL rHBridgeControl;
- unsigned short tval;
-
- if (!pSettings->bDSPEnabled) {
- pr_err("%s: Error: DSP not enabled. Aborting.\n", __func__);
- return -EIO;
- }
-
- if (pSettings->bModemEnabled) {
- rUartCfg1.Reserved = rUartCfg2.Reserved = 0;
- rUartCfg1.IrqActiveLow = pSettings->bUartIrqActiveLow;
- rUartCfg1.IrqPulse = pSettings->bUartIrqPulse;
- rUartCfg1.Irq =
- (unsigned char) pIrqMap[pSettings->usUartIrq];
- switch (pSettings->usUartBaseIO) {
- case 0x03F8:
- rUartCfg1.BaseIO = 0;
- break;
- case 0x02F8:
- rUartCfg1.BaseIO = 1;
- break;
- case 0x03E8:
- rUartCfg1.BaseIO = 2;
- break;
- case 0x02E8:
- rUartCfg1.BaseIO = 3;
- break;
- }
- rUartCfg2.Enable = true;
- }
-
- rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0;
- rHBridgeCfg1.IrqActiveLow = pSettings->bDspIrqActiveLow;
- rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse;
- rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq];
- rHBridgeCfg1.AccessMode = 1;
- rHBridgeCfg2.Enable = true;
-
-
- rBusmasterCfg2.Reserved = 0;
- rBusmasterCfg1.Dma = (unsigned char) pDmaMap[pSettings->usDspDma];
- rBusmasterCfg1.NumTransfers =
- (unsigned char) pSettings->usNumTransfers;
- rBusmasterCfg1.ReRequest = (unsigned char) pSettings->usReRequest;
- rBusmasterCfg1.MEMCS16 = pSettings->bEnableMEMCS16;
- rBusmasterCfg2.IsaMemCmdWidth =
- (unsigned char) pSettings->usIsaMemCmdWidth;
-
-
- rIsaProtCfg.Reserved = 0;
- rIsaProtCfg.GateIOCHRDY = pSettings->bGateIOCHRDY;
-
- rPowerMgmtCfg.Reserved = 0;
- rPowerMgmtCfg.Enable = pSettings->bEnablePwrMgmt;
-
- rHBusTimerCfg.LoadValue =
- (unsigned char) pSettings->usHBusTimerLoadValue;
-
- rLBusTimeoutDisable.Reserved = 0;
- rLBusTimeoutDisable.DisableTimeout =
- pSettings->bDisableLBusTimeout;
-
- MKWORD(rChipReset) = ~pSettings->usChipletEnable;
-
- rClockControl1.Reserved1 = rClockControl1.Reserved2 = 0;
- rClockControl1.N_Divisor = pSettings->usN_Divisor;
- rClockControl1.M_Multiplier = pSettings->usM_Multiplier;
-
- rClockControl2.Reserved = 0;
- rClockControl2.PllBypass = pSettings->bPllBypass;
-
- /* Issue a soft reset to the chip */
- /* Note: Since we may be coming in with 3780i clocks suspended, we must keep
- * soft-reset active for 10ms.
- */
- rSlaveControl.ClockControl = 0;
- rSlaveControl.SoftReset = true;
- rSlaveControl.ConfigMode = false;
- rSlaveControl.Reserved = 0;
-
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
- MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl);
-
- for (i = 0; i < 11; i++)
- udelay(2000);
-
- rSlaveControl.SoftReset = false;
- OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
-
- MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl);
-
- /* Program our general configuration registers */
- WriteGenCfg(DSP_HBridgeCfg1Index, MKBYTE(rHBridgeCfg1));
- WriteGenCfg(DSP_HBridgeCfg2Index, MKBYTE(rHBridgeCfg2));
- WriteGenCfg(DSP_BusMasterCfg1Index, MKBYTE(rBusmasterCfg1));
- WriteGenCfg(DSP_BusMasterCfg2Index, MKBYTE(rBusmasterCfg2));
- WriteGenCfg(DSP_IsaProtCfgIndex, MKBYTE(rIsaProtCfg));
- WriteGenCfg(DSP_PowerMgCfgIndex, MKBYTE(rPowerMgmtCfg));
- WriteGenCfg(DSP_HBusTimerCfgIndex, MKBYTE(rHBusTimerCfg));
-
- if (pSettings->bModemEnabled) {
- WriteGenCfg(DSP_UartCfg1Index, MKBYTE(rUartCfg1));
- WriteGenCfg(DSP_UartCfg2Index, MKBYTE(rUartCfg2));
- }
-
-
- rHBridgeControl.EnableDspInt = false;
- rHBridgeControl.MemAutoInc = true;
- rHBridgeControl.IoAutoInc = false;
- rHBridgeControl.DiagnosticMode = false;
-
- OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
- spin_unlock_irqrestore(&dsp_lock, flags);
- WriteMsaCfg(DSP_LBusTimeoutDisable, MKWORD(rLBusTimeoutDisable));
- WriteMsaCfg(DSP_ClockControl_1, MKWORD(rClockControl1));
- WriteMsaCfg(DSP_ClockControl_2, MKWORD(rClockControl2));
- WriteMsaCfg(DSP_ChipReset, MKWORD(rChipReset));
-
- ReadMsaCfg(DSP_ChipID);
-
- return 0;
-}
-
-int dsp3780I_DisableDSP(struct dsp_3780i_config_settings *pSettings)
-{
- unsigned long flags;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- DSP_ISA_SLAVE_CONTROL rSlaveControl;
-
- rSlaveControl.ClockControl = 0;
- rSlaveControl.SoftReset = true;
- rSlaveControl.ConfigMode = false;
- rSlaveControl.Reserved = 0;
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
-
- udelay(5);
-
- rSlaveControl.ClockControl = 1;
- OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- udelay(5);
-
- return 0;
-}
-
-int dsp3780I_Reset(struct dsp_3780i_config_settings *pSettings)
-{
- unsigned long flags;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- DSP_BOOT_DOMAIN rBootDomain;
- DSP_HBRIDGE_CONTROL rHBridgeControl;
-
- spin_lock_irqsave(&dsp_lock, flags);
- /* Mask DSP to PC interrupt */
- MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
-
- rHBridgeControl.EnableDspInt = false;
- OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- /* Reset the core via the boot domain register */
- rBootDomain.ResetCore = true;
- rBootDomain.Halt = true;
- rBootDomain.NMI = true;
- rBootDomain.Reserved = 0;
-
- WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
-
- /* Reset all the chiplets and then reactivate them */
- WriteMsaCfg(DSP_ChipReset, 0xFFFF);
- udelay(5);
- WriteMsaCfg(DSP_ChipReset,
- (unsigned short) (~pSettings->usChipletEnable));
-
- return 0;
-}
-
-
-int dsp3780I_Run(struct dsp_3780i_config_settings *pSettings)
-{
- unsigned long flags;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- DSP_BOOT_DOMAIN rBootDomain;
- DSP_HBRIDGE_CONTROL rHBridgeControl;
-
- /* Transition the core to a running state */
- rBootDomain.ResetCore = true;
- rBootDomain.Halt = false;
- rBootDomain.NMI = true;
- rBootDomain.Reserved = 0;
- WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
-
- udelay(5);
-
- rBootDomain.ResetCore = false;
- WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
- udelay(5);
-
- rBootDomain.NMI = false;
- WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
- udelay(5);
-
- /* Enable DSP to PC interrupt */
- spin_lock_irqsave(&dsp_lock, flags);
- MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
- rHBridgeControl.EnableDspInt = true;
-
- OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- return 0;
-}
-
-
-int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr)
-{
- unsigned long flags;
- unsigned short __user *pusBuffer = pvBuffer;
- unsigned short val;
-
- /* Set the initial MSA address. No adjustments need to be made to data store addresses */
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
- OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- /* Transfer the memory block */
- while (uCount-- != 0) {
- spin_lock_irqsave(&dsp_lock, flags);
- val = InWordDsp(DSP_MsaDataDSISHigh);
- spin_unlock_irqrestore(&dsp_lock, flags);
- if(put_user(val, pusBuffer++))
- return -EFAULT;
-
- PaceMsaAccess(usDspBaseIO);
- }
-
- return 0;
-}
-
-int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
- void __user *pvBuffer, unsigned uCount,
- unsigned long ulDSPAddr)
-{
- unsigned long flags;
- unsigned short __user *pusBuffer = pvBuffer;
- unsigned short val;
-
- /* Set the initial MSA address. No adjustments need to be made to data store addresses */
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
- OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- /* Transfer the memory block */
- while (uCount-- != 0) {
- spin_lock_irqsave(&dsp_lock, flags);
- val = InWordDsp(DSP_ReadAndClear);
- spin_unlock_irqrestore(&dsp_lock, flags);
- if(put_user(val, pusBuffer++))
- return -EFAULT;
-
- PaceMsaAccess(usDspBaseIO);
- }
-
- return 0;
-}
-
-
-int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr)
-{
- unsigned long flags;
- unsigned short __user *pusBuffer = pvBuffer;
-
- /* Set the initial MSA address. No adjustments need to be made to data store addresses */
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
- OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- /* Transfer the memory block */
- while (uCount-- != 0) {
- unsigned short val;
- if(get_user(val, pusBuffer++))
- return -EFAULT;
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaDataDSISHigh, val);
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- PaceMsaAccess(usDspBaseIO);
- }
-
- return 0;
-}
-
-
-int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr)
-{
- unsigned long flags;
- unsigned short __user *pusBuffer = pvBuffer;
-
- /*
- * Set the initial MSA address. To convert from an instruction store
- * address to an MSA address
- * shift the address two bits to the left and set bit 22
- */
- ulDSPAddr = (ulDSPAddr << 2) | (1 << 22);
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
- OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- /* Transfer the memory block */
- while (uCount-- != 0) {
- unsigned short val_lo, val_hi;
- spin_lock_irqsave(&dsp_lock, flags);
- val_lo = InWordDsp(DSP_MsaDataISLow);
- val_hi = InWordDsp(DSP_MsaDataDSISHigh);
- spin_unlock_irqrestore(&dsp_lock, flags);
- if(put_user(val_lo, pusBuffer++))
- return -EFAULT;
- if(put_user(val_hi, pusBuffer++))
- return -EFAULT;
-
- PaceMsaAccess(usDspBaseIO);
-
- }
-
- return 0;
-}
-
-
-int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr)
-{
- unsigned long flags;
- unsigned short __user *pusBuffer = pvBuffer;
-
- /*
- * Set the initial MSA address. To convert from an instruction store
- * address to an MSA address
- * shift the address two bits to the left and set bit 22
- */
- ulDSPAddr = (ulDSPAddr << 2) | (1 << 22);
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
- OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- /* Transfer the memory block */
- while (uCount-- != 0) {
- unsigned short val_lo, val_hi;
- if(get_user(val_lo, pusBuffer++))
- return -EFAULT;
- if(get_user(val_hi, pusBuffer++))
- return -EFAULT;
- spin_lock_irqsave(&dsp_lock, flags);
- OutWordDsp(DSP_MsaDataISLow, val_lo);
- OutWordDsp(DSP_MsaDataDSISHigh, val_hi);
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- PaceMsaAccess(usDspBaseIO);
- }
-
- return 0;
-}
-
-
-int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
- unsigned short *pusIPCSource)
-{
- unsigned long flags;
- DSP_HBRIDGE_CONTROL rHBridgeControl;
-
- /*
- * Disable DSP to PC interrupts, read the interrupt register,
- * clear the pending IPC bits, and reenable DSP to PC interrupts
- */
- spin_lock_irqsave(&dsp_lock, flags);
- MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
- rHBridgeControl.EnableDspInt = false;
- OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
-
- *pusIPCSource = InWordDsp(DSP_Interrupt);
- OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource));
-
- rHBridgeControl.EnableDspInt = true;
- OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
- spin_unlock_irqrestore(&dsp_lock, flags);
-
- return 0;
-}
diff --git a/drivers/char/mwave/3780i.h b/drivers/char/mwave/3780i.h
deleted file mode 100644
index 53dafceb20e0..000000000000
--- a/drivers/char/mwave/3780i.h
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
-*
-* 3780i.h -- declarations for 3780i.c
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#ifndef _LINUX_3780I_H
-#define _LINUX_3780I_H
-
-#include <asm/io.h>
-
-/* DSP I/O port offsets and definitions */
-#define DSP_IsaSlaveControl 0x0000 /* ISA slave control register */
-#define DSP_IsaSlaveStatus 0x0001 /* ISA slave status register */
-#define DSP_ConfigAddress 0x0002 /* General config address register */
-#define DSP_ConfigData 0x0003 /* General config data register */
-#define DSP_HBridgeControl 0x0002 /* HBridge control register */
-#define DSP_MsaAddrLow 0x0004 /* MSP System Address, low word */
-#define DSP_MsaAddrHigh 0x0006 /* MSP System Address, high word */
-#define DSP_MsaDataDSISHigh 0x0008 /* MSA data register: d-store word or high byte of i-store */
-#define DSP_MsaDataISLow 0x000A /* MSA data register: low word of i-store */
-#define DSP_ReadAndClear 0x000C /* MSA read and clear data register */
-#define DSP_Interrupt 0x000E /* Interrupt register (IPC source) */
-
-typedef struct {
- unsigned char ClockControl:1; /* RW: Clock control: 0=normal, 1=stop 3780i clocks */
- unsigned char SoftReset:1; /* RW: Soft reset 0=normal, 1=soft reset active */
- unsigned char ConfigMode:1; /* RW: Configuration mode, 0=normal, 1=config mode */
- unsigned short Reserved:13; /* 0: Reserved */
-} DSP_ISA_SLAVE_CONTROL;
-
-
-typedef struct {
- unsigned short EnableDspInt:1; /* RW: Enable DSP to X86 ISA interrupt 0=mask it, 1=enable it */
- unsigned short MemAutoInc:1; /* RW: Memory address auto increment, 0=disable, 1=enable */
- unsigned short IoAutoInc:1; /* RW: I/O address auto increment, 0=disable, 1=enable */
- unsigned short DiagnosticMode:1; /* RW: Disgnostic mode 0=nromal, 1=diagnostic mode */
- unsigned short IsaPacingTimer:12; /* R: ISA access pacing timer: count of core cycles stolen */
-} DSP_HBRIDGE_CONTROL;
-
-
-/* DSP register indexes used with the configuration register address (index) register */
-#define DSP_UartCfg1Index 0x0003 /* UART config register 1 */
-#define DSP_UartCfg2Index 0x0004 /* UART config register 2 */
-#define DSP_HBridgeCfg1Index 0x0007 /* HBridge config register 1 */
-#define DSP_HBridgeCfg2Index 0x0008 /* HBridge config register 2 */
-#define DSP_BusMasterCfg1Index 0x0009 /* ISA bus master config register 1 */
-#define DSP_BusMasterCfg2Index 0x000A /* ISA bus master config register 2 */
-#define DSP_IsaProtCfgIndex 0x000F /* ISA protocol control register */
-#define DSP_PowerMgCfgIndex 0x0010 /* Low poser suspend/resume enable */
-#define DSP_HBusTimerCfgIndex 0x0011 /* HBUS timer load value */
-
-typedef struct {
- unsigned char IrqActiveLow:1; /* RW: IRQ active high or low: 0=high, 1=low */
- unsigned char IrqPulse:1; /* RW: IRQ pulse or level: 0=level, 1=pulse */
- unsigned char Irq:3; /* RW: IRQ selection */
- unsigned char BaseIO:2; /* RW: Base I/O selection */
- unsigned char Reserved:1; /* 0: Reserved */
-} DSP_UART_CFG_1;
-
-typedef struct {
- unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=false, 1=true */
- unsigned char Reserved:7; /* 0: Reserved */
-} DSP_UART_CFG_2;
-
-typedef struct {
- unsigned char IrqActiveLow:1; /* RW: IRQ active high=0 or low=1 */
- unsigned char IrqPulse:1; /* RW: IRQ pulse=1 or level=0 */
- unsigned char Irq:3; /* RW: IRQ selection */
- unsigned char AccessMode:1; /* RW: 16-bit register access method 0=byte, 1=word */
- unsigned char Reserved:2; /* 0: Reserved */
-} DSP_HBRIDGE_CFG_1;
-
-typedef struct {
- unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=false, 1=true */
- unsigned char Reserved:7; /* 0: Reserved */
-} DSP_HBRIDGE_CFG_2;
-
-
-typedef struct {
- unsigned char Dma:3; /* RW: DMA channel selection */
- unsigned char NumTransfers:2; /* RW: Maximum # of transfers once being granted the ISA bus */
- unsigned char ReRequest:2; /* RW: Minimum delay between releasing the ISA bus and requesting it again */
- unsigned char MEMCS16:1; /* RW: ISA signal MEMCS16: 0=disabled, 1=enabled */
-} DSP_BUSMASTER_CFG_1;
-
-typedef struct {
- unsigned char IsaMemCmdWidth:2; /* RW: ISA memory command width */
- unsigned char Reserved:6; /* 0: Reserved */
-} DSP_BUSMASTER_CFG_2;
-
-
-typedef struct {
- unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=false, 1=true */
- unsigned char Reserved:7; /* 0: Reserved */
-} DSP_ISA_PROT_CFG;
-
-typedef struct {
- unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=false, 1=true */
- unsigned char Reserved:7; /* 0: Reserved */
-} DSP_POWER_MGMT_CFG;
-
-typedef struct {
- unsigned char LoadValue:8; /* RW: HBUS timer load value */
-} DSP_HBUS_TIMER_CFG;
-
-
-
-/* DSP registers that exist in MSA I/O space */
-#define DSP_ChipID 0x80000000
-#define DSP_MspBootDomain 0x80000580
-#define DSP_LBusTimeoutDisable 0x80000580
-#define DSP_ClockControl_1 0x8000058A
-#define DSP_ClockControl_2 0x8000058C
-#define DSP_ChipReset 0x80000588
-#define DSP_GpioModeControl_15_8 0x80000082
-#define DSP_GpioDriverEnable_15_8 0x80000076
-#define DSP_GpioOutputData_15_8 0x80000072
-
-typedef struct {
- unsigned short NMI:1; /* RW: non maskable interrupt */
- unsigned short Halt:1; /* RW: Halt MSP clock */
- unsigned short ResetCore:1; /* RW: Reset MSP core interface */
- unsigned short Reserved:13; /* 0: Reserved */
-} DSP_BOOT_DOMAIN;
-
-typedef struct {
- unsigned short DisableTimeout:1; /* RW: Disable LBus timeout */
- unsigned short Reserved:15; /* 0: Reserved */
-} DSP_LBUS_TIMEOUT_DISABLE;
-
-typedef struct {
- unsigned short Memory:1; /* RW: Reset memory interface */
- unsigned short SerialPort1:1; /* RW: Reset serial port 1 interface */
- unsigned short SerialPort2:1; /* RW: Reset serial port 2 interface */
- unsigned short SerialPort3:1; /* RW: Reset serial port 3 interface */
- unsigned short Gpio:1; /* RW: Reset GPIO interface */
- unsigned short Dma:1; /* RW: Reset DMA interface */
- unsigned short SoundBlaster:1; /* RW: Reset soundblaster interface */
- unsigned short Uart:1; /* RW: Reset UART interface */
- unsigned short Midi:1; /* RW: Reset MIDI interface */
- unsigned short IsaMaster:1; /* RW: Reset ISA master interface */
- unsigned short Reserved:6; /* 0: Reserved */
-} DSP_CHIP_RESET;
-
-typedef struct {
- unsigned short N_Divisor:6; /* RW: (N) PLL output clock divisor */
- unsigned short Reserved1:2; /* 0: reserved */
- unsigned short M_Multiplier:6; /* RW: (M) PLL feedback clock multiplier */
- unsigned short Reserved2:2; /* 0: reserved */
-} DSP_CLOCK_CONTROL_1;
-
-typedef struct {
- unsigned short PllBypass:1; /* RW: PLL Bypass */
- unsigned short Reserved:15; /* 0: Reserved */
-} DSP_CLOCK_CONTROL_2;
-
-typedef struct {
- unsigned short Latch8:1;
- unsigned short Latch9:1;
- unsigned short Latch10:1;
- unsigned short Latch11:1;
- unsigned short Latch12:1;
- unsigned short Latch13:1;
- unsigned short Latch14:1;
- unsigned short Latch15:1;
- unsigned short Mask8:1;
- unsigned short Mask9:1;
- unsigned short Mask10:1;
- unsigned short Mask11:1;
- unsigned short Mask12:1;
- unsigned short Mask13:1;
- unsigned short Mask14:1;
- unsigned short Mask15:1;
-} DSP_GPIO_OUTPUT_DATA_15_8;
-
-typedef struct {
- unsigned short Enable8:1;
- unsigned short Enable9:1;
- unsigned short Enable10:1;
- unsigned short Enable11:1;
- unsigned short Enable12:1;
- unsigned short Enable13:1;
- unsigned short Enable14:1;
- unsigned short Enable15:1;
- unsigned short Mask8:1;
- unsigned short Mask9:1;
- unsigned short Mask10:1;
- unsigned short Mask11:1;
- unsigned short Mask12:1;
- unsigned short Mask13:1;
- unsigned short Mask14:1;
- unsigned short Mask15:1;
-} DSP_GPIO_DRIVER_ENABLE_15_8;
-
-typedef struct {
- unsigned short GpioMode8:2;
- unsigned short GpioMode9:2;
- unsigned short GpioMode10:2;
- unsigned short GpioMode11:2;
- unsigned short GpioMode12:2;
- unsigned short GpioMode13:2;
- unsigned short GpioMode14:2;
- unsigned short GpioMode15:2;
-} DSP_GPIO_MODE_15_8;
-
-/* Component masks that are defined in dspmgr.h */
-#define MW_ADC_MASK 0x0001
-#define MW_AIC2_MASK 0x0006
-#define MW_MIDI_MASK 0x0008
-#define MW_CDDAC_MASK 0x8001
-#define MW_AIC1_MASK 0xE006
-#define MW_UART_MASK 0xE00A
-#define MW_ACI_MASK 0xE00B
-
-/*
-* Definition of 3780i configuration structure. Unless otherwise stated,
-* these values are provided as input to the 3780i support layer. At present,
-* the only values maintained by the 3780i support layer are the saved UART
-* registers.
-*/
-struct dsp_3780i_config_settings {
-
- /* Location of base configuration register */
- unsigned short usBaseConfigIO;
-
- /* Enables for various DSP components */
- int bDSPEnabled;
- int bModemEnabled;
- int bInterruptClaimed;
-
- /* IRQ, DMA, and Base I/O addresses for various DSP components */
- unsigned short usDspIrq;
- unsigned short usDspDma;
- unsigned short usDspBaseIO;
- unsigned short usUartIrq;
- unsigned short usUartBaseIO;
-
- /* IRQ modes for various DSP components */
- int bDspIrqActiveLow;
- int bUartIrqActiveLow;
- int bDspIrqPulse;
- int bUartIrqPulse;
-
- /* Card abilities */
- unsigned uIps;
- unsigned uDStoreSize;
- unsigned uIStoreSize;
- unsigned uDmaBandwidth;
-
- /* Adapter specific 3780i settings */
- unsigned short usNumTransfers;
- unsigned short usReRequest;
- int bEnableMEMCS16;
- unsigned short usIsaMemCmdWidth;
- int bGateIOCHRDY;
- int bEnablePwrMgmt;
- unsigned short usHBusTimerLoadValue;
- int bDisableLBusTimeout;
- unsigned short usN_Divisor;
- unsigned short usM_Multiplier;
- int bPllBypass;
- unsigned short usChipletEnable; /* Used with the chip reset register to enable specific chiplets */
-
- /* Saved UART registers. These are maintained by the 3780i support layer. */
- int bUartSaved; /* True after a successful save of the UART registers */
- unsigned char ucIER; /* Interrupt enable register */
- unsigned char ucFCR; /* FIFO control register */
- unsigned char ucLCR; /* Line control register */
- unsigned char ucMCR; /* Modem control register */
- unsigned char ucSCR; /* Scratch register */
- unsigned char ucDLL; /* Divisor latch, low byte */
- unsigned char ucDLM; /* Divisor latch, high byte */
-};
-
-
-/* 3780i support functions */
-int dsp3780I_EnableDSP(struct dsp_3780i_config_settings *pSettings,
- unsigned short *pIrqMap,
- unsigned short *pDmaMap);
-int dsp3780I_DisableDSP(struct dsp_3780i_config_settings *pSettings);
-int dsp3780I_Reset(struct dsp_3780i_config_settings *pSettings);
-int dsp3780I_Run(struct dsp_3780i_config_settings *pSettings);
-int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr);
-int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
- void __user *pvBuffer, unsigned uCount,
- unsigned long ulDSPAddr);
-int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr);
-int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr);
-int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
- unsigned uCount, unsigned long ulDSPAddr);
-unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO,
- unsigned long ulMsaAddr);
-void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO,
- unsigned long ulMsaAddr, unsigned short usValue);
-int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
- unsigned short *pusIPCSource);
-
-/* I/O port access macros */
-#define MKWORD(var) (*((unsigned short *)(&var)))
-#define MKBYTE(var) (*((unsigned char *)(&var)))
-
-#define WriteMsaCfg(addr,value) dsp3780I_WriteMsaCfg(usDspBaseIO,addr,value)
-#define ReadMsaCfg(addr) dsp3780I_ReadMsaCfg(usDspBaseIO,addr)
-#define WriteGenCfg(index,value) dsp3780I_WriteGenCfg(usDspBaseIO,index,value)
-#define ReadGenCfg(index) dsp3780I_ReadGenCfg(usDspBaseIO,index)
-
-#define InWordDsp(index) inw(usDspBaseIO+index)
-#define InByteDsp(index) inb(usDspBaseIO+index)
-#define OutWordDsp(index,value) outw(value,usDspBaseIO+index)
-#define OutByteDsp(index,value) outb(value,usDspBaseIO+index)
-
-#endif
diff --git a/drivers/char/mwave/Makefile b/drivers/char/mwave/Makefile
deleted file mode 100644
index e56c1a375535..000000000000
--- a/drivers/char/mwave/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Makefile for ACP Modem (Mwave).
-#
-# See the README file in this directory for more info. <paulsch@us.ibm.com>
-#
-
-obj-$(CONFIG_MWAVE) += mwave.o
-
-mwave-y := mwavedd.o smapi.o tp3780i.o 3780i.o
diff --git a/drivers/char/mwave/README b/drivers/char/mwave/README
deleted file mode 100644
index 6224aa814c62..000000000000
--- a/drivers/char/mwave/README
+++ /dev/null
@@ -1,37 +0,0 @@
-Module options
---------------
-
-The mwave module takes the following options. Note that these options
-are not saved by the BIOS and so do not persist after unload and reload.
-
- mwave_3780i_irq=5/7/10/11/15
- If the dsp irq has not been setup and stored in bios by the
- thinkpad configuration utility then this parameter allows the
- irq used by the dsp to be configured.
-
- mwave_3780i_io=0x130/0x350/0x0070/0xDB0
- If the dsp io range has not been setup and stored in bios by the
- thinkpad configuration utility then this parameter allows the
- io range used by the dsp to be configured.
-
- mwave_uart_irq=3/4
- If the mwave's uart irq has not been setup and stored in bios by the
- thinkpad configuration utility then this parameter allows the
- irq used by the mwave uart to be configured.
-
- mwave_uart_io=0x3f8/0x2f8/0x3E8/0x2E8
- If the uart io range has not been setup and stored in bios by the
- thinkpad configuration utility then this parameter allows the
- io range used by the mwave uart to be configured.
-
-Example to enable the 3780i DSP using ttyS1 resources:
-
- insmod mwave mwave_3780i_irq=10 mwave_3780i_io=0x0130 mwave_uart_irq=3 mwave_uart_io=0x2f8
-
-Accessing the driver
---------------------
-
-You must also create a node for the driver:
- mkdir -p /dev/modems
- mknod --mode=660 /dev/modems/mwave c 10 219
-
diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c
deleted file mode 100644
index 640a9cb0dd8d..000000000000
--- a/drivers/char/mwave/mwavedd.c
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
-*
-* mwavedd.c -- mwave device driver
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#define pr_fmt(fmt) "mwavedd: " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/major.h>
-#include <linux/miscdevice.h>
-#include <linux/device.h>
-#include <linux/serial.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/serial_8250.h>
-#include <linux/nospec.h>
-#include "smapi.h"
-#include "mwavedd.h"
-#include "3780i.h"
-#include "tp3780i.h"
-
-MODULE_DESCRIPTION("3780i Advanced Communications Processor (Mwave) driver");
-MODULE_AUTHOR("Mike Sullivan and Paul Schroeder");
-MODULE_LICENSE("GPL");
-
-/*
-* These parameters support the setting of MWave resources. Note that no
-* checks are made against other devices (ie. superio) for conflicts.
-* We'll depend on users using the tpctl utility to do that for now
-*/
-static DEFINE_MUTEX(mwave_mutex);
-int mwave_3780i_irq = 0;
-int mwave_3780i_io = 0;
-int mwave_uart_irq = 0;
-int mwave_uart_io = 0;
-module_param_hw(mwave_3780i_irq, int, irq, 0);
-module_param_hw(mwave_3780i_io, int, ioport, 0);
-module_param_hw(mwave_uart_irq, int, irq, 0);
-module_param_hw(mwave_uart_io, int, ioport, 0);
-
-struct mwave_device_data mwave_s_mdd;
-
-static long mwave_ioctl(struct file *file, unsigned int iocmd,
- unsigned long ioarg)
-{
- unsigned int retval = 0;
- struct mwave_device_data *pDrvData = &mwave_s_mdd;
- void __user *arg = (void __user *)ioarg;
-
- switch (iocmd) {
-
- case IOCTL_MW_RESET:
- mutex_lock(&mwave_mutex);
- retval = tp3780I_ResetDSP(&pDrvData->rBDData);
- mutex_unlock(&mwave_mutex);
- break;
-
- case IOCTL_MW_RUN:
- mutex_lock(&mwave_mutex);
- retval = tp3780I_StartDSP(&pDrvData->rBDData);
- mutex_unlock(&mwave_mutex);
- break;
-
- case IOCTL_MW_DSP_ABILITIES: {
- struct mw_abilities rAbilities;
-
- mutex_lock(&mwave_mutex);
- retval = tp3780I_QueryAbilities(&pDrvData->rBDData,
- &rAbilities);
- mutex_unlock(&mwave_mutex);
- if (retval == 0) {
- if (copy_to_user(arg, &rAbilities, sizeof(rAbilities)))
- return -EFAULT;
- }
- }
- break;
-
- case IOCTL_MW_READ_DATA:
- case IOCTL_MW_READCLEAR_DATA: {
- struct mw_readwrite rReadData;
- unsigned short __user *pusBuffer = NULL;
-
- if( copy_from_user(&rReadData, arg,
- sizeof(struct mw_readwrite)) )
- return -EFAULT;
- pusBuffer = (unsigned short __user *) (rReadData.pBuf);
-
- mutex_lock(&mwave_mutex);
- retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
- iocmd,
- pusBuffer,
- rReadData.ulDataLength,
- rReadData.usDspAddress);
- mutex_unlock(&mwave_mutex);
- }
- break;
-
- case IOCTL_MW_READ_INST: {
- struct mw_readwrite rReadData;
- unsigned short __user *pusBuffer = NULL;
-
- if (copy_from_user(&rReadData, arg, sizeof(rReadData)))
- return -EFAULT;
- pusBuffer = (unsigned short __user *) (rReadData.pBuf);
-
- mutex_lock(&mwave_mutex);
- retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
- iocmd, pusBuffer,
- rReadData.ulDataLength / 2,
- rReadData.usDspAddress);
- mutex_unlock(&mwave_mutex);
- }
- break;
-
- case IOCTL_MW_WRITE_DATA: {
- struct mw_readwrite rWriteData;
- unsigned short __user *pusBuffer = NULL;
-
- if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
- return -EFAULT;
- pusBuffer = (unsigned short __user *) (rWriteData.pBuf);
-
- mutex_lock(&mwave_mutex);
- retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
- iocmd, pusBuffer,
- rWriteData.ulDataLength,
- rWriteData.usDspAddress);
- mutex_unlock(&mwave_mutex);
- }
- break;
-
- case IOCTL_MW_WRITE_INST: {
- struct mw_readwrite rWriteData;
- unsigned short __user *pusBuffer = NULL;
-
- if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
- return -EFAULT;
- pusBuffer = (unsigned short __user *)(rWriteData.pBuf);
-
- mutex_lock(&mwave_mutex);
- retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData,
- iocmd, pusBuffer,
- rWriteData.ulDataLength,
- rWriteData.usDspAddress);
- mutex_unlock(&mwave_mutex);
- }
- break;
-
- case IOCTL_MW_REGISTER_IPC: {
- unsigned int ipcnum = (unsigned int) ioarg;
-
- if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
- pr_err("%s: IOCTL_MW_REGISTER_IPC: Error: Invalid ipcnum %x\n",
- __func__, ipcnum);
- return -EINVAL;
- }
- ipcnum = array_index_nospec(ipcnum,
- ARRAY_SIZE(pDrvData->IPCs));
-
- mutex_lock(&mwave_mutex);
- pDrvData->IPCs[ipcnum].bIsHere = false;
- pDrvData->IPCs[ipcnum].bIsEnabled = true;
- mutex_unlock(&mwave_mutex);
- }
- break;
-
- case IOCTL_MW_GET_IPC: {
- unsigned int ipcnum = (unsigned int) ioarg;
-
- if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
- pr_err("%s: IOCTL_MW_GET_IPC: Error: Invalid ipcnum %x\n", __func__,
- ipcnum);
- return -EINVAL;
- }
- ipcnum = array_index_nospec(ipcnum,
- ARRAY_SIZE(pDrvData->IPCs));
-
- mutex_lock(&mwave_mutex);
- if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
- DECLARE_WAITQUEUE(wait, current);
-
- add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
- pDrvData->IPCs[ipcnum].bIsHere = true;
- set_current_state(TASK_INTERRUPTIBLE);
- /* check whether an event was signalled by */
- /* the interrupt handler while we were gone */
- if (pDrvData->IPCs[ipcnum].usIntCount == 1) { /* first int has occurred (race condition) */
- pDrvData->IPCs[ipcnum].usIntCount = 2; /* first int has been handled */
- } else { /* either 1st int has not yet occurred, or we have already handled the first int */
- schedule();
- if (pDrvData->IPCs[ipcnum].usIntCount == 1) {
- pDrvData->IPCs[ipcnum].usIntCount = 2;
- }
- }
- pDrvData->IPCs[ipcnum].bIsHere = false;
- remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
- set_current_state(TASK_RUNNING);
- }
- mutex_unlock(&mwave_mutex);
- }
- break;
-
- case IOCTL_MW_UNREGISTER_IPC: {
- unsigned int ipcnum = (unsigned int) ioarg;
-
- if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
- pr_err("%s: IOCTL_MW_UNREGISTER_IPC: Error: Invalid ipcnum %x\n",
- __func__, ipcnum);
- return -EINVAL;
- }
- ipcnum = array_index_nospec(ipcnum,
- ARRAY_SIZE(pDrvData->IPCs));
- mutex_lock(&mwave_mutex);
- if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
- pDrvData->IPCs[ipcnum].bIsEnabled = false;
- if (pDrvData->IPCs[ipcnum].bIsHere == true) {
- wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
- }
- }
- mutex_unlock(&mwave_mutex);
- }
- break;
-
- default:
- return -ENOTTY;
- } /* switch */
-
- return retval;
-}
-
-static int register_serial_portandirq(unsigned int port, int irq)
-{
- struct uart_8250_port uart;
-
- switch ( port ) {
- case 0x3f8:
- case 0x2f8:
- case 0x3e8:
- case 0x2e8:
- /* OK */
- break;
- default:
- pr_err("%s: Error: Illegal port %x\n", __func__, port);
- return -1;
- } /* switch */
- /* port is okay */
-
- switch ( irq ) {
- case 3:
- case 4:
- case 5:
- case 7:
- /* OK */
- break;
- default:
- pr_err("%s: Error: Illegal irq %x\n", __func__, irq);
- return -1;
- } /* switch */
- /* irq is okay */
-
- memset(&uart, 0, sizeof(uart));
-
- uart.port.uartclk = 1843200;
- uart.port.iobase = port;
- uart.port.irq = irq;
- uart.port.iotype = UPIO_PORT;
- uart.port.flags = UPF_SHARE_IRQ;
- return serial8250_register_8250_port(&uart);
-}
-
-static const struct file_operations mwave_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = mwave_ioctl,
- .llseek = default_llseek,
-};
-
-static struct miscdevice mwave_misc_dev = { MWAVE_MINOR, "mwave", &mwave_fops };
-
-/*
-* mwave_init is called on module load
-*
-* mwave_exit is called on module unload
-* mwave_exit is also used to clean up after an aborted mwave_init
-*/
-static void mwave_exit(void)
-{
- struct mwave_device_data *pDrvData = &mwave_s_mdd;
-
- if ( pDrvData->sLine >= 0 ) {
- serial8250_unregister_port(pDrvData->sLine);
- }
- if (pDrvData->bMwaveDevRegistered) {
- misc_deregister(&mwave_misc_dev);
- }
- if (pDrvData->bDSPEnabled) {
- tp3780I_DisableDSP(&pDrvData->rBDData);
- }
- if (pDrvData->bResourcesClaimed) {
- tp3780I_ReleaseResources(&pDrvData->rBDData);
- }
- if (pDrvData->bBDInitialized) {
- tp3780I_Cleanup(&pDrvData->rBDData);
- }
-}
-
-module_exit(mwave_exit);
-
-static int __init mwave_init(void)
-{
- int i;
- int retval = 0;
- struct mwave_device_data *pDrvData = &mwave_s_mdd;
-
- memset(&mwave_s_mdd, 0, sizeof(mwave_s_mdd));
-
- pDrvData->bBDInitialized = false;
- pDrvData->bResourcesClaimed = false;
- pDrvData->bDSPEnabled = false;
- pDrvData->bDSPReset = false;
- pDrvData->bMwaveDevRegistered = false;
- pDrvData->sLine = -1;
-
- for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) {
- pDrvData->IPCs[i].bIsEnabled = false;
- pDrvData->IPCs[i].bIsHere = false;
- pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */
- init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue);
- }
-
- retval = tp3780I_InitializeBoardData(&pDrvData->rBDData);
- if (retval) {
- pr_err("%s: Error: Failed to initialize board data\n", __func__);
- goto cleanup_error;
- }
- pDrvData->bBDInitialized = true;
-
- retval = tp3780I_CalcResources(&pDrvData->rBDData);
- if (retval) {
- pr_err("%s: Error: Failed to calculate resources\n", __func__);
- goto cleanup_error;
- }
-
- retval = tp3780I_ClaimResources(&pDrvData->rBDData);
- if (retval) {
- pr_err("%s: Error: Failed to claim resources\n", __func__);
- goto cleanup_error;
- }
- pDrvData->bResourcesClaimed = true;
-
- retval = tp3780I_EnableDSP(&pDrvData->rBDData);
- if (retval) {
- pr_err("%s: Error: Failed to enable DSP\n", __func__);
- goto cleanup_error;
- }
- pDrvData->bDSPEnabled = true;
-
- if (misc_register(&mwave_misc_dev) < 0) {
- pr_err("%s: Error: Failed to register misc device\n", __func__);
- goto cleanup_error;
- }
- pDrvData->bMwaveDevRegistered = true;
-
- pDrvData->sLine = register_serial_portandirq(
- pDrvData->rBDData.rDspSettings.usUartBaseIO,
- pDrvData->rBDData.rDspSettings.usUartIrq
- );
- if (pDrvData->sLine < 0) {
- pr_err("%s: Error: Failed to register serial driver\n", __func__);
- goto cleanup_error;
- }
- /* uart is registered */
-
- /* SUCCESS! */
- return 0;
-
-cleanup_error:
- pr_err("%s: Error: Failed to initialize\n", __func__);
- mwave_exit(); /* clean up */
-
- return -EIO;
-}
-
-module_init(mwave_init);
-
diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h
deleted file mode 100644
index e1da1493eec5..000000000000
--- a/drivers/char/mwave/mwavedd.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-*
-* mwavedd.h -- declarations for mwave device driver
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#ifndef _LINUX_MWAVEDD_H
-#define _LINUX_MWAVEDD_H
-#include "3780i.h"
-#include "tp3780i.h"
-#include "smapi.h"
-#include "mwavepub.h"
-#include <linux/ioctl.h>
-#include <linux/uaccess.h>
-#include <linux/wait.h>
-
-extern int mwave_3780i_irq;
-extern int mwave_3780i_io;
-extern int mwave_uart_irq;
-extern int mwave_uart_io;
-
-struct mwave_ipc {
- unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */
- bool bIsEnabled;
- bool bIsHere;
- /* entry spin lock */
- wait_queue_head_t ipc_wait_queue;
-};
-
-struct mwave_device_data {
- struct thinkpad_bd_data rBDData; /* board driver's data area */
- unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */
- unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */
- bool bBDInitialized;
- bool bResourcesClaimed;
- bool bDSPEnabled;
- bool bDSPReset;
- struct mwave_ipc IPCs[16];
- bool bMwaveDevRegistered;
- short sLine;
- int nr_registered_attrs;
- int device_registered;
-
-};
-
-extern struct mwave_device_data mwave_s_mdd;
-
-#endif
diff --git a/drivers/char/mwave/mwavepub.h b/drivers/char/mwave/mwavepub.h
deleted file mode 100644
index 280327bdaa38..000000000000
--- a/drivers/char/mwave/mwavepub.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-*
-* mwavepub.h -- PUBLIC declarations for the mwave driver
-* and applications using it
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#ifndef _LINUX_MWAVEPUB_H
-#define _LINUX_MWAVEPUB_H
-
-#include <linux/miscdevice.h>
-
-
-struct mw_abilities {
- unsigned long instr_per_sec;
- unsigned long data_size;
- unsigned long inst_size;
- unsigned long bus_dma_bw;
- unsigned short uart_enable;
- short component_count;
- unsigned long component_list[7];
- char mwave_os_name[16];
- char bios_task_name[16];
-};
-
-
-struct mw_readwrite {
- unsigned short usDspAddress; /* The dsp address */
- unsigned long ulDataLength; /* The size in bytes of the data or user buffer */
- void __user *pBuf; /* Input:variable sized buffer */
-};
-
-#define IOCTL_MW_RESET _IO(MWAVE_MINOR,1)
-#define IOCTL_MW_RUN _IO(MWAVE_MINOR,2)
-#define IOCTL_MW_DSP_ABILITIES _IOR(MWAVE_MINOR,3,struct mw_abilities)
-#define IOCTL_MW_READ_DATA _IOR(MWAVE_MINOR,4,struct mw_readwrite)
-#define IOCTL_MW_READCLEAR_DATA _IOR(MWAVE_MINOR,5,struct mw_readwrite)
-#define IOCTL_MW_READ_INST _IOR(MWAVE_MINOR,6,struct mw_readwrite)
-#define IOCTL_MW_WRITE_DATA _IOW(MWAVE_MINOR,7,struct mw_readwrite)
-#define IOCTL_MW_WRITE_INST _IOW(MWAVE_MINOR,8,struct mw_readwrite)
-#define IOCTL_MW_REGISTER_IPC _IOW(MWAVE_MINOR,9,int)
-#define IOCTL_MW_UNREGISTER_IPC _IOW(MWAVE_MINOR,10,int)
-#define IOCTL_MW_GET_IPC _IOW(MWAVE_MINOR,11,int)
-#define IOCTL_MW_TRACE _IOR(MWAVE_MINOR,12,struct mw_readwrite)
-
-
-#endif
diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c
deleted file mode 100644
index df6354b24339..000000000000
--- a/drivers/char/mwave/smapi.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
-*
-* smapi.c -- SMAPI interface routines
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#define pr_fmt(fmt) "smapi: " fmt
-
-#include <linux/kernel.h>
-#include <linux/mc146818rtc.h> /* CMOS defines */
-#include "smapi.h"
-#include "mwavedd.h"
-
-static unsigned short g_usSmapiPort = 0;
-
-
-static int smapi_request(unsigned short inBX, unsigned short inCX,
- unsigned short inDI, unsigned short inSI,
- unsigned short *outAX, unsigned short *outBX,
- unsigned short *outCX, unsigned short *outDX,
- unsigned short *outDI, unsigned short *outSI)
-{
- unsigned short myoutAX = 2, *pmyoutAX = &myoutAX;
- unsigned short myoutBX = 3, *pmyoutBX = &myoutBX;
- unsigned short myoutCX = 4, *pmyoutCX = &myoutCX;
- unsigned short myoutDX = 5, *pmyoutDX = &myoutDX;
- unsigned short myoutDI = 6, *pmyoutDI = &myoutDI;
- unsigned short myoutSI = 7, *pmyoutSI = &myoutSI;
- unsigned short usSmapiOK = -EIO, *pusSmapiOK = &usSmapiOK;
- unsigned int inBXCX = (inBX << 16) | inCX;
- unsigned int inDISI = (inDI << 16) | inSI;
-
- __asm__ __volatile__("movw $0x5380,%%ax\n\t"
- "movl %7,%%ebx\n\t"
- "shrl $16, %%ebx\n\t"
- "movw %7,%%cx\n\t"
- "movl %8,%%edi\n\t"
- "shrl $16,%%edi\n\t"
- "movw %8,%%si\n\t"
- "movw %9,%%dx\n\t"
- "out %%al,%%dx\n\t"
- "out %%al,$0x4F\n\t"
- "cmpb $0x53,%%ah\n\t"
- "je 2f\n\t"
- "1:\n\t"
- "orb %%ah,%%ah\n\t"
- "jnz 2f\n\t"
- "movw %%ax,%0\n\t"
- "movw %%bx,%1\n\t"
- "movw %%cx,%2\n\t"
- "movw %%dx,%3\n\t"
- "movw %%di,%4\n\t"
- "movw %%si,%5\n\t"
- "movw $1,%6\n\t"
- "2:\n\t":"=m"(*(unsigned short *) pmyoutAX),
- "=m"(*(unsigned short *) pmyoutBX),
- "=m"(*(unsigned short *) pmyoutCX),
- "=m"(*(unsigned short *) pmyoutDX),
- "=m"(*(unsigned short *) pmyoutDI),
- "=m"(*(unsigned short *) pmyoutSI),
- "=m"(*(unsigned short *) pusSmapiOK)
- :"m"(inBXCX), "m"(inDISI), "m"(g_usSmapiPort)
- :"%eax", "%ebx", "%ecx", "%edx", "%edi",
- "%esi");
-
- *outAX = myoutAX;
- *outBX = myoutBX;
- *outCX = myoutCX;
- *outDX = myoutDX;
- *outDI = myoutDI;
- *outSI = myoutSI;
-
- return usSmapiOK == 1 ? 0 : -EIO;
-}
-
-
-int smapi_query_DSP_cfg(struct smapi_dsp_settings *pSettings)
-{
- int bRC;
- unsigned short usAX, usBX, usCX, usDX, usDI, usSI;
- static const unsigned short ausDspBases[] = {
- 0x0030, 0x4E30, 0x8E30, 0xCE30,
- 0x0130, 0x0350, 0x0070, 0x0DB0 };
- static const unsigned short ausUartBases[] = {
- 0x03F8, 0x02F8, 0x03E8, 0x02E8 };
-
- bRC = smapi_request(0x1802, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) {
- pr_err("%s: Error: Could not get DSP Settings. Aborting.\n", __func__);
- return bRC;
- }
-
- pSettings->bDSPPresent = ((usBX & 0x0100) != 0);
- pSettings->bDSPEnabled = ((usCX & 0x0001) != 0);
- pSettings->usDspIRQ = usSI & 0x00FF;
- pSettings->usDspDMA = (usSI & 0xFF00) >> 8;
- if ((usDI & 0x00FF) < ARRAY_SIZE(ausDspBases)) {
- pSettings->usDspBaseIO = ausDspBases[usDI & 0x00FF];
- } else {
- pSettings->usDspBaseIO = 0;
- }
-
- /* check for illegal values */
- if ( pSettings->usDspBaseIO == 0 )
- pr_err("%s: Worry: DSP base I/O address is 0\n", __func__);
- if ( pSettings->usDspIRQ == 0 )
- pr_err("%s: Worry: DSP IRQ line is 0\n", __func__);
-
- bRC = smapi_request(0x1804, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) {
- pr_err("%s: Error: Could not get DSP modem settings. Aborting.\n", __func__);
- return bRC;
- }
-
- pSettings->bModemEnabled = ((usCX & 0x0001) != 0);
- pSettings->usUartIRQ = usSI & 0x000F;
- if (((usSI & 0xFF00) >> 8) < ARRAY_SIZE(ausUartBases)) {
- pSettings->usUartBaseIO = ausUartBases[(usSI & 0xFF00) >> 8];
- } else {
- pSettings->usUartBaseIO = 0;
- }
-
- /* check for illegal values */
- if ( pSettings->usUartBaseIO == 0 )
- pr_err("%s: Worry: UART base I/O address is 0\n", __func__);
- if ( pSettings->usUartIRQ == 0 )
- pr_err("%s: Worry: UART IRQ line is 0\n", __func__);
-
- return bRC;
-}
-
-
-int smapi_set_DSP_cfg(void)
-{
- int bRC = -EIO;
- int i;
- unsigned short usAX, usBX, usCX, usDX, usDI, usSI;
- static const unsigned short ausDspBases[] = {
- 0x0030, 0x4E30, 0x8E30, 0xCE30,
- 0x0130, 0x0350, 0x0070, 0x0DB0 };
- static const unsigned short ausUartBases[] = {
- 0x03F8, 0x02F8, 0x03E8, 0x02E8 };
- static const unsigned short ausDspIrqs[] = {
- 5, 7, 10, 11, 15 };
- static const unsigned short ausUartIrqs[] = {
- 3, 4 };
-
- unsigned short dspio_index = 0, uartio_index = 0;
-
- if (mwave_3780i_io) {
- for (i = 0; i < ARRAY_SIZE(ausDspBases); i++) {
- if (mwave_3780i_io == ausDspBases[i])
- break;
- }
- if (i == ARRAY_SIZE(ausDspBases)) {
- pr_err("%s: Error: Invalid mwave_3780i_io address %x. Aborting.\n",
- __func__, mwave_3780i_io);
- return bRC;
- }
- dspio_index = i;
- }
-
- if (mwave_3780i_irq) {
- for (i = 0; i < ARRAY_SIZE(ausDspIrqs); i++) {
- if (mwave_3780i_irq == ausDspIrqs[i])
- break;
- }
- if (i == ARRAY_SIZE(ausDspIrqs)) {
- pr_err("%s: Error: Invalid mwave_3780i_irq %x. Aborting.\n", __func__,
- mwave_3780i_irq);
- return bRC;
- }
- }
-
- if (mwave_uart_io) {
- for (i = 0; i < ARRAY_SIZE(ausUartBases); i++) {
- if (mwave_uart_io == ausUartBases[i])
- break;
- }
- if (i == ARRAY_SIZE(ausUartBases)) {
- pr_err("%s: Error: Invalid mwave_uart_io address %x. Aborting.\n", __func__,
- mwave_uart_io);
- return bRC;
- }
- uartio_index = i;
- }
-
-
- if (mwave_uart_irq) {
- for (i = 0; i < ARRAY_SIZE(ausUartIrqs); i++) {
- if (mwave_uart_irq == ausUartIrqs[i])
- break;
- }
- if (i == ARRAY_SIZE(ausUartIrqs)) {
- pr_err("%s: Error: Invalid mwave_uart_irq %x. Aborting.\n", __func__,
- mwave_uart_irq);
- return bRC;
- }
- }
-
- if (mwave_uart_irq || mwave_uart_io) {
-
- /* Check serial port A */
- bRC = smapi_request(0x1402, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- /* bRC == 0 */
- if (usBX & 0x0100) { /* serial port A is present */
- if (usCX & 1) { /* serial port is enabled */
- if ((usSI & 0xFF) == mwave_uart_irq) {
- pr_err("%s: Serial port A irq %x conflicts with mwave_uart_irq %x\n",
- __func__, usSI & 0xFF, mwave_uart_irq);
- goto exit_conflict;
- } else {
- if ((usSI >> 8) == uartio_index) {
- pr_err("%s: Serial port A base I/O address %x conflicts with mwave uart I/O %x\n",
- __func__, ausUartBases[usSI >> 8],
- ausUartBases[uartio_index]);
- goto exit_conflict;
- }
- }
- }
- }
-
- /* Check serial port B */
- bRC = smapi_request(0x1404, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- /* bRC == 0 */
- if (usBX & 0x0100) { /* serial port B is present */
- if (usCX & 1) { /* serial port is enabled */
- if ((usSI & 0xFF) == mwave_uart_irq) {
- pr_err("%s: Serial port B irq %x conflicts with mwave_uart_irq %x\n",
- __func__, usSI & 0xFF, mwave_uart_irq);
- goto exit_conflict;
- } else {
- if ((usSI >> 8) == uartio_index) {
- pr_err("%s: Serial port B base I/O address %x conflicts with mwave uart I/O %x\n",
- __func__, ausUartBases[usSI >> 8],
- ausUartBases[uartio_index]);
- goto exit_conflict;
- }
- }
- }
- }
-
- /* Check IR port */
- bRC = smapi_request(0x1700, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1704, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- /* bRC == 0 */
- if ((usCX & 0xff) != 0xff) { /* IR port not disabled */
- if ((usCX & 0xff) == mwave_uart_irq) {
- pr_err("%s: IR port irq %x conflicts with mwave_uart_irq %x\n",
- __func__, usCX & 0xff, mwave_uart_irq);
- goto exit_conflict;
- } else {
- if ((usSI & 0xff) == uartio_index) {
- pr_err("%s: IR port base I/O address %x conflicts with mwave uart I/O %x\n",
- __func__, ausUartBases[usSI & 0xff],
- ausUartBases[uartio_index]);
- goto exit_conflict;
- }
- }
- }
- }
-
- bRC = smapi_request(0x1802, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-
- if (mwave_3780i_io) {
- usDI = dspio_index;
- }
- if (mwave_3780i_irq) {
- usSI = (usSI & 0xff00) | mwave_3780i_irq;
- }
-
- bRC = smapi_request(0x1803, 0x0101, usDI, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-
- bRC = smapi_request(0x1804, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-
- if (mwave_uart_io) {
- usSI = (usSI & 0x00ff) | (uartio_index << 8);
- }
- if (mwave_uart_irq) {
- usSI = (usSI & 0xff00) | mwave_uart_irq;
- }
- bRC = smapi_request(0x1805, 0x0101, 0, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-
- bRC = smapi_request(0x1802, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-
- bRC = smapi_request(0x1804, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-
-/* normal exit: */
- return 0;
-
-exit_conflict:
- /* Message has already been printed */
- return -EIO;
-
-exit_smapi_request_error:
- pr_err("%s: exit on smapi_request error bRC %x\n", __func__, bRC);
- return bRC;
-}
-
-
-int smapi_set_DSP_power_state(bool bOn)
-{
- unsigned short usAX, usBX, usCX, usDX, usDI, usSI;
- unsigned short usPowerFunction;
-
- usPowerFunction = (bOn) ? 1 : 0;
-
- return smapi_request(0x4901, 0x0000, 0, usPowerFunction, &usAX, &usBX, &usCX, &usDX, &usDI,
- &usSI);
-}
-
-int smapi_init(void)
-{
- int retval = -EIO;
- unsigned short usSmapiID = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&rtc_lock, flags);
- usSmapiID = CMOS_READ(0x7C);
- usSmapiID |= (CMOS_READ(0x7D) << 8);
- spin_unlock_irqrestore(&rtc_lock, flags);
-
- if (usSmapiID == 0x5349) {
- spin_lock_irqsave(&rtc_lock, flags);
- g_usSmapiPort = CMOS_READ(0x7E);
- g_usSmapiPort |= (CMOS_READ(0x7F) << 8);
- spin_unlock_irqrestore(&rtc_lock, flags);
- if (g_usSmapiPort == 0) {
- pr_err("%s: ERROR unable to read from SMAPI port\n", __func__);
- } else {
- retval = 0;
- //SmapiQuerySystemID();
- }
- } else {
- pr_err("%s: ERROR invalid usSmapiID\n", __func__);
- retval = -ENXIO;
- }
-
- return retval;
-}
diff --git a/drivers/char/mwave/smapi.h b/drivers/char/mwave/smapi.h
deleted file mode 100644
index e605b16ed23c..000000000000
--- a/drivers/char/mwave/smapi.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-*
-* smapi.h -- declarations for SMAPI interface routines
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#ifndef _LINUX_SMAPI_H
-#define _LINUX_SMAPI_H
-
-struct smapi_dsp_settings {
- int bDSPPresent;
- int bDSPEnabled;
- int bModemEnabled;
- int bMIDIEnabled;
- int bSblstEnabled;
- unsigned short usDspIRQ;
- unsigned short usDspDMA;
- unsigned short usDspBaseIO;
- unsigned short usUartIRQ;
- unsigned short usUartBaseIO;
- unsigned short usMidiIRQ;
- unsigned short usMidiBaseIO;
- unsigned short usSndblstIRQ;
- unsigned short usSndblstDMA;
- unsigned short usSndblstBaseIO;
-};
-
-int smapi_init(void);
-int smapi_query_DSP_cfg(struct smapi_dsp_settings *pSettings);
-int smapi_set_DSP_cfg(void);
-int smapi_set_DSP_power_state(bool bOn);
-
-
-#endif
diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c
deleted file mode 100644
index 7363b0f764e0..000000000000
--- a/drivers/char/mwave/tp3780i.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
-*
-* tp3780i.c -- board driver for 3780i on ThinkPads
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#define pr_fmt(fmt) "tp3780i: " fmt
-
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <asm/io.h>
-#include "smapi.h"
-#include "mwavedd.h"
-#include "tp3780i.h"
-#include "3780i.h"
-#include "mwavepub.h"
-
-static unsigned short s_ausThinkpadIrqToField[16] =
- { 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0002, 0x0003, 0xFFFF, 0x0004,
- 0xFFFF, 0xFFFF, 0x0005, 0x0006, 0xFFFF, 0xFFFF, 0xFFFF, 0x0007 };
-static unsigned short s_ausThinkpadDmaToField[8] =
- { 0x0001, 0x0002, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0003, 0x0004 };
-static unsigned short s_numIrqs = 16, s_numDmas = 8;
-
-
-static void EnableSRAM(struct thinkpad_bd_data *pBDData)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- DSP_GPIO_OUTPUT_DATA_15_8 rGpioOutputData;
- DSP_GPIO_DRIVER_ENABLE_15_8 rGpioDriverEnable;
- DSP_GPIO_MODE_15_8 rGpioMode;
-
- MKWORD(rGpioMode) = ReadMsaCfg(DSP_GpioModeControl_15_8);
- rGpioMode.GpioMode10 = 0;
- WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode));
-
- MKWORD(rGpioDriverEnable) = 0;
- rGpioDriverEnable.Enable10 = true;
- rGpioDriverEnable.Mask10 = true;
- WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable));
-
- MKWORD(rGpioOutputData) = 0;
- rGpioOutputData.Latch10 = 0;
- rGpioOutputData.Mask10 = true;
- WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData));
-}
-
-
-static irqreturn_t UartInterrupt(int irq, void *dev_id)
-{
- return IRQ_HANDLED;
-}
-
-static irqreturn_t DspInterrupt(int irq, void *dev_id)
-{
- struct mwave_device_data *pDrvData = &mwave_s_mdd;
- struct dsp_3780i_config_settings *pSettings = &pDrvData->rBDData.rDspSettings;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- unsigned short usIPCSource = 0, usIsolationMask, usPCNum;
-
- if (dsp3780I_GetIPCSource(usDspBaseIO, &usIPCSource) == 0) {
- usIsolationMask = 1;
- for (usPCNum = 1; usPCNum <= 16; usPCNum++) {
- if (usIPCSource & usIsolationMask) {
- usIPCSource &= ~usIsolationMask;
- if (pDrvData->IPCs[usPCNum - 1].usIntCount == 0) {
- pDrvData->IPCs[usPCNum - 1].usIntCount = 1;
- }
- if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) {
- wake_up_interruptible(&pDrvData->IPCs[usPCNum - 1].ipc_wait_queue);
- }
- }
- if (usIPCSource == 0)
- break;
- /* try next IPC */
- usIsolationMask = usIsolationMask << 1;
- }
- }
- return IRQ_HANDLED;
-}
-
-
-int tp3780I_InitializeBoardData(struct thinkpad_bd_data *pBDData)
-{
- int retval = 0;
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
-
- pBDData->bDSPEnabled = false;
- pSettings->bInterruptClaimed = false;
-
- retval = smapi_init();
- if (retval) {
- pr_err("%s: Error: SMAPI is not available on this machine\n", __func__);
- } else {
- if (mwave_3780i_irq || mwave_3780i_io || mwave_uart_irq || mwave_uart_io) {
- retval = smapi_set_DSP_cfg();
- }
- }
-
- return retval;
-}
-
-void tp3780I_Cleanup(struct thinkpad_bd_data *pBDData)
-{
-}
-
-int tp3780I_CalcResources(struct thinkpad_bd_data *pBDData)
-{
- struct smapi_dsp_settings rSmapiInfo;
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
-
- if (smapi_query_DSP_cfg(&rSmapiInfo)) {
- pr_err("%s: Error: Could not query DSP config. Aborting.\n", __func__);
- return -EIO;
- }
-
- /* Sanity check */
- if (
- ( rSmapiInfo.usDspIRQ == 0 )
- || ( rSmapiInfo.usDspBaseIO == 0 )
- || ( rSmapiInfo.usUartIRQ == 0 )
- || ( rSmapiInfo.usUartBaseIO == 0 )
- ) {
- pr_err("%s: Error: Illegal resource setting. Aborting.\n", __func__);
- return -EIO;
- }
-
- pSettings->bDSPEnabled = (rSmapiInfo.bDSPEnabled && rSmapiInfo.bDSPPresent);
- pSettings->bModemEnabled = rSmapiInfo.bModemEnabled;
- pSettings->usDspIrq = rSmapiInfo.usDspIRQ;
- pSettings->usDspDma = rSmapiInfo.usDspDMA;
- pSettings->usDspBaseIO = rSmapiInfo.usDspBaseIO;
- pSettings->usUartIrq = rSmapiInfo.usUartIRQ;
- pSettings->usUartBaseIO = rSmapiInfo.usUartBaseIO;
-
- pSettings->uDStoreSize = TP_ABILITIES_DATA_SIZE;
- pSettings->uIStoreSize = TP_ABILITIES_INST_SIZE;
- pSettings->uIps = TP_ABILITIES_INTS_PER_SEC;
-
- if (pSettings->bDSPEnabled && pSettings->bModemEnabled && pSettings->usDspIrq == pSettings->usUartIrq) {
- pBDData->bShareDspIrq = pBDData->bShareUartIrq = 1;
- } else {
- pBDData->bShareDspIrq = pBDData->bShareUartIrq = 0;
- }
-
- return 0;
-}
-
-
-int tp3780I_ClaimResources(struct thinkpad_bd_data *pBDData)
-{
- int retval = 0;
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
- struct resource *pres;
-
- pres = request_region(pSettings->usDspBaseIO, 16, "mwave_3780i");
- if ( pres == NULL ) retval = -EIO;
-
- if (retval) {
- pr_err("%s: Error: Could not claim I/O region starting at %x\n", __func__,
- pSettings->usDspBaseIO);
- return -EIO;
- }
-
- return retval;
-}
-
-int tp3780I_ReleaseResources(struct thinkpad_bd_data *pBDData)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
-
- release_region(pSettings->usDspBaseIO & (~3), 16);
-
- if (pSettings->bInterruptClaimed) {
- free_irq(pSettings->usDspIrq, NULL);
- pSettings->bInterruptClaimed = false;
- }
-
- return 0;
-}
-
-
-
-int tp3780I_EnableDSP(struct thinkpad_bd_data *pBDData)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
- bool bDSPPoweredUp = false, bInterruptAllocated = false;
-
- if (pBDData->bDSPEnabled) {
- pr_err("%s: Error: DSP already enabled!\n", __func__);
- goto exit_cleanup;
- }
-
- if (!pSettings->bDSPEnabled) {
- pr_err("%s: Error: pSettings->bDSPEnabled not set\n", __func__);
- goto exit_cleanup;
- }
-
- if (
- (pSettings->usDspIrq >= s_numIrqs)
- || (pSettings->usDspDma >= s_numDmas)
- || (s_ausThinkpadIrqToField[pSettings->usDspIrq] == 0xFFFF)
- || (s_ausThinkpadDmaToField[pSettings->usDspDma] == 0xFFFF)
- ) {
- pr_err("%s: Error: invalid irq %x\n", __func__, pSettings->usDspIrq);
- goto exit_cleanup;
- }
-
- if (
- ((pSettings->usDspBaseIO & 0xF00F) != 0)
- || (pSettings->usDspBaseIO & 0x0FF0) == 0
- ) {
- pr_err("%s: Error: Invalid DSP base I/O address %x\n", __func__,
- pSettings->usDspBaseIO);
- goto exit_cleanup;
- }
-
- if (pSettings->bModemEnabled) {
- if (
- pSettings->usUartIrq >= s_numIrqs
- || s_ausThinkpadIrqToField[pSettings->usUartIrq] == 0xFFFF
- ) {
- pr_err("%s: Error: Invalid UART IRQ %x\n", __func__, pSettings->usUartIrq);
- goto exit_cleanup;
- }
- switch (pSettings->usUartBaseIO) {
- case 0x03F8:
- case 0x02F8:
- case 0x03E8:
- case 0x02E8:
- break;
-
- default:
- pr_err("%s: Error: Invalid UART base I/O address %x\n", __func__,
- pSettings->usUartBaseIO);
- goto exit_cleanup;
- }
- }
-
- pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = true;
- pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = true;
-
- if (pBDData->bShareDspIrq) {
- pSettings->bDspIrqActiveLow = false;
- }
- if (pBDData->bShareUartIrq) {
- pSettings->bUartIrqActiveLow = false;
- }
-
- pSettings->usNumTransfers = TP_CFG_NumTransfers;
- pSettings->usReRequest = TP_CFG_RerequestTimer;
- pSettings->bEnableMEMCS16 = TP_CFG_MEMCS16;
- pSettings->usIsaMemCmdWidth = TP_CFG_IsaMemCmdWidth;
- pSettings->bGateIOCHRDY = TP_CFG_GateIOCHRDY;
- pSettings->bEnablePwrMgmt = TP_CFG_EnablePwrMgmt;
- pSettings->usHBusTimerLoadValue = TP_CFG_HBusTimerValue;
- pSettings->bDisableLBusTimeout = TP_CFG_DisableLBusTimeout;
- pSettings->usN_Divisor = TP_CFG_N_Divisor;
- pSettings->usM_Multiplier = TP_CFG_M_Multiplier;
- pSettings->bPllBypass = TP_CFG_PllBypass;
- pSettings->usChipletEnable = TP_CFG_ChipletEnable;
-
- if (request_irq(pSettings->usUartIrq, &UartInterrupt, 0, "mwave_uart", NULL)) {
- pr_err("%s: Error: Could not get UART IRQ %x\n", __func__, pSettings->usUartIrq);
- goto exit_cleanup;
- } else { /* no conflict just release */
- free_irq(pSettings->usUartIrq, NULL);
- }
-
- if (request_irq(pSettings->usDspIrq, &DspInterrupt, 0, "mwave_3780i", NULL)) {
- pr_err("%s: Error: Could not get 3780i IRQ %x\n", __func__, pSettings->usDspIrq);
- goto exit_cleanup;
- } else {
- bInterruptAllocated = true;
- pSettings->bInterruptClaimed = true;
- }
-
- smapi_set_DSP_power_state(false);
- if (smapi_set_DSP_power_state(true)) {
- pr_err("%s: Error: smapi_set_DSP_power_state(true) failed\n", __func__);
- goto exit_cleanup;
- } else {
- bDSPPoweredUp = true;
- }
-
- if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) {
- pr_err("%s: Error: dsp7880I_EnableDSP() failed\n", __func__);
- goto exit_cleanup;
- }
-
- EnableSRAM(pBDData);
-
- pBDData->bDSPEnabled = true;
-
- return 0;
-
-exit_cleanup:
- pr_err("%s: Cleaning up\n", __func__);
- if (bDSPPoweredUp)
- smapi_set_DSP_power_state(false);
- if (bInterruptAllocated) {
- free_irq(pSettings->usDspIrq, NULL);
- pSettings->bInterruptClaimed = false;
- }
- return -EIO;
-}
-
-
-int tp3780I_DisableDSP(struct thinkpad_bd_data *pBDData)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
-
- if (pBDData->bDSPEnabled) {
- dsp3780I_DisableDSP(&pBDData->rDspSettings);
- if (pSettings->bInterruptClaimed) {
- free_irq(pSettings->usDspIrq, NULL);
- pSettings->bInterruptClaimed = false;
- }
- smapi_set_DSP_power_state(false);
- pBDData->bDSPEnabled = false;
- }
-
- return 0;
-}
-
-
-int tp3780I_ResetDSP(struct thinkpad_bd_data *pBDData)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
-
- if (dsp3780I_Reset(pSettings) == 0) {
- EnableSRAM(pBDData);
- return 0;
- }
- return -EIO;
-}
-
-
-int tp3780I_StartDSP(struct thinkpad_bd_data *pBDData)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
-
- if (dsp3780I_Run(pSettings) == 0) {
- // @BUG @TBD EnableSRAM(pBDData);
- } else {
- return -EIO;
- }
-
- return 0;
-}
-
-
-int tp3780I_QueryAbilities(struct thinkpad_bd_data *pBDData, struct mw_abilities *pAbilities)
-{
- memset(pAbilities, 0, sizeof(*pAbilities));
- /* fill out standard constant fields */
- pAbilities->instr_per_sec = pBDData->rDspSettings.uIps;
- pAbilities->data_size = pBDData->rDspSettings.uDStoreSize;
- pAbilities->inst_size = pBDData->rDspSettings.uIStoreSize;
- pAbilities->bus_dma_bw = pBDData->rDspSettings.uDmaBandwidth;
-
- /* fill out dynamically determined fields */
- pAbilities->component_list[0] = 0x00010000 | MW_ADC_MASK;
- pAbilities->component_list[1] = 0x00010000 | MW_ACI_MASK;
- pAbilities->component_list[2] = 0x00010000 | MW_AIC1_MASK;
- pAbilities->component_list[3] = 0x00010000 | MW_AIC2_MASK;
- pAbilities->component_list[4] = 0x00010000 | MW_CDDAC_MASK;
- pAbilities->component_list[5] = 0x00010000 | MW_MIDI_MASK;
- pAbilities->component_list[6] = 0x00010000 | MW_UART_MASK;
- pAbilities->component_count = 7;
-
- /* Fill out Mwave OS and BIOS task names */
-
- memcpy(pAbilities->mwave_os_name, TP_ABILITIES_MWAVEOS_NAME,
- sizeof(TP_ABILITIES_MWAVEOS_NAME));
- memcpy(pAbilities->bios_task_name, TP_ABILITIES_BIOSTASK_NAME,
- sizeof(TP_ABILITIES_BIOSTASK_NAME));
-
- return 0;
-}
-
-int tp3780I_ReadWriteDspDStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
- void __user *pvBuffer, unsigned int uCount,
- unsigned long ulDSPAddr)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- bool bRC = 0;
-
- if (pBDData->bDSPEnabled) {
- switch (uOpcode) {
- case IOCTL_MW_READ_DATA:
- bRC = dsp3780I_ReadDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr);
- break;
-
- case IOCTL_MW_READCLEAR_DATA:
- bRC = dsp3780I_ReadAndClearDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr);
- break;
-
- case IOCTL_MW_WRITE_DATA:
- bRC = dsp3780I_WriteDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr);
- break;
- }
- }
-
- return bRC ? -EIO : 0;
-}
-
-
-int tp3780I_ReadWriteDspIStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
- void __user *pvBuffer, unsigned int uCount,
- unsigned long ulDSPAddr)
-{
- struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
- unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- bool bRC = 0;
-
- if (pBDData->bDSPEnabled) {
- switch (uOpcode) {
- case IOCTL_MW_READ_INST:
- bRC = dsp3780I_ReadIStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr);
- break;
-
- case IOCTL_MW_WRITE_INST:
- bRC = dsp3780I_WriteIStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr);
- break;
- }
- }
-
- return bRC ? -EIO : 0;
-}
-
diff --git a/drivers/char/mwave/tp3780i.h b/drivers/char/mwave/tp3780i.h
deleted file mode 100644
index c0001a344741..000000000000
--- a/drivers/char/mwave/tp3780i.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-*
-* tp3780i.h -- declarations for tp3780i.c
-*
-*
-* Written By: Mike Sullivan IBM Corporation
-*
-* Copyright (C) 1999 IBM Corporation
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* NO WARRANTY
-* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
-* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
-* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
-* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
-* solely responsible for determining the appropriateness of using and
-* distributing the Program and assumes all risks associated with its
-* exercise of rights under this Agreement, including but not limited to
-* the risks and costs of program errors, damage to or loss of data,
-* programs or equipment, and unavailability or interruption of operations.
-*
-* DISCLAIMER OF LIABILITY
-* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*
-*
-* 10/23/2000 - Alpha Release
-* First release to the public
-*/
-
-#ifndef _LINUX_TP3780I_H
-#define _LINUX_TP3780I_H
-
-#include <asm/io.h>
-#include "mwavepub.h"
-
-
-/* DSP abilities constants for 3780i based Thinkpads */
-#define TP_ABILITIES_INTS_PER_SEC 39160800
-#define TP_ABILITIES_DATA_SIZE 32768
-#define TP_ABILITIES_INST_SIZE 32768
-#define TP_ABILITIES_MWAVEOS_NAME "mwaveos0700.dsp"
-#define TP_ABILITIES_BIOSTASK_NAME "mwbio701.dsp"
-
-
-/* DSP configuration values for 3780i based Thinkpads */
-#define TP_CFG_NumTransfers 3 /* 16 transfers */
-#define TP_CFG_RerequestTimer 1 /* 2 usec */
-#define TP_CFG_MEMCS16 0 /* Disabled, 16-bit memory assumed */
-#define TP_CFG_IsaMemCmdWidth 3 /* 295 nsec (16-bit) */
-#define TP_CFG_GateIOCHRDY 0 /* No IOCHRDY gating */
-#define TP_CFG_EnablePwrMgmt 1 /* Enable low poser suspend/resume */
-#define TP_CFG_HBusTimerValue 255 /* HBus timer load value */
-#define TP_CFG_DisableLBusTimeout 0 /* Enable LBus timeout */
-#define TP_CFG_N_Divisor 32 /* Clock = 39.1608 Mhz */
-#define TP_CFG_M_Multiplier 37 /* " */
-#define TP_CFG_PllBypass 0 /* don't bypass */
-#define TP_CFG_ChipletEnable 0xFFFF /* Enable all chiplets */
-
-struct thinkpad_bd_data {
- int bDSPEnabled;
- int bShareDspIrq;
- int bShareUartIrq;
- struct dsp_3780i_config_settings rDspSettings;
-};
-
-int tp3780I_InitializeBoardData(struct thinkpad_bd_data *pBDData);
-int tp3780I_CalcResources(struct thinkpad_bd_data *pBDData);
-int tp3780I_ClaimResources(struct thinkpad_bd_data *pBDData);
-int tp3780I_ReleaseResources(struct thinkpad_bd_data *pBDData);
-int tp3780I_EnableDSP(struct thinkpad_bd_data *pBDData);
-int tp3780I_DisableDSP(struct thinkpad_bd_data *pBDData);
-int tp3780I_ResetDSP(struct thinkpad_bd_data *pBDData);
-int tp3780I_StartDSP(struct thinkpad_bd_data *pBDData);
-int tp3780I_QueryAbilities(struct thinkpad_bd_data *pBDData, struct mw_abilities *pAbilities);
-void tp3780I_Cleanup(struct thinkpad_bd_data *pBDData);
-int tp3780I_ReadWriteDspDStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
- void __user *pvBuffer, unsigned int uCount,
- unsigned long ulDSPAddr);
-int tp3780I_ReadWriteDspIStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
- void __user *pvBuffer, unsigned int uCount,
- unsigned long ulDSPAddr);
-
-
-#endif
diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c
index 619f3a30ec55..7bd786fa1be8 100644
--- a/drivers/char/xilinx_hwicap/fifo_icap.c
+++ b/drivers/char/xilinx_hwicap/fifo_icap.c
@@ -48,7 +48,7 @@
#define XHI_GIER_GIE_MASK 0x80000000 /* Global Interrupt enable Mask */
-/**
+/*
* HwIcap Device Interrupt Status/Enable Registers
*
* Interrupt Status Register (IPISR) : This register holds the
@@ -102,6 +102,8 @@ static inline void fifo_icap_fifo_write(struct hwicap_drvdata *drvdata,
* @drvdata: a pointer to the drvdata.
*
* This function will silently fail if the fifo is empty.
+ *
+ * Returns: 32-bit data from the Read FIFO
**/
static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata)
{
@@ -156,6 +158,8 @@ static inline void fifo_icap_start_readback(struct hwicap_drvdata *drvdata)
* D2 - Always 1
* D1 - Always 1
* D0 - Done bit
+ *
+ * Returns: the 32-bit ICAP status register
**/
u32 fifo_icap_get_status(struct hwicap_drvdata *drvdata)
{
@@ -165,8 +169,11 @@ u32 fifo_icap_get_status(struct hwicap_drvdata *drvdata)
}
/**
- * fifo_icap_busy - Return true if the ICAP is still processing a transaction.
+ * fifo_icap_busy - Check the ICAP busy status.
* @drvdata: a pointer to the drvdata.
+ *
+ * Returns: %true if the ICAP is still processing a transaction,
+ * otherwise %false
**/
static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata)
{
@@ -178,7 +185,7 @@ static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata)
* fifo_icap_write_fifo_vacancy - Query the write fifo available space.
* @drvdata: a pointer to the drvdata.
*
- * Return the number of words that can be safely pushed into the write fifo.
+ * Returns: the number of words that can be safely pushed into the write fifo.
**/
static inline u32 fifo_icap_write_fifo_vacancy(
struct hwicap_drvdata *drvdata)
@@ -190,7 +197,7 @@ static inline u32 fifo_icap_write_fifo_vacancy(
* fifo_icap_read_fifo_occupancy - Query the read fifo available data.
* @drvdata: a pointer to the drvdata.
*
- * Return the number of words that can be safely read from the read fifo.
+ * Returns: the number of words that can be safely read from the read fifo.
**/
static inline u32 fifo_icap_read_fifo_occupancy(
struct hwicap_drvdata *drvdata)
@@ -205,10 +212,12 @@ static inline u32 fifo_icap_read_fifo_occupancy(
* ICAP device.
* @num_words: the number of words (32 bit) to write to the ICAP
* device.
-
+ *
* This function writes the given user data to the Write FIFO in
* polled mode and starts the transfer of the data to
* the ICAP device.
+ *
+ * Returns: %0 on success or %-errno on failure.
**/
int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata,
u32 *frame_buffer, u32 num_words)
@@ -280,11 +289,13 @@ int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata,
/**
* fifo_icap_get_configuration - Read configuration data from the device.
* @drvdata: a pointer to the drvdata.
- * @data: Address of the data representing the partial bitstream
- * @size: the size of the partial bitstream in 32 bit words.
+ * @frame_buffer: Address of the data representing the partial bitstream
+ * @num_words: the size of the partial bitstream in 32 bit words.
*
* This function reads the specified number of words from the ICAP device in
* the polled mode.
+ *
+ * Returns: %0 on success or %-errno on failure.
*/
int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata,
u32 *frame_buffer, u32 num_words)
@@ -347,7 +358,7 @@ int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata,
}
/**
- * buffer_icap_reset - Reset the logic of the icap device.
+ * fifo_icap_reset - Reset the logic of the icap device.
* @drvdata: a pointer to the drvdata.
*
* This function forces the software reset of the complete HWICAP device.
diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c
index 2c3eb9e89571..25aa4296f3b0 100644
--- a/drivers/comedi/comedi_fops.c
+++ b/drivers/comedi/comedi_fops.c
@@ -1169,6 +1169,8 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
* COMEDI_BUFINFO ioctl
* buffer information
*
+ * Note that the comedi device's mutex has not been locked for this ioctl.
+ *
* arg:
* pointer to comedi_bufinfo structure
*
@@ -1186,24 +1188,42 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
struct comedi_async *async;
unsigned int runflags;
int retval = 0;
+ unsigned int old_detach_count;
+ unsigned int cmd_flags;
bool become_nonbusy = false;
+ bool attach_locked;
- lockdep_assert_held(&dev->mutex);
if (copy_from_user(&bi, arg, sizeof(bi)))
return -EFAULT;
- if (bi.subdevice >= dev->n_subdevices)
- return -EINVAL;
+ /* Protect against device detachment during operation. */
+ down_read(&dev->attach_lock);
+ attach_locked = true;
+ old_detach_count = dev->detach_count;
+
+ if (!dev->attached) {
+ dev_dbg(dev->class_dev, "no driver attached\n");
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (bi.subdevice >= dev->n_subdevices) {
+ retval = -EINVAL;
+ goto out;
+ }
s = &dev->subdevices[bi.subdevice];
async = s->async;
- if (!async || s->busy != file)
- return -EINVAL;
+ if (!async || s->busy != file) {
+ retval = -EINVAL;
+ goto out;
+ }
runflags = comedi_get_subdevice_runflags(s);
- if (!(async->cmd.flags & CMDF_WRITE)) {
+ cmd_flags = async->cmd.flags;
+ if (!(cmd_flags & CMDF_WRITE)) {
/* command was set up in "read" direction */
if (bi.bytes_read) {
_comedi_buf_read_alloc(s, bi.bytes_read);
@@ -1243,8 +1263,41 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
bi.buf_read_count = async->buf_read_count;
bi.buf_read_ptr = async->buf_read_ptr;
- if (become_nonbusy)
- do_become_nonbusy(dev, s);
+ if (become_nonbusy) {
+ struct comedi_subdevice *new_s = NULL;
+
+ /*
+ * To avoid deadlock, cannot acquire dev->mutex
+ * while dev->attach_lock is held.
+ */
+ up_read(&dev->attach_lock);
+ attach_locked = false;
+ mutex_lock(&dev->mutex);
+ /*
+ * Check device hasn't become detached behind our back.
+ * Checking dev->detach_count is unchanged ought to be
+ * sufficient, but check the subdevice pointer as well,
+ * and check the subdevice is still in a suitable state
+ * to become non-busy. It should still be "busy" after
+ * running an asynchronous commands, which should now have
+ * stopped, and for a command in the "read" direction, all
+ * available data should have been read.
+ */
+ if (dev->attached && old_detach_count == dev->detach_count &&
+ bi.subdevice < dev->n_subdevices)
+ new_s = &dev->subdevices[bi.subdevice];
+ if (s == new_s && new_s->async == async && s->busy == file &&
+ async->cmd.flags == cmd_flags &&
+ !comedi_is_subdevice_running(s) &&
+ ((cmd_flags & CMDF_WRITE) != 0 ||
+ _comedi_buf_read_n_available(s) == 0))
+ do_become_nonbusy(dev, s);
+ mutex_unlock(&dev->mutex);
+ }
+
+out:
+ if (attach_locked)
+ up_read(&dev->attach_lock);
if (retval)
return retval;
@@ -2225,6 +2278,13 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
struct comedi_device *dev = cfp->dev;
int rc;
+ /* Handle COMEDI_BUFINFO without locking the mutex first. */
+ if (cmd == COMEDI_BUFINFO) {
+ return do_bufinfo_ioctl(dev,
+ (struct comedi_bufinfo __user *)arg,
+ file);
+ }
+
mutex_lock(&dev->mutex);
/*
@@ -2294,11 +2354,6 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
rc = do_rangeinfo_ioctl(dev, &it);
break;
}
- case COMEDI_BUFINFO:
- rc = do_bufinfo_ioctl(dev,
- (struct comedi_bufinfo __user *)arg,
- file);
- break;
case COMEDI_LOCK:
rc = do_lock_ioctl(dev, arg, file);
break;
diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c
index 7984950f0f99..01aafce20ef8 100644
--- a/drivers/comedi/drivers/comedi_test.c
+++ b/drivers/comedi/drivers/comedi_test.c
@@ -692,6 +692,44 @@ static int waveform_ao_insn_config(struct comedi_device *dev,
return -EINVAL;
}
+static int waveform_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ u32 driven_low;
+ u16 wires;
+
+ /* Update the channel outputs. */
+ comedi_dio_update_state(s, data);
+ /*
+ * We are modelling the outputs as NPN open collector (0 = driven low,
+ * 1 = high impedance), with the lower 16 channels wired to the upper
+ * 16 channels in pairs (0 to 16, 1 to 17, ..., 15 to 31), with a
+ * pull-up resistor on each wire. When reading back each channel, we
+ * read back the state of the wire to which it is connected.
+ *
+ * The state of each wire and the value read back from both channels
+ * connected to it will be logic 1 unless either channel connected to
+ * the wire is configured as an output in the logic 0 state.
+ */
+ driven_low = s->io_bits & ~s->state;
+ wires = 0xFFFF & ~driven_low & ~(driven_low >> 16);
+ /* Read back the state of the wires for each pair of channels. */
+ data[1] = wires | (wires << 16);
+
+ return insn->n;
+}
+
+static int waveform_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ /* configure each channel as input or output individually */
+ return comedi_dio_insn_config(dev, s, insn, data, 0);
+}
+
static int waveform_common_attach(struct comedi_device *dev,
int amplitude, int period)
{
@@ -707,7 +745,7 @@ static int waveform_common_attach(struct comedi_device *dev,
devpriv->wf_amplitude = amplitude;
devpriv->wf_period = period;
- ret = comedi_alloc_subdevices(dev, 2);
+ ret = comedi_alloc_subdevices(dev, 3);
if (ret)
return ret;
@@ -746,6 +784,16 @@ static int waveform_common_attach(struct comedi_device *dev,
for (i = 0; i < s->n_chan; i++)
devpriv->ao_loopbacks[i] = s->maxdata / 2;
+ s = &dev->subdevices[2];
+ /* digital input/output subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = waveform_dio_insn_bits;
+ s->insn_config = waveform_dio_insn_config;
+
devpriv->dev = dev;
timer_setup(&devpriv->ai_timer, waveform_ai_timer, 0);
timer_setup(&devpriv->ao_timer, waveform_ao_timer, 0);
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index 515b948ff320..dbed404a71fc 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -1317,7 +1317,7 @@ int stratix10_svc_async_send(struct stratix10_svc_chan *chan, void *msg,
dev_dbg(ctrl->dev,
"Async message sent with transaction_id 0x%02x\n",
handle->transaction_id);
- *handler = handle;
+ *handler = handle;
return 0;
case INTEL_SIP_SMC_STATUS_BUSY:
dev_warn(ctrl->dev, "Mailbox is busy, try after some time\n");
@@ -1702,12 +1702,12 @@ int stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg)
kthread_run_on_cpu(svc_normal_to_secure_thread,
(void *)chan->ctrl,
cpu, "svc_smc_hvc_thread");
- if (IS_ERR(chan->ctrl->task)) {
- dev_err(chan->ctrl->dev,
- "failed to create svc_smc_hvc_thread\n");
- kfree(p_data);
- return -EINVAL;
- }
+ if (IS_ERR(chan->ctrl->task)) {
+ dev_err(chan->ctrl->dev,
+ "failed to create svc_smc_hvc_thread\n");
+ kfree(p_data);
+ return -EINVAL;
+ }
}
pr_debug("%s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__,
diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index 7022657243c0..449c3a082e23 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -2018,7 +2018,7 @@ static void __exit dfl_fpga_exit(void)
bus_unregister(&dfl_bus_type);
}
-module_init(dfl_fpga_init);
+subsys_initcall(dfl_fpga_init);
module_exit(dfl_fpga_exit);
MODULE_DESCRIPTION("FPGA Device Feature List (DFL) Support");
diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
index 95539f1213cb..9549f63b00eb 100644
--- a/drivers/fpga/dfl.h
+++ b/drivers/fpga/dfl.h
@@ -82,7 +82,7 @@
#define DFH_TYPE_FIU 4
/*
- * DFHv1 Register Offset definitons
+ * DFHv1 Register Offset definitions
* In DHFv1, DFH + GUID + CSR_START + CSR_SIZE_GROUP + PARAM_HDR + PARAM_DATA
* as common header registers
*/
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index 43db4bb77138..caa091224dc5 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -83,7 +83,7 @@ static struct fpga_manager *of_fpga_region_get_mgr(struct device_node *np)
* done with the bridges.
*
* Return: 0 for success (even if there are no bridges specified)
- * or -EBUSY if any of the bridges are in use.
+ * or an error code if any of the bridges are not available.
*/
static int of_fpga_region_get_bridges(struct fpga_region *region)
{
@@ -130,10 +130,10 @@ static int of_fpga_region_get_bridges(struct fpga_region *region)
&region->bridge_list);
of_node_put(br);
- /* If any of the bridges are in use, give up */
- if (ret == -EBUSY) {
+ /* If any of the bridges are not available, give up */
+ if (ret) {
fpga_bridges_put(&region->bridge_list);
- return -EBUSY;
+ return ret;
}
}
diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
index 822751fad18a..6994d68e9036 100644
--- a/drivers/fpga/xilinx-pr-decoupler.c
+++ b/drivers/fpga/xilinx-pr-decoupler.c
@@ -173,5 +173,5 @@ module_platform_driver(xlnx_pr_decoupler_driver);
MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
-MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
+MODULE_AUTHOR("Michal Simek <michal.simek@amd.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index b7629a0e4813..9d1d599ef718 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -652,6 +652,6 @@ static struct platform_driver zynq_fpga_driver = {
module_platform_driver(zynq_fpga_driver);
MODULE_AUTHOR("Moritz Fischer <moritz.fischer@ettus.com>");
-MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
+MODULE_AUTHOR("Michal Simek <michal.simek@amd.com>");
MODULE_DESCRIPTION("Xilinx Zynq FPGA Manager");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index c6c115993ebc..83599a1c548b 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -100,6 +100,61 @@ static int fsi_master_write(struct fsi_master *master, int link,
uint8_t slave_id, uint32_t addr, const void *val, size_t size);
static int fsi_master_break(struct fsi_master *master, int link);
+/* FSI core & Linux bus type definitions */
+
+static int fsi_bus_match(struct device *dev, const struct device_driver *drv)
+{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ const struct fsi_driver *fsi_drv = to_fsi_drv(drv);
+ const struct fsi_device_id *id;
+
+ if (!fsi_drv->id_table)
+ return 0;
+
+ for (id = fsi_drv->id_table; id->engine_type; id++) {
+ if (id->engine_type != fsi_dev->engine_type)
+ continue;
+ if (id->version == FSI_VERSION_ANY ||
+ id->version == fsi_dev->version) {
+ if (drv->of_match_table) {
+ if (of_driver_match_device(dev, drv))
+ return 1;
+ } else {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int fsi_probe(struct device *dev)
+{
+ struct fsi_device *fsidev = to_fsi_dev(dev);
+ struct fsi_driver *fsidrv = to_fsi_drv(dev->driver);
+
+ if (fsidrv->probe)
+ return fsidrv->probe(fsidev);
+ else
+ return 0;
+}
+
+static void fsi_remove(struct device *dev)
+{
+ struct fsi_device *fsidev = to_fsi_dev(dev);
+ struct fsi_driver *fsidrv = to_fsi_drv(dev->driver);
+
+ if (fsidrv->remove)
+ fsidrv->remove(fsidev);
+}
+
+static const struct bus_type fsi_bus_type = {
+ .name = "fsi",
+ .match = fsi_bus_match,
+ .probe = fsi_probe,
+ .remove = fsi_remove,
+};
+
/*
* fsi_device_read() / fsi_device_write() / fsi_device_peek()
*
@@ -1359,32 +1414,23 @@ void fsi_master_unregister(struct fsi_master *master)
}
EXPORT_SYMBOL_GPL(fsi_master_unregister);
-/* FSI core & Linux bus type definitions */
-
-static int fsi_bus_match(struct device *dev, const struct device_driver *drv)
+static int fsi_legacy_probe(struct fsi_device *fsidev)
{
- struct fsi_device *fsi_dev = to_fsi_dev(dev);
- const struct fsi_driver *fsi_drv = to_fsi_drv(drv);
- const struct fsi_device_id *id;
+ struct device *dev = &fsidev->dev;
+ struct device_driver *driver = dev->driver;
- if (!fsi_drv->id_table)
- return 0;
+ return driver->probe(dev);
+}
- for (id = fsi_drv->id_table; id->engine_type; id++) {
- if (id->engine_type != fsi_dev->engine_type)
- continue;
- if (id->version == FSI_VERSION_ANY ||
- id->version == fsi_dev->version) {
- if (drv->of_match_table) {
- if (of_driver_match_device(dev, drv))
- return 1;
- } else {
- return 1;
- }
- }
- }
+static void fsi_legacy_remove(struct fsi_device *fsidev)
+{
+ struct device *dev = &fsidev->dev;
+ struct device_driver *driver = dev->driver;
+ int ret;
- return 0;
+ ret = driver->remove(dev);
+ if (unlikely(ret))
+ dev_warn(dev, "Ignoring return value of remove callback (%pe)\n", ERR_PTR(ret));
}
int fsi_driver_register(struct fsi_driver *fsi_drv)
@@ -1394,6 +1440,17 @@ int fsi_driver_register(struct fsi_driver *fsi_drv)
if (!fsi_drv->id_table)
return -EINVAL;
+ fsi_drv->drv.bus = &fsi_bus_type;
+
+ /*
+ * This driver needs updating. Note that driver_register() warns about
+ * this, so we're not adding another warning here.
+ */
+ if (!fsi_drv->probe && fsi_drv->drv.probe)
+ fsi_drv->probe = fsi_legacy_probe;
+ if (!fsi_drv->remove && fsi_drv->drv.remove)
+ fsi_drv->remove = fsi_legacy_remove;
+
return driver_register(&fsi_drv->drv);
}
EXPORT_SYMBOL_GPL(fsi_driver_register);
@@ -1404,12 +1461,6 @@ void fsi_driver_unregister(struct fsi_driver *fsi_drv)
}
EXPORT_SYMBOL_GPL(fsi_driver_unregister);
-const struct bus_type fsi_bus_type = {
- .name = "fsi",
- .match = fsi_bus_match,
-};
-EXPORT_SYMBOL_GPL(fsi_bus_type);
-
static int __init fsi_init(void)
{
int rc;
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 6568fed7db3c..a84955bb23b1 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -192,9 +192,9 @@ static int hub_master_init(struct fsi_master_hub *hub)
return fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
}
-static int hub_master_probe(struct device *dev)
+static int hub_master_probe(struct fsi_device *fsi_dev)
{
- struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct device *dev = &fsi_dev->dev;
struct fsi_master_hub *hub;
uint32_t reg, links;
__be32 __reg;
@@ -235,7 +235,7 @@ static int hub_master_probe(struct device *dev)
hub->master.send_break = hub_master_break;
hub->master.link_enable = hub_master_link_enable;
- dev_set_drvdata(dev, hub);
+ fsi_set_drvdata(fsi_dev, hub);
hub_master_init(hub);
@@ -259,9 +259,9 @@ err_release:
return rc;
}
-static int hub_master_remove(struct device *dev)
+static void hub_master_remove(struct fsi_device *fsi_dev)
{
- struct fsi_master_hub *hub = dev_get_drvdata(dev);
+ struct fsi_master_hub *hub = fsi_get_drvdata(fsi_dev);
fsi_master_unregister(&hub->master);
fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
@@ -272,8 +272,6 @@ static int hub_master_remove(struct device *dev)
* the hub
*/
put_device(&hub->master.dev);
-
- return 0;
}
static const struct fsi_device_id hub_master_ids[] = {
@@ -286,11 +284,10 @@ static const struct fsi_device_id hub_master_ids[] = {
static struct fsi_driver hub_master_driver = {
.id_table = hub_master_ids,
+ .probe = hub_master_probe,
+ .remove = hub_master_remove,
.drv = {
.name = "fsi-master-hub",
- .bus = &fsi_bus_type,
- .probe = hub_master_probe,
- .remove = hub_master_remove,
}
};
diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index 0a98517f3959..6ca5817910cd 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -1022,9 +1022,9 @@ static void sbefifo_free(struct device *dev)
* Probe/remove
*/
-static int sbefifo_probe(struct device *dev)
+static int sbefifo_probe(struct fsi_device *fsi_dev)
{
- struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct device *dev = &fsi_dev->dev;
struct sbefifo *sbefifo;
struct device_node *np;
struct platform_device *child;
@@ -1045,7 +1045,7 @@ static int sbefifo_probe(struct device *dev)
sbefifo->magic = SBEFIFO_MAGIC;
sbefifo->fsi_dev = fsi_dev;
- dev_set_drvdata(dev, sbefifo);
+ fsi_set_drvdata(fsi_dev, sbefifo);
mutex_init(&sbefifo->lock);
sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD;
sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
@@ -1101,9 +1101,10 @@ static int sbefifo_unregister_child(struct device *dev, void *data)
return 0;
}
-static int sbefifo_remove(struct device *dev)
+static void sbefifo_remove(struct fsi_device *fsi_dev)
{
- struct sbefifo *sbefifo = dev_get_drvdata(dev);
+ struct device *dev = &fsi_dev->dev;
+ struct sbefifo *sbefifo = fsi_get_drvdata(fsi_dev);
dev_dbg(dev, "Removing sbefifo device...\n");
@@ -1117,8 +1118,6 @@ static int sbefifo_remove(struct device *dev)
fsi_free_minor(sbefifo->dev.devt);
device_for_each_child(dev, NULL, sbefifo_unregister_child);
put_device(&sbefifo->dev);
-
- return 0;
}
static const struct fsi_device_id sbefifo_ids[] = {
@@ -1131,26 +1130,14 @@ static const struct fsi_device_id sbefifo_ids[] = {
static struct fsi_driver sbefifo_drv = {
.id_table = sbefifo_ids,
+ .probe = sbefifo_probe,
+ .remove = sbefifo_remove,
.drv = {
.name = DEVICE_NAME,
- .bus = &fsi_bus_type,
- .probe = sbefifo_probe,
- .remove = sbefifo_remove,
}
};
-static int sbefifo_init(void)
-{
- return fsi_driver_register(&sbefifo_drv);
-}
-
-static void sbefifo_exit(void)
-{
- fsi_driver_unregister(&sbefifo_drv);
-}
-
-module_init(sbefifo_init);
-module_exit(sbefifo_exit);
+module_fsi_driver(sbefifo_drv);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Brad Bishop <bradleyb@fuzziesquirrel.com>");
MODULE_AUTHOR("Eddie James <eajames@linux.vnet.ibm.com>");
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index 411ddc018cd8..67cd45605fe4 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -527,16 +527,16 @@ static void scom_free(struct device *dev)
kfree(scom);
}
-static int scom_probe(struct device *dev)
+static int scom_probe(struct fsi_device *fsi_dev)
{
- struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct device *dev = &fsi_dev->dev;
struct scom_device *scom;
int rc, didx;
scom = kzalloc(sizeof(*scom), GFP_KERNEL);
if (!scom)
return -ENOMEM;
- dev_set_drvdata(dev, scom);
+ fsi_set_drvdata(fsi_dev, scom);
mutex_init(&scom->lock);
/* Grab a reference to the device (parent of our cdev), we'll drop it later */
@@ -574,9 +574,9 @@ static int scom_probe(struct device *dev)
return rc;
}
-static int scom_remove(struct device *dev)
+static void scom_remove(struct fsi_device *fsi_dev)
{
- struct scom_device *scom = dev_get_drvdata(dev);
+ struct scom_device *scom = fsi_get_drvdata(fsi_dev);
mutex_lock(&scom->lock);
scom->dead = true;
@@ -584,8 +584,6 @@ static int scom_remove(struct device *dev)
cdev_device_del(&scom->cdev, &scom->dev);
fsi_free_minor(scom->dev.devt);
put_device(&scom->dev);
-
- return 0;
}
static const struct of_device_id scom_of_ids[] = {
@@ -604,26 +602,14 @@ static const struct fsi_device_id scom_ids[] = {
static struct fsi_driver scom_drv = {
.id_table = scom_ids,
+ .probe = scom_probe,
+ .remove = scom_remove,
.drv = {
.name = "scom",
- .bus = &fsi_bus_type,
.of_match_table = scom_of_ids,
- .probe = scom_probe,
- .remove = scom_remove,
}
};
-static int scom_init(void)
-{
- return fsi_driver_register(&scom_drv);
-}
-
-static void scom_exit(void)
-{
- fsi_driver_unregister(&scom_drv);
-}
-
-module_init(scom_init);
-module_exit(scom_exit);
+module_fsi_driver(scom_drv);
MODULE_DESCRIPTION("SCOM FSI Client device driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/fsi/i2cr-scom.c b/drivers/fsi/i2cr-scom.c
index cb7e02213032..3efca2e944bb 100644
--- a/drivers/fsi/i2cr-scom.c
+++ b/drivers/fsi/i2cr-scom.c
@@ -81,9 +81,9 @@ static const struct file_operations i2cr_scom_fops = {
.write = i2cr_scom_write,
};
-static int i2cr_scom_probe(struct device *dev)
+static int i2cr_scom_probe(struct fsi_device *fsi_dev)
{
- struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct device *dev = &fsi_dev->dev;
struct i2cr_scom *scom;
int didx;
int ret;
@@ -115,14 +115,12 @@ static int i2cr_scom_probe(struct device *dev)
return ret;
}
-static int i2cr_scom_remove(struct device *dev)
+static void i2cr_scom_remove(struct fsi_device *fsi_dev)
{
- struct i2cr_scom *scom = dev_get_drvdata(dev);
+ struct i2cr_scom *scom = dev_get_drvdata(&fsi_dev->dev);
cdev_device_del(&scom->cdev, &scom->dev);
fsi_free_minor(scom->dev.devt);
-
- return 0;
}
static const struct of_device_id i2cr_scom_of_ids[] = {
@@ -137,13 +135,12 @@ static const struct fsi_device_id i2cr_scom_ids[] = {
};
static struct fsi_driver i2cr_scom_driver = {
+ .probe = i2cr_scom_probe,
+ .remove = i2cr_scom_remove,
.id_table = i2cr_scom_ids,
.drv = {
.name = "i2cr_scom",
- .bus = &fsi_bus_type,
.of_match_table = i2cr_scom_of_ids,
- .probe = i2cr_scom_probe,
- .remove = i2cr_scom_remove,
}
};
diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.c b/drivers/gpib/agilent_82350b/agilent_82350b.c
index 01a5bb43cd2d..d55d097aa6f0 100644
--- a/drivers/gpib/agilent_82350b/agilent_82350b.c
+++ b/drivers/gpib/agilent_82350b/agilent_82350b.c
@@ -599,8 +599,9 @@ static int agilent_82350b_generic_attach(struct gpib_board *board,
board->status = 0;
- if (agilent_82350b_allocate_private(board))
- return -ENOMEM;
+ retval = agilent_82350b_allocate_private(board);
+ if (retval)
+ return retval;
a_priv = board->private_data;
a_priv->using_fifos = use_fifos;
tms_priv = &a_priv->tms9914_priv;
diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c
index 77c8e549b208..26d2830c3fa3 100644
--- a/drivers/gpib/agilent_82357a/agilent_82357a.c
+++ b/drivers/gpib/agilent_82357a/agilent_82357a.c
@@ -1316,7 +1316,7 @@ static int agilent_82357a_attach(struct gpib_board *board, const struct gpib_boa
return -ERESTARTSYS;
retval = agilent_82357a_allocate_private(board);
- if (retval < 0) {
+ if (retval) {
mutex_unlock(&agilent_82357a_hotplug_lock);
return retval;
}
diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c
index 24c61b151071..e9d5fd19b495 100644
--- a/drivers/gpib/cb7210/cb7210.c
+++ b/drivers/gpib/cb7210/cb7210.c
@@ -856,11 +856,10 @@ static int cb7210_allocate_private(struct gpib_board *board)
{
struct cb7210_priv *priv;
- board->private_data = kmalloc(sizeof(struct cb7210_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct cb7210_priv), GFP_KERNEL);
if (!board->private_data)
return -ENOMEM;
priv = board->private_data;
- memset(priv, 0, sizeof(struct cb7210_priv));
init_nec7210_private(&priv->nec7210_priv);
return 0;
}
@@ -876,11 +875,13 @@ static int cb7210_generic_attach(struct gpib_board *board)
{
struct cb7210_priv *cb_priv;
struct nec7210_priv *nec_priv;
+ int retval;
board->status = 0;
- if (cb7210_allocate_private(board))
- return -ENOMEM;
+ retval = cb7210_allocate_private(board);
+ if (retval)
+ return retval;
cb_priv = board->private_data;
nec_priv = &cb_priv->nec7210_priv;
nec_priv->read_byte = nec7210_locking_ioport_read_byte;
diff --git a/drivers/gpib/cec/cec_gpib.c b/drivers/gpib/cec/cec_gpib.c
index dbf9b95baabc..7e57d65d6c32 100644
--- a/drivers/gpib/cec/cec_gpib.c
+++ b/drivers/gpib/cec/cec_gpib.c
@@ -220,11 +220,10 @@ static int cec_allocate_private(struct gpib_board *board)
{
struct cec_priv *priv;
- board->private_data = kmalloc(sizeof(struct cec_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct cec_priv), GFP_KERNEL);
if (!board->private_data)
- return -1;
+ return -ENOMEM;
priv = board->private_data;
- memset(priv, 0, sizeof(struct cec_priv));
init_nec7210_private(&priv->nec7210_priv);
return 0;
}
@@ -239,11 +238,13 @@ static int cec_generic_attach(struct gpib_board *board)
{
struct cec_priv *cec_priv;
struct nec7210_priv *nec_priv;
+ int retval;
board->status = 0;
- if (cec_allocate_private(board))
- return -ENOMEM;
+ retval = cec_allocate_private(board);
+ if (retval)
+ return retval;
cec_priv = board->private_data;
nec_priv = &cec_priv->nec7210_priv;
nec_priv->read_byte = nec7210_ioport_read_byte;
diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c
index 7cbb6a467177..b672dd6aad25 100644
--- a/drivers/gpib/common/iblib.c
+++ b/drivers/gpib/common/iblib.c
@@ -227,11 +227,10 @@ int ibonline(struct gpib_board *board)
#ifndef CONFIG_NIOS2
board->autospoll_task = kthread_run(&autospoll_thread, board,
"gpib%d_autospoll_kthread", board->minor);
- retval = IS_ERR(board->autospoll_task);
- if (retval) {
+ if (IS_ERR(board->autospoll_task)) {
dev_err(board->gpib_dev, "failed to create autospoll thread\n");
board->interface->detach(board);
- return retval;
+ return PTR_ERR(board->autospoll_task);
}
#endif
board->online = 1;
diff --git a/drivers/gpib/eastwood/fluke_gpib.c b/drivers/gpib/eastwood/fluke_gpib.c
index 3ae848e3f738..56153b8a1cb2 100644
--- a/drivers/gpib/eastwood/fluke_gpib.c
+++ b/drivers/gpib/eastwood/fluke_gpib.c
@@ -853,11 +853,10 @@ static int fluke_allocate_private(struct gpib_board *board)
{
struct fluke_priv *priv;
- board->private_data = kmalloc(sizeof(struct fluke_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct fluke_priv), GFP_KERNEL);
if (!board->private_data)
return -ENOMEM;
priv = board->private_data;
- memset(priv, 0, sizeof(struct fluke_priv));
init_nec7210_private(&priv->nec7210_priv);
priv->dma_buffer_size = 0x7ff;
priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL);
@@ -887,7 +886,7 @@ static int fluke_generic_attach(struct gpib_board *board)
board->status = 0;
retval = fluke_allocate_private(board);
- if (retval < 0)
+ if (retval)
return retval;
e_priv = board->private_data;
nec_priv = &e_priv->nec7210_priv;
diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c
index f7bfb4a8e553..d82ec06b3085 100644
--- a/drivers/gpib/fmh_gpib/fmh_gpib.c
+++ b/drivers/gpib/fmh_gpib/fmh_gpib.c
@@ -1250,11 +1250,10 @@ static int fmh_gpib_allocate_private(struct gpib_board *board)
{
struct fmh_priv *priv;
- board->private_data = kmalloc(sizeof(struct fmh_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct fmh_priv), GFP_KERNEL);
if (!board->private_data)
return -ENOMEM;
priv = board->private_data;
- memset(priv, 0, sizeof(struct fmh_priv));
init_nec7210_private(&priv->nec7210_priv);
priv->dma_buffer_size = 0x800;
priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL);
@@ -1286,7 +1285,7 @@ static int fmh_gpib_generic_attach(struct gpib_board *board)
board->status = 0;
retval = fmh_gpib_allocate_private(board);
- if (retval < 0)
+ if (retval)
return retval;
e_priv = board->private_data;
nec_priv = &e_priv->nec7210_priv;
diff --git a/drivers/gpib/gpio/gpib_bitbang.c b/drivers/gpib/gpio/gpib_bitbang.c
index 374cd61355e9..ba99d6c95ddf 100644
--- a/drivers/gpib/gpio/gpib_bitbang.c
+++ b/drivers/gpib/gpio/gpib_bitbang.c
@@ -1068,7 +1068,7 @@ static int allocate_private(struct gpib_board *board)
{
board->private_data = kzalloc(sizeof(struct bb_priv), GFP_KERNEL);
if (!board->private_data)
- return -1;
+ return -ENOMEM;
return 0;
}
@@ -1205,14 +1205,15 @@ static void bb_detach(struct gpib_board *board)
static int bb_attach(struct gpib_board *board, const struct gpib_board_config *config)
{
struct bb_priv *priv;
- int retval = 0;
+ int retval;
dbg_printk(2, "%s\n", "Enter ...");
board->status = 0;
- if (allocate_private(board))
- return -ENOMEM;
+ retval = allocate_private(board);
+ if (retval)
+ return retval;
priv = board->private_data;
priv->direction = -1;
priv->t1_delay = 2000;
diff --git a/drivers/gpib/hp_82335/hp82335.c b/drivers/gpib/hp_82335/hp82335.c
index d0e47ef77c87..9baf1d3b6751 100644
--- a/drivers/gpib/hp_82335/hp82335.c
+++ b/drivers/gpib/hp_82335/hp82335.c
@@ -212,7 +212,7 @@ static int hp82335_allocate_private(struct gpib_board *board)
{
board->private_data = kzalloc(sizeof(struct hp82335_priv), GFP_KERNEL);
if (!board->private_data)
- return -1;
+ return -ENOMEM;
return 0;
}
@@ -253,8 +253,9 @@ static int hp82335_attach(struct gpib_board *board, const struct gpib_board_conf
board->status = 0;
- if (hp82335_allocate_private(board))
- return -ENOMEM;
+ retval = hp82335_allocate_private(board);
+ if (retval)
+ return retval;
hp_priv = board->private_data;
tms_priv = &hp_priv->tms9914_priv;
tms_priv->read_byte = hp82335_read_byte;
diff --git a/drivers/gpib/hp_82341/hp_82341.c b/drivers/gpib/hp_82341/hp_82341.c
index 1a2ad0560e14..5aaf5b15b98b 100644
--- a/drivers/gpib/hp_82341/hp_82341.c
+++ b/drivers/gpib/hp_82341/hp_82341.c
@@ -693,8 +693,9 @@ static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_con
int retval;
board->status = 0;
- if (hp_82341_allocate_private(board))
- return -ENOMEM;
+ retval = hp_82341_allocate_private(board);
+ if (retval)
+ return retval;
hp_priv = board->private_data;
tms_priv = &hp_priv->tms9914_priv;
tms_priv->read_byte = hp_82341_read_byte;
diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c
index a3cf846fd0f9..737de5fef4b3 100644
--- a/drivers/gpib/ines/ines_gpib.c
+++ b/drivers/gpib/ines/ines_gpib.c
@@ -657,11 +657,10 @@ static int ines_allocate_private(struct gpib_board *board)
{
struct ines_priv *priv;
- board->private_data = kmalloc(sizeof(struct ines_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct ines_priv), GFP_KERNEL);
if (!board->private_data)
- return -1;
+ return -ENOMEM;
priv = board->private_data;
- memset(priv, 0, sizeof(struct ines_priv));
init_nec7210_private(&priv->nec7210_priv);
return 0;
}
@@ -676,11 +675,13 @@ static int ines_generic_attach(struct gpib_board *board)
{
struct ines_priv *ines_priv;
struct nec7210_priv *nec_priv;
+ int retval;
board->status = 0;
- if (ines_allocate_private(board))
- return -ENOMEM;
+ retval = ines_allocate_private(board);
+ if (retval)
+ return retval;
ines_priv = board->private_data;
nec_priv = &ines_priv->nec7210_priv;
nec_priv->read_byte = nec7210_ioport_read_byte;
diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.c b/drivers/gpib/ni_usb/ni_usb_gpib.c
index 1f8412de9fa3..c6f1a70d44f5 100644
--- a/drivers/gpib/ni_usb/ni_usb_gpib.c
+++ b/drivers/gpib/ni_usb/ni_usb_gpib.c
@@ -566,7 +566,7 @@ static int ni_usb_write_registers(struct ni_usb_priv *ni_priv,
retval, bytes_read);
ni_usb_dump_raw_block(in_data, bytes_read);
kfree(in_data);
- return retval;
+ return retval ?: -EINVAL;
}
mutex_unlock(&ni_priv->addressed_transfer_lock);
@@ -1659,11 +1659,10 @@ static int ni_usb_allocate_private(struct gpib_board *board)
{
struct ni_usb_priv *ni_priv;
- board->private_data = kmalloc(sizeof(struct ni_usb_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct ni_usb_priv), GFP_KERNEL);
if (!board->private_data)
return -ENOMEM;
ni_priv = board->private_data;
- memset(ni_priv, 0, sizeof(struct ni_usb_priv));
mutex_init(&ni_priv->bulk_transfer_lock);
mutex_init(&ni_priv->control_transfer_lock);
mutex_init(&ni_priv->interrupt_transfer_lock);
@@ -1780,7 +1779,7 @@ static int ni_usb_setup_init(struct gpib_board *board, struct ni_usb_register *w
i++;
if (i > NUM_INIT_WRITES) {
dev_err(&usb_dev->dev, "bug!, buffer overrun, i=%i\n", i);
- return 0;
+ return -EINVAL;
}
return i;
}
@@ -1799,10 +1798,12 @@ static int ni_usb_init(struct gpib_board *board)
return -ENOMEM;
writes_len = ni_usb_setup_init(board, writes);
- if (writes_len)
- retval = ni_usb_write_registers(ni_priv, writes, writes_len, &ibsta);
- else
- return -EFAULT;
+ if (writes_len < 0) {
+ kfree(writes);
+ return writes_len;
+ }
+
+ retval = ni_usb_write_registers(ni_priv, writes, writes_len, &ibsta);
kfree(writes);
if (retval) {
dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
@@ -2233,7 +2234,7 @@ static int ni_usb_attach(struct gpib_board *board, const struct gpib_board_confi
mutex_lock(&ni_usb_hotplug_lock);
retval = ni_usb_allocate_private(board);
- if (retval < 0) {
+ if (retval) {
mutex_unlock(&ni_usb_hotplug_lock);
return retval;
}
diff --git a/drivers/gpib/pc2/pc2_gpib.c b/drivers/gpib/pc2/pc2_gpib.c
index 9f3943d1df66..1664861d360d 100644
--- a/drivers/gpib/pc2/pc2_gpib.c
+++ b/drivers/gpib/pc2/pc2_gpib.c
@@ -237,11 +237,10 @@ static int allocate_private(struct gpib_board *board)
{
struct pc2_priv *priv;
- board->private_data = kmalloc(sizeof(struct pc2_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct pc2_priv), GFP_KERNEL);
if (!board->private_data)
- return -1;
+ return -ENOMEM;
priv = board->private_data;
- memset(priv, 0, sizeof(struct pc2_priv));
init_nec7210_private(&priv->nec7210_priv);
return 0;
}
@@ -257,10 +256,12 @@ static int pc2_generic_attach(struct gpib_board *board, const struct gpib_board_
{
struct pc2_priv *pc2_priv;
struct nec7210_priv *nec_priv;
+ int retval;
board->status = 0;
- if (allocate_private(board))
- return -ENOMEM;
+ retval = allocate_private(board);
+ if (retval)
+ return retval;
pc2_priv = board->private_data;
nec_priv = &pc2_priv->nec7210_priv;
nec_priv->read_byte = nec7210_ioport_read_byte;
diff --git a/drivers/gpib/tnt4882/tnt4882_gpib.c b/drivers/gpib/tnt4882/tnt4882_gpib.c
index c03a976b7380..6d241509419e 100644
--- a/drivers/gpib/tnt4882/tnt4882_gpib.c
+++ b/drivers/gpib/tnt4882/tnt4882_gpib.c
@@ -843,11 +843,10 @@ static int tnt4882_allocate_private(struct gpib_board *board)
{
struct tnt4882_priv *tnt_priv;
- board->private_data = kmalloc(sizeof(struct tnt4882_priv), GFP_KERNEL);
+ board->private_data = kzalloc(sizeof(struct tnt4882_priv), GFP_KERNEL);
if (!board->private_data)
- return -1;
+ return -ENOMEM;
tnt_priv = board->private_data;
- memset(tnt_priv, 0, sizeof(struct tnt4882_priv));
init_nec7210_private(&tnt_priv->nec7210_priv);
return 0;
}
@@ -916,8 +915,9 @@ static int ni_pci_attach(struct gpib_board *board, const struct gpib_board_confi
board->status = 0;
- if (tnt4882_allocate_private(board))
- return -ENOMEM;
+ retval = tnt4882_allocate_private(board);
+ if (retval)
+ return retval;
tnt_priv = board->private_data;
nec_priv = &tnt_priv->nec7210_priv;
nec_priv->type = TNT4882;
@@ -1039,8 +1039,9 @@ static int ni_isa_attach_common(struct gpib_board *board, const struct gpib_boar
board->status = 0;
- if (tnt4882_allocate_private(board))
- return -ENOMEM;
+ retval = tnt4882_allocate_private(board);
+ if (retval)
+ return retval;
tnt_priv = board->private_data;
nec_priv = &tnt_priv->nec7210_priv;
nec_priv->type = chipset;
@@ -1725,8 +1726,9 @@ static int ni_pcmcia_attach(struct gpib_board *board, const struct gpib_board_co
board->status = 0;
- if (tnt4882_allocate_private(board))
- return -ENOMEM;
+ retval = tnt4882_allocate_private(board);
+ if (retval)
+ return retval;
tnt_priv = board->private_data;
nec_priv = &tnt_priv->nec7210_priv;
diff --git a/drivers/greybus/core.c b/drivers/greybus/core.c
index 313eb65cf703..45c5437c460f 100644
--- a/drivers/greybus/core.c
+++ b/drivers/greybus/core.c
@@ -185,13 +185,6 @@ static void greybus_shutdown(struct device *dev)
}
}
-const struct bus_type greybus_bus_type = {
- .name = "greybus",
- .match = greybus_match_device,
- .uevent = greybus_uevent,
- .shutdown = greybus_shutdown,
-};
-
static int greybus_probe(struct device *dev)
{
struct greybus_driver *driver = to_greybus_driver(dev->driver);
@@ -252,7 +245,7 @@ static int greybus_probe(struct device *dev)
return 0;
}
-static int greybus_remove(struct device *dev)
+static void greybus_remove(struct device *dev)
{
struct greybus_driver *driver = to_greybus_driver(dev->driver);
struct gb_bundle *bundle = to_gb_bundle(dev);
@@ -291,10 +284,17 @@ static int greybus_remove(struct device *dev)
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_put_noidle(dev);
-
- return 0;
}
+const struct bus_type greybus_bus_type = {
+ .name = "greybus",
+ .match = greybus_match_device,
+ .uevent = greybus_uevent,
+ .probe = greybus_probe,
+ .remove = greybus_remove,
+ .shutdown = greybus_shutdown,
+};
+
int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
const char *mod_name)
{
@@ -305,8 +305,6 @@ int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
driver->driver.bus = &greybus_bus_type;
driver->driver.name = driver->name;
- driver->driver.probe = greybus_probe;
- driver->driver.remove = greybus_remove;
driver->driver.owner = owner;
driver->driver.mod_name = mod_name;
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 17afa0f4cdee..3c8a6f795094 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -13,6 +13,7 @@
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/perf_event.h>
+#include <linux/perf/arm_pmu.h>
#include <linux/percpu-defs.h>
#include <linux/slab.h>
#include <linux/stringhash.h>
@@ -50,26 +51,22 @@ struct etm_ctxt {
static DEFINE_PER_CPU(struct etm_ctxt, etm_ctxt);
static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
-/*
- * The PMU formats were orignally for ETMv3.5/PTM's ETMCR 'config';
- * now take them as general formats and apply on all ETMs.
- */
-PMU_FORMAT_ATTR(branch_broadcast, "config:"__stringify(ETM_OPT_BRANCH_BROADCAST));
-PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC));
-/* contextid1 enables tracing CONTEXTIDR_EL1 for ETMv4 */
-PMU_FORMAT_ATTR(contextid1, "config:" __stringify(ETM_OPT_CTXTID));
-/* contextid2 enables tracing CONTEXTIDR_EL2 for ETMv4 */
-PMU_FORMAT_ATTR(contextid2, "config:" __stringify(ETM_OPT_CTXTID2));
-PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS));
-PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK));
+GEN_PMU_FORMAT_ATTR(cycacc);
+GEN_PMU_FORMAT_ATTR(timestamp);
+GEN_PMU_FORMAT_ATTR(retstack);
+GEN_PMU_FORMAT_ATTR(sinkid);
+
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+GEN_PMU_FORMAT_ATTR(branch_broadcast);
+/* contextid1 enables tracing CONTEXTIDR_EL1*/
+GEN_PMU_FORMAT_ATTR(contextid1);
+/* contextid2 enables tracing CONTEXTIDR_EL2*/
+GEN_PMU_FORMAT_ATTR(contextid2);
/* preset - if sink ID is used as a configuration selector */
-PMU_FORMAT_ATTR(preset, "config:0-3");
-/* Sink ID - same for all ETMs */
-PMU_FORMAT_ATTR(sinkid, "config2:0-31");
+GEN_PMU_FORMAT_ATTR(preset);
/* config ID - set if a system configuration is selected */
-PMU_FORMAT_ATTR(configid, "config2:32-63");
-PMU_FORMAT_ATTR(cc_threshold, "config3:0-11");
-
+GEN_PMU_FORMAT_ATTR(configid);
+GEN_PMU_FORMAT_ATTR(cc_threshold);
/*
* contextid always traces the "PID". The PID is in CONTEXTIDR_EL1
@@ -80,29 +77,35 @@ static ssize_t format_attr_contextid_show(struct device *dev,
struct device_attribute *attr,
char *page)
{
- int pid_fmt = ETM_OPT_CTXTID;
-
-#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
- pid_fmt = is_kernel_in_hyp_mode() ? ETM_OPT_CTXTID2 : ETM_OPT_CTXTID;
-#endif
- return sprintf(page, "config:%d\n", pid_fmt);
+ if (is_kernel_in_hyp_mode())
+ return contextid2_show(dev, attr, page);
+ return contextid1_show(dev, attr, page);
}
static struct device_attribute format_attr_contextid =
__ATTR(contextid, 0444, format_attr_contextid_show, NULL);
+#endif
+/*
+ * ETMv3 only uses the first 3 attributes for programming itself (see
+ * ETM3X_SUPPORTED_OPTIONS). Sink ID is also supported for selecting a
+ * sink in both, but not used for configuring the ETM. The remaining
+ * attributes are ETMv4 specific.
+ */
static struct attribute *etm_config_formats_attr[] = {
&format_attr_cycacc.attr,
- &format_attr_contextid.attr,
- &format_attr_contextid1.attr,
- &format_attr_contextid2.attr,
&format_attr_timestamp.attr,
&format_attr_retstack.attr,
&format_attr_sinkid.attr,
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+ &format_attr_contextid.attr,
+ &format_attr_contextid1.attr,
+ &format_attr_contextid2.attr,
&format_attr_preset.attr,
&format_attr_configid.attr,
&format_attr_branch_broadcast.attr,
&format_attr_cc_threshold.attr,
+#endif
NULL,
};
@@ -315,7 +318,7 @@ static bool sinks_compatible(struct coresight_device *a,
static void *etm_setup_aux(struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
{
- u32 id, cfg_hash;
+ u32 sink_hash, cfg_hash;
int cpu = event->cpu;
cpumask_t *mask;
struct coresight_device *sink = NULL;
@@ -328,13 +331,12 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
INIT_WORK(&event_data->work, free_event_data);
/* First get the selected sink from user space. */
- if (event->attr.config2 & GENMASK_ULL(31, 0)) {
- id = (u32)event->attr.config2;
- sink = user_sink = coresight_get_sink_by_id(id);
- }
+ sink_hash = ATTR_CFG_GET_FLD(&event->attr, sinkid);
+ if (sink_hash)
+ sink = user_sink = coresight_get_sink_by_id(sink_hash);
/* check if user wants a coresight configuration selected */
- cfg_hash = (u32)((event->attr.config2 & GENMASK_ULL(63, 32)) >> 32);
+ cfg_hash = ATTR_CFG_GET_FLD(&event->attr, configid);
if (cfg_hash) {
if (cscfg_activate_config(cfg_hash))
goto err;
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
index 5febbcdb8696..24d929428633 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.h
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -20,6 +20,44 @@ struct cscfg_config_desc;
*/
#define ETM_ADDR_CMP_MAX 8
+#define ATTR_CFG_FLD_preset_CFG config
+#define ATTR_CFG_FLD_preset_LO 0
+#define ATTR_CFG_FLD_preset_HI 3
+#define ATTR_CFG_FLD_timestamp_CFG config
+#define ATTR_CFG_FLD_timestamp_LO 4
+#define ATTR_CFG_FLD_timestamp_HI 7
+#define ATTR_CFG_FLD_branch_broadcast_CFG config
+#define ATTR_CFG_FLD_branch_broadcast_LO 8
+#define ATTR_CFG_FLD_branch_broadcast_HI 8
+#define ATTR_CFG_FLD_cycacc_CFG config
+#define ATTR_CFG_FLD_cycacc_LO 12
+#define ATTR_CFG_FLD_cycacc_HI 12
+#define ATTR_CFG_FLD_contextid1_CFG config
+#define ATTR_CFG_FLD_contextid1_LO 14
+#define ATTR_CFG_FLD_contextid1_HI 14
+#define ATTR_CFG_FLD_contextid2_CFG config
+#define ATTR_CFG_FLD_contextid2_LO 15
+#define ATTR_CFG_FLD_contextid2_HI 15
+/*
+ * Old position of 'timestamp' and not published in sysfs. Remove at a later
+ * date if necessary.
+ */
+#define ATTR_CFG_FLD_deprecated_timestamp_CFG config
+#define ATTR_CFG_FLD_deprecated_timestamp_LO 28
+#define ATTR_CFG_FLD_deprecated_timestamp_HI 28
+#define ATTR_CFG_FLD_retstack_CFG config
+#define ATTR_CFG_FLD_retstack_LO 29
+#define ATTR_CFG_FLD_retstack_HI 29
+#define ATTR_CFG_FLD_sinkid_CFG config2
+#define ATTR_CFG_FLD_sinkid_LO 0
+#define ATTR_CFG_FLD_sinkid_HI 31
+#define ATTR_CFG_FLD_configid_CFG config2
+#define ATTR_CFG_FLD_configid_LO 32
+#define ATTR_CFG_FLD_configid_HI 63
+#define ATTR_CFG_FLD_cc_threshold_CFG config3
+#define ATTR_CFG_FLD_cc_threshold_LO 0
+#define ATTR_CFG_FLD_cc_threshold_HI 11
+
/**
* struct etm_filter - single instruction range or start/stop configuration.
* @start_addr: The address to start tracing on.
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c
index a5e809589d3e..a547a6d2e0bd 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include <linux/clk.h>
#include <linux/perf_event.h>
+#include <linux/perf/arm_pmu.h>
#include <asm/sections.h>
#include "coresight-etm.h"
@@ -309,6 +310,7 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata,
{
struct etm_config *config = &drvdata->config;
struct perf_event_attr *attr = &event->attr;
+ u8 ts_level;
if (!attr)
return -EINVAL;
@@ -332,28 +334,31 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata,
if (config->mode)
etm_config_trace_mode(config);
- /*
- * At this time only cycle accurate, return stack and timestamp
- * options are available.
- */
- if (attr->config & ~ETM3X_SUPPORTED_OPTIONS)
- return -EINVAL;
+ config->ctrl = 0;
+
+ if (ATTR_CFG_GET_FLD(attr, cycacc))
+ config->ctrl |= ETMCR_CYC_ACC;
- config->ctrl = attr->config;
+ ts_level = max(ATTR_CFG_GET_FLD(attr, timestamp),
+ ATTR_CFG_GET_FLD(attr, deprecated_timestamp));
+
+ if (ts_level > 1) {
+ dev_dbg(&drvdata->csdev->dev,
+ "timestamp format attribute should be 0 (off) or 1 (on)\n");
+ return -EINVAL;
+ }
- /* Don't trace contextID when runs in non-root PID namespace */
- if (!task_is_in_init_pid_ns(current))
- config->ctrl &= ~ETMCR_CTXID_SIZE;
+ if (ts_level)
+ config->ctrl |= ETMCR_TIMESTAMP_EN;
/*
- * Possible to have cores with PTM (supports ret stack) and ETM
- * (never has ret stack) on the same SoC. So if we have a request
- * for return stack that can't be honoured on this core then
- * clear the bit - trace will still continue normally
+ * Possible to have cores with PTM (supports ret stack) and ETM (never
+ * has ret stack) on the same SoC. So only enable when it can be honored
+ * - trace will still continue normally otherwise.
*/
- if ((config->ctrl & ETMCR_RETURN_STACK) &&
- !(drvdata->etmccer & ETMCCER_RETSTACK))
- config->ctrl &= ~ETMCR_RETURN_STACK;
+ if (ATTR_CFG_GET_FLD(attr, retstack) &&
+ (drvdata->etmccer & ETMCCER_RETSTACK))
+ config->ctrl |= ETMCR_RETURN_STACK;
return 0;
}
@@ -795,16 +800,16 @@ static int __init etm_hp_setup(void)
{
int ret;
- ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING,
- "arm/coresight:starting",
- etm_starting_cpu, etm_dying_cpu);
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
+ "arm/coresight:starting",
+ etm_starting_cpu, etm_dying_cpu);
if (ret)
return ret;
- ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN,
- "arm/coresight:online",
- etm_online_cpu, NULL);
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "arm/coresight:online",
+ etm_online_cpu, NULL);
/* HP dyn state ID returned in ret on success */
if (ret > 0) {
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 560975b70474..d565a73f0042 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -29,6 +29,7 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/perf_event.h>
+#include <linux/perf/arm_pmu.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
@@ -642,18 +643,34 @@ static void etm4_enable_sysfs_smp_call(void *info)
* TRCRSCTLR1 (always true) used to get the counter to decrement. From
* there a resource selector is configured with the counter and the
* timestamp control register to use the resource selector to trigger the
- * event that will insert a timestamp packet in the stream.
+ * event that will insert a timestamp packet in the stream:
+ *
+ * +--------------+
+ * | Resource 1 | fixed "always-true" resource
+ * +--------------+
+ * |
+ * +------v-------+
+ * | Counter x | (reload to 2 ^ (ts_level - 1) on underflow)
+ * +--------------+
+ * |
+ * +------v--------------+
+ * | Resource Selector y | (trigger on counter x == 0)
+ * +---------------------+
+ * |
+ * +------v---------------+
+ * | Timestamp Generator | (timestamp on resource y)
+ * +----------------------+
*/
-static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
+static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata,
+ u8 ts_level)
{
- int ctridx, ret = -EINVAL;
- int counter, rselector;
- u32 val = 0;
+ int ctridx;
+ int rselector;
struct etmv4_config *config = &drvdata->config;
/* No point in trying if we don't have at least one counter */
if (!drvdata->nr_cntr)
- goto out;
+ return -EINVAL;
/* Find a counter that hasn't been initialised */
for (ctridx = 0; ctridx < drvdata->nr_cntr; ctridx++)
@@ -663,15 +680,19 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
/* All the counters have been configured already, bail out */
if (ctridx == drvdata->nr_cntr) {
pr_debug("%s: no available counter found\n", __func__);
- ret = -ENOSPC;
- goto out;
+ return -ENOSPC;
}
/*
- * Searching for an available resource selector to use, starting at
- * '2' since every implementation has at least 2 resource selector.
- * ETMIDR4 gives the number of resource selector _pairs_,
- * hence multiply by 2.
+ * Searching for an available resource selector to use, starting at '2'
+ * since resource 0 is the fixed 'always returns false' resource and 1
+ * is the fixed 'always returns true' resource. See IHI0064H_b '7.3.64
+ * TRCRSCTLRn, Resource Selection Control Registers, n=2-31'. If there
+ * are no resources, there would also be no counters so wouldn't get
+ * here.
+ *
+ * ETMIDR4 gives the number of resource selector _pairs_, hence multiply
+ * by 2.
*/
for (rselector = 2; rselector < drvdata->nr_resource * 2; rselector++)
if (!config->res_ctrl[rselector])
@@ -680,40 +701,47 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
if (rselector == drvdata->nr_resource * 2) {
pr_debug("%s: no available resource selector found\n",
__func__);
- ret = -ENOSPC;
- goto out;
+ return -ENOSPC;
}
- /* Remember what counter we used */
- counter = 1 << ctridx;
+ /* Initialise original and reload counter value. */
+ config->cntr_val[ctridx] = config->cntrldvr[ctridx] = 1 << (ts_level - 1);
/*
- * Initialise original and reload counter value to the smallest
- * possible value in order to get as much precision as we can.
+ * Trace Counter Control Register TRCCNTCTLRn
+ *
+ * CNTCHAIN = 0, don't reload on the previous counter
+ * RLDSELF = true, reload counter automatically on underflow
+ * RLDEVENT = RES_SEL_FALSE (0), reload on single false resource (never reload)
+ * CNTEVENT = RES_SEL_TRUE (1), count single fixed 'always true' resource (always decrement)
*/
- config->cntr_val[ctridx] = 1;
- config->cntrldvr[ctridx] = 1;
-
- /* Set the trace counter control register */
- val = 0x1 << 16 | /* Bit 16, reload counter automatically */
- 0x0 << 7 | /* Select single resource selector */
- 0x1; /* Resource selector 1, i.e always true */
-
- config->cntr_ctrl[ctridx] = val;
+ config->cntr_ctrl[ctridx] = TRCCNTCTLRn_RLDSELF |
+ FIELD_PREP(TRCCNTCTLRn_RLDEVENT_MASK,
+ etm4_res_sel_single(ETM4_RES_SEL_FALSE)) |
+ FIELD_PREP(TRCCNTCTLRn_CNTEVENT_MASK,
+ etm4_res_sel_single(ETM4_RES_SEL_TRUE));
- val = 0x2 << 16 | /* Group 0b0010 - Counter and sequencers */
- counter << 0; /* Counter to use */
-
- config->res_ctrl[rselector] = val;
-
- val = 0x0 << 7 | /* Select single resource selector */
- rselector; /* Resource selector */
+ /*
+ * Resource Selection Control Register TRCRSCTLRn
+ *
+ * PAIRINV = 0, INV = 0, don't invert
+ * GROUP = 2, SELECT = ctridx, trigger when counter 'ctridx' reaches 0
+ *
+ * Multiple counters can be selected, and each bit signifies a counter,
+ * so set bit 'ctridx' to select our counter.
+ */
+ config->res_ctrl[rselector] = FIELD_PREP(TRCRSCTLRn_GROUP_MASK, 2) |
+ FIELD_PREP(TRCRSCTLRn_SELECT_MASK, 1 << ctridx);
- config->ts_ctrl = val;
+ /*
+ * Global Timestamp Control Register TRCTSCTLR
+ *
+ * EVENT = generate timestamp on single resource 'rselector'
+ */
+ config->ts_ctrl = FIELD_PREP(TRCTSCTLR_EVENT_MASK,
+ etm4_res_sel_single(rselector));
- ret = 0;
-out:
- return ret;
+ return 0;
}
static int etm4_parse_event_config(struct coresight_device *csdev,
@@ -722,9 +750,13 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etmv4_config *config = &drvdata->config;
+ struct perf_event_attr max_timestamp = {
+ .ATTR_CFG_FLD_timestamp_CFG = U64_MAX,
+ };
struct perf_event_attr *attr = &event->attr;
unsigned long cfg_hash;
int preset, cc_threshold;
+ u8 ts_level;
/* Clear configuration from previous run */
memset(config, 0, sizeof(struct etmv4_config));
@@ -750,47 +782,51 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
goto out;
/* Go from generic option to ETMv4 specifics */
- if (attr->config & BIT(ETM_OPT_CYCACC)) {
+ if (ATTR_CFG_GET_FLD(attr, cycacc)) {
config->cfg |= TRCCONFIGR_CCI;
/* TRM: Must program this for cycacc to work */
- cc_threshold = attr->config3 & ETM_CYC_THRESHOLD_MASK;
+ cc_threshold = ATTR_CFG_GET_FLD(attr, cc_threshold);
if (!cc_threshold)
cc_threshold = ETM_CYC_THRESHOLD_DEFAULT;
if (cc_threshold < drvdata->ccitmin)
cc_threshold = drvdata->ccitmin;
config->ccctlr = cc_threshold;
}
- if (attr->config & BIT(ETM_OPT_TS)) {
- /*
- * Configure timestamps to be emitted at regular intervals in
- * order to correlate instructions executed on different CPUs
- * (CPU-wide trace scenarios).
- */
- ret = etm4_config_timestamp_event(drvdata);
+ ts_level = max(ATTR_CFG_GET_FLD(attr, timestamp),
+ ATTR_CFG_GET_FLD(attr, deprecated_timestamp));
+ if (ts_level) {
/*
- * No need to go further if timestamp intervals can't
- * be configured.
+ * Don't do counter generated timestamps when ts_level == MAX.
+ * Leave only SYNC timestamps from TRCCONFIGR_TS.
*/
- if (ret)
- goto out;
+ if (ts_level != ATTR_CFG_GET_FLD(&max_timestamp, timestamp)) {
+ ret = etm4_config_timestamp_event(drvdata, ts_level);
+
+ /*
+ * Error if user asked for timestamps but there was no
+ * free counter.
+ */
+ if (ret)
+ goto out;
+ }
/* bit[11], Global timestamp tracing bit */
config->cfg |= TRCCONFIGR_TS;
}
/* Only trace contextID when runs in root PID namespace */
- if ((attr->config & BIT(ETM_OPT_CTXTID)) &&
+ if (ATTR_CFG_GET_FLD(attr, contextid1) &&
task_is_in_init_pid_ns(current))
/* bit[6], Context ID tracing bit */
config->cfg |= TRCCONFIGR_CID;
/*
- * If set bit ETM_OPT_CTXTID2 in perf config, this asks to trace VMID
- * for recording CONTEXTIDR_EL2. Do not enable VMID tracing if the
- * kernel is not running in EL2.
+ * If set bit contextid2 in perf config, this asks to trace VMID for
+ * recording CONTEXTIDR_EL2. Do not enable VMID tracing if the kernel
+ * is not running in EL2.
*/
- if (attr->config & BIT(ETM_OPT_CTXTID2)) {
+ if (ATTR_CFG_GET_FLD(attr, contextid2)) {
if (!is_kernel_in_hyp_mode()) {
ret = -EINVAL;
goto out;
@@ -801,26 +837,22 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
}
/* return stack - enable if selected and supported */
- if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack)
+ if (ATTR_CFG_GET_FLD(attr, retstack) && drvdata->retstack)
/* bit[12], Return stack enable bit */
config->cfg |= TRCCONFIGR_RS;
/*
- * Set any selected configuration and preset.
- *
- * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset)
- * in the perf attributes defined in coresight-etm-perf.c.
- * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config.
- * A zero configid means no configuration active, preset = 0 means no preset selected.
+ * Set any selected configuration and preset. A zero configid means no
+ * configuration active, preset = 0 means no preset selected.
*/
- if (attr->config2 & GENMASK_ULL(63, 32)) {
- cfg_hash = (u32)(attr->config2 >> 32);
- preset = attr->config & 0xF;
+ cfg_hash = ATTR_CFG_GET_FLD(attr, configid);
+ if (cfg_hash) {
+ preset = ATTR_CFG_GET_FLD(attr, preset);
ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
}
/* branch broadcast - enable if selected and supported */
- if (attr->config & BIT(ETM_OPT_BRANCH_BROADCAST)) {
+ if (ATTR_CFG_GET_FLD(attr, branch_broadcast)) {
if (!drvdata->trcbb) {
/*
* Missing BB support could cause silent decode errors
@@ -829,7 +861,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
ret = -EINVAL;
goto out;
} else {
- config->cfg |= BIT(ETM4_CFG_BIT_BB);
+ config->cfg |= TRCCONFIGR_BB;
}
}
@@ -1053,11 +1085,8 @@ static int etm4_disable_perf(struct coresight_device *csdev,
return -EINVAL;
etm4_disable_hw(drvdata);
- /*
- * The config_id occupies bits 63:32 of the config2 perf event attr
- * field. If this is non-zero then we will have enabled a config.
- */
- if (attr->config2 & GENMASK_ULL(63, 32))
+ /* If configid is non-zero then we will have enabled a config. */
+ if (ATTR_CFG_GET_FLD(attr, configid))
cscfg_csdev_disable_active_config(csdev);
/*
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index 012c52fd1933..89d81ce4e04e 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -225,6 +225,50 @@
#define TRCRSCTLRn_GROUP_MASK GENMASK(19, 16)
#define TRCRSCTLRn_SELECT_MASK GENMASK(15, 0)
+#define TRCCNTCTLRn_CNTCHAIN BIT(17)
+#define TRCCNTCTLRn_RLDSELF BIT(16)
+#define TRCCNTCTLRn_RLDEVENT_MASK GENMASK(15, 8)
+#define TRCCNTCTLRn_CNTEVENT_MASK GENMASK(7, 0)
+
+#define TRCTSCTLR_EVENT_MASK GENMASK(7, 0)
+
+#define ETM4_RES_SEL_FALSE 0 /* Fixed function 'always false' resource selector */
+#define ETM4_RES_SEL_TRUE 1 /* Fixed function 'always true' resource selector */
+
+#define ETM4_RES_SEL_SINGLE_MASK GENMASK(4, 0)
+#define ETM4_RES_SEL_PAIR_MASK GENMASK(3, 0)
+#define ETM4_RES_SEL_TYPE_PAIR BIT(7)
+
+/*
+ * Utilities for programming EVENT resource selectors, e.g. TRCCNTCTLRn_RLDEVENT.
+ *
+ * Resource selectors have a common format across registers:
+ *
+ * 7 6 5 4 0
+ * +------+------+-------+
+ * | TYPE | RES0 | SEL |
+ * +------+------+-------+
+ *
+ * Where TYPE indicates whether the selector is for a single event or a pair.
+ * When TYPE is pair, SEL is 4 bits wide and using pair 0 is UNPREDICTABLE.
+ * Otherwise for single it's 5 bits wide.
+ */
+static inline u32 etm4_res_sel_single(u8 res_sel_idx)
+{
+ WARN_ON_ONCE(!FIELD_FIT(ETM4_RES_SEL_SINGLE_MASK, res_sel_idx));
+ return FIELD_PREP(ETM4_RES_SEL_SINGLE_MASK, res_sel_idx);
+}
+
+static inline u32 etm4_res_sel_pair(u8 res_sel_idx)
+{
+ if (__builtin_constant_p(res_sel_idx))
+ BUILD_BUG_ON(res_sel_idx == 0);
+ WARN_ON_ONCE(!FIELD_FIT(ETM4_RES_SEL_PAIR_MASK, res_sel_idx) ||
+ (res_sel_idx == 0));
+ return FIELD_PREP(ETM4_RES_SEL_PAIR_MASK, res_sel_idx) |
+ ETM4_RES_SEL_TYPE_PAIR;
+}
+
/*
* System instructions to access ETM registers.
* See ETMv4.4 spec ARM IHI0064F section 4.3.6 System instructions
@@ -824,8 +868,7 @@ struct etmv4_config {
u32 eventctrl0;
u32 eventctrl1;
u32 stall_ctrl;
- u32 ts_ctrl;
- u32 syncfreq;
+ u32 ts_ctrl; /* TRCTSCTLR */
u32 ccctlr;
u32 bb_ctrl;
u32 vinst_ctrl;
@@ -833,15 +876,16 @@ struct etmv4_config {
u32 vissctlr;
u32 vipcssctlr;
u8 seq_idx;
+ u8 syncfreq;
u32 seq_ctrl[ETM_MAX_SEQ_STATES];
u32 seq_rst;
u32 seq_state;
u8 cntr_idx;
- u32 cntrldvr[ETMv4_MAX_CNTR];
- u32 cntr_ctrl[ETMv4_MAX_CNTR];
- u32 cntr_val[ETMv4_MAX_CNTR];
+ u32 cntrldvr[ETMv4_MAX_CNTR]; /* TRCCNTRLDVRn */
+ u32 cntr_ctrl[ETMv4_MAX_CNTR]; /* TRCCNTCTLRn */
+ u32 cntr_val[ETMv4_MAX_CNTR]; /* TRCCNTVRn */
u8 res_idx;
- u32 res_ctrl[ETM_MAX_RES_SEL];
+ u32 res_ctrl[ETM_MAX_RES_SEL]; /* TRCRSCTLRn */
u8 ss_idx;
u32 ss_ctrl[ETM_MAX_SS_CMP];
u32 ss_status[ETM_MAX_SS_CMP];
@@ -1016,27 +1060,27 @@ struct etmv4_drvdata {
u8 ns_ex_level;
u8 q_support;
u8 os_lock_model;
- bool sticky_enable;
- bool boot_enable;
- bool os_unlock;
- bool instrp0;
- bool q_filt;
- bool trcbb;
- bool trccond;
- bool retstack;
- bool trccci;
- bool trc_error;
- bool syncpr;
- bool stallctl;
- bool sysstall;
- bool nooverflow;
- bool atbtrig;
- bool lpoverride;
+ bool sticky_enable : 1;
+ bool boot_enable : 1;
+ bool os_unlock : 1;
+ bool instrp0 : 1;
+ bool q_filt : 1;
+ bool trcbb : 1;
+ bool trccond : 1;
+ bool retstack : 1;
+ bool trccci : 1;
+ bool trc_error : 1;
+ bool syncpr : 1;
+ bool stallctl : 1;
+ bool sysstall : 1;
+ bool nooverflow : 1;
+ bool atbtrig : 1;
+ bool lpoverride : 1;
+ bool skip_power_up : 1;
+ bool paused : 1;
u64 trfcr;
struct etmv4_config config;
struct etmv4_save_state *save_state;
- bool skip_power_up;
- bool paused;
DECLARE_BITMAP(arch_features, ETM4_IMPDEF_FEATURE_MAX);
};
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index e0d83ee01b77..cee82e52c4ea 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1307,6 +1307,19 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
/*
+ * Since the sysfs buffer allocation and the hardware enablement is not
+ * in the same critical region, it's possible to race with the perf.
+ */
+ if (coresight_get_mode(csdev) == CS_MODE_PERF) {
+ drvdata->sysfs_buf = NULL;
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+ /* Free allocated memory out side of the spinlock */
+ tmc_etr_free_sysfs_buf(sysfs_buf);
+ return -EBUSY;
+ }
+
+ /*
* In sysFS mode we can have multiple writers per sink. Since this
* sink is already enabled no memory is needed and the HW need not be
* touched, even if the buffer size has changed.
@@ -1354,9 +1367,7 @@ EXPORT_SYMBOL_GPL(tmc_etr_get_buffer);
/*
* alloc_etr_buf: Allocate ETR buffer for use by perf.
- * The size of the hardware buffer is dependent on the size configured
- * via sysfs and the perf ring buffer size. We prefer to allocate the
- * largest possible size, scaling down the size by half until it
+ * Allocate the largest possible size, scaling down the size by half until it
* reaches a minimum limit (1M), beyond which we give up.
*/
static struct etr_buf *
@@ -1365,36 +1376,26 @@ alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event,
{
int node;
struct etr_buf *etr_buf;
- unsigned long size;
+ ssize_t size;
node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu);
- /*
- * Try to match the perf ring buffer size if it is larger
- * than the size requested via sysfs.
- */
- if ((nr_pages << PAGE_SHIFT) > drvdata->size) {
- etr_buf = tmc_alloc_etr_buf(drvdata, ((ssize_t)nr_pages << PAGE_SHIFT),
- 0, node, NULL);
- if (!IS_ERR(etr_buf))
- goto done;
- }
+
+ /* Use the minimum limit if the required size is smaller */
+ size = nr_pages << PAGE_SHIFT;
+ size = max_t(ssize_t, size, TMC_ETR_PERF_MIN_BUF_SIZE);
/*
- * Else switch to configured size for this ETR
- * and scale down until we hit the minimum limit.
+ * Try to allocate the required size for this ETR, if failed scale
+ * down until we hit the minimum limit.
*/
- size = drvdata->size;
do {
etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL);
if (!IS_ERR(etr_buf))
- goto done;
+ return etr_buf;
size /= 2;
} while (size >= TMC_ETR_PERF_MIN_BUF_SIZE);
return ERR_PTR(-ENOMEM);
-
-done:
- return etr_buf;
}
static struct etr_buf *
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 95473d131032..319a354ede9f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -221,6 +221,7 @@ struct tmc_resrv_buf {
* @pid: Process ID of the process that owns the session that is using
* this component. For example this would be the pid of the Perf
* process.
+ * @reading: buffer's in the reading through "/dev/xyz.tmc" entry
* @stop_on_flush: Stop on flush trigger user configuration.
* @buf: Snapshot of the trace data for ETF/ETB.
* @etr_buf: details of buffer used in TMC-ETR
@@ -233,6 +234,7 @@ struct tmc_resrv_buf {
* @trigger_cntr: amount of words to store after a trigger.
* @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the
* device configuration register (DEVID)
+ * @etr_mode: User preferred mode of the ETR device, default auto mode.
* @idr: Holds etr_bufs allocated for this ETR.
* @idr_mutex: Access serialisation for idr.
* @sysfs_buf: SYSFS buffer for ETR.
diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c
index ff9a0a9cfe96..1128612e70a7 100644
--- a/drivers/hwtracing/coresight/coresight-tnoc.c
+++ b/drivers/hwtracing/coresight/coresight-tnoc.c
@@ -34,6 +34,7 @@
* @base: memory mapped base address for this component.
* @dev: device node for trace_noc_drvdata.
* @csdev: component vitals needed by the framework.
+ * @pclk: APB clock if present, otherwise NULL
* @spinlock: serialize enable/disable operation.
* @atid: id for the trace packet.
*/
@@ -41,8 +42,9 @@ struct trace_noc_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
+ struct clk *pclk;
spinlock_t spinlock;
- u32 atid;
+ int atid;
};
DEFINE_CORESIGHT_DEVLIST(trace_noc_devs, "traceNoc");
@@ -51,6 +53,12 @@ static void trace_noc_enable_hw(struct trace_noc_drvdata *drvdata)
{
u32 val;
+ /* No valid ATID, simply enable the unit */
+ if (drvdata->atid == -EOPNOTSUPP) {
+ writel(TRACE_NOC_CTRL_PORTEN, drvdata->base + TRACE_NOC_CTRL);
+ return;
+ }
+
/* Set ATID */
writel_relaxed(drvdata->atid, drvdata->base + TRACE_NOC_XLD);
@@ -124,6 +132,11 @@ static int trace_noc_init_default_data(struct trace_noc_drvdata *drvdata)
{
int atid;
+ if (!dev_is_amba(drvdata->dev)) {
+ drvdata->atid = -EOPNOTSUPP;
+ return 0;
+ }
+
atid = coresight_trace_id_get_system_id();
if (atid < 0)
return atid;
@@ -149,8 +162,21 @@ static struct attribute *coresight_tnoc_attrs[] = {
NULL,
};
+static umode_t trace_id_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ if (attr == &dev_attr_traceid.attr && drvdata->atid < 0)
+ return 0;
+
+ return attr->mode;
+}
+
static const struct attribute_group coresight_tnoc_group = {
.attrs = coresight_tnoc_attrs,
+ .is_visible = trace_id_is_visible,
};
static const struct attribute_group *coresight_tnoc_groups[] = {
@@ -158,9 +184,8 @@ static const struct attribute_group *coresight_tnoc_groups[] = {
NULL,
};
-static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
+static int _tnoc_probe(struct device *dev, struct resource *res)
{
- struct device *dev = &adev->dev;
struct coresight_platform_data *pdata;
struct trace_noc_drvdata *drvdata;
struct coresight_desc desc = { 0 };
@@ -173,16 +198,20 @@ static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
- adev->dev.platform_data = pdata;
+ dev->platform_data = pdata;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- drvdata->dev = &adev->dev;
+ drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
- drvdata->base = devm_ioremap_resource(dev, &adev->res);
+ ret = coresight_get_enable_clocks(dev, &drvdata->pclk, NULL);
+ if (ret)
+ return ret;
+
+ drvdata->base = devm_ioremap_resource(dev, res);
if (IS_ERR(drvdata->base))
return PTR_ERR(drvdata->base);
@@ -195,20 +224,31 @@ static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
desc.ops = &trace_noc_cs_ops;
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
- desc.pdata = adev->dev.platform_data;
- desc.dev = &adev->dev;
+ desc.pdata = pdata;
+ desc.dev = dev;
desc.access = CSDEV_ACCESS_IOMEM(drvdata->base);
desc.groups = coresight_tnoc_groups;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
- coresight_trace_id_put_system_id(drvdata->atid);
+ if (drvdata->atid > 0)
+ coresight_trace_id_put_system_id(drvdata->atid);
return PTR_ERR(drvdata->csdev);
}
- pm_runtime_put(&adev->dev);
return 0;
}
+static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ int ret;
+
+ ret = _tnoc_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
+}
+
static void trace_noc_remove(struct amba_device *adev)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(&adev->dev);
@@ -240,7 +280,81 @@ static struct amba_driver trace_noc_driver = {
.id_table = trace_noc_ids,
};
-module_amba_driver(trace_noc_driver);
+static int itnoc_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 = _tnoc_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void itnoc_remove(struct platform_device *pdev)
+{
+ struct trace_noc_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ coresight_unregister(drvdata->csdev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+#ifdef CONFIG_PM
+static int itnoc_runtime_suspend(struct device *dev)
+{
+ struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(drvdata->pclk);
+
+ return 0;
+}
+
+static int itnoc_runtime_resume(struct device *dev)
+{
+ struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(drvdata->pclk);
+}
+#endif
+
+static const struct dev_pm_ops itnoc_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(itnoc_runtime_suspend, itnoc_runtime_resume, NULL)
+};
+
+static const struct of_device_id itnoc_of_match[] = {
+ { .compatible = "qcom,coresight-itnoc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, itnoc_of_match);
+
+static struct platform_driver itnoc_driver = {
+ .probe = itnoc_probe,
+ .remove = itnoc_remove,
+ .driver = {
+ .name = "coresight-itnoc",
+ .of_match_table = itnoc_of_match,
+ .suppress_bind_attrs = true,
+ .pm = &itnoc_dev_pm_ops,
+ },
+};
+
+static int __init tnoc_init(void)
+{
+ return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver, THIS_MODULE);
+}
+
+static void __exit tnoc_exit(void)
+{
+ coresight_remove_driver(&trace_noc_driver, &itnoc_driver);
+}
+module_init(tnoc_init);
+module_exit(tnoc_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Trace NOC driver");
diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c
index 3a3825d27f86..7055f8f13427 100644
--- a/drivers/hwtracing/coresight/coresight-tpda.c
+++ b/drivers/hwtracing/coresight/coresight-tpda.c
@@ -137,12 +137,46 @@ static int tpda_get_element_size(struct tpda_drvdata *drvdata,
/* Settings pre enabling port control register */
static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
{
- u32 val;
+ u32 val = 0;
- val = readl_relaxed(drvdata->base + TPDA_CR);
- val &= ~TPDA_CR_ATID;
val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid);
+ if (drvdata->trig_async)
+ val |= TPDA_CR_SRIE;
+
+ if (drvdata->trig_flag_ts)
+ val |= TPDA_CR_FLRIE;
+
+ if (drvdata->trig_freq)
+ val |= TPDA_CR_FRIE;
+
+ if (drvdata->freq_ts)
+ val |= TPDA_CR_FREQTS;
+
+ if (drvdata->cmbchan_mode)
+ val |= TPDA_CR_CMBCHANMODE;
+
writel_relaxed(val, drvdata->base + TPDA_CR);
+
+ /*
+ * If FLRIE bit is set, set the master and channel
+ * id as zero
+ */
+ if (drvdata->trig_flag_ts)
+ writel_relaxed(0x0, drvdata->base + TPDA_FPID_CR);
+
+ /* Initialize with a value of 0 */
+ val = 0;
+ if (drvdata->syncr_mode)
+ val |= TPDA_SYNCR_MODE_CTRL_MASK;
+
+ if (drvdata->syncr_count > 0 &&
+ drvdata->syncr_count < TPDA_SYNCR_COUNT_MASK)
+ val |= drvdata->syncr_count;
+ else
+ /* Program the count to its MAX value by default */
+ val |= TPDA_SYNCR_COUNT_MASK;
+
+ writel_relaxed(val, drvdata->base + TPDA_SYNCR);
}
static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
@@ -258,6 +292,248 @@ static const struct coresight_ops tpda_cs_ops = {
.link_ops = &tpda_link_ops,
};
+/* Read cross-trigger register member */
+static ssize_t tpda_trig_sysfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_trig_sysfs_attribute *tpda_attr =
+ container_of(attr, struct tpda_trig_sysfs_attribute, attr);
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ guard(spinlock)(&drvdata->spinlock);
+ switch (tpda_attr->mem) {
+ case FREQTS:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->freq_ts);
+ case FRIE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->trig_freq);
+ case FLRIE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->trig_flag_ts);
+ case SRIE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->trig_async);
+ case CMBCHANMODE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->cmbchan_mode);
+
+ }
+ return -EINVAL;
+}
+
+static ssize_t tpda_trig_sysfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_trig_sysfs_attribute *tpda_attr =
+ container_of(attr, struct tpda_trig_sysfs_attribute, attr);
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ switch (tpda_attr->mem) {
+ case FREQTS:
+ drvdata->freq_ts = !!val;
+ break;
+ case FRIE:
+ drvdata->trig_freq = !!val;
+ break;
+ case FLRIE:
+ drvdata->trig_flag_ts = !!val;
+ break;
+ case SRIE:
+ drvdata->trig_async = !!val;
+ break;
+ case CMBCHANMODE:
+ drvdata->cmbchan_mode = !!val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+static ssize_t global_flush_req_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_CR);
+ /* read global_flush_req bit */
+ val &= TPDA_CR_FLREQ;
+
+ return sysfs_emit(buf, "%lu\n", val);
+}
+
+static ssize_t global_flush_req_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (!drvdata->csdev->refcnt || !val)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_CR);
+ /* set global_flush_req bit */
+ val |= TPDA_CR_FLREQ;
+ CS_UNLOCK(drvdata->base);
+ writel_relaxed(val, drvdata->base + TPDA_CR);
+ CS_LOCK(drvdata->base);
+
+ return size;
+}
+static DEVICE_ATTR_RW(global_flush_req);
+
+static ssize_t syncr_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val, syncr_val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ syncr_val = readl_relaxed(drvdata->base + TPDA_SYNCR);
+ val = FIELD_GET(TPDA_SYNCR_MODE_CTRL_MASK, syncr_val);
+
+ return sysfs_emit(buf, "%lu\n", val);
+}
+
+static ssize_t syncr_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ /* set the mode when first enabling the device */
+ drvdata->syncr_mode = !!val;
+
+ return size;
+}
+static DEVICE_ATTR_RW(syncr_mode);
+
+static ssize_t syncr_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_SYNCR);
+ val &= TPDA_SYNCR_COUNT_MASK;
+
+ return sysfs_emit(buf, "%lu\n", val);
+}
+
+static ssize_t syncr_count_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val > TPDA_SYNCR_COUNT_MASK)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ drvdata->syncr_count = val;
+
+ return size;
+}
+static DEVICE_ATTR_RW(syncr_count);
+
+static ssize_t port_flush_req_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_FLUSH_CR);
+
+ return sysfs_emit(buf, "0x%lx\n", val);
+}
+
+static ssize_t port_flush_req_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u32 val;
+
+ if (kstrtou32(buf, 0, &val))
+ return -EINVAL;
+
+ if (!drvdata->csdev->refcnt || !val)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ CS_UNLOCK(drvdata->base);
+ writel_relaxed(val, drvdata->base + TPDA_FLUSH_CR);
+ CS_LOCK(drvdata->base);
+
+ return size;
+}
+static DEVICE_ATTR_RW(port_flush_req);
+
+static struct attribute *tpda_attrs[] = {
+ &dev_attr_global_flush_req.attr,
+ &dev_attr_syncr_mode.attr,
+ &dev_attr_syncr_count.attr,
+ &dev_attr_port_flush_req.attr,
+ tpda_trig_sysfs_rw(freq_ts_enable, FREQTS),
+ tpda_trig_sysfs_rw(trig_freq_enable, FRIE),
+ tpda_trig_sysfs_rw(trig_flag_ts_enable, FLRIE),
+ tpda_trig_sysfs_rw(trig_async_enable, SRIE),
+ tpda_trig_sysfs_rw(cmbchan_mode, CMBCHANMODE),
+ NULL,
+};
+
+static struct attribute_group tpda_attr_grp = {
+ .attrs = tpda_attrs,
+};
+
+static const struct attribute_group *tpda_attr_grps[] = {
+ &tpda_attr_grp,
+ NULL,
+};
+
static int tpda_init_default_data(struct tpda_drvdata *drvdata)
{
int atid;
@@ -273,6 +549,7 @@ static int tpda_init_default_data(struct tpda_drvdata *drvdata)
return atid;
drvdata->atid = atid;
+ drvdata->freq_ts = true;
return 0;
}
@@ -316,6 +593,7 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id)
desc.ops = &tpda_cs_ops;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
+ desc.groups = tpda_attr_grps;
desc.access = CSDEV_ACCESS_IOMEM(base);
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
diff --git a/drivers/hwtracing/coresight/coresight-tpda.h b/drivers/hwtracing/coresight/coresight-tpda.h
index c6af3d2da3ef..a090352009bb 100644
--- a/drivers/hwtracing/coresight/coresight-tpda.h
+++ b/drivers/hwtracing/coresight/coresight-tpda.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023,2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _CORESIGHT_CORESIGHT_TPDA_H
@@ -8,6 +8,29 @@
#define TPDA_CR (0x000)
#define TPDA_Pn_CR(n) (0x004 + (n * 4))
+#define TPDA_FPID_CR (0x084)
+#define TPDA_SYNCR (0x08C)
+#define TPDA_FLUSH_CR (0x090)
+
+/* Cross trigger global (all ports) flush request bit */
+#define TPDA_CR_FLREQ BIT(0)
+/* Cross trigger FREQ packets timestamp bit */
+#define TPDA_CR_FREQTS BIT(2)
+/* Cross trigger FREQ packet request bit */
+#define TPDA_CR_FRIE BIT(3)
+/* Cross trigger FLAG packet request interface bit */
+#define TPDA_CR_FLRIE BIT(4)
+/* Cross trigger synchronization bit */
+#define TPDA_CR_SRIE BIT(5)
+/* Bits 6 ~ 12 is for atid value */
+#define TPDA_CR_ATID GENMASK(12, 6)
+/*
+ * Channel mode bit of the packetization of CMB/MCB traffic
+ * 0 - raw channel mapping mode
+ * 1 - channel pair marking mode
+ */
+#define TPDA_CR_CMBCHANMODE BIT(20)
+
/* Aggregator port enable bit */
#define TPDA_Pn_CR_ENA BIT(0)
/* Aggregator port CMB data set element size bit */
@@ -15,10 +38,12 @@
/* Aggregator port DSB data set element size bit */
#define TPDA_Pn_CR_DSBSIZE BIT(8)
-#define TPDA_MAX_INPORTS 32
+/* TPDA_SYNCR count mask */
+#define TPDA_SYNCR_COUNT_MASK GENMASK(11, 0)
+/* TPDA_SYNCR mode control bit */
+#define TPDA_SYNCR_MODE_CTRL_MASK GENMASK(12, 12)
-/* Bits 6 ~ 12 is for atid value */
-#define TPDA_CR_ATID GENMASK(12, 6)
+#define TPDA_MAX_INPORTS 32
/**
* struct tpda_drvdata - specifics associated to an TPDA component
@@ -29,6 +54,13 @@
* @enable: enable status of the component.
* @dsb_esize Record the DSB element size.
* @cmb_esize Record the CMB element size.
+ * @trig_async: Enable/disable cross trigger synchronization sequence interface.
+ * @trig_flag_ts: Enable/disable cross trigger FLAG packet request interface.
+ * @trig_freq: Enable/disable cross trigger FREQ packet request interface.
+ * @freq_ts: Enable/disable the timestamp for all FREQ packets.
+ * @cmbchan_mode: Configure the CMB/MCMB channel mode.
+ * @syncr_mode: Setting the mode for counting packets.
+ * @syncr_count: Setting the value of the count.
*/
struct tpda_drvdata {
void __iomem *base;
@@ -38,6 +70,42 @@ struct tpda_drvdata {
u8 atid;
u32 dsb_esize;
u32 cmb_esize;
+ bool trig_async;
+ bool trig_flag_ts;
+ bool trig_freq;
+ bool freq_ts;
+ bool cmbchan_mode;
+ bool syncr_mode;
+ u32 syncr_count;
+};
+
+/* Enumerate members of global control register(cr) */
+enum tpda_cr_mem {
+ FREQTS,
+ FRIE,
+ FLRIE,
+ SRIE,
+ CMBCHANMODE
+};
+
+/**
+ * struct tpda_trig_sysfs_attribute - Record the member variables of cross
+ * trigger register that need to be operated by sysfs file
+ * @attr: The device attribute
+ * @mem: The member in the control register data structure
+ */
+struct tpda_trig_sysfs_attribute {
+ struct device_attribute attr;
+ enum tpda_cr_mem mem;
};
+#define tpda_trig_sysfs_rw(name, mem) \
+ (&((struct tpda_trig_sysfs_attribute[]) { \
+ { \
+ __ATTR(name, 0644, tpda_trig_sysfs_show, \
+ tpda_trig_sysfs_store), \
+ mem, \
+ } \
+ })[0].attr.attr)
+
#endif /* _CORESIGHT_CORESIGHT_TPDA_H */
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index e3def163d5cf..6cd5ac3c6430 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -14,6 +14,7 @@
#include <linux/pci.h>
#include "intel_th.h"
+#include "pci_ids.h"
#define DRIVER_NAME "intel_th_pci"
@@ -141,225 +142,55 @@ static const struct intel_th_drvdata intel_th_2x = {
};
static const struct pci_device_id intel_th_pci_id_table[] = {
- {
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa126),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- /* Apollo Lake */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a8e),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- /* Broxton */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0a80),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- /* Broxton B-step */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1a8e),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- /* Kaby Lake PCH-H */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa2a6),
- .driver_data = (kernel_ulong_t)&intel_th_1x_multi_is_broken,
- },
- {
- /* Denverton */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x19e1),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- /* Lewisburg PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa1a6),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- /* Lewisburg PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa226),
- .driver_data = (kernel_ulong_t)0,
- },
- {
- /* Gemini Lake */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Cannon Lake H */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Cannon Lake LP */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Cedar Fork PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x18e1),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Ice Lake PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x34a6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Comet Lake */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x02a6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Comet Lake PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x06a6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Comet Lake PCH-V */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa3a6),
- .driver_data = (kernel_ulong_t)&intel_th_1x_multi_is_broken,
- },
- {
- /* Ice Lake NNPI */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x45c5),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Ice Lake CPU */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8a29),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Tiger Lake CPU */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9a33),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Tiger Lake PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa0a6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Tiger Lake PCH-H */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x43a6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Jasper Lake PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4da6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Jasper Lake CPU */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4e29),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Elkhart Lake CPU */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4529),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Elkhart Lake */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4b26),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Emmitsburg PCH */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1bcc),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Alder Lake */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7aa6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Alder Lake-P */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x51a6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Alder Lake-M */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x54a6),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Meteor Lake-P */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7e24),
- .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,
- },
- {
- /* Raptor Lake-S CPU */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa76f),
- .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,
- },
- {
- /* Arrow Lake */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7724),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Panther Lake-H */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe324),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- {
- /* Panther Lake-P/U */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe424),
- .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,
- },
- {
- /* Rocket Lake CPU */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c19),
- .driver_data = (kernel_ulong_t)&intel_th_2x,
- },
- { 0 },
+ { PCI_DEVICE_DATA(INTEL, NPK_CML, &intel_th_2x) }, /* Comet Lake */
+ { PCI_DEVICE_DATA(INTEL, NPK_CML_PCH, &intel_th_2x) }, /* Comet Lake PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_GNR, &intel_th_2x) }, /* Granite Rapids */
+ { PCI_DEVICE_DATA(INTEL, NPK_BXT, NULL) }, /* Broxton */
+ { PCI_DEVICE_DATA(INTEL, NPK_CDF, &intel_th_2x) }, /* Cedar Fork PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_DNV, NULL) }, /* Denverton */
+ { PCI_DEVICE_DATA(INTEL, NPK_BXT_B, NULL) }, /* Broxton B-step */
+ { PCI_DEVICE_DATA(INTEL, NPK_EBG, &intel_th_2x) }, /* Emmitsburg PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_GLK, &intel_th_2x) }, /* Gemini Lake */
+ { PCI_DEVICE_DATA(INTEL, NPK_GNR_SOC, &intel_th_2x) }, /* Granite Rapids SOC */
+ { PCI_DEVICE_DATA(INTEL, NPK_SPR, &intel_th_2x) }, /* Sapphire Rapids SOC */
+ { PCI_DEVICE_DATA(INTEL, NPK_ICL_PCH, &intel_th_2x) }, /* Ice Lake PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_TGL_PCH_H, &intel_th_2x) }, /* Tiger Lake PCH-H */
+ { PCI_DEVICE_DATA(INTEL, NPK_EHL_CPU, &intel_th_2x) }, /* Elkhart Lake CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_ICL_NNPI, &intel_th_2x) }, /* Ice Lake NNPI */
+ { PCI_DEVICE_DATA(INTEL, NPK_ADL_CPU, &intel_th_2x) }, /* Alder Lake CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_EHL, &intel_th_2x) }, /* Elkhart Lake */
+ { PCI_DEVICE_DATA(INTEL, NPK_RKL, &intel_th_2x) }, /* Rocket Lake CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_JSL_PCH, &intel_th_2x) }, /* Jasper Lake PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_JSL_CPU, &intel_th_2x) }, /* Jasper Lake CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_ADL_P, &intel_th_2x) }, /* Alder Lake-P */
+ { PCI_DEVICE_DATA(INTEL, NPK_ADL_M, &intel_th_2x) }, /* Alder Lake-M */
+ { PCI_DEVICE_DATA(INTEL, NPK_APL, NULL) }, /* Apollo Lake */
+ { PCI_DEVICE_DATA(INTEL, NPK_NVL_PCH, &intel_th_2x) }, /* Nova Lake-PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_ARL, &intel_th_2x) }, /* Arrow Lake */
+ { PCI_DEVICE_DATA(INTEL, NPK_RPL_S, &intel_th_2x) }, /* Raptor Lake-S */
+ { PCI_DEVICE_DATA(INTEL, NPK_ADL, &intel_th_2x) }, /* Alder Lake */
+ { PCI_DEVICE_DATA(INTEL, NPK_MTL_P, &intel_th_2x) }, /* Meteor Lake-P */
+ { PCI_DEVICE_DATA(INTEL, NPK_MTL_S, &intel_th_2x) }, /* Meteor Lake-S */
+ { PCI_DEVICE_DATA(INTEL, NPK_ICL_CPU, &intel_th_2x) }, /* Ice Lake CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_TGL_CPU, &intel_th_2x) }, /* Tiger Lake CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_0, NULL) },
+ { PCI_DEVICE_DATA(INTEL, NPK_CNL_LP, &intel_th_2x) }, /* Cannon Lake LP */
+ { PCI_DEVICE_DATA(INTEL, NPK_TGL_PCH, &intel_th_2x) }, /* Tiger Lake PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_1, NULL) },
+ { PCI_DEVICE_DATA(INTEL, NPK_LBG_PCH, NULL) }, /* Lewisburg PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_LBG_PCH_2, NULL) }, /* Lewisburg PCH */
+ { PCI_DEVICE_DATA(INTEL, NPK_KBL_PCH, &intel_th_1x_multi_is_broken) }, /* Kaby Lake PCH-H */
+ { PCI_DEVICE_DATA(INTEL, NPK_CNL_H, &intel_th_2x) }, /* Cannon Lake H */
+ { PCI_DEVICE_DATA(INTEL, NPK_CML_PCH_V, &intel_th_1x_multi_is_broken) }, /* Comet Lake PCH-V */
+ { PCI_DEVICE_DATA(INTEL, NPK_RPL_S_CPU, &intel_th_2x) }, /* Raptor Lake-S CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_LNL, &intel_th_2x) }, /* Lunar Lake */
+ { PCI_DEVICE_DATA(INTEL, NPK_MTL_S_CPU, &intel_th_2x) }, /* Meteor Lake-S CPU */
+ { PCI_DEVICE_DATA(INTEL, NPK_NVL_P, &intel_th_2x) }, /* Nova Lake-P */
+ { PCI_DEVICE_DATA(INTEL, NPK_NVL_H, &intel_th_2x) }, /* Nova Lake-H */
+ { PCI_DEVICE_DATA(INTEL, NPK_NVL_S, &intel_th_2x) }, /* Nova Lake-S */
+ { PCI_DEVICE_DATA(INTEL, NPK_PTL_H, &intel_th_2x) }, /* Panther Lake-H */
+ { PCI_DEVICE_DATA(INTEL, NPK_PTL_PU, &intel_th_2x) }, /* Panther Lake-P/U */
+ { }
};
MODULE_DEVICE_TABLE(pci, intel_th_pci_id_table);
diff --git a/drivers/hwtracing/intel_th/pci_ids.h b/drivers/hwtracing/intel_th/pci_ids.h
new file mode 100644
index 000000000000..4a0c53beac22
--- /dev/null
+++ b/drivers/hwtracing/intel_th/pci_ids.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel(R) Trace Hub driver debugging
+ *
+ * Copyright (C) 2025 Intel Corporation.
+ */
+
+#ifndef __INTEL_TH_PCI_IDS_H__
+#define __INTEL_TH_PCI_IDS_H__
+
+#define PCI_DEVICE_ID_INTEL_NPK_CML 0x02a6
+#define PCI_DEVICE_ID_INTEL_NPK_CML_PCH 0x06a6
+#define PCI_DEVICE_ID_INTEL_NPK_GNR 0x0963
+#define PCI_DEVICE_ID_INTEL_NPK_BXT 0x0a80
+#define PCI_DEVICE_ID_INTEL_NPK_CDF 0x18e1
+#define PCI_DEVICE_ID_INTEL_NPK_DNV 0x19e1
+#define PCI_DEVICE_ID_INTEL_NPK_BXT_B 0x1a8e
+#define PCI_DEVICE_ID_INTEL_NPK_EBG 0x1bcc
+#define PCI_DEVICE_ID_INTEL_NPK_GLK 0x318e
+#define PCI_DEVICE_ID_INTEL_NPK_GNR_SOC 0x3256
+#define PCI_DEVICE_ID_INTEL_NPK_SPR 0x3456
+#define PCI_DEVICE_ID_INTEL_NPK_ICL_PCH 0x34a6
+#define PCI_DEVICE_ID_INTEL_NPK_TGL_PCH_H 0x43a6
+#define PCI_DEVICE_ID_INTEL_NPK_EHL_CPU 0x4529
+#define PCI_DEVICE_ID_INTEL_NPK_ICL_NNPI 0x45c5
+#define PCI_DEVICE_ID_INTEL_NPK_ADL_CPU 0x466f
+#define PCI_DEVICE_ID_INTEL_NPK_EHL 0x4b26
+#define PCI_DEVICE_ID_INTEL_NPK_RKL 0x4c19
+#define PCI_DEVICE_ID_INTEL_NPK_JSL_PCH 0x4da6
+#define PCI_DEVICE_ID_INTEL_NPK_JSL_CPU 0x4e29
+#define PCI_DEVICE_ID_INTEL_NPK_ADL_P 0x51a6
+#define PCI_DEVICE_ID_INTEL_NPK_ADL_M 0x54a6
+#define PCI_DEVICE_ID_INTEL_NPK_APL 0x5a8e
+#define PCI_DEVICE_ID_INTEL_NPK_NVL_PCH 0x6e26
+#define PCI_DEVICE_ID_INTEL_NPK_ARL 0x7724
+#define PCI_DEVICE_ID_INTEL_NPK_RPL_S 0x7a26
+#define PCI_DEVICE_ID_INTEL_NPK_ADL 0x7aa6
+#define PCI_DEVICE_ID_INTEL_NPK_MTL_P 0x7e24
+#define PCI_DEVICE_ID_INTEL_NPK_MTL_S 0x7f26
+#define PCI_DEVICE_ID_INTEL_NPK_ICL_CPU 0x8a29
+#define PCI_DEVICE_ID_INTEL_NPK_TGL_CPU 0x9a33
+#define PCI_DEVICE_ID_INTEL_NPK_0 0x9d26
+#define PCI_DEVICE_ID_INTEL_NPK_CNL_LP 0x9da6
+#define PCI_DEVICE_ID_INTEL_NPK_TGL_PCH 0xa0a6
+#define PCI_DEVICE_ID_INTEL_NPK_1 0xa126
+#define PCI_DEVICE_ID_INTEL_NPK_LBG_PCH 0xa1a6
+#define PCI_DEVICE_ID_INTEL_NPK_LBG_PCH_2 0xa226
+#define PCI_DEVICE_ID_INTEL_NPK_KBL_PCH 0xa2a6
+#define PCI_DEVICE_ID_INTEL_NPK_CNL_H 0xa326
+#define PCI_DEVICE_ID_INTEL_NPK_CML_PCH_V 0xa3a6
+#define PCI_DEVICE_ID_INTEL_NPK_RPL_S_CPU 0xa76f
+#define PCI_DEVICE_ID_INTEL_NPK_LNL 0xa824
+#define PCI_DEVICE_ID_INTEL_NPK_MTL_S_CPU 0xae24
+#define PCI_DEVICE_ID_INTEL_NPK_NVL_P 0xd224
+#define PCI_DEVICE_ID_INTEL_NPK_NVL_H 0xd324
+#define PCI_DEVICE_ID_INTEL_NPK_NVL_S 0xd424
+#define PCI_DEVICE_ID_INTEL_NPK_PTL_H 0xe324
+#define PCI_DEVICE_ID_INTEL_NPK_PTL_PU 0xe424
+
+#endif /* __INTEL_TH_PCI_IDS_H__ */
diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig
index eda6b11d40a1..cd7f0b0f3fbe 100644
--- a/drivers/hwtracing/stm/Kconfig
+++ b/drivers/hwtracing/stm/Kconfig
@@ -13,7 +13,7 @@ if STM
config STM_PROTO_BASIC
tristate "Basic STM framing protocol driver"
- default CONFIG_STM
+ default STM
help
This is a simple framing protocol for sending data over STM
devices. This was the protocol that the STM framework used
@@ -28,7 +28,7 @@ config STM_PROTO_BASIC
config STM_PROTO_SYS_T
tristate "MIPI SyS-T STM framing protocol driver"
- default CONFIG_STM
+ default STM
help
This is an implementation of MIPI SyS-T protocol to be used
over the STP transport. In addition to the data payload, it
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index ae016a9431da..3a7e577e6eac 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -674,8 +674,9 @@ static struct device_node *fsi_i2c_find_port_of_node(struct device_node *fsi,
return NULL;
}
-static int fsi_i2c_probe(struct device *dev)
+static int fsi_i2c_probe(struct fsi_device *fsi_dev)
{
+ struct device *dev = &fsi_dev->dev;
struct fsi_i2c_ctrl *i2c;
struct fsi_i2c_port *port;
struct device_node *np;
@@ -735,14 +736,14 @@ static int fsi_i2c_probe(struct device *dev)
list_add(&port->list, &i2c->ports);
}
- dev_set_drvdata(dev, i2c);
+ fsi_set_drvdata(fsi_dev, i2c);
return 0;
}
-static int fsi_i2c_remove(struct device *dev)
+static void fsi_i2c_remove(struct fsi_device *fsi_dev)
{
- struct fsi_i2c_ctrl *i2c = dev_get_drvdata(dev);
+ struct fsi_i2c_ctrl *i2c = fsi_get_drvdata(fsi_dev);
struct fsi_i2c_port *port, *tmp;
list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
@@ -750,8 +751,6 @@ static int fsi_i2c_remove(struct device *dev)
i2c_del_adapter(&port->adapter);
kfree(port);
}
-
- return 0;
}
static const struct fsi_device_id fsi_i2c_ids[] = {
@@ -761,11 +760,10 @@ static const struct fsi_device_id fsi_i2c_ids[] = {
static struct fsi_driver fsi_i2c_driver = {
.id_table = fsi_i2c_ids,
+ .probe = fsi_i2c_probe,
+ .remove = fsi_i2c_remove,
.drv = {
.name = "i2c-fsi",
- .bus = &fsi_bus_type,
- .probe = fsi_i2c_probe,
- .remove = fsi_i2c_remove,
},
};
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 76911278fb21..3d3f8d8673dd 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -64,7 +64,7 @@ config ADXL345
config ADXL345_I2C
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer I2C Driver"
- depends on INPUT_ADXL34X=n
+ depends on !INPUT_ADXL34X
depends on I2C
select ADXL345
select REGMAP_I2C
@@ -74,11 +74,12 @@ config ADXL345_I2C
To compile this driver as a module, choose M here: the module
will be called adxl345_i2c and you will also get adxl345_core
- for the core module.
+ for the core module. INPUT_ADXL34X share compatibles with this
+ driver, do not add both modules to the kernel.
config ADXL345_SPI
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer SPI Driver"
- depends on INPUT_ADXL34X=n
+ depends on !INPUT_ADXL34X
depends on SPI
select ADXL345
select REGMAP_SPI
@@ -88,7 +89,8 @@ config ADXL345_SPI
To compile this driver as a module, choose M here: the module
will be called adxl345_spi and you will also get adxl345_core
- for the core module.
+ for the core module. INPUT_ADXL34X share compatibles with this
+ driver, do not add both modules to the kernel.
config ADXL355
tristate
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 5fc7f814b907..1c1d64d5cbcb 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -768,9 +768,8 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq)
data->dready_trig->ops = &adxl355_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
- ret = devm_request_irq(data->dev, irq,
- &iio_trigger_generic_data_rdy_poll,
- IRQF_ONESHOT, "adxl355_irq", data->dready_trig);
+ ret = devm_request_irq(data->dev, irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, "adxl355_irq", data->dready_trig);
if (ret)
return dev_err_probe(data->dev, ret, "request irq %d failed\n",
irq);
diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index 46d518a2a029..28a8793a53b6 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -295,7 +295,6 @@ struct adxl372_state {
u32 inact_time_ms;
u8 fifo_set_size;
unsigned long int1_bitmask;
- unsigned long int2_bitmask;
u16 watermark;
__be16 fifo_buf[ADXL372_FIFO_SIZE];
bool peak_fifo_mode_en;
@@ -1247,11 +1246,10 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
indio_dev->trig = iio_trigger_get(st->dready_trig);
- ret = devm_request_threaded_irq(dev, st->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- indio_dev->name, st->dready_trig);
+ ret = devm_request_irq(dev, st->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ indio_dev->name, st->dready_trig);
if (ret < 0)
return ret;
}
diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c
index aef5109c1ddd..8fab2fdbe147 100644
--- a/drivers/iio/accel/adxl380.c
+++ b/drivers/iio/accel/adxl380.c
@@ -232,25 +232,46 @@ bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg)
}
EXPORT_SYMBOL_NS_GPL(adxl380_readable_noinc_reg, "IIO_ADXL380");
+static int adxl380_act_inact_enabled(struct adxl380_state *st, bool *enabled)
+{
+ unsigned int act_inact_ctl;
+ int ret;
+
+ if (!st->chip_info->has_low_power) {
+ *enabled = false;
+ return 0;
+ }
+
+ ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl);
+ if (ret)
+ return ret;
+
+ *enabled = FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) ||
+ FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl);
+
+ return 0;
+}
+
static int adxl380_set_measure_en(struct adxl380_state *st, bool en)
{
int ret;
- unsigned int act_inact_ctl;
u8 op_mode = ADXL380_OP_MODE_STANDBY;
if (en) {
- ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl);
+ bool act_inact_enabled;
+
+ ret = adxl380_act_inact_enabled(st, &act_inact_enabled);
if (ret)
return ret;
/*
* Activity/Inactivity detection available only in VLP/ULP
- * mode and for devices that support low power modes. Otherwise
- * go straight to measure mode (same bits as ADXL380_OP_MODE_HP).
+ * mode and for devices that support low power modes.
*/
- if (st->chip_info->has_low_power &&
- (FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) ||
- FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl)))
+ if (act_inact_enabled)
+ st->odr = ADXL380_ODR_VLP;
+
+ if (st->odr == ADXL380_ODR_VLP)
op_mode = ADXL380_OP_MODE_VLP;
else
op_mode = ADXL380_OP_MODE_HP;
@@ -417,17 +438,7 @@ static int adxl380_read_chn(struct adxl380_state *st, u8 addr)
static int adxl380_get_odr(struct adxl380_state *st, int *odr)
{
- int ret;
- unsigned int trig_cfg, odr_idx;
-
- ret = regmap_read(st->regmap, ADXL380_TRIG_CFG_REG, &trig_cfg);
- if (ret)
- return ret;
-
- odr_idx = (FIELD_GET(ADXL380_TRIG_CFG_SINC_RATE_MSK, trig_cfg) << 1) |
- (FIELD_GET(ADXL380_TRIG_CFG_DEC_2X_MSK, trig_cfg) & 1);
-
- *odr = st->chip_info->samp_freq_tbl[odr_idx];
+ *odr = st->chip_info->samp_freq_tbl[st->odr];
return 0;
}
@@ -488,18 +499,24 @@ static int adxl380_set_odr(struct adxl380_state *st, u8 odr)
if (ret)
return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
- ADXL380_TRIG_CFG_DEC_2X_MSK,
- FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, odr & 1));
- if (ret)
- return ret;
+ if (odr >= ADXL380_ODR_DSM) {
+ u8 mul = odr - ADXL380_ODR_DSM;
+ u8 field;
- ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
- ADXL380_TRIG_CFG_SINC_RATE_MSK,
- FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, odr >> 1));
- if (ret)
- return ret;
+ field = FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, mul & 1);
+ ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
+ ADXL380_TRIG_CFG_DEC_2X_MSK, field);
+ if (ret)
+ return ret;
+ field = FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, mul >> 1);
+ ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
+ ADXL380_TRIG_CFG_SINC_RATE_MSK, field);
+ if (ret)
+ return ret;
+ }
+
+ st->odr = odr;
ret = adxl380_set_measure_en(st, true);
if (ret)
return ret;
@@ -949,14 +966,13 @@ static irqreturn_t adxl380_irq_handler(int irq, void *p)
if (ret)
return IRQ_HANDLED;
- for (i = 0; i < fifo_entries; i += st->fifo_set_size) {
- ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA,
- &st->fifo_buf[i],
- 2 * st->fifo_set_size);
- if (ret)
- return IRQ_HANDLED;
+ fifo_entries = rounddown(fifo_entries, st->fifo_set_size);
+ ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA, &st->fifo_buf,
+ sizeof(*st->fifo_buf) * fifo_entries);
+ if (ret)
+ return IRQ_HANDLED;
+ for (i = 0; i < fifo_entries; i += st->fifo_set_size)
iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
- }
return IRQ_HANDLED;
}
@@ -1138,6 +1154,32 @@ static const struct iio_buffer_setup_ops adxl380_buffer_ops = {
.predisable = adxl380_buffer_predisable,
};
+static int adxl380_samp_freq_avail(struct adxl380_state *st, const int **vals,
+ int *length)
+{
+ bool act_inact_enabled;
+ int ret;
+
+ if (!st->chip_info->has_low_power) {
+ *vals = st->chip_info->samp_freq_tbl + ADXL380_ODR_DSM;
+ *length = ADXL380_ODR_MAX - ADXL380_ODR_DSM;
+ return 0;
+ }
+
+ ret = adxl380_act_inact_enabled(st, &act_inact_enabled);
+ if (ret)
+ return 0;
+
+ /*
+ * Motion detection is only functional in low-power mode, and this
+ * affects the available sampling frequencies.
+ */
+ *vals = st->chip_info->samp_freq_tbl;
+ *length = act_inact_enabled ? ADXL380_ODR_DSM : ADXL380_ODR_MAX;
+
+ return 0;
+}
+
static int adxl380_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
@@ -1218,6 +1260,7 @@ static int adxl380_read_avail(struct iio_dev *indio_dev,
long mask)
{
struct adxl380_state *st = iio_priv(indio_dev);
+ int ret;
if (chan->type != IIO_ACCEL)
return -EINVAL;
@@ -1229,9 +1272,11 @@ static int adxl380_read_avail(struct iio_dev *indio_dev,
*length = ARRAY_SIZE(st->chip_info->scale_tbl) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
- *vals = (const int *)st->chip_info->samp_freq_tbl;
+ ret = adxl380_samp_freq_avail(st, vals, length);
+ if (ret)
+ return ret;
+
*type = IIO_VAL_INT;
- *length = ARRAY_SIZE(st->chip_info->samp_freq_tbl);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = (const int *)st->lpf_tbl;
@@ -1254,12 +1299,16 @@ static int adxl380_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
struct adxl380_state *st = iio_priv(indio_dev);
- int odr_index, lpf_index, hpf_index, range_index;
+ const int *freq_vals;
+ int odr_index, lpf_index, hpf_index, range_index, freq_count, ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
- odr_index = adxl380_find_match_1d_tbl(st->chip_info->samp_freq_tbl,
- ARRAY_SIZE(st->chip_info->samp_freq_tbl),
+ ret = adxl380_samp_freq_avail(st, &freq_vals, &freq_count);
+ if (ret)
+ return ret;
+
+ odr_index = adxl380_find_match_1d_tbl(freq_vals, freq_count,
val);
return adxl380_set_odr(st, odr_index);
case IIO_CHAN_INFO_CALIBBIAS:
@@ -1621,7 +1670,7 @@ const struct adxl380_chip_info adxl318_chip_info = {
[ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 },
[ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 },
},
- .samp_freq_tbl = { 8000, 16000, 32000 },
+ .samp_freq_tbl = { 0, 8000, 16000, 32000 },
/*
* The datasheet defines an intercept of 550 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1639,7 +1688,7 @@ const struct adxl380_chip_info adxl319_chip_info = {
[ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 },
[ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 },
},
- .samp_freq_tbl = { 16000, 32000, 64000 },
+ .samp_freq_tbl = { 0, 16000, 32000, 64000 },
/*
* The datasheet defines an intercept of 550 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1657,7 +1706,7 @@ const struct adxl380_chip_info adxl380_chip_info = {
[ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 },
[ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 },
},
- .samp_freq_tbl = { 8000, 16000, 32000 },
+ .samp_freq_tbl = { 1000, 8000, 16000, 32000 },
/*
* The datasheet defines an intercept of 470 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1677,7 +1726,7 @@ const struct adxl380_chip_info adxl382_chip_info = {
[ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 },
[ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 },
},
- .samp_freq_tbl = { 16000, 32000, 64000 },
+ .samp_freq_tbl = { 1000, 16000, 32000, 64000 },
/*
* The datasheet defines an intercept of 570 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1916,6 +1965,7 @@ int adxl380_probe(struct device *dev, struct regmap *regmap,
st->dev = dev;
st->regmap = regmap;
st->chip_info = chip_info;
+ st->odr = ADXL380_ODR_DSM;
mutex_init(&st->lock);
diff --git a/drivers/iio/accel/adxl380.h b/drivers/iio/accel/adxl380.h
index e67c5aab8efc..d2c260c8b2fa 100644
--- a/drivers/iio/accel/adxl380.h
+++ b/drivers/iio/accel/adxl380.h
@@ -8,10 +8,18 @@
#ifndef _ADXL380_H_
#define _ADXL380_H_
+enum adxl380_odr {
+ ADXL380_ODR_VLP,
+ ADXL380_ODR_DSM,
+ ADXL380_ODR_DSM_2X,
+ ADXL380_ODR_DSM_4X,
+ ADXL380_ODR_MAX
+};
+
struct adxl380_chip_info {
const char *name;
const int scale_tbl[3][2];
- const int samp_freq_tbl[3];
+ const int samp_freq_tbl[ADXL380_ODR_MAX];
const struct iio_info *info;
const int temp_offset;
const u16 chip_id;
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index 8925f5279e62..7bc6761f5135 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -986,8 +986,9 @@ static int bma180_probe(struct i2c_client *client)
}
ret = devm_request_irq(dev, client->irq,
- iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING,
- "bma180_event", data->trig);
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "bma180_event", data->trig);
if (ret) {
dev_err(dev, "unable to request IRQ\n");
goto err_trigger_free;
diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
index ac973d871c8b..a2c3cf13d098 100644
--- a/drivers/iio/accel/mxc4005.c
+++ b/drivers/iio/accel/mxc4005.c
@@ -486,13 +486,10 @@ static int mxc4005_probe(struct i2c_client *client)
if (!data->dready_trig)
return -ENOMEM;
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- "mxc4005_event",
- data->dready_trig);
+ ret = devm_request_irq(&client->dev, client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
+ "mxc4005_event", data->dready_trig);
if (ret) {
dev_err(&client->dev,
"failed to init threaded irq\n");
diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
index bfa8a3f5a92f..4a827be439a2 100644
--- a/drivers/iio/accel/sca3000.c
+++ b/drivers/iio/accel/sca3000.c
@@ -153,7 +153,6 @@
* struct sca3000_state - device instance state information
* @us: the associated spi device
* @info: chip variant information
- * @last_timestamp: the timestamp of the last event
* @mo_det_use_count: reference counter for the motion detection unit
* @lock: lock used to protect elements of sca3000_state
* and the underlying device state.
@@ -163,7 +162,6 @@
struct sca3000_state {
struct spi_device *us;
const struct sca3000_chip_info *info;
- s64 last_timestamp;
int mo_det_use_count;
struct mutex lock;
/* Can these share a cacheline ? */
@@ -1489,7 +1487,11 @@ static int sca3000_probe(struct spi_device *spi)
if (ret)
goto error_free_irq;
- return iio_device_register(indio_dev);
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_irq;
+
+ return 0;
error_free_irq:
if (spi->irq)
diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c
index 384f1fbcbcb3..a9ff2a273fe1 100644
--- a/drivers/iio/accel/stk8ba50.c
+++ b/drivers/iio/accel/stk8ba50.c
@@ -428,13 +428,10 @@ static int stk8ba50_probe(struct i2c_client *client)
}
if (client->irq > 0) {
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- stk8ba50_data_rdy_trig_poll,
- NULL,
- IRQF_TRIGGER_RISING |
- IRQF_ONESHOT,
- "stk8ba50_event",
- indio_dev);
+ ret = devm_request_irq(&client->dev, client->irq,
+ stk8ba50_data_rdy_trig_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "stk8ba50_event", indio_dev);
if (ret < 0) {
dev_err(&client->dev, "request irq %d failed\n",
client->irq);
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 58da8255525e..60038ae8dfc4 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -70,6 +70,19 @@ config AD4030
To compile this driver as a module, choose M here: the module will be
called ad4030.
+config AD4062
+ tristate "Analog Devices AD4062 Driver"
+ depends on I3C
+ select REGMAP_I3C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD4062 I3C analog
+ to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4062.
+
config AD4080
tristate "Analog Devices AD4080 high speed ADC"
depends on SPI
@@ -99,6 +112,17 @@ config AD4130
To compile this driver as a module, choose M here: the module will be
called ad4130.
+config AD4134
+ tristate "Analog Device AD4134 ADC Driver"
+ depends on SPI
+ select REGMAP_SPI
+ select CRC8
+ help
+ Say yes here to build support for Analog Devices AD4134 SPI analog to
+ digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4134_spi.
config AD4170_4
tristate "Analog Device AD4170-4 ADC Driver"
@@ -387,6 +411,7 @@ config AD7768_1
depends on SPI
select REGULATOR
select REGMAP_SPI
+ select RATIONAL
select IIO_BUFFER
select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
@@ -1222,6 +1247,18 @@ config NPCM_ADC
This driver can also be built as a module. If so, the module
will be called npcm_adc.
+config NXP_SAR_ADC
+ tristate "NXP S32G SAR-ADC driver"
+ depends on ARCH_S32 || COMPILE_TEST
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for S32G platforms
+ analog-to-digital converter.
+
+ This driver can also be built as a module. If so, the module will be
+ called nxp_sar_adc.
+
config PAC1921
tristate "Microchip Technology PAC1921 driver"
depends on I2C
@@ -1664,6 +1701,18 @@ config TI_ADS1015
This driver can also be built as a module. If so, the module will be
called ti-ads1015.
+config TI_ADS1018
+ tristate "Texas Instruments ADS1018 ADC"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADS1018 and
+ ADS1118 ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads1018.
+
config TI_ADS1100
tristate "Texas Instruments ADS1100 and ADS1000 ADC"
depends on I2C
@@ -1722,6 +1771,17 @@ config TI_ADS131E08
This driver can also be built as a module. If so, the module will be
called ti-ads131e08.
+config TI_ADS131M02
+ tristate "Texas Instruments ADS131M02"
+ depends on SPI && REGULATOR
+ select CRC_ITU_T
+ help
+ Say yes here to get support for Texas Instruments ADS131M02, ADS131M03,
+ ADS131M04, ADS131M06 and ADS131M08 chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads131m02.
+
config TI_ADS7138
tristate "Texas Instruments ADS7128 and ADS7138 ADC driver"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7cc8f9a12f76..c76550415ff1 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -11,8 +11,10 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o
obj-$(CONFIG_AD4030) += ad4030.o
+obj-$(CONFIG_AD4062) += ad4062.o
obj-$(CONFIG_AD4080) += ad4080.o
obj-$(CONFIG_AD4130) += ad4130.o
+obj-$(CONFIG_AD4134) += ad4134.o
obj-$(CONFIG_AD4170_4) += ad4170-4.o
obj-$(CONFIG_AD4695) += ad4695.o
obj-$(CONFIG_AD4851) += ad4851.o
@@ -108,6 +110,7 @@ obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NCT7201) += nct7201.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
+obj-$(CONFIG_NXP_SAR_ADC) += nxp-sar-adc.o
obj-$(CONFIG_PAC1921) += pac1921.o
obj-$(CONFIG_PAC1934) += pac1934.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
@@ -145,11 +148,13 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
+obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
+obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o
obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
new file mode 100644
index 000000000000..dd4ad32aa6f5
--- /dev/null
+++ b/drivers/iio/adc/ad4062.c
@@ -0,0 +1,1609 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD4062 I3C ADC driver
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/devm-helpers.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/units.h>
+#include <linux/unaligned.h>
+#include <linux/util_macros.h>
+
+#define AD4062_REG_INTERFACE_CONFIG_A 0x00
+#define AD4062_REG_DEVICE_CONFIG 0x02
+#define AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK GENMASK(1, 0)
+#define AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE 3
+#define AD4062_REG_PROD_ID_1 0x05
+#define AD4062_REG_DEVICE_GRADE 0x06
+#define AD4062_REG_SCRATCH_PAD 0x0A
+#define AD4062_REG_VENDOR_H 0x0D
+#define AD4062_REG_STREAM_MODE 0x0E
+#define AD4062_REG_INTERFACE_STATUS 0x11
+#define AD4062_REG_MODE_SET 0x20
+#define AD4062_REG_MODE_SET_ENTER_ADC BIT(0)
+#define AD4062_REG_ADC_MODES 0x21
+#define AD4062_REG_ADC_MODES_MODE_MSK GENMASK(1, 0)
+#define AD4062_REG_ADC_CONFIG 0x22
+#define AD4062_REG_ADC_CONFIG_REF_EN_MSK BIT(5)
+#define AD4062_REG_ADC_CONFIG_SCALE_EN_MSK BIT(4)
+#define AD4062_REG_AVG_CONFIG 0x23
+#define AD4062_REG_GP_CONF 0x24
+#define AD4062_REG_GP_CONF_MODE_MSK_0 GENMASK(2, 0)
+#define AD4062_REG_GP_CONF_MODE_MSK_1 GENMASK(6, 4)
+#define AD4062_REG_INTR_CONF 0x25
+#define AD4062_REG_INTR_CONF_EN_MSK_0 GENMASK(1, 0)
+#define AD4062_REG_INTR_CONF_EN_MSK_1 GENMASK(5, 4)
+#define AD4062_REG_TIMER_CONFIG 0x27
+#define AD4062_REG_TIMER_CONFIG_FS_MASK GENMASK(7, 4)
+#define AD4062_REG_MAX_LIMIT 0x29
+#define AD4062_REG_MIN_LIMIT 0x2B
+#define AD4062_REG_MAX_HYST 0x2C
+#define AD4062_REG_MIN_HYST 0x2D
+#define AD4062_REG_MON_VAL 0x2F
+#define AD4062_REG_ADC_IBI_EN 0x31
+#define AD4062_REG_ADC_IBI_EN_CONV_TRIGGER BIT(2)
+#define AD4062_REG_ADC_IBI_EN_MAX BIT(1)
+#define AD4062_REG_ADC_IBI_EN_MIN BIT(0)
+#define AD4062_REG_FUSE_CRC 0x40
+#define AD4062_REG_DEVICE_STATUS 0x41
+#define AD4062_REG_DEVICE_STATUS_DEVICE_RESET BIT(6)
+#define AD4062_REG_IBI_STATUS 0x48
+#define AD4062_REG_CONV_READ_LSB 0x50
+#define AD4062_REG_CONV_READ_16BITS 0x51
+#define AD4062_REG_CONV_READ_32BITS 0x53
+#define AD4062_REG_CONV_TRIGGER_16BITS 0x57
+#define AD4062_REG_CONV_TRIGGER_32BITS 0x59
+#define AD4062_REG_CONV_AUTO 0x61
+#define AD4062_MAX_REG AD4062_REG_CONV_AUTO
+
+#define AD4062_MON_VAL_MIDDLE_POINT 0x8000
+
+#define AD4062_I3C_VENDOR 0x0177
+#define AD4062_SOFT_RESET 0x81
+#define AD4060_PROD_ID 0x7A
+#define AD4062_PROD_ID 0x7C
+
+#define AD4062_GP_DISABLED 0x0
+#define AD4062_GP_INTR 0x1
+#define AD4062_GP_DRDY 0x2
+#define AD4062_GP_STATIC_LOW 0x5
+#define AD4062_GP_STATIC_HIGH 0x6
+
+#define AD4062_LIMIT_BITS 12
+
+#define AD4062_INTR_EN_NEITHER 0x0
+#define AD4062_INTR_EN_EITHER 0x3
+
+#define AD4062_TCONV_NS 270
+
+enum ad4062_operation_mode {
+ AD4062_SAMPLE_MODE = 0x0,
+ AD4062_BURST_AVERAGING_MODE = 0x1,
+ AD4062_MONITOR_MODE = 0x3,
+};
+
+struct ad4062_chip_info {
+ const struct iio_chan_spec channels[1];
+ const char *name;
+ u16 prod_id;
+ u16 avg_max;
+};
+
+enum {
+ AD4062_SCAN_TYPE_SAMPLE,
+ AD4062_SCAN_TYPE_BURST_AVG,
+};
+
+static const struct iio_scan_type ad4062_scan_type_12_s[] = {
+ [AD4062_SCAN_TYPE_SAMPLE] = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ [AD4062_SCAN_TYPE_BURST_AVG] = {
+ .sign = 's',
+ .realbits = 14,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+};
+
+static const struct iio_scan_type ad4062_scan_type_16_s[] = {
+ [AD4062_SCAN_TYPE_SAMPLE] = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ [AD4062_SCAN_TYPE_BURST_AVG] = {
+ .sign = 's',
+ .realbits = 20,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+};
+
+static const unsigned int ad4062_conversion_freqs[] = {
+ 2000000, 1000000, 300000, 100000, /* 0 - 3 */
+ 33300, 10000, 3000, 500, /* 4 - 7 */
+ 333, 250, 200, 166, /* 8 - 11 */
+ 140, 124, 111, /* 12 - 15 */
+};
+
+struct ad4062_state {
+ const struct ad4062_chip_info *chip;
+ const struct ad4062_bus_ops *ops;
+ enum ad4062_operation_mode mode;
+ struct work_struct trig_conv;
+ struct completion completion;
+ struct iio_trigger *trigger;
+ struct iio_dev *indio_dev;
+ struct i3c_device *i3cdev;
+ struct regmap *regmap;
+ bool wait_event;
+ int vref_uV;
+ unsigned int samp_freqs[ARRAY_SIZE(ad4062_conversion_freqs)];
+ bool gpo_irq[2];
+ u16 sampling_frequency;
+ u16 events_frequency;
+ u8 oversamp_ratio;
+ u8 conv_sizeof;
+ u8 conv_addr;
+ union {
+ __be32 be32;
+ __be16 be16;
+ } buf __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct regmap_range ad4062_regmap_rd_ranges[] = {
+ regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_GRADE),
+ regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_INTERFACE_STATUS),
+ regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN),
+ regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_IBI_STATUS),
+ regmap_reg_range(AD4062_REG_CONV_READ_LSB, AD4062_REG_CONV_AUTO),
+};
+
+static const struct regmap_access_table ad4062_regmap_rd_table = {
+ .yes_ranges = ad4062_regmap_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4062_regmap_rd_ranges),
+};
+
+static const struct regmap_range ad4062_regmap_wr_ranges[] = {
+ regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_CONFIG),
+ regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_SCRATCH_PAD),
+ regmap_reg_range(AD4062_REG_STREAM_MODE, AD4062_REG_INTERFACE_STATUS),
+ regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN),
+ regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_DEVICE_STATUS),
+};
+
+static const struct regmap_access_table ad4062_regmap_wr_table = {
+ .yes_ranges = ad4062_regmap_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4062_regmap_wr_ranges),
+};
+
+static const struct iio_event_spec ad4062_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+};
+
+#define AD4062_CHAN(bits) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = 1, \
+ .channel = 0, \
+ .event_spec = ad4062_events, \
+ .num_event_specs = ARRAY_SIZE(ad4062_events), \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = ad4062_scan_type_##bits##_s, \
+ .num_ext_scan_type = ARRAY_SIZE(ad4062_scan_type_##bits##_s), \
+}
+
+static const struct ad4062_chip_info ad4060_chip_info = {
+ .name = "ad4060",
+ .channels = { AD4062_CHAN(12) },
+ .prod_id = AD4060_PROD_ID,
+ .avg_max = 256,
+};
+
+static const struct ad4062_chip_info ad4062_chip_info = {
+ .name = "ad4062",
+ .channels = { AD4062_CHAN(16) },
+ .prod_id = AD4062_PROD_ID,
+ .avg_max = 4096,
+};
+
+static ssize_t sampling_frequency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad4062_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%d\n", ad4062_conversion_freqs[st->events_frequency]);
+}
+
+static int sampling_frequency_store_dispatch(struct iio_dev *indio_dev,
+ const char *buf)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int val, ret;
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ ret = kstrtoint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ st->events_frequency = find_closest_descending(val, ad4062_conversion_freqs,
+ ARRAY_SIZE(ad4062_conversion_freqs));
+ return 0;
+}
+
+static ssize_t sampling_frequency_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = sampling_frequency_store_dispatch(indio_dev, buf);
+ iio_device_release_direct(indio_dev);
+ return ret ?: len;
+}
+
+static IIO_DEVICE_ATTR_RW(sampling_frequency, 0);
+
+static ssize_t sampling_frequency_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret = 0;
+
+ for (u8 i = 0; i < ARRAY_SIZE(ad4062_conversion_freqs); i++)
+ ret += sysfs_emit_at(buf, ret, "%d%s", ad4062_conversion_freqs[i],
+ i != (ARRAY_SIZE(ad4062_conversion_freqs) - 1) ? " " : "\n");
+ return ret;
+}
+
+static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
+
+static struct attribute *ad4062_event_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad4062_event_attribute_group = {
+ .attrs = ad4062_event_attributes,
+};
+
+static int ad4062_set_oversampling_ratio(struct ad4062_state *st, int val, int val2)
+{
+ const u32 _max = st->chip->avg_max;
+ const u32 _min = 1;
+ int ret;
+
+ if (!in_range(val, _min, _max) || val2 != 0)
+ return -EINVAL;
+
+ /* 1 disables oversampling */
+ val = ilog2(val);
+ if (val == 0) {
+ st->mode = AD4062_SAMPLE_MODE;
+ } else {
+ st->mode = AD4062_BURST_AVERAGING_MODE;
+ ret = regmap_write(st->regmap, AD4062_REG_AVG_CONFIG, val - 1);
+ if (ret)
+ return ret;
+ }
+ st->oversamp_ratio = val;
+
+ return 0;
+}
+
+static int ad4062_get_oversampling_ratio(struct ad4062_state *st, int *val)
+{
+ int ret, buf;
+
+ if (st->mode == AD4062_SAMPLE_MODE) {
+ *val = 1;
+ return 0;
+ }
+
+ ret = regmap_read(st->regmap, AD4062_REG_AVG_CONFIG, &buf);
+ if (ret)
+ return ret;
+
+ *val = BIT(buf + 1);
+ return 0;
+}
+
+static int ad4062_calc_sampling_frequency(unsigned int fosc, unsigned int oversamp_ratio)
+{
+ /* From datasheet p.31: (n_avg - 1)/fosc + tconv */
+ u32 n_avg = BIT(oversamp_ratio) - 1;
+ u32 period_ns = NSEC_PER_SEC / fosc;
+
+ /* Result is less than 1 Hz */
+ if (n_avg >= fosc)
+ return 1;
+
+ return NSEC_PER_SEC / (n_avg * period_ns + AD4062_TCONV_NS);
+}
+
+static int ad4062_populate_sampling_frequency(struct ad4062_state *st)
+{
+ for (u8 i = 0; i < ARRAY_SIZE(ad4062_conversion_freqs); i++)
+ st->samp_freqs[i] =
+ ad4062_calc_sampling_frequency(ad4062_conversion_freqs[i],
+ st->oversamp_ratio);
+ return 0;
+}
+
+static int ad4062_get_sampling_frequency(struct ad4062_state *st, int *val)
+{
+ int freq = ad4062_conversion_freqs[st->sampling_frequency];
+
+ *val = ad4062_calc_sampling_frequency(freq, st->oversamp_ratio);
+ return IIO_VAL_INT;
+}
+
+static int ad4062_set_sampling_frequency(struct ad4062_state *st, int val, int val2)
+{
+ int ret;
+
+ if (val2 != 0)
+ return -EINVAL;
+
+ ret = ad4062_populate_sampling_frequency(st);
+ if (ret)
+ return ret;
+
+ st->sampling_frequency =
+ find_closest_descending(val, st->samp_freqs,
+ ARRAY_SIZE(ad4062_conversion_freqs));
+ return 0;
+}
+
+static int ad4062_check_ids(struct ad4062_state *st)
+{
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+ u16 val;
+
+ ret = regmap_bulk_read(st->regmap, AD4062_REG_PROD_ID_1,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ val = be16_to_cpu(st->buf.be16);
+ if (val != st->chip->prod_id)
+ dev_warn(dev, "Production ID x%x does not match known values", val);
+
+ ret = regmap_bulk_read(st->regmap, AD4062_REG_VENDOR_H,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ val = be16_to_cpu(st->buf.be16);
+ if (val != AD4062_I3C_VENDOR) {
+ dev_err(dev, "Vendor ID x%x does not match expected value\n", val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ad4062_conversion_frequency_set(struct ad4062_state *st, u8 val)
+{
+ return regmap_write(st->regmap, AD4062_REG_TIMER_CONFIG,
+ FIELD_PREP(AD4062_REG_TIMER_CONFIG_FS_MASK, val));
+}
+
+static int ad4062_set_operation_mode(struct ad4062_state *st,
+ enum ad4062_operation_mode mode)
+{
+ const unsigned int samp_freq = mode == AD4062_MONITOR_MODE ?
+ st->events_frequency : st->sampling_frequency;
+ int ret;
+
+ ret = ad4062_conversion_frequency_set(st, samp_freq);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_MODES,
+ AD4062_REG_ADC_MODES_MODE_MSK, mode);
+ if (ret)
+ return ret;
+
+ if (mode == AD4062_MONITOR_MODE) {
+ /* Change address pointer to enter monitor mode */
+ struct i3c_xfer xfer_trigger = {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ };
+ st->conv_addr = AD4062_REG_CONV_TRIGGER_32BITS;
+ return i3c_device_do_xfers(st->i3cdev, &xfer_trigger, 1, I3C_SDR);
+ }
+
+ return regmap_write(st->regmap, AD4062_REG_MODE_SET,
+ AD4062_REG_MODE_SET_ENTER_ADC);
+}
+
+static int ad4062_soft_reset(struct ad4062_state *st)
+{
+ u8 val = AD4062_SOFT_RESET;
+ int ret;
+
+ ret = regmap_write(st->regmap, AD4062_REG_INTERFACE_CONFIG_A, val);
+ if (ret)
+ return ret;
+
+ /* Wait AD4062 treset time, datasheet p8 */
+ ndelay(60);
+
+ return 0;
+}
+
+static int ad4062_setup(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ const bool *ref_sel)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
+ int ret;
+
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_0,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0,
+ AD4062_GP_INTR));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_1,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1,
+ AD4062_GP_DRDY));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG,
+ AD4062_REG_ADC_CONFIG_REF_EN_MSK,
+ FIELD_PREP(AD4062_REG_ADC_CONFIG_REF_EN_MSK,
+ *ref_sel));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4062_REG_DEVICE_STATUS,
+ AD4062_REG_DEVICE_STATUS_DEVICE_RESET);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_INTR_CONF,
+ AD4062_REG_INTR_CONF_EN_MSK_0,
+ FIELD_PREP(AD4062_REG_INTR_CONF_EN_MSK_0,
+ AD4062_INTR_EN_EITHER));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_INTR_CONF,
+ AD4062_REG_INTR_CONF_EN_MSK_1,
+ FIELD_PREP(AD4062_REG_INTR_CONF_EN_MSK_1,
+ AD4062_INTR_EN_NEITHER));
+ if (ret)
+ return ret;
+
+ st->buf.be16 = cpu_to_be16(AD4062_MON_VAL_MIDDLE_POINT);
+ return regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL,
+ &st->buf.be16, sizeof(st->buf.be16));
+}
+
+static irqreturn_t ad4062_irq_handler_thresh(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ad4062_irq_handler_drdy(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev))
+ iio_trigger_poll(st->trigger);
+ else
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static void ad4062_ibi_handler(struct i3c_device *i3cdev,
+ const struct i3c_ibi_payload *payload)
+{
+ struct ad4062_state *st = i3cdev_get_drvdata(i3cdev);
+
+ if (st->wait_event) {
+ iio_push_event(st->indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(st->indio_dev));
+ return;
+ }
+ if (iio_buffer_enabled(st->indio_dev))
+ iio_trigger_poll_nested(st->trigger);
+ else
+ complete(&st->completion);
+}
+
+static void ad4062_trigger_work(struct work_struct *work)
+{
+ struct ad4062_state *st =
+ container_of(work, struct ad4062_state, trig_conv);
+ int ret;
+
+ /*
+ * Read current conversion, if at reg CONV_READ, stop bit triggers
+ * next sample and does not need writing the address.
+ */
+ struct i3c_xfer xfer_sample = {
+ .data.in = &st->buf.be32,
+ .len = st->conv_sizeof,
+ .rnw = true,
+ };
+ struct i3c_xfer xfer_trigger = {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ };
+
+ ret = i3c_device_do_xfers(st->i3cdev, &xfer_sample, 1, I3C_SDR);
+ if (ret)
+ return;
+
+ iio_push_to_buffers_with_ts(st->indio_dev, &st->buf.be32, st->conv_sizeof,
+ iio_get_time_ns(st->indio_dev));
+ if (st->gpo_irq[1])
+ return;
+
+ i3c_device_do_xfers(st->i3cdev, &xfer_trigger, 1, I3C_SDR);
+}
+
+static irqreturn_t ad4062_poll_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ iio_trigger_notify_done(indio_dev->trig);
+ schedule_work(&st->trig_conv);
+
+ return IRQ_HANDLED;
+}
+
+static void ad4062_disable_ibi(void *data)
+{
+ struct i3c_device *i3cdev = data;
+
+ i3c_device_disable_ibi(i3cdev);
+}
+
+static void ad4062_free_ibi(void *data)
+{
+ struct i3c_device *i3cdev = data;
+
+ i3c_device_free_ibi(i3cdev);
+}
+
+static int ad4062_request_ibi(struct i3c_device *i3cdev)
+{
+ const struct i3c_ibi_setup ibireq = {
+ .max_payload_len = 1,
+ .num_slots = 1,
+ .handler = ad4062_ibi_handler,
+ };
+ int ret;
+
+ ret = i3c_device_request_ibi(i3cdev, &ibireq);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i3cdev->dev, ad4062_free_ibi, i3cdev);
+ if (ret)
+ return ret;
+
+ ret = i3c_device_enable_ibi(i3cdev);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(&i3cdev->dev, ad4062_disable_ibi, i3cdev);
+}
+
+static int ad4062_request_irq(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+
+ ret = fwnode_irq_get_byname(dev_fwnode(&st->i3cdev->dev), "gp0");
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ if (ret < 0) {
+ st->gpo_irq[0] = false;
+ ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN,
+ AD4062_REG_ADC_IBI_EN_MAX | AD4062_REG_ADC_IBI_EN_MIN,
+ AD4062_REG_ADC_IBI_EN_MAX | AD4062_REG_ADC_IBI_EN_MIN);
+ if (ret)
+ return ret;
+ } else {
+ st->gpo_irq[0] = true;
+ ret = devm_request_threaded_irq(dev, ret, NULL,
+ ad4062_irq_handler_thresh,
+ IRQF_ONESHOT, indio_dev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = fwnode_irq_get_byname(dev_fwnode(&st->i3cdev->dev), "gp1");
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ if (ret < 0) {
+ st->gpo_irq[1] = false;
+ return regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN,
+ AD4062_REG_ADC_IBI_EN_CONV_TRIGGER,
+ AD4062_REG_ADC_IBI_EN_CONV_TRIGGER);
+ }
+ st->gpo_irq[1] = true;
+
+ return devm_request_threaded_irq(dev, ret,
+ ad4062_irq_handler_drdy,
+ NULL, IRQF_ONESHOT, indio_dev->name,
+ indio_dev);
+}
+
+static const struct iio_trigger_ops ad4062_trigger_ops = {
+ .validate_device = &iio_trigger_validate_own_device,
+};
+
+static int ad4062_request_trigger(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+
+ st->trigger = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!st->trigger)
+ return -ENOMEM;
+
+ st->trigger->ops = &ad4062_trigger_ops;
+ iio_trigger_set_drvdata(st->trigger, indio_dev);
+
+ ret = devm_iio_trigger_register(dev, st->trigger);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trigger);
+
+ return 0;
+}
+
+static const int ad4062_oversampling_avail[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, /* 0 - 7 */
+ 256, 512, 1024, 2048, 4096, /* 8 - 12 */
+};
+
+static int ad4062_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *len, long mask)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = ad4062_oversampling_avail;
+ *len = ARRAY_SIZE(ad4062_oversampling_avail);
+ *len -= st->chip->avg_max == 256 ? 4 : 0;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = ad4062_populate_sampling_frequency(st);
+ if (ret)
+ return ret;
+ *vals = st->samp_freqs;
+ *len = st->oversamp_ratio ? ARRAY_SIZE(ad4062_conversion_freqs) : 1;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_get_chan_scale(struct iio_dev *indio_dev, int *val, int *val2)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
+
+ /*
+ * In burst averaging mode the averaging filter accumulates resulting
+ * in a sample with increased precision.
+ */
+ scan_type = iio_get_current_scan_type(indio_dev, st->chip->channels);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ *val = (st->vref_uV * 2) / (MICRO / MILLI); /* signed */
+ *val2 = scan_type->realbits - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+}
+
+static int ad4062_get_chan_calibscale(struct ad4062_state *st, int *val, int *val2)
+{
+ int ret;
+
+ ret = regmap_bulk_read(st->regmap, AD4062_REG_MON_VAL,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ /* From datasheet: code out = code in × mon_val/0x8000 */
+ *val = be16_to_cpu(st->buf.be16) * 2;
+ *val2 = 16;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+}
+
+static int ad4062_set_chan_calibscale(struct ad4062_state *st, int gain_int,
+ int gain_frac)
+{
+ /* Divide numerator and denumerator by known great common divider */
+ const u32 mon_val = AD4062_MON_VAL_MIDDLE_POINT / 64;
+ const u32 micro = MICRO / 64;
+ const u32 gain_fp = gain_int * MICRO + gain_frac;
+ const u32 reg_val = DIV_ROUND_CLOSEST(gain_fp * mon_val, micro);
+ int ret;
+
+ /* Checks if the gain is in range and the value fits the field */
+ if (gain_int < 0 || gain_int > 1 || reg_val > BIT(16) - 1)
+ return -EINVAL;
+
+ st->buf.be16 = cpu_to_be16(reg_val);
+ ret = regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ /* Enable scale if gain is not equal to one */
+ return regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG,
+ AD4062_REG_ADC_CONFIG_SCALE_EN_MSK,
+ FIELD_PREP(AD4062_REG_ADC_CONFIG_SCALE_EN_MSK,
+ !(gain_int == 1 && gain_frac == 0)));
+}
+
+static int ad4062_read_chan_raw(struct ad4062_state *st, int *val)
+{
+ struct i3c_device *i3cdev = st->i3cdev;
+ struct i3c_xfer xfer_trigger = {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ };
+ struct i3c_xfer xfer_sample = {
+ .data.in = &st->buf.be32,
+ .len = sizeof(st->buf.be32),
+ .rnw = true,
+ };
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = ad4062_set_operation_mode(st, st->mode);
+ if (ret)
+ return ret;
+
+ reinit_completion(&st->completion);
+ /* Change address pointer to trigger conversion */
+ st->conv_addr = AD4062_REG_CONV_TRIGGER_32BITS;
+ ret = i3c_device_do_xfers(i3cdev, &xfer_trigger, 1, I3C_SDR);
+ if (ret)
+ return ret;
+ /*
+ * Single sample read should be used only for oversampling and
+ * sampling frequency pairs that take less than 1 sec.
+ */
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ ret = i3c_device_do_xfers(i3cdev, &xfer_sample, 1, I3C_SDR);
+ if (ret)
+ return ret;
+ *val = be32_to_cpu(st->buf.be32);
+ return 0;
+}
+
+static int ad4062_read_raw_dispatch(struct ad4062_state *st,
+ int *val, int *val2, long info)
+{
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return ad4062_read_chan_raw(st, val);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4062_get_chan_calibscale(st, val, val2);
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4062_get_oversampling_ratio(st, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ return ad4062_get_chan_scale(indio_dev, val, val2);
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad4062_get_sampling_frequency(st, val);
+ }
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_read_raw_dispatch(st, val, val2, info);
+ iio_device_release_direct(indio_dev);
+ return ret ?: IIO_VAL_INT;
+}
+
+static int ad4062_write_raw_dispatch(struct ad4062_state *st, int val, int val2,
+ long info)
+{
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4062_set_oversampling_ratio(st, val, val2);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4062_set_chan_calibscale(st, val, val2);
+
+ default:
+ return -EINVAL;
+ }
+};
+
+static int ad4062_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long info)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad4062_set_sampling_frequency(st, val, val2);
+ }
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_write_raw_dispatch(st, val, val2, info);
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int pm_ad4062_monitor_mode_enable(struct ad4062_state *st)
+{
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ return ad4062_set_operation_mode(st, AD4062_MONITOR_MODE);
+}
+
+static int ad4062_monitor_mode_enable(struct ad4062_state *st)
+{
+ int ret;
+
+ ret = pm_ad4062_monitor_mode_enable(st);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(&st->i3cdev->dev);
+ return 0;
+}
+
+static int ad4062_monitor_mode_disable(struct ad4062_state *st)
+{
+ pm_runtime_put_autosuspend(&st->i3cdev->dev);
+ return 0;
+}
+
+static int ad4062_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 ad4062_state *st = iio_priv(indio_dev);
+
+ return st->wait_event;
+}
+
+static int ad4062_write_event_config_dispatch(struct iio_dev *indio_dev,
+ bool state)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (st->wait_event == state)
+ ret = 0;
+ else if (state)
+ ret = ad4062_monitor_mode_enable(st);
+ else
+ ret = ad4062_monitor_mode_disable(st);
+ if (ret)
+ return ret;
+
+ st->wait_event = state;
+ return 0;
+}
+
+static int ad4062_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_write_event_config_dispatch(indio_dev, state);
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int __ad4062_read_event_info_value(struct ad4062_state *st,
+ enum iio_event_direction dir, int *val)
+{
+ int ret;
+ u8 reg;
+
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_LIMIT;
+ else
+ reg = AD4062_REG_MIN_LIMIT;
+
+ ret = regmap_bulk_read(st->regmap, reg, &st->buf.be16,
+ sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(be16_to_cpu(st->buf.be16), AD4062_LIMIT_BITS - 1);
+
+ return 0;
+}
+
+static int __ad4062_read_event_info_hysteresis(struct ad4062_state *st,
+ enum iio_event_direction dir, int *val)
+{
+ u8 reg;
+
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_HYST;
+ else
+ reg = AD4062_REG_MIN_HYST;
+ return regmap_read(st->regmap, reg, val);
+}
+
+static int ad4062_read_event_config_dispatch(struct iio_dev *indio_dev,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return __ad4062_read_event_info_value(st, dir, val);
+ case IIO_EV_INFO_HYSTERESIS:
+ return __ad4062_read_event_info_hysteresis(st, dir, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_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)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_read_event_config_dispatch(indio_dev, dir, info, val);
+ iio_device_release_direct(indio_dev);
+ return ret ?: IIO_VAL_INT;
+}
+
+static int __ad4062_write_event_info_value(struct ad4062_state *st,
+ enum iio_event_direction dir, int val)
+{
+ u8 reg;
+
+ if (val != sign_extend32(val, AD4062_LIMIT_BITS - 1))
+ return -EINVAL;
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_LIMIT;
+ else
+ reg = AD4062_REG_MIN_LIMIT;
+ st->buf.be16 = cpu_to_be16(val);
+
+ return regmap_bulk_write(st->regmap, reg, &st->buf.be16,
+ sizeof(st->buf.be16));
+}
+
+static int __ad4062_write_event_info_hysteresis(struct ad4062_state *st,
+ enum iio_event_direction dir, int val)
+{
+ u8 reg;
+
+ if (val > BIT(7) - 1)
+ return -EINVAL;
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_HYST;
+ else
+ reg = AD4062_REG_MIN_HYST;
+
+ return regmap_write(st->regmap, reg, val);
+}
+
+static int ad4062_write_event_value_dispatch(struct iio_dev *indio_dev,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return __ad4062_write_event_info_value(st, dir, val);
+ case IIO_EV_INFO_HYSTERESIS:
+ return __ad4062_write_event_info_hysteresis(st, dir, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_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)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_write_event_value_dispatch(indio_dev, type, dir, info, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+/*
+ * The AD4062 in burst averaging mode increases realbits from 16-bits to
+ * 20-bits, increasing the storagebits from 16-bits to 32-bits.
+ */
+static inline size_t ad4062_sizeof_storagebits(struct ad4062_state *st)
+{
+ const struct iio_scan_type *scan_type =
+ iio_get_current_scan_type(st->indio_dev, st->chip->channels);
+
+ return BITS_TO_BYTES(scan_type->storagebits);
+}
+
+/* Read registers only with realbits (no sign extension bytes) */
+static inline size_t ad4062_get_conv_addr(struct ad4062_state *st, size_t _sizeof)
+{
+ if (st->gpo_irq[1])
+ return _sizeof == sizeof(u32) ? AD4062_REG_CONV_READ_32BITS :
+ AD4062_REG_CONV_READ_16BITS;
+ return _sizeof == sizeof(u32) ? AD4062_REG_CONV_TRIGGER_32BITS :
+ AD4062_REG_CONV_TRIGGER_16BITS;
+}
+
+static int pm_ad4062_triggered_buffer_postenable(struct ad4062_state *st)
+{
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ ret = ad4062_set_operation_mode(st, st->mode);
+ if (ret)
+ return ret;
+
+ st->conv_sizeof = ad4062_sizeof_storagebits(st);
+ st->conv_addr = ad4062_get_conv_addr(st, st->conv_sizeof);
+ /* CONV_READ requires read to trigger first sample. */
+ struct i3c_xfer xfer_sample[2] = {
+ {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ },
+ {
+ .data.in = &st->buf.be32,
+ .len = sizeof(st->buf.be32),
+ .rnw = true,
+ }
+ };
+
+ return i3c_device_do_xfers(st->i3cdev, xfer_sample,
+ st->gpo_irq[1] ? 2 : 1, I3C_SDR);
+}
+
+static int ad4062_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pm_ad4062_triggered_buffer_postenable(st);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(&st->i3cdev->dev);
+ return 0;
+}
+
+static int ad4062_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ pm_runtime_put_autosuspend(&st->i3cdev->dev);
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad4062_triggered_buffer_setup_ops = {
+ .postenable = &ad4062_triggered_buffer_postenable,
+ .predisable = &ad4062_triggered_buffer_predisable,
+};
+
+static int ad4062_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+ else
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int ad4062_get_current_scan_type(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ return st->mode == AD4062_BURST_AVERAGING_MODE ?
+ AD4062_SCAN_TYPE_BURST_AVG :
+ AD4062_SCAN_TYPE_SAMPLE;
+}
+
+static const struct iio_info ad4062_info = {
+ .read_raw = ad4062_read_raw,
+ .write_raw = ad4062_write_raw,
+ .read_avail = ad4062_read_avail,
+ .read_event_config = ad4062_read_event_config,
+ .write_event_config = ad4062_write_event_config,
+ .read_event_value = ad4062_read_event_value,
+ .write_event_value = ad4062_write_event_value,
+ .event_attrs = &ad4062_event_attribute_group,
+ .get_current_scan_type = ad4062_get_current_scan_type,
+ .debugfs_reg_access = ad4062_debugfs_reg_access,
+};
+
+static const struct regmap_config ad4062_regmap_config = {
+ .name = "ad4062",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AD4062_MAX_REG,
+ .rd_table = &ad4062_regmap_rd_table,
+ .wr_table = &ad4062_regmap_wr_table,
+ .can_sleep = true,
+};
+
+static int ad4062_regulators_get(struct ad4062_state *st, bool *ref_sel)
+{
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+
+ ret = devm_regulator_get_enable(dev, "vio");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable vio voltage\n");
+
+ st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref");
+ *ref_sel = st->vref_uV == -ENODEV;
+ if (st->vref_uV < 0 && !*ref_sel)
+ return dev_err_probe(dev, st->vref_uV,
+ "Failed to enable and read ref voltage\n");
+
+ if (*ref_sel) {
+ st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "vdd");
+ if (st->vref_uV < 0)
+ return dev_err_probe(dev, st->vref_uV,
+ "Failed to enable and read vdd voltage\n");
+ } else {
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable vdd regulator\n");
+ }
+
+ return 0;
+}
+
+static int ad4062_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int ad4062_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct ad4062_state *st = gpiochip_get_data(gc);
+ unsigned int reg_val = value ? AD4062_GP_STATIC_HIGH : AD4062_GP_STATIC_LOW;
+
+ if (offset)
+ return regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_1,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, reg_val));
+ else
+ return regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_0,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, reg_val));
+}
+
+static int ad4062_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct ad4062_state *st = gpiochip_get_data(gc);
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD4062_REG_GP_CONF, &reg_val);
+ if (ret)
+ return ret;
+
+ if (offset)
+ reg_val = FIELD_GET(AD4062_REG_GP_CONF_MODE_MSK_1, reg_val);
+ else
+ reg_val = FIELD_GET(AD4062_REG_GP_CONF_MODE_MSK_0, reg_val);
+
+ return reg_val == AD4062_GP_STATIC_HIGH;
+}
+
+static void ad4062_gpio_disable(void *data)
+{
+ struct ad4062_state *st = data;
+ u8 val = FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, AD4062_GP_DISABLED) |
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, AD4062_GP_DISABLED);
+
+ regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_1 | AD4062_REG_GP_CONF_MODE_MSK_0,
+ val);
+}
+
+static int ad4062_gpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct ad4062_state *st = gpiochip_get_data(gc);
+
+ bitmap_zero(valid_mask, ngpios);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(st->gpo_irq); i++)
+ __assign_bit(i, valid_mask, !st->gpo_irq[i]);
+
+ return 0;
+}
+
+static int ad4062_gpio_init(struct ad4062_state *st)
+{
+ struct device *dev = &st->i3cdev->dev;
+ struct gpio_chip *gc;
+ u8 val, mask;
+ int ret;
+
+ if (!device_property_read_bool(dev, "gpio-controller"))
+ return 0;
+
+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
+ if (!gc)
+ return -ENOMEM;
+
+ val = 0;
+ mask = 0;
+ if (!st->gpo_irq[0]) {
+ mask |= AD4062_REG_GP_CONF_MODE_MSK_0;
+ val |= FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, AD4062_GP_STATIC_LOW);
+ }
+ if (!st->gpo_irq[1]) {
+ mask |= AD4062_REG_GP_CONF_MODE_MSK_1;
+ val |= FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, AD4062_GP_STATIC_LOW);
+ }
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ mask, val);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, ad4062_gpio_disable, st);
+ if (ret)
+ return ret;
+
+ gc->parent = dev;
+ gc->label = st->chip->name;
+ gc->owner = THIS_MODULE;
+ gc->base = -1;
+ gc->ngpio = 2;
+ gc->init_valid_mask = ad4062_gpio_init_valid_mask;
+ gc->get_direction = ad4062_gpio_get_direction;
+ gc->set = ad4062_gpio_set;
+ gc->get = ad4062_gpio_get;
+ gc->can_sleep = true;
+
+ ret = devm_gpiochip_add_data(dev, gc, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to register GPIO chip\n");
+
+ return 0;
+}
+
+static const struct i3c_device_id ad4062_id_table[] = {
+ I3C_DEVICE(AD4062_I3C_VENDOR, AD4060_PROD_ID, &ad4060_chip_info),
+ I3C_DEVICE(AD4062_I3C_VENDOR, AD4062_PROD_ID, &ad4062_chip_info),
+ { }
+};
+MODULE_DEVICE_TABLE(i3c, ad4062_id_table);
+
+static int ad4062_probe(struct i3c_device *i3cdev)
+{
+ const struct i3c_device_id *id = i3c_device_match_id(i3cdev, ad4062_id_table);
+ const struct ad4062_chip_info *chip = id->data;
+ struct device *dev = &i3cdev->dev;
+ struct iio_dev *indio_dev;
+ struct ad4062_state *st;
+ bool ref_sel;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->i3cdev = i3cdev;
+ i3cdev_set_drvdata(i3cdev, st);
+ init_completion(&st->completion);
+
+ ret = ad4062_regulators_get(st, &ref_sel);
+ if (ret)
+ return ret;
+
+ st->regmap = devm_regmap_init_i3c(i3cdev, &ad4062_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to initialize regmap\n");
+
+ st->mode = AD4062_SAMPLE_MODE;
+ st->wait_event = false;
+ st->chip = chip;
+ st->sampling_frequency = 0;
+ st->events_frequency = 0;
+ st->oversamp_ratio = 0;
+ st->indio_dev = indio_dev;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &ad4062_info;
+ indio_dev->name = chip->name;
+ indio_dev->channels = chip->channels;
+
+ ret = ad4062_soft_reset(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "AD4062 failed to soft reset\n");
+
+ ret = ad4062_check_ids(st);
+ if (ret)
+ return ret;
+
+ ret = ad4062_setup(indio_dev, indio_dev->channels, &ref_sel);
+ if (ret)
+ return ret;
+
+ ret = ad4062_request_irq(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad4062_request_trigger(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&i3cdev->dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad4062_poll_handler,
+ &ad4062_triggered_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n");
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = ad4062_request_ibi(i3cdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request i3c ibi\n");
+
+ ret = ad4062_gpio_init(st);
+ if (ret)
+ return ret;
+
+ ret = devm_work_autocancel(dev, &st->trig_conv, ad4062_trigger_work);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int ad4062_runtime_suspend(struct device *dev)
+{
+ struct ad4062_state *st = dev_get_drvdata(dev);
+
+ return regmap_write(st->regmap, AD4062_REG_DEVICE_CONFIG,
+ FIELD_PREP(AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK,
+ AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE));
+}
+
+static int ad4062_runtime_resume(struct device *dev)
+{
+ struct ad4062_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_clear_bits(st->regmap, AD4062_REG_DEVICE_CONFIG,
+ AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK);
+ if (ret)
+ return ret;
+
+ /* Wait device functional blocks to power up */
+ fsleep(3 * USEC_PER_MSEC);
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ad4062_pm_ops,
+ ad4062_runtime_suspend, ad4062_runtime_resume, NULL);
+
+static struct i3c_driver ad4062_driver = {
+ .driver = {
+ .name = "ad4062",
+ .pm = pm_ptr(&ad4062_pm_ops),
+ },
+ .probe = ad4062_probe,
+ .id_table = ad4062_id_table,
+};
+module_i3c_driver(ad4062_driver);
+
+MODULE_AUTHOR("Jorge Marques <jorge.marques@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD4062");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c
new file mode 100644
index 000000000000..e42ee328fcbf
--- /dev/null
+++ b/drivers/iio/adc/ad4134.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Analog Devices, Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#define AD4134_RESET_TIME_US (10 * USEC_PER_SEC)
+
+#define AD4134_REG_READ_MASK BIT(7)
+#define AD4134_SPI_MAX_XFER_LEN 3
+
+#define AD4134_EXT_CLOCK_MHZ (48 * HZ_PER_MHZ)
+
+#define AD4134_NUM_CHANNELS 4
+#define AD4134_CHAN_PRECISION_BITS 24
+
+#define AD4134_IFACE_CONFIG_A_REG 0x00
+#define AD4134_IFACE_CONFIG_B_REG 0x01
+#define AD4134_IFACE_CONFIG_B_SINGLE_INSTR BIT(7)
+
+#define AD4134_DEVICE_CONFIG_REG 0x02
+#define AD4134_DEVICE_CONFIG_POWER_MODE_MASK BIT(0)
+#define AD4134_POWER_MODE_HIGH_PERF 0x1
+
+#define AD4134_SILICON_REV_REG 0x07
+#define AD4134_SCRATCH_PAD_REG 0x0A
+#define AD4134_STREAM_MODE_REG 0x0E
+#define AD4134_SDO_PIN_SRC_SEL_REG 0x10
+#define AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK BIT(2)
+
+#define AD4134_DATA_PACKET_CONFIG_REG 0x11
+#define AD4134_DATA_PACKET_CONFIG_FRAME_MASK GENMASK(5, 4)
+#define AD4134_DATA_PACKET_24BIT_FRAME 0x2
+
+#define AD4134_DIG_IF_CFG_REG 0x12
+#define AD4134_DIF_IF_CFG_FORMAT_MASK GENMASK(1, 0)
+#define AD4134_DATA_FORMAT_SINGLE_CH_MODE 0x0
+
+#define AD4134_PW_DOWN_CTRL_REG 0x13
+#define AD4134_DEVICE_STATUS_REG 0x15
+#define AD4134_ODR_VAL_INT_LSB_REG 0x16
+#define AD4134_CH3_OFFSET_MSB_REG 0x3E
+#define AD4134_AIN_OR_ERROR_REG 0x48
+
+/*
+ * AD4134 register map ends at address 0x48 and there is no register for
+ * retrieving ADC sample data. Though, to make use of Linux regmap API both
+ * for register access and sample read, we define one virtual register for each
+ * ADC channel. AD4134_CH_VREG(x) maps a channel number to it's virtual register
+ * address while AD4134_VREG_CH(x) tells which channel given the address.
+ */
+#define AD4134_CH_VREG(x) ((x) + 0x50)
+#define AD4134_VREG_CH(x) ((x) - 0x50)
+
+#define AD4134_SPI_CRC_POLYNOM 0x07
+#define AD4134_SPI_CRC_INIT_VALUE 0xA5
+static unsigned char ad4134_spi_crc_table[CRC8_TABLE_SIZE];
+
+#define AD4134_CHANNEL(_index) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec ad4134_chan_set[] = {
+ AD4134_CHANNEL(0),
+ AD4134_CHANNEL(1),
+ AD4134_CHANNEL(2),
+ AD4134_CHANNEL(3),
+};
+
+struct ad4134_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ unsigned long sys_clk_hz;
+ struct gpio_desc *odr_gpio;
+ int refin_mv;
+ /*
+ * DMA (thus cache coherency maintenance) requires the transfer buffers
+ * to live in their own cache lines.
+ */
+ u8 rx_buf[AD4134_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[AD4134_SPI_MAX_XFER_LEN];
+};
+
+static const struct regmap_range ad4134_regmap_rd_range[] = {
+ regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_SILICON_REV_REG),
+ regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_PW_DOWN_CTRL_REG),
+ regmap_reg_range(AD4134_DEVICE_STATUS_REG, AD4134_AIN_OR_ERROR_REG),
+ regmap_reg_range(AD4134_CH_VREG(0), AD4134_CH_VREG(AD4134_NUM_CHANNELS)),
+};
+
+static const struct regmap_range ad4134_regmap_wr_range[] = {
+ regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_DEVICE_CONFIG_REG),
+ regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_SCRATCH_PAD_REG),
+ regmap_reg_range(AD4134_STREAM_MODE_REG, AD4134_PW_DOWN_CTRL_REG),
+ regmap_reg_range(AD4134_ODR_VAL_INT_LSB_REG, AD4134_CH3_OFFSET_MSB_REG),
+};
+
+static const struct regmap_access_table ad4134_regmap_rd_table = {
+ .yes_ranges = ad4134_regmap_rd_range,
+ .n_yes_ranges = ARRAY_SIZE(ad4134_regmap_rd_range),
+};
+
+static const struct regmap_access_table ad4134_regmap_wr_table = {
+ .yes_ranges = ad4134_regmap_wr_range,
+ .n_yes_ranges = ARRAY_SIZE(ad4134_regmap_wr_range),
+};
+
+static int ad4134_calc_spi_crc(u8 inst, u8 data)
+{
+ u8 buf[] = { inst, data };
+
+ return crc8(ad4134_spi_crc_table, buf, ARRAY_SIZE(buf),
+ AD4134_SPI_CRC_INIT_VALUE);
+}
+
+static void ad4134_prepare_spi_tx_buf(u8 inst, u8 data, u8 *buf)
+{
+ buf[0] = inst;
+ buf[1] = data;
+ buf[2] = ad4134_calc_spi_crc(inst, data);
+}
+
+static int ad4134_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct ad4134_state *st = context;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_buf,
+ .rx_buf = st->rx_buf,
+ .len = AD4134_SPI_MAX_XFER_LEN,
+ };
+ int ret;
+
+ ad4134_prepare_spi_tx_buf(reg, val, st->tx_buf);
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ if (st->rx_buf[2] != st->tx_buf[2])
+ dev_dbg(&st->spi->dev, "reg write CRC check failed\n");
+
+ return 0;
+}
+
+static int ad4134_data_read(struct ad4134_state *st, unsigned int reg,
+ unsigned int *val)
+{
+ unsigned int i;
+ int ret;
+
+ /*
+ * To be able to read data from all 4 channels through a single line, we
+ * set DOUTx output format to 0 in the digital interface config register
+ * (0x12). With that, data from all four channels is serialized and
+ * output on DOUT0. During the probe, we also set SDO_PIN_SRC_SEL in
+ * DEVICE_CONFIG_1 register to duplicate DOUT0 on the SDO pin. Combined,
+ * those configurations enable ADC data read through a conventional SPI
+ * interface. Now we read data from all channels but keep only the bits
+ * from the requested one.
+ */
+ for (i = 0; i < ARRAY_SIZE(ad4134_chan_set); i++) {
+ ret = spi_write_then_read(st->spi, NULL, 0, st->rx_buf,
+ BITS_TO_BYTES(AD4134_CHAN_PRECISION_BITS));
+ if (ret)
+ return ret;
+
+ /*
+ * AD4134 has a built-in feature that flags when data transfers
+ * don't run enough clock cycles to read the entire data frame.
+ * Clock out data from all channels to avoid that.
+ */
+ if (i == AD4134_VREG_CH(reg))
+ *val = get_unaligned_be24(st->rx_buf);
+ }
+
+ return 0;
+}
+
+static int ad4134_register_read(struct ad4134_state *st, unsigned int reg,
+ unsigned int *val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_buf,
+ .rx_buf = st->rx_buf,
+ .len = AD4134_SPI_MAX_XFER_LEN,
+ };
+ unsigned int inst;
+ int ret;
+
+ inst = AD4134_REG_READ_MASK | reg;
+ ad4134_prepare_spi_tx_buf(inst, 0, st->tx_buf);
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ *val = st->rx_buf[1];
+
+ /* Check CRC */
+ if (st->rx_buf[2] != st->tx_buf[2])
+ dev_dbg(&st->spi->dev, "reg read CRC check failed\n");
+
+ return 0;
+}
+
+static int ad4134_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct ad4134_state *st = context;
+
+ if (reg >= AD4134_CH_VREG(0))
+ return ad4134_data_read(st, reg, val);
+
+ return ad4134_register_read(st, reg, val);
+}
+
+static const struct regmap_config ad4134_regmap_config = {
+ .reg_read = ad4134_reg_read,
+ .reg_write = ad4134_reg_write,
+ .rd_table = &ad4134_regmap_rd_table,
+ .wr_table = &ad4134_regmap_wr_table,
+ .max_register = AD4134_CH_VREG(ARRAY_SIZE(ad4134_chan_set)),
+};
+
+static int ad4134_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad4134_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ gpiod_set_value_cansleep(st->odr_gpio, 1);
+ /*
+ * For slave mode gated DCLK (data sheet page 11), the minimum
+ * ODR high time is 3 * tDIGCLK. The internal digital clock
+ * period is tDIGCLK = 1/fDIGCLK = 2/fSYSCLK.
+ * The System clock frequency (fSYSCLK) is typically 48 MHz.
+ * Thus, ODR high time = 3 * (2 / (48 * HZ_PER_MHZ))
+ * ODR high time = 0.000000125 s = 125 ns
+ * 1 micro second should be more than enough. Not worth it
+ * tweaking for shorter dealy since this is not a fast data path.
+ */
+ fsleep(1);
+ gpiod_set_value_cansleep(st->odr_gpio, 0);
+ ret = regmap_read(st->regmap, AD4134_CH_VREG(chan->channel), val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->refin_mv;
+ *val2 = AD4134_CHAN_PRECISION_BITS - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4134_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad4134_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int ad4134_min_io_mode_setup(struct ad4134_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ int ret;
+
+ st->odr_gpio = devm_gpiod_get(dev, "odr", GPIOD_OUT_LOW);
+ if (IS_ERR(st->odr_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->odr_gpio),
+ "failed to get ODR GPIO\n");
+
+ ret = regmap_update_bits(st->regmap, AD4134_DIG_IF_CFG_REG,
+ AD4134_DIF_IF_CFG_FORMAT_MASK,
+ FIELD_PREP(AD4134_DIF_IF_CFG_FORMAT_MASK,
+ AD4134_DATA_FORMAT_SINGLE_CH_MODE));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set single channel mode\n");
+
+ ret = regmap_set_bits(st->regmap, AD4134_SDO_PIN_SRC_SEL_REG,
+ AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set SDO source selection\n");
+
+ return regmap_set_bits(st->regmap, AD4134_IFACE_CONFIG_B_REG,
+ AD4134_IFACE_CONFIG_B_SINGLE_INSTR);
+}
+
+static const struct iio_info ad4134_info = {
+ .read_raw = ad4134_read_raw,
+ .debugfs_reg_access = ad4134_debugfs_reg_access,
+};
+
+static const char * const ad4143_required_regulators[] = {
+ "avdd5", "dvdd5", "iovdd",
+};
+
+static const char * const ad4143_optional_regulators[] = {
+ "avdd1v8", "dvdd1v8", "clkvdd",
+};
+
+static int ad4134_regulator_setup(struct ad4134_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ int ret;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_required_regulators),
+ ad4143_required_regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable power supplies\n");
+
+ /* Required regulator that we need to read the voltage */
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get REFIN voltage.\n");
+
+ st->refin_mv = ret / (MICRO / MILLI);
+
+ ret = devm_regulator_get_enable_optional(dev, "ldoin");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to enable ldoin supply\n");
+
+ /* If ldoin was provided, then use the use the internal LDO regulators */
+ if (ret == 0)
+ return 0;
+
+ /*
+ * If ldoin is not provided, then avdd1v8, dvdd1v8, and clkvdd are
+ * required.
+ */
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_optional_regulators),
+ ad4143_optional_regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable 1V8 power supplies\n");
+
+ return 0;
+}
+
+static int ad4134_clock_select(struct ad4134_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct clk *xtal_clk, *clkin_clk;
+
+ /*
+ * AD4134 requires one external clock source and only one external clock
+ * source can be provided at a time. Try to get a crystal provided clock.
+ * If that fails, try to get a CMOS clock.
+ */
+ xtal_clk = devm_clk_get_optional_enabled(dev, "xtal");
+ if (!xtal_clk)
+ xtal_clk = devm_clk_get_optional_enabled(dev, "xtal");
+ if (IS_ERR(xtal_clk))
+ return dev_err_probe(dev, PTR_ERR(xtal_clk),
+ "failed to get xtal\n");
+
+ clkin_clk = devm_clk_get_optional_enabled(dev, "clkin");
+ if (!clkin_clk)
+ clkin_clk = devm_clk_get_optional_enabled(dev, "clkin");
+ if (IS_ERR(clkin_clk))
+ return dev_err_probe(dev, PTR_ERR(clkin_clk),
+ "failed to get clkin\n");
+
+ st->sys_clk_hz = clk_get_rate(xtal_clk) | clk_get_rate(clkin_clk);
+ if (st->sys_clk_hz != AD4134_EXT_CLOCK_MHZ)
+ dev_warn(dev, "invalid external clock frequency %lu\n",
+ st->sys_clk_hz);
+
+ return 0;
+}
+
+static int ad4134_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct reset_control *rst;
+ struct iio_dev *indio_dev;
+ struct ad4134_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ indio_dev->name = "ad4134";
+ indio_dev->channels = ad4134_chan_set;
+ indio_dev->num_channels = ARRAY_SIZE(ad4134_chan_set);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad4134_info;
+
+ ret = ad4134_regulator_setup(st);
+ if (ret)
+ return ret;
+
+ ret = ad4134_clock_select(st);
+ if (ret)
+ return ret;
+
+ rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst),
+ "failed to get and deassert reset\n");
+
+ crc8_populate_msb(ad4134_spi_crc_table, AD4134_SPI_CRC_POLYNOM);
+
+ st->regmap = devm_regmap_init(dev, NULL, st, &ad4134_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "failed to initialize regmap");
+
+ ret = ad4134_min_io_mode_setup(st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to setup minimum I/O mode\n");
+
+ /* Bump precision to 24-bit */
+ ret = regmap_update_bits(st->regmap, AD4134_DATA_PACKET_CONFIG_REG,
+ AD4134_DATA_PACKET_CONFIG_FRAME_MASK,
+ FIELD_PREP(AD4134_DATA_PACKET_CONFIG_FRAME_MASK,
+ AD4134_DATA_PACKET_24BIT_FRAME));
+ if (ret)
+ return ret;
+
+ /* Set high performance power mode */
+ ret = regmap_update_bits(st->regmap, AD4134_DEVICE_CONFIG_REG,
+ AD4134_DEVICE_CONFIG_POWER_MODE_MASK,
+ FIELD_PREP(AD4134_DEVICE_CONFIG_POWER_MODE_MASK,
+ AD4134_POWER_MODE_HIGH_PERF));
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id ad4134_id[] = {
+ { "ad4134" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad4134_id);
+
+static const struct of_device_id ad4134_of_match[] = {
+ { .compatible = "adi,ad4134" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad4134_of_match);
+
+static struct spi_driver ad4134_driver = {
+ .driver = {
+ .name = "ad4134",
+ .of_match_table = ad4134_of_match,
+ },
+ .probe = ad4134_probe,
+ .id_table = ad4134_id,
+};
+module_spi_driver(ad4134_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD4134 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_AD4134");
diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c
index efaed92191f1..82205bfae531 100644
--- a/drivers/iio/adc/ad4170-4.c
+++ b/drivers/iio/adc/ad4170-4.c
@@ -2973,7 +2973,7 @@ static int ad4170_probe(struct spi_device *spi)
if (spi->irq) {
ret = devm_request_irq(dev, spi->irq, &ad4170_irq_handler,
- IRQF_ONESHOT, indio_dev->name, indio_dev);
+ IRQF_NO_THREAD, indio_dev->name, indio_dev);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index 1bec6657394c..21d3f6aae972 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -16,7 +16,6 @@
#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
-#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index f28e4ca37707..7e17ccbcedd0 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -345,7 +345,7 @@ static int ad7606_spi_update_scan_mode(struct iio_dev *indio_dev,
* has no way of demuxing the data to filter out unwanted
* channels.
*/
- if (bitmap_weight(scan_mask, num_adc_ch) != num_adc_ch)
+ if (!bitmap_full(scan_mask, num_adc_ch))
return -EINVAL;
}
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 4d570383ef02..9e4a66477d2d 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -184,12 +184,6 @@ static const struct iio_info ad7766_info = {
.read_raw = &ad7766_read_raw,
};
-static irqreturn_t ad7766_irq(int irq, void *private)
-{
- iio_trigger_poll(private);
- return IRQ_HANDLED;
-}
-
static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
@@ -260,8 +254,8 @@ static int ad7766_probe(struct spi_device *spi)
* Some platforms might not allow the option to power it down so
* don't enable the interrupt to avoid extra load on the system
*/
- ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
- IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
+ ret = devm_request_irq(&spi->dev, spi->irq, iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN | IRQF_NO_THREAD,
dev_name(&spi->dev),
ad7766->trig);
if (ret < 0)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index d96802b7847a..fcd8aea7152e 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -6,6 +6,7 @@
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@@ -14,8 +15,12 @@
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
+#include <linux/limits.h>
+#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rational.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
@@ -107,10 +112,15 @@
#define AD7768_VCM_OFF 0x07
+#define ADAQ776X_GAIN_MAX_NANO (128 * NANO)
+#define ADAQ776X_MAX_GAIN_MODES 8
+
#define AD7768_TRIGGER_SOURCE_SYNC_IDX 0
#define AD7768_MAX_CHANNELS 1
+#define ADAQ7768_PGA_PINS 3
+
enum ad7768_conv_mode {
AD7768_CONTINUOUS,
AD7768_ONE_SHOT,
@@ -153,6 +163,51 @@ enum ad7768_scan_type {
AD7768_SCAN_TYPE_HIGH_SPEED,
};
+enum {
+ AD7768_PGA_GAIN_0,
+ AD7768_PGA_GAIN_1,
+ AD7768_PGA_GAIN_2,
+ AD7768_PGA_GAIN_3,
+ AD7768_PGA_GAIN_4,
+ AD7768_PGA_GAIN_5,
+ AD7768_PGA_GAIN_6,
+ AD7768_PGA_GAIN_7,
+};
+
+enum {
+ AD7768_AAF_IN1,
+ AD7768_AAF_IN2,
+ AD7768_AAF_IN3,
+};
+
+/* PGA and AAF gains in V/V */
+static const int adaq7768_gains[] = {
+ [AD7768_PGA_GAIN_0] = 325, /* 0.325 */
+ [AD7768_PGA_GAIN_1] = 650, /* 0.650 */
+ [AD7768_PGA_GAIN_2] = 1300, /* 1.300 */
+ [AD7768_PGA_GAIN_3] = 2600, /* 2.600 */
+ [AD7768_PGA_GAIN_4] = 5200, /* 5.200 */
+ [AD7768_PGA_GAIN_5] = 10400, /* 10.400 */
+ [AD7768_PGA_GAIN_6] = 20800, /* 20.800 */
+};
+
+static const int adaq7769_gains[] = {
+ [AD7768_PGA_GAIN_0] = 1000, /* 1.000 */
+ [AD7768_PGA_GAIN_1] = 2000, /* 2.000 */
+ [AD7768_PGA_GAIN_2] = 4000, /* 4.000 */
+ [AD7768_PGA_GAIN_3] = 8000, /* 8.000 */
+ [AD7768_PGA_GAIN_4] = 16000, /* 16.000 */
+ [AD7768_PGA_GAIN_5] = 32000, /* 32.000 */
+ [AD7768_PGA_GAIN_6] = 64000, /* 64.000 */
+ [AD7768_PGA_GAIN_7] = 128000, /* 128.000 */
+};
+
+static const int ad7768_aaf_gains_bp[] = {
+ [AD7768_AAF_IN1] = 10000, /* 1.000 */
+ [AD7768_AAF_IN2] = 3640, /* 0.364 */
+ [AD7768_AAF_IN3] = 1430, /* 0.143 */
+};
+
/* -3dB cutoff frequency multipliers (relative to ODR) for each filter type. */
static const int ad7768_filter_3db_odr_multiplier[] = {
[AD7768_FILTER_SINC5] = 204, /* 0.204 */
@@ -213,6 +268,19 @@ static const struct iio_scan_type ad7768_scan_type[] = {
},
};
+struct ad7768_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channel_spec;
+ int num_channels;
+ const int *pga_gains;
+ int num_pga_modes;
+ int default_pga_mode;
+ int pgia_mode2pin_offset;
+ bool has_pga;
+ bool has_variable_aaf;
+ bool has_vcm_regulator;
+};
+
struct ad7768_state {
struct spi_device *spi;
struct regmap *regmap;
@@ -228,13 +296,19 @@ struct ad7768_state {
unsigned int samp_freq;
unsigned int samp_freq_avail[ARRAY_SIZE(ad7768_mclk_div_rates)];
unsigned int samp_freq_avail_len;
+ unsigned int pga_gain_mode;
+ unsigned int aaf_gain;
+ int scale_tbl[ADAQ776X_MAX_GAIN_MODES][2];
struct completion completion;
struct iio_trigger *trig;
+ struct gpio_descs *pga_gpios;
struct gpio_desc *gpio_sync_in;
struct gpio_desc *gpio_reset;
const char *labels[AD7768_MAX_CHANNELS];
struct gpio_chip gpiochip;
+ const struct ad7768_chip_info *chip;
bool en_spi_sync;
+ struct mutex pga_lock; /* protect device internal state (PGA) */
/*
* DMA (thus cache coherency maintenance) may require the
* transfer buffers to live in their own cache lines.
@@ -457,6 +531,42 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
return ret;
}
+static void ad7768_fill_scale_tbl(struct iio_dev *dev)
+{
+ struct ad7768_state *st = iio_priv(dev);
+ const struct iio_scan_type *scan_type;
+ int val, val2, tmp0, tmp1, i;
+ struct u32_fract fract;
+ unsigned long n, d;
+ u64 tmp2;
+
+ scan_type = iio_get_current_scan_type(dev, &dev->channels[0]);
+ if (scan_type->sign == 's')
+ val2 = scan_type->realbits - 1;
+ else
+ val2 = scan_type->realbits;
+
+ for (i = 0; i < st->chip->num_pga_modes; i++) {
+ /* Convert gain to a fraction format */
+ fract.numerator = st->chip->pga_gains[i];
+ fract.denominator = MILLI;
+ if (st->chip->has_variable_aaf) {
+ fract.numerator *= ad7768_aaf_gains_bp[st->aaf_gain];
+ fract.denominator *= PERMYRIAD;
+ }
+
+ rational_best_approximation(fract.numerator, fract.denominator,
+ INT_MAX, INT_MAX, &n, &d);
+
+ val = mult_frac(st->vref_uv, d, n);
+ /* Would multiply by NANO here, but value is already in milli */
+ tmp2 = ((u64)val * MICRO) >> val2;
+ tmp0 = div_u64_rem(tmp2, NANO, &tmp1);
+ st->scale_tbl[i][0] = tmp0; /* Integer part */
+ st->scale_tbl[i][1] = abs(tmp1); /* Fractional part */
+ }
+}
+
static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st,
unsigned int dec_rate)
{
@@ -558,12 +668,66 @@ static int ad7768_configure_dig_fil(struct iio_dev *dev,
st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_idx];
}
+ /* Update scale table: scale values vary according to the precision */
+ ad7768_fill_scale_tbl(dev);
+
ad7768_fill_samp_freq_tbl(st);
/* A sync-in pulse is required after every configuration change */
return ad7768_send_sync_pulse(st);
}
+static int ad7768_setup_pga(struct device *dev, struct ad7768_state *st)
+{
+ st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW);
+ if (IS_ERR(st->pga_gpios))
+ return dev_err_probe(dev, PTR_ERR(st->pga_gpios),
+ "Failed to get PGA gpios.\n");
+
+ if (st->pga_gpios->ndescs != ADAQ7768_PGA_PINS)
+ return dev_err_probe(dev, -EINVAL,
+ "Expected %d GPIOs for PGA control.\n",
+ ADAQ7768_PGA_PINS);
+ return 0;
+}
+
+static int ad7768_calc_pga_gain(struct ad7768_state *st, int gain_int,
+ int gain_fract, int precision)
+{
+ u64 gain_nano;
+ u32 tmp;
+
+ gain_nano = gain_int * NANO + gain_fract;
+ gain_nano = clamp(gain_nano, 0, ADAQ776X_GAIN_MAX_NANO);
+ tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << precision, NANO);
+ gain_nano = DIV_ROUND_CLOSEST(st->vref_uv, tmp);
+ if (st->chip->has_variable_aaf)
+ gain_nano = DIV_ROUND_CLOSEST_ULL(gain_nano * PERMYRIAD,
+ ad7768_aaf_gains_bp[st->aaf_gain]);
+
+ return find_closest(gain_nano, st->chip->pga_gains,
+ (int)st->chip->num_pga_modes);
+}
+
+static int ad7768_set_pga_gain(struct ad7768_state *st,
+ int gain_mode)
+{
+ int pgia_pins_value = abs(gain_mode - st->chip->pgia_mode2pin_offset);
+ DECLARE_BITMAP(bitmap, ADAQ7768_PGA_PINS) = { };
+ int ret;
+
+ guard(mutex)(&st->pga_lock);
+
+ bitmap_write(bitmap, pgia_pins_value, 0, ADAQ7768_PGA_PINS);
+ ret = gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap);
+ if (ret)
+ return ret;
+
+ st->pga_gain_mode = gain_mode;
+
+ return 0;
+}
+
static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
struct iio_dev *indio_dev = gpiochip_get_data(chip);
@@ -735,6 +899,19 @@ static int ad7768_get_filter_type_attr(struct iio_dev *dev,
return ad7768_filter_regval_to_type[FIELD_GET(mask, mode)];
}
+static int ad7768_update_dec_rate(struct iio_dev *dev, unsigned int dec_rate)
+{
+ struct ad7768_state *st = iio_priv(dev);
+ int ret;
+
+ ret = ad7768_configure_dig_fil(dev, st->filter_type, dec_rate);
+ if (ret)
+ return ret;
+
+ /* Update sampling frequency */
+ return ad7768_set_freq(st, st->samp_freq);
+}
+
static const struct iio_enum ad7768_filter_type_iio_enum = {
.items = ad7768_filter_enum,
.num_items = ARRAY_SIZE(ad7768_filter_enum),
@@ -748,24 +925,32 @@ static const struct iio_chan_spec_ext_info ad7768_ext_info[] = {
{ }
};
+#define AD7768_CHAN(_idx, _msk_avail) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate_available = _msk_avail, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .ext_info = ad7768_ext_info, \
+ .indexed = 1, \
+ .channel = _idx, \
+ .scan_index = _idx, \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = ad7768_scan_type, \
+ .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type), \
+}
+
static const struct iio_chan_spec ad7768_channels[] = {
- {
- .type = IIO_VOLTAGE,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
- BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
- .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
- .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
- .ext_info = ad7768_ext_info,
- .indexed = 1,
- .channel = 0,
- .scan_index = 0,
- .has_ext_scan_type = 1,
- .ext_scan_type = ad7768_scan_type,
- .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type),
- },
+ AD7768_CHAN(0, 0),
+};
+
+static const struct iio_chan_spec adaq776x_channels[] = {
+ AD7768_CHAN(0, BIT(IIO_CHAN_INFO_SCALE)),
};
static int ad7768_read_raw(struct iio_dev *indio_dev,
@@ -795,7 +980,19 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = (st->vref_uv * 2) / 1000;
+ if (st->chip->has_pga) {
+ guard(mutex)(&st->pga_lock);
+
+ *val = st->scale_tbl[st->pga_gain_mode][0];
+ *val2 = st->scale_tbl[st->pga_gain_mode][1];
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ temp = (st->vref_uv * 2) / 1000;
+ if (st->chip->has_variable_aaf)
+ temp = (temp * PERMYRIAD) / ad7768_aaf_gains_bp[st->aaf_gain];
+
+ *val = temp;
*val2 = scan_type->realbits;
return IIO_VAL_FRACTIONAL_LOG2;
@@ -851,31 +1048,24 @@ static int ad7768_read_avail(struct iio_dev *indio_dev,
*length = st->samp_freq_avail_len;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (int *)st->scale_tbl;
+ *length = st->chip->num_pga_modes * 2;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
-static int __ad7768_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long info)
+static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
{
- struct ad7768_state *st = iio_priv(indio_dev);
- int ret;
-
- switch (info) {
- case IIO_CHAN_INFO_SAMP_FREQ:
- return ad7768_set_freq(st, val);
-
- case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = ad7768_configure_dig_fil(indio_dev, st->filter_type, val);
- if (ret)
- return ret;
-
- /* Update sampling frequency */
- return ad7768_set_freq(st, st->samp_freq);
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
default:
- return -EINVAL;
+ return IIO_VAL_INT_PLUS_MICRO;
}
}
@@ -883,15 +1073,47 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
int ret;
- if (!iio_device_claim_direct(indio_dev))
- return -EBUSY;
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
- ret = __ad7768_write_raw(indio_dev, chan, val, val2, info);
- iio_device_release_direct(indio_dev);
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- return ret;
+ ret = ad7768_set_freq(st, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad7768_update_dec_rate(indio_dev, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SCALE: {
+ int gain_mode;
+
+ if (!st->chip->has_pga)
+ return -EOPNOTSUPP;
+
+ if (scan_type->sign == 's')
+ gain_mode = ad7768_calc_pga_gain(st, val, val2,
+ scan_type->realbits - 1);
+ else
+ gain_mode = ad7768_calc_pga_gain(st, val, val2,
+ scan_type->realbits);
+
+ return ad7768_set_pga_gain(st, gain_mode);
+ }
+ default:
+ return -EINVAL;
+ }
}
static int ad7768_read_label(struct iio_dev *indio_dev,
@@ -915,6 +1137,7 @@ static const struct iio_info ad7768_info = {
.read_raw = &ad7768_read_raw,
.read_avail = &ad7768_read_avail,
.write_raw = &ad7768_write_raw,
+ .write_raw_get_fmt = &ad7768_write_raw_get_fmt,
.read_label = ad7768_read_label,
.get_current_scan_type = &ad7768_get_current_scan_type,
.debugfs_reg_access = &ad7768_reg_access,
@@ -1298,8 +1521,9 @@ static const struct regulator_desc vcm_desc = {
.owner = THIS_MODULE,
};
-static int ad7768_register_regulators(struct device *dev, struct ad7768_state *st,
- struct iio_dev *indio_dev)
+static int ad7768_register_vcm_regulator(struct device *dev,
+ struct ad7768_state *st,
+ struct iio_dev *indio_dev)
{
struct regulator_config config = {
.dev = dev,
@@ -1321,6 +1545,79 @@ static int ad7768_register_regulators(struct device *dev, struct ad7768_state *s
return 0;
}
+static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st)
+{
+ u32 val;
+ int ret;
+
+ ret = device_property_read_u32(dev, "adi,aaf-gain-bp", &val);
+ if (ret == -EINVAL) {
+ /* If controllable, use default */
+ if (st->chip->has_variable_aaf)
+ st->aaf_gain = AD7768_AAF_IN1;
+ return 0;
+ }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get AAF gain value\n");
+
+ if (!st->chip->has_variable_aaf)
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "AAF gain provided, but not supported for %s\n", st->chip->name);
+
+ switch (val) {
+ case 10000:
+ st->aaf_gain = AD7768_AAF_IN1;
+ break;
+ case 3640:
+ st->aaf_gain = AD7768_AAF_IN2;
+ break;
+ case 1430:
+ st->aaf_gain = AD7768_AAF_IN3;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid firmware provided AAF gain\n");
+ }
+
+ return 0;
+}
+
+static const struct ad7768_chip_info ad7768_chip_info = {
+ .name = "ad7768-1",
+ .channel_spec = ad7768_channels,
+ .num_channels = ARRAY_SIZE(ad7768_channels),
+ .has_vcm_regulator = true,
+};
+
+static const struct ad7768_chip_info adaq7767_chip_info = {
+ .name = "adaq7767-1",
+ .channel_spec = ad7768_channels,
+ .num_channels = ARRAY_SIZE(ad7768_channels),
+ .has_variable_aaf = true,
+};
+
+static const struct ad7768_chip_info adaq7768_chip_info = {
+ .name = "adaq7768-1",
+ .channel_spec = adaq776x_channels,
+ .num_channels = ARRAY_SIZE(adaq776x_channels),
+ .pga_gains = adaq7768_gains,
+ .default_pga_mode = AD7768_PGA_GAIN_2,
+ .num_pga_modes = ARRAY_SIZE(adaq7768_gains),
+ .pgia_mode2pin_offset = 6,
+ .has_pga = true,
+};
+
+static const struct ad7768_chip_info adaq7769_chip_info = {
+ .name = "adaq7769-1",
+ .channel_spec = adaq776x_channels,
+ .num_channels = ARRAY_SIZE(adaq776x_channels),
+ .pga_gains = adaq7769_gains,
+ .default_pga_mode = AD7768_PGA_GAIN_0,
+ .num_pga_modes = ARRAY_SIZE(adaq7769_gains),
+ .pgia_mode2pin_offset = 0,
+ .has_pga = true,
+ .has_variable_aaf = true,
+};
+
static int ad7768_probe(struct spi_device *spi)
{
struct ad7768_state *st;
@@ -1347,6 +1644,7 @@ static int ad7768_probe(struct spi_device *spi)
return ret;
}
+ st->chip = spi_get_device_match_data(spi);
st->spi = spi;
st->regmap = devm_regmap_init_spi(spi, &ad7768_regmap_config);
@@ -1371,14 +1669,20 @@ static int ad7768_probe(struct spi_device *spi)
st->mclk_freq = clk_get_rate(st->mclk);
- indio_dev->channels = ad7768_channels;
- indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
- indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->channels = st->chip->channel_spec;
+ indio_dev->num_channels = st->chip->num_channels;
+ indio_dev->name = st->chip->name;
indio_dev->info = &ad7768_info;
indio_dev->modes = INDIO_DIRECT_MODE;
/* Register VCM output regulator */
- ret = ad7768_register_regulators(&spi->dev, st, indio_dev);
+ if (st->chip->has_vcm_regulator) {
+ ret = ad7768_register_vcm_regulator(&spi->dev, st, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = ad7768_parse_aaf_gain(&spi->dev, st);
if (ret)
return ret;
@@ -1389,14 +1693,26 @@ static int ad7768_probe(struct spi_device *spi)
}
init_completion(&st->completion);
+ ret = devm_mutex_init(&spi->dev, &st->pga_lock);
+ if (ret)
+ return ret;
+
+ if (st->chip->has_pga) {
+ ret = ad7768_setup_pga(&spi->dev, st);
+ if (ret)
+ return ret;
+
+ ret = ad7768_set_pga_gain(st, st->chip->default_pga_mode);
+ if (ret)
+ return ret;
+ }
- ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels));
+ ret = ad7768_set_channel_label(indio_dev, st->chip->num_channels);
if (ret)
return ret;
- ret = devm_request_irq(&spi->dev, spi->irq,
- &ad7768_interrupt,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ret = devm_request_irq(&spi->dev, spi->irq, &ad7768_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
indio_dev->name, indio_dev);
if (ret)
return ret;
@@ -1409,13 +1725,19 @@ static int ad7768_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7768_id_table[] = {
- { "ad7768-1", 0 },
+ { "ad7768-1", (kernel_ulong_t)&ad7768_chip_info },
+ { "adaq7767-1", (kernel_ulong_t)&adaq7767_chip_info },
+ { "adaq7768-1", (kernel_ulong_t)&adaq7768_chip_info },
+ { "adaq7769-1", (kernel_ulong_t)&adaq7769_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7768_id_table);
static const struct of_device_id ad7768_of_match[] = {
- { .compatible = "adi,ad7768-1" },
+ { .compatible = "adi,ad7768-1", .data = &ad7768_chip_info },
+ { .compatible = "adi,adaq7767-1", .data = &adaq7767_chip_info },
+ { .compatible = "adi,adaq7768-1", .data = &adaq7768_chip_info },
+ { .compatible = "adi,adaq7769-1", .data = &adaq7769_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad7768_of_match);
diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c
index aac5049c9a07..695cc79e78da 100644
--- a/drivers/iio/adc/ad7779.c
+++ b/drivers/iio/adc/ad7779.c
@@ -840,7 +840,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev
iio_trigger_set_drvdata(st->trig, st);
ret = devm_request_irq(dev, st->spi->irq, iio_trigger_generic_data_rdy_poll,
- IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name,
+ IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name,
st->trig);
if (ret)
return dev_err_probe(dev, ret, "request IRQ %d failed\n",
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 2d8f8da3671d..022888545580 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -5,29 +5,29 @@
* Copyright 2012-2020 Analog Devices Inc.
*/
+#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
+#include <linux/clk.h>
#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
+#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/seq_file.h>
#include <linux/err.h>
-#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
-
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include <linux/clk.h>
-
/*
* ADI High-Speed ADC common spi interface registers
* See Application-Note AN-877:
@@ -73,6 +73,7 @@
#define AN877_ADC_OUTPUT_MODE_OFFSET_BINARY 0x0
#define AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT 0x1
#define AN877_ADC_OUTPUT_MODE_GRAY_CODE 0x2
+#define AN877_ADC_OUTPUT_MODE_MASK GENMASK(1, 0)
/* AN877_ADC_REG_OUTPUT_PHASE */
#define AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN 0x20
@@ -82,11 +83,19 @@
#define AN877_ADC_DCO_DELAY_ENABLE 0x80
/*
+ * Analog Devices AD9211 10-Bit, 200/250/300 MSPS ADC
+ */
+
+#define CHIPID_AD9211 0x06
+#define AD9211_DEF_OUTPUT_MODE 0x01
+#define AD9211_REG_VREF_MASK GENMASK(4, 0)
+
+/*
* Analog Devices AD9265 16-Bit, 125/105/80 MSPS ADC
*/
#define CHIPID_AD9265 0x64
-#define AD9265_DEF_OUTPUT_MODE 0x40
+#define AD9265_DEF_OUTPUT_MODE 0x41
#define AD9265_REG_VREF_MASK 0xC0
/*
@@ -94,7 +103,7 @@
*/
#define CHIPID_AD9434 0x6A
-#define AD9434_DEF_OUTPUT_MODE 0x00
+#define AD9434_DEF_OUTPUT_MODE 0x01
#define AD9434_REG_VREF_MASK GENMASK(4, 0)
/*
@@ -102,7 +111,7 @@
*/
#define CHIPID_AD9467 0x50
-#define AD9467_DEF_OUTPUT_MODE 0x08
+#define AD9467_DEF_OUTPUT_MODE 0x09
#define AD9467_REG_VREF_MASK 0x0F
/*
@@ -110,6 +119,7 @@
*/
#define CHIPID_AD9643 0x82
+#define AD9643_DEF_OUTPUT_MODE 0x01
#define AD9643_REG_VREF_MASK 0x1F
/*
@@ -117,6 +127,7 @@
*/
#define CHIPID_AD9652 0xC1
+#define AD9652_DEF_OUTPUT_MODE 0x01
#define AD9652_REG_VREF_MASK 0xC0
/*
@@ -124,6 +135,7 @@
*/
#define CHIPID_AD9649 0x6F
+#define AD9649_DEF_OUTPUT_MODE 0x01
#define AD9649_TEST_POINTS 8
#define AD9647_MAX_TEST_POINTS 32
@@ -145,6 +157,7 @@ struct ad9467_chip_info {
unsigned int num_lanes;
unsigned int dco_en;
unsigned int test_points;
+ const int *offset_range;
/* data clock output */
bool has_dco;
bool has_dco_invert;
@@ -234,6 +247,21 @@ static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg,
return 0;
}
+static const int ad9434_offset_range[] = {
+ -128, 1, 127,
+};
+
+static const unsigned int ad9211_scale_table[][2] = {
+ {980, 0x10}, {1000, 0x11}, {1020, 0x12}, {1040, 0x13},
+ {1060, 0x14}, {1080, 0x15}, {1100, 0x16}, {1120, 0x17},
+ {1140, 0x18}, {1160, 0x19}, {1180, 0x1A}, {1190, 0x1B},
+ {1200, 0x1C}, {1210, 0x1D}, {1220, 0x1E}, {1230, 0x1F},
+ {1250, 0x0}, {1270, 0x1}, {1290, 0x2}, {1310, 0x3},
+ {1330, 0x4}, {1350, 0x5}, {1370, 0x6}, {1390, 0x7},
+ {1410, 0x8}, {1430, 0x9}, {1450, 0xA}, {1460, 0xB},
+ {1470, 0xC}, {1480, 0xD}, {1490, 0xE}, {1500, 0xF},
+};
+
static const unsigned int ad9265_scale_table[][2] = {
{1250, 0x00}, {1500, 0x40}, {1750, 0x80}, {2000, 0xC0},
};
@@ -297,8 +325,29 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index,
}, \
}
+static const struct iio_chan_spec ad9211_channels[] = {
+ AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 10, 's'),
+};
+
static const struct iio_chan_spec ad9434_channels[] = {
- AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 12, 's'),
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
};
static const struct iio_chan_spec ad9467_channels[] = {
@@ -367,6 +416,24 @@ static const struct ad9467_chip_info ad9434_chip_tbl = {
.default_output_mode = AD9434_DEF_OUTPUT_MODE,
.vref_mask = AD9434_REG_VREF_MASK,
.num_lanes = 6,
+ .offset_range = ad9434_offset_range,
+};
+
+static const struct ad9467_chip_info ad9211_chip_tbl = {
+ .name = "ad9211",
+ .id = CHIPID_AD9211,
+ .max_rate = 300 * HZ_PER_MHZ,
+ .scale_table = ad9211_scale_table,
+ .num_scales = ARRAY_SIZE(ad9211_scale_table),
+ .channels = ad9211_channels,
+ .num_channels = ARRAY_SIZE(ad9211_channels),
+ .test_points = AD9647_MAX_TEST_POINTS,
+ .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
+ AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
+ .default_output_mode = AD9211_DEF_OUTPUT_MODE,
+ .vref_mask = AD9211_REG_VREF_MASK,
+ .has_dco = true,
};
static const struct ad9467_chip_info ad9265_chip_tbl = {
@@ -399,6 +466,7 @@ static const struct ad9467_chip_info ad9643_chip_tbl = {
.test_mask = BIT(AN877_ADC_TESTMODE_RAMP) |
GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_RAMP + 1,
+ .default_output_mode = AD9643_DEF_OUTPUT_MODE,
.vref_mask = AD9643_REG_VREF_MASK,
.has_dco = true,
.has_dco_invert = true,
@@ -417,6 +485,7 @@ static const struct ad9467_chip_info ad9649_chip_tbl = {
.test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1,
+ .default_output_mode = AD9649_DEF_OUTPUT_MODE,
.has_dco = true,
.has_dco_invert = true,
.dco_en = AN877_ADC_DCO_DELAY_ENABLE,
@@ -434,6 +503,7 @@ static const struct ad9467_chip_info ad9652_chip_tbl = {
.test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
+ .default_output_mode = AD9652_DEF_OUTPUT_MODE,
.vref_mask = AD9652_REG_VREF_MASK,
.has_dco = true,
};
@@ -499,6 +569,33 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
return -EINVAL;
}
+static int ad9467_get_offset(struct ad9467_state *st, int *val)
+{
+ int ret;
+
+ ret = ad9467_spi_read(st, AN877_ADC_REG_OFFSET);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+
+ return IIO_VAL_INT;
+}
+
+static int ad9467_set_offset(struct ad9467_state *st, int val)
+{
+ int ret;
+
+ if (val < st->info->offset_range[0] || val > st->info->offset_range[2])
+ return -EINVAL;
+
+ ret = ad9467_spi_write(st, AN877_ADC_REG_OFFSET, val);
+ if (ret < 0)
+ return ret;
+
+ return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+}
+
static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode)
{
int ret;
@@ -582,10 +679,14 @@ static int ad9467_backend_testmode_off(struct ad9467_state *st,
static int ad9647_calibrate_prepare(struct ad9467_state *st)
{
+ unsigned int cmode;
unsigned int c;
int ret;
- ret = ad9467_outputmode_set(st, st->info->default_output_mode);
+ cmode = st->info->default_output_mode;
+ FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode,
+ AN877_ADC_OUTPUT_MODE_OFFSET_BINARY);
+ ret = ad9467_outputmode_set(st, cmode);
if (ret)
return ret;
@@ -689,7 +790,7 @@ static int ad9647_calibrate_stop(struct ad9467_state *st)
return ret;
}
- mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+ mode = st->info->default_output_mode;
return ad9467_outputmode_set(st, mode);
}
@@ -802,6 +903,8 @@ static int ad9467_read_raw(struct iio_dev *indio_dev,
struct ad9467_state *st = iio_priv(indio_dev);
switch (m) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad9467_get_offset(st, val);
case IIO_CHAN_INFO_SCALE:
return ad9467_get_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -836,6 +939,8 @@ static int ad9467_write_raw(struct iio_dev *indio_dev,
int ret;
switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad9467_set_offset(st, val);
case IIO_CHAN_INFO_SCALE:
return ad9467_set_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -874,6 +979,10 @@ static int ad9467_read_avail(struct iio_dev *indio_dev,
const struct ad9467_chip_info *info = st->info;
switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *type = IIO_VAL_INT;
+ *vals = info->offset_range;
+ return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)st->scales;
*type = IIO_VAL_INT_PLUS_MICRO;
@@ -1077,12 +1186,17 @@ static ssize_t ad9467_chan_test_mode_write(struct file *file,
if (ret)
return ret;
- out_mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+ out_mode = st->info->default_output_mode;
ret = ad9467_outputmode_set(st, out_mode);
if (ret)
return ret;
} else {
- ret = ad9467_outputmode_set(st, st->info->default_output_mode);
+ unsigned int cmode;
+
+ cmode = st->info->default_output_mode;
+ FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode,
+ AN877_ADC_OUTPUT_MODE_OFFSET_BINARY);
+ ret = ad9467_outputmode_set(st, cmode);
if (ret)
return ret;
@@ -1264,6 +1378,7 @@ static int ad9467_probe(struct spi_device *spi)
}
static const struct of_device_id ad9467_of_match[] = {
+ { .compatible = "adi,ad9211", .data = &ad9211_chip_tbl, },
{ .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, },
{ .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, },
{ .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, },
@@ -1275,6 +1390,7 @@ static const struct of_device_id ad9467_of_match[] = {
MODULE_DEVICE_TABLE(of, ad9467_of_match);
static const struct spi_device_id ad9467_ids[] = {
+ { "ad9211", (kernel_ulong_t)&ad9211_chip_tbl },
{ "ad9265", (kernel_ulong_t)&ad9265_chip_tbl },
{ "ad9434", (kernel_ulong_t)&ad9434_chip_tbl },
{ "ad9467", (kernel_ulong_t)&ad9467_chip_tbl },
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
index 2de8a718d62a..db085dc5e526 100644
--- a/drivers/iio/adc/ade9000.c
+++ b/drivers/iio/adc/ade9000.c
@@ -964,7 +964,7 @@ static irqreturn_t ade9000_dready_thread(int irq, void *data)
struct iio_dev *indio_dev = data;
/* Handle data ready interrupt from C4/EVENT/DREADY pin */
- if (!iio_device_claim_buffer_mode(indio_dev)) {
+ if (iio_device_try_claim_buffer_mode(indio_dev)) {
ade9000_iio_push_buffer(indio_dev);
iio_device_release_buffer_mode(indio_dev);
}
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 14fa4238c2b9..5f445e0de9ea 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -591,17 +591,12 @@ static int axi_adc_create_platform_device(struct adi_axi_adc_state *st,
.size_data = st->info->pdata_sz,
};
struct platform_device *pdev;
- int ret;
pdev = platform_device_register_full(&pi);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
- ret = devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
- if (ret)
- return ret;
-
- return 0;
+ return devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
}
static const struct iio_backend_ops adi_axi_adc_ops = {
@@ -674,13 +669,14 @@ static const struct iio_backend_info axi_ad408x = {
static int adi_axi_adc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
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);
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
@@ -688,20 +684,19 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- st->dev = &pdev->dev;
- st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &axi_adc_regmap_config);
+ st->dev = dev;
+ st->regmap = devm_regmap_init_mmio(dev, base, &axi_adc_regmap_config);
if (IS_ERR(st->regmap))
- return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
"failed to init register map\n");
- st->info = device_get_match_data(&pdev->dev);
+ st->info = device_get_match_data(dev);
if (!st->info)
return -ENODEV;
- clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ return dev_err_probe(dev, PTR_ERR(clk),
"failed to get clock\n");
/*
@@ -716,47 +711,42 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
- ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
- dev_err(&pdev->dev,
- "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
- ADI_AXI_PCORE_VER_MAJOR(st->info->version),
- ADI_AXI_PCORE_VER_MINOR(st->info->version),
- ADI_AXI_PCORE_VER_PATCH(st->info->version),
- ADI_AXI_PCORE_VER_MAJOR(ver),
- ADI_AXI_PCORE_VER_MINOR(ver),
- ADI_AXI_PCORE_VER_PATCH(ver));
- return -ENODEV;
- }
-
- ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(st->info->version))
+ return dev_err_probe(dev, -ENODEV,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version),
+ ADI_AXI_PCORE_VER_MINOR(st->info->version),
+ ADI_AXI_PCORE_VER_PATCH(st->info->version),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+
+ ret = devm_iio_backend_register(dev, st->info->backend_info, st);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "failed to register iio backend\n");
+ return dev_err_probe(dev, ret, "failed to register iio backend\n");
- device_for_each_child_node_scoped(&pdev->dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
int val;
if (!st->info->has_child_nodes)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"invalid fdt axi-dac compatible.");
/* Processing only reg 0 node */
ret = fwnode_property_read_u32(child, "reg", &val);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "invalid reg property.");
+ return dev_err_probe(dev, ret, "invalid reg property.");
if (val != 0)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"invalid node address.");
ret = axi_adc_create_platform_device(st, child);
if (ret)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"cannot create device.");
}
- dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
+ dev_info(dev, "AXI ADC 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));
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index bf2bfd6bdc41..4be44c524b4d 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -472,16 +472,18 @@ static int aspeed_adc_probe(struct platform_device *pdev)
struct aspeed_adc_data *data;
int ret;
u32 adc_engine_control_reg_val;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev_of_node(dev);
unsigned long scaler_flags = 0;
char clk_name[32], clk_parent_name[32];
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
- data->dev = &pdev->dev;
- data->model_data = of_device_get_match_data(&pdev->dev);
+ data->dev = dev;
+ data->model_data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, indio_dev);
data->base = devm_platform_ioremap_resource(pdev, 0);
@@ -491,16 +493,15 @@ static int aspeed_adc_probe(struct platform_device *pdev)
/* Register ADC clock prescaler with source specified by device tree. */
spin_lock_init(&data->clk_lock);
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
- of_clk_get_parent_name(pdev->dev.of_node, 0));
+ of_clk_get_parent_name(np, 0));
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
data->model_data->model_name);
- data->fixed_div_clk = clk_hw_register_fixed_factor(
- &pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
+ data->fixed_div_clk = clk_hw_register_fixed_factor(dev, clk_name,
+ clk_parent_name, 0, 1, 2);
if (IS_ERR(data->fixed_div_clk))
return PTR_ERR(data->fixed_div_clk);
- ret = devm_add_action_or_reset(data->dev,
- aspeed_adc_unregister_fixed_divider,
+ ret = devm_add_action_or_reset(dev, aspeed_adc_unregister_fixed_divider,
data->fixed_div_clk);
if (ret)
return ret;
@@ -510,7 +511,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
data->model_data->model_name);
data->clk_prescaler = devm_clk_hw_register_divider(
- &pdev->dev, clk_name, clk_parent_name, 0,
+ dev, clk_name, clk_parent_name, 0,
data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
&data->clk_lock);
if (IS_ERR(data->clk_prescaler))
@@ -526,7 +527,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
data->model_data->model_name);
data->clk_scaler = devm_clk_hw_register_divider(
- &pdev->dev, clk_name, clk_parent_name, scaler_flags,
+ dev, clk_name, clk_parent_name, scaler_flags,
data->base + ASPEED_REG_CLOCK_CONTROL, 0,
data->model_data->scaler_bit_width,
data->model_data->need_prescaler ? CLK_DIVIDER_ONE_BASED : 0,
@@ -534,16 +535,14 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (IS_ERR(data->clk_scaler))
return PTR_ERR(data->clk_scaler);
- data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
- if (IS_ERR(data->rst)) {
- dev_err(&pdev->dev,
- "invalid or missing reset controller device tree entry");
- return PTR_ERR(data->rst);
- }
+ data->rst = devm_reset_control_get_shared(dev, NULL);
+ if (IS_ERR(data->rst))
+ return dev_err_probe(dev, PTR_ERR(data->rst),
+ "invalid or missing reset controller device tree entry");
+
reset_control_deassert(data->rst);
- ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
- data->rst);
+ ret = devm_add_action_or_reset(dev, aspeed_adc_reset_assert, data->rst);
if (ret)
return ret;
@@ -555,7 +554,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (of_property_present(data->dev->of_node, "aspeed,battery-sensing")) {
+ if (of_property_present(np, "aspeed,battery-sensing")) {
if (data->model_data->bat_sense_sup) {
data->battery_sensing = 1;
if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
@@ -567,15 +566,13 @@ static int aspeed_adc_probe(struct platform_device *pdev)
data->battery_mode_gain.div = 2;
}
} else
- dev_warn(&pdev->dev,
- "Failed to enable battery-sensing mode\n");
+ dev_warn(dev, "Failed to enable battery-sensing mode\n");
}
ret = clk_prepare_enable(data->clk_scaler->clk);
if (ret)
return ret;
- ret = devm_add_action_or_reset(data->dev,
- aspeed_adc_clk_disable_unprepare,
+ ret = devm_add_action_or_reset(dev, aspeed_adc_clk_disable_unprepare,
data->clk_scaler->clk);
if (ret)
return ret;
@@ -593,8 +590,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
writel(adc_engine_control_reg_val,
data->base + ASPEED_REG_ENGINE_CONTROL);
- ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
- data);
+ ret = devm_add_action_or_reset(dev, aspeed_adc_power_down, data);
if (ret)
return ret;
@@ -626,8 +622,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
aspeed_adc_iio_channels;
indio_dev->num_channels = data->model_data->num_channels;
- ret = devm_iio_device_register(data->dev, indio_dev);
- return ret;
+ return devm_iio_device_register(dev, indio_dev);
}
static const struct aspeed_adc_trim_locate ast2500_adc_trim = {
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index f2400897818c..c1a0061b16ca 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -543,22 +543,21 @@ static const struct iio_chan_spec exynos_adc_iio_channels[] = {
static int exynos_adc_probe(struct platform_device *pdev)
{
struct exynos_adc *info = NULL;
+ struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
int ret;
int irq;
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct exynos_adc));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->data = exynos_adc_get_data(pdev);
- if (!info->data) {
- dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
- return -EINVAL;
- }
+ if (!info->data)
+ return dev_err_probe(dev, -EINVAL, "failed getting exynos_adc_data\n");
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
@@ -566,44 +565,34 @@ static int exynos_adc_probe(struct platform_device *pdev)
if (info->data->needs_adc_phy) {
- info->pmu_map = syscon_regmap_lookup_by_phandle(
- pdev->dev.of_node,
- "samsung,syscon-phandle");
- if (IS_ERR(info->pmu_map)) {
- dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
- return PTR_ERR(info->pmu_map);
- }
+ info->pmu_map = syscon_regmap_lookup_by_phandle(np, "samsung,syscon-phandle");
+ if (IS_ERR(info->pmu_map))
+ return dev_err_probe(dev, PTR_ERR(info->pmu_map),
+ "syscon regmap lookup failed.\n");
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
info->irq = irq;
- info->dev = &pdev->dev;
+ info->dev = dev;
init_completion(&info->completion);
- info->clk = devm_clk_get(&pdev->dev, "adc");
- if (IS_ERR(info->clk)) {
- dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
- PTR_ERR(info->clk));
- return PTR_ERR(info->clk);
- }
+ info->clk = devm_clk_get(dev, "adc");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n");
if (info->data->needs_sclk) {
- info->sclk = devm_clk_get(&pdev->dev, "sclk");
- if (IS_ERR(info->sclk)) {
- dev_err(&pdev->dev,
- "failed getting sclk clock, err = %ld\n",
- PTR_ERR(info->sclk));
- return PTR_ERR(info->sclk);
- }
+ info->sclk = devm_clk_get(dev, "sclk");
+ if (IS_ERR(info->sclk))
+ return dev_err_probe(dev, PTR_ERR(info->sclk),
+ "failed getting sclk clock\n");
}
- info->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ info->vdd = devm_regulator_get(dev, "vdd");
if (IS_ERR(info->vdd))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->vdd),
- "failed getting regulator");
+ return dev_err_probe(dev, PTR_ERR(info->vdd), "failed getting regulator");
ret = regulator_enable(info->vdd);
if (ret)
@@ -619,7 +608,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
- indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->name = dev_name(dev);
indio_dev->info = &exynos_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = exynos_adc_iio_channels;
@@ -627,11 +616,9 @@ static int exynos_adc_probe(struct platform_device *pdev)
mutex_init(&info->lock);
- ret = request_irq(info->irq, exynos_adc_isr,
- 0, dev_name(&pdev->dev), info);
+ ret = request_irq(info->irq, exynos_adc_isr, 0, dev_name(dev), info);
if (ret < 0) {
- dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
- info->irq);
+ dev_err(dev, "failed requesting irq, irq = %d\n", info->irq);
goto err_disable_clk;
}
@@ -644,7 +631,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
if (ret < 0) {
- dev_err(&pdev->dev, "failed adding child nodes\n");
+ dev_err(dev, "failed adding child nodes\n");
goto err_of_populate;
}
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
index a6f21791c685..ddc3721f3f68 100644
--- a/drivers/iio/adc/mcp3911.c
+++ b/drivers/iio/adc/mcp3911.c
@@ -815,7 +815,7 @@ static int mcp3911_probe(struct spi_device *spi)
* don't enable the interrupt to avoid extra load on the system.
*/
ret = devm_request_irq(dev, spi->irq, &iio_trigger_generic_data_rdy_poll,
- IRQF_NO_AUTOEN | IRQF_ONESHOT,
+ IRQF_NO_AUTOEN | IRQF_NO_THREAD,
indio_dev->name, adc->trig);
if (ret)
return ret;
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
index cf8a8c0412ec..90919d282e7b 100644
--- a/drivers/iio/adc/men_z188_adc.c
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -171,5 +171,4 @@ module_mcb_driver(men_z188_driver);
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
-MODULE_ALIAS("mcb:16z188");
MODULE_IMPORT_NS("MCB");
diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c
new file mode 100644
index 000000000000..9efa883c277d
--- /dev/null
+++ b/drivers/iio/adc/nxp-sar-adc.c
@@ -0,0 +1,1016 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP SAR-ADC driver (adapted from Freescale Vybrid vf610 ADC driver
+ * by Fugang Duan <B38611@freescale.com>)
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2017, 2020-2025 NXP
+ * Copyright 2025, Linaro Ltd
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/circ_buf.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+/* SAR ADC registers. */
+#define NXP_SAR_ADC_CDR(__base, __channel) (((__base) + 0x100) + ((__channel) * 0x4))
+
+#define NXP_SAR_ADC_CDR_CDATA_MASK GENMASK(11, 0)
+#define NXP_SAR_ADC_CDR_VALID BIT(19)
+
+/* Main Configuration Register */
+#define NXP_SAR_ADC_MCR(__base) ((__base) + 0x00)
+
+#define NXP_SAR_ADC_MCR_PWDN BIT(0)
+#define NXP_SAR_ADC_MCR_ACKO BIT(5)
+#define NXP_SAR_ADC_MCR_ADCLKSEL BIT(8)
+#define NXP_SAR_ADC_MCR_TSAMP_MASK GENMASK(10, 9)
+#define NXP_SAR_ADC_MCR_NRSMPL_MASK GENMASK(12, 11)
+#define NXP_SAR_ADC_MCR_AVGEN BIT(13)
+#define NXP_SAR_ADC_MCR_CALSTART BIT(14)
+#define NXP_SAR_ADC_MCR_NSTART BIT(24)
+#define NXP_SAR_ADC_MCR_MODE BIT(29)
+#define NXP_SAR_ADC_MCR_OWREN BIT(31)
+
+/* Main Status Register */
+#define NXP_SAR_ADC_MSR(__base) ((__base) + 0x04)
+
+#define NXP_SAR_ADC_MSR_CALBUSY BIT(29)
+#define NXP_SAR_ADC_MSR_CALFAIL BIT(30)
+
+/* Interrupt Status Register */
+#define NXP_SAR_ADC_ISR(__base) ((__base) + 0x10)
+
+#define NXP_SAR_ADC_ISR_ECH BIT(0)
+
+/* Channel Pending Register */
+#define NXP_SAR_ADC_CEOCFR0(__base) ((__base) + 0x14)
+#define NXP_SAR_ADC_CEOCFR1(__base) ((__base) + 0x18)
+
+#define NXP_SAR_ADC_EOC_CH(c) BIT(c)
+
+/* Interrupt Mask Register */
+#define NXP_SAR_ADC_IMR(__base) ((__base) + 0x20)
+
+/* Channel Interrupt Mask Register */
+#define NXP_SAR_ADC_CIMR0(__base) ((__base) + 0x24)
+#define NXP_SAR_ADC_CIMR1(__base) ((__base) + 0x28)
+
+/* DMA Setting Register */
+#define NXP_SAR_ADC_DMAE(__base) ((__base) + 0x40)
+
+#define NXP_SAR_ADC_DMAE_DMAEN BIT(0)
+#define NXP_SAR_ADC_DMAE_DCLR BIT(1)
+
+/* DMA Control register */
+#define NXP_SAR_ADC_DMAR0(__base) ((__base) + 0x44)
+#define NXP_SAR_ADC_DMAR1(__base) ((__base) + 0x48)
+
+/* Conversion Timing Register */
+#define NXP_SAR_ADC_CTR0(__base) ((__base) + 0x94)
+#define NXP_SAR_ADC_CTR1(__base) ((__base) + 0x98)
+
+#define NXP_SAR_ADC_CTR_INPSAMP_MIN 0x08
+#define NXP_SAR_ADC_CTR_INPSAMP_MAX 0xff
+
+/* Normal Conversion Mask Register */
+#define NXP_SAR_ADC_NCMR0(__base) ((__base) + 0xa4)
+#define NXP_SAR_ADC_NCMR1(__base) ((__base) + 0xa8)
+
+/* Normal Conversion Mask Register field define */
+#define NXP_SAR_ADC_CH_MASK GENMASK(7, 0)
+
+/* Other field define */
+#define NXP_SAR_ADC_CONV_TIMEOUT (msecs_to_jiffies(100))
+#define NXP_SAR_ADC_CAL_TIMEOUT_US (100 * USEC_PER_MSEC)
+#define NXP_SAR_ADC_WAIT_US (2 * USEC_PER_MSEC)
+#define NXP_SAR_ADC_RESOLUTION 12
+
+/* Duration of conversion phases */
+#define NXP_SAR_ADC_TPT 2
+#define NXP_SAR_ADC_DP 2
+#define NXP_SAR_ADC_CT ((NXP_SAR_ADC_RESOLUTION + 2) * 4)
+#define NXP_SAR_ADC_CONV_TIME (NXP_SAR_ADC_TPT + NXP_SAR_ADC_CT + NXP_SAR_ADC_DP)
+
+#define NXP_SAR_ADC_NR_CHANNELS 8
+
+#define NXP_PAGE_SIZE SZ_4K
+#define NXP_SAR_ADC_DMA_SAMPLE_SZ DMA_SLAVE_BUSWIDTH_4_BYTES
+#define NXP_SAR_ADC_DMA_BUFF_SZ (NXP_PAGE_SIZE * NXP_SAR_ADC_DMA_SAMPLE_SZ)
+#define NXP_SAR_ADC_DMA_SAMPLE_CNT (NXP_SAR_ADC_DMA_BUFF_SZ / NXP_SAR_ADC_DMA_SAMPLE_SZ)
+
+struct nxp_sar_adc {
+ void __iomem *regs;
+ phys_addr_t regs_phys;
+ u8 current_channel;
+ u8 channels_used;
+ u16 value;
+ u32 vref_mV;
+
+ /* Save and restore context. */
+ u32 inpsamp;
+ u32 pwdn;
+
+ struct clk *clk;
+ struct dma_chan *dma_chan;
+ struct completion completion;
+ struct circ_buf dma_buf;
+
+ dma_addr_t rx_dma_buf;
+ dma_cookie_t cookie;
+
+ /* Protect circular buffers access. */
+ spinlock_t lock;
+
+ /* Array of enabled channels. */
+ u16 buffered_chan[NXP_SAR_ADC_NR_CHANNELS];
+
+ /* Buffer to be filled by the DMA. */
+ IIO_DECLARE_BUFFER_WITH_TS(u16, buffer, NXP_SAR_ADC_NR_CHANNELS);
+};
+
+struct nxp_sar_adc_data {
+ u32 vref_mV;
+ const char *model;
+};
+
+#define ADC_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+}
+
+static const struct iio_chan_spec nxp_sar_adc_iio_channels[] = {
+ ADC_CHAN(0, IIO_VOLTAGE),
+ ADC_CHAN(1, IIO_VOLTAGE),
+ ADC_CHAN(2, IIO_VOLTAGE),
+ ADC_CHAN(3, IIO_VOLTAGE),
+ ADC_CHAN(4, IIO_VOLTAGE),
+ ADC_CHAN(5, IIO_VOLTAGE),
+ ADC_CHAN(6, IIO_VOLTAGE),
+ ADC_CHAN(7, IIO_VOLTAGE),
+ /*
+ * The NXP SAR ADC documentation marks the channels 8 to 31 as
+ * "Reserved". Reflect the same in the driver in case new ADC
+ * variants comes with more channels.
+ */
+ IIO_CHAN_SOFT_TIMESTAMP(32),
+};
+
+static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable)
+{
+ if (enable)
+ writel(NXP_SAR_ADC_ISR_ECH, NXP_SAR_ADC_IMR(info->regs));
+ else
+ writel(0, NXP_SAR_ADC_IMR(info->regs));
+}
+
+static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable)
+{
+ u32 mcr;
+ bool pwdn;
+
+ mcr = readl(NXP_SAR_ADC_MCR(info->regs));
+
+ /*
+ * Get the current state and return it later. This is used for
+ * suspend/resume to get the power state
+ */
+ pwdn = FIELD_GET(NXP_SAR_ADC_MCR_PWDN, mcr);
+
+ /* When the enabled flag is not set, we set the power down bit */
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_PWDN, &mcr, !enable);
+
+ writel(mcr, NXP_SAR_ADC_MCR(info->regs));
+
+ /*
+ * Ensure there are at least three cycles between the
+ * configuration of NCMR and the setting of NSTART.
+ */
+ if (enable)
+ ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk) * 3));
+
+ return pwdn;
+}
+
+static inline bool nxp_sar_adc_enable(struct nxp_sar_adc *info)
+{
+ return nxp_sar_adc_set_enabled(info, true);
+}
+
+static inline bool nxp_sar_adc_disable(struct nxp_sar_adc *info)
+{
+ return nxp_sar_adc_set_enabled(info, false);
+}
+
+static inline void nxp_sar_adc_calibration_start(void __iomem *base)
+{
+ u32 mcr = readl(NXP_SAR_ADC_MCR(base));
+
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_CALSTART, &mcr, 0x1);
+
+ writel(mcr, NXP_SAR_ADC_MCR(base));
+}
+
+static inline int nxp_sar_adc_calibration_wait(void __iomem *base)
+{
+ u32 msr, ret;
+
+ ret = readl_poll_timeout(NXP_SAR_ADC_MSR(base), msr,
+ !FIELD_GET(NXP_SAR_ADC_MSR_CALBUSY, msr),
+ NXP_SAR_ADC_WAIT_US,
+ NXP_SAR_ADC_CAL_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(NXP_SAR_ADC_MSR_CALFAIL, msr)) {
+ /*
+ * If the calibration fails, the status register bit must be
+ * cleared.
+ */
+ FIELD_MODIFY(NXP_SAR_ADC_MSR_CALFAIL, &msr, 0x0);
+ writel(msr, NXP_SAR_ADC_MSR(base));
+
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int nxp_sar_adc_calibration(struct nxp_sar_adc *info)
+{
+ int ret;
+
+ /* Calibration works only if the ADC is powered up. */
+ nxp_sar_adc_enable(info);
+
+ /* The calibration operation starts. */
+ nxp_sar_adc_calibration_start(info->regs);
+
+ ret = nxp_sar_adc_calibration_wait(info->regs);
+
+ /*
+ * Calibration works only if the ADC is powered up. However
+ * the calibration is called from the probe function where the
+ * iio is not enabled, so we disable after the calibration.
+ */
+ nxp_sar_adc_disable(info);
+
+ return ret;
+}
+
+static void nxp_sar_adc_conversion_timing_set(struct nxp_sar_adc *info, u32 inpsamp)
+{
+ inpsamp = clamp(inpsamp, NXP_SAR_ADC_CTR_INPSAMP_MIN, NXP_SAR_ADC_CTR_INPSAMP_MAX);
+
+ writel(inpsamp, NXP_SAR_ADC_CTR0(info->regs));
+}
+
+static u32 nxp_sar_adc_conversion_timing_get(struct nxp_sar_adc *info)
+{
+ return readl(NXP_SAR_ADC_CTR0(info->regs));
+}
+
+static void nxp_sar_adc_read_notify(struct nxp_sar_adc *info)
+{
+ writel(NXP_SAR_ADC_CH_MASK, NXP_SAR_ADC_CEOCFR0(info->regs));
+ writel(NXP_SAR_ADC_CH_MASK, NXP_SAR_ADC_CEOCFR1(info->regs));
+}
+
+static int nxp_sar_adc_read_data(struct nxp_sar_adc *info, unsigned int chan)
+{
+ u32 ceocfr, cdr;
+
+ ceocfr = readl(NXP_SAR_ADC_CEOCFR0(info->regs));
+
+ /*
+ * FIELD_GET() can not be used here because EOC_CH is not constant.
+ * TODO: Switch to field_get() when it will be available.
+ */
+ if (!(NXP_SAR_ADC_EOC_CH(chan) & ceocfr))
+ return -EIO;
+
+ cdr = readl(NXP_SAR_ADC_CDR(info->regs, chan));
+ if (!(FIELD_GET(NXP_SAR_ADC_CDR_VALID, cdr)))
+ return -EIO;
+
+ return FIELD_GET(NXP_SAR_ADC_CDR_CDATA_MASK, cdr);
+}
+
+static void nxp_sar_adc_isr_buffer(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < info->channels_used; i++) {
+ ret = nxp_sar_adc_read_data(info, info->buffered_chan[i]);
+ if (ret < 0) {
+ nxp_sar_adc_read_notify(info);
+ return;
+ }
+
+ info->buffer[i] = ret;
+ }
+
+ nxp_sar_adc_read_notify(info);
+
+ iio_push_to_buffers_with_ts(indio_dev, info->buffer, sizeof(info->buffer),
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+}
+
+static void nxp_sar_adc_isr_read_raw(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = nxp_sar_adc_read_data(info, info->current_channel);
+ nxp_sar_adc_read_notify(info);
+ if (ret < 0)
+ return;
+
+ info->value = ret;
+ complete(&info->completion);
+}
+
+static irqreturn_t nxp_sar_adc_isr(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int isr;
+
+ isr = readl(NXP_SAR_ADC_ISR(info->regs));
+ if (!(FIELD_GET(NXP_SAR_ADC_ISR_ECH, isr)))
+ return IRQ_NONE;
+
+ if (iio_buffer_enabled(indio_dev))
+ nxp_sar_adc_isr_buffer(indio_dev);
+ else
+ nxp_sar_adc_isr_read_raw(indio_dev);
+
+ writel(NXP_SAR_ADC_ISR_ECH, NXP_SAR_ADC_ISR(info->regs));
+
+ return IRQ_HANDLED;
+}
+
+static void nxp_sar_adc_channels_disable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 ncmr, cimr;
+
+ ncmr = readl(NXP_SAR_ADC_NCMR0(info->regs));
+ cimr = readl(NXP_SAR_ADC_CIMR0(info->regs));
+
+ /* FIELD_MODIFY() can not be used because the mask is not constant */
+ ncmr &= ~mask;
+ cimr &= ~mask;
+
+ writel(ncmr, NXP_SAR_ADC_NCMR0(info->regs));
+ writel(cimr, NXP_SAR_ADC_CIMR0(info->regs));
+}
+
+static void nxp_sar_adc_channels_enable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 ncmr, cimr;
+
+ ncmr = readl(NXP_SAR_ADC_NCMR0(info->regs));
+ cimr = readl(NXP_SAR_ADC_CIMR0(info->regs));
+
+ ncmr |= mask;
+ cimr |= mask;
+
+ writel(ncmr, NXP_SAR_ADC_NCMR0(info->regs));
+ writel(cimr, NXP_SAR_ADC_CIMR0(info->regs));
+}
+
+static void nxp_sar_adc_dma_channels_enable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 dmar;
+
+ dmar = readl(NXP_SAR_ADC_DMAR0(info->regs));
+
+ dmar |= mask;
+
+ writel(dmar, NXP_SAR_ADC_DMAR0(info->regs));
+}
+
+static void nxp_sar_adc_dma_channels_disable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 dmar;
+
+ dmar = readl(NXP_SAR_ADC_DMAR0(info->regs));
+
+ dmar &= ~mask;
+
+ writel(dmar, NXP_SAR_ADC_DMAR0(info->regs));
+}
+
+static void nxp_sar_adc_dma_cfg(struct nxp_sar_adc *info, bool enable)
+{
+ u32 dmae;
+
+ dmae = readl(NXP_SAR_ADC_DMAE(info->regs));
+
+ FIELD_MODIFY(NXP_SAR_ADC_DMAE_DMAEN, &dmae, enable);
+
+ writel(dmae, NXP_SAR_ADC_DMAE(info->regs));
+}
+
+static void nxp_sar_adc_stop_conversion(struct nxp_sar_adc *info)
+{
+ u32 mcr;
+
+ mcr = readl(NXP_SAR_ADC_MCR(info->regs));
+
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_NSTART, &mcr, 0x0);
+
+ writel(mcr, NXP_SAR_ADC_MCR(info->regs));
+
+ /*
+ * On disable, we have to wait for the transaction to finish.
+ * ADC does not abort the transaction if a chain conversion is
+ * in progress. Wait for the worst case scenario - 80 ADC clk
+ * cycles. The clock rate is 80MHz, this routine is called
+ * only when the capture finishes. The delay will be very
+ * short, usec-ish, which is acceptable in the atomic context.
+ */
+ ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk)) * 80);
+}
+
+static int nxp_sar_adc_start_conversion(struct nxp_sar_adc *info, bool raw)
+{
+ u32 mcr;
+
+ mcr = readl(NXP_SAR_ADC_MCR(info->regs));
+
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_NSTART, &mcr, 0x1);
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_MODE, &mcr, raw ? 0 : 1);
+
+ writel(mcr, NXP_SAR_ADC_MCR(info->regs));
+
+ return 0;
+}
+
+static int nxp_sar_adc_read_channel(struct nxp_sar_adc *info, int channel)
+{
+ int ret;
+
+ info->current_channel = channel;
+ nxp_sar_adc_channels_enable(info, BIT(channel));
+ nxp_sar_adc_irq_cfg(info, true);
+ nxp_sar_adc_enable(info);
+
+ reinit_completion(&info->completion);
+ ret = nxp_sar_adc_start_conversion(info, true);
+ if (ret < 0)
+ goto out_disable;
+
+ if (!wait_for_completion_interruptible_timeout(&info->completion,
+ NXP_SAR_ADC_CONV_TIMEOUT))
+ ret = -ETIMEDOUT;
+
+ nxp_sar_adc_stop_conversion(info);
+
+out_disable:
+ nxp_sar_adc_channels_disable(info, BIT(channel));
+ nxp_sar_adc_irq_cfg(info, false);
+ nxp_sar_adc_disable(info);
+
+ return ret;
+}
+
+static int nxp_sar_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ u32 inpsamp;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = nxp_sar_adc_read_channel(info, chan->channel);
+
+ iio_device_release_direct(indio_dev);
+
+ if (ret)
+ return ret;
+
+ *val = info->value;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = info->vref_mV;
+ *val2 = NXP_SAR_ADC_RESOLUTION;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ inpsamp = nxp_sar_adc_conversion_timing_get(info);
+ *val = clk_get_rate(info->clk) / (inpsamp + NXP_SAR_ADC_CONV_TIME);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int nxp_sar_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ u32 inpsamp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * Configures the sample period duration in terms of the SAR
+ * controller clock. The minimum acceptable value is 8.
+ * Configuring it to a value lower than 8 sets the sample period
+ * to 8 cycles. We read the clock value and divide by the
+ * sampling timing which gives us the number of cycles expected.
+ * The value is 8-bit wide, consequently the max value is 0xFF.
+ */
+ inpsamp = clk_get_rate(info->clk) / val - NXP_SAR_ADC_CONV_TIME;
+ nxp_sar_adc_conversion_timing_set(info, inpsamp);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void nxp_sar_adc_dma_cb(void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ struct dma_tx_state state;
+ struct circ_buf *dma_buf;
+ struct device *dev_dma;
+ u32 *dma_samples;
+ s64 timestamp;
+ int idx, ret;
+
+ guard(spinlock_irqsave)(&info->lock);
+
+ dma_buf = &info->dma_buf;
+ dma_samples = (u32 *)dma_buf->buf;
+ dev_dma = info->dma_chan->device->dev;
+
+ /*
+ * DMA in some corner cases might have already be charged for
+ * the next transfer. Potentially there can be a race where
+ * the residue changes while the dma engine updates the
+ * buffer. That could be handled by using the
+ * callback_result() instead of callback() because the residue
+ * will be passed as a parameter to the function. However this
+ * new callback is pretty new and the backend does not update
+ * the residue. So let's stick to the version other drivers do
+ * which has proven running well in production since several
+ * years.
+ */
+ dmaengine_tx_status(info->dma_chan, info->cookie, &state);
+
+ dma_sync_single_for_cpu(dev_dma, info->rx_dma_buf,
+ NXP_SAR_ADC_DMA_BUFF_SZ, DMA_FROM_DEVICE);
+
+ /* Current head position. */
+ dma_buf->head = (NXP_SAR_ADC_DMA_BUFF_SZ - state.residue) /
+ NXP_SAR_ADC_DMA_SAMPLE_SZ;
+
+ /* If everything was transferred, avoid an off by one error. */
+ if (!state.residue)
+ dma_buf->head--;
+
+ /* Something went wrong and nothing transferred. */
+ if (state.residue != NXP_SAR_ADC_DMA_BUFF_SZ) {
+ /* Make sure that head is multiple of info->channels_used. */
+ dma_buf->head -= dma_buf->head % info->channels_used;
+
+ /*
+ * dma_buf->tail != dma_buf->head condition will become false
+ * because dma_buf->tail will be incremented with 1.
+ */
+ while (dma_buf->tail != dma_buf->head) {
+ idx = dma_buf->tail % info->channels_used;
+ info->buffer[idx] = dma_samples[dma_buf->tail];
+ dma_buf->tail = (dma_buf->tail + 1) % NXP_SAR_ADC_DMA_SAMPLE_CNT;
+ if (idx != info->channels_used - 1)
+ continue;
+
+ /*
+ * iio_push_to_buffers_with_ts() should not be
+ * called with dma_samples as parameter. The samples
+ * will be smashed if timestamp is enabled.
+ */
+ timestamp = iio_get_time_ns(indio_dev);
+ ret = iio_push_to_buffers_with_ts(indio_dev, info->buffer,
+ sizeof(info->buffer),
+ timestamp);
+ if (ret < 0 && ret != -EBUSY)
+ dev_err_ratelimited(&indio_dev->dev,
+ "failed to push iio buffer: %d",
+ ret);
+ }
+
+ dma_buf->tail = dma_buf->head;
+ }
+
+ dma_sync_single_for_device(dev_dma, info->rx_dma_buf,
+ NXP_SAR_ADC_DMA_BUFF_SZ, DMA_FROM_DEVICE);
+}
+
+static int nxp_sar_adc_start_cyclic_dma(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ struct dma_slave_config config;
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+
+ info->dma_buf.head = 0;
+ info->dma_buf.tail = 0;
+
+ config.direction = DMA_DEV_TO_MEM;
+ config.src_addr_width = NXP_SAR_ADC_DMA_SAMPLE_SZ;
+ config.src_addr = NXP_SAR_ADC_CDR(info->regs_phys, info->buffered_chan[0]);
+ config.src_port_window_size = info->channels_used;
+ config.src_maxburst = info->channels_used;
+ ret = dmaengine_slave_config(info->dma_chan, &config);
+ if (ret < 0)
+ return ret;
+
+ desc = dmaengine_prep_dma_cyclic(info->dma_chan,
+ info->rx_dma_buf,
+ NXP_SAR_ADC_DMA_BUFF_SZ,
+ NXP_SAR_ADC_DMA_BUFF_SZ / 2,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EINVAL;
+
+ desc->callback = nxp_sar_adc_dma_cb;
+ desc->callback_param = indio_dev;
+ info->cookie = dmaengine_submit(desc);
+ ret = dma_submit_error(info->cookie);
+ if (ret) {
+ dmaengine_terminate_async(info->dma_chan);
+ return ret;
+ }
+
+ dma_async_issue_pending(info->dma_chan);
+
+ return 0;
+}
+
+static void nxp_sar_adc_buffer_software_do_predisable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+
+ /*
+ * The ADC DMAEN bit should be cleared before DMA transaction
+ * is canceled.
+ */
+ nxp_sar_adc_stop_conversion(info);
+ dmaengine_terminate_sync(info->dma_chan);
+ nxp_sar_adc_dma_cfg(info, false);
+ nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask);
+
+ dma_release_channel(info->dma_chan);
+}
+
+static int nxp_sar_adc_buffer_software_do_postenable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ nxp_sar_adc_dma_channels_enable(info, *indio_dev->active_scan_mask);
+
+ nxp_sar_adc_dma_cfg(info, true);
+
+ ret = nxp_sar_adc_start_cyclic_dma(indio_dev);
+ if (ret)
+ goto out_dma_channels_disable;
+
+ ret = nxp_sar_adc_start_conversion(info, false);
+ if (ret)
+ goto out_stop_cyclic_dma;
+
+ return 0;
+
+out_stop_cyclic_dma:
+ dmaengine_terminate_sync(info->dma_chan);
+
+out_dma_channels_disable:
+ nxp_sar_adc_dma_cfg(info, false);
+ nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask);
+
+ return ret;
+}
+
+static void nxp_sar_adc_buffer_trigger_do_predisable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+
+ nxp_sar_adc_irq_cfg(info, false);
+}
+
+static int nxp_sar_adc_buffer_trigger_do_postenable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+
+ nxp_sar_adc_irq_cfg(info, true);
+
+ return 0;
+}
+
+static int nxp_sar_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int current_mode = iio_device_get_current_mode(indio_dev);
+ unsigned long channel;
+ int ret;
+
+ info->dma_chan = dma_request_chan(indio_dev->dev.parent, "rx");
+ if (IS_ERR(info->dma_chan))
+ return PTR_ERR(info->dma_chan);
+
+ info->channels_used = 0;
+
+ /*
+ * The SAR-ADC has two groups of channels.
+ *
+ * - Group #0:
+ * * bit 0-7 : channel 0 -> channel 7
+ * * bit 8-31 : reserved
+ *
+ * - Group #32:
+ * * bit 0-7 : Internal
+ * * bit 8-31 : reserved
+ *
+ * The 8 channels from group #0 are used in this driver for
+ * ADC as described when declaring the IIO device and the
+ * mapping is the same. That means the active_scan_mask can be
+ * used directly to write the channel interrupt mask.
+ */
+ nxp_sar_adc_channels_enable(info, *indio_dev->active_scan_mask);
+
+ for_each_set_bit(channel, indio_dev->active_scan_mask, NXP_SAR_ADC_NR_CHANNELS)
+ info->buffered_chan[info->channels_used++] = channel;
+
+ nxp_sar_adc_enable(info);
+
+ if (current_mode == INDIO_BUFFER_SOFTWARE)
+ ret = nxp_sar_adc_buffer_software_do_postenable(indio_dev);
+ else
+ ret = nxp_sar_adc_buffer_trigger_do_postenable(indio_dev);
+ if (ret)
+ goto out_postenable;
+
+ return 0;
+
+out_postenable:
+ nxp_sar_adc_disable(info);
+ nxp_sar_adc_channels_disable(info, *indio_dev->active_scan_mask);
+
+ return ret;
+}
+
+static int nxp_sar_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int currentmode = iio_device_get_current_mode(indio_dev);
+
+ if (currentmode == INDIO_BUFFER_SOFTWARE)
+ nxp_sar_adc_buffer_software_do_predisable(indio_dev);
+ else
+ nxp_sar_adc_buffer_trigger_do_predisable(indio_dev);
+
+ nxp_sar_adc_disable(info);
+
+ nxp_sar_adc_channels_disable(info, *indio_dev->active_scan_mask);
+
+ return 0;
+}
+
+static irqreturn_t nxp_sar_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = nxp_sar_adc_start_conversion(info, true);
+ if (ret < 0)
+ dev_dbg(&indio_dev->dev, "Failed to start conversion\n");
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = nxp_sar_adc_buffer_postenable,
+ .predisable = nxp_sar_adc_buffer_predisable,
+};
+
+static const struct iio_info nxp_sar_adc_iio_info = {
+ .read_raw = nxp_sar_adc_read_raw,
+ .write_raw = nxp_sar_adc_write_raw,
+};
+
+static int nxp_sar_adc_dma_probe(struct device *dev, struct nxp_sar_adc *info)
+{
+ u8 *rx_buf;
+
+ rx_buf = dmam_alloc_coherent(dev, NXP_SAR_ADC_DMA_BUFF_SZ,
+ &info->rx_dma_buf, GFP_KERNEL);
+ if (!rx_buf)
+ return -ENOMEM;
+
+ info->dma_buf.buf = rx_buf;
+
+ return 0;
+}
+
+/*
+ * The documentation describes the reset values for the registers.
+ * However some registers do not have these values after a reset. It
+ * is not a desirable situation. In some other SoC family
+ * documentation NXP recommends not assuming the default values are
+ * set and to initialize the registers conforming to the documentation
+ * reset information to prevent this situation. Assume the same rule
+ * applies here as there is a discrepancy between what is read from
+ * the registers at reset time and the documentation.
+ */
+static void nxp_sar_adc_set_default_values(struct nxp_sar_adc *info)
+{
+ writel(0x00003901, NXP_SAR_ADC_MCR(info->regs));
+ writel(0x00000001, NXP_SAR_ADC_MSR(info->regs));
+ writel(0x00000014, NXP_SAR_ADC_CTR0(info->regs));
+ writel(0x00000014, NXP_SAR_ADC_CTR1(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_CIMR0(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_CIMR1(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_NCMR0(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_NCMR1(info->regs));
+}
+
+static int nxp_sar_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct nxp_sar_adc_data *data = device_get_match_data(dev);
+ struct nxp_sar_adc *info;
+ struct iio_dev *indio_dev;
+ struct resource *mem;
+ int irq, ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ info->vref_mV = data->vref_mV;
+ spin_lock_init(&info->lock);
+ info->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(info->regs))
+ return dev_err_probe(dev, PTR_ERR(info->regs),
+ "Failed to get and remap resource");
+
+ info->regs_phys = mem->start;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, nxp_sar_adc_isr, 0, dev_name(dev),
+ indio_dev);
+ if (ret < 0)
+ return ret;
+
+ info->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk),
+ "Failed to get the clock\n");
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = data->model;
+ indio_dev->info = &nxp_sar_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->channels = nxp_sar_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(nxp_sar_adc_iio_channels);
+
+ nxp_sar_adc_set_default_values(info);
+
+ ret = nxp_sar_adc_calibration(info);
+ if (ret)
+ dev_err_probe(dev, ret, "Calibration failed\n");
+
+ ret = nxp_sar_adc_dma_probe(dev, info);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize the DMA\n");
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &nxp_sar_adc_trigger_handler,
+ &iio_triggered_buffer_setup_ops);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Couldn't initialise the buffer\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Couldn't register the device\n");
+
+ return 0;
+}
+
+static int nxp_sar_adc_suspend(struct device *dev)
+{
+ struct nxp_sar_adc *info = iio_priv(dev_get_drvdata(dev));
+
+ info->pwdn = nxp_sar_adc_disable(info);
+ info->inpsamp = nxp_sar_adc_conversion_timing_get(info);
+
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+static int nxp_sar_adc_resume(struct device *dev)
+{
+ struct nxp_sar_adc *info = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
+
+ nxp_sar_adc_conversion_timing_set(info, info->inpsamp);
+
+ if (!info->pwdn)
+ nxp_sar_adc_enable(info);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(nxp_sar_adc_pm_ops, nxp_sar_adc_suspend,
+ nxp_sar_adc_resume);
+
+static const struct nxp_sar_adc_data s32g2_sar_adc_data = {
+ .vref_mV = 1800,
+ .model = "s32g2-sar-adc",
+};
+
+static const struct of_device_id nxp_sar_adc_match[] = {
+ { .compatible = "nxp,s32g2-sar-adc", .data = &s32g2_sar_adc_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nxp_sar_adc_match);
+
+static struct platform_driver nxp_sar_adc_driver = {
+ .probe = nxp_sar_adc_probe,
+ .driver = {
+ .name = "nxp-sar-adc",
+ .of_match_table = nxp_sar_adc_match,
+ .pm = pm_sleep_ptr(&nxp_sar_adc_pm_ops),
+ },
+};
+module_platform_driver(nxp_sar_adc_driver);
+
+MODULE_AUTHOR("NXP");
+MODULE_DESCRIPTION("NXP SAR-ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c
index b245416bae12..8e75665204d1 100644
--- a/drivers/iio/adc/qcom-spmi-rradc.c
+++ b/drivers/iio/adc/qcom-spmi-rradc.c
@@ -934,20 +934,15 @@ static int rradc_probe(struct platform_device *pdev)
chip = iio_priv(indio_dev);
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!chip->regmap) {
- dev_err(dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
+ if (!chip->regmap)
+ return dev_err_probe(dev, -EINVAL, "Couldn't get parent's regmap\n");
chip->dev = dev;
mutex_init(&chip->conversion_lock);
ret = device_property_read_u32(dev, "reg", &chip->base);
- if (ret < 0) {
- dev_err(chip->dev, "Couldn't find reg address, ret = %d\n",
- ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Couldn't find reg address\n");
batt_id_delay = -1;
ret = device_property_read_u32(dev, "qcom,batt-id-delay-ms",
@@ -975,10 +970,9 @@ static int rradc_probe(struct platform_device *pdev)
/* Get the PMIC revision, we need it to handle some varying coefficients */
chip->pmic = qcom_pmic_get(chip->dev);
- if (IS_ERR(chip->pmic)) {
- dev_err(chip->dev, "Unable to get reference to PMIC device\n");
- return PTR_ERR(chip->pmic);
- }
+ if (IS_ERR(chip->pmic))
+ return dev_err_probe(dev, PTR_ERR(chip->pmic),
+ "Unable to get reference to PMIC device\n");
switch (chip->pmic->subtype) {
case PMI8998_SUBTYPE:
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 6721da0ed7bb..0f0bf2906af0 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -456,6 +456,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
{
const struct rockchip_saradc_data *match_data;
struct rockchip_saradc *info = NULL;
+ struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
int ret;
@@ -464,23 +465,21 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (!np)
return -ENODEV;
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
- match_data = of_device_get_match_data(&pdev->dev);
+ match_data = of_device_get_match_data(dev);
if (!match_data)
- return dev_err_probe(&pdev->dev, -ENODEV,
- "failed to match device\n");
+ return dev_err_probe(dev, -ENODEV, "failed to match device\n");
info->data = match_data;
/* Sanity check for possible later IP variants with more channels */
if (info->data->num_channels > SARADC_MAX_CHANNELS)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "max channels exceeded");
+ return dev_err_probe(dev, -EINVAL, "max channels exceeded");
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
@@ -490,12 +489,10 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
* The reset should be an optional property, as it should work
* with old devicetrees as well
*/
- info->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
- "saradc-apb");
- if (IS_ERR(info->reset)) {
- ret = PTR_ERR(info->reset);
- return dev_err_probe(&pdev->dev, ret, "failed to get saradc-apb\n");
- }
+ info->reset = devm_reset_control_get_optional_exclusive(dev, "saradc-apb");
+ if (IS_ERR(info->reset))
+ return dev_err_probe(dev, PTR_ERR(info->reset),
+ "failed to get saradc-apb\n");
init_completion(&info->completion);
@@ -503,16 +500,14 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
+ ret = devm_request_irq(dev, irq, rockchip_saradc_isr,
0, dev_name(&pdev->dev), info);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq);
- info->vref = devm_regulator_get(&pdev->dev, "vref");
+ info->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(info->vref))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->vref),
+ return dev_err_probe(dev, PTR_ERR(info->vref),
"failed to get regulator\n");
if (info->reset)
@@ -520,11 +515,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
ret = regulator_enable(info->vref);
if (ret < 0)
- return dev_err_probe(&pdev->dev, ret,
- "failed to enable vref regulator\n");
+ return dev_err_probe(dev, ret, "failed to enable vref regulator\n");
- ret = devm_add_action_or_reset(&pdev->dev,
- rockchip_saradc_regulator_disable, info);
+ ret = devm_add_action_or_reset(dev, rockchip_saradc_regulator_disable, info);
if (ret)
return ret;
@@ -534,14 +527,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
info->uv_vref = ret;
- info->pclk = devm_clk_get_enabled(&pdev->dev, "apb_pclk");
+ info->pclk = devm_clk_get_enabled(dev, "apb_pclk");
if (IS_ERR(info->pclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->pclk),
- "failed to get pclk\n");
+ return dev_err_probe(dev, PTR_ERR(info->pclk), "failed to get pclk\n");
- info->clk = devm_clk_get_enabled(&pdev->dev, "saradc");
+ info->clk = devm_clk_get_enabled(dev, "saradc");
if (IS_ERR(info->clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->clk),
+ return dev_err_probe(dev, PTR_ERR(info->clk),
"failed to get adc clock\n");
/*
* Use a default value for the converter clock.
@@ -549,18 +541,17 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
*/
ret = clk_set_rate(info->clk, info->data->clk_rate);
if (ret < 0)
- return dev_err_probe(&pdev->dev, ret,
- "failed to set adc clk rate\n");
+ return dev_err_probe(dev, ret, "failed to set adc clk rate\n");
platform_set_drvdata(pdev, indio_dev);
- indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->name = dev_name(dev);
indio_dev->info = &rockchip_saradc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = info->data->channels;
indio_dev->num_channels = info->data->num_channels;
- ret = devm_iio_triggered_buffer_setup(&indio_dev->dev, indio_dev, NULL,
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
rockchip_saradc_trigger_handler,
NULL);
if (ret)
@@ -571,7 +562,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = devm_add_action_or_reset(&pdev->dev,
+ ret = devm_add_action_or_reset(dev,
rockchip_saradc_regulator_unreg_notifier,
info);
if (ret)
@@ -579,7 +570,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
mutex_init(&info->lock);
- return devm_iio_device_register(&pdev->dev, indio_dev);
+ return devm_iio_device_register(dev, indio_dev);
}
static int rockchip_saradc_suspend(struct device *dev)
diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c
index 2535c2c3e60b..6209499c5c37 100644
--- a/drivers/iio/adc/sc27xx_adc.c
+++ b/drivers/iio/adc/sc27xx_adc.c
@@ -867,10 +867,8 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
int ret;
pdata = of_device_get_match_data(dev);
- if (!pdata) {
- dev_err(dev, "No matching driver data found\n");
- return -EINVAL;
- }
+ if (!pdata)
+ return dev_err_probe(dev, -EINVAL, "No matching driver data found\n");
indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
if (!indio_dev)
@@ -879,56 +877,43 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
sc27xx_data = iio_priv(indio_dev);
sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
- if (!sc27xx_data->regmap) {
- dev_err(dev, "failed to get ADC regmap\n");
- return -ENODEV;
- }
+ if (!sc27xx_data->regmap)
+ return dev_err_probe(dev, -ENODEV, "failed to get ADC regmap\n");
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
- if (ret) {
- dev_err(dev, "failed to get ADC base address\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get ADC base address\n");
sc27xx_data->irq = platform_get_irq(pdev, 0);
if (sc27xx_data->irq < 0)
return sc27xx_data->irq;
ret = of_hwspin_lock_get_id(np, 0);
- if (ret < 0) {
- dev_err(dev, "failed to get hwspinlock id\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get hwspinlock id\n");
sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret);
- if (!sc27xx_data->hwlock) {
- dev_err(dev, "failed to request hwspinlock\n");
- return -ENXIO;
- }
+ if (!sc27xx_data->hwlock)
+ return dev_err_probe(dev, -ENXIO, "failed to request hwspinlock\n");
sc27xx_data->dev = dev;
if (pdata->set_volref) {
sc27xx_data->volref = devm_regulator_get(dev, "vref");
- if (IS_ERR(sc27xx_data->volref)) {
- ret = PTR_ERR(sc27xx_data->volref);
- return dev_err_probe(dev, ret, "failed to get ADC volref\n");
- }
+ if (IS_ERR(sc27xx_data->volref))
+ return dev_err_probe(dev, PTR_ERR(sc27xx_data->volref),
+ "failed to get ADC volref\n");
}
sc27xx_data->var_data = pdata;
sc27xx_data->var_data->init_scale(sc27xx_data);
ret = sc27xx_adc_enable(sc27xx_data);
- if (ret) {
- dev_err(dev, "failed to enable ADC module\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable ADC module\n");
ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
- if (ret) {
- dev_err(dev, "failed to add ADC disable action\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add ADC disable action\n");
indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;
diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c
new file mode 100644
index 000000000000..6246b3cab71f
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1018.c
@@ -0,0 +1,739 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments ADS1018 ADC driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/dev_printk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/math.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define ADS1018_CFG_OS_TRIG BIT(15)
+#define ADS1018_CFG_TS_MODE_EN BIT(4)
+#define ADS1018_CFG_PULL_UP BIT(3)
+#define ADS1018_CFG_NOP BIT(1)
+#define ADS1018_CFG_VALID (ADS1018_CFG_PULL_UP | ADS1018_CFG_NOP)
+
+#define ADS1018_CFG_MUX_MASK GENMASK(14, 12)
+
+#define ADS1018_CFG_PGA_MASK GENMASK(11, 9)
+#define ADS1018_PGA_DEFAULT 2
+
+#define ADS1018_CFG_MODE_MASK BIT(8)
+#define ADS1018_MODE_CONTINUOUS 0
+#define ADS1018_MODE_ONESHOT 1
+
+#define ADS1018_CFG_DRATE_MASK GENMASK(7, 5)
+#define ADS1018_DRATE_DEFAULT 4
+
+#define ADS1018_NUM_PGA_MODES 6
+#define ADS1018_CHANNELS_MAX 10
+
+struct ads1018_chan_data {
+ u8 pga_mode;
+ u8 data_rate_mode;
+};
+
+struct ads1018_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels;
+ unsigned long num_channels;
+
+ /* IIO_VAL_INT */
+ const u32 *data_rate_mode_to_hz;
+ unsigned long num_data_rate_mode_to_hz;
+
+ /*
+ * Let `res` be the chip's resolution and `fsr` (millivolts) be the
+ * full-scale range corresponding to the PGA mode given by the array
+ * index. Then, the gain is calculated using the following formula:
+ *
+ * gain = |fsr| / 2^(res - 1)
+ *
+ * This value then has to be represented in IIO_VAL_INT_PLUS_NANO
+ * format. For example if:
+ *
+ * gain = 6144 / 2^(16 - 1) = 0.1875
+ *
+ * ...then the formatted value is:
+ *
+ * { 0, 187500000 }
+ */
+ const u32 pga_mode_to_gain[ADS1018_NUM_PGA_MODES][2];
+
+ /* IIO_VAL_INT_PLUS_MICRO */
+ const u32 temp_scale[2];
+};
+
+struct ads1018 {
+ struct spi_device *spi;
+ struct iio_trigger *indio_trig;
+
+ struct gpio_desc *drdy_gpiod;
+ int drdy_irq;
+
+ struct ads1018_chan_data chan_data[ADS1018_CHANNELS_MAX];
+ const struct ads1018_chip_info *chip_info;
+
+ struct spi_message msg_read;
+ struct spi_transfer xfer;
+ __be16 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
+ __be16 rx_buf[2];
+};
+
+#define ADS1018_VOLT_DIFF_CHAN(_index, _chan, _chan2, _realbits) { \
+ .type = IIO_VOLTAGE, \
+ .channel = _chan, \
+ .channel2 = _chan2, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = _realbits, \
+ .storagebits = 16, \
+ .shift = 16 - _realbits, \
+ .endianness = IIO_BE, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = true, \
+ .differential = true, \
+}
+
+#define ADS1018_VOLT_CHAN(_index, _chan, _realbits) { \
+ .type = IIO_VOLTAGE, \
+ .channel = _chan, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = _realbits, \
+ .storagebits = 16, \
+ .shift = 16 - _realbits, \
+ .endianness = IIO_BE, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = true, \
+}
+
+#define ADS1018_TEMP_CHAN(_index, _realbits) { \
+ .type = IIO_TEMP, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = _realbits, \
+ .storagebits = 16, \
+ .shift = 16 - _realbits, \
+ .endianness = IIO_BE, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec ads1118_iio_channels[] = {
+ ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 16),
+ ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 16),
+ ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 16),
+ ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 16),
+ ADS1018_VOLT_CHAN(4, 0, 16),
+ ADS1018_VOLT_CHAN(5, 1, 16),
+ ADS1018_VOLT_CHAN(6, 2, 16),
+ ADS1018_VOLT_CHAN(7, 3, 16),
+ ADS1018_TEMP_CHAN(8, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(9),
+};
+
+static const struct iio_chan_spec ads1018_iio_channels[] = {
+ ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 12),
+ ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 12),
+ ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 12),
+ ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 12),
+ ADS1018_VOLT_CHAN(4, 0, 12),
+ ADS1018_VOLT_CHAN(5, 1, 12),
+ ADS1018_VOLT_CHAN(6, 2, 12),
+ ADS1018_VOLT_CHAN(7, 3, 12),
+ ADS1018_TEMP_CHAN(8, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(9),
+};
+
+/**
+ * ads1018_calc_delay - Calculates a suitable delay for a single-shot reading
+ * @hz: Sampling frequency
+ *
+ * Calculates an appropriate delay for a single shot reading given a sampling
+ * frequency.
+ *
+ * Return: Delay in microseconds (Always greater than 0).
+ */
+static u32 ads1018_calc_delay(unsigned int hz)
+{
+ /*
+ * Calculate the worst-case sampling rate by subtracting 10% error
+ * specified in the datasheet...
+ */
+ hz -= DIV_ROUND_UP(hz, 10);
+
+ /* ...Then calculate time per sample in microseconds. */
+ return DIV_ROUND_UP(HZ_PER_MHZ, hz);
+}
+
+/**
+ * ads1018_spi_read_exclusive - Reads a conversion value from the device
+ * @ads1018: Device data
+ * @cnv: ADC Conversion value (optional)
+ * @hold_cs: Keep CS line asserted after the SPI transfer
+ *
+ * Reads the most recent ADC conversion value, without updating the
+ * device's configuration.
+ *
+ * Context: Expects SPI bus *exclusive* use.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+static int ads1018_spi_read_exclusive(struct ads1018 *ads1018, __be16 *cnv,
+ bool hold_cs)
+{
+ int ret;
+
+ ads1018->xfer.cs_change = hold_cs;
+
+ ret = spi_sync_locked(ads1018->spi, &ads1018->msg_read);
+ if (ret)
+ return ret;
+
+ if (cnv)
+ *cnv = ads1018->rx_buf[0];
+
+ return 0;
+}
+
+/**
+ * ads1018_single_shot - Performs a one-shot reading sequence
+ * @ads1018: Device data
+ * @chan: Channel specification
+ * @cnv: Conversion value
+ *
+ * Writes a new configuration, waits an appropriate delay, then reads the most
+ * recent conversion.
+ *
+ * Context: Expects iio_device_claim_direct() is held.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+static int ads1018_single_shot(struct ads1018 *ads1018,
+ struct iio_chan_spec const *chan, u16 *cnv)
+{
+ u8 max_drate_mode = ads1018->chip_info->num_data_rate_mode_to_hz - 1;
+ u8 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode];
+ u8 pga_mode = ads1018->chan_data[chan->scan_index].pga_mode;
+ struct spi_transfer xfer[2] = {
+ {
+ .tx_buf = ads1018->tx_buf,
+ .len = sizeof(ads1018->tx_buf[0]),
+ .delay = {
+ .value = ads1018_calc_delay(drate),
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
+ .cs_change = 1, /* 16-bit mode requires CS de-assert */
+ },
+ {
+ .rx_buf = ads1018->rx_buf,
+ .len = sizeof(ads1018->rx_buf[0]),
+ },
+ };
+ u16 cfg;
+ int ret;
+
+ cfg = ADS1018_CFG_VALID | ADS1018_CFG_OS_TRIG;
+ cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, chan->scan_index);
+ cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga_mode);
+ cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT);
+ cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, max_drate_mode);
+
+ if (chan->type == IIO_TEMP)
+ cfg |= ADS1018_CFG_TS_MODE_EN;
+
+ ads1018->tx_buf[0] = cpu_to_be16(cfg);
+ ret = spi_sync_transfer(ads1018->spi, xfer, ARRAY_SIZE(xfer));
+ if (ret)
+ return ret;
+
+ *cnv = be16_to_cpu(ads1018->rx_buf[0]);
+
+ return 0;
+}
+
+static int
+ads1018_read_raw_direct_mode(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ const struct ads1018_chip_info *chip_info = ads1018->chip_info;
+ u8 addr = chan->scan_index;
+ u8 pga_mode, drate_mode;
+ u16 cnv;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads1018_single_shot(ads1018, chan, &cnv);
+ if (ret)
+ return ret;
+
+ cnv >>= chan->scan_type.shift;
+ *val = sign_extend32(cnv, chan->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ pga_mode = ads1018->chan_data[addr].pga_mode;
+ *val = chip_info->pga_mode_to_gain[pga_mode][0];
+ *val2 = chip_info->pga_mode_to_gain[pga_mode][1];
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_TEMP:
+ *val = chip_info->temp_scale[0];
+ *val2 = chip_info->temp_scale[1];
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ drate_mode = ads1018->chan_data[addr].data_rate_mode;
+ *val = chip_info->data_rate_mode_to_hz[drate_mode];
+ return IIO_VAL_INT;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+ads1018_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ads1018_read_raw_direct_mode(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int
+ads1018_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length, long mask)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *vals = (const int *)ads1018->chip_info->pga_mode_to_gain;
+ *length = ADS1018_NUM_PGA_MODES * 2;
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT;
+ *vals = ads1018->chip_info->data_rate_mode_to_hz;
+ *length = ads1018->chip_info->num_data_rate_mode_to_hz;
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+ads1018_write_raw_direct_mode(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ const struct ads1018_chip_info *info = ads1018->chip_info;
+ unsigned int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ for (i = 0; i < ADS1018_NUM_PGA_MODES; i++) {
+ if (val == info->pga_mode_to_gain[i][0] &&
+ val2 == info->pga_mode_to_gain[i][1])
+ break;
+ }
+ if (i == ADS1018_NUM_PGA_MODES)
+ return -EINVAL;
+
+ ads1018->chan_data[chan->scan_index].pga_mode = i;
+ return 0;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < info->num_data_rate_mode_to_hz; i++) {
+ if (val == info->data_rate_mode_to_hz[i])
+ break;
+ }
+ if (i == info->num_data_rate_mode_to_hz)
+ return -EINVAL;
+
+ ads1018->chan_data[chan->scan_index].data_rate_mode = i;
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+ads1018_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ads1018_write_raw_direct_mode(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int
+ads1018_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;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static const struct iio_info ads1018_iio_info = {
+ .read_raw = ads1018_read_raw,
+ .read_avail = ads1018_read_avail,
+ .write_raw = ads1018_write_raw,
+ .write_raw_get_fmt = ads1018_write_raw_get_fmt,
+};
+
+static void ads1018_set_trigger_enable(struct ads1018 *ads1018)
+{
+ spi_bus_lock(ads1018->spi->controller);
+ ads1018_spi_read_exclusive(ads1018, NULL, true);
+ enable_irq(ads1018->drdy_irq);
+}
+
+static void ads1018_set_trigger_disable(struct ads1018 *ads1018)
+{
+ disable_irq(ads1018->drdy_irq);
+ ads1018_spi_read_exclusive(ads1018, NULL, false);
+ spi_bus_unlock(ads1018->spi->controller);
+}
+
+static int ads1018_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct ads1018 *ads1018 = iio_trigger_get_drvdata(trig);
+
+ /*
+ * We need to lock the SPI bus and tie CS low (hold_cs) to catch
+ * data-ready interrupts, otherwise the MISO line enters a Hi-Z state.
+ */
+
+ if (state)
+ ads1018_set_trigger_enable(ads1018);
+ else
+ ads1018_set_trigger_disable(ads1018);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops ads1018_trigger_ops = {
+ .set_trigger_state = ads1018_set_trigger_state,
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int ads1018_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ const struct ads1018_chip_info *chip_info = ads1018->chip_info;
+ unsigned int pga, drate, addr;
+ u16 cfg;
+
+ addr = find_first_bit(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ pga = ads1018->chan_data[addr].pga_mode;
+ drate = ads1018->chan_data[addr].data_rate_mode;
+
+ cfg = ADS1018_CFG_VALID;
+ cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, addr);
+ cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga);
+ cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_CONTINUOUS);
+ cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, drate);
+
+ if (chip_info->channels[addr].type == IIO_TEMP)
+ cfg |= ADS1018_CFG_TS_MODE_EN;
+
+ ads1018->tx_buf[0] = cpu_to_be16(cfg);
+ ads1018->tx_buf[1] = 0;
+
+ return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf));
+}
+
+static int ads1018_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ u16 cfg;
+
+ cfg = ADS1018_CFG_VALID;
+ cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT);
+
+ ads1018->tx_buf[0] = cpu_to_be16(cfg);
+ ads1018->tx_buf[1] = 0;
+
+ return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf));
+}
+
+static const struct iio_buffer_setup_ops ads1018_buffer_ops = {
+ .preenable = ads1018_buffer_preenable,
+ .postdisable = ads1018_buffer_postdisable,
+ .validate_scan_mask = iio_validate_scan_mask_onehot,
+};
+
+static irqreturn_t ads1018_irq_handler(int irq, void *dev_id)
+{
+ struct ads1018 *ads1018 = dev_id;
+
+ /*
+ * We need to check if the "drdy" pin is actually active or if it's a
+ * pending interrupt triggered by the SPI transfer.
+ */
+ if (!gpiod_get_value(ads1018->drdy_gpiod))
+ return IRQ_HANDLED;
+
+ iio_trigger_poll(ads1018->indio_trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ads1018_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ struct {
+ __be16 conv;
+ aligned_s64 ts;
+ } scan = {};
+ int ret;
+
+ if (iio_trigger_using_own(indio_dev)) {
+ disable_irq(ads1018->drdy_irq);
+ ret = ads1018_spi_read_exclusive(ads1018, &scan.conv, true);
+ enable_irq(ads1018->drdy_irq);
+ } else {
+ ret = spi_read(ads1018->spi, ads1018->rx_buf, sizeof(ads1018->rx_buf));
+ scan.conv = ads1018->rx_buf[0];
+ }
+
+ if (!ret)
+ iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan),
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1018_trigger_setup(struct iio_dev *indio_dev)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ struct spi_device *spi = ads1018->spi;
+ struct device *dev = &spi->dev;
+ const char *con_id = "drdy";
+ int ret;
+
+ ads1018->drdy_gpiod = devm_gpiod_get_optional(dev, con_id, GPIOD_IN);
+ if (IS_ERR(ads1018->drdy_gpiod))
+ return dev_err_probe(dev, PTR_ERR(ads1018->drdy_gpiod),
+ "Failed to get %s GPIO.\n", con_id);
+
+ /* First try to get IRQ from SPI core, then from GPIO */
+ if (spi->irq > 0)
+ ads1018->drdy_irq = spi->irq;
+ else if (ads1018->drdy_gpiod)
+ ads1018->drdy_irq = gpiod_to_irq(ads1018->drdy_gpiod);
+ if (ads1018->drdy_irq < 0)
+ return dev_err_probe(dev, ads1018->drdy_irq,
+ "Failed to get IRQ from %s GPIO.\n", con_id);
+
+ /* An IRQ line is only an optional requirement for the IIO trigger */
+ if (ads1018->drdy_irq == 0)
+ return 0;
+
+ ads1018->indio_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-%s",
+ indio_dev->name,
+ iio_device_id(indio_dev),
+ con_id);
+ if (!ads1018->indio_trig)
+ return -ENOMEM;
+
+ iio_trigger_set_drvdata(ads1018->indio_trig, ads1018);
+ ads1018->indio_trig->ops = &ads1018_trigger_ops;
+
+ ret = devm_iio_trigger_register(dev, ads1018->indio_trig);
+ if (ret)
+ return ret;
+
+ /*
+ * The "data-ready" IRQ line is shared with the MOSI pin, thus we need
+ * to keep it disabled until we actually request data.
+ */
+ return devm_request_irq(dev, ads1018->drdy_irq, ads1018_irq_handler,
+ IRQF_NO_AUTOEN, ads1018->chip_info->name, ads1018);
+}
+
+static int ads1018_spi_probe(struct spi_device *spi)
+{
+ const struct ads1018_chip_info *info = spi_get_device_match_data(spi);
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ads1018 *ads1018;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ads1018));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ads1018 = iio_priv(indio_dev);
+ ads1018->spi = spi;
+ ads1018->chip_info = info;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = info->name;
+ indio_dev->info = &ads1018_iio_info;
+ indio_dev->channels = info->channels;
+ indio_dev->num_channels = info->num_channels;
+
+ for (unsigned int i = 0; i < ADS1018_CHANNELS_MAX; i++) {
+ ads1018->chan_data[i].data_rate_mode = ADS1018_DRATE_DEFAULT;
+ ads1018->chan_data[i].pga_mode = ADS1018_PGA_DEFAULT;
+ }
+
+ ads1018->xfer.rx_buf = ads1018->rx_buf;
+ ads1018->xfer.len = sizeof(ads1018->rx_buf);
+ spi_message_init_with_transfers(&ads1018->msg_read, &ads1018->xfer, 1);
+
+ ret = ads1018_trigger_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads1018_trigger_handler,
+ &ads1018_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const unsigned int ads1018_data_rate_table[] = {
+ 128, 250, 490, 920, 1600, 2400, 3300,
+};
+
+static const unsigned int ads1118_data_rate_table[] = {
+ 8, 16, 32, 64, 128, 250, 475, 860,
+};
+
+static const struct ads1018_chip_info ads1018_chip_info = {
+ .name = "ads1018",
+ .channels = ads1018_iio_channels,
+ .num_channels = ARRAY_SIZE(ads1018_iio_channels),
+ .data_rate_mode_to_hz = ads1018_data_rate_table,
+ .num_data_rate_mode_to_hz = ARRAY_SIZE(ads1018_data_rate_table),
+ .pga_mode_to_gain = {
+ { 3, 0 }, /* fsr = 6144 mV */
+ { 2, 0 }, /* fsr = 4096 mV */
+ { 1, 0 }, /* fsr = 2048 mV */
+ { 0, 500000000 }, /* fsr = 1024 mV */
+ { 0, 250000000 }, /* fsr = 512 mV */
+ { 0, 125000000 }, /* fsr = 256 mV */
+ },
+ .temp_scale = { 125, 0 },
+};
+
+static const struct ads1018_chip_info ads1118_chip_info = {
+ .name = "ads1118",
+ .channels = ads1118_iio_channels,
+ .num_channels = ARRAY_SIZE(ads1118_iio_channels),
+ .data_rate_mode_to_hz = ads1118_data_rate_table,
+ .num_data_rate_mode_to_hz = ARRAY_SIZE(ads1118_data_rate_table),
+ .pga_mode_to_gain = {
+ { 0, 187500000 }, /* fsr = 6144 mV */
+ { 0, 125000000 }, /* fsr = 4096 mV */
+ { 0, 62500000 }, /* fsr = 2048 mV */
+ { 0, 31250000 }, /* fsr = 1024 mV */
+ { 0, 15625000 }, /* fsr = 512 mV */
+ { 0, 7812500 }, /* fsr = 256 mV */
+ },
+ .temp_scale = { 31, 250000 },
+};
+
+static const struct of_device_id ads1018_of_match[] = {
+ { .compatible = "ti,ads1018", .data = &ads1018_chip_info },
+ { .compatible = "ti,ads1118", .data = &ads1118_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads1018_of_match);
+
+static const struct spi_device_id ads1018_spi_match[] = {
+ { "ads1018", (kernel_ulong_t)&ads1018_chip_info },
+ { "ads1118", (kernel_ulong_t)&ads1118_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads1018_spi_match);
+
+static struct spi_driver ads1018_spi_driver = {
+ .driver = {
+ .name = "ads1018",
+ .of_match_table = ads1018_of_match,
+ },
+ .probe = ads1018_spi_probe,
+ .id_table = ads1018_spi_match,
+};
+module_spi_driver(ads1018_spi_driver);
+
+MODULE_DESCRIPTION("Texas Instruments ADS1018 ADC Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index c9a20024d6b1..a585621b0bc3 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -827,7 +827,7 @@ static int ads131e08_probe(struct spi_device *spi)
if (spi->irq) {
ret = devm_request_irq(&spi->dev, spi->irq,
ads131e08_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
spi->dev.driver->name, indio_dev);
if (ret)
return dev_err_probe(&spi->dev, ret,
diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c
new file mode 100644
index 000000000000..07d63bf62c5f
--- /dev/null
+++ b/drivers/iio/adc/ti-ads131m02.c
@@ -0,0 +1,968 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Texas Instruments ADS131M02 family ADC chips.
+ *
+ * Copyright (C) 2024 Protonic Holland
+ * Copyright (C) 2025 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix
+ *
+ * Primary Datasheet Reference (used for citations):
+ * ADS131M08 8-Channel, Simultaneously-Sampling, 24-Bit, Delta-Sigma ADC
+ * Document SBAS950B, Revised February 2021
+ * https://www.ti.com/lit/ds/symlink/ads131m08.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/crc-itu-t.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/lockdep.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+/* Max channels supported by the largest variant in the family (ADS131M08) */
+#define ADS131M_MAX_CHANNELS 8
+
+/* Section 6.7, t_REGACQ (min time after reset) is 5us */
+#define ADS131M_RESET_DELAY_US 5
+
+#define ADS131M_WORD_SIZE_BYTES 3
+#define ADS131M_RESPONSE_WORDS 1
+#define ADS131M_CRC_WORDS 1
+
+/*
+ * SPI Frame word count calculation.
+ * Frame = N channel words + 1 response word + 1 CRC word.
+ * Word size depends on WLENGTH bits in MODE register (Default 24-bit).
+ */
+#define ADS131M_FRAME_WORDS(nch) \
+ ((nch) + ADS131M_RESPONSE_WORDS + ADS131M_CRC_WORDS)
+
+/*
+ * SPI Frame byte size calculation.
+ * Assumes default word size of 24 bits (3 bytes).
+ */
+#define ADS131M_FRAME_BYTES(nch) \
+ (ADS131M_FRAME_WORDS(nch) * ADS131M_WORD_SIZE_BYTES)
+
+/*
+ * Index calculation for the start byte of channel 'x' data within the RX buffer.
+ * Assumes 24-bit words (3 bytes per word).
+ * The received frame starts with the response word (e.g., STATUS register
+ * content when NULL command was sent), followed by data for channels 0 to N-1,
+ * and finally the output CRC word.
+ * Response = index 0..2, Chan0 = index 3..5, Chan1 = index 6..8, ...
+ * Index for ChanX = 3 (response) + x * 3 (channel data size).
+ */
+#define ADS131M_CHANNEL_INDEX(x) \
+ ((x) * ADS131M_WORD_SIZE_BYTES + ADS131M_WORD_SIZE_BYTES)
+
+#define ADS131M_CMD_NULL 0x0000
+#define ADS131M_CMD_RESET 0x0011
+
+#define ADS131M_CMD_ADDR_MASK GENMASK(11, 7)
+#define ADS131M_CMD_NUM_MASK GENMASK(6, 0)
+
+#define ADS131M_CMD_RREG_OP 0xa000
+#define ADS131M_CMD_WREG_OP 0x6000
+
+#define ADS131M_CMD_RREG(a, n) \
+ (ADS131M_CMD_RREG_OP | \
+ FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \
+ FIELD_PREP(ADS131M_CMD_NUM_MASK, n))
+#define ADS131M_CMD_WREG(a, n) \
+ (ADS131M_CMD_WREG_OP | \
+ FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \
+ FIELD_PREP(ADS131M_CMD_NUM_MASK, n))
+
+/* STATUS Register (0x01h) bit definitions */
+#define ADS131M_STATUS_CRC_ERR BIT(12) /* Input CRC error */
+
+#define ADS131M_REG_MODE 0x02
+#define ADS131M_MODE_RX_CRC_EN BIT(12) /* Enable Input CRC */
+#define ADS131M_MODE_CRC_TYPE_ANSI BIT(11) /* 0 = CCITT, 1 = ANSI */
+#define ADS131M_MODE_RESET_FLAG BIT(10)
+
+#define ADS131M_REG_CLOCK 0x03
+#define ADS131M_CLOCK_XTAL_DIS BIT(7)
+#define ADS131M_CLOCK_EXTREF_EN BIT(6)
+
+/* 1.2V internal reference, in millivolts, for IIO_VAL_FRACTIONAL_LOG2 */
+#define ADS131M_VREF_INTERNAL_mV 1200
+/* 24-bit resolution */
+#define ADS131M_RESOLUTION_BITS 24
+/* Signed data uses (RESOLUTION_BITS - 1) magnitude bits */
+#define ADS131M_CODE_BITS (ADS131M_RESOLUTION_BITS - 1)
+
+/* External ref FSR = Vref * 0.96 */
+#define ADS131M_EXTREF_SCALE_NUM 96
+#define ADS131M_EXTREF_SCALE_DEN 100
+
+struct ads131m_configuration {
+ const struct iio_chan_spec *channels;
+ const char *name;
+ u16 reset_ack;
+ u8 num_channels;
+ u8 supports_extref:1;
+ u8 supports_xtal:1;
+};
+
+struct ads131m_priv {
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ const struct ads131m_configuration *config;
+
+ bool use_external_ref;
+ int scale_val;
+ int scale_val2;
+
+ struct spi_transfer xfer;
+ struct spi_message msg;
+
+ /*
+ * Protects the shared tx_buffer and rx_buffer. More importantly,
+ * this serializes all SPI communication to ensure the atomicity
+ * of multi-cycle command sequences (like WREG, RREG, or RESET).
+ */
+ struct mutex lock;
+
+ /* DMA-safe buffers should be placed at the end of the struct. */
+ u8 tx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)]
+ __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)];
+};
+
+/**
+ * ads131m_tx_frame_unlocked - Sends a command frame with Input CRC
+ * @priv: Device private data structure.
+ * @command: The 16-bit command to send (e.g., NULL, RREG, RESET).
+ *
+ * This function sends a command in Word 0, and its calculated 16-bit
+ * CRC in Word 1, as required when Input CRC is enabled.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_tx_frame_unlocked(struct ads131m_priv *priv, u32 command)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ u16 crc;
+
+ lockdep_assert_held(&priv->lock);
+
+ memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels));
+
+ /* Word 0: 16-bit command, MSB-aligned in 24-bit word */
+ put_unaligned_be16(command, &priv->tx_buffer[0]);
+
+ /* Word 1: Input CRC. Calculated over the 3 bytes of Word 0. */
+ crc = crc_itu_t(0xffff, priv->tx_buffer, 3);
+ put_unaligned_be16(crc, &priv->tx_buffer[3]);
+
+ return spi_sync(priv->spi, &priv->msg);
+}
+
+/**
+ * ads131m_rx_frame_unlocked - Receives a full SPI data frame.
+ * @priv: Device private data structure.
+ *
+ * This function sends a NULL command (with its CRC) to clock out a
+ * full SPI frame from the device (e.g., response + channel data + CRC).
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_rx_frame_unlocked(struct ads131m_priv *priv)
+{
+ return ads131m_tx_frame_unlocked(priv, ADS131M_CMD_NULL);
+}
+
+/**
+ * ads131m_check_status_crc_err - Checks for an Input CRC error.
+ * @priv: Device private data structure.
+ *
+ * Sends a NULL command to fetch the STATUS register and checks the
+ * CRC_ERR bit. This is used to verify the integrity of the previous
+ * command (like RREG or WREG).
+ *
+ * Return: 0 on success, -EIO if CRC_ERR bit is set.
+ */
+static int ads131m_check_status_crc_err(struct ads131m_priv *priv)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 status;
+ int ret;
+
+ lockdep_assert_held(&priv->lock);
+
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0) {
+ dev_err_ratelimited(dev,
+ "SPI error on STATUS read for CRC check\n");
+ return ret;
+ }
+
+ status = get_unaligned_be16(&priv->rx_buffer[0]);
+ if (status & ADS131M_STATUS_CRC_ERR) {
+ dev_err_ratelimited(dev,
+ "Input CRC error reported in STATUS = 0x%04x\n",
+ status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ads131m_write_reg_unlocked - Writes a single register and verifies the ACK.
+ * @priv: Device private data structure.
+ * @reg: The 8-bit register address.
+ * @val: The 16-bit value to write.
+ *
+ * This function performs the full 3-cycle WREG operation with Input CRC:
+ * 1. (Cycle 1) Sends WREG command, data, and its calculated CRC.
+ * 2. (Cycle 2) Sends NULL+CRC to retrieve the response from Cycle 1.
+ * 3. Verifies the response is the correct ACK for the WREG.
+ * 4. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_write_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 val)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ u16 command, expected_ack, response, crc;
+ struct device *dev = &priv->spi->dev;
+ int ret_crc_err = 0;
+ int ret;
+
+ lockdep_assert_held(&priv->lock);
+
+ command = ADS131M_CMD_WREG(reg, 0); /* n = 0 for 1 register */
+ /*
+ * Per Table 8-11, WREG response is: 010a aaaa ammm mmmm
+ * For 1 reg (n = 0 -> m = 0): 010a aaaa a000 0000 = 0x4000 | (reg << 7)
+ */
+ expected_ack = 0x4000 | (reg << 7);
+
+ /* Cycle 1: Send WREG Command + Data + Input CRC */
+
+ memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels));
+
+ /* Word 0: WREG command, 1 reg (n = 0), MSB-aligned */
+ put_unaligned_be16(command, &priv->tx_buffer[0]);
+
+ /* Word 1: Data, MSB-aligned */
+ put_unaligned_be16(val, &priv->tx_buffer[3]);
+
+ /* Word 2: Input CRC. Calculated over Word 0 (Cmd) and Word 1 (Data). */
+ crc = crc_itu_t(0xffff, priv->tx_buffer, 6);
+ put_unaligned_be16(crc, &priv->tx_buffer[6]);
+
+ /* Ignore the RX buffer (it's from the previous command) */
+ ret = spi_sync(priv->spi, &priv->msg);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on WREG (cycle 1)\n");
+ return ret;
+ }
+
+ /* Cycle 2: Send NULL Command to get the WREG response */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on WREG ACK (cycle 2)\n");
+ return ret;
+ }
+
+ /*
+ * Response is in the first 2 bytes of the RX buffer
+ * (MSB-aligned 16-bit response)
+ */
+ response = get_unaligned_be16(&priv->rx_buffer[0]);
+ if (response != expected_ack) {
+ dev_err_ratelimited(dev, "WREG(0x%02x) failed, expected ACK 0x%04x, got 0x%04x\n",
+ reg, expected_ack, response);
+ ret_crc_err = -EIO;
+ /*
+ * Don't return yet, still need to do Cycle 3 to clear
+ * any potential CRC_ERR flag from this failed command.
+ */
+ }
+
+ /*
+ * Cycle 3: Check STATUS for Input CRC error.
+ * This is necessary even if ACK was wrong, to clear the CRC_ERR flag.
+ */
+ ret = ads131m_check_status_crc_err(priv);
+ if (ret < 0)
+ return ret;
+
+ return ret_crc_err;
+}
+
+/**
+ * ads131m_read_reg_unlocked - Reads a single register from the device.
+ * @priv: Device private data structure.
+ * @reg: The 8-bit register address.
+ * @val: Pointer to store the 16-bit register value.
+ *
+ * This function performs the full 3-cycle RREG operation with Input CRC:
+ * 1. (Cycle 1) Sends the RREG command + Input CRC.
+ * 2. (Cycle 2) Sends NULL+CRC to retrieve the register data.
+ * 3. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_read_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 *val)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 command;
+ int ret;
+
+ lockdep_assert_held(&priv->lock);
+
+ command = ADS131M_CMD_RREG(reg, 0); /* n=0 for 1 register */
+
+ /*
+ * Cycle 1: Send RREG Command + Input CRC
+ * Ignore the RX buffer (it's from the previous command)
+ */
+ ret = ads131m_tx_frame_unlocked(priv, command);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on RREG (cycle 1)\n");
+ return ret;
+ }
+
+ /* Cycle 2: Send NULL Command to get the register data */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on RREG data (cycle 2)\n");
+ return ret;
+ }
+
+ /*
+ * Per datasheet, for a single reg read, the response is the data.
+ * It's in the first 2 bytes of the RX buffer (MSB-aligned 16-bit).
+ */
+ *val = get_unaligned_be16(&priv->rx_buffer[0]);
+
+ /*
+ * Cycle 3: Check STATUS for Input CRC error.
+ * The RREG command does not execute if CRC is bad, but we read
+ * STATUS anyway to clear the flag in case it was set.
+ */
+ return ads131m_check_status_crc_err(priv);
+}
+
+/**
+ * ads131m_rmw_reg - Reads, modifies, and writes a single register.
+ * @priv: Device private data structure.
+ * @reg: The 8-bit register address.
+ * @clear: Bitmask of bits to clear.
+ * @set: Bitmask of bits to set.
+ *
+ * This function performs an atomic read-modify-write operation on a register.
+ * It reads the register, applies the clear and set masks, and writes
+ * the new value back if it has changed.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_rmw_reg(struct ads131m_priv *priv, u8 reg, u16 clear, u16 set)
+{
+ u16 old_val, new_val;
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ ret = ads131m_read_reg_unlocked(priv, reg, &old_val);
+ if (ret < 0)
+ return ret;
+
+ new_val = (old_val & ~clear) | set;
+ if (new_val == old_val)
+ return 0;
+
+ return ads131m_write_reg_unlocked(priv, reg, new_val);
+}
+
+/**
+ * ads131m_verify_output_crc - Verifies the CRC of the received SPI frame.
+ * @priv: Device private data structure.
+ *
+ * This function calculates the CRC-16-CCITT (Poly 0x1021, Seed 0xFFFF) over
+ * the received response and channel data, and compares it to the CRC word
+ * received at the end of the SPI frame.
+ *
+ * Return: 0 on success, -EIO on CRC mismatch.
+ */
+static int ads131m_verify_output_crc(struct ads131m_priv *priv)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ struct device *dev = &priv->spi->dev;
+ u16 calculated_crc, received_crc;
+ size_t data_len;
+
+ lockdep_assert_held(&priv->lock);
+
+ /*
+ * Frame: [Response][Chan 0]...[Chan N-1][CRC Word]
+ * Data for CRC: [Response][Chan 0]...[Chan N-1]
+ * Data length = (N_channels + 1) * 3 bytes (at 24-bit word size)
+ */
+ data_len = ADS131M_FRAME_BYTES(indio_dev->num_channels) - 3;
+ calculated_crc = crc_itu_t(0xffff, priv->rx_buffer, data_len);
+
+ /*
+ * The received 16-bit CRC is MSB-aligned in the last 24-bit word.
+ * We extract it from the first 2 bytes (BE) of that word.
+ */
+ received_crc = get_unaligned_be16(&priv->rx_buffer[data_len]);
+ if (calculated_crc != received_crc) {
+ dev_err_ratelimited(dev, "Output CRC error. Got %04x, expected %04x\n",
+ received_crc, calculated_crc);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ads131m_adc_read - Reads channel data, checks input and output CRCs.
+ * @priv: Device private data structure.
+ * @channel: The channel number to read.
+ * @val: Pointer to store the raw 24-bit value.
+ *
+ * This function sends a NULL command (with Input CRC) to retrieve data.
+ * It checks the received STATUS word for any Input CRC errors from the
+ * previous command, and then verifies the Output CRC of the current
+ * data frame.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_adc_read(struct ads131m_priv *priv, u8 channel, s32 *val)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 status;
+ int ret;
+ u8 *buf;
+
+ guard(mutex)(&priv->lock);
+
+ /* Send NULL command + Input CRC, and receive data frame */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Check STATUS for Input CRC error from the previous command frame.
+ * Note: the STATUS word belongs to the frame before this NULL command.
+ */
+ status = get_unaligned_be16(&priv->rx_buffer[0]);
+ if (status & ADS131M_STATUS_CRC_ERR) {
+ dev_err_ratelimited(dev,
+ "Previous input CRC error reported in STATUS (0x%04x)\n",
+ status);
+ }
+
+ ret = ads131m_verify_output_crc(priv);
+ if (ret < 0)
+ return ret;
+
+ buf = &priv->rx_buffer[ADS131M_CHANNEL_INDEX(channel)];
+ *val = sign_extend32(get_unaligned_be24(buf), ADS131M_CODE_BITS);
+
+ return 0;
+}
+
+static int ads131m_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel,
+ int *val, int *val2, long mask)
+{
+ struct ads131m_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads131m_adc_read(priv, channel->channel, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = priv->scale_val;
+ *val2 = priv->scale_val2;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define ADS131M_VOLTAGE_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .differential = 1, \
+ .indexed = 1, \
+ .channel = (num), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ }
+
+static const struct iio_chan_spec ads131m02_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+};
+
+static const struct iio_chan_spec ads131m03_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+};
+
+static const struct iio_chan_spec ads131m04_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+ ADS131M_VOLTAGE_CHANNEL(3),
+};
+
+static const struct iio_chan_spec ads131m06_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+ ADS131M_VOLTAGE_CHANNEL(3),
+ ADS131M_VOLTAGE_CHANNEL(4),
+ ADS131M_VOLTAGE_CHANNEL(5),
+};
+
+static const struct iio_chan_spec ads131m08_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+ ADS131M_VOLTAGE_CHANNEL(3),
+ ADS131M_VOLTAGE_CHANNEL(4),
+ ADS131M_VOLTAGE_CHANNEL(5),
+ ADS131M_VOLTAGE_CHANNEL(6),
+ ADS131M_VOLTAGE_CHANNEL(7),
+};
+
+static const struct ads131m_configuration ads131m02_config = {
+ .channels = ads131m02_channels,
+ .num_channels = ARRAY_SIZE(ads131m02_channels),
+ .reset_ack = 0xff22,
+ .name = "ads131m02",
+};
+
+static const struct ads131m_configuration ads131m03_config = {
+ .channels = ads131m03_channels,
+ .num_channels = ARRAY_SIZE(ads131m03_channels),
+ .reset_ack = 0xff23,
+ .name = "ads131m03",
+};
+
+static const struct ads131m_configuration ads131m04_config = {
+ .channels = ads131m04_channels,
+ .num_channels = ARRAY_SIZE(ads131m04_channels),
+ .reset_ack = 0xff24,
+ .name = "ads131m04",
+};
+
+static const struct ads131m_configuration ads131m06_config = {
+ .channels = ads131m06_channels,
+ .num_channels = ARRAY_SIZE(ads131m06_channels),
+ .reset_ack = 0xff26,
+ .supports_extref = true,
+ .supports_xtal = true,
+ .name = "ads131m06",
+};
+
+static const struct ads131m_configuration ads131m08_config = {
+ .channels = ads131m08_channels,
+ .num_channels = ARRAY_SIZE(ads131m08_channels),
+ .reset_ack = 0xff28,
+ .supports_extref = true,
+ .supports_xtal = true,
+ .name = "ads131m08",
+};
+
+static const struct iio_info ads131m_info = {
+ .read_raw = ads131m_read_raw,
+};
+
+/*
+ * Prepares the reusable SPI message structure for a full-duplex transfer.
+ * The ADS131M requires sending a command frame while simultaneously
+ * receiving the response/data frame from the previous command cycle.
+ *
+ * This message is optimized for the primary data acquisition workflow:
+ * sending a single-word command (like NULL) and receiving a full data
+ * frame (Response + N*Channels + CRC).
+ *
+ * This message is sized for a full data frame and is reused for all
+ * command/data cycles. The driver does not implement variable-length SPI
+ * messages.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_prepare_message(struct ads131m_priv *priv)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ struct device *dev = &priv->spi->dev;
+ int ret;
+
+ priv->xfer.tx_buf = priv->tx_buffer;
+ priv->xfer.rx_buf = priv->rx_buffer;
+ priv->xfer.len = ADS131M_FRAME_BYTES(indio_dev->num_channels);
+ spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
+
+ ret = devm_spi_optimize_message(dev, priv->spi, &priv->msg);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to optimize SPI message\n");
+
+ return 0;
+}
+
+/**
+ * ads131m_hw_reset - Pulses the optional hardware reset.
+ * @priv: Device private data structure.
+ * @rstc: Reset control for the /RESET line.
+ *
+ * Pulses the /RESET line to perform a hardware reset and waits the
+ * required t_REGACQ time for the device to be ready.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_hw_reset(struct ads131m_priv *priv,
+ struct reset_control *rstc)
+{
+ struct device *dev = &priv->spi->dev;
+ int ret;
+
+ /*
+ * Manually pulse the reset line using the framework.
+ * The reset-gpio provider does not implement the .reset op,
+ * so we must use .assert and .deassert.
+ */
+ ret = reset_control_assert(rstc);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to assert reset\n");
+
+ /* Datasheet: Hold /RESET low for > 2 f_CLKIN cycles. 1us is ample. */
+ fsleep(1);
+
+ ret = reset_control_deassert(rstc);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to deassert reset\n");
+
+ /* Wait t_REGACQ (5us) for registers to be accessible */
+ fsleep(ADS131M_RESET_DELAY_US);
+
+ return 0;
+}
+
+/**
+ * ads131m_sw_reset - Issues a software RESET and verifies ACK.
+ * @priv: Device private data structure.
+ *
+ * This function sends a RESET command (with Input CRC), waits t_REGACQ,
+ * reads back the RESET ACK, and then sends a final NULL to check for
+ * any input CRC errors.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_sw_reset(struct ads131m_priv *priv)
+{
+ u16 expected_ack = priv->config->reset_ack;
+ struct device *dev = &priv->spi->dev;
+ u16 response;
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ ret = ads131m_tx_frame_unlocked(priv, ADS131M_CMD_RESET);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to send RESET command\n");
+
+ /* Wait t_REGACQ (5us) for device to be ready after reset */
+ fsleep(ADS131M_RESET_DELAY_US);
+
+ /* Cycle 2: Send NULL + CRC to retrieve the response to the RESET */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read RESET ACK\n");
+
+ response = get_unaligned_be16(&priv->rx_buffer[0]);
+
+ /* Check against the device-specific ACK value */
+ if (response != expected_ack)
+ return dev_err_probe(dev, -EIO,
+ "RESET ACK mismatch, got 0x%04x, expected 0x%04x\n",
+ response, expected_ack);
+
+ /* Cycle 3: Check STATUS for Input CRC error on the RESET command. */
+ return ads131m_check_status_crc_err(priv);
+}
+
+/**
+ * ads131m_reset - Resets the device using hardware or software.
+ * @priv: Device private data structure.
+ * @rstc: Optional reset control, or NULL for software reset.
+ *
+ * This function performs a hardware reset if supported (rstc provided),
+ * otherwise it issues a software RESET command via SPI.
+ *
+ * Note: The software reset path also validates the device's reset
+ * acknowledgment against the expected ID for the compatible string.
+ * The hardware reset path bypasses this ID check.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_reset(struct ads131m_priv *priv, struct reset_control *rstc)
+{
+ if (rstc)
+ return ads131m_hw_reset(priv, rstc);
+
+ return ads131m_sw_reset(priv);
+}
+
+static int ads131m_power_init(struct ads131m_priv *priv)
+{
+ static const char * const supply_ids[] = { "avdd", "dvdd" };
+ struct device *dev = &priv->spi->dev;
+ int vref_uV;
+ int ret;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supply_ids), supply_ids);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to enable regulators\n");
+
+ /* Default to Internal 1.2V reference: 1200mV / 2^23 */
+ priv->scale_val = ADS131M_VREF_INTERNAL_mV;
+ priv->scale_val2 = BIT(ADS131M_CODE_BITS);
+
+ if (!priv->config->supports_extref)
+ return 0;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to get refin supply\n");
+
+ if (ret == 0)
+ return dev_err_probe(dev, -EINVAL, "refin supply reports 0V\n");
+
+ if (ret == -ENODEV)
+ return 0;
+
+ vref_uV = ret;
+
+ /*
+ * External reference found: Scale(mV) = (vref_uV * 0.96) / 1000
+ * The denominator is 100 * 2^23 because of the 0.96 factor (96/100).
+ */
+ priv->scale_val = div_s64((s64)vref_uV * ADS131M_EXTREF_SCALE_NUM, 1000);
+ priv->scale_val2 = ADS131M_EXTREF_SCALE_DEN * BIT(ADS131M_CODE_BITS);
+ priv->use_external_ref = true;
+
+ return 0;
+}
+
+/**
+ * ads131m_hw_init - Initialize the ADC hardware.
+ * @priv: Device private data structure.
+ * @rstc: Optional reset control, or NULL for software reset.
+ * @is_xtal: True if 'clock-names' is "xtal", false if "clkin".
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_hw_init(struct ads131m_priv *priv,
+ struct reset_control *rstc, bool is_xtal)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 mode_clear, mode_set;
+ int ret;
+
+ ret = ads131m_reset(priv, rstc);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Configure CLOCK register (0x03) based on DT properties.
+ * This register only needs configuration for 32-pin (M06/M08)
+ * variants, as the configurable bits (XTAL_DIS, EXTREF_EN)
+ * are reserved on 20-pin (M02/M03/M04) variants.
+ */
+ if (priv->config->supports_xtal || priv->config->supports_extref) {
+ u16 clk_set = 0;
+
+ if (priv->config->supports_xtal && !is_xtal)
+ clk_set |= ADS131M_CLOCK_XTAL_DIS;
+
+ if (priv->config->supports_extref && priv->use_external_ref)
+ clk_set |= ADS131M_CLOCK_EXTREF_EN;
+
+ ret = ads131m_rmw_reg(priv, ADS131M_REG_CLOCK,
+ ADS131M_CLOCK_EXTREF_EN | ADS131M_CLOCK_XTAL_DIS,
+ clk_set);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to configure CLOCK register\n");
+ }
+
+ /*
+ * The RESET command sets all registers to default, which means:
+ * 1. The RESET bit (Bit 10) in MODE is set to '1'.
+ * 2. The CRC_TYPE bit (Bit 11) in MODE is '0' (CCITT).
+ * 3. The RX_CRC_EN bit (Bit 12) in MODE is '0' (Disabled).
+ *
+ * We must:
+ * 1. Clear the RESET bit.
+ * 2. Enable Input CRC (RX_CRC_EN).
+ * 3. Explicitly clear the ANSI CRC bit (for certainty).
+ */
+ mode_clear = ADS131M_MODE_CRC_TYPE_ANSI | ADS131M_MODE_RESET_FLAG;
+ mode_set = ADS131M_MODE_RX_CRC_EN;
+
+ ret = ads131m_rmw_reg(priv, ADS131M_REG_MODE, mode_clear, mode_set);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to configure MODE register\n");
+
+ return 0;
+}
+
+/**
+ * ads131m_parse_clock - enable clock and detect "xtal" selection
+ * @priv: Device private data structure.
+ * @is_xtal: result flag (true if "xtal", false if default "clkin")
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_parse_clock(struct ads131m_priv *priv, bool *is_xtal)
+{
+ struct device *dev = &priv->spi->dev;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR_OR_NULL(clk)) {
+ if (IS_ERR(clk))
+ ret = PTR_ERR(clk);
+ else
+ ret = -ENODEV;
+
+ return dev_err_probe(dev, ret, "clk get enabled failed\n");
+ }
+
+ ret = device_property_match_string(dev, "clock-names", "xtal");
+ if (ret > 0)
+ return dev_err_probe(dev, -EINVAL,
+ "'xtal' must be the only or first clock name");
+
+ if (ret < 0 && ret != -ENODATA)
+ return dev_err_probe(dev, ret,
+ "failed to read 'clock-names' property");
+
+ if (ret == 0 && !priv->config->supports_xtal)
+ return dev_err_probe(dev, -EINVAL,
+ "'xtal' clock not supported on this device");
+
+ *is_xtal = !ret;
+
+ return 0;
+}
+
+static int ads131m_probe(struct spi_device *spi)
+{
+ const struct ads131m_configuration *config;
+ struct device *dev = &spi->dev;
+ struct reset_control *rstc;
+ struct iio_dev *indio_dev;
+ struct ads131m_priv *priv;
+ bool is_xtal;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->indio_dev = indio_dev;
+ priv->spi = spi;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads131m_info;
+
+ config = spi_get_device_match_data(spi);
+
+ priv->config = config;
+ indio_dev->name = config->name;
+ indio_dev->channels = config->channels;
+ indio_dev->num_channels = config->num_channels;
+
+ rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc),
+ "Failed to get reset controller\n");
+
+ ret = devm_mutex_init(dev, &priv->lock);
+ if (ret < 0)
+ return ret;
+
+ ret = ads131m_prepare_message(priv);
+ if (ret < 0)
+ return ret;
+
+ ret = ads131m_power_init(priv);
+ if (ret < 0)
+ return ret;
+
+ /* Power must be applied and stable before the clock is enabled. */
+ ret = ads131m_parse_clock(priv, &is_xtal);
+ if (ret < 0)
+ return ret;
+
+ ret = ads131m_hw_init(priv, rstc, is_xtal);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ads131m_of_match[] = {
+ { .compatible = "ti,ads131m02", .data = &ads131m02_config },
+ { .compatible = "ti,ads131m03", .data = &ads131m03_config },
+ { .compatible = "ti,ads131m04", .data = &ads131m04_config },
+ { .compatible = "ti,ads131m06", .data = &ads131m06_config },
+ { .compatible = "ti,ads131m08", .data = &ads131m08_config },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads131m_of_match);
+
+static const struct spi_device_id ads131m_id[] = {
+ { "ads131m02", (kernel_ulong_t)&ads131m02_config },
+ { "ads131m03", (kernel_ulong_t)&ads131m03_config },
+ { "ads131m04", (kernel_ulong_t)&ads131m04_config },
+ { "ads131m06", (kernel_ulong_t)&ads131m06_config },
+ { "ads131m08", (kernel_ulong_t)&ads131m08_config },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads131m_id);
+
+static struct spi_driver ads131m_driver = {
+ .driver = {
+ .name = "ads131m02",
+ .of_match_table = ads131m_of_match,
+ },
+ .probe = ads131m_probe,
+ .id_table = ads131m_id,
+};
+module_spi_driver(ads131m_driver);
+
+MODULE_AUTHOR("David Jander <david@protonic.nl>");
+MODULE_DESCRIPTION("Texas Instruments ADS131M02 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig
index 55eb16b32f6c..a8a604863eed 100644
--- a/drivers/iio/amplifiers/Kconfig
+++ b/drivers/iio/amplifiers/Kconfig
@@ -36,6 +36,18 @@ config ADA4250
To compile this driver as a module, choose M here: the
module will be called ada4250.
+config ADL8113
+ tristate "Analog Devices ADL8113 Low Noise Amplifier"
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices ADL8113 Low Noise
+ Amplifier with integrated bypass switches. The device supports four
+ operation modes controlled by GPIO pins: internal amplifier,
+ internal bypass, and two external bypass modes.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adl8113.
+
config HMC425
tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers"
depends on GPIOLIB
diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile
index 2126331129cf..0a76443be1aa 100644
--- a/drivers/iio/amplifiers/Makefile
+++ b/drivers/iio/amplifiers/Makefile
@@ -6,4 +6,5 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD8366) += ad8366.o
obj-$(CONFIG_ADA4250) += ada4250.o
+obj-$(CONFIG_ADL8113) += adl8113.o
obj-$(CONFIG_HMC425) += hmc425a.o
diff --git a/drivers/iio/amplifiers/adl8113.c b/drivers/iio/amplifiers/adl8113.c
new file mode 100644
index 000000000000..b8a431b6616b
--- /dev/null
+++ b/drivers/iio/amplifiers/adl8113.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADL8113 Low Noise Amplifier with integrated bypass switches
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitmap.h>
+#include <linux/device/driver.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+enum adl8113_signal_path {
+ ADL8113_INTERNAL_AMP,
+ ADL8113_INTERNAL_BYPASS,
+ ADL8113_EXTERNAL_A,
+ ADL8113_EXTERNAL_B,
+};
+
+struct adl8113_gain_config {
+ enum adl8113_signal_path path;
+ int gain_db;
+};
+
+struct adl8113_state {
+ struct gpio_descs *gpios;
+ struct adl8113_gain_config *gain_configs;
+ unsigned int num_gain_configs;
+ enum adl8113_signal_path current_path;
+};
+
+static const char * const adl8113_supply_names[] = {
+ "vdd1",
+ "vss2",
+ "vdd2",
+};
+
+static int adl8113_set_path(struct adl8113_state *st,
+ enum adl8113_signal_path path)
+{
+ DECLARE_BITMAP(values, 2);
+ int ret;
+
+ /*
+ * Determine GPIO values based on signal path.
+ * Va: bit 0, Vb: bit 1.
+ */
+ switch (path) {
+ case ADL8113_INTERNAL_AMP:
+ bitmap_write(values, 0x00, 0, 2);
+ break;
+ case ADL8113_INTERNAL_BYPASS:
+ bitmap_write(values, 0x03, 0, 2);
+ break;
+ case ADL8113_EXTERNAL_A:
+ bitmap_write(values, 0x02, 0, 2);
+ break;
+ case ADL8113_EXTERNAL_B:
+ bitmap_write(values, 0x01, 0, 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc,
+ st->gpios->info, values);
+ if (ret)
+ return ret;
+
+ st->current_path = path;
+ return 0;
+}
+
+static int adl8113_find_gain_config(struct adl8113_state *st, int gain_db)
+{
+ unsigned int i;
+
+ for (i = 0; i < st->num_gain_configs; i++) {
+ if (st->gain_configs[i].gain_db == gain_db)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adl8113_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ },
+};
+
+static int adl8113_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct adl8113_state *st = iio_priv(indio_dev);
+ unsigned int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ /* Find current gain configuration */
+ for (i = 0; i < st->num_gain_configs; i++) {
+ if (st->gain_configs[i].path == st->current_path) {
+ *val = st->gain_configs[i].gain_db;
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO_DB;
+ }
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adl8113_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct adl8113_state *st = iio_priv(indio_dev);
+ int config_idx;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ if (val2 != 0)
+ return -EINVAL;
+
+ config_idx = adl8113_find_gain_config(st, val);
+ if (config_idx < 0)
+ return config_idx;
+
+ return adl8113_set_path(st, st->gain_configs[config_idx].path);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info adl8113_info = {
+ .read_raw = adl8113_read_raw,
+ .write_raw = adl8113_write_raw,
+};
+
+static int adl8113_init_gain_configs(struct device *dev, struct adl8113_state *st)
+{
+ int external_a_gain, external_b_gain;
+ unsigned int i;
+
+ /*
+ * Allocate for all 4 possible paths:
+ * - Internal amp and bypass (always present)
+ * - External bypass A and B (optional if configured)
+ */
+ st->gain_configs = devm_kcalloc(dev, 4, sizeof(*st->gain_configs),
+ GFP_KERNEL);
+ if (!st->gain_configs)
+ return -ENOMEM;
+
+ /* Start filling the gain configurations with data */
+ i = 0;
+
+ /* Always include internal amplifier (14dB) */
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_INTERNAL_AMP,
+ .gain_db = 14,
+ };
+
+ /* Always include internal bypass (-2dB insertion loss) */
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_INTERNAL_BYPASS,
+ .gain_db = -2,
+ };
+
+ /* Add external bypass A if configured */
+ if (!device_property_read_u32(dev, "adi,external-bypass-a-gain-db",
+ &external_a_gain)) {
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_EXTERNAL_A,
+ .gain_db = external_a_gain,
+ };
+ }
+
+ /* Add external bypass B if configured */
+ if (!device_property_read_u32(dev, "adi,external-bypass-b-gain-db",
+ &external_b_gain)) {
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_EXTERNAL_B,
+ .gain_db = external_b_gain,
+ };
+ }
+
+ st->num_gain_configs = i;
+
+ return 0;
+}
+
+static int adl8113_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct adl8113_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->gpios = devm_gpiod_get_array(dev, "ctrl", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpios))
+ return dev_err_probe(dev, PTR_ERR(st->gpios),
+ "failed to get control GPIOs\n");
+
+ if (st->gpios->ndescs != 2)
+ return dev_err_probe(dev, -EINVAL,
+ "expected 2 control GPIOs, got %u\n",
+ st->gpios->ndescs);
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(adl8113_supply_names),
+ adl8113_supply_names);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable supplies\n");
+
+ /* Initialize gain configurations from devicetree */
+ ret = adl8113_init_gain_configs(dev, st);
+ if (ret)
+ return ret;
+
+ /* Initialize to internal amplifier path (14dB) */
+ ret = adl8113_set_path(st, ADL8113_INTERNAL_AMP);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &adl8113_info;
+ indio_dev->name = "adl8113";
+ indio_dev->channels = adl8113_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adl8113_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id adl8113_of_match[] = {
+ { .compatible = "adi,adl8113" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adl8113_of_match);
+
+static struct platform_driver adl8113_driver = {
+ .driver = {
+ .name = "adl8113",
+ .of_match_table = adl8113_of_match,
+ },
+ .probe = adl8113_probe,
+};
+module_platform_driver(adl8113_driver);
+
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADL8113 Low Noise Amplifier");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 7a7a9d37339b..1c94b334f987 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -6,6 +6,7 @@
#include <linux/atomic.h>
#include <linux/cleanup.h>
+#include <linux/lockdep.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -135,9 +136,8 @@ static void iio_dma_buffer_cleanup_worker(struct work_struct *work)
struct iio_dma_buffer_block *block, *_block;
LIST_HEAD(block_list);
- spin_lock_irq(&iio_dma_buffer_dead_blocks_lock);
- list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
- spin_unlock_irq(&iio_dma_buffer_dead_blocks_lock);
+ scoped_guard(spinlock_irq, &iio_dma_buffer_dead_blocks_lock)
+ list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
list_for_each_entry_safe(block, _block, &block_list, head)
iio_buffer_block_release(&block->kref);
@@ -147,13 +147,11 @@ static DECLARE_WORK(iio_dma_buffer_cleanup_work, iio_dma_buffer_cleanup_worker);
static void iio_buffer_block_release_atomic(struct kref *kref)
{
struct iio_dma_buffer_block *block;
- unsigned long flags;
block = container_of(kref, struct iio_dma_buffer_block, kref);
- spin_lock_irqsave(&iio_dma_buffer_dead_blocks_lock, flags);
- list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
- spin_unlock_irqrestore(&iio_dma_buffer_dead_blocks_lock, flags);
+ scoped_guard(spinlock_irqsave, &iio_dma_buffer_dead_blocks_lock)
+ list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
schedule_work(&iio_dma_buffer_cleanup_work);
}
@@ -171,22 +169,20 @@ static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf)
return container_of(buf, struct iio_dma_buffer_queue, buffer);
}
-static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
- struct iio_dma_buffer_queue *queue, size_t size, bool fileio)
+static struct iio_dma_buffer_block *
+iio_dma_buffer_alloc_block(struct iio_dma_buffer_queue *queue, size_t size,
+ bool fileio)
{
- struct iio_dma_buffer_block *block;
-
- block = kzalloc(sizeof(*block), GFP_KERNEL);
+ struct iio_dma_buffer_block *block __free(kfree) =
+ kzalloc(sizeof(*block), GFP_KERNEL);
if (!block)
return NULL;
if (fileio) {
block->vaddr = dma_alloc_coherent(queue->dev, PAGE_ALIGN(size),
&block->phys_addr, GFP_KERNEL);
- if (!block->vaddr) {
- kfree(block);
+ if (!block->vaddr)
return NULL;
- }
}
block->fileio = fileio;
@@ -201,7 +197,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
if (!fileio)
atomic_inc(&queue->num_dmabufs);
- return block;
+ return_ptr(block);
}
static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
@@ -232,14 +228,12 @@ static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
{
struct iio_dma_buffer_queue *queue = block->queue;
- unsigned long flags;
bool cookie;
cookie = dma_fence_begin_signalling();
- spin_lock_irqsave(&queue->list_lock, flags);
- _iio_dma_buffer_block_done(block);
- spin_unlock_irqrestore(&queue->list_lock, flags);
+ scoped_guard(spinlock_irqsave, &queue->list_lock)
+ _iio_dma_buffer_block_done(block);
if (!block->fileio)
iio_buffer_signal_dmabuf_done(block->fence, 0);
@@ -261,25 +255,25 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_block_done, "IIO_DMA_BUFFER");
* hand the blocks back to the queue.
*/
void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
- struct list_head *list)
+ struct list_head *list)
{
struct iio_dma_buffer_block *block, *_block;
- unsigned long flags;
bool cookie;
cookie = dma_fence_begin_signalling();
- spin_lock_irqsave(&queue->list_lock, flags);
- list_for_each_entry_safe(block, _block, list, head) {
- list_del(&block->head);
- block->bytes_used = 0;
- _iio_dma_buffer_block_done(block);
+ scoped_guard(spinlock_irqsave, &queue->list_lock) {
+ list_for_each_entry_safe(block, _block, list, head) {
+ list_del(&block->head);
+ block->bytes_used = 0;
+ _iio_dma_buffer_block_done(block);
- if (!block->fileio)
- iio_buffer_signal_dmabuf_done(block->fence, -EINTR);
- iio_buffer_block_put_atomic(block);
+ if (!block->fileio)
+ iio_buffer_signal_dmabuf_done(block->fence,
+ -EINTR);
+ iio_buffer_block_put_atomic(block);
+ }
}
- spin_unlock_irqrestore(&queue->list_lock, flags);
if (queue->fileio.enabled)
queue->fileio.enabled = false;
@@ -328,7 +322,6 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
struct iio_dma_buffer_block *block;
bool try_reuse = false;
size_t size;
- int ret = 0;
int i;
/*
@@ -339,13 +332,13 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
size = DIV_ROUND_UP(queue->buffer.bytes_per_datum *
queue->buffer.length, 2);
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
queue->fileio.enabled = iio_dma_buffer_can_use_fileio(queue);
/* If DMABUFs were created, disable fileio interface */
if (!queue->fileio.enabled)
- goto out_unlock;
+ return 0;
/* Allocations are page aligned */
if (PAGE_ALIGN(queue->fileio.block_size) == PAGE_ALIGN(size))
@@ -354,21 +347,21 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
queue->fileio.block_size = size;
queue->fileio.active_block = NULL;
- spin_lock_irq(&queue->list_lock);
- for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
- block = queue->fileio.blocks[i];
+ scoped_guard(spinlock_irq, &queue->list_lock) {
+ for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
+ block = queue->fileio.blocks[i];
- /* If we can't re-use it free it */
- if (block && (!iio_dma_block_reusable(block) || !try_reuse))
- block->state = IIO_BLOCK_STATE_DEAD;
- }
+ /* If we can't re-use it free it */
+ if (block && (!iio_dma_block_reusable(block) || !try_reuse))
+ block->state = IIO_BLOCK_STATE_DEAD;
+ }
- /*
- * At this point all blocks are either owned by the core or marked as
- * dead. This means we can reset the lists without having to fear
- * corrution.
- */
- spin_unlock_irq(&queue->list_lock);
+ /*
+ * At this point all blocks are either owned by the core or
+ * marked as dead. This means we can reset the lists without
+ * having to fear corruption.
+ */
+ }
INIT_LIST_HEAD(&queue->incoming);
@@ -388,10 +381,9 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
if (!block) {
block = iio_dma_buffer_alloc_block(queue, size, true);
- if (!block) {
- ret = -ENOMEM;
- goto out_unlock;
- }
+ if (!block)
+ return -ENOMEM;
+
queue->fileio.blocks[i] = block;
}
@@ -415,10 +407,7 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
}
}
-out_unlock:
- mutex_unlock(&queue->lock);
-
- return ret;
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_request_update, "IIO_DMA_BUFFER");
@@ -426,13 +415,13 @@ static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue)
{
unsigned int i;
- spin_lock_irq(&queue->list_lock);
- for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
- if (!queue->fileio.blocks[i])
- continue;
- queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
+ scoped_guard(spinlock_irq, &queue->list_lock) {
+ for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
+ if (!queue->fileio.blocks[i])
+ continue;
+ queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
+ }
}
- spin_unlock_irq(&queue->list_lock);
INIT_LIST_HEAD(&queue->incoming);
@@ -446,7 +435,7 @@ static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue)
}
static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
- struct iio_dma_buffer_block *block)
+ struct iio_dma_buffer_block *block)
{
int ret;
@@ -490,19 +479,17 @@ static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
*
* This will allocate the DMA buffers and start the DMA transfers.
*/
-int iio_dma_buffer_enable(struct iio_buffer *buffer,
- struct iio_dev *indio_dev)
+int iio_dma_buffer_enable(struct iio_buffer *buffer, struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block, *_block;
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
queue->active = true;
list_for_each_entry_safe(block, _block, &queue->incoming, head) {
list_del(&block->head);
iio_dma_buffer_submit_block(queue, block);
}
- mutex_unlock(&queue->lock);
return 0;
}
@@ -516,24 +503,22 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_enable, "IIO_DMA_BUFFER");
* Needs to be called when the device that the buffer is attached to stops
* sampling. Typically should be the iio_buffer_access_ops disable callback.
*/
-int iio_dma_buffer_disable(struct iio_buffer *buffer,
- struct iio_dev *indio_dev)
+int iio_dma_buffer_disable(struct iio_buffer *buffer, struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
queue->active = false;
if (queue->ops && queue->ops->abort)
queue->ops->abort(queue);
- mutex_unlock(&queue->lock);
return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_disable, "IIO_DMA_BUFFER");
static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
- struct iio_dma_buffer_block *block)
+ struct iio_dma_buffer_block *block)
{
if (block->state == IIO_BLOCK_STATE_DEAD) {
iio_buffer_block_put(block);
@@ -545,25 +530,22 @@ static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
}
}
-static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
- struct iio_dma_buffer_queue *queue)
+static struct iio_dma_buffer_block *
+iio_dma_buffer_dequeue(struct iio_dma_buffer_queue *queue)
{
struct iio_dma_buffer_block *block;
unsigned int idx;
- spin_lock_irq(&queue->list_lock);
+ guard(spinlock_irq)(&queue->list_lock);
idx = queue->fileio.next_dequeue;
block = queue->fileio.blocks[idx];
- if (block->state == IIO_BLOCK_STATE_DONE) {
- idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks);
- queue->fileio.next_dequeue = idx;
- } else {
- block = NULL;
- }
+ if (block->state != IIO_BLOCK_STATE_DONE)
+ return NULL;
- spin_unlock_irq(&queue->list_lock);
+ idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks);
+ queue->fileio.next_dequeue = idx;
return block;
}
@@ -579,14 +561,13 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
if (n < buffer->bytes_per_datum)
return -EINVAL;
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
if (!queue->fileio.active_block) {
block = iio_dma_buffer_dequeue(queue);
- if (block == NULL) {
- ret = 0;
- goto out_unlock;
- }
+ if (!block)
+ return 0;
+
queue->fileio.pos = 0;
queue->fileio.active_block = block;
} else {
@@ -602,10 +583,8 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
ret = copy_from_user(addr, user_buffer, n);
else
ret = copy_to_user(user_buffer, addr, n);
- if (ret) {
- ret = -EFAULT;
- goto out_unlock;
- }
+ if (ret)
+ return -EFAULT;
queue->fileio.pos += n;
@@ -614,12 +593,7 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
iio_dma_buffer_enqueue(queue, block);
}
- ret = n;
-
-out_unlock:
- mutex_unlock(&queue->lock);
-
- return ret;
+ return n;
}
/**
@@ -677,23 +651,19 @@ size_t iio_dma_buffer_usage(struct iio_buffer *buf)
* but won't increase since all blocks are in use.
*/
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
if (queue->fileio.active_block)
data_available += queue->fileio.active_block->size;
- spin_lock_irq(&queue->list_lock);
+ guard(spinlock_irq)(&queue->list_lock);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
block = queue->fileio.blocks[i];
- if (block != queue->fileio.active_block
- && block->state == IIO_BLOCK_STATE_DONE)
+ if (block != queue->fileio.active_block && block->state == IIO_BLOCK_STATE_DONE)
data_available += block->size;
}
- spin_unlock_irq(&queue->list_lock);
- mutex_unlock(&queue->lock);
-
return data_available;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_usage, "IIO_DMA_BUFFER");
@@ -764,7 +734,7 @@ int iio_dma_buffer_enqueue_dmabuf(struct iio_buffer *buffer,
bool cookie;
int ret;
- WARN_ON(!mutex_is_locked(&queue->lock));
+ lockdep_assert_held(&queue->lock);
cookie = dma_fence_begin_signalling();
@@ -854,8 +824,8 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_set_length, "IIO_DMA_BUFFER");
* should refer to the device that will perform the DMA to ensure that
* allocations are done from a memory region that can be accessed by the device.
*/
-int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
- struct device *dev, const struct iio_dma_buffer_ops *ops)
+void iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, struct device *dev,
+ const struct iio_dma_buffer_ops *ops)
{
iio_buffer_init(&queue->buffer);
queue->buffer.length = PAGE_SIZE;
@@ -867,8 +837,6 @@ int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
mutex_init(&queue->lock);
spin_lock_init(&queue->list_lock);
-
- return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, "IIO_DMA_BUFFER");
@@ -881,12 +849,10 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, "IIO_DMA_BUFFER");
*/
void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue)
{
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
iio_dma_buffer_fileio_free(queue);
queue->ops = NULL;
-
- mutex_unlock(&queue->lock);
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_exit, "IIO_DMA_BUFFER");
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 27dd56334345..0d3454f4adb0 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -6,6 +6,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/cleanup.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
@@ -39,27 +40,24 @@ struct dmaengine_buffer {
size_t max_size;
};
-static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(
- struct iio_buffer *buffer)
+static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(struct iio_buffer *buffer)
{
return container_of(buffer, struct dmaengine_buffer, queue.buffer);
}
static void iio_dmaengine_buffer_block_done(void *data,
- const struct dmaengine_result *result)
+ const struct dmaengine_result *result)
{
struct iio_dma_buffer_block *block = data;
- unsigned long flags;
- spin_lock_irqsave(&block->queue->list_lock, flags);
- list_del(&block->head);
- spin_unlock_irqrestore(&block->queue->list_lock, flags);
+ scoped_guard(spinlock_irqsave, &block->queue->list_lock)
+ list_del(&block->head);
block->bytes_used -= result->residue;
iio_dma_buffer_block_done(block);
}
static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
- struct iio_dma_buffer_block *block)
+ struct iio_dma_buffer_block *block)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(&queue->buffer);
@@ -131,9 +129,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
if (dma_submit_error(cookie))
return dma_submit_error(cookie);
- spin_lock_irq(&dmaengine_buffer->queue.list_lock);
- list_add_tail(&block->head, &dmaengine_buffer->active);
- spin_unlock_irq(&dmaengine_buffer->queue.list_lock);
+ scoped_guard(spinlock_irq, &dmaengine_buffer->queue.list_lock)
+ list_add_tail(&block->head, &dmaengine_buffer->active);
dma_async_issue_pending(dmaengine_buffer->chan);
@@ -189,7 +186,7 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
};
static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
struct dmaengine_buffer *dmaengine_buffer =
@@ -248,7 +245,7 @@ static struct iio_buffer *iio_dmaengine_buffer_alloc(struct dma_chan *chan)
dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev);
iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
- &iio_dmaengine_default_ops);
+ &iio_dmaengine_default_ops);
dmaengine_buffer->queue.buffer.attrs = iio_dmaengine_buffer_attrs;
dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops;
diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c
index 86bde4a91bf7..bbc96c4c6283 100644
--- a/drivers/iio/chemical/ens160_core.c
+++ b/drivers/iio/chemical/ens160_core.c
@@ -316,12 +316,9 @@ static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq)
indio_dev->trig = iio_trigger_get(trig);
- ret = devm_request_threaded_irq(dev, irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_ONESHOT,
- indio_dev->name,
- indio_dev->trig);
+ ret = devm_request_irq(dev, irq, iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, indio_dev->name,
+ indio_dev->trig);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq\n");
diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c
index 0fd839176e26..23a326fb62a7 100644
--- a/drivers/iio/chemical/scd4x.c
+++ b/drivers/iio/chemical/scd4x.c
@@ -59,6 +59,8 @@ enum scd4x_channel_idx {
SCD4X_CO2,
SCD4X_TEMP,
SCD4X_HR,
+ /* kernel timestamp, at the end of buffer */
+ SCD4X_TS,
};
struct scd4x_state {
@@ -615,6 +617,7 @@ static const struct iio_chan_spec scd4x_channels[] = {
.endianness = IIO_CPU,
},
},
+ IIO_CHAN_SOFT_TIMESTAMP(SCD4X_TS),
};
static int scd4x_suspend(struct device *dev)
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
index 9ac80e4b7d75..5133755c2ea6 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -188,11 +188,8 @@ int cros_ec_sensors_push_data(struct iio_dev *indio_dev,
/*
* Ignore samples if the buffer is not set: it is needed if the ODR is
* set but the buffer is not enabled yet.
- *
- * Note: iio_device_claim_buffer_mode() returns -EBUSY if the buffer
- * is not enabled.
*/
- if (iio_device_claim_buffer_mode(indio_dev) < 0)
+ if (!iio_device_try_claim_buffer_mode(indio_dev))
return 0;
out = (s16 *)st->samples;
@@ -444,14 +441,14 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
ret = kstrtobool(buf, &calibrate);
if (ret < 0)
return ret;
- if (!calibrate)
- return -EINVAL;
mutex_lock(&st->cmd_lock);
st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
+ st->param.perform_calib.enable = calibrate;
ret = cros_ec_motion_send_host_cmd(st, 0);
if (ret != 0) {
- dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
+ dev_warn(&indio_dev->dev, "Unable to calibrate sensor: %d\n",
+ ret);
} else {
/* Save values */
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 7cd3caec1262..db9f5c711b3d 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -482,6 +482,19 @@ config MAX517
This driver can also be built as a module. If so, the module
will be called max517.
+config MAX22007
+ tristate "Analog Devices MAX22007 DAC Driver"
+ depends on SPI
+ select REGMAP_SPI
+ select CRC8
+ help
+ Say Y here if you want to build a driver for Analog Devices MAX22007.
+
+ MAX22007 is a quad-channel, 12-bit, voltage-output digital to
+ analog converter (DAC) with SPI interface.
+
+ If compiled as a module, it will be called max22007.
+
config MAX5522
tristate "Maxim MAX5522 DAC driver"
depends on SPI_MASTER
@@ -524,6 +537,26 @@ config MCP4728
To compile this driver as a module, choose M here: the module
will be called mcp4728.
+config MCP47FEB02
+ tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver"
+ depends on I2C
+ help
+ Say yes here if you want to build the driver for the Microchip:
+ - 8-bit DAC:
+ MCP47FEB01, MCP47FEB02, MCP47FEB04, MCP47FEB08,
+ MCP47FVB01, MCP47FVB02, MCP47FVB04, MCP47FVB08
+ - 10-bit DAC:
+ MCP47FEB11, MCP47FEB12, MCP47FEB14, MCP47FEB18,
+ MCP47FVB11, MCP47FVB12, MCP47FVB14, MCP47FVB18
+ - 12-bit DAC:
+ MCP47FEB21, MCP47FEB22, MCP47FEB24, MCP47FEB28,
+ MCP47FVB21, MCP47FVB22, MCP47FVB24, MCP47FVB28
+ having 1 to 8 channels, 8/10/12-bit digital-to-analog converter
+ (DAC) with I2C interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcp47feb02.
+
config MCP4821
tristate "MCP4801/02/11/12/21/22 DAC driver"
depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index e6ac4c67e337..2a80bbf4e80a 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -48,10 +48,12 @@ obj-$(CONFIG_LTC2664) += ltc2664.o
obj-$(CONFIG_LTC2688) += ltc2688.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
+obj-$(CONFIG_MAX22007) += max22007.o
obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
+obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o
obj-$(CONFIG_MCP4821) += mcp4821.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
index 0d525272a8a8..9cc895bbe51a 100644
--- a/drivers/iio/dac/adi-axi-dac.c
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -885,34 +885,35 @@ static const struct regmap_config axi_dac_regmap_config = {
static int axi_dac_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
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);
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
- st->info = device_get_match_data(&pdev->dev);
+ st->info = device_get_match_data(dev);
if (!st->info)
return -ENODEV;
- clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
+ clk = devm_clk_get_enabled(dev, "s_axi_aclk");
if (IS_ERR(clk)) {
/* Backward compat., old fdt versions without clock-names. */
- clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(clk),
- "failed to get clock\n");
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "failed to get clock\n");
}
if (st->info->has_dac_clk) {
struct clk *dac_clk;
- dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
+ dac_clk = devm_clk_get_enabled(dev, "dac_clk");
if (IS_ERR(dac_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
+ return dev_err_probe(dev, PTR_ERR(dac_clk),
"failed to get dac_clk clock\n");
/* We only care about the streaming mode rate */
@@ -923,11 +924,10 @@ static int axi_dac_probe(struct platform_device *pdev)
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);
+ st->dev = dev;
+ st->regmap = devm_regmap_init_mmio(dev, base, &axi_dac_regmap_config);
if (IS_ERR(st->regmap))
- return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
"failed to init register map\n");
/*
@@ -942,18 +942,15 @@ static int axi_dac_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
- ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
- dev_err(&pdev->dev,
- "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
- ADI_AXI_PCORE_VER_MAJOR(st->info->version),
- ADI_AXI_PCORE_VER_MINOR(st->info->version),
- ADI_AXI_PCORE_VER_PATCH(st->info->version),
- ADI_AXI_PCORE_VER_MAJOR(ver),
- ADI_AXI_PCORE_VER_MINOR(ver),
- ADI_AXI_PCORE_VER_PATCH(ver));
- return -ENODEV;
- }
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(st->info->version))
+ return dev_err_probe(dev, -ENODEV,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version),
+ ADI_AXI_PCORE_VER_MINOR(st->info->version),
+ ADI_AXI_PCORE_VER_PATCH(st->info->version),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
/* Let's get the core read only configuration */
ret = regmap_read(st->regmap, AXI_DAC_CONFIG_REG, &st->reg_config);
@@ -975,34 +972,33 @@ static int axi_dac_probe(struct platform_device *pdev)
mutex_init(&st->lock);
- ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
+ ret = devm_iio_backend_register(dev, st->info->backend_info, st);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
+ return dev_err_probe(dev, ret,
"failed to register iio backend\n");
- device_for_each_child_node_scoped(&pdev->dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
int val;
if (!st->info->has_child_nodes)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"invalid fdt axi-dac compatible.");
/* Processing only reg 0 node */
ret = fwnode_property_read_u32(child, "reg", &val);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "invalid reg property.");
+ return dev_err_probe(dev, ret, "invalid reg property.");
if (val != 0)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "invalid node address.");
+ return dev_err_probe(dev, -EINVAL,
+ "invalid node address.");
ret = axi_dac_create_platform_device(st, child);
if (ret)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "cannot create device.");
+ return dev_err_probe(dev, -EINVAL,
+ "cannot create device.");
}
- dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+ dev_info(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));
diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c
index a8198ba4f98a..6dda8918975a 100644
--- a/drivers/iio/dac/ds4424.c
+++ b/drivers/iio/dac/ds4424.c
@@ -14,7 +14,6 @@
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/machine.h>
-#include <linux/iio/consumer.h>
#define DS4422_MAX_DAC_CHANNELS 2
#define DS4424_MAX_DAC_CHANNELS 4
diff --git a/drivers/iio/dac/max22007.c b/drivers/iio/dac/max22007.c
new file mode 100644
index 000000000000..182ac7155a89
--- /dev/null
+++ b/drivers/iio/dac/max22007.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * max22007.c - MAX22007 DAC driver
+ *
+ * Driver for Analog Devices MAX22007 Digital to Analog Converter.
+ *
+ * Copyright (c) 2026 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/kstrtox.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.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/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include <dt-bindings/iio/addac/adi,ad74413r.h>
+struct device;
+
+#define MAX22007_NUM_CHANNELS 4
+#define MAX22007_REV_ID_REG 0x00
+#define MAX22007_STAT_INTR_REG 0x01
+#define MAX22007_INTERRUPT_EN_REG 0x02
+#define MAX22007_CONFIG_REG 0x03
+#define MAX22007_CONTROL_REG 0x04
+#define MAX22007_CHANNEL_MODE_REG 0x05
+#define MAX22007_SOFT_RESET_REG 0x06
+#define MAX22007_DAC_CHANNEL_REG(ch) (0x07 + (ch))
+#define MAX22007_GPIO_CTRL_REG 0x0B
+#define MAX22007_GPIO_DATA_REG 0x0C
+#define MAX22007_GPI_EDGE_INT_CTRL_REG 0x0D
+#define MAX22007_GPI_INT_STATUS_REG 0x0E
+
+/* Channel mask definitions */
+#define MAX22007_CH_MODE_CH_MASK(ch) BIT(12 + (ch))
+#define MAX22007_CH_PWRON_CH_MASK(ch) BIT(8 + (ch))
+#define MAX22007_DAC_LATCH_MODE_MASK(ch) BIT(12 + (ch))
+#define MAX22007_LDAC_UPDATE_MASK(ch) BIT(12 + (ch))
+#define MAX22007_SW_RST_MASK BIT(8)
+#define MAX22007_SW_CLR_MASK BIT(12)
+#define MAX22007_SOFT_RESET_BITS_MASK (MAX22007_SW_RST_MASK | \
+ MAX22007_SW_CLR_MASK)
+#define MAX22007_DAC_DATA_MASK GENMASK(15, 4)
+#define MAX22007_DAC_MAX_RAW GENMASK(11, 0)
+#define MAX22007_CRC8_POLYNOMIAL 0x8C
+#define MAX22007_CRC_EN_MASK BIT(0)
+#define MAX22007_RW_MASK BIT(0)
+#define MAX22007_CRC_OVERHEAD 1
+#define MAX22007_NUM_SUPPLIES 3
+#define MAX22007_REF_MV 2500
+
+/* Field value preparation macros with masking */
+#define MAX22007_CH_PWR_VAL(ch, val) (((val) & 0x1) << (8 + (ch)))
+#define MAX22007_CH_MODE_VAL(ch, val) (((val) & 0x1) << (12 + (ch)))
+#define MAX22007_DAC_LATCH_MODE_VAL(ch, val) (((val) & 0x1) << (12 + (ch)))
+
+static u8 max22007_crc8_table[CRC8_TABLE_SIZE];
+
+static const char * const max22007_supply_names[MAX22007_NUM_SUPPLIES] = {
+ "vdd",
+ "hvdd",
+ "hvss",
+};
+
+struct max22007_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct iio_chan_spec *iio_chans;
+ u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[4];
+};
+
+static int max22007_spi_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct max22007_state *st = context;
+ u8 calculated_crc, received_crc;
+ u8 rx_buf[4];
+ u8 reg_byte;
+ int ret;
+
+ if (reg_size != 1)
+ return -EINVAL;
+
+ if (val_size == 0 || val_size > 3)
+ return -EINVAL;
+
+ memcpy(&reg_byte, reg, 1);
+
+ ret = spi_write_then_read(st->spi, &reg_byte, 1, rx_buf,
+ val_size + MAX22007_CRC_OVERHEAD);
+ if (ret) {
+ dev_err(&st->spi->dev, "SPI transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ calculated_crc = crc8(max22007_crc8_table, &reg_byte, 1, 0x00);
+ calculated_crc = crc8(max22007_crc8_table, rx_buf, 2, calculated_crc);
+ received_crc = rx_buf[val_size];
+
+ if (calculated_crc != received_crc) {
+ dev_err(&st->spi->dev, "CRC mismatch on read register %02x\n", reg_byte);
+ return -EIO;
+ }
+
+ memcpy(val, rx_buf, val_size);
+
+ return 0;
+}
+
+static int max22007_spi_write(void *context, const void *data, size_t count)
+{
+ struct max22007_state *st = context;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_buf,
+ .rx_buf = st->rx_buf,
+ };
+
+ if (count + MAX22007_CRC_OVERHEAD > sizeof(st->tx_buf))
+ return -EINVAL;
+
+ memset(st->tx_buf, 0, sizeof(st->tx_buf));
+
+ xfer.len = count + MAX22007_CRC_OVERHEAD;
+
+ memcpy(st->tx_buf, data, count);
+ st->tx_buf[count] = crc8(max22007_crc8_table, st->tx_buf,
+ sizeof(st->tx_buf) - 1, 0x00);
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static bool max22007_reg_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX22007_REV_ID_REG:
+ case MAX22007_STAT_INTR_REG:
+ case MAX22007_CONFIG_REG:
+ case MAX22007_CONTROL_REG:
+ case MAX22007_CHANNEL_MODE_REG:
+ case MAX22007_SOFT_RESET_REG:
+ case MAX22007_GPIO_CTRL_REG:
+ case MAX22007_GPIO_DATA_REG:
+ case MAX22007_GPI_EDGE_INT_CTRL_REG:
+ case MAX22007_GPI_INT_STATUS_REG:
+ return true;
+ case MAX22007_DAC_CHANNEL_REG(0) ... MAX22007_DAC_CHANNEL_REG(MAX22007_NUM_CHANNELS - 1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max22007_reg_writable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX22007_CONFIG_REG:
+ case MAX22007_CONTROL_REG:
+ case MAX22007_CHANNEL_MODE_REG:
+ case MAX22007_SOFT_RESET_REG:
+ case MAX22007_GPIO_CTRL_REG:
+ case MAX22007_GPIO_DATA_REG:
+ case MAX22007_GPI_EDGE_INT_CTRL_REG:
+ return true;
+ case MAX22007_DAC_CHANNEL_REG(0) ... MAX22007_DAC_CHANNEL_REG(MAX22007_NUM_CHANNELS - 1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_bus max22007_regmap_bus = {
+ .read = max22007_spi_read,
+ .write = max22007_spi_write,
+ .read_flag_mask = MAX22007_RW_MASK,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config max22007_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .reg_shift = -1,
+ .readable_reg = max22007_reg_readable,
+ .writeable_reg = max22007_reg_writable,
+ .max_register = 0x0E,
+};
+
+static int max22007_write_channel_data(struct max22007_state *st,
+ unsigned int channel, int data)
+{
+ unsigned int reg_val;
+
+ if (data < 0 || data > MAX22007_DAC_MAX_RAW)
+ return -EINVAL;
+
+ reg_val = FIELD_PREP(MAX22007_DAC_DATA_MASK, data);
+
+ return regmap_write(st->regmap, MAX22007_DAC_CHANNEL_REG(channel), reg_val);
+}
+
+static int max22007_read_channel_data(struct max22007_state *st,
+ unsigned int channel, int *data)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(st->regmap, MAX22007_DAC_CHANNEL_REG(channel), &reg_val);
+ if (ret)
+ return ret;
+
+ *data = FIELD_GET(MAX22007_DAC_DATA_MASK, reg_val);
+
+ return 0;
+}
+
+static int max22007_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max22007_read_channel_data(st, chan->channel, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLTAGE)
+ *val = 5 * MAX22007_REF_MV; /* 5 * Vref in mV */
+ else
+ *val = 25; /* Vref / (2 * Rsense) = MAX22007_REF_MV / 100 */
+ *val2 = 12; /* 12-bit DAC resolution */
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max22007_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return max22007_write_channel_data(st, chan->channel, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info max22007_info = {
+ .read_raw = max22007_read_raw,
+ .write_raw = max22007_write_raw,
+};
+
+static ssize_t max22007_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+ unsigned int reg_val;
+ bool powerdown;
+ int ret;
+
+ ret = regmap_read(st->regmap, MAX22007_CHANNEL_MODE_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ powerdown = !(reg_val & MAX22007_CH_PWRON_CH_MASK(chan->channel));
+
+ return sysfs_emit(buf, "%d\n", powerdown);
+}
+
+static ssize_t max22007_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+ bool powerdown;
+ int ret;
+
+ ret = kstrtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, MAX22007_CHANNEL_MODE_REG,
+ MAX22007_CH_PWRON_CH_MASK(chan->channel),
+ MAX22007_CH_PWR_VAL(chan->channel, powerdown ? 0 : 1));
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static const struct iio_chan_spec_ext_info max22007_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = max22007_read_dac_powerdown,
+ .write = max22007_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ { }
+};
+
+static int max22007_parse_channel_cfg(struct max22007_state *st, u8 *num_channels)
+{
+ struct device *dev = &st->spi->dev;
+ int ret, num_chan;
+ int i = 0;
+ u32 reg;
+
+ num_chan = device_get_child_node_count(dev);
+ if (!num_chan)
+ return dev_err_probe(dev, -ENODEV, "no channels configured\n");
+
+ st->iio_chans = devm_kcalloc(dev, num_chan, sizeof(*st->iio_chans), GFP_KERNEL);
+ if (!st->iio_chans)
+ return -ENOMEM;
+
+ device_for_each_child_node_scoped(dev, child) {
+ u32 ch_func;
+ enum iio_chan_type chan_type;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read reg property of %pfwP\n", child);
+
+ if (reg >= MAX22007_NUM_CHANNELS)
+ return dev_err_probe(dev, -EINVAL,
+ "reg out of range in %pfwP\n", child);
+
+ ret = fwnode_property_read_u32(child, "adi,ch-func", &ch_func);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing adi,ch-func property for %pfwP\n", child);
+
+ switch (ch_func) {
+ case CH_FUNC_VOLTAGE_OUTPUT:
+ chan_type = IIO_VOLTAGE;
+ break;
+ case CH_FUNC_CURRENT_OUTPUT:
+ chan_type = IIO_CURRENT;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "invalid adi,ch-func %u for %pfwP\n",
+ ch_func, child);
+ }
+
+ st->iio_chans[i++] = (struct iio_chan_spec) {
+ .output = 1,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = max22007_ext_info,
+ .channel = reg,
+ .type = chan_type,
+ };
+
+ ret = regmap_update_bits(st->regmap, MAX22007_CHANNEL_MODE_REG,
+ MAX22007_CH_MODE_CH_MASK(reg),
+ MAX22007_CH_MODE_VAL(reg, ch_func - 1));
+ if (ret)
+ return ret;
+
+ /* Set DAC to transparent mode (immediate update) */
+ ret = regmap_update_bits(st->regmap, MAX22007_CONFIG_REG,
+ MAX22007_DAC_LATCH_MODE_MASK(reg),
+ MAX22007_DAC_LATCH_MODE_VAL(reg, 1));
+ if (ret)
+ return ret;
+ }
+
+ *num_channels = num_chan;
+
+ return 0;
+}
+
+static int max22007_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct gpio_desc *reset_gpio;
+ struct max22007_state *st;
+ struct iio_dev *indio_dev;
+ u8 num_channels;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ crc8_populate_lsb(max22007_crc8_table, MAX22007_CRC8_POLYNOMIAL);
+
+ st->regmap = devm_regmap_init(dev, &max22007_regmap_bus, st,
+ &max22007_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to initialize regmap\n");
+
+ ret = devm_regulator_bulk_get_enable(dev, MAX22007_NUM_SUPPLIES,
+ max22007_supply_names);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable regulators\n");
+
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(reset_gpio),
+ "Failed to get reset GPIO\n");
+
+ if (reset_gpio) {
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ usleep_range(1000, 5000);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ usleep_range(1000, 5000);
+ } else {
+ ret = regmap_write(st->regmap, MAX22007_SOFT_RESET_REG,
+ MAX22007_SOFT_RESET_BITS_MASK);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_set_bits(st->regmap, MAX22007_CONFIG_REG,
+ MAX22007_CRC_EN_MASK);
+ if (ret)
+ return ret;
+
+ ret = max22007_parse_channel_cfg(st, &num_channels);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &max22007_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->iio_chans;
+ indio_dev->num_channels = num_channels;
+ indio_dev->name = "max22007";
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id max22007_id[] = {
+ { "max22007" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, max22007_id);
+
+static const struct of_device_id max22007_of_match[] = {
+ { .compatible = "adi,max22007" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max22007_of_match);
+
+static struct spi_driver max22007_driver = {
+ .driver = {
+ .name = "max22007",
+ .of_match_table = max22007_of_match,
+ },
+ .probe = max22007_probe,
+ .id_table = max22007_id,
+};
+module_spi_driver(max22007_driver);
+
+MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>");
+MODULE_DESCRIPTION("Analog Devices MAX22007 DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c
new file mode 100644
index 000000000000..b218f0c3a0bd
--- /dev/null
+++ b/drivers/iio/dac/mcp47feb02.c
@@ -0,0 +1,1250 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface
+ *
+ * Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Ariana Lazar <ariana.lazar@microchip.com>
+ *
+ * Datasheet links:
+ * [MCP47FEBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
+ * [MCP47FVBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
+ * [MCP47FxBx4/8] https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/kstrtox.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+/* Register addresses must be left shifted with 3 positions in order to append command mask */
+#define MCP47FEB02_DAC0_REG_ADDR 0x00
+#define MCP47FEB02_VREF_REG_ADDR 0x40
+#define MCP47FEB02_POWER_DOWN_REG_ADDR 0x48
+#define MCP47FEB02_DAC_CTRL_MASK GENMASK(1, 0)
+
+#define MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR 0x50
+#define MCP47FEB02_GAIN_BIT_MASK BIT(0)
+#define MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK BIT(6)
+#define MCP47FEB02_GAIN_BITS_MASK GENMASK(15, 8)
+
+#define MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR 0x58
+
+#define MCP47FEB02_NV_DAC0_REG_ADDR 0x80
+#define MCP47FEB02_NV_VREF_REG_ADDR 0xC0
+#define MCP47FEB02_NV_POWER_DOWN_REG_ADDR 0xC8
+#define MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR 0xD0
+#define MCP47FEB02_NV_I2C_SLAVE_ADDR_MASK GENMASK(7, 0)
+
+/* Voltage reference, Power-Down control register and DAC Wiperlock status register fields */
+#define DAC_CTRL_MASK(ch) (GENMASK(1, 0) << (2 * (ch)))
+#define DAC_CTRL_VAL(ch, val) ((val) << (2 * (ch)))
+
+/* Gain Control and I2C Slave Address Reguster fields */
+#define DAC_GAIN_MASK(ch) (BIT(0) << (8 + (ch)))
+#define DAC_GAIN_VAL(ch, val) ((val) << (8 + (ch)))
+
+#define REG_ADDR(reg) ((reg) << 3)
+#define NV_REG_ADDR(reg) ((NV_DAC_ADDR_OFFSET + (reg)) << 3)
+#define READFLAG_MASK GENMASK(2, 1)
+
+#define MCP47FEB02_MAX_CH 8
+#define MCP47FEB02_MAX_SCALES_CH 3
+#define MCP47FEB02_DAC_WIPER_UNLOCKED 0
+#define MCP47FEB02_NORMAL_OPERATION 0
+#define MCP47FEB02_INTERNAL_BAND_GAP_mV 2440
+#define NV_DAC_ADDR_OFFSET 0x10
+
+enum mcp47feb02_vref_mode {
+ MCP47FEB02_VREF_VDD = 0,
+ MCP47FEB02_INTERNAL_BAND_GAP = 1,
+ MCP47FEB02_EXTERNAL_VREF_UNBUFFERED = 2,
+ MCP47FEB02_EXTERNAL_VREF_BUFFERED = 3,
+};
+
+enum mcp47feb02_scale {
+ MCP47FEB02_SCALE_VDD = 0,
+ MCP47FEB02_SCALE_GAIN_X1 = 1,
+ MCP47FEB02_SCALE_GAIN_X2 = 2,
+};
+
+enum mcp47feb02_gain_bit_mode {
+ MCP47FEB02_GAIN_BIT_X1 = 0,
+ MCP47FEB02_GAIN_BIT_X2 = 1,
+};
+
+static const char * const mcp47feb02_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "open_circuit",
+};
+
+/**
+ * struct mcp47feb02_features - chip specific data
+ * @name: device name
+ * @phys_channels: number of hardware channels
+ * @resolution: DAC resolution
+ * @have_ext_vref1: does the hardware have an the second external voltage reference?
+ * @have_eeprom: does the hardware have an internal eeprom?
+ */
+struct mcp47feb02_features {
+ const char *name;
+ unsigned int phys_channels;
+ unsigned int resolution;
+ bool have_ext_vref1;
+ bool have_eeprom;
+};
+
+static const struct mcp47feb02_features mcp47feb01_chip_features = {
+ .name = "mcp47feb01",
+ .phys_channels = 1,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb02_chip_features = {
+ .name = "mcp47feb02",
+ .phys_channels = 2,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb04_chip_features = {
+ .name = "mcp47feb04",
+ .phys_channels = 4,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb08_chip_features = {
+ .name = "mcp47feb08",
+ .phys_channels = 8,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb11_chip_features = {
+ .name = "mcp47feb11",
+ .phys_channels = 1,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb12_chip_features = {
+ .name = "mcp47feb12",
+ .phys_channels = 2,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb14_chip_features = {
+ .name = "mcp47feb14",
+ .phys_channels = 4,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb18_chip_features = {
+ .name = "mcp47feb18",
+ .phys_channels = 8,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb21_chip_features = {
+ .name = "mcp47feb21",
+ .phys_channels = 1,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb22_chip_features = {
+ .name = "mcp47feb22",
+ .phys_channels = 2,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb24_chip_features = {
+ .name = "mcp47feb24",
+ .phys_channels = 4,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb28_chip_features = {
+ .name = "mcp47feb28",
+ .phys_channels = 8,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47fvb01_chip_features = {
+ .name = "mcp47fvb01",
+ .phys_channels = 1,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb02_chip_features = {
+ .name = "mcp47fvb02",
+ .phys_channels = 2,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb04_chip_features = {
+ .name = "mcp47fvb04",
+ .phys_channels = 4,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb08_chip_features = {
+ .name = "mcp47fvb08",
+ .phys_channels = 8,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb11_chip_features = {
+ .name = "mcp47fvb11",
+ .phys_channels = 1,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb12_chip_features = {
+ .name = "mcp47fvb12",
+ .phys_channels = 2,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb14_chip_features = {
+ .name = "mcp47fvb14",
+ .phys_channels = 4,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb18_chip_features = {
+ .name = "mcp47fvb18",
+ .phys_channels = 8,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb21_chip_features = {
+ .name = "mcp47fvb21",
+ .phys_channels = 1,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb22_chip_features = {
+ .name = "mcp47fvb22",
+ .phys_channels = 2,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb24_chip_features = {
+ .name = "mcp47fvb24",
+ .phys_channels = 4,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb28_chip_features = {
+ .name = "mcp47fvb28",
+ .phys_channels = 8,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+/**
+ * struct mcp47feb02_channel_data - channel configuration
+ * @ref_mode: chosen voltage for reference
+ * @use_2x_gain: output driver gain control
+ * @powerdown: is false if the channel is in normal operation mode
+ * @powerdown_mode: selected power-down mode
+ * @dac_data: dac value
+ */
+struct mcp47feb02_channel_data {
+ u8 ref_mode;
+ bool use_2x_gain;
+ bool powerdown;
+ u8 powerdown_mode;
+ u16 dac_data;
+};
+
+/**
+ * struct mcp47feb02_data - chip configuration
+ * @chdata: options configured for each channel on the device
+ * @lock: prevents concurrent reads/writes to driver's state members
+ * @chip_features: pointer to features struct
+ * @scale_1: scales set on channels that are based on Vref1
+ * @scale: scales set on channels that are based on Vref/Vref0
+ * @active_channels_mask: enabled channels
+ * @regmap: regmap for directly accessing device register
+ * @labels: table with channels labels
+ * @phys_channels: physical channels on the device
+ * @vref1_buffered: Vref1 buffer is enabled
+ * @vref_buffered: Vref/Vref0 buffer is enabled
+ * @use_vref1: vref1-supply is defined
+ * @use_vref: vref-supply is defined
+ */
+struct mcp47feb02_data {
+ struct mcp47feb02_channel_data chdata[MCP47FEB02_MAX_CH];
+ struct mutex lock; /* prevents concurrent reads/writes to driver's state members */
+ const struct mcp47feb02_features *chip_features;
+ int scale_1[2 * MCP47FEB02_MAX_SCALES_CH];
+ int scale[2 * MCP47FEB02_MAX_SCALES_CH];
+ unsigned long active_channels_mask;
+ struct regmap *regmap;
+ const char *labels[MCP47FEB02_MAX_CH];
+ u16 phys_channels;
+ bool vref1_buffered;
+ bool vref_buffered;
+ bool use_vref1;
+ bool use_vref;
+};
+
+static const struct regmap_range mcp47feb02_readable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+};
+
+static const struct regmap_range mcp47feb02_writable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+};
+
+static const struct regmap_range mcp47feb02_volatile_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+};
+
+static const struct regmap_access_table mcp47feb02_readable_table = {
+ .yes_ranges = mcp47feb02_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47feb02_readable_ranges),
+};
+
+static const struct regmap_access_table mcp47feb02_writable_table = {
+ .yes_ranges = mcp47feb02_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47feb02_writable_ranges),
+};
+
+static const struct regmap_access_table mcp47feb02_volatile_table = {
+ .yes_ranges = mcp47feb02_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47feb02_volatile_ranges),
+};
+
+static const struct regmap_config mcp47feb02_regmap_config = {
+ .name = "mcp47feb02_regmap",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .rd_table = &mcp47feb02_readable_table,
+ .wr_table = &mcp47feb02_writable_table,
+ .volatile_table = &mcp47feb02_volatile_table,
+ .max_register = MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR,
+ .read_flag_mask = READFLAG_MASK,
+ .cache_type = REGCACHE_MAPLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+/* For devices that doesn't have nonvolatile memory */
+static const struct regmap_range mcp47fvb02_readable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+};
+
+static const struct regmap_range mcp47fvb02_writable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+};
+
+static const struct regmap_range mcp47fvb02_volatile_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+};
+
+static const struct regmap_access_table mcp47fvb02_readable_table = {
+ .yes_ranges = mcp47fvb02_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47fvb02_readable_ranges),
+};
+
+static const struct regmap_access_table mcp47fvb02_writable_table = {
+ .yes_ranges = mcp47fvb02_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47fvb02_writable_ranges),
+};
+
+static const struct regmap_access_table mcp47fvb02_volatile_table = {
+ .yes_ranges = mcp47fvb02_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47fvb02_volatile_ranges),
+};
+
+static const struct regmap_config mcp47fvb02_regmap_config = {
+ .name = "mcp47fvb02_regmap",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .rd_table = &mcp47fvb02_readable_table,
+ .wr_table = &mcp47fvb02_writable_table,
+ .volatile_table = &mcp47fvb02_volatile_table,
+ .max_register = MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR,
+ .read_flag_mask = READFLAG_MASK,
+ .cache_type = REGCACHE_MAPLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int mcp47feb02_write_to_eeprom(struct mcp47feb02_data *data, unsigned int reg,
+ unsigned int val)
+{
+ int eewa_val, ret;
+
+ /*
+ * Wait until the currently occurring EEPROM Write Cycle is completed.
+ * Only serial commands to the volatile memory are allowed.
+ */
+ guard(mutex)(&data->lock);
+
+ ret = regmap_read_poll_timeout(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR,
+ eewa_val,
+ !(eewa_val & MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK),
+ USEC_PER_MSEC, USEC_PER_MSEC * 5);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, reg, val);
+}
+
+static ssize_t store_eeprom_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct mcp47feb02_data *data = iio_priv(dev_to_iio_dev(dev));
+ unsigned int i, val, val1, eewa_val;
+ bool state;
+ int ret;
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ if (!state)
+ return 0;
+
+ /*
+ * Verify DAC Wiper and DAC Configuration are unlocked. If both are disabled,
+ * writing to EEPROM is available.
+ */
+ ret = regmap_read(data->regmap, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ if (val) {
+ dev_err(dev, "DAC Wiper and DAC Configuration not are unlocked.\n");
+ return -EINVAL;
+ }
+
+ for_each_set_bit(i, &data->active_channels_mask, data->phys_channels) {
+ ret = mcp47feb02_write_to_eeprom(data, NV_REG_ADDR(i),
+ data->chdata[i].dac_data);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_read(data->regmap, MCP47FEB02_VREF_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ ret = mcp47feb02_write_to_eeprom(data, MCP47FEB02_NV_VREF_REG_ADDR, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ ret = mcp47feb02_write_to_eeprom(data, MCP47FEB02_NV_POWER_DOWN_REG_ADDR, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, eewa_val,
+ !(eewa_val & MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK),
+ USEC_PER_MSEC, USEC_PER_MSEC * 5);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, &val1);
+ if (ret)
+ return ret;
+
+ ret = mcp47feb02_write_to_eeprom(data, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR,
+ (val1 & MCP47FEB02_GAIN_BITS_MASK) |
+ (val & MCP47FEB02_NV_I2C_SLAVE_ADDR_MASK));
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_WO(store_eeprom, 0);
+
+static struct attribute *mcp47feb02_attributes[] = {
+ &iio_dev_attr_store_eeprom.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mcp47feb02_attribute_group = {
+ .attrs = mcp47feb02_attributes,
+};
+
+static int mcp47feb02_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ int ret;
+ u8 ch;
+
+ guard(mutex)(&data->lock);
+
+ for_each_set_bit(ch, &data->active_channels_mask, data->phys_channels) {
+ u8 pd_mode;
+
+ data->chdata[ch].powerdown = true;
+ pd_mode = data->chdata[ch].powerdown_mode + 1;
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR,
+ DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, pd_mode));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, REG_ADDR(ch), data->chdata[ch].dac_data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ u8 ch;
+
+ guard(mutex)(&data->lock);
+
+ for_each_set_bit(ch, &data->active_channels_mask, data->phys_channels) {
+ u8 pd_mode;
+ int ret;
+
+ data->chdata[ch].powerdown = false;
+ pd_mode = data->chdata[ch].powerdown_mode + 1;
+
+ ret = regmap_write(data->regmap, REG_ADDR(ch), data->chdata[ch].dac_data);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_VREF_REG_ADDR,
+ DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, pd_mode));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR,
+ DAC_GAIN_MASK(ch),
+ DAC_GAIN_VAL(ch, data->chdata[ch].use_2x_gain));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR,
+ DAC_CTRL_MASK(ch),
+ DAC_CTRL_VAL(ch, MCP47FEB02_NORMAL_OPERATION));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ return data->chdata[chan->address].powerdown_mode;
+}
+
+static int mcp47feb02_set_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *ch,
+ unsigned int mode)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ data->chdata[ch->address].powerdown_mode = mode;
+
+ return 0;
+}
+
+static ssize_t mcp47feb02_read_powerdown(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *ch, char *buf)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ /* Print if channel is in a power-down mode or not */
+ return sysfs_emit(buf, "%d\n", data->chdata[ch->address].powerdown);
+}
+
+static ssize_t mcp47feb02_write_powerdown(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *ch, const char *buf,
+ size_t len)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ u32 reg = ch->address;
+ u8 tmp_pd_mode;
+ bool state;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ /*
+ * Set the channel to the specified power-down mode. Exiting power-down mode
+ * requires writing normal operation mode (0) to the channel-specific register bits.
+ */
+ tmp_pd_mode = state ? (data->chdata[reg].powerdown_mode + 1) : MCP47FEB02_NORMAL_OPERATION;
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR,
+ DAC_CTRL_MASK(reg), DAC_CTRL_VAL(reg, tmp_pd_mode));
+ if (ret)
+ return ret;
+
+ data->chdata[reg].powerdown = state;
+
+ return len;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mcp47feb02_pm_ops, mcp47feb02_suspend, mcp47feb02_resume);
+
+static const struct iio_enum mcp47febxx_powerdown_mode_enum = {
+ .items = mcp47feb02_powerdown_modes,
+ .num_items = ARRAY_SIZE(mcp47feb02_powerdown_modes),
+ .get = mcp47feb02_get_powerdown_mode,
+ .set = mcp47feb02_set_powerdown_mode,
+};
+
+static const struct iio_chan_spec_ext_info mcp47feb02_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = mcp47feb02_read_powerdown,
+ .write = mcp47feb02_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp47febxx_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &mcp47febxx_powerdown_mode_enum),
+ { }
+};
+
+static const struct iio_chan_spec mcp47febxx_ch_template = {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = mcp47feb02_ext_info,
+};
+
+static void mcp47feb02_init_scale(struct mcp47feb02_data *data, enum mcp47feb02_scale scale,
+ int vref_mV, int scale_avail[])
+{
+ u32 value_micro, value_int;
+ u64 tmp;
+
+ /* vref_mV should not be negative */
+ tmp = (u64)vref_mV * MICRO >> data->chip_features->resolution;
+ value_int = div_u64_rem(tmp, MICRO, &value_micro);
+ scale_avail[scale * 2] = value_int;
+ scale_avail[scale * 2 + 1] = value_micro;
+}
+
+static int mcp47feb02_init_scales_avail(struct mcp47feb02_data *data, int vdd_mV,
+ int vref_mV, int vref1_mV)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int tmp_vref;
+
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale);
+
+ if (data->use_vref)
+ tmp_vref = vref_mV;
+ else
+ tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV;
+
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1, tmp_vref, data->scale);
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X2, tmp_vref * 2, data->scale);
+
+ if (data->phys_channels >= 4) {
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale_1);
+
+ if (data->use_vref1 && vref1_mV <= 0)
+ return dev_err_probe(dev, vref1_mV, "Invalid voltage for Vref1\n");
+
+ if (data->use_vref1)
+ tmp_vref = vref1_mV;
+ else
+ tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV;
+
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1,
+ tmp_vref, data->scale_1);
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X2,
+ tmp_vref * 2, data->scale_1);
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ const int **vals, int *type, int *length, long info)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (ch->type) {
+ case IIO_VOLTAGE:
+ if (data->phys_channels >= 4 && (ch->address % 2))
+ *vals = data->scale_1;
+ else
+ *vals = data->scale;
+
+ *length = 2 * MCP47FEB02_MAX_SCALES_CH;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static void mcp47feb02_get_scale(int ch, struct mcp47feb02_data *data, int *val, int *val2)
+{
+ enum mcp47feb02_scale current_scale;
+
+ if (data->chdata[ch].ref_mode == MCP47FEB02_VREF_VDD)
+ current_scale = MCP47FEB02_SCALE_VDD;
+ else if (data->chdata[ch].use_2x_gain)
+ current_scale = MCP47FEB02_SCALE_GAIN_X2;
+ else
+ current_scale = MCP47FEB02_SCALE_GAIN_X1;
+
+ if (data->phys_channels >= 4 && (ch % 2)) {
+ *val = data->scale_1[current_scale * 2];
+ *val2 = data->scale_1[current_scale * 2 + 1];
+ } else {
+ *val = data->scale[current_scale * 2];
+ *val2 = data->scale[current_scale * 2 + 1];
+ }
+}
+
+static int mcp47feb02_check_scale(struct mcp47feb02_data *data, int val, int val2, int scale[])
+{
+ unsigned int i;
+
+ for (i = 0; i < MCP47FEB02_MAX_SCALES_CH; i++) {
+ if (scale[i * 2] == val && scale[i * 2 + 1] == val2)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int mcp47feb02_ch_scale(struct mcp47feb02_data *data, int ch, int scale)
+{
+ int tmp_val, ret;
+
+ if (scale == MCP47FEB02_SCALE_VDD) {
+ tmp_val = MCP47FEB02_VREF_VDD;
+ } else if (data->phys_channels >= 4 && (ch % 2)) {
+ if (data->use_vref1) {
+ if (data->vref1_buffered)
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_BUFFERED;
+ else
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_UNBUFFERED;
+ } else {
+ tmp_val = MCP47FEB02_INTERNAL_BAND_GAP;
+ }
+ } else if (data->use_vref) {
+ if (data->vref_buffered)
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_BUFFERED;
+ else
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_UNBUFFERED;
+ } else {
+ tmp_val = MCP47FEB02_INTERNAL_BAND_GAP;
+ }
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_VREF_REG_ADDR,
+ DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, tmp_val));
+ if (ret)
+ return ret;
+
+ data->chdata[ch].ref_mode = tmp_val;
+
+ return 0;
+}
+
+/*
+ * Setting the scale in order to choose between VDD and (Vref or Band Gap) from the user
+ * space. The VREF pin is either an input or an output, therefore the user cannot
+ * simultaneously connect an external voltage reference to the pin and select the
+ * internal Band Gap.
+ * When the DAC’s voltage reference is configured as the VREF pin, the pin is an input.
+ * When the DAC’s voltage reference is configured as the internal Band Gap,
+ * the VREF pin is an output.
+ * If Vref/Vref1 voltage is not available, then the internal Band Gap will be used
+ * to calculate the values for the scale.
+ */
+static int mcp47feb02_set_scale(struct mcp47feb02_data *data, int ch, int scale)
+{
+ int tmp_val, ret;
+
+ ret = mcp47feb02_ch_scale(data, ch, scale);
+ if (ret)
+ return ret;
+
+ if (scale == MCP47FEB02_SCALE_GAIN_X2)
+ tmp_val = MCP47FEB02_GAIN_BIT_X2;
+ else
+ tmp_val = MCP47FEB02_GAIN_BIT_X1;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR,
+ DAC_GAIN_MASK(ch), DAC_GAIN_VAL(ch, tmp_val));
+ if (ret)
+ return ret;
+
+ data->chdata[ch].use_2x_gain = tmp_val;
+
+ return 0;
+}
+
+static int mcp47feb02_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ int *val, int *val2, long mask)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(data->regmap, REG_ADDR(ch->address), val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ mcp47feb02_get_scale(ch->address, data, val, val2);
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp47feb02_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ int val, int val2, long mask)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ int *tmp_scale, ret;
+
+ guard(mutex)(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_write(data->regmap, REG_ADDR(ch->address), val);
+ if (ret)
+ return ret;
+
+ data->chdata[ch->address].dac_data = val;
+ return 0;
+ case IIO_CHAN_INFO_SCALE:
+ if (data->phys_channels >= 4 && (ch->address % 2))
+ tmp_scale = data->scale_1;
+ else
+ tmp_scale = data->scale;
+
+ ret = mcp47feb02_check_scale(data, val, val2, tmp_scale);
+ if (ret < 0)
+ return ret;
+
+ return mcp47feb02_set_scale(data, ch->address, ret);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp47feb02_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ char *label)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ return sysfs_emit(label, "%s\n", data->labels[ch->address]);
+}
+
+static const struct iio_info mcp47feb02_info = {
+ .read_raw = mcp47feb02_read_raw,
+ .write_raw = mcp47feb02_write_raw,
+ .read_label = mcp47feb02_read_label,
+ .read_avail = &mcp47feb02_read_avail,
+ .attrs = &mcp47feb02_attribute_group,
+};
+
+static const struct iio_info mcp47fvb02_info = {
+ .read_raw = mcp47feb02_read_raw,
+ .write_raw = mcp47feb02_write_raw,
+ .read_label = mcp47feb02_read_label,
+ .read_avail = &mcp47feb02_read_avail,
+};
+
+static int mcp47feb02_parse_fw(struct iio_dev *indio_dev,
+ const struct mcp47feb02_features *chip_features)
+{
+ struct iio_chan_spec chanspec = mcp47febxx_ch_template;
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(data->regmap);
+ struct iio_chan_spec *channels;
+ u32 num_channels;
+ u8 chan_idx = 0;
+
+ guard(mutex)(&data->lock);
+
+ num_channels = device_get_child_node_count(dev);
+ if (num_channels > chip_features->phys_channels)
+ return dev_err_probe(dev, -EINVAL, "More channels than the chip supports\n");
+
+ if (!num_channels)
+ return dev_err_probe(dev, -EINVAL, "No channel specified in the devicetree.\n");
+
+ channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ device_for_each_child_node_scoped(dev, child) {
+ u32 reg = 0;
+ int ret;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Invalid channel number\n");
+
+ if (reg >= chip_features->phys_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "The index of the channels does not match the chip\n");
+
+ set_bit(reg, &data->active_channels_mask);
+
+ ret = fwnode_property_read_string(child, "label", &data->labels[reg]);
+ if (ret)
+ return dev_err_probe(dev, ret, "%pfw: invalid label\n",
+ fwnode_get_name(child));
+
+ chanspec.address = reg;
+ chanspec.channel = reg;
+ channels[chan_idx] = chanspec;
+ chan_idx++;
+ }
+
+ indio_dev->num_channels = num_channels;
+ indio_dev->channels = channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ data->phys_channels = chip_features->phys_channels;
+
+ data->vref_buffered = device_property_read_bool(dev, "microchip,vref-buffered");
+
+ if (chip_features->have_ext_vref1)
+ data->vref1_buffered = device_property_read_bool(dev, "microchip,vref1-buffered");
+
+ return 0;
+}
+
+static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data)
+{
+ unsigned int i, vref_ch, gain_ch, pd_ch;
+ int ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_VREF_REG_ADDR, &vref_ch);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, &gain_ch);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR, &pd_ch);
+ if (ret)
+ return ret;
+
+ gain_ch = gain_ch & MCP47FEB02_GAIN_BITS_MASK;
+ for_each_set_bit(i, &data->active_channels_mask, data->phys_channels) {
+ struct device *dev = regmap_get_device(data->regmap);
+ unsigned int pd_tmp;
+
+ data->chdata[i].ref_mode = (vref_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK;
+ data->chdata[i].use_2x_gain = (gain_ch >> i) & MCP47FEB02_GAIN_BIT_MASK;
+
+ /*
+ * Inform the user that the current voltage reference read from the volatile
+ * register of the chip is different from the one specified in the device tree.
+ * Considering that the user cannot have an external voltage reference connected
+ * to the pin and select the internal Band Gap at the same time, in order to avoid
+ * miscofiguring the reference voltage, the volatile register will not be written.
+ * In order to overwrite the setting from volatile register with the one from the
+ * device tree, the user needs to write the chosen scale.
+ */
+ switch (data->chdata[i].ref_mode) {
+ case MCP47FEB02_INTERNAL_BAND_GAP:
+ if (data->phys_channels >= 4 && (i % 2) && data->use_vref1) {
+ dev_dbg(dev, "ch[%u]: was configured to use internal band gap", i);
+ dev_dbg(dev, "ch[%u]: reference voltage set to VREF1", i);
+ break;
+ }
+ if ((data->phys_channels < 4 || (data->phys_channels >= 4 && !(i % 2))) &&
+ data->use_vref) {
+ dev_dbg(dev, "ch[%u]: was configured to use internal band gap", i);
+ dev_dbg(dev, "ch[%u]: reference voltage set to VREF", i);
+ break;
+ }
+ break;
+ case MCP47FEB02_EXTERNAL_VREF_UNBUFFERED:
+ case MCP47FEB02_EXTERNAL_VREF_BUFFERED:
+ if (data->phys_channels >= 4 && (i % 2) && !data->use_vref1) {
+ dev_dbg(dev, "ch[%u]: was configured to use VREF1", i);
+ dev_dbg(dev,
+ "ch[%u]: reference voltage set to internal band gap", i);
+ break;
+ }
+ if ((data->phys_channels < 4 || (data->phys_channels >= 4 && !(i % 2))) &&
+ !data->use_vref) {
+ dev_dbg(dev, "ch[%u]: was configured to use VREF", i);
+ dev_dbg(dev,
+ "ch[%u]: reference voltage set to internal band gap", i);
+ break;
+ }
+ break;
+ }
+
+ pd_tmp = (pd_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK;
+ data->chdata[i].powerdown_mode = pd_tmp ? (pd_tmp - 1) : pd_tmp;
+ data->chdata[i].powerdown = !!(data->chdata[i].powerdown_mode);
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_mV,
+ int vref_mV, int vref1_mV)
+{
+ unsigned int i;
+
+ for_each_set_bit(i, &data->active_channels_mask, data->phys_channels) {
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = mcp47feb02_init_scales_avail(data, vdd_mV, vref_mV, vref1_mV);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init scales for ch %u\n", i);
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_probe(struct i2c_client *client)
+{
+ const struct mcp47feb02_features *chip_features;
+ struct device *dev = &client->dev;
+ struct mcp47feb02_data *data;
+ struct iio_dev *indio_dev;
+ int vref1_mV = 0;
+ int vref_mV = 0;
+ int vdd_mV;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ chip_features = i2c_get_match_data(client);
+ if (!chip_features)
+ return -EINVAL;
+
+ data->chip_features = chip_features;
+
+ if (chip_features->have_eeprom) {
+ data->regmap = devm_regmap_init_i2c(client, &mcp47feb02_regmap_config);
+ indio_dev->info = &mcp47feb02_info;
+ } else {
+ data->regmap = devm_regmap_init_i2c(client, &mcp47fvb02_regmap_config);
+ indio_dev->info = &mcp47fvb02_info;
+ }
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap), "Error initializing i2c regmap\n");
+
+ indio_dev->name = chip_features->name;
+
+ ret = mcp47feb02_parse_fw(indio_dev, chip_features);
+ if (ret)
+ return dev_err_probe(dev, ret, "Error parsing firmware data\n");
+
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vdd");
+ if (ret < 0)
+ return ret;
+
+ vdd_mV = ret / MILLI;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret > 0) {
+ vref_mV = ret / MILLI;
+ data->use_vref = true;
+ } else {
+ dev_dbg(dev, "using internal band gap as voltage reference.\n");
+ dev_dbg(dev, "Vref is unavailable.\n");
+ }
+
+ if (chip_features->have_ext_vref1) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref1");
+ if (ret > 0) {
+ vref1_mV = ret / MILLI;
+ data->use_vref1 = true;
+ } else {
+ dev_dbg(dev, "using internal band gap as voltage reference 1.\n");
+ dev_dbg(dev, "Vref1 is unavailable.\n");
+ }
+ }
+
+ ret = mcp47feb02_init_ctrl_regs(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Error initialising vref register\n");
+
+ ret = mcp47feb02_init_ch_scales(data, vdd_mV, vref_mV, vref1_mV);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id mcp47feb02_id[] = {
+ { "mcp47feb01", (kernel_ulong_t)&mcp47feb01_chip_features },
+ { "mcp47feb02", (kernel_ulong_t)&mcp47feb02_chip_features },
+ { "mcp47feb04", (kernel_ulong_t)&mcp47feb04_chip_features },
+ { "mcp47feb08", (kernel_ulong_t)&mcp47feb08_chip_features },
+ { "mcp47feb11", (kernel_ulong_t)&mcp47feb11_chip_features },
+ { "mcp47feb12", (kernel_ulong_t)&mcp47feb12_chip_features },
+ { "mcp47feb14", (kernel_ulong_t)&mcp47feb14_chip_features },
+ { "mcp47feb18", (kernel_ulong_t)&mcp47feb18_chip_features },
+ { "mcp47feb21", (kernel_ulong_t)&mcp47feb21_chip_features },
+ { "mcp47feb22", (kernel_ulong_t)&mcp47feb22_chip_features },
+ { "mcp47feb24", (kernel_ulong_t)&mcp47feb24_chip_features },
+ { "mcp47feb28", (kernel_ulong_t)&mcp47feb28_chip_features },
+ { "mcp47fvb01", (kernel_ulong_t)&mcp47fvb01_chip_features },
+ { "mcp47fvb02", (kernel_ulong_t)&mcp47fvb02_chip_features },
+ { "mcp47fvb04", (kernel_ulong_t)&mcp47fvb04_chip_features },
+ { "mcp47fvb08", (kernel_ulong_t)&mcp47fvb08_chip_features },
+ { "mcp47fvb11", (kernel_ulong_t)&mcp47fvb11_chip_features },
+ { "mcp47fvb12", (kernel_ulong_t)&mcp47fvb12_chip_features },
+ { "mcp47fvb14", (kernel_ulong_t)&mcp47fvb14_chip_features },
+ { "mcp47fvb18", (kernel_ulong_t)&mcp47fvb18_chip_features },
+ { "mcp47fvb21", (kernel_ulong_t)&mcp47fvb21_chip_features },
+ { "mcp47fvb22", (kernel_ulong_t)&mcp47fvb22_chip_features },
+ { "mcp47fvb24", (kernel_ulong_t)&mcp47fvb24_chip_features },
+ { "mcp47fvb28", (kernel_ulong_t)&mcp47fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp47feb02_id);
+
+static const struct of_device_id mcp47feb02_of_match[] = {
+ { .compatible = "microchip,mcp47feb01", .data = &mcp47feb01_chip_features },
+ { .compatible = "microchip,mcp47feb02", .data = &mcp47feb02_chip_features },
+ { .compatible = "microchip,mcp47feb04", .data = &mcp47feb04_chip_features },
+ { .compatible = "microchip,mcp47feb08", .data = &mcp47feb08_chip_features },
+ { .compatible = "microchip,mcp47feb11", .data = &mcp47feb11_chip_features },
+ { .compatible = "microchip,mcp47feb12", .data = &mcp47feb12_chip_features },
+ { .compatible = "microchip,mcp47feb14", .data = &mcp47feb14_chip_features },
+ { .compatible = "microchip,mcp47feb18", .data = &mcp47feb18_chip_features },
+ { .compatible = "microchip,mcp47feb21", .data = &mcp47feb21_chip_features },
+ { .compatible = "microchip,mcp47feb22", .data = &mcp47feb22_chip_features },
+ { .compatible = "microchip,mcp47feb24", .data = &mcp47feb24_chip_features },
+ { .compatible = "microchip,mcp47feb28", .data = &mcp47feb28_chip_features },
+ { .compatible = "microchip,mcp47fvb01", .data = &mcp47fvb01_chip_features },
+ { .compatible = "microchip,mcp47fvb02", .data = &mcp47fvb02_chip_features },
+ { .compatible = "microchip,mcp47fvb04", .data = &mcp47fvb04_chip_features },
+ { .compatible = "microchip,mcp47fvb08", .data = &mcp47fvb08_chip_features },
+ { .compatible = "microchip,mcp47fvb11", .data = &mcp47fvb11_chip_features },
+ { .compatible = "microchip,mcp47fvb12", .data = &mcp47fvb12_chip_features },
+ { .compatible = "microchip,mcp47fvb14", .data = &mcp47fvb14_chip_features },
+ { .compatible = "microchip,mcp47fvb18", .data = &mcp47fvb18_chip_features },
+ { .compatible = "microchip,mcp47fvb21", .data = &mcp47fvb21_chip_features },
+ { .compatible = "microchip,mcp47fvb22", .data = &mcp47fvb22_chip_features },
+ { .compatible = "microchip,mcp47fvb24", .data = &mcp47fvb24_chip_features },
+ { .compatible = "microchip,mcp47fvb28", .data = &mcp47fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp47feb02_of_match);
+
+static struct i2c_driver mcp47feb02_driver = {
+ .driver = {
+ .name = "mcp47feb02",
+ .of_match_table = mcp47feb02_of_match,
+ .pm = pm_sleep_ptr(&mcp47feb02_pm_ops),
+ },
+ .probe = mcp47feb02_probe,
+ .id_table = mcp47feb02_id,
+};
+module_i2c_driver(mcp47feb02_driver);
+
+MODULE_AUTHOR("Ariana Lazar <ariana.lazar@microchip.com>");
+MODULE_DESCRIPTION("IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c
index 08833b7035e4..fa686f785fa4 100644
--- a/drivers/iio/frequency/adf4377.c
+++ b/drivers/iio/frequency/adf4377.c
@@ -8,7 +8,9 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
@@ -435,9 +437,14 @@ struct adf4377_state {
struct gpio_desc *gpio_ce;
struct gpio_desc *gpio_enclk1;
struct gpio_desc *gpio_enclk2;
+ struct clk *clk;
+ struct clk *clkout;
+ struct clk_hw hw;
u8 buf[2] __aligned(IIO_DMA_MINALIGN);
};
+#define to_adf4377_state(h) container_of(h, struct adf4377_state, hw)
+
static const char * const adf4377_muxout_modes[] = {
[ADF4377_MUXOUT_HIGH_Z] = "high_z",
[ADF4377_MUXOUT_LKDET] = "lock_detect",
@@ -929,6 +936,110 @@ static int adf4377_freq_change(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
+static unsigned long adf4377_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+ u64 freq;
+ int ret;
+
+ ret = adf4377_get_freq(st, &freq);
+ if (ret)
+ return 0;
+
+ return freq;
+}
+
+static int adf4377_clk_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+
+ return adf4377_set_freq(st, rate);
+}
+
+static int adf4377_clk_prepare(struct clk_hw *hw)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+
+ return regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
+ ADF4377_001A_PD_CLKOUT2_MSK,
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 0) |
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 0));
+}
+
+static void adf4377_clk_unprepare(struct clk_hw *hw)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+
+ regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
+ ADF4377_001A_PD_CLKOUT2_MSK,
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 1) |
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 1));
+}
+
+static int adf4377_clk_is_prepared(struct clk_hw *hw)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+ unsigned int readval;
+ int ret;
+
+ ret = regmap_read(st->regmap, 0x1a, &readval);
+ if (ret)
+ return ret;
+
+ return !(readval & (ADF4377_001A_PD_CLKOUT1_MSK | ADF4377_001A_PD_CLKOUT2_MSK));
+}
+
+static const struct clk_ops adf4377_clk_ops = {
+ .recalc_rate = adf4377_clk_recalc_rate,
+ .set_rate = adf4377_clk_set_rate,
+ .prepare = adf4377_clk_prepare,
+ .unprepare = adf4377_clk_unprepare,
+ .is_prepared = adf4377_clk_is_prepared,
+};
+
+static int adf4377_clk_register(struct adf4377_state *st)
+{
+ struct spi_device *spi = st->spi;
+ struct device *dev = &spi->dev;
+ struct clk_init_data init;
+ struct clk_parent_data parent_data;
+ int ret;
+
+ if (!device_property_present(dev, "#clock-cells"))
+ return 0;
+
+ ret = device_property_read_string(dev, "clock-output-names", &init.name);
+ if (ret) {
+ init.name = devm_kasprintf(dev, GFP_KERNEL, "%pfw-clk",
+ dev_fwnode(dev));
+ if (!init.name)
+ return -ENOMEM;
+ }
+
+ parent_data.fw_name = "ref_in";
+
+ init.ops = &adf4377_clk_ops;
+ init.parent_data = &parent_data;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT;
+
+ st->hw.init = &init;
+ ret = devm_clk_hw_register(dev, &st->hw);
+ if (ret)
+ return ret;
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &st->hw);
+ if (ret)
+ return ret;
+
+ st->clkout = st->hw.clk;
+
+ return 0;
+}
+
static const struct adf4377_chip_info adf4377_chip_info = {
.name = "adf4377",
.has_gpio_enclk2 = true,
@@ -958,8 +1069,6 @@ static int adf4377_probe(struct spi_device *spi)
indio_dev->info = &adf4377_info;
indio_dev->name = "adf4377";
- indio_dev->channels = adf4377_channels;
- indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
st->regmap = regmap;
st->spi = spi;
@@ -979,6 +1088,15 @@ static int adf4377_probe(struct spi_device *spi)
if (ret)
return ret;
+ ret = adf4377_clk_register(st);
+ if (ret)
+ return ret;
+
+ if (!st->clkout) {
+ indio_dev->channels = adf4377_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
+ }
+
return devm_iio_device_register(&spi->dev, indio_dev);
}
diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c
index 8fcb41f45baa..3efe385ebedc 100644
--- a/drivers/iio/gyro/adxrs290.c
+++ b/drivers/iio/gyro/adxrs290.c
@@ -597,7 +597,7 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev)
ret = devm_request_irq(&st->spi->dev, st->spi->irq,
&iio_trigger_generic_data_rdy_poll,
- IRQF_ONESHOT, "adxrs290_irq", st->dready_trig);
+ IRQF_NO_THREAD, "adxrs290_irq", st->dready_trig);
if (ret < 0)
return dev_err_probe(&st->spi->dev, ret,
"request irq %d failed\n", st->spi->irq);
diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c
index a624400a239c..cf97adfa9727 100644
--- a/drivers/iio/gyro/itg3200_buffer.c
+++ b/drivers/iio/gyro/itg3200_buffer.c
@@ -118,11 +118,9 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev)
if (!st->trig)
return -ENOMEM;
- ret = request_irq(st->i2c->irq,
- &iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_RISING,
- "itg3200_data_rdy",
- st->trig);
+ ret = request_irq(st->i2c->irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "itg3200_data_rdy", st->trig);
if (ret)
goto error_free_trig;
diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c
index cd8a2dae56cd..bfe95ec1abda 100644
--- a/drivers/iio/gyro/itg3200_core.c
+++ b/drivers/iio/gyro/itg3200_core.c
@@ -93,6 +93,8 @@ static int itg3200_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
reg = (u8)chan->address;
ret = itg3200_read_reg_s16(indio_dev, reg, val);
+ if (ret)
+ return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c
index 67ae7d1012bc..ee2fcd20545d 100644
--- a/drivers/iio/gyro/mpu3050-core.c
+++ b/drivers/iio/gyro/mpu3050-core.c
@@ -1162,10 +1162,8 @@ int mpu3050_common_probe(struct device *dev,
mpu3050->regs[1].supply = mpu3050_reg_vlogic;
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(mpu3050->regs),
mpu3050->regs);
- if (ret) {
- dev_err(dev, "Cannot get regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot get regulators\n");
ret = mpu3050_power_up(mpu3050);
if (ret)
diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c
index 0e5a512e3bb8..d358f4d5e5da 100644
--- a/drivers/iio/health/afe4403.c
+++ b/drivers/iio/health/afe4403.c
@@ -540,11 +540,10 @@ static int afe4403_probe(struct spi_device *spi)
return ret;
}
- ret = devm_request_threaded_irq(dev, afe->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL, IRQF_ONESHOT,
- AFE4403_DRIVER_NAME,
- afe->trig);
+ ret = devm_request_irq(dev, afe->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, AFE4403_DRIVER_NAME,
+ afe->trig);
if (ret) {
dev_err(dev, "Unable to request IRQ\n");
return ret;
diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c
index 768d794e574b..032da52a96d0 100644
--- a/drivers/iio/health/afe4404.c
+++ b/drivers/iio/health/afe4404.c
@@ -547,11 +547,10 @@ static int afe4404_probe(struct i2c_client *client)
return ret;
}
- ret = devm_request_threaded_irq(dev, afe->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL, IRQF_ONESHOT,
- AFE4404_DRIVER_NAME,
- afe->trig);
+ ret = devm_request_irq(dev, afe->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, AFE4404_DRIVER_NAME,
+ afe->trig);
if (ret) {
dev_err(dev, "Unable to request IRQ\n");
return ret;
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
index 3d441013893c..7dfdb5eb305e 100644
--- a/drivers/iio/health/max30100.c
+++ b/drivers/iio/health/max30100.c
@@ -417,13 +417,7 @@ static int max30100_read_raw(struct iio_dev *indio_dev,
* Temperature reading can only be acquired while engine
* is running
*/
- if (iio_device_claim_buffer_mode(indio_dev)) {
- /*
- * Replacing -EBUSY or other error code
- * returned by iio_device_claim_buffer_mode()
- * because user space may rely on the current
- * one.
- */
+ if (!iio_device_try_claim_buffer_mode(indio_dev)) {
ret = -EAGAIN;
} else {
ret = max30100_get_temp(data, val);
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index a48c0881a4c7..47da44efd68b 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -467,44 +467,29 @@ static int max30102_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct max30102_data *data = iio_priv(indio_dev);
- int ret = -EINVAL;
+ int ret;
switch (mask) {
- case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_RAW: {
/*
* Temperature reading can only be acquired when not in
* shutdown; leave shutdown briefly when buffer not running
*/
-any_mode_retry:
- if (iio_device_claim_buffer_mode(indio_dev)) {
- /*
- * This one is a *bit* hacky. If we cannot claim buffer
- * mode, then try direct mode so that we make sure
- * things cannot concurrently change. And we just keep
- * trying until we get one of the modes...
- */
- if (!iio_device_claim_direct(indio_dev))
- goto any_mode_retry;
-
- ret = max30102_get_temp(data, val, true);
- iio_device_release_direct(indio_dev);
- } else {
- ret = max30102_get_temp(data, val, false);
- iio_device_release_buffer_mode(indio_dev);
- }
+ IIO_DEV_GUARD_CURRENT_MODE(indio_dev);
+
+ ret = max30102_get_temp(data, val, !iio_buffer_enabled(indio_dev));
if (ret)
return ret;
- ret = IIO_VAL_INT;
- break;
+ return IIO_VAL_INT;
+ }
case IIO_CHAN_INFO_SCALE:
*val = 1000; /* 62.5 */
*val2 = 16;
- ret = IIO_VAL_FRACTIONAL;
- break;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
}
-
- return ret;
}
static const struct iio_info max30102_info = {
diff --git a/drivers/iio/imu/bmi270/bmi270_i2c.c b/drivers/iio/imu/bmi270/bmi270_i2c.c
index b909a421ad01..b92da4e0776f 100644
--- a/drivers/iio/imu/bmi270/bmi270_i2c.c
+++ b/drivers/iio/imu/bmi270/bmi270_i2c.c
@@ -37,6 +37,7 @@ static const struct i2c_device_id bmi270_i2c_id[] = {
{ "bmi270", (kernel_ulong_t)&bmi270_chip_info },
{ }
};
+MODULE_DEVICE_TABLE(i2c, bmi270_i2c_id);
static const struct acpi_device_id bmi270_acpi_match[] = {
/* GPD Win Mini, Aya Neo AIR Pro, OXP Mini Pro, etc. */
@@ -45,12 +46,14 @@ static const struct acpi_device_id bmi270_acpi_match[] = {
{ "BMI0260", (kernel_ulong_t)&bmi260_chip_info },
{ }
};
+MODULE_DEVICE_TABLE(acpi, bmi270_acpi_match);
static const struct of_device_id bmi270_of_match[] = {
{ .compatible = "bosch,bmi260", .data = &bmi260_chip_info },
{ .compatible = "bosch,bmi270", .data = &bmi270_chip_info },
{ }
};
+MODULE_DEVICE_TABLE(of, bmi270_of_match);
static struct i2c_driver bmi270_i2c_driver = {
.driver = {
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
index 30f6a9595eea..727b03d541a5 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
@@ -59,10 +59,7 @@ int inv_icm42600_temp_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (!iio_device_claim_direct(indio_dev))
- return -EBUSY;
ret = inv_icm42600_temp_read(st, &temp);
- iio_device_release_direct(indio_dev);
if (ret)
return ret;
*val = temp;
diff --git a/drivers/iio/imu/smi330/smi330_core.c b/drivers/iio/imu/smi330/smi330_core.c
index 7564f12543e0..7ec5ae7f521a 100644
--- a/drivers/iio/imu/smi330/smi330_core.c
+++ b/drivers/iio/imu/smi330/smi330_core.c
@@ -67,10 +67,6 @@
#define SMI330_CHIP_ID 0x42
#define SMI330_SOFT_RESET_DELAY 2000
-/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
-#define smi330_field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
-#define smi330_field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
-
#define SMI330_ACCEL_CHANNEL(_axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -361,7 +357,7 @@ static int smi330_get_sensor_config(struct smi330_data *data,
if (ret)
return ret;
- reg_val = smi330_field_get(attr->mask, reg_val);
+ reg_val = field_get(attr->mask, reg_val);
if (attr->type == IIO_VAL_INT) {
for (i = 0; i < attr->len; i++) {
@@ -410,7 +406,7 @@ static int smi330_set_sensor_config(struct smi330_data *data,
if (ret)
return ret;
- reg_val = smi330_field_prep(attr->mask, reg_val);
+ reg_val = field_prep(attr->mask, reg_val);
ret = regmap_update_bits(data->regmap, reg, attr->mask, reg_val);
if (ret)
return ret;
@@ -475,7 +471,6 @@ static int smi330_read_avail(struct iio_dev *indio_dev,
*vals = smi330_average_attr.vals;
*length = smi330_average_attr.len;
*type = smi330_average_attr.type;
- *type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = smi330_bandwidth_attr.vals;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index 6405a5367d76..07b1773c87bd 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -79,10 +79,11 @@ enum st_lsm6dsx_hw_id {
#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \
* ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
+#define st_lsm6dsx_field_get(mask, reg) ((reg & mask) >> __ffs(mask))
-#define ST_LSM6DSX_CHANNEL_ACC(chan_type, addr, mod, scan_idx) \
+#define ST_LSM6DSX_CHANNEL_ACC(addr, mod, scan_idx, events) \
{ \
- .type = chan_type, \
+ .type = IIO_ACCEL, \
.address = addr, \
.modified = 1, \
.channel2 = mod, \
@@ -96,9 +97,9 @@ enum st_lsm6dsx_hw_id {
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
- .event_spec = &st_lsm6dsx_event, \
+ .event_spec = events, \
+ .num_event_specs = ARRAY_SIZE(events), \
.ext_info = st_lsm6dsx_ext_info, \
- .num_event_specs = 1, \
}
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
@@ -260,14 +261,31 @@ struct st_lsm6dsx_shub_settings {
u8 pause;
};
+enum st_lsm6dsx_event_id {
+ ST_LSM6DSX_EVENT_WAKEUP,
+ ST_LSM6DSX_EVENT_TAP,
+ ST_LSM6DSX_EVENT_MAX
+};
+
+struct st_lsm6dsx_event_src {
+ struct st_lsm6dsx_reg value;
+ struct st_lsm6dsx_reg x_value;
+ struct st_lsm6dsx_reg y_value;
+ struct st_lsm6dsx_reg z_value;
+ u8 enable_mask;
+ u8 enable_axis_reg;
+ u8 enable_x_mask;
+ u8 enable_y_mask;
+ u8 enable_z_mask;
+ struct st_lsm6dsx_reg status;
+ u8 status_x_mask;
+ u8 status_y_mask;
+ u8 status_z_mask;
+};
+
struct st_lsm6dsx_event_settings {
struct st_lsm6dsx_reg enable_reg;
- struct st_lsm6dsx_reg wakeup_reg;
- u8 wakeup_src_reg;
- u8 wakeup_src_status_mask;
- u8 wakeup_src_z_mask;
- u8 wakeup_src_y_mask;
- u8 wakeup_src_x_mask;
+ struct st_lsm6dsx_event_src sources[ST_LSM6DSX_EVENT_MAX];
};
enum st_lsm6dsx_sensor_id {
@@ -353,8 +371,8 @@ struct st_lsm6dsx_settings {
struct {
struct st_lsm6dsx_reg irq1;
struct st_lsm6dsx_reg irq2;
- struct st_lsm6dsx_reg irq1_func;
- struct st_lsm6dsx_reg irq2_func;
+ u8 irq1_func;
+ u8 irq2_func;
struct st_lsm6dsx_reg lir;
struct st_lsm6dsx_reg clear_on_read;
struct st_lsm6dsx_reg hla;
@@ -430,7 +448,6 @@ struct st_lsm6dsx_sensor {
* @sip: Total number of samples (acc/gyro/ts) in a given pattern.
* @buff: Device read buffer.
* @irq_routing: pointer to interrupt routing configuration.
- * @event_threshold: wakeup event threshold.
* @enable_event: enabled event bitmask.
* @iio_devs: Pointers to acc/gyro iio_dev instances.
* @settings: Pointer to the specific sensor settings in use.
@@ -453,9 +470,8 @@ struct st_lsm6dsx_hw {
u8 ts_sip;
u8 sip;
- const struct st_lsm6dsx_reg *irq_routing;
- u8 event_threshold;
- u8 enable_event;
+ u8 irq_routing;
+ u8 enable_event[ST_LSM6DSX_EVENT_MAX];
u8 *buff;
@@ -471,13 +487,6 @@ struct st_lsm6dsx_hw {
} scan[ST_LSM6DSX_ID_MAX];
};
-static __maybe_unused const struct iio_event_spec st_lsm6dsx_event = {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_EITHER,
- .mask_separate = BIT(IIO_EV_INFO_VALUE) |
- BIT(IIO_EV_INFO_ENABLE)
-};
-
static __maybe_unused const unsigned long st_lsm6dsx_available_scan_masks[] = {
0x7, 0x0,
};
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index dc78227952a7..450cb5b47346 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -94,10 +94,41 @@
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
+static const struct iio_event_spec st_lsm6dsx_ev_motion[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_event_spec st_lsm6dsx_ev_motion_tap[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_GESTURE,
+ .dir = IIO_EV_DIR_SINGLETAP,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
- ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
- ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
- ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
+ ST_LSM6DSX_CHANNEL_ACC(0x28, IIO_MOD_X, 0, st_lsm6dsx_ev_motion),
+ ST_LSM6DSX_CHANNEL_ACC(0x2a, IIO_MOD_Y, 1, st_lsm6dsx_ev_motion),
+ ST_LSM6DSX_CHANNEL_ACC(0x2c, IIO_MOD_Z, 2, st_lsm6dsx_ev_motion),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec st_lsm6dsx_acc_tap_channels[] = {
+ ST_LSM6DSX_CHANNEL_ACC(0x28, IIO_MOD_X, 0, st_lsm6dsx_ev_motion_tap),
+ ST_LSM6DSX_CHANNEL_ACC(0x2a, IIO_MOD_Y, 1, st_lsm6dsx_ev_motion_tap),
+ ST_LSM6DSX_CHANNEL_ACC(0x2c, IIO_MOD_Z, 2, st_lsm6dsx_ev_motion_tap),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
@@ -326,14 +357,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -386,15 +411,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
},
.event_settings = {
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -492,14 +524,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -552,15 +578,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
},
.event_settings = {
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -688,14 +721,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -789,15 +816,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -937,14 +971,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(6),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -1028,15 +1056,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5b,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1152,14 +1187,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(6),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -1211,15 +1240,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1248,8 +1284,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.channels = {
[ST_LSM6DSX_ID_ACC] = {
- .chan = st_lsm6dsx_acc_channels,
- .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ .chan = st_lsm6dsx_acc_tap_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_tap_channels),
},
[ST_LSM6DSX_ID_GYRO] = {
.chan = st_lsm6dsx_gyro_channels,
@@ -1329,14 +1365,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x03,
.mask = BIT(4),
@@ -1419,15 +1449,48 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x50,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5b,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x45,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
+ [ST_LSM6DSX_EVENT_TAP] = {
+ .x_value = {
+ .addr = 0x57,
+ .mask = GENMASK(4, 0),
+ },
+ .y_value = {
+ .addr = 0x58,
+ .mask = GENMASK(4, 0),
+ },
+ .z_value = {
+ .addr = 0x59,
+ .mask = GENMASK(4, 0),
+ },
+ .enable_mask = BIT(6),
+ .enable_axis_reg = 0x56,
+ .enable_x_mask = BIT(3),
+ .enable_y_mask = BIT(2),
+ .enable_z_mask = BIT(1),
+ .status = {
+ .addr = 0x46,
+ .mask = BIT(5),
+ },
+ .status_x_mask = BIT(2),
+ .status_y_mask = BIT(1),
+ .status_z_mask = BIT(0),
+ },
},
- .wakeup_src_reg = 0x45,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1754,20 +1817,25 @@ __st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
}
static int
-st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor, bool enable)
+st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor)
{
struct st_lsm6dsx_hw *hw = sensor->hw;
+ int event;
- if (sensor->id == ST_LSM6DSX_ID_GYRO || enable)
+ if (sensor->id != ST_LSM6DSX_ID_ACC)
return 0;
- return hw->enable_event;
+ for (event = 0; event < ST_LSM6DSX_EVENT_MAX; event++) {
+ if (hw->enable_event[event])
+ return true;
+ }
+ return false;
}
int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
bool enable)
{
- if (st_lsm6dsx_check_events(sensor, enable))
+ if (st_lsm6dsx_check_events(sensor))
return 0;
return __st_lsm6dsx_sensor_set_enable(sensor, enable);
@@ -1795,11 +1863,9 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
if (err < 0)
return err;
- if (!hw->enable_event) {
- err = st_lsm6dsx_sensor_set_enable(sensor, false);
- if (err < 0)
- return err;
- }
+ err = st_lsm6dsx_sensor_set_enable(sensor, false);
+ if (err < 0)
+ return err;
*val = (s16)le16_to_cpu(data);
@@ -1876,28 +1942,106 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
return err;
}
-static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, bool state)
+static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw,
+ enum st_lsm6dsx_event_id event, int axis,
+ bool state)
{
- const struct st_lsm6dsx_reg *reg;
+ const struct st_lsm6dsx_event_src *src;
unsigned int data;
int err;
+ u8 old_enable, new_enable;
- if (!hw->settings->irq_config.irq1_func.addr)
+ if (!hw->irq_routing)
return -ENOTSUPP;
- reg = &hw->settings->event_settings.enable_reg;
- if (reg->addr) {
- data = ST_LSM6DSX_SHIFT_VAL(state, reg->mask);
- err = st_lsm6dsx_update_bits_locked(hw, reg->addr,
- reg->mask, data);
- if (err < 0)
- return err;
+ /* Enable/disable event interrupt */
+ src = &hw->settings->event_settings.sources[event];
+ if (src->enable_axis_reg) {
+ u8 enable_mask;
+
+ switch (axis) {
+ case IIO_MOD_X:
+ enable_mask = src->enable_x_mask;
+ break;
+ case IIO_MOD_Y:
+ enable_mask = src->enable_y_mask;
+ break;
+ case IIO_MOD_Z:
+ enable_mask = src->enable_z_mask;
+ break;
+ default:
+ enable_mask = 0;
+ }
+ if (enable_mask) {
+ data = ST_LSM6DSX_SHIFT_VAL(state, enable_mask);
+ err = st_lsm6dsx_update_bits_locked(hw,
+ src->enable_axis_reg,
+ enable_mask, data);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /*
+ * If the set of axes for which the event source is enabled does not
+ * change from empty to non-empty or vice versa, there is nothing else
+ * to do.
+ */
+ old_enable = hw->enable_event[event];
+ new_enable = state ? (old_enable | BIT(axis)) :
+ (old_enable & ~BIT(axis));
+ if (!old_enable == !new_enable)
+ return 0;
+
+ data = ST_LSM6DSX_SHIFT_VAL(state, src->enable_mask);
+ return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing,
+ src->enable_mask, data);
+}
+
+static enum st_lsm6dsx_event_id
+st_lsm6dsx_get_event_id(enum iio_event_type type)
+{
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ return ST_LSM6DSX_EVENT_WAKEUP;
+ case IIO_EV_TYPE_GESTURE:
+ return ST_LSM6DSX_EVENT_TAP;
+ default:
+ return ST_LSM6DSX_EVENT_MAX;
+ }
+}
+
+static const struct st_lsm6dsx_reg *
+st_lsm6dsx_get_event_reg(struct st_lsm6dsx_hw *hw,
+ enum st_lsm6dsx_event_id event,
+ const struct iio_chan_spec *chan)
+{
+ const struct st_lsm6dsx_event_src *src;
+ const struct st_lsm6dsx_reg *reg;
+
+ src = &hw->settings->event_settings.sources[event];
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = &src->x_value;
+ break;
+ case IIO_MOD_Y:
+ reg = &src->y_value;
+ break;
+ case IIO_MOD_Z:
+ reg = &src->z_value;
+ break;
+ default:
+ return NULL;
}
+ if (reg->addr)
+ return reg;
- /* Enable wakeup interrupt */
- data = ST_LSM6DSX_SHIFT_VAL(state, hw->irq_routing->mask);
- return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing->addr,
- hw->irq_routing->mask, data);
+ /*
+ * The sensor does not support configuring this event source on a per
+ * axis basis: return the register to configure the event source for all
+ * axes.
+ */
+ return &src->value;
}
static int st_lsm6dsx_read_event(struct iio_dev *iio_dev,
@@ -1907,14 +2051,26 @@ static int st_lsm6dsx_read_event(struct iio_dev *iio_dev,
enum iio_event_info info,
int *val, int *val2)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
+ const struct st_lsm6dsx_reg *reg;
+ u8 data;
+ int err;
+
+ if (event == ST_LSM6DSX_EVENT_MAX)
+ return -EINVAL;
- if (type != IIO_EV_TYPE_THRESH)
+ reg = st_lsm6dsx_get_event_reg(hw, event, chan);
+ if (!reg)
return -EINVAL;
+ err = st_lsm6dsx_read_locked(hw, reg->addr, &data, sizeof(data));
+ if (err < 0)
+ return err;
+
*val2 = 0;
- *val = hw->event_threshold;
+ *val = st_lsm6dsx_field_get(reg->mask, data);
return IIO_VAL_INT;
}
@@ -1927,27 +2083,29 @@ st_lsm6dsx_write_event(struct iio_dev *iio_dev,
enum iio_event_info info,
int val, int val2)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
const struct st_lsm6dsx_reg *reg;
unsigned int data;
int err;
- if (type != IIO_EV_TYPE_THRESH)
+ if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
if (val < 0 || val > 31)
return -EINVAL;
- reg = &hw->settings->event_settings.wakeup_reg;
+ reg = st_lsm6dsx_get_event_reg(hw, event, chan);
+ if (!reg)
+ return -EINVAL;
+
data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
err = st_lsm6dsx_update_bits_locked(hw, reg->addr,
reg->mask, data);
if (err < 0)
return -EINVAL;
- hw->event_threshold = val;
-
return 0;
}
@@ -1957,13 +2115,56 @@ st_lsm6dsx_read_event_config(struct iio_dev *iio_dev,
enum iio_event_type type,
enum iio_event_direction dir)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
- if (type != IIO_EV_TYPE_THRESH)
+ if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
- return !!(hw->enable_event & BIT(chan->channel2));
+ return !!(hw->enable_event[event] & BIT(chan->channel2));
+}
+
+/**
+ * st_lsm6dsx_check_other_events - Check for enabled sensor events.
+ * @hw: Sensor hardware instance.
+ * @curr: Current event type.
+ *
+ * Return: whether any events other than @curr are enabled.
+ */
+static bool st_lsm6dsx_check_other_events(struct st_lsm6dsx_hw *hw,
+ enum st_lsm6dsx_event_id curr)
+{
+ enum st_lsm6dsx_event_id other;
+
+ for (other = 0; other < ST_LSM6DSX_EVENT_MAX; other++) {
+ if (other != curr && hw->enable_event[other])
+ return true;
+ }
+
+ return false;
+}
+
+static int st_lsm6dsx_events_enable(struct st_lsm6dsx_sensor *sensor,
+ bool state)
+{
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ const struct st_lsm6dsx_reg *reg;
+
+ reg = &hw->settings->event_settings.enable_reg;
+ if (reg->addr) {
+ int err;
+
+ err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
+ ST_LSM6DSX_SHIFT_VAL(state, reg->mask));
+ if (err)
+ return err;
+ }
+
+ if (state || !(hw->fifo_mask & BIT(sensor->id)))
+ return __st_lsm6dsx_sensor_set_enable(sensor, state);
+
+ return 0;
}
static int
@@ -1972,45 +2173,38 @@ st_lsm6dsx_write_event_config(struct iio_dev *iio_dev,
enum iio_event_type type,
enum iio_event_direction dir, bool state)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
u8 enable_event;
int err;
- if (type != IIO_EV_TYPE_THRESH)
+ if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
- if (state) {
- enable_event = hw->enable_event | BIT(chan->channel2);
-
- /* do not enable events if they are already enabled */
- if (hw->enable_event)
- goto out;
- } else {
- enable_event = hw->enable_event & ~BIT(chan->channel2);
-
- /* only turn off sensor if no events is enabled */
- if (enable_event)
- goto out;
- }
+ if (state)
+ enable_event = hw->enable_event[event] | BIT(chan->channel2);
+ else
+ enable_event = hw->enable_event[event] & ~BIT(chan->channel2);
/* stop here if no changes have been made */
- if (hw->enable_event == enable_event)
+ if (hw->enable_event[event] == enable_event)
return 0;
- err = st_lsm6dsx_event_setup(hw, state);
+ err = st_lsm6dsx_event_setup(hw, event, chan->channel2, state);
if (err < 0)
return err;
mutex_lock(&hw->conf_lock);
- if (enable_event || !(hw->fifo_mask & BIT(sensor->id)))
- err = __st_lsm6dsx_sensor_set_enable(sensor, state);
+ if (enable_event)
+ err = st_lsm6dsx_events_enable(sensor, true);
+ else if (!st_lsm6dsx_check_other_events(hw, event))
+ err = st_lsm6dsx_events_enable(sensor, false);
mutex_unlock(&hw->conf_lock);
if (err < 0)
return err;
-out:
- hw->enable_event = enable_event;
+ hw->enable_event[event] = enable_event;
return 0;
}
@@ -2151,11 +2345,11 @@ st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw,
switch (drdy_pin) {
case 1:
- hw->irq_routing = &hw->settings->irq_config.irq1_func;
+ hw->irq_routing = hw->settings->irq_config.irq1_func;
*drdy_reg = &hw->settings->irq_config.irq1;
break;
case 2:
- hw->irq_routing = &hw->settings->irq_config.irq2_func;
+ hw->irq_routing = hw->settings->irq_config.irq2_func;
*drdy_reg = &hw->settings->irq_config.irq2;
break;
default:
@@ -2414,53 +2608,70 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
}
static bool
-st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw)
+st_lsm6dsx_report_events(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_event_id id,
+ enum iio_event_type type, enum iio_event_direction dir)
{
const struct st_lsm6dsx_event_settings *event_settings;
+ const struct st_lsm6dsx_event_src *src;
int err, data;
s64 timestamp;
- if (!hw->enable_event)
+ if (!hw->enable_event[id])
return false;
event_settings = &hw->settings->event_settings;
- err = st_lsm6dsx_read_locked(hw, event_settings->wakeup_src_reg,
+ src = &event_settings->sources[id];
+ err = st_lsm6dsx_read_locked(hw, src->status.addr,
&data, sizeof(data));
if (err < 0)
return false;
timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
- if ((data & hw->settings->event_settings.wakeup_src_z_mask) &&
- (hw->enable_event & BIT(IIO_MOD_Z)))
+ if ((data & src->status_z_mask) &&
+ (hw->enable_event[id] & BIT(IIO_MOD_Z)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Z,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
+ type,
+ dir),
timestamp);
- if ((data & hw->settings->event_settings.wakeup_src_y_mask) &&
- (hw->enable_event & BIT(IIO_MOD_Y)))
+ if ((data & src->status_y_mask) &&
+ (hw->enable_event[id] & BIT(IIO_MOD_Y)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Y,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
+ type,
+ dir),
timestamp);
- if ((data & hw->settings->event_settings.wakeup_src_x_mask) &&
- (hw->enable_event & BIT(IIO_MOD_X)))
+ if ((data & src->status_x_mask) &&
+ (hw->enable_event[id] & BIT(IIO_MOD_X)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_X,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
+ type,
+ dir),
timestamp);
- return data & event_settings->wakeup_src_status_mask;
+ return data & src->status.mask;
+}
+
+static bool st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw)
+{
+ bool events_found;
+
+ events_found = st_lsm6dsx_report_events(hw, ST_LSM6DSX_EVENT_WAKEUP,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ events_found |= st_lsm6dsx_report_events(hw, ST_LSM6DSX_EVENT_TAP,
+ IIO_EV_TYPE_GESTURE,
+ IIO_EV_DIR_SINGLETAP);
+
+ return events_found;
}
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
@@ -2749,7 +2960,7 @@ static int st_lsm6dsx_suspend(struct device *dev)
continue;
if (device_may_wakeup(dev) &&
- sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) {
+ st_lsm6dsx_check_events(sensor)) {
/* Enable wake from IRQ */
enable_irq_wake(hw->irq);
continue;
@@ -2780,7 +2991,7 @@ static int st_lsm6dsx_resume(struct device *dev)
sensor = iio_priv(hw->iio_devs[i]);
if (device_may_wakeup(dev) &&
- sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event)
+ st_lsm6dsx_check_events(sensor))
disable_irq_wake(hw->irq);
if (!(hw->suspend_mask & BIT(sensor->id)))
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 117ffad4f376..819bfb9c289e 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -2174,88 +2174,34 @@ int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev,
EXPORT_SYMBOL_GPL(__devm_iio_device_register);
/**
- * __iio_device_claim_direct - Keep device in direct mode
- * @indio_dev: the iio_dev associated with the device
+ * __iio_dev_mode_lock() - Locks the current IIO device mode
+ * @indio_dev: the iio_dev associated with the device
*
- * If the device is in direct mode it is guaranteed to stay
- * that way until __iio_device_release_direct() is called.
+ * If the device is either in direct or buffer mode, it's guaranteed to stay
+ * that way until __iio_dev_mode_unlock() is called.
*
- * Use with __iio_device_release_direct().
+ * This function is not meant to be used directly by drivers to protect internal
+ * state; a driver should have it's own mechanisms for that matter.
*
- * Drivers should only call iio_device_claim_direct().
- *
- * Returns: true on success, false on failure.
- */
-bool __iio_device_claim_direct(struct iio_dev *indio_dev)
-{
- struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
-
- mutex_lock(&iio_dev_opaque->mlock);
-
- if (iio_buffer_enabled(indio_dev)) {
- mutex_unlock(&iio_dev_opaque->mlock);
- return false;
- }
- return true;
-}
-EXPORT_SYMBOL_GPL(__iio_device_claim_direct);
-
-/**
- * __iio_device_release_direct - releases claim on direct mode
- * @indio_dev: the iio_dev associated with the device
- *
- * Release the claim. Device is no longer guaranteed to stay
- * in direct mode.
- *
- * Drivers should only call iio_device_release_direct().
- *
- * Use with __iio_device_claim_direct()
+ * There are very few cases where a driver actually needs to lock the current
+ * mode unconditionally. It's recommended to use iio_device_claim_direct() or
+ * iio_device_try_claim_buffer_mode() pairs or related helpers instead.
*/
-void __iio_device_release_direct(struct iio_dev *indio_dev)
+void __iio_dev_mode_lock(struct iio_dev *indio_dev)
{
- mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock);
+ mutex_lock(&to_iio_dev_opaque(indio_dev)->mlock);
}
-EXPORT_SYMBOL_GPL(__iio_device_release_direct);
+EXPORT_SYMBOL_GPL(__iio_dev_mode_lock);
/**
- * iio_device_claim_buffer_mode - Keep device in buffer mode
- * @indio_dev: the iio_dev associated with the device
- *
- * If the device is in buffer mode it is guaranteed to stay
- * that way until iio_device_release_buffer_mode() is called.
- *
- * Use with iio_device_release_buffer_mode().
- *
- * Returns: 0 on success, -EBUSY on failure.
- */
-int iio_device_claim_buffer_mode(struct iio_dev *indio_dev)
-{
- struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
-
- mutex_lock(&iio_dev_opaque->mlock);
-
- if (iio_buffer_enabled(indio_dev))
- return 0;
-
- mutex_unlock(&iio_dev_opaque->mlock);
- return -EBUSY;
-}
-EXPORT_SYMBOL_GPL(iio_device_claim_buffer_mode);
-
-/**
- * iio_device_release_buffer_mode - releases claim on buffer mode
- * @indio_dev: the iio_dev associated with the device
- *
- * Release the claim. Device is no longer guaranteed to stay
- * in buffer mode.
- *
- * Use with iio_device_claim_buffer_mode().
+ * __iio_dev_mode_unlock() - Unlocks the current IIO device mode
+ * @indio_dev: the iio_dev associated with the device
*/
-void iio_device_release_buffer_mode(struct iio_dev *indio_dev)
+void __iio_dev_mode_unlock(struct iio_dev *indio_dev)
{
mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock);
}
-EXPORT_SYMBOL_GPL(iio_device_release_buffer_mode);
+EXPORT_SYMBOL_GPL(__iio_dev_mode_unlock);
/**
* iio_device_get_current_mode() - helper function providing read-only access to
diff --git a/drivers/iio/industrialio-sw-device.c b/drivers/iio/industrialio-sw-device.c
index cdaf30a3f233..3582524de0b8 100644
--- a/drivers/iio/industrialio-sw-device.c
+++ b/drivers/iio/industrialio-sw-device.c
@@ -148,7 +148,7 @@ static void device_drop_group(struct config_group *group,
config_item_put(item);
}
-static struct configfs_group_operations device_ops = {
+static const struct configfs_group_operations device_ops = {
.make_group = &device_make_group,
.drop_item = &device_drop_group,
};
diff --git a/drivers/iio/industrialio-sw-trigger.c b/drivers/iio/industrialio-sw-trigger.c
index d86a3305d9e8..334b6b10a784 100644
--- a/drivers/iio/industrialio-sw-trigger.c
+++ b/drivers/iio/industrialio-sw-trigger.c
@@ -152,7 +152,7 @@ static void trigger_drop_group(struct config_group *group,
config_item_put(item);
}
-static struct configfs_group_operations trigger_ops = {
+static const struct configfs_group_operations trigger_ops = {
.make_group = &trigger_make_group,
.drop_item = &trigger_drop_group,
};
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
index 1b4c18423048..b6ab726d1dae 100644
--- a/drivers/iio/light/isl29018.c
+++ b/drivers/iio/light/isl29018.c
@@ -273,9 +273,9 @@ static ssize_t in_illuminance_scale_available_show
mutex_lock(&chip->lock);
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
- len += sprintf(buf + len, "%d.%06d ",
- isl29018_scales[chip->int_time][i].scale,
- isl29018_scales[chip->int_time][i].uscale);
+ len += sysfs_emit_at(buf, len, "%d.%06d ",
+ isl29018_scales[chip->int_time][i].scale,
+ isl29018_scales[chip->int_time][i].uscale);
mutex_unlock(&chip->lock);
buf[len - 1] = '\n';
@@ -293,8 +293,8 @@ static ssize_t in_illuminance_integration_time_available_show
int len = 0;
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
- len += sprintf(buf + len, "0.%06d ",
- isl29018_int_utimes[chip->type][i]);
+ len += sysfs_emit_at(buf, len, "0.%06d ",
+ isl29018_int_utimes[chip->type][i]);
buf[len - 1] = '\n';
@@ -330,7 +330,7 @@ static ssize_t proximity_on_chip_ambient_infrared_suppression_show
* Return the "proximity scheme" i.e. if the chip does on chip
* infrared suppression (1 means perform on chip suppression)
*/
- return sprintf(buf, "%d\n", chip->prox_scheme);
+ return sysfs_emit(buf, "%d\n", chip->prox_scheme);
}
static ssize_t proximity_on_chip_ambient_infrared_suppression_store
diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c
index 981c704e7df5..d6e915ab355d 100644
--- a/drivers/iio/light/opt4060.c
+++ b/drivers/iio/light/opt4060.c
@@ -302,41 +302,23 @@ static int opt4060_set_driver_state(struct iio_dev *indio_dev,
bool continuous_irq)
{
struct opt4060_chip *chip = iio_priv(indio_dev);
- int ret = 0;
-any_mode_retry:
- if (iio_device_claim_buffer_mode(indio_dev)) {
- /*
- * This one is a *bit* hacky. If we cannot claim buffer mode,
- * then try direct mode so that we make sure things cannot
- * concurrently change. And we just keep trying until we get one
- * of the modes...
- */
- if (!iio_device_claim_direct(indio_dev))
- goto any_mode_retry;
- /*
- * This path means that we managed to claim direct mode. In
- * this case the buffer isn't enabled and it's okay to leave
- * continuous mode for sampling and/or irq.
- */
- ret = opt4060_set_state_common(chip, continuous_sampling,
- continuous_irq);
- iio_device_release_direct(indio_dev);
- return ret;
- } else {
- /*
- * This path means that we managed to claim buffer mode. In
- * this case the buffer is enabled and irq and sampling must go
- * to or remain continuous, but only if the trigger is from this
- * device.
- */
- if (!iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
- ret = opt4060_set_state_common(chip, true, true);
- else
- ret = opt4060_set_state_common(chip, continuous_sampling,
- continuous_irq);
- iio_device_release_buffer_mode(indio_dev);
- }
- return ret;
+
+ IIO_DEV_GUARD_CURRENT_MODE(indio_dev);
+
+ /*
+ * If we manage to claim buffer mode and we are using our own trigger,
+ * IRQ and sampling must go to or remain continuous.
+ */
+ if (iio_buffer_enabled(indio_dev) &&
+ iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
+ return opt4060_set_state_common(chip, true, true);
+
+ /*
+ * This path means that we managed to claim direct mode. In this case
+ * the buffer isn't enabled and it's okay to leave continuous mode for
+ * sampling and/or irq.
+ */
+ return opt4060_set_state_common(chip, continuous_sampling, continuous_irq);
}
/*
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
index f8eb251eca8d..ef0abc4499b7 100644
--- a/drivers/iio/light/si1145.c
+++ b/drivers/iio/light/si1145.c
@@ -1248,7 +1248,7 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev)
ret = devm_request_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
"si1145_irq",
trig);
if (ret < 0) {
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 4dbb2294a843..a36c23813679 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1078,20 +1078,17 @@ static int vcnl4010_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- case IIO_CHAN_INFO_SCALE:
- if (!iio_device_claim_direct(indio_dev))
+ case IIO_CHAN_INFO_SCALE: {
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
/* Protect against event capture. */
- if (vcnl4010_is_in_periodic_mode(data)) {
- ret = -EBUSY;
- } else {
- ret = vcnl4000_read_raw(indio_dev, chan, val, val2,
- mask);
- }
+ if (vcnl4010_is_in_periodic_mode(data))
+ return -EBUSY;
- iio_device_release_direct(indio_dev);
- return ret;
+ return vcnl4000_read_raw(indio_dev, chan, val, val2, mask);
+ }
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_PROXIMITY:
@@ -1148,36 +1145,27 @@ static int vcnl4010_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
- int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
- if (!iio_device_claim_direct(indio_dev))
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
/* Protect against event capture. */
- if (vcnl4010_is_in_periodic_mode(data)) {
- ret = -EBUSY;
- goto end;
- }
+ if (vcnl4010_is_in_periodic_mode(data))
+ return -EBUSY;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_PROXIMITY:
- ret = vcnl4010_write_proxy_samp_freq(data, val, val2);
- goto end;
+ return vcnl4010_write_proxy_samp_freq(data, val, val2);
default:
- ret = -EINVAL;
- goto end;
+ return -EINVAL;
}
default:
- ret = -EINVAL;
- goto end;
+ return -EINVAL;
}
-
-end:
- iio_device_release_direct(indio_dev);
- return ret;
}
static int vcnl4010_read_event(struct iio_dev *indio_dev,
@@ -1438,14 +1426,13 @@ static int vcnl4010_config_threshold_disable(struct vcnl4000_data *data)
static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
{
struct vcnl4000_data *data = iio_priv(indio_dev);
- int ret;
if (state) {
- if (!iio_device_claim_direct(indio_dev))
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
- ret = vcnl4010_config_threshold_enable(data);
- iio_device_release_direct(indio_dev);
- return ret;
+
+ return vcnl4010_config_threshold_enable(data);
} else {
return vcnl4010_config_threshold_disable(data);
}
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 81b812a29044..9345fb6d5317 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -139,6 +139,19 @@ config MMC35240
To compile this driver as a module, choose M here: the module
will be called mmc35240.
+config MMC5633
+ tristate "MEMSIC MMC5633 3-axis magnetic sensor"
+ select REGMAP_I2C
+ select REGMAP_I3C if I3C
+ depends on I2C
+ depends on I3C || !I3C
+ help
+ Say yes here to build support for the MEMSIC MMC5633 3-axis
+ magnetic sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mmc5633
+
config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index dfe970fcacb8..5bd227f8c120 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
obj-$(CONFIG_MMC35240) += mmc35240.o
+obj-$(CONFIG_MMC5633) += mmc5633.o
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
st_magn-y := st_magn_core.o
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index 3fd0171e5d69..d30315ad85de 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -581,7 +581,7 @@ static int ak8975_setup_irq(struct ak8975_data *data)
irq = gpiod_to_irq(data->eoc_gpiod);
rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING,
dev_name(&client->dev), data);
if (rc < 0) {
dev_err(&client->dev, "irq %d request failed: %d\n", irq, rc);
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
index 6a73f6e2f1f0..a022e1805dff 100644
--- a/drivers/iio/magnetometer/bmc150_magn.c
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -906,12 +906,9 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
goto err_poweroff;
}
- ret = request_threaded_irq(irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "bmc150_magn_event",
- data->dready_trig);
+ ret = request_irq(irq, iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "bmc150_magn_event", data->dready_trig);
if (ret < 0) {
dev_err(dev, "request irq %d failed\n", irq);
goto err_trigger_unregister;
diff --git a/drivers/iio/magnetometer/mmc5633.c b/drivers/iio/magnetometer/mmc5633.c
new file mode 100644
index 000000000000..9d8e27486963
--- /dev/null
+++ b/drivers/iio/magnetometer/mmc5633.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MMC5633 - MEMSIC 3-axis Magnetic Sensor
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ * Copyright (c) 2025, NXP
+ *
+ * IIO driver for MMC5633, base on mmc35240.c
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/i3c/device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#define MMC5633_REG_XOUT0 0x00
+#define MMC5633_REG_XOUT1 0x01
+#define MMC5633_REG_YOUT0 0x02
+#define MMC5633_REG_YOUT1 0x03
+#define MMC5633_REG_ZOUT0 0x04
+#define MMC5633_REG_ZOUT1 0x05
+#define MMC5633_REG_XOUT2 0x06
+#define MMC5633_REG_YOUT2 0x07
+#define MMC5633_REG_ZOUT2 0x08
+#define MMC5633_REG_TOUT 0x09
+
+#define MMC5633_REG_STATUS1 0x18
+#define MMC5633_REG_STATUS0 0x19
+#define MMC5633_REG_CTRL0 0x1b
+#define MMC5633_REG_CTRL1 0x1c
+#define MMC5633_REG_CTRL2 0x1d
+
+#define MMC5633_REG_ID 0x39
+
+#define MMC5633_STATUS1_MEAS_T_DONE_BIT BIT(7)
+#define MMC5633_STATUS1_MEAS_M_DONE_BIT BIT(6)
+
+#define MMC5633_CTRL0_CMM_FREQ_EN BIT(7)
+#define MMC5633_CTRL0_AUTO_ST_EN BIT(6)
+#define MMC5633_CTRL0_AUTO_SR_EN BIT(5)
+#define MMC5633_CTRL0_RESET BIT(4)
+#define MMC5633_CTRL0_SET BIT(3)
+#define MMC5633_CTRL0_MEAS_T BIT(1)
+#define MMC5633_CTRL0_MEAS_M BIT(0)
+
+#define MMC5633_CTRL1_BW_MASK GENMASK(1, 0)
+
+#define MMC5633_WAIT_SET_RESET_US (1 * USEC_PER_MSEC)
+
+#define MMC5633_HDR_CTRL0_MEAS_M 0x01
+#define MMC5633_HDR_CTRL0_MEAS_T 0x03
+#define MMC5633_HDR_CTRL0_SET 0x05
+#define MMC5633_HDR_CTRL0_RESET 0x07
+
+enum mmc5633_axis {
+ MMC5633_AXIS_X,
+ MMC5633_AXIS_Y,
+ MMC5633_AXIS_Z,
+ MMC5633_TEMPERATURE,
+};
+
+struct mmc5633_data {
+ struct regmap *regmap;
+ struct i3c_device *i3cdev;
+ struct mutex mutex; /* protect to finish one whole measurement */
+};
+
+static int mmc5633_samp_freq[][2] = {
+ { 1, 200000 },
+ { 2, 0 },
+ { 3, 500000 },
+ { 6, 600000 },
+};
+
+#define MMC5633_CHANNEL(_axis) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## _axis, \
+ .address = MMC5633_AXIS_ ## _axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mmc5633_channels[] = {
+ MMC5633_CHANNEL(X),
+ MMC5633_CHANNEL(Y),
+ MMC5633_CHANNEL(Z),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = MMC5633_TEMPERATURE,
+ },
+};
+
+static int mmc5633_get_samp_freq_index(struct mmc5633_data *data,
+ int val, int val2)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mmc5633_samp_freq); i++)
+ if (mmc5633_samp_freq[i][0] == val &&
+ mmc5633_samp_freq[i][1] == val2)
+ return i;
+ return -EINVAL;
+}
+
+static int mmc5633_init(struct mmc5633_data *data)
+{
+ unsigned int reg_id;
+ int ret;
+
+ ret = regmap_read(data->regmap, MMC5633_REG_ID, &reg_id);
+ if (ret)
+ return dev_err_probe(regmap_get_device(data->regmap), ret,
+ "Error reading product id\n");
+
+ /*
+ * Make sure we restore sensor characteristics, by doing
+ * a SET/RESET sequence, the axis polarity being naturally
+ * aligned after RESET.
+ */
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_SET);
+ if (ret)
+ return ret;
+
+ /*
+ * Minimum time interval between SET or RESET to other operations is
+ * 1ms according to Operating Timing Diagram in datasheet.
+ */
+ fsleep(MMC5633_WAIT_SET_RESET_US);
+
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_RESET);
+ if (ret)
+ return ret;
+
+ /* set default sampling frequency */
+ return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
+ MMC5633_CTRL1_BW_MASK,
+ FIELD_PREP(MMC5633_CTRL1_BW_MASK, 0));
+}
+
+static int mmc5633_take_measurement(struct mmc5633_data *data, int address)
+{
+ unsigned int reg_status, val;
+ int ret;
+
+ val = (address == MMC5633_TEMPERATURE) ? MMC5633_CTRL0_MEAS_T : MMC5633_CTRL0_MEAS_M;
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, val);
+ if (ret < 0)
+ return ret;
+
+ val = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
+ ret = regmap_read_poll_timeout(data->regmap, MMC5633_REG_STATUS1, reg_status,
+ reg_status & val,
+ 10 * USEC_PER_MSEC,
+ 100 * 10 * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(regmap_get_device(data->regmap), "data not ready\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool mmc5633_is_support_hdr(struct mmc5633_data *data)
+{
+ if (!data->i3cdev)
+ return false;
+
+ return i3c_device_get_supported_xfer_mode(data->i3cdev) & BIT(I3C_HDR_DDR);
+}
+
+static int mmc5633_read_measurement(struct mmc5633_data *data, int address, void *buf, size_t sz)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ u8 data_cmd[2], status[2];
+ unsigned int val, ready;
+ int ret;
+
+ if (mmc5633_is_support_hdr(data)) {
+ struct i3c_xfer xfers_wr_cmd[] = {
+ {
+ .cmd = 0x3b,
+ .len = 2,
+ .data.out = data_cmd,
+ }
+ };
+ struct i3c_xfer xfers_rd_sta_cmd[] = {
+ {
+ .cmd = 0x23 | BIT(7), /* RDSTA CMD */
+ .len = 2,
+ .data.in = status,
+ },
+ };
+ struct i3c_xfer xfers_rd_data_cmd[] = {
+ {
+ .cmd = 0x22 | BIT(7), /* RDLONG CMD */
+ .len = sz,
+ .data.in = buf,
+ },
+ };
+
+ data_cmd[0] = 0;
+ data_cmd[1] = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_HDR_CTRL0_MEAS_T : MMC5633_HDR_CTRL0_MEAS_M;
+
+ ret = i3c_device_do_xfers(data->i3cdev, xfers_wr_cmd,
+ ARRAY_SIZE(xfers_wr_cmd), I3C_HDR_DDR);
+ if (ret < 0)
+ return ret;
+
+ ready = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
+ ret = read_poll_timeout(i3c_device_do_xfers, val,
+ val || (status[0] & ready),
+ 10 * USEC_PER_MSEC,
+ 100 * 10 * USEC_PER_MSEC, 0,
+ data->i3cdev, xfers_rd_sta_cmd,
+ ARRAY_SIZE(xfers_rd_sta_cmd), I3C_HDR_DDR);
+ if (ret) {
+ dev_err(dev, "data not ready\n");
+ return ret;
+ }
+ if (val) {
+ dev_err(dev, "i3c transfer error\n");
+ return val;
+ }
+ return i3c_device_do_xfers(data->i3cdev, xfers_rd_data_cmd,
+ ARRAY_SIZE(xfers_rd_data_cmd), I3C_HDR_DDR);
+ }
+
+ /* Fallback to use SDR/I2C mode */
+ ret = mmc5633_take_measurement(data, address);
+ if (ret < 0)
+ return ret;
+
+ if (address == MMC5633_TEMPERATURE)
+ /*
+ * Put tempeature to last byte of buff to align HDR case.
+ * I3C will early terminate data read if previous data is not
+ * available.
+ */
+ return regmap_bulk_read(data->regmap, MMC5633_REG_TOUT, buf + sz - 1, 1);
+
+ return regmap_bulk_read(data->regmap, MMC5633_REG_XOUT0, buf, sz);
+}
+
+/* X,Y,Z 3 channels, each channel has 3 byte and TEMP */
+#define MMC5633_ALL_SIZE (3 * 3 + 1)
+
+static int mmc5633_get_raw(struct mmc5633_data *data, int index, unsigned char *buf, int *val)
+{
+ if (index == MMC5633_TEMPERATURE) {
+ *val = buf[MMC5633_ALL_SIZE - 1];
+ return 0;
+ }
+ /*
+ * X[19..12] X[11..4] Y[19..12] Y[11..4] Z[19..12] Z[11..4] X[3..0] Y[3..0] Z[3..0]
+ */
+ *val = get_unaligned_be16(buf + 2 * index) << 4;
+ *val |= buf[index + 6] >> 4;
+
+ return 0;
+}
+
+static int mmc5633_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct mmc5633_data *data = iio_priv(indio_dev);
+ char buf[MMC5633_ALL_SIZE];
+ unsigned int reg, i;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ scoped_guard(mutex, &data->mutex) {
+ ret = mmc5633_read_measurement(data, chan->address, buf, MMC5633_ALL_SIZE);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = mmc5633_get_raw(data, chan->address, buf, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ *val = 0;
+ *val2 = 62500;
+ } else {
+ *val = 0;
+ *val2 = 800000000; /* 0.8C */
+ }
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ *val = -75;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ scoped_guard(mutex, &data->mutex) {
+ ret = regmap_read(data->regmap, MMC5633_REG_CTRL1, &reg);
+ if (ret < 0)
+ return ret;
+ }
+
+ i = FIELD_GET(MMC5633_CTRL1_BW_MASK, reg);
+ if (i >= ARRAY_SIZE(mmc5633_samp_freq))
+ return -EINVAL;
+
+ *val = mmc5633_samp_freq[i][0];
+ *val2 = mmc5633_samp_freq[i][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mmc5633_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct mmc5633_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ ret = mmc5633_get_samp_freq_index(data, val, val2);
+ if (ret < 0)
+ return ret;
+
+ guard(mutex)(&data->mutex);
+
+ return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
+ MMC5633_CTRL1_BW_MASK,
+ FIELD_PREP(MMC5633_CTRL1_BW_MASK, ret));
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mmc5633_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)mmc5633_samp_freq;
+ *length = ARRAY_SIZE(mmc5633_samp_freq) * 2;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mmc5633_info = {
+ .read_raw = mmc5633_read_raw,
+ .write_raw = mmc5633_write_raw,
+ .read_avail = mmc5633_read_avail,
+};
+
+static bool mmc5633_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_CTRL0:
+ case MMC5633_REG_CTRL1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc5633_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_XOUT0:
+ case MMC5633_REG_XOUT1:
+ case MMC5633_REG_YOUT0:
+ case MMC5633_REG_YOUT1:
+ case MMC5633_REG_ZOUT0:
+ case MMC5633_REG_ZOUT1:
+ case MMC5633_REG_XOUT2:
+ case MMC5633_REG_YOUT2:
+ case MMC5633_REG_ZOUT2:
+ case MMC5633_REG_TOUT:
+ case MMC5633_REG_STATUS1:
+ case MMC5633_REG_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc5633_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_CTRL0:
+ case MMC5633_REG_CTRL1:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct reg_default mmc5633_reg_defaults[] = {
+ { MMC5633_REG_CTRL0, 0x00 },
+ { MMC5633_REG_CTRL1, 0x00 },
+};
+
+static const struct regmap_config mmc5633_regmap_config = {
+ .name = "mmc5633_regmap",
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MMC5633_REG_ID,
+ .cache_type = REGCACHE_MAPLE,
+
+ .writeable_reg = mmc5633_is_writeable_reg,
+ .readable_reg = mmc5633_is_readable_reg,
+ .volatile_reg = mmc5633_is_volatile_reg,
+
+ .reg_defaults = mmc5633_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(mmc5633_reg_defaults),
+};
+
+static int mmc5633_common_probe(struct regmap *regmap, char *name,
+ struct i3c_device *i3cdev)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct mmc5633_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);
+
+ data->regmap = regmap;
+ data->i3cdev = i3cdev;
+
+ ret = devm_mutex_init(dev, &data->mutex);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &mmc5633_info;
+ indio_dev->name = name;
+ indio_dev->channels = mmc5633_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mmc5633_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = mmc5633_init(data);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "mmc5633 chip init failed\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int mmc5633_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, true);
+
+ return 0;
+}
+
+static int mmc5633_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ regcache_mark_dirty(regmap);
+ ret = regcache_sync_region(regmap, MMC5633_REG_CTRL0, MMC5633_REG_CTRL1);
+ if (ret)
+ dev_err(dev, "Failed to restore control registers\n");
+
+ regcache_cache_only(regmap, false);
+
+ return 0;
+}
+
+static int mmc5633_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &mmc5633_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
+
+ return mmc5633_common_probe(regmap, client->name, NULL);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mmc5633_pm_ops, mmc5633_suspend, mmc5633_resume);
+
+static const struct of_device_id mmc5633_of_match[] = {
+ { .compatible = "memsic,mmc5603" },
+ { .compatible = "memsic,mmc5633" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mmc5633_of_match);
+
+static const struct i2c_device_id mmc5633_i2c_id[] = {
+ { "mmc5603" },
+ { "mmc5633" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mmc5633_i2c_id);
+
+static struct i2c_driver mmc5633_i2c_driver = {
+ .driver = {
+ .name = "mmc5633_i2c",
+ .of_match_table = mmc5633_of_match,
+ .pm = pm_sleep_ptr(&mmc5633_pm_ops),
+ },
+ .probe = mmc5633_i2c_probe,
+ .id_table = mmc5633_i2c_id,
+};
+
+static const struct i3c_device_id mmc5633_i3c_ids[] = {
+ I3C_DEVICE(0x0251, 0x0000, NULL),
+ { }
+};
+MODULE_DEVICE_TABLE(i3c, mmc5633_i3c_ids);
+
+static int mmc5633_i3c_probe(struct i3c_device *i3cdev)
+{
+ struct device *dev = i3cdev_to_dev(i3cdev);
+ struct regmap *regmap;
+ char *name;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "mmc5633_%s", dev_name(dev));
+ if (!name)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i3c(i3cdev, &mmc5633_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to register i3c regmap\n");
+
+ return mmc5633_common_probe(regmap, name, i3cdev);
+}
+
+static struct i3c_driver mmc5633_i3c_driver = {
+ .driver = {
+ .name = "mmc5633_i3c",
+ },
+ .probe = mmc5633_i3c_probe,
+ .id_table = mmc5633_i3c_ids,
+};
+module_i3c_i2c_driver(mmc5633_i3c_driver, &mmc5633_i2c_driver)
+
+MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>");
+MODULE_DESCRIPTION("MEMSIC MMC5633 magnetic sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index 2fe9dc90cceb..838a8340c4c0 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -16,6 +16,35 @@ config ABP060MG
To compile this driver as a module, choose M here: the module
will be called abp060mg.
+config ABP2030PA
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config ABP2030PA_I2C
+ tristate "Honeywell ABP2 pressure sensor series I2C driver"
+ depends on I2C
+ select ABP2030PA
+ help
+ Say Y here to build I2C bus support for the Honeywell ABP2
+ series pressure and temperature digital sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp2030pa_i2c and you will also get abp2030pa
+ for the core module.
+
+config ABP2030PA_SPI
+ tristate "Honeywell ABP2 pressure sensor series SPI driver"
+ depends on SPI_MASTER
+ select ABP2030PA
+ help
+ Say Y here to build SPI bus support for the Honeywell ABP2
+ series pressure and temperature digital sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp2030pa_spi and you will also get abp2030pa
+ for the core module.
+
config ROHM_BM1390
tristate "ROHM BM1390GLV-Z pressure sensor driver"
depends on I2C
@@ -187,29 +216,33 @@ config MPL3115
will be called mpl3115.
config MPRLS0025PA
- tristate "Honeywell MPRLS0025PA (MicroPressure sensors series)"
- depends on (I2C || SPI_MASTER)
- select MPRLS0025PA_I2C if I2C
- select MPRLS0025PA_SPI if SPI_MASTER
+ tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
- help
- Say Y here to build support for the Honeywell MicroPressure pressure
- sensor series. There are many different types with different pressure
- range. These ranges can be set up in the device tree.
-
- To compile this driver as a module, choose M here: the module will be
- called mprls0025pa.
config MPRLS0025PA_I2C
- tristate
- depends on MPRLS0025PA
+ tristate "Honeywell MPR pressure sensor series I2C driver"
depends on I2C
+ select MPRLS0025PA
+ help
+ Say Y here to build I2C bus support for the Honeywell MicroPressure
+ series sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mprls0025pa_i2c and you will also get mprls0025pa
+ for the core module.
config MPRLS0025PA_SPI
- tristate
- depends on MPRLS0025PA
+ tristate "Honeywell MPR pressure sensor series SPI driver"
depends on SPI_MASTER
+ select MPRLS0025PA
+ help
+ Say Y here to build SPI bus support for the Honeywell MicroPressure
+ series sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mprls0025pa_spi and you will also get mprls0025pa
+ for the core module.
config MS5611
tristate "Measurement Specialties MS5611 pressure sensor driver"
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index a21443e992b9..bc0d11a20acc 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -5,6 +5,9 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ABP060MG) += abp060mg.o
+obj-$(CONFIG_ABP2030PA) += abp2030pa.o
+obj-$(CONFIG_ABP2030PA_I2C) += abp2030pa_i2c.o
+obj-$(CONFIG_ABP2030PA_SPI) += abp2030pa_spi.o
obj-$(CONFIG_ADP810) += adp810.o
obj-$(CONFIG_ROHM_BM1390) += rohm-bm1390.o
obj-$(CONFIG_BMP280) += bmp280.o
diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c
new file mode 100644
index 000000000000..4ca056a73cef
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa.c
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/basic-abp2-series/documents/sps-siot-abp2-series-datasheet-32350268-en.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.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 "abp2030pa.h"
+
+/* Status byte flags */
+#define ABP2_ST_POWER BIT(6) /* 1 if device is powered */
+#define ABP2_ST_BUSY BIT(5) /* 1 if device is busy */
+
+#define ABP2_CMD_NOP 0xf0
+#define ABP2_CMD_SYNC 0xaa
+#define ABP2_PKT_SYNC_LEN 3
+#define ABP2_PKT_NOP_LEN ABP2_MEASUREMENT_RD_SIZE
+
+struct abp2_func_spec {
+ u32 output_min;
+ u32 output_max;
+};
+
+/* transfer function A: 10% to 90% of 2^24 */
+static const struct abp2_func_spec abp2_func_spec[] = {
+ [ABP2_FUNCTION_A] = { .output_min = 1677722, .output_max = 15099494 },
+};
+
+enum abp2_variants {
+ ABP2001BA, ABP21_6BA, ABP22_5BA, ABP2004BA, ABP2006BA, ABP2008BA,
+ ABP2010BA, ABP2012BA, ABP2001BD, ABP21_6BD, ABP22_5BD, ABP2004BD,
+ ABP2001BG, ABP21_6BG, ABP22_5BG, ABP2004BG, ABP2006BG, ABP2008BG,
+ ABP2010BG, ABP2012BG, ABP2001GG, ABP21_2GG, ABP2100KA, ABP2160KA,
+ ABP2250KA, ABP2001KD, ABP21_6KD, ABP22_5KD, ABP2004KD, ABP2006KD,
+ ABP2010KD, ABP2016KD, ABP2025KD, ABP2040KD, ABP2060KD, ABP2100KD,
+ ABP2160KD, ABP2250KD, ABP2400KD, ABP2001KG, ABP21_6KG, ABP22_5KG,
+ ABP2004KG, ABP2006KG, ABP2010KG, ABP2016KG, ABP2025KG, ABP2040KG,
+ ABP2060KG, ABP2100KG, ABP2160KG, ABP2250KG, ABP2400KG, ABP2600KG,
+ ABP2800KG, ABP2250LD, ABP2600LD, ABP2600LG, ABP22_5MD, ABP2006MD,
+ ABP2010MD, ABP2016MD, ABP2025MD, ABP2040MD, ABP2060MD, ABP2100MD,
+ ABP2160MD, ABP2250MD, ABP2400MD, ABP2600MD, ABP2006MG, ABP2010MG,
+ ABP2016MG, ABP2025MG, ABP2040MG, ABP2060MG, ABP2100MG, ABP2160MG,
+ ABP2250MG, ABP2400MG, ABP2600MG, ABP2001ND, ABP2002ND, ABP2004ND,
+ ABP2005ND, ABP2010ND, ABP2020ND, ABP2030ND, ABP2002NG, ABP2004NG,
+ ABP2005NG, ABP2010NG, ABP2020NG, ABP2030NG, ABP2015PA, ABP2030PA,
+ ABP2060PA, ABP2100PA, ABP2150PA, ABP2175PA, ABP2001PD, ABP2005PD,
+ ABP2015PD, ABP2030PD, ABP2060PD, ABP2001PG, ABP2005PG, ABP2015PG,
+ ABP2030PG, ABP2060PG, ABP2100PG, ABP2150PG, ABP2175PG,
+};
+
+static const char * const abp2_triplet_variants[] = {
+ [ABP2001BA] = "001BA", [ABP21_6BA] = "1.6BA", [ABP22_5BA] = "2.5BA",
+ [ABP2004BA] = "004BA", [ABP2006BA] = "006BA", [ABP2008BA] = "008BA",
+ [ABP2010BA] = "010BA", [ABP2012BA] = "012BA", [ABP2001BD] = "001BD",
+ [ABP21_6BD] = "1.6BD", [ABP22_5BD] = "2.5BD", [ABP2004BD] = "004BD",
+ [ABP2001BG] = "001BG", [ABP21_6BG] = "1.6BG", [ABP22_5BG] = "2.5BG",
+ [ABP2004BG] = "004BG", [ABP2006BG] = "006BG", [ABP2008BG] = "008BG",
+ [ABP2010BG] = "010BG", [ABP2012BG] = "012BG", [ABP2001GG] = "001GG",
+ [ABP21_2GG] = "1.2GG", [ABP2100KA] = "100KA", [ABP2160KA] = "160KA",
+ [ABP2250KA] = "250KA", [ABP2001KD] = "001KD", [ABP21_6KD] = "1.6KD",
+ [ABP22_5KD] = "2.5KD", [ABP2004KD] = "004KD", [ABP2006KD] = "006KD",
+ [ABP2010KD] = "010KD", [ABP2016KD] = "016KD", [ABP2025KD] = "025KD",
+ [ABP2040KD] = "040KD", [ABP2060KD] = "060KD", [ABP2100KD] = "100KD",
+ [ABP2160KD] = "160KD", [ABP2250KD] = "250KD", [ABP2400KD] = "400KD",
+ [ABP2001KG] = "001KG", [ABP21_6KG] = "1.6KG", [ABP22_5KG] = "2.5KG",
+ [ABP2004KG] = "004KG", [ABP2006KG] = "006KG", [ABP2010KG] = "010KG",
+ [ABP2016KG] = "016KG", [ABP2025KG] = "025KG", [ABP2040KG] = "040KG",
+ [ABP2060KG] = "060KG", [ABP2100KG] = "100KG", [ABP2160KG] = "160KG",
+ [ABP2250KG] = "250KG", [ABP2400KG] = "400KG", [ABP2600KG] = "600KG",
+ [ABP2800KG] = "800KG", [ABP2250LD] = "250LD", [ABP2600LD] = "600LD",
+ [ABP2600LG] = "600LG", [ABP22_5MD] = "2.5MD", [ABP2006MD] = "006MD",
+ [ABP2010MD] = "010MD", [ABP2016MD] = "016MD", [ABP2025MD] = "025MD",
+ [ABP2040MD] = "040MD", [ABP2060MD] = "060MD", [ABP2100MD] = "100MD",
+ [ABP2160MD] = "160MD", [ABP2250MD] = "250MD", [ABP2400MD] = "400MD",
+ [ABP2600MD] = "600MD", [ABP2006MG] = "006MG", [ABP2010MG] = "010MG",
+ [ABP2016MG] = "016MG", [ABP2025MG] = "025MG", [ABP2040MG] = "040MG",
+ [ABP2060MG] = "060MG", [ABP2100MG] = "100MG", [ABP2160MG] = "160MG",
+ [ABP2250MG] = "250MG", [ABP2400MG] = "400MG", [ABP2600MG] = "600MG",
+ [ABP2001ND] = "001ND", [ABP2002ND] = "002ND", [ABP2004ND] = "004ND",
+ [ABP2005ND] = "005ND", [ABP2010ND] = "010ND", [ABP2020ND] = "020ND",
+ [ABP2030ND] = "030ND", [ABP2002NG] = "002NG", [ABP2004NG] = "004NG",
+ [ABP2005NG] = "005NG", [ABP2010NG] = "010NG", [ABP2020NG] = "020NG",
+ [ABP2030NG] = "030NG", [ABP2015PA] = "015PA", [ABP2030PA] = "030PA",
+ [ABP2060PA] = "060PA", [ABP2100PA] = "100PA", [ABP2150PA] = "150PA",
+ [ABP2175PA] = "175PA", [ABP2001PD] = "001PD", [ABP2005PD] = "005PD",
+ [ABP2015PD] = "015PD", [ABP2030PD] = "030PD", [ABP2060PD] = "060PD",
+ [ABP2001PG] = "001PG", [ABP2005PG] = "005PG", [ABP2015PG] = "015PG",
+ [ABP2030PG] = "030PG", [ABP2060PG] = "060PG", [ABP2100PG] = "100PG",
+ [ABP2150PG] = "150PG", [ABP2175PG] = "175PG",
+};
+
+/**
+ * struct abp2_range_config - list of pressure ranges based on nomenclature
+ * @pmin: lowest pressure that can be measured
+ * @pmax: highest pressure that can be measured
+ */
+struct abp2_range_config {
+ s32 pmin;
+ s32 pmax;
+};
+
+/* All min max limits have been converted to pascals */
+static const struct abp2_range_config abp2_range_config[] = {
+ [ABP2001BA] = { .pmin = 0, .pmax = 100000 },
+ [ABP21_6BA] = { .pmin = 0, .pmax = 160000 },
+ [ABP22_5BA] = { .pmin = 0, .pmax = 250000 },
+ [ABP2004BA] = { .pmin = 0, .pmax = 400000 },
+ [ABP2006BA] = { .pmin = 0, .pmax = 600000 },
+ [ABP2008BA] = { .pmin = 0, .pmax = 800000 },
+ [ABP2010BA] = { .pmin = 0, .pmax = 1000000 },
+ [ABP2012BA] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2001BD] = { .pmin = -100000, .pmax = 100000 },
+ [ABP21_6BD] = { .pmin = -160000, .pmax = 160000 },
+ [ABP22_5BD] = { .pmin = -250000, .pmax = 250000 },
+ [ABP2004BD] = { .pmin = -400000, .pmax = 400000 },
+ [ABP2001BG] = { .pmin = 0, .pmax = 100000 },
+ [ABP21_6BG] = { .pmin = 0, .pmax = 160000 },
+ [ABP22_5BG] = { .pmin = 0, .pmax = 250000 },
+ [ABP2004BG] = { .pmin = 0, .pmax = 400000 },
+ [ABP2006BG] = { .pmin = 0, .pmax = 600000 },
+ [ABP2008BG] = { .pmin = 0, .pmax = 800000 },
+ [ABP2010BG] = { .pmin = 0, .pmax = 1000000 },
+ [ABP2012BG] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2001GG] = { .pmin = 0, .pmax = 1000000 },
+ [ABP21_2GG] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2100KA] = { .pmin = 0, .pmax = 100000 },
+ [ABP2160KA] = { .pmin = 0, .pmax = 160000 },
+ [ABP2250KA] = { .pmin = 0, .pmax = 250000 },
+ [ABP2001KD] = { .pmin = -1000, .pmax = 1000 },
+ [ABP21_6KD] = { .pmin = -1600, .pmax = 1600 },
+ [ABP22_5KD] = { .pmin = -2500, .pmax = 2500 },
+ [ABP2004KD] = { .pmin = -4000, .pmax = 4000 },
+ [ABP2006KD] = { .pmin = -6000, .pmax = 6000 },
+ [ABP2010KD] = { .pmin = -10000, .pmax = 10000 },
+ [ABP2016KD] = { .pmin = -16000, .pmax = 16000 },
+ [ABP2025KD] = { .pmin = -25000, .pmax = 25000 },
+ [ABP2040KD] = { .pmin = -40000, .pmax = 40000 },
+ [ABP2060KD] = { .pmin = -60000, .pmax = 60000 },
+ [ABP2100KD] = { .pmin = -100000, .pmax = 100000 },
+ [ABP2160KD] = { .pmin = -160000, .pmax = 160000 },
+ [ABP2250KD] = { .pmin = -250000, .pmax = 250000 },
+ [ABP2400KD] = { .pmin = -400000, .pmax = 400000 },
+ [ABP2001KG] = { .pmin = 0, .pmax = 1000 },
+ [ABP21_6KG] = { .pmin = 0, .pmax = 1600 },
+ [ABP22_5KG] = { .pmin = 0, .pmax = 2500 },
+ [ABP2004KG] = { .pmin = 0, .pmax = 4000 },
+ [ABP2006KG] = { .pmin = 0, .pmax = 6000 },
+ [ABP2010KG] = { .pmin = 0, .pmax = 10000 },
+ [ABP2016KG] = { .pmin = 0, .pmax = 16000 },
+ [ABP2025KG] = { .pmin = 0, .pmax = 25000 },
+ [ABP2040KG] = { .pmin = 0, .pmax = 40000 },
+ [ABP2060KG] = { .pmin = 0, .pmax = 60000 },
+ [ABP2100KG] = { .pmin = 0, .pmax = 100000 },
+ [ABP2160KG] = { .pmin = 0, .pmax = 160000 },
+ [ABP2250KG] = { .pmin = 0, .pmax = 250000 },
+ [ABP2400KG] = { .pmin = 0, .pmax = 400000 },
+ [ABP2600KG] = { .pmin = 0, .pmax = 600000 },
+ [ABP2800KG] = { .pmin = 0, .pmax = 800000 },
+ [ABP2250LD] = { .pmin = -250, .pmax = 250 },
+ [ABP2600LD] = { .pmin = -600, .pmax = 600 },
+ [ABP2600LG] = { .pmin = 0, .pmax = 600 },
+ [ABP22_5MD] = { .pmin = -250, .pmax = 250 },
+ [ABP2006MD] = { .pmin = -600, .pmax = 600 },
+ [ABP2010MD] = { .pmin = -1000, .pmax = 1000 },
+ [ABP2016MD] = { .pmin = -1600, .pmax = 1600 },
+ [ABP2025MD] = { .pmin = -2500, .pmax = 2500 },
+ [ABP2040MD] = { .pmin = -4000, .pmax = 4000 },
+ [ABP2060MD] = { .pmin = -6000, .pmax = 6000 },
+ [ABP2100MD] = { .pmin = -10000, .pmax = 10000 },
+ [ABP2160MD] = { .pmin = -16000, .pmax = 16000 },
+ [ABP2250MD] = { .pmin = -25000, .pmax = 25000 },
+ [ABP2400MD] = { .pmin = -40000, .pmax = 40000 },
+ [ABP2600MD] = { .pmin = -60000, .pmax = 60000 },
+ [ABP2006MG] = { .pmin = 0, .pmax = 600 },
+ [ABP2010MG] = { .pmin = 0, .pmax = 1000 },
+ [ABP2016MG] = { .pmin = 0, .pmax = 1600 },
+ [ABP2025MG] = { .pmin = 0, .pmax = 2500 },
+ [ABP2040MG] = { .pmin = 0, .pmax = 4000 },
+ [ABP2060MG] = { .pmin = 0, .pmax = 6000 },
+ [ABP2100MG] = { .pmin = 0, .pmax = 10000 },
+ [ABP2160MG] = { .pmin = 0, .pmax = 16000 },
+ [ABP2250MG] = { .pmin = 0, .pmax = 25000 },
+ [ABP2400MG] = { .pmin = 0, .pmax = 40000 },
+ [ABP2600MG] = { .pmin = 0, .pmax = 60000 },
+ [ABP2001ND] = { .pmin = -249, .pmax = 249 },
+ [ABP2002ND] = { .pmin = -498, .pmax = 498 },
+ [ABP2004ND] = { .pmin = -996, .pmax = 996 },
+ [ABP2005ND] = { .pmin = -1245, .pmax = 1245 },
+ [ABP2010ND] = { .pmin = -2491, .pmax = 2491 },
+ [ABP2020ND] = { .pmin = -4982, .pmax = 4982 },
+ [ABP2030ND] = { .pmin = -7473, .pmax = 7473 },
+ [ABP2002NG] = { .pmin = 0, .pmax = 498 },
+ [ABP2004NG] = { .pmin = 0, .pmax = 996 },
+ [ABP2005NG] = { .pmin = 0, .pmax = 1245 },
+ [ABP2010NG] = { .pmin = 0, .pmax = 2491 },
+ [ABP2020NG] = { .pmin = 0, .pmax = 4982 },
+ [ABP2030NG] = { .pmin = 0, .pmax = 7473 },
+ [ABP2015PA] = { .pmin = 0, .pmax = 103421 },
+ [ABP2030PA] = { .pmin = 0, .pmax = 206843 },
+ [ABP2060PA] = { .pmin = 0, .pmax = 413685 },
+ [ABP2100PA] = { .pmin = 0, .pmax = 689476 },
+ [ABP2150PA] = { .pmin = 0, .pmax = 1034214 },
+ [ABP2175PA] = { .pmin = 0, .pmax = 1206583 },
+ [ABP2001PD] = { .pmin = -6895, .pmax = 6895 },
+ [ABP2005PD] = { .pmin = -34474, .pmax = 34474 },
+ [ABP2015PD] = { .pmin = -103421, .pmax = 103421 },
+ [ABP2030PD] = { .pmin = -206843, .pmax = 206843 },
+ [ABP2060PD] = { .pmin = -413685, .pmax = 413685 },
+ [ABP2001PG] = { .pmin = 0, .pmax = 6895 },
+ [ABP2005PG] = { .pmin = 0, .pmax = 34474 },
+ [ABP2015PG] = { .pmin = 0, .pmax = 103421 },
+ [ABP2030PG] = { .pmin = 0, .pmax = 206843 },
+ [ABP2060PG] = { .pmin = 0, .pmax = 413685 },
+ [ABP2100PG] = { .pmin = 0, .pmax = 689476 },
+ [ABP2150PG] = { .pmin = 0, .pmax = 1034214 },
+ [ABP2175PG] = { .pmin = 0, .pmax = 1206583 },
+};
+
+static_assert(ARRAY_SIZE(abp2_triplet_variants) == ARRAY_SIZE(abp2_range_config));
+
+static int abp2_get_measurement(struct abp2_data *data)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ reinit_completion(&data->completion);
+
+ ret = data->ops->write(data, ABP2_CMD_SYNC, ABP2_PKT_SYNC_LEN);
+ if (ret < 0)
+ return ret;
+
+ if (data->irq > 0) {
+ ret = wait_for_completion_timeout(&data->completion, HZ);
+ if (!ret) {
+ dev_err(dev, "timeout waiting for EOC interrupt\n");
+ return -ETIMEDOUT;
+ }
+ } else {
+ fsleep(5 * USEC_PER_MSEC);
+ }
+
+ memset(data->rx_buf, 0, sizeof(data->rx_buf));
+ ret = data->ops->read(data, ABP2_CMD_NOP, ABP2_PKT_NOP_LEN);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Status byte flags
+ * bit7 SANITY_CHK - must always be 0
+ * bit6 ABP2_ST_POWER - 1 if device is powered
+ * bit5 ABP2_ST_BUSY - 1 if device has no new conversion ready
+ * bit4 SANITY_CHK - must always be 0
+ * bit3 SANITY_CHK - must always be 0
+ * bit2 MEMORY_ERR - 1 if integrity test has failed
+ * bit1 SANITY_CHK - must always be 0
+ * bit0 MATH_ERR - 1 during internal math saturation error
+ */
+
+ if (data->rx_buf[0] == (ABP2_ST_POWER | ABP2_ST_BUSY))
+ return -EBUSY;
+
+ /*
+ * The ABP2 sensor series seem to have a noticeable latch-up sensitivity.
+ * A partial latch-up condition manifests as either
+ * - output of invalid status bytes
+ * - zeroed out conversions (despite a normal status byte)
+ * - the MOSI line being pulled low randomly in sync with the SCLK
+ * signal (visible during the ABP2_CMD_NOP command).
+ * https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1588325/am3358-spi-tx-data-corruption
+ */
+
+ if (data->rx_buf[0] != ABP2_ST_POWER) {
+ dev_err(data->dev,
+ "unexpected status byte 0x%02x\n", data->rx_buf[0]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static irqreturn_t abp2_eoc_handler(int irq, void *private)
+{
+ struct abp2_data *data = private;
+
+ complete(&data->completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t abp2_trigger_handler(int irq, void *private)
+{
+ int ret;
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct abp2_data *data = iio_priv(indio_dev);
+
+ ret = abp2_get_measurement(data);
+ if (ret < 0)
+ goto out_notify_done;
+
+ data->scan.chan[0] = get_unaligned_be24(&data->rx_buf[1]);
+ data->scan.chan[1] = get_unaligned_be24(&data->rx_buf[4]);
+
+ iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan),
+ iio_get_time_ns(indio_dev));
+
+out_notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * IIO ABI expects
+ * value = (conv + offset) * scale
+ *
+ * temp[C] = conv * a + b
+ * where a = 200/16777215; b = -50
+ *
+ * temp[C] = (conv + (b/a)) * a * (1000)
+ * =>
+ * scale = a * 1000 = .0000119209296 * 1000 = .01192092966562
+ * offset = b/a = -50 * 16777215 / 200 = -4194303.75
+ *
+ * pressure = (conv - Omin) * Q + Pmin =
+ * ((conv - Omin) + Pmin/Q) * Q
+ * =>
+ * scale = Q = (Pmax - Pmin) / (Omax - Omin)
+ * offset = Pmin/Q - Omin = Pmin * (Omax - Omin) / (Pmax - Pmin) - Omin
+ */
+static int abp2_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct abp2_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = abp2_get_measurement(data);
+ if (ret < 0)
+ return ret;
+
+ switch (channel->type) {
+ case IIO_PRESSURE:
+ *val = get_unaligned_be24(&data->rx_buf[1]);
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = get_unaligned_be24(&data->rx_buf[4]);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 11920929;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_PRESSURE:
+ *val = data->p_scale;
+ *val2 = data->p_scale_dec;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = -4194304;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = data->p_offset;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info abp2_info = {
+ .read_raw = &abp2_read_raw,
+};
+
+static const unsigned long abp2_scan_masks[] = {0x3, 0};
+
+static const struct iio_chan_spec abp2_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq)
+{
+ int ret;
+ struct abp2_data *data;
+ struct iio_dev *indio_dev;
+ const char *triplet;
+ s32 tmp;
+ s64 odelta, pdelta;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ data->ops = ops;
+ data->irq = irq;
+
+ init_completion(&data->completion);
+
+ indio_dev->name = "abp2030pa";
+ indio_dev->info = &abp2_info;
+ indio_dev->channels = abp2_channels;
+ indio_dev->num_channels = ARRAY_SIZE(abp2_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = abp2_scan_masks;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "can't get and enable vdd supply\n");
+
+ ret = device_property_read_string(dev, "honeywell,pressure-triplet",
+ &triplet);
+ if (ret) {
+ ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
+ &data->pmin);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmin-pascal could not be read\n");
+
+ ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
+ &data->pmax);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmax-pascal could not be read\n");
+ } else {
+ ret = device_property_match_property_string(dev,
+ "honeywell,pressure-triplet",
+ abp2_triplet_variants,
+ ARRAY_SIZE(abp2_triplet_variants));
+ if (ret < 0)
+ return dev_err_probe(dev, -EINVAL, "honeywell,pressure-triplet is invalid\n");
+
+ data->pmin = abp2_range_config[ret].pmin;
+ data->pmax = abp2_range_config[ret].pmax;
+ }
+
+ if (data->pmin >= data->pmax)
+ return dev_err_probe(dev, -EINVAL, "pressure limits are invalid\n");
+
+ data->outmin = abp2_func_spec[data->function].output_min;
+ data->outmax = abp2_func_spec[data->function].output_max;
+
+ odelta = data->outmax - data->outmin;
+ pdelta = data->pmax - data->pmin;
+
+ data->p_scale = div_s64_rem(div_s64(pdelta * NANO, odelta), NANO, &tmp);
+ data->p_scale_dec = tmp;
+
+ data->p_offset = div_s64(odelta * data->pmin, pdelta) - data->outmin;
+
+ if (data->irq > 0) {
+ ret = devm_request_irq(dev, irq, abp2_eoc_handler, IRQF_ONESHOT,
+ dev_name(dev), data);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ abp2_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "unable to register iio device\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(abp2_common_probe, "IIO_HONEYWELL_ABP2030PA");
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/abp2030pa.h b/drivers/iio/pressure/abp2030pa.h
new file mode 100644
index 000000000000..57e5ed784686
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#ifndef _ABP2030PA_H
+#define _ABP2030PA_H
+
+#include <linux/completion.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define ABP2_MEASUREMENT_RD_SIZE 7
+
+struct device;
+
+struct abp2_data;
+struct abp2_ops;
+
+enum abp2_func_id {
+ ABP2_FUNCTION_A,
+};
+
+/**
+ * struct abp2_data
+ * @dev: current device structure
+ * @ops: pointers for bus specific read and write functions
+ * @pmin: minimal pressure in pascal
+ * @pmax: maximal pressure in pascal
+ * @outmin: minimum raw pressure in counts (based on transfer function)
+ * @outmax: maximum raw pressure in counts (based on transfer function)
+ * @function: transfer function
+ * @p_scale: pressure scale
+ * @p_scale_dec: pressure scale, decimal number
+ * @p_offset: pressure offset
+ * @irq: end of conversion - applies only to the i2c sensor
+ * @completion: handshake from irq to read
+ * @scan: channel values for buffered mode
+ * @tx_buf: transmit buffer used during the SPI communication
+ * @rx_buf: raw data provided by sensor
+ */
+struct abp2_data {
+ struct device *dev;
+ const struct abp2_ops *ops;
+ s32 pmin;
+ s32 pmax;
+ u32 outmin;
+ u32 outmax;
+ enum abp2_func_id function;
+ int p_scale;
+ int p_scale_dec;
+ int p_offset;
+ int irq;
+ struct completion completion;
+ struct {
+ u32 chan[2];
+ aligned_s64 timestamp;
+ } scan;
+ u8 rx_buf[ABP2_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[ABP2_MEASUREMENT_RD_SIZE];
+};
+
+struct abp2_ops {
+ int (*read)(struct abp2_data *data, u8 cmd, u8 nbytes);
+ int (*write)(struct abp2_data *data, u8 cmd, u8 nbytes);
+};
+
+int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq);
+
+#endif
diff --git a/drivers/iio/pressure/abp2030pa_i2c.c b/drivers/iio/pressure/abp2030pa_i2c.c
new file mode 100644
index 000000000000..9f1c1c8a9afb
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa_i2c.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "abp2030pa.h"
+
+static int abp2_i2c_read(struct abp2_data *data, u8 unused, u8 nbytes)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+ int ret;
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ ret = i2c_master_recv(client, data->rx_buf, nbytes);
+ if (ret < 0)
+ return ret;
+ if (ret != nbytes)
+ return -EIO;
+
+ return 0;
+}
+
+static int abp2_i2c_write(struct abp2_data *data, u8 cmd, u8 nbytes)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+ int ret;
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ data->tx_buf[0] = cmd;
+ ret = i2c_master_send(client, data->tx_buf, nbytes);
+ if (ret < 0)
+ return ret;
+ if (ret != nbytes)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct abp2_ops abp2_i2c_ops = {
+ .read = abp2_i2c_read,
+ .write = abp2_i2c_write,
+};
+
+static int abp2_i2c_probe(struct i2c_client *client)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ return abp2_common_probe(&client->dev, &abp2_i2c_ops, client->irq);
+}
+
+static const struct of_device_id abp2_i2c_match[] = {
+ { .compatible = "honeywell,abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, abp2_i2c_match);
+
+static const struct i2c_device_id abp2_i2c_id[] = {
+ { "abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abp2_i2c_id);
+
+static struct i2c_driver abp2_i2c_driver = {
+ .driver = {
+ .name = "abp2030pa",
+ .of_match_table = abp2_i2c_match,
+ },
+ .probe = abp2_i2c_probe,
+ .id_table = abp2_i2c_id,
+};
+module_i2c_driver(abp2_i2c_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor i2c driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_ABP2030PA");
diff --git a/drivers/iio/pressure/abp2030pa_spi.c b/drivers/iio/pressure/abp2030pa_spi.c
new file mode 100644
index 000000000000..eaea9a3ebf11
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa_spi.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#include <linux/errno.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "abp2030pa.h"
+
+static int abp2_spi_xfer(struct abp2_data *data, u8 cmd, u8 nbytes)
+{
+ struct spi_device *spi = to_spi_device(data->dev);
+ struct spi_transfer xfer = { };
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ data->tx_buf[0] = cmd;
+ xfer.tx_buf = data->tx_buf;
+ xfer.rx_buf = data->rx_buf;
+ xfer.len = nbytes;
+
+ return spi_sync_transfer(spi, &xfer, 1);
+}
+
+static const struct abp2_ops abp2_spi_ops = {
+ .read = abp2_spi_xfer,
+ .write = abp2_spi_xfer,
+};
+
+static int abp2_spi_probe(struct spi_device *spi)
+{
+ return abp2_common_probe(&spi->dev, &abp2_spi_ops, spi->irq);
+}
+
+static const struct of_device_id abp2_spi_match[] = {
+ { .compatible = "honeywell,abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, abp2_spi_match);
+
+static const struct spi_device_id abp2_spi_id[] = {
+ { "abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, abp2_spi_id);
+
+static struct spi_driver abp2_spi_driver = {
+ .driver = {
+ .name = "abp2030pa",
+ .of_match_table = abp2_spi_match,
+ },
+ .probe = abp2_spi_probe,
+ .id_table = abp2_spi_id,
+};
+module_spi_driver(abp2_spi_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor spi driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_ABP2030PA");
diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c
index 8bad7162fec6..46feb27fe632 100644
--- a/drivers/iio/pressure/dlhl60d.c
+++ b/drivers/iio/pressure/dlhl60d.c
@@ -306,10 +306,9 @@ static int dlh_probe(struct i2c_client *client)
indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
if (client->irq > 0) {
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- dlh_interrupt, NULL,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- st->info->name, indio_dev);
+ ret = devm_request_irq(&client->dev, client->irq, dlh_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ st->info->name, indio_dev);
if (ret) {
dev_err(&client->dev, "failed to allocate threaded irq");
return ret;
diff --git a/drivers/iio/pressure/mprls0025pa.c b/drivers/iio/pressure/mprls0025pa.c
index 2336f2760eae..e8c495f336ff 100644
--- a/drivers/iio/pressure/mprls0025pa.c
+++ b/drivers/iio/pressure/mprls0025pa.c
@@ -3,6 +3,7 @@
* MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver
*
* Copyright (c) Andreas Klinger <ak@it-klinger.de>
+ * Copyright (c) 2023-2025 Petre Rodan <petre.rodan@subdimension.ro>
*
* Data sheet:
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
@@ -12,15 +13,24 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
+#include <linux/string.h>
+#include <linux/time.h>
#include <linux/units.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -33,10 +43,6 @@
/* bits in status byte */
#define MPR_ST_POWER BIT(6) /* device is powered */
#define MPR_ST_BUSY BIT(5) /* device is busy */
-#define MPR_ST_MEMORY BIT(2) /* integrity test passed */
-#define MPR_ST_MATH BIT(0) /* internal math saturation */
-
-#define MPR_ST_ERR_FLAG (MPR_ST_BUSY | MPR_ST_MEMORY | MPR_ST_MATH)
/*
* support _RAW sysfs interface:
@@ -59,7 +65,7 @@
*
* Values given to the userspace in sysfs interface:
* * raw - press_cnt
- * * offset - (-1 * outputmin) - pmin / scale
+ * * offset - (-1 * outputmin) + pmin / scale
* note: With all sensors from the datasheet pmin = 0
* which reduces the offset to (-1 * outputmin)
*/
@@ -160,8 +166,8 @@ static const struct iio_chan_spec mpr_channels[] = {
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
- .sign = 's',
- .realbits = 32,
+ .sign = 'u',
+ .realbits = 24,
.storagebits = 32,
.endianness = IIO_CPU,
},
@@ -190,15 +196,15 @@ static void mpr_reset(struct mpr_data *data)
*
* Context: The function can sleep and data->lock should be held when calling it
* Return:
- * * 0 - OK, the pressure value could be read
- * * -ETIMEDOUT - Timeout while waiting for the EOC interrupt or busy flag is
- * still set after nloops attempts of reading
+ * * 0 - OK, the pressure value could be read
+ * * -EBUSY - Sensor does not have a new conversion ready
+ * * -ETIMEDOUT - Timeout while waiting for the EOC interrupt
+ * * -EIO - Invalid status byte received from sensor
*/
static int mpr_read_pressure(struct mpr_data *data, s32 *press)
{
struct device *dev = data->dev;
- int ret, i;
- int nloops = 10;
+ int ret;
reinit_completion(&data->completion);
@@ -215,44 +221,38 @@ static int mpr_read_pressure(struct mpr_data *data, s32 *press)
return -ETIMEDOUT;
}
} else {
- /* wait until status indicates data is ready */
- for (i = 0; i < nloops; i++) {
- /*
- * datasheet only says to wait at least 5 ms for the
- * data but leave the maximum response time open
- * --> let's try it nloops (10) times which seems to be
- * quite long
- */
- usleep_range(5000, 10000);
- ret = data->ops->read(data, MPR_CMD_NOP, 1);
- if (ret < 0) {
- dev_err(dev,
- "error while reading, status: %d\n",
- ret);
- return ret;
- }
- if (!(data->buffer[0] & MPR_ST_ERR_FLAG))
- break;
- }
- if (i == nloops) {
- dev_err(dev, "timeout while reading\n");
- return -ETIMEDOUT;
- }
+ fsleep(5 * USEC_PER_MSEC);
}
+ memset(data->rx_buf, 0, sizeof(data->rx_buf));
ret = data->ops->read(data, MPR_CMD_NOP, MPR_PKT_NOP_LEN);
if (ret < 0)
return ret;
- if (data->buffer[0] & MPR_ST_ERR_FLAG) {
+ /*
+ * Status byte flags
+ * bit7 SANITY_CHK - must always be 0
+ * bit6 MPR_ST_POWER - 1 if device is powered
+ * bit5 MPR_ST_BUSY - 1 if device has no new conversion ready
+ * bit4 SANITY_CHK - must always be 0
+ * bit3 SANITY_CHK - must always be 0
+ * bit2 MEMORY_ERR - 1 if integrity test has failed
+ * bit1 SANITY_CHK - must always be 0
+ * bit0 MATH_ERR - 1 during internal math saturation error
+ */
+
+ if (data->rx_buf[0] == (MPR_ST_POWER | MPR_ST_BUSY))
+ return -EBUSY;
+
+ if (data->rx_buf[0] != MPR_ST_POWER) {
dev_err(data->dev,
- "unexpected status byte %02x\n", data->buffer[0]);
- return -ETIMEDOUT;
+ "unexpected status byte 0x%02x\n", data->rx_buf[0]);
+ return -EIO;
}
- *press = get_unaligned_be24(&data->buffer[1]);
+ *press = get_unaligned_be24(&data->rx_buf[1]);
- dev_dbg(dev, "received: %*ph cnt: %d\n", ret, data->buffer, *press);
+ dev_dbg(dev, "received: %*ph cnt: %d\n", ret, data->rx_buf, *press);
return 0;
}
@@ -313,8 +313,7 @@ static int mpr_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
*val = data->offset;
- *val2 = data->offset2;
- return IIO_VAL_INT_PLUS_NANO;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -330,8 +329,9 @@ int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
struct mpr_data *data;
struct iio_dev *indio_dev;
const char *triplet;
- s64 scale, offset;
+ s64 odelta, pdelta;
u32 func;
+ s32 tmp;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
@@ -356,10 +356,6 @@ int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
return dev_err_probe(dev, ret,
"can't get and enable vdd supply\n");
- ret = data->ops->init(data->dev);
- if (ret)
- return ret;
-
ret = device_property_read_u32(dev,
"honeywell,transfer-function", &func);
if (ret)
@@ -405,26 +401,19 @@ int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
data->outmin = mpr_func_spec[data->function].output_min;
data->outmax = mpr_func_spec[data->function].output_max;
- /* use 64 bit calculation for preserving a reasonable precision */
- scale = div_s64(((s64)(data->pmax - data->pmin)) * NANO,
- data->outmax - data->outmin);
- data->scale = div_s64_rem(scale, NANO, &data->scale2);
- /*
- * multiply with NANO before dividing by scale and later divide by NANO
- * again.
- */
- offset = ((-1LL) * (s64)data->outmin) * NANO -
- div_s64(div_s64((s64)data->pmin * NANO, scale), NANO);
- data->offset = div_s64_rem(offset, NANO, &data->offset2);
+ odelta = data->outmax - data->outmin;
+ pdelta = data->pmax - data->pmin;
+
+ data->scale = div_s64_rem(div_s64(pdelta * NANO, odelta), NANO, &tmp);
+ data->scale2 = tmp;
+
+ data->offset = div_s64(odelta * data->pmin, pdelta) - data->outmin;
if (data->irq > 0) {
- ret = devm_request_irq(dev, data->irq, mpr_eoc_handler,
- IRQF_TRIGGER_RISING,
- dev_name(dev),
- data);
+ ret = devm_request_irq(dev, data->irq, mpr_eoc_handler, 0,
+ dev_name(dev), data);
if (ret)
- return dev_err_probe(dev, ret,
- "request irq %d failed\n", data->irq);
+ return ret;
}
data->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
diff --git a/drivers/iio/pressure/mprls0025pa.h b/drivers/iio/pressure/mprls0025pa.h
index d62a018eaff3..9f43273e635f 100644
--- a/drivers/iio/pressure/mprls0025pa.h
+++ b/drivers/iio/pressure/mprls0025pa.h
@@ -12,10 +12,7 @@
#define _MPRLS0025PA_H
#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/device.h>
#include <linux/mutex.h>
-#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
@@ -28,9 +25,6 @@
struct device;
-struct iio_chan_spec;
-struct iio_dev;
-
struct mpr_data;
struct mpr_ops;
@@ -53,7 +47,6 @@ enum mpr_func_id {
* @scale: pressure scale
* @scale2: pressure scale, decimal number
* @offset: pressure offset
- * @offset2: pressure offset, decimal number
* @gpiod_reset: reset
* @irq: end of conversion irq. used to distinguish between irq mode and
* reading in a loop until data is ready
@@ -61,7 +54,8 @@ enum mpr_func_id {
* @chan: channel values for buffered mode
* @chan.pres: pressure value
* @chan.ts: timestamp
- * @buffer: raw conversion data
+ * @rx_buf: raw conversion data
+ * @tx_buf: output buffer
*/
struct mpr_data {
struct device *dev;
@@ -75,7 +69,6 @@ struct mpr_data {
int scale;
int scale2;
int offset;
- int offset2;
struct gpio_desc *gpiod_reset;
int irq;
struct completion completion;
@@ -83,11 +76,11 @@ struct mpr_data {
s32 pres;
aligned_s64 ts;
} chan;
- u8 buffer[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[MPR_MEASUREMENT_RD_SIZE];
};
struct mpr_ops {
- int (*init)(struct device *dev);
int (*read)(struct mpr_data *data, const u8 cmd, const u8 cnt);
int (*write)(struct mpr_data *data, const u8 cmd, const u8 cnt);
};
diff --git a/drivers/iio/pressure/mprls0025pa_i2c.c b/drivers/iio/pressure/mprls0025pa_i2c.c
index 79811fd4a02b..0fe8cfe0d7e7 100644
--- a/drivers/iio/pressure/mprls0025pa_i2c.c
+++ b/drivers/iio/pressure/mprls0025pa_i2c.c
@@ -17,11 +17,6 @@
#include "mprls0025pa.h"
-static int mpr_i2c_init(struct device *unused)
-{
- return 0;
-}
-
static int mpr_i2c_read(struct mpr_data *data, const u8 unused, const u8 cnt)
{
int ret;
@@ -30,8 +25,7 @@ static int mpr_i2c_read(struct mpr_data *data, const u8 unused, const u8 cnt)
if (cnt > MPR_MEASUREMENT_RD_SIZE)
return -EOVERFLOW;
- memset(data->buffer, 0, MPR_MEASUREMENT_RD_SIZE);
- ret = i2c_master_recv(client, data->buffer, cnt);
+ ret = i2c_master_recv(client, data->rx_buf, cnt);
if (ret < 0)
return ret;
else if (ret != cnt)
@@ -44,9 +38,9 @@ static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused)
{
int ret;
struct i2c_client *client = to_i2c_client(data->dev);
- u8 wdata[MPR_PKT_SYNC_LEN] = { cmd };
- ret = i2c_master_send(client, wdata, MPR_PKT_SYNC_LEN);
+ data->tx_buf[0] = cmd;
+ ret = i2c_master_send(client, data->tx_buf, MPR_PKT_SYNC_LEN);
if (ret < 0)
return ret;
else if (ret != MPR_PKT_SYNC_LEN)
@@ -56,7 +50,6 @@ static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused)
}
static const struct mpr_ops mpr_i2c_ops = {
- .init = mpr_i2c_init,
.read = mpr_i2c_read,
.write = mpr_i2c_write,
};
diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c
index d04102f8a4a0..8c8c726f703f 100644
--- a/drivers/iio/pressure/mprls0025pa_spi.c
+++ b/drivers/iio/pressure/mprls0025pa_spi.c
@@ -8,6 +8,7 @@
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
*/
+#include <linux/array_size.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/mod_devicetable.h>
@@ -18,43 +19,31 @@
#include "mprls0025pa.h"
-struct mpr_spi_buf {
- u8 tx[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
-};
-
-static int mpr_spi_init(struct device *dev)
-{
- struct spi_device *spi = to_spi_device(dev);
- struct mpr_spi_buf *buf;
-
- buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- spi_set_drvdata(spi, buf);
-
- return 0;
-}
-
static int mpr_spi_xfer(struct mpr_data *data, const u8 cmd, const u8 pkt_len)
{
struct spi_device *spi = to_spi_device(data->dev);
- struct mpr_spi_buf *buf = spi_get_drvdata(spi);
- struct spi_transfer xfer;
+ struct spi_transfer xfers[2] = { };
if (pkt_len > MPR_MEASUREMENT_RD_SIZE)
return -EOVERFLOW;
- buf->tx[0] = cmd;
- xfer.tx_buf = buf->tx;
- xfer.rx_buf = data->buffer;
- xfer.len = pkt_len;
+ data->tx_buf[0] = cmd;
+
+ /*
+ * Dummy transfer with no data, just cause a 2.5us+ delay between the CS assert
+ * and the first clock edge as per the datasheet tHDSS timing requirement.
+ */
+ xfers[0].delay.value = 2500;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].tx_buf = data->tx_buf;
+ xfers[1].rx_buf = data->rx_buf;
+ xfers[1].len = pkt_len;
- return spi_sync_transfer(spi, &xfer, 1);
+ return spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
}
static const struct mpr_ops mpr_spi_ops = {
- .init = mpr_spi_init,
.read = mpr_spi_xfer,
.write = mpr_spi_xfer,
};
diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c
index aff60a3c1a6f..6afdbfca3e5a 100644
--- a/drivers/iio/proximity/rfd77402.c
+++ b/drivers/iio/proximity/rfd77402.c
@@ -6,19 +6,28 @@
*
* 7-bit I2C slave address 0x4c
*
- * TODO: interrupt
* https://media.digikey.com/pdf/Data%20Sheets/RF%20Digital%20PDFs/RFD77402.pdf
*/
-#include <linux/module.h>
-#include <linux/i2c.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/types.h>
#include <linux/iio/iio.h>
#define RFD77402_DRV_NAME "rfd77402"
#define RFD77402_ICSR 0x00 /* Interrupt Control Status Register */
+#define RFD77402_ICSR_CLR_CFG BIT(0)
+#define RFD77402_ICSR_CLR_TYPE BIT(1)
#define RFD77402_ICSR_INT_MODE BIT(2)
#define RFD77402_ICSR_INT_POL BIT(3)
#define RFD77402_ICSR_RESULT BIT(4)
@@ -26,6 +35,12 @@
#define RFD77402_ICSR_H2M_MSG BIT(6)
#define RFD77402_ICSR_RESET BIT(7)
+#define RFD77402_IER 0x02
+#define RFD77402_IER_RESULT BIT(0)
+#define RFD77402_IER_M2H_MSG BIT(1)
+#define RFD77402_IER_H2M_MSG BIT(2)
+#define RFD77402_IER_RESET BIT(3)
+
#define RFD77402_CMD_R 0x04
#define RFD77402_CMD_SINGLE 0x01
#define RFD77402_CMD_STANDBY 0x10
@@ -76,10 +91,18 @@ static const struct {
{RFD77402_HFCFG_3, 0x45d4},
};
+/**
+ * struct rfd77402_data - device-specific data for the RFD77402 sensor
+ * @client: I2C client handle
+ * @lock: mutex to serialize sensor reads
+ * @completion: completion used for interrupt-driven measurements
+ * @irq_en: indicates whether interrupt mode is enabled
+ */
struct rfd77402_data {
struct i2c_client *client;
- /* Serialize reads from the sensor */
struct mutex lock;
+ struct completion completion;
+ bool irq_en;
};
static const struct iio_chan_spec rfd77402_channels[] = {
@@ -90,6 +113,41 @@ static const struct iio_chan_spec rfd77402_channels[] = {
},
};
+static irqreturn_t rfd77402_interrupt_handler(int irq, void *pdata)
+{
+ struct rfd77402_data *data = pdata;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, RFD77402_ICSR);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ /* Check if the interrupt is from our device */
+ if (!(ret & RFD77402_ICSR_RESULT))
+ return IRQ_NONE;
+
+ /* Signal completion of measurement */
+ complete(&data->completion);
+ return IRQ_HANDLED;
+}
+
+static int rfd77402_wait_for_irq(struct rfd77402_data *data)
+{
+ int ret;
+
+ /*
+ * According to RFD77402 Datasheet v1.8,
+ * Section 3.1.1 "Single Measure" (Figure: Single Measure Flow Chart),
+ * the suggested timeout for single measure is 100 ms.
+ */
+ ret = wait_for_completion_timeout(&data->completion,
+ msecs_to_jiffies(100));
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
static int rfd77402_set_state(struct i2c_client *client, u8 state, u16 check)
{
int ret;
@@ -110,10 +168,36 @@ static int rfd77402_set_state(struct i2c_client *client, u8 state, u16 check)
return 0;
}
-static int rfd77402_measure(struct i2c_client *client)
+static int rfd77402_wait_for_result(struct rfd77402_data *data)
+{
+ struct i2c_client *client = data->client;
+ int val, ret;
+
+ if (data->irq_en) {
+ reinit_completion(&data->completion);
+ return rfd77402_wait_for_irq(data);
+ }
+
+ /*
+ * As per RFD77402 datasheet section '3.1.1 Single Measure', the
+ * suggested timeout value for single measure is 100ms.
+ */
+ ret = read_poll_timeout(i2c_smbus_read_byte_data, val,
+ (val < 0) || (val & RFD77402_ICSR_RESULT),
+ 10 * USEC_PER_MSEC,
+ 10 * 10 * USEC_PER_MSEC,
+ false,
+ client, RFD77402_ICSR);
+ if (val < 0)
+ return val;
+
+ return ret;
+}
+
+static int rfd77402_measure(struct rfd77402_data *data)
{
+ struct i2c_client *client = data->client;
int ret;
- int tries = 10;
ret = rfd77402_set_state(client, RFD77402_CMD_MCPU_ON,
RFD77402_STATUS_MCPU_ON);
@@ -126,19 +210,9 @@ static int rfd77402_measure(struct i2c_client *client)
if (ret < 0)
goto err;
- while (tries-- > 0) {
- ret = i2c_smbus_read_byte_data(client, RFD77402_ICSR);
- if (ret < 0)
- goto err;
- if (ret & RFD77402_ICSR_RESULT)
- break;
- msleep(20);
- }
-
- if (tries < 0) {
- ret = -ETIMEDOUT;
+ ret = rfd77402_wait_for_result(data);
+ if (ret < 0)
goto err;
- }
ret = i2c_smbus_read_word_data(client, RFD77402_RESULT_R);
if (ret < 0)
@@ -168,7 +242,7 @@ static int rfd77402_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->lock);
- ret = rfd77402_measure(data->client);
+ ret = rfd77402_measure(data);
mutex_unlock(&data->lock);
if (ret < 0)
return ret;
@@ -188,8 +262,20 @@ static const struct iio_info rfd77402_info = {
.read_raw = rfd77402_read_raw,
};
-static int rfd77402_init(struct i2c_client *client)
+static int rfd77402_config_irq(struct i2c_client *client, u8 csr, u8 ier)
{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, RFD77402_ICSR, csr);
+ if (ret)
+ return ret;
+
+ return i2c_smbus_write_byte_data(client, RFD77402_IER, ier);
+}
+
+static int rfd77402_init(struct rfd77402_data *data)
+{
+ struct i2c_client *client = data->client;
int ret, i;
ret = rfd77402_set_state(client, RFD77402_CMD_STANDBY,
@@ -197,10 +283,26 @@ static int rfd77402_init(struct i2c_client *client)
if (ret < 0)
return ret;
- /* configure INT pad as push-pull, active low */
- ret = i2c_smbus_write_byte_data(client, RFD77402_ICSR,
- RFD77402_ICSR_INT_MODE);
- if (ret < 0)
+ if (data->irq_en) {
+ /*
+ * Enable interrupt mode:
+ * - Configure ICSR for auto-clear on read and
+ * push-pull output
+ * - Enable "result ready" interrupt in IER
+ */
+ ret = rfd77402_config_irq(client,
+ RFD77402_ICSR_CLR_CFG |
+ RFD77402_ICSR_INT_MODE,
+ RFD77402_IER_RESULT);
+ } else {
+ /*
+ * Disable all interrupts:
+ * - Clear ICSR configuration
+ * - Disable all interrupts in IER
+ */
+ ret = rfd77402_config_irq(client, 0, 0);
+ }
+ if (ret)
return ret;
/* I2C configuration */
@@ -275,7 +377,26 @@ static int rfd77402_probe(struct i2c_client *client)
data = iio_priv(indio_dev);
data->client = client;
- mutex_init(&data->lock);
+
+ ret = devm_mutex_init(&client->dev, &data->lock);
+ if (ret)
+ return ret;
+
+ init_completion(&data->completion);
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, rfd77402_interrupt_handler,
+ IRQF_ONESHOT,
+ "rfd77402", data);
+ if (ret)
+ return ret;
+
+ data->irq_en = true;
+ dev_dbg(&client->dev, "Using interrupt mode\n");
+ } else {
+ dev_dbg(&client->dev, "Using polling mode\n");
+ }
indio_dev->info = &rfd77402_info;
indio_dev->channels = rfd77402_channels;
@@ -283,7 +404,7 @@ static int rfd77402_probe(struct i2c_client *client)
indio_dev->name = RFD77402_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- ret = rfd77402_init(client);
+ ret = rfd77402_init(data);
if (ret < 0)
return ret;
@@ -301,7 +422,10 @@ static int rfd77402_suspend(struct device *dev)
static int rfd77402_resume(struct device *dev)
{
- return rfd77402_init(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rfd77402_data *data = iio_priv(indio_dev);
+
+ return rfd77402_init(data);
}
static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend,
@@ -313,10 +437,17 @@ static const struct i2c_device_id rfd77402_id[] = {
};
MODULE_DEVICE_TABLE(i2c, rfd77402_id);
+static const struct of_device_id rfd77402_of_match[] = {
+ { .compatible = "rfdigital,rfd77402" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rfd77402_of_match);
+
static struct i2c_driver rfd77402_driver = {
.driver = {
.name = RFD77402_DRV_NAME,
.pm = pm_sleep_ptr(&rfd77402_pm_ops),
+ .of_match_table = rfd77402_of_match,
},
.probe = rfd77402_probe,
.id_table = rfd77402_id,
diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c
index 10bd3f221929..d8d8c8936d17 100644
--- a/drivers/iio/temperature/tmp006.c
+++ b/drivers/iio/temperature/tmp006.c
@@ -356,12 +356,10 @@ static int tmp006_probe(struct i2c_client *client)
indio_dev->trig = iio_trigger_get(data->drdy_trig);
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_ONESHOT,
- "tmp006_irq",
- data->drdy_trig);
+ ret = devm_request_irq(&client->dev, client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, "tmp006_irq",
+ data->drdy_trig);
if (ret < 0)
return ret;
}
diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig
index 6e65e929791c..4fc17dd0dcd7 100644
--- a/drivers/iio/test/Kconfig
+++ b/drivers/iio/test/Kconfig
@@ -8,7 +8,6 @@ config IIO_GTS_KUNIT_TEST
tristate "Test IIO gain-time-scale helpers" if !KUNIT_ALL_TESTS
depends on KUNIT
select IIO_GTS_HELPER
- select TEST_KUNIT_DEVICE_HELPERS
default KUNIT_ALL_TESTS
help
build unit tests for the IIO light sensor gain-time-scale helpers.
diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
index f2e49bd97d31..882dcb0b4a5b 100644
--- a/drivers/interconnect/Kconfig
+++ b/drivers/interconnect/Kconfig
@@ -22,4 +22,18 @@ config INTERCONNECT_CLK
help
Support for wrapping clocks into the interconnect nodes.
+config INTERCONNECT_KUNIT_TEST
+ tristate "KUnit tests for Interconnect framework"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds the KUnit test suite for the generic system interconnect
+ framework.
+
+ The tests cover the core functionality of the interconnect subsystem,
+ including provider/consumer APIs, topology management, and bandwidth
+ aggregation logic.
+
+ If unsure, say N.
+
endif
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
index b0a9a6753b9d..dc4c7b657c9d 100644
--- a/drivers/interconnect/Makefile
+++ b/drivers/interconnect/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
obj-$(CONFIG_INTERCONNECT_SAMSUNG) += samsung/
obj-$(CONFIG_INTERCONNECT_CLK) += icc-clk.o
+
+obj-$(CONFIG_INTERCONNECT_KUNIT_TEST) += icc-kunit.o
diff --git a/drivers/interconnect/icc-kunit.c b/drivers/interconnect/icc-kunit.c
new file mode 100644
index 000000000000..bad2b583737b
--- /dev/null
+++ b/drivers/interconnect/icc-kunit.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for the Interconnect framework.
+ *
+ * Copyright (c) 2025 Kuan-Wei Chiu <visitorckw@gmail.com>
+ *
+ * This suite verifies the behavior of the interconnect core, including
+ * topology construction, bandwidth aggregation, and path lifecycle.
+ */
+
+#include <kunit/platform_device.h>
+#include <kunit/test.h>
+#include <linux/interconnect-provider.h>
+#include <linux/interconnect.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "internal.h"
+
+enum {
+ NODE_CPU,
+ NODE_GPU,
+ NODE_BUS,
+ NODE_DDR,
+ NODE_MAX
+};
+
+struct test_node_data {
+ int id;
+ const char *name;
+ int num_links;
+ int links[2];
+};
+
+/*
+ * Static Topology:
+ * CPU -\
+ * -> BUS -> DDR
+ * GPU -/
+ */
+static const struct test_node_data test_topology[] = {
+ { NODE_CPU, "cpu", 1, { NODE_BUS } },
+ { NODE_GPU, "gpu", 1, { NODE_BUS } },
+ { NODE_BUS, "bus", 1, { NODE_DDR } },
+ { NODE_DDR, "ddr", 0, { } },
+};
+
+struct icc_test_priv {
+ struct icc_provider provider;
+ struct platform_device *pdev;
+ struct icc_node *nodes[NODE_MAX];
+};
+
+static struct icc_node *get_node(struct icc_test_priv *priv, int id)
+{
+ int idx = id - NODE_CPU;
+
+ if (idx < 0 || idx >= ARRAY_SIZE(test_topology))
+ return NULL;
+ return priv->nodes[idx];
+}
+
+static int icc_test_set(struct icc_node *src, struct icc_node *dst)
+{
+ return 0;
+}
+
+static int icc_test_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
+ u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
+{
+ return icc_std_aggregate(node, tag, avg_bw, peak_bw, agg_avg, agg_peak);
+}
+
+static struct icc_node *icc_test_xlate(const struct of_phandle_args *spec, void *data)
+{
+ return NULL;
+}
+
+static int icc_test_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
+{
+ *avg = 0;
+ *peak = 0;
+
+ return 0;
+}
+
+static int icc_test_init(struct kunit *test)
+{
+ struct icc_test_priv *priv;
+ struct icc_node *node;
+ int i, j, ret;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+ test->priv = priv;
+
+ priv->pdev = kunit_platform_device_alloc(test, "icc-test-dev", -1);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->pdev);
+ KUNIT_ASSERT_EQ(test, kunit_platform_device_add(test, priv->pdev), 0);
+
+ priv->provider.set = icc_test_set;
+ priv->provider.aggregate = icc_test_aggregate;
+ priv->provider.xlate = icc_test_xlate;
+ priv->provider.get_bw = icc_test_get_bw;
+ priv->provider.dev = &priv->pdev->dev;
+ priv->provider.data = priv;
+ INIT_LIST_HEAD(&priv->provider.nodes);
+
+ ret = icc_provider_register(&priv->provider);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ for (i = 0; i < ARRAY_SIZE(test_topology); i++) {
+ const struct test_node_data *data = &test_topology[i];
+
+ node = icc_node_create(data->id);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
+
+ node->name = data->name;
+ icc_node_add(node, &priv->provider);
+ priv->nodes[i] = node;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(test_topology); i++) {
+ const struct test_node_data *data = &test_topology[i];
+ struct icc_node *src = get_node(priv, data->id);
+
+ for (j = 0; j < data->num_links; j++) {
+ ret = icc_link_create(src, data->links[j]);
+ KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Failed to link %s->%d",
+ src->name, data->links[j]);
+ }
+ }
+
+ icc_sync_state(&priv->pdev->dev);
+
+ return 0;
+}
+
+static void icc_test_exit(struct kunit *test)
+{
+ struct icc_test_priv *priv = test->priv;
+
+ icc_nodes_remove(&priv->provider);
+ icc_provider_deregister(&priv->provider);
+}
+
+/*
+ * Helper to construct a mock path.
+ *
+ * Because we are bypassing icc_get(), we must manually link the requests
+ * to the nodes' req_list so that icc_std_aggregate() can discover them.
+ */
+static struct icc_path *icc_test_create_path(struct kunit *test,
+ struct icc_node **nodes, int num)
+{
+ struct icc_path *path;
+ int i;
+
+ path = kunit_kzalloc(test, struct_size(path, reqs, num), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, path);
+
+ path->num_nodes = num;
+ for (i = 0; i < num; i++) {
+ path->reqs[i].node = nodes[i];
+ hlist_add_head(&path->reqs[i].req_node, &nodes[i]->req_list);
+ }
+ path->name = "mock-path";
+
+ return path;
+}
+
+static void icc_test_destroy_path(struct kunit *test, struct icc_path *path)
+{
+ int i;
+
+ for (i = 0; i < path->num_nodes; i++)
+ hlist_del(&path->reqs[i].req_node);
+
+ kunit_kfree(test, path);
+}
+
+static void icc_test_topology_integrity(struct kunit *test)
+{
+ struct icc_test_priv *priv = test->priv;
+ struct icc_node *cpu = get_node(priv, NODE_CPU);
+ struct icc_node *bus = get_node(priv, NODE_BUS);
+
+ KUNIT_EXPECT_EQ(test, cpu->num_links, 1);
+ KUNIT_EXPECT_PTR_EQ(test, cpu->links[0], bus);
+ KUNIT_EXPECT_PTR_EQ(test, cpu->provider, &priv->provider);
+}
+
+static void icc_test_set_bw(struct kunit *test)
+{
+ struct icc_test_priv *priv = test->priv;
+ struct icc_path *path;
+ struct icc_node *path_nodes[3];
+ int ret;
+
+ /* Path: CPU -> BUS -> DDR */
+ path_nodes[0] = get_node(priv, NODE_CPU);
+ path_nodes[1] = get_node(priv, NODE_BUS);
+ path_nodes[2] = get_node(priv, NODE_DDR);
+
+ path = icc_test_create_path(test, path_nodes, 3);
+
+ ret = icc_enable(path);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = icc_set_bw(path, 1000, 2000);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, path_nodes[0]->avg_bw, 1000);
+ KUNIT_EXPECT_EQ(test, path_nodes[0]->peak_bw, 2000);
+ KUNIT_EXPECT_EQ(test, path_nodes[1]->avg_bw, 1000);
+ KUNIT_EXPECT_EQ(test, path_nodes[1]->peak_bw, 2000);
+
+ icc_set_tag(path, 0xABC);
+ KUNIT_EXPECT_EQ(test, path->reqs[0].tag, 0xABC);
+
+ icc_disable(path);
+ KUNIT_EXPECT_EQ(test, path_nodes[0]->avg_bw, 0);
+
+ icc_test_destroy_path(test, path);
+}
+
+static void icc_test_aggregation(struct kunit *test)
+{
+ struct icc_test_priv *priv = test->priv;
+ struct icc_path *path_cpu, *path_gpu;
+ struct icc_node *nodes_cpu[3], *nodes_gpu[2];
+ struct icc_node *bus = get_node(priv, NODE_BUS);
+ int ret;
+
+ nodes_cpu[0] = get_node(priv, NODE_CPU);
+ nodes_cpu[1] = bus;
+ nodes_cpu[2] = get_node(priv, NODE_DDR);
+ path_cpu = icc_test_create_path(test, nodes_cpu, 3);
+
+ nodes_gpu[0] = get_node(priv, NODE_GPU);
+ nodes_gpu[1] = bus;
+ path_gpu = icc_test_create_path(test, nodes_gpu, 2);
+
+ icc_enable(path_cpu);
+ icc_enable(path_gpu);
+
+ ret = icc_set_bw(path_cpu, 1000, 1000);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, bus->avg_bw, 1000);
+
+ ret = icc_set_bw(path_gpu, 2000, 2000);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ /* Bus aggregates: CPU(1000) + GPU(2000) */
+ KUNIT_EXPECT_EQ(test, bus->avg_bw, 3000);
+ /* Peak aggregates: max(CPU, GPU) */
+ KUNIT_EXPECT_EQ(test, bus->peak_bw, 2000);
+
+ icc_test_destroy_path(test, path_cpu);
+ icc_test_destroy_path(test, path_gpu);
+}
+
+static void icc_test_bulk_ops(struct kunit *test)
+{
+ struct icc_test_priv *priv = test->priv;
+ struct icc_node *nodes_cpu[3], *nodes_gpu[2];
+ struct icc_bulk_data bulk[2];
+ int ret;
+
+ nodes_cpu[0] = get_node(priv, NODE_CPU);
+ nodes_cpu[1] = get_node(priv, NODE_BUS);
+ nodes_cpu[2] = get_node(priv, NODE_DDR);
+
+ nodes_gpu[0] = get_node(priv, NODE_GPU);
+ nodes_gpu[1] = get_node(priv, NODE_BUS);
+
+ bulk[0].path = icc_test_create_path(test, nodes_cpu, 3);
+ bulk[0].avg_bw = 500;
+ bulk[0].peak_bw = 500;
+
+ bulk[1].path = icc_test_create_path(test, nodes_gpu, 2);
+ bulk[1].avg_bw = 600;
+ bulk[1].peak_bw = 600;
+
+ ret = icc_bulk_set_bw(2, bulk);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ /* Paths disabled, bandwidth should be 0 */
+ KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 0);
+
+ ret = icc_bulk_enable(2, bulk);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ /* Paths enabled, aggregation applies */
+ KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 1100);
+
+ icc_bulk_disable(2, bulk);
+ KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 0);
+
+ icc_test_destroy_path(test, bulk[0].path);
+ icc_test_destroy_path(test, bulk[1].path);
+}
+
+static struct kunit_case icc_test_cases[] = {
+ KUNIT_CASE(icc_test_topology_integrity),
+ KUNIT_CASE(icc_test_set_bw),
+ KUNIT_CASE(icc_test_aggregation),
+ KUNIT_CASE(icc_test_bulk_ops),
+ {}
+};
+
+static struct kunit_suite icc_test_suite = {
+ .name = "interconnect",
+ .init = icc_test_init,
+ .exit = icc_test_exit,
+ .test_cases = icc_test_cases,
+};
+
+kunit_test_suite(icc_test_suite);
+
+MODULE_AUTHOR("Kuan-Wei Chiu <visitorckw@gmail.com>");
+MODULE_DESCRIPTION("KUnit tests for the Interconnect framework");
+MODULE_LICENSE("GPL");
diff --git a/drivers/interconnect/mediatek/Kconfig b/drivers/interconnect/mediatek/Kconfig
index 985c849efac3..9fd3f2170443 100644
--- a/drivers/interconnect/mediatek/Kconfig
+++ b/drivers/interconnect/mediatek/Kconfig
@@ -27,3 +27,10 @@ config INTERCONNECT_MTK_MT8195
help
This is a driver for the MediaTek bus interconnect on MT8195-based
platforms.
+
+config INTERCONNECT_MTK_MT8196
+ tristate "MediaTek MT8196 interconnect driver"
+ depends on INTERCONNECT_MTK_DVFSRC_EMI
+ help
+ This is a driver for the MediaTek bus interconnect on MT8196-based
+ platforms.
diff --git a/drivers/interconnect/mediatek/Makefile b/drivers/interconnect/mediatek/Makefile
index 8e2283a9a5b5..6bd656668f5d 100644
--- a/drivers/interconnect/mediatek/Makefile
+++ b/drivers/interconnect/mediatek/Makefile
@@ -3,3 +3,4 @@
obj-$(CONFIG_INTERCONNECT_MTK_DVFSRC_EMI) += icc-emi.o
obj-$(CONFIG_INTERCONNECT_MTK_MT8183) += mt8183.o
obj-$(CONFIG_INTERCONNECT_MTK_MT8195) += mt8195.o
+obj-$(CONFIG_INTERCONNECT_MTK_MT8195) += mt8196.o
diff --git a/drivers/interconnect/mediatek/icc-emi.c b/drivers/interconnect/mediatek/icc-emi.c
index 7da740b5fa8d..dfa3a9cd9399 100644
--- a/drivers/interconnect/mediatek/icc-emi.c
+++ b/drivers/interconnect/mediatek/icc-emi.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/dvfsrc.h>
@@ -22,7 +23,9 @@ static int mtk_emi_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
{
struct mtk_icc_node *in = node->data;
- *agg_avg += avg_bw;
+ if (check_add_overflow(*agg_avg, avg_bw, agg_avg))
+ *agg_avg = U32_MAX;
+
*agg_peak = max_t(u32, *agg_peak, peak_bw);
in->sum_avg = *agg_avg;
@@ -40,7 +43,7 @@ static int mtk_emi_icc_set(struct icc_node *src, struct icc_node *dst)
if (unlikely(!src->provider))
return -EINVAL;
- dev = src->provider->dev;
+ dev = src->provider->dev->parent;
switch (node->ep) {
case 0:
@@ -97,7 +100,7 @@ int mtk_emi_icc_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
- provider->dev = pdev->dev.parent;
+ provider->dev = dev;
provider->set = mtk_emi_icc_set;
provider->aggregate = mtk_emi_icc_aggregate;
provider->xlate = of_icc_xlate_onecell;
diff --git a/drivers/interconnect/mediatek/mt8196.c b/drivers/interconnect/mediatek/mt8196.c
new file mode 100644
index 000000000000..e9af32065be1
--- /dev/null
+++ b/drivers/interconnect/mediatek/mt8196.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/device.h>
+#include <linux/interconnect.h>
+#include <linux/interconnect-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/interconnect/mediatek,mt8196.h>
+
+#include "icc-emi.h"
+
+static struct mtk_icc_node ddr_emi = {
+ .name = "ddr-emi",
+ .id = SLAVE_DDR_EMI,
+ .ep = 1,
+};
+
+static struct mtk_icc_node mcusys = {
+ .name = "mcusys",
+ .id = MASTER_MCUSYS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mcu_port0 = {
+ .name = "mcu-port0",
+ .id = MASTER_MCU_0,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mcu_port1 = {
+ .name = "mcu-port1",
+ .id = MASTER_MCU_1,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mcu_port2 = {
+ .name = "mcu-port2",
+ .id = MASTER_MCU_2,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mcu_port3 = {
+ .name = "mcu-port3",
+ .id = MASTER_MCU_3,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mcu_port4 = {
+ .name = "mcu-port4",
+ .id = MASTER_MCU_4,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node gpu = {
+ .name = "gpu",
+ .id = MASTER_GPUSYS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mmsys = {
+ .name = "mmsys",
+ .id = MASTER_MMSYS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mm_vpu = {
+ .name = "mm-vpu",
+ .id = MASTER_MM_VPU,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MMSYS }
+};
+
+static struct mtk_icc_node mm_disp = {
+ .name = "mm-disp",
+ .id = MASTER_MM_DISP,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MMSYS }
+};
+
+static struct mtk_icc_node mm_vdec = {
+ .name = "mm-vdec",
+ .id = MASTER_MM_VDEC,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MMSYS }
+};
+
+static struct mtk_icc_node mm_venc = {
+ .name = "mm-venc",
+ .id = MASTER_MM_VENC,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MMSYS }
+};
+
+static struct mtk_icc_node mm_cam = {
+ .name = "mm-cam",
+ .id = MASTER_MM_CAM,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MMSYS }
+};
+
+static struct mtk_icc_node mm_img = {
+ .name = "mm-img",
+ .id = MASTER_MM_IMG,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MMSYS }
+};
+
+static struct mtk_icc_node mm_mdp = {
+ .name = "mm-mdp",
+ .id = MASTER_MM_MDP,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MMSYS }
+};
+
+static struct mtk_icc_node vpusys = {
+ .name = "vpusys",
+ .id = MASTER_VPUSYS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node vpu_port0 = {
+ .name = "vpu-port0",
+ .id = MASTER_VPU_0,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_VPUSYS }
+};
+
+static struct mtk_icc_node vpu_port1 = {
+ .name = "vpu-port1",
+ .id = MASTER_VPU_1,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_VPUSYS }
+};
+
+static struct mtk_icc_node mdlasys = {
+ .name = "mdlasys",
+ .id = MASTER_MDLASYS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node mdla_port0 = {
+ .name = "mdla-port0",
+ .id = MASTER_MDLA_0,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_MDLASYS }
+};
+
+static struct mtk_icc_node ufs = {
+ .name = "ufs",
+ .id = MASTER_UFS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node pcie = {
+ .name = "pcie",
+ .id = MASTER_PCIE,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node usb = {
+ .name = "usb",
+ .id = MASTER_USB,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node wifi = {
+ .name = "wifi",
+ .id = MASTER_WIFI,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node bt = {
+ .name = "bt",
+ .id = MASTER_BT,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node netsys = {
+ .name = "netsys",
+ .id = MASTER_NETSYS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node dbgif = {
+ .name = "dbgif",
+ .id = MASTER_DBGIF,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_DDR_EMI }
+};
+
+static struct mtk_icc_node hrt_ddr_emi = {
+ .name = "hrt-ddr-emi",
+ .id = SLAVE_HRT_DDR_EMI,
+ .ep = 2,
+};
+
+static struct mtk_icc_node hrt_mmsys = {
+ .name = "hrt-mmsys",
+ .id = MASTER_HRT_MMSYS,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_HRT_DDR_EMI }
+};
+
+static struct mtk_icc_node hrt_mm_disp = {
+ .name = "hrt-mm-disp",
+ .id = MASTER_HRT_MM_DISP,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_HRT_MMSYS }
+};
+
+static struct mtk_icc_node hrt_mm_vdec = {
+ .name = "hrt-mm-vdec",
+ .id = MASTER_HRT_MM_VDEC,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_HRT_MMSYS }
+};
+
+static struct mtk_icc_node hrt_mm_venc = {
+ .name = "hrt-mm-venc",
+ .id = MASTER_HRT_MM_VENC,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_HRT_MMSYS }
+};
+
+static struct mtk_icc_node hrt_mm_cam = {
+ .name = "hrt-mm-cam",
+ .id = MASTER_HRT_MM_CAM,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_HRT_MMSYS }
+};
+
+static struct mtk_icc_node hrt_mm_img = {
+ .name = "hrt-mm-img",
+ .id = MASTER_HRT_MM_IMG,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_HRT_MMSYS }
+};
+
+static struct mtk_icc_node hrt_mm_mdp = {
+ .name = "hrt-mm-mdp",
+ .id = MASTER_HRT_MM_MDP,
+ .ep = 0,
+ .num_links = 1,
+ .links = { MASTER_HRT_MMSYS }
+};
+
+static struct mtk_icc_node hrt_adsp = {
+ .name = "hrt-adsp",
+ .id = MASTER_HRT_ADSP,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_HRT_DDR_EMI }
+};
+
+static struct mtk_icc_node hrt_dbgif = {
+ .name = "hrt-dbgif",
+ .id = MASTER_HRT_DBGIF,
+ .ep = 0,
+ .num_links = 1,
+ .links = { SLAVE_HRT_DDR_EMI }
+};
+
+static struct mtk_icc_node *mt8196_emi_icc_nodes[] = {
+ [SLAVE_DDR_EMI] = &ddr_emi,
+ [MASTER_MCUSYS] = &mcusys,
+ [MASTER_MCU_0] = &mcu_port0,
+ [MASTER_MCU_1] = &mcu_port1,
+ [MASTER_MCU_2] = &mcu_port2,
+ [MASTER_MCU_3] = &mcu_port3,
+ [MASTER_MCU_4] = &mcu_port4,
+ [MASTER_GPUSYS] = &gpu,
+ [MASTER_MMSYS] = &mmsys,
+ [MASTER_MM_VPU] = &mm_vpu,
+ [MASTER_MM_DISP] = &mm_disp,
+ [MASTER_MM_VDEC] = &mm_vdec,
+ [MASTER_MM_VENC] = &mm_venc,
+ [MASTER_MM_CAM] = &mm_cam,
+ [MASTER_MM_IMG] = &mm_img,
+ [MASTER_MM_MDP] = &mm_mdp,
+ [MASTER_VPUSYS] = &vpusys,
+ [MASTER_VPU_0] = &vpu_port0,
+ [MASTER_VPU_1] = &vpu_port1,
+ [MASTER_MDLASYS] = &mdlasys,
+ [MASTER_MDLA_0] = &mdla_port0,
+ [MASTER_UFS] = &ufs,
+ [MASTER_PCIE] = &pcie,
+ [MASTER_USB] = &usb,
+ [MASTER_WIFI] = &wifi,
+ [MASTER_BT] = &bt,
+ [MASTER_NETSYS] = &netsys,
+ [MASTER_DBGIF] = &dbgif,
+ [SLAVE_HRT_DDR_EMI] = &hrt_ddr_emi,
+ [MASTER_HRT_MMSYS] = &hrt_mmsys,
+ [MASTER_HRT_MM_DISP] = &hrt_mm_disp,
+ [MASTER_HRT_MM_VDEC] = &hrt_mm_vdec,
+ [MASTER_HRT_MM_VENC] = &hrt_mm_venc,
+ [MASTER_HRT_MM_CAM] = &hrt_mm_cam,
+ [MASTER_HRT_MM_IMG] = &hrt_mm_img,
+ [MASTER_HRT_MM_MDP] = &hrt_mm_mdp,
+ [MASTER_HRT_ADSP] = &hrt_adsp,
+ [MASTER_HRT_DBGIF] = &hrt_dbgif
+};
+
+static struct mtk_icc_desc mt8196_emi_icc = {
+ .nodes = mt8196_emi_icc_nodes,
+ .num_nodes = ARRAY_SIZE(mt8196_emi_icc_nodes),
+};
+
+static const struct of_device_id mtk_mt8196_emi_icc_of_match[] = {
+ { .compatible = "mediatek,mt8196-emi", .data = &mt8196_emi_icc },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_mt8196_emi_icc_of_match);
+
+static struct platform_driver mtk_emi_icc_mt8196_driver = {
+ .driver = {
+ .name = "emi-icc-mt8196",
+ .of_match_table = mtk_mt8196_emi_icc_of_match,
+ .sync_state = icc_sync_state,
+ },
+ .probe = mtk_emi_icc_probe,
+ .remove = mtk_emi_icc_remove,
+
+};
+module_platform_driver(mtk_emi_icc_mt8196_driver);
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_DESCRIPTION("MediaTek MT8196 EMI ICC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c
index 469fc48ebfe9..3239edc37f02 100644
--- a/drivers/interconnect/qcom/msm8974.c
+++ b/drivers/interconnect/qcom/msm8974.c
@@ -173,9 +173,6 @@ enum {
MSM8974_SNOC_SLV_QDSS_STM,
};
-#define RPM_BUS_MASTER_REQ 0x73616d62
-#define RPM_BUS_SLAVE_REQ 0x766c7362
-
#define to_msm8974_icc_provider(_provider) \
container_of(_provider, struct msm8974_icc_provider, provider)
diff --git a/drivers/interconnect/qcom/qcs8300.c b/drivers/interconnect/qcom/qcs8300.c
index 70a377bbcf29..bc403a9bf68c 100644
--- a/drivers/interconnect/qcom/qcs8300.c
+++ b/drivers/interconnect/qcom/qcs8300.c
@@ -629,7 +629,7 @@ static struct qcom_icc_node qxm_nsp = {
.name = "qxm_nsp",
.channels = 2,
.buswidth = 32,
- .num_links = 1,
+ .num_links = 2,
.link_nodes = { &qns_hcp, &qns_nsp_gemnoc },
};
diff --git a/drivers/interconnect/qcom/smd-rpm.c b/drivers/interconnect/qcom/smd-rpm.c
index 8316c87a2c60..dbc7ae50b02b 100644
--- a/drivers/interconnect/qcom/smd-rpm.c
+++ b/drivers/interconnect/qcom/smd-rpm.c
@@ -14,7 +14,6 @@
#include "icc-rpm.h"
#define RPM_KEY_BW 0x00007762
-#define QCOM_RPM_SMD_KEY_RATE 0x007a484b
static struct qcom_smd_rpm *icc_smd_rpm;
diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c
index c1367223e71a..3d487d75c483 100644
--- a/drivers/mcb/mcb-core.c
+++ b/drivers/mcb/mcb-core.c
@@ -85,7 +85,8 @@ static void mcb_remove(struct device *dev)
struct mcb_device *mdev = to_mcb_device(dev);
struct module *carrier_mod;
- mdrv->remove(mdev);
+ if (mdrv->remove)
+ mdrv->remove(mdev);
carrier_mod = mdev->dev.parent->driver->owner;
module_put(carrier_mod);
@@ -176,13 +177,13 @@ static const struct device_type mcb_carrier_device_type = {
* @owner: The @mcb_driver's module
* @mod_name: The name of the @mcb_driver's module
*
- * Register a @mcb_driver at the system. Perform some sanity checks, if
- * the .probe and .remove methods are provided by the driver.
+ * Register a @mcb_driver at the system. Perform a sanity check, if
+ * .probe method is provided by the driver.
*/
int __mcb_register_driver(struct mcb_driver *drv, struct module *owner,
const char *mod_name)
{
- if (!drv->probe || !drv->remove)
+ if (!drv->probe)
return -EINVAL;
drv->driver.owner = owner;
diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c
index 1f42d1d5a630..665a3888708a 100644
--- a/drivers/misc/bcm-vk/bcm_vk_msg.c
+++ b/drivers/misc/bcm-vk/bcm_vk_msg.c
@@ -1010,6 +1010,9 @@ ssize_t bcm_vk_read(struct file *p_file,
struct device *dev = &vk->pdev->dev;
struct bcm_vk_msg_chan *chan = &vk->to_h_msg_chan;
struct bcm_vk_wkent *entry = NULL, *iter;
+ struct vk_msg_blk tmp_msg;
+ u32 tmp_usr_msg_id;
+ u32 tmp_blks;
u32 q_num;
u32 rsp_length;
@@ -1034,6 +1037,9 @@ ssize_t bcm_vk_read(struct file *p_file,
entry = iter;
} else {
/* buffer not big enough */
+ tmp_msg = iter->to_h_msg[0];
+ tmp_usr_msg_id = iter->usr_msg_id;
+ tmp_blks = iter->to_h_blks;
rc = -EMSGSIZE;
}
goto read_loop_exit;
@@ -1052,14 +1058,12 @@ read_loop_exit:
bcm_vk_free_wkent(dev, entry);
} else if (rc == -EMSGSIZE) {
- struct vk_msg_blk tmp_msg = entry->to_h_msg[0];
-
/*
* in this case, return just the first block, so
* that app knows what size it is looking for.
*/
- set_msg_id(&tmp_msg, entry->usr_msg_id);
- tmp_msg.size = entry->to_h_blks - 1;
+ set_msg_id(&tmp_msg, tmp_usr_msg_id);
+ tmp_msg.size = tmp_blks - 1;
if (copy_to_user(buf, &tmp_msg, VK_MSGQ_BLK_SIZE) != 0) {
dev_err(dev, "Error return 1st block in -EMSGSIZE\n");
rc = -EFAULT;
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 883dfd0ed658..bc2cfb75d9bb 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -34,6 +34,7 @@
*/
#define FM25_SN_LEN 8 /* serial number length */
+#define FM25_MAX_ID_LEN 9 /* ID length */
#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */
struct at25_data {
@@ -44,6 +45,8 @@ struct at25_data {
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
u8 sernum[FM25_SN_LEN];
+ u8 id[FM25_MAX_ID_LEN];
+ u8 id_len;
};
#define AT25_WREN 0x06 /* latch the write enable */
@@ -64,8 +67,6 @@ struct at25_data {
#define AT25_INSTR_BIT3 0x08 /* additional address bit in instr */
-#define FM25_ID_LEN 9 /* ID length */
-
/*
* Specs often allow 5ms for a page write, sometimes 20ms;
* it's important to recover from write timeouts.
@@ -180,11 +181,25 @@ static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, ch
}
static DEVICE_ATTR_RO(sernum);
-static struct attribute *sernum_attrs[] = {
+static ssize_t jedec_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct at25_data *at25;
+
+ at25 = dev_get_drvdata(dev);
+
+ if (!at25->id_len)
+ return -EOPNOTSUPP;
+
+ return sysfs_emit(buf, "%*phN\n", at25->id_len, at25->id);
+}
+static DEVICE_ATTR_RO(jedec_id);
+
+static struct attribute *at25_attrs[] = {
&dev_attr_sernum.attr,
+ &dev_attr_jedec_id.attr,
NULL,
};
-ATTRIBUTE_GROUPS(sernum);
+ATTRIBUTE_GROUPS(at25);
/*
* Poll Read Status Register with timeout
@@ -378,7 +393,7 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
{
struct at25_data *at25 = container_of(chip, struct at25_data, chip);
u8 sernum[FM25_SN_LEN];
- u8 id[FM25_ID_LEN];
+ u8 id[FM25_MAX_ID_LEN];
u32 val;
int i;
@@ -388,7 +403,12 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
chip->byte_len = val;
} else {
/* Get ID of chip */
- fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN);
+ fm25_aux_read(at25, id, FM25_RDID, FM25_MAX_ID_LEN);
+
+ /* Store the unprocessed ID for exposing via sysfs */
+ memcpy(at25->id, id, FM25_MAX_ID_LEN);
+ at25->id_len = FM25_MAX_ID_LEN;
+
/* There are inside-out FRAM variations, detect them and reverse the ID bytes */
if (id[6] == 0x7f && id[2] == 0xc2)
for (i = 0; i < ARRAY_SIZE(id) / 2; i++) {
@@ -398,30 +418,42 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
id[i] = id[j];
id[j] = tmp;
}
- if (id[6] != 0xc2) {
- dev_err(dev, "Error: no Cypress FRAM with device ID (manufacturer ID bank 7: %02x)\n", id[6]);
- return -ENODEV;
- }
- switch (id[7]) {
- case 0x21 ... 0x26:
- chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024;
- break;
- case 0x2a ... 0x30:
- /* CY15B102QN ... CY15B116QN */
- chip->byte_len = BIT(((id[7] >> 1) & 0xf) + 13);
- break;
- default:
- dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]);
+ if (id[6] == 0xc2) {
+ at25->id_len = 9;
+ switch (id[7]) {
+ case 0x21 ... 0x26:
+ chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024;
+ break;
+ case 0x2a ... 0x30:
+ /* CY15B102QN ... CY15B116QN */
+ chip->byte_len = BIT(((id[7] >> 1) & 0xf) + 13);
+ break;
+ default:
+ dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]);
+ return -ENODEV;
+ }
+ } else if (id[2] == 0x82 && id[3] == 0x06) {
+ at25->id_len = 8;
+ switch (id[1]) {
+ case 0x51 ... 0x54:
+ /* CY15B102QSN ... CY15B204QSN */
+ chip->byte_len = BIT(((id[0] >> 3) & 0x1F) + 9);
+ break;
+ default:
+ dev_err(dev, "Error: unsupported product id %02x\n", id[1]);
+ return -ENODEV;
+ }
+ } else {
+ dev_err(dev, "Error: unrecognized JEDEC ID format: %*ph\n",
+ FM25_MAX_ID_LEN, id);
return -ENODEV;
}
- if (id[8]) {
- fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN);
- /* Swap byte order */
- for (i = 0; i < FM25_SN_LEN; i++)
- at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i];
- }
+ fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN);
+ /* Swap byte order */
+ for (i = 0; i < FM25_SN_LEN; i++)
+ at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i];
}
if (chip->byte_len > 64 * 1024)
@@ -539,7 +571,7 @@ static struct spi_mem_driver at25_driver = {
.driver = {
.name = "at25",
.of_match_table = at25_of_match,
- .dev_groups = sernum_groups,
+ .dev_groups = at25_groups,
},
.id_table = at25_spi_ids,
},
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c
index 9cae6f530679..5230e910a1d1 100644
--- a/drivers/misc/eeprom/eeprom_93xx46.c
+++ b/drivers/misc/eeprom/eeprom_93xx46.c
@@ -45,6 +45,7 @@ struct eeprom_93xx46_platform_data {
#define OP_START 0x4
#define OP_WRITE (OP_START | 0x1)
#define OP_READ (OP_START | 0x2)
+/* The following addresses are offset for the 1K EEPROM variant in 16-bit mode */
#define ADDR_EWDS 0x00
#define ADDR_ERAL 0x20
#define ADDR_EWEN 0x30
@@ -191,10 +192,7 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
bits = edev->addrlen + 3;
cmd_addr = OP_START << edev->addrlen;
- if (edev->pdata->flags & EE_ADDR8)
- cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1;
- else
- cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS);
+ cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << (edev->addrlen - 6);
if (has_quirk_instruction_length(edev)) {
cmd_addr <<= 2;
@@ -328,10 +326,7 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
bits = edev->addrlen + 3;
cmd_addr = OP_START << edev->addrlen;
- if (edev->pdata->flags & EE_ADDR8)
- cmd_addr |= ADDR_ERAL << 1;
- else
- cmd_addr |= ADDR_ERAL;
+ cmd_addr |= ADDR_ERAL << (edev->addrlen - 6);
if (has_quirk_instruction_length(edev)) {
cmd_addr <<= 2;
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index ee652ef01534..4f5a79c50f58 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -22,6 +22,7 @@
#include <linux/firmware/qcom/qcom_scm.h>
#include <uapi/misc/fastrpc.h>
#include <linux/of_reserved_mem.h>
+#include <linux/bits.h>
#define ADSP_DOMAIN_ID (0)
#define MDSP_DOMAIN_ID (1)
@@ -33,7 +34,6 @@
#define FASTRPC_ALIGN 128
#define FASTRPC_MAX_FDLIST 16
#define FASTRPC_MAX_CRCLIST 64
-#define FASTRPC_PHYS(p) ((p) & 0xffffffff)
#define FASTRPC_CTX_MAX (256)
#define FASTRPC_INIT_HANDLE 1
#define FASTRPC_DSP_UTILITIES_HANDLE 2
@@ -106,7 +106,7 @@
#define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev)
struct fastrpc_phy_page {
- u64 addr; /* physical address */
+ dma_addr_t addr; /* dma address */
u64 size; /* size of contiguous region */
};
@@ -171,7 +171,7 @@ struct fastrpc_msg {
u64 ctx; /* invoke caller context */
u32 handle; /* handle to invoke */
u32 sc; /* scalars structure describing the data */
- u64 addr; /* physical address */
+ dma_addr_t addr; /* dma address */
u64 size; /* size of contiguous region */
};
@@ -194,7 +194,7 @@ struct fastrpc_buf {
struct dma_buf *dmabuf;
struct device *dev;
void *virt;
- u64 phys;
+ dma_addr_t dma_addr;
u64 size;
/* Lock for dma buf attachments */
struct mutex lock;
@@ -217,7 +217,7 @@ struct fastrpc_map {
struct dma_buf *buf;
struct sg_table *table;
struct dma_buf_attachment *attach;
- u64 phys;
+ dma_addr_t dma_addr;
u64 size;
void *va;
u64 len;
@@ -257,6 +257,12 @@ struct fastrpc_session_ctx {
bool valid;
};
+struct fastrpc_soc_data {
+ u32 sid_pos;
+ u32 dma_addr_bits_cdsp;
+ u32 dma_addr_bits_default;
+};
+
struct fastrpc_channel_ctx {
int domain_id;
int sesscount;
@@ -278,6 +284,7 @@ struct fastrpc_channel_ctx {
bool secure;
bool unsigned_support;
u64 dma_mask;
+ const struct fastrpc_soc_data *soc_data;
};
struct fastrpc_device {
@@ -305,6 +312,24 @@ struct fastrpc_user {
struct mutex mutex;
};
+/* Extract SMMU PA from consolidated IOVA */
+static inline dma_addr_t fastrpc_ipa_to_dma_addr(struct fastrpc_channel_ctx *cctx, dma_addr_t iova)
+{
+ if (!cctx->soc_data->sid_pos)
+ return 0;
+ return iova & GENMASK_ULL(cctx->soc_data->sid_pos - 1, 0);
+}
+
+/*
+ * Prepare the consolidated iova to send to DSP by prepending the SID
+ * to smmu PA at the appropriate position
+ */
+static inline u64 fastrpc_sid_offset(struct fastrpc_channel_ctx *cctx,
+ struct fastrpc_session_ctx *sctx)
+{
+ return (u64)sctx->sid << cctx->soc_data->sid_pos;
+}
+
static void fastrpc_free_map(struct kref *ref)
{
struct fastrpc_map *map;
@@ -320,11 +345,12 @@ static void fastrpc_free_map(struct kref *ref)
perm.vmid = QCOM_SCM_VMID_HLOS;
perm.perm = QCOM_SCM_PERM_RWX;
- err = qcom_scm_assign_mem(map->phys, map->len,
+ err = qcom_scm_assign_mem(map->dma_addr, map->len,
&src_perms, &perm, 1);
if (err) {
- dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d\n",
- map->phys, map->len, err);
+ dev_err(map->fl->sctx->dev,
+ "Failed to assign memory dma_addr %pad size 0x%llx err %d\n",
+ &map->dma_addr, map->len, err);
return;
}
}
@@ -389,7 +415,7 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
static void fastrpc_buf_free(struct fastrpc_buf *buf)
{
dma_free_coherent(buf->dev, buf->size, buf->virt,
- FASTRPC_PHYS(buf->phys));
+ fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr));
kfree(buf);
}
@@ -408,12 +434,12 @@ static int __fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
buf->fl = fl;
buf->virt = NULL;
- buf->phys = 0;
+ buf->dma_addr = 0;
buf->size = size;
buf->dev = dev;
buf->raddr = 0;
- buf->virt = dma_alloc_coherent(dev, buf->size, (dma_addr_t *)&buf->phys,
+ buf->virt = dma_alloc_coherent(dev, buf->size, &buf->dma_addr,
GFP_KERNEL);
if (!buf->virt) {
mutex_destroy(&buf->lock);
@@ -439,7 +465,7 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
buf = *obuf;
if (fl->sctx && fl->sctx->sid)
- buf->phys += ((u64)fl->sctx->sid << 32);
+ buf->dma_addr += fastrpc_sid_offset(fl->cctx, fl->sctx);
return 0;
}
@@ -684,7 +710,8 @@ static int fastrpc_dma_buf_attach(struct dma_buf *dmabuf,
return -ENOMEM;
ret = dma_get_sgtable(buffer->dev, &a->sgt, buffer->virt,
- FASTRPC_PHYS(buffer->phys), buffer->size);
+ fastrpc_ipa_to_dma_addr(buffer->fl->cctx, buffer->dma_addr),
+ buffer->size);
if (ret < 0) {
dev_err(buffer->dev, "failed to get scatterlist from DMA API\n");
kfree(a);
@@ -733,7 +760,7 @@ static int fastrpc_mmap(struct dma_buf *dmabuf,
dma_resv_assert_held(dmabuf->resv);
return dma_mmap_coherent(buf->dev, vma, buf->virt,
- FASTRPC_PHYS(buf->phys), size);
+ fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr), size);
}
static const struct dma_buf_ops fastrpc_dma_buf_ops = {
@@ -746,6 +773,11 @@ static const struct dma_buf_ops fastrpc_dma_buf_ops = {
.release = fastrpc_release,
};
+static dma_addr_t fastrpc_compute_dma_addr(struct fastrpc_user *fl, dma_addr_t sg_dma_addr)
+{
+ return sg_dma_addr + fastrpc_sid_offset(fl->cctx, fl->sctx);
+}
+
static int fastrpc_map_attach(struct fastrpc_user *fl, int fd,
u64 len, u32 attr, struct fastrpc_map **ppmap)
{
@@ -784,12 +816,10 @@ static int fastrpc_map_attach(struct fastrpc_user *fl, int fd,
}
map->table = table;
- if (attr & FASTRPC_ATTR_SECUREMAP) {
- map->phys = sg_phys(map->table->sgl);
- } else {
- map->phys = sg_dma_address(map->table->sgl);
- map->phys += ((u64)fl->sctx->sid << 32);
- }
+ if (attr & FASTRPC_ATTR_SECUREMAP)
+ map->dma_addr = sg_phys(map->table->sgl);
+ else
+ map->dma_addr = fastrpc_compute_dma_addr(fl, sg_dma_address(map->table->sgl));
for_each_sg(map->table->sgl, sgl, map->table->nents,
sgl_index)
map->size += sg_dma_len(sgl);
@@ -815,10 +845,11 @@ static int fastrpc_map_attach(struct fastrpc_user *fl, int fd,
dst_perms[1].vmid = fl->cctx->vmperms[0].vmid;
dst_perms[1].perm = QCOM_SCM_PERM_RWX;
map->attr = attr;
- err = qcom_scm_assign_mem(map->phys, (u64)map->len, &src_perms, dst_perms, 2);
+ err = qcom_scm_assign_mem(map->dma_addr, (u64)map->len, &src_perms, dst_perms, 2);
if (err) {
- dev_err(sess->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d\n",
- map->phys, map->len, err);
+ dev_err(sess->dev,
+ "Failed to assign memory with dma_addr %pad size 0x%llx err %d\n",
+ &map->dma_addr, map->len, err);
goto map_err;
}
}
@@ -1009,7 +1040,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
struct vm_area_struct *vma = NULL;
rpra[i].buf.pv = (u64) ctx->args[i].ptr;
- pages[i].addr = ctx->maps[i]->phys;
+ pages[i].addr = ctx->maps[i]->dma_addr;
mmap_read_lock(current->mm);
vma = find_vma(current->mm, ctx->args[i].ptr);
@@ -1036,7 +1067,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
goto bail;
rpra[i].buf.pv = args - ctx->olaps[oix].offset;
- pages[i].addr = ctx->buf->phys -
+ pages[i].addr = ctx->buf->dma_addr -
ctx->olaps[oix].offset +
(pkt_size - rlen);
pages[i].addr = pages[i].addr & PAGE_MASK;
@@ -1068,7 +1099,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
list[i].num = ctx->args[i].length ? 1 : 0;
list[i].pgidx = i;
if (ctx->maps[i]) {
- pages[i].addr = ctx->maps[i]->phys;
+ pages[i].addr = ctx->maps[i]->dma_addr;
pages[i].size = ctx->maps[i]->size;
}
rpra[i].dma.fd = ctx->args[i].fd;
@@ -1150,7 +1181,7 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx,
msg->ctx = ctx->ctxid | fl->pd;
msg->handle = handle;
msg->sc = ctx->sc;
- msg->addr = ctx->buf ? ctx->buf->phys : 0;
+ msg->addr = ctx->buf ? ctx->buf->dma_addr : 0;
msg->size = roundup(ctx->msg_sz, PAGE_SIZE);
fastrpc_context_get(ctx);
@@ -1306,13 +1337,15 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
if (fl->cctx->vmcount) {
u64 src_perms = BIT(QCOM_SCM_VMID_HLOS);
- err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys,
+ err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
(u64)fl->cctx->remote_heap->size,
&src_perms,
fl->cctx->vmperms, fl->cctx->vmcount);
if (err) {
- dev_err(fl->sctx->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d\n",
- fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err);
+ dev_err(fl->sctx->dev,
+ "Failed to assign memory with dma_addr %pad size 0x%llx err %d\n",
+ &fl->cctx->remote_heap->dma_addr,
+ fl->cctx->remote_heap->size, err);
goto err_map;
}
scm_done = true;
@@ -1332,7 +1365,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
args[1].length = inbuf.namelen;
args[1].fd = -1;
- pages[0].addr = fl->cctx->remote_heap->phys;
+ pages[0].addr = fl->cctx->remote_heap->dma_addr;
pages[0].size = fl->cctx->remote_heap->size;
args[2].ptr = (u64)(uintptr_t) pages;
@@ -1361,12 +1394,12 @@ err_invoke:
dst_perms.vmid = QCOM_SCM_VMID_HLOS;
dst_perms.perm = QCOM_SCM_PERM_RWX;
- err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys,
+ err = qcom_scm_assign_mem(fl->cctx->remote_heap->dma_addr,
(u64)fl->cctx->remote_heap->size,
&src_perms, &dst_perms, 1);
if (err)
- dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d\n",
- fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err);
+ dev_err(fl->sctx->dev, "Failed to assign memory dma_addr %pad size 0x%llx err %d\n",
+ &fl->cctx->remote_heap->dma_addr, fl->cctx->remote_heap->size, err);
}
err_map:
fastrpc_buf_free(fl->cctx->remote_heap);
@@ -1455,7 +1488,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl,
args[2].length = inbuf.filelen;
args[2].fd = init.filefd;
- pages[0].addr = imem->phys;
+ pages[0].addr = imem->dma_addr;
pages[0].size = imem->size;
args[3].ptr = (u64)(uintptr_t) pages;
@@ -1913,7 +1946,7 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
args[0].ptr = (u64) (uintptr_t) &req_msg;
args[0].length = sizeof(req_msg);
- pages.addr = buf->phys;
+ pages.addr = buf->dma_addr;
pages.size = buf->size;
args[1].ptr = (u64) (uintptr_t) &pages;
@@ -1941,11 +1974,12 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
if (req.flags == ADSP_MMAP_REMOTE_HEAP_ADDR && fl->cctx->vmcount) {
u64 src_perms = BIT(QCOM_SCM_VMID_HLOS);
- err = qcom_scm_assign_mem(buf->phys, (u64)buf->size,
+ err = qcom_scm_assign_mem(buf->dma_addr, (u64)buf->size,
&src_perms, fl->cctx->vmperms, fl->cctx->vmcount);
if (err) {
- dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
- buf->phys, buf->size, err);
+ dev_err(fl->sctx->dev,
+ "Failed to assign memory dma_addr %pad size 0x%llx err %d",
+ &buf->dma_addr, buf->size, err);
goto err_assign;
}
}
@@ -2059,7 +2093,7 @@ static int fastrpc_req_mem_map(struct fastrpc_user *fl, char __user *argp)
args[0].ptr = (u64) (uintptr_t) &req_msg;
args[0].length = sizeof(req_msg);
- pages.addr = map->phys;
+ pages.addr = map->dma_addr;
pages.size = map->len;
args[1].ptr = (u64) (uintptr_t) &pages;
@@ -2165,6 +2199,7 @@ static int fastrpc_cb_probe(struct platform_device *pdev)
int i, sessions = 0;
unsigned long flags;
int rc;
+ u32 dma_bits;
cctx = dev_get_drvdata(dev->parent);
if (!cctx)
@@ -2178,12 +2213,16 @@ static int fastrpc_cb_probe(struct platform_device *pdev)
spin_unlock_irqrestore(&cctx->lock, flags);
return -ENOSPC;
}
+ dma_bits = cctx->soc_data->dma_addr_bits_default;
sess = &cctx->session[cctx->sesscount++];
sess->used = false;
sess->valid = true;
sess->dev = dev;
dev_set_drvdata(dev, sess);
+ if (cctx->domain_id == CDSP_DOMAIN_ID)
+ dma_bits = cctx->soc_data->dma_addr_bits_cdsp;
+
if (of_property_read_u32(dev->of_node, "reg", &sess->sid))
dev_info(dev, "FastRPC Session ID not specified in DT\n");
@@ -2198,9 +2237,9 @@ static int fastrpc_cb_probe(struct platform_device *pdev)
}
}
spin_unlock_irqrestore(&cctx->lock, flags);
- rc = dma_set_mask(dev, DMA_BIT_MASK(32));
+ rc = dma_set_mask(dev, DMA_BIT_MASK(dma_bits));
if (rc) {
- dev_err(dev, "32-bit DMA enable failed\n");
+ dev_err(dev, "%u-bit DMA enable failed\n", dma_bits);
return rc;
}
@@ -2285,6 +2324,18 @@ static int fastrpc_get_domain_id(const char *domain)
return -EINVAL;
}
+static const struct fastrpc_soc_data kaanapali_soc_data = {
+ .sid_pos = 56,
+ .dma_addr_bits_cdsp = 34,
+ .dma_addr_bits_default = 32,
+};
+
+static const struct fastrpc_soc_data default_soc_data = {
+ .sid_pos = 32,
+ .dma_addr_bits_cdsp = 32,
+ .dma_addr_bits_default = 32,
+};
+
static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct device *rdev = &rpdev->dev;
@@ -2293,6 +2344,9 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
const char *domain;
bool secure_dsp;
unsigned int vmids[FASTRPC_MAX_VMIDS];
+ const struct fastrpc_soc_data *soc_data;
+
+ soc_data = device_get_match_data(rdev);
err = of_property_read_string(rdev->of_node, "label", &domain);
if (err) {
@@ -2345,6 +2399,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
data->secure = secure_dsp;
+ data->soc_data = soc_data;
switch (domain_id) {
case ADSP_DOMAIN_ID:
@@ -2482,7 +2537,8 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
}
static const struct of_device_id fastrpc_rpmsg_of_match[] = {
- { .compatible = "qcom,fastrpc" },
+ { .compatible = "qcom,kaanapali-fastrpc", .data = &kaanapali_soc_data },
+ { .compatible = "qcom,fastrpc", .data = &default_soc_data },
{ },
};
MODULE_DEVICE_TABLE(of, fastrpc_rpmsg_of_match);
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 3b7a041ea351..9d3218330f0a 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -1067,7 +1067,7 @@ static void kgdbts_run_tests(void)
configured = 0;
}
-static int kgdbts_option_setup(char *opt)
+static int __init kgdbts_option_setup(char *opt)
{
if (strlen(opt) >= MAX_CONFIG_LEN) {
printk(KERN_ERR "kgdbts: config string too long\n");
diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig
index f4eb307cd35e..5902dd1ee44b 100644
--- a/drivers/misc/mei/Kconfig
+++ b/drivers/misc/mei/Kconfig
@@ -2,7 +2,7 @@
# Copyright (c) 2003-2019, Intel Corporation. All rights reserved.
config INTEL_MEI
tristate "Intel Management Engine Interface"
- depends on X86 && PCI
+ depends on PCI
default X86_64 || MATOM
help
The Intel Management Engine (Intel ME) provides Manageability,
@@ -49,7 +49,7 @@ config INTEL_MEI_TXE
config INTEL_MEI_GSC
tristate "Intel MEI GSC embedded device"
depends on INTEL_MEI_ME
- depends on DRM_I915 || DRM_XE
+ depends on DRM_I915!=n || DRM_XE!=n || COMPILE_TEST
help
Intel auxiliary driver for GSC devices embedded in Intel graphics devices.
@@ -84,7 +84,7 @@ config INTEL_MEI_VSC
config INTEL_MEI_LB
tristate "Intel Late Binding (LB) support on ME Interface"
depends on INTEL_MEI_ME
- depends on DRM_XE
+ depends on DRM_XE!=n || COMPILE_TEST
help
Enable support for Intel Late Binding (LB) via the MEI interface.
diff --git a/drivers/misc/mei/gsc_proxy/Kconfig b/drivers/misc/mei/gsc_proxy/Kconfig
index ac78b9d1eccd..bd8f955f548e 100644
--- a/drivers/misc/mei/gsc_proxy/Kconfig
+++ b/drivers/misc/mei/gsc_proxy/Kconfig
@@ -4,7 +4,7 @@
config INTEL_MEI_GSC_PROXY
tristate "Intel GSC Proxy services of ME Interface"
depends on INTEL_MEI_ME
- depends on DRM_I915
+ depends on DRM_I915!=n || DRM_XE!=n || COMPILE_TEST
help
MEI Support for GSC Proxy Services on Intel platforms.
diff --git a/drivers/misc/mei/hdcp/Kconfig b/drivers/misc/mei/hdcp/Kconfig
index 631dd9651d7c..b9d5205c5b1a 100644
--- a/drivers/misc/mei/hdcp/Kconfig
+++ b/drivers/misc/mei/hdcp/Kconfig
@@ -4,7 +4,7 @@
config INTEL_MEI_HDCP
tristate "Intel HDCP2.2 services of ME Interface"
depends on INTEL_MEI_ME
- depends on DRM_I915 || DRM_XE
+ depends on DRM_I915!=n || DRM_XE!=n || COMPILE_TEST
help
MEI Support for HDCP2.2 Services on Intel platforms.
diff --git a/drivers/misc/mei/pxp/Kconfig b/drivers/misc/mei/pxp/Kconfig
index aa2dece4a927..2c5c00dc4b6f 100644
--- a/drivers/misc/mei/pxp/Kconfig
+++ b/drivers/misc/mei/pxp/Kconfig
@@ -4,7 +4,7 @@
config INTEL_MEI_PXP
tristate "Intel PXP services of ME Interface"
depends on INTEL_MEI_ME
- depends on DRM_I915 || DRM_XE
+ depends on DRM_I915!=n || DRM_XE!=n || COMPILE_TEST
help
MEI Support for PXP Services on Intel platforms.
diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c
index 7964e46c7448..8eb2b5ac9850 100644
--- a/drivers/misc/ti_fpc202.c
+++ b/drivers/misc/ti_fpc202.c
@@ -309,7 +309,6 @@ static void fpc202_remove_port(struct fpc202_priv *priv, int port_id)
static int fpc202_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
- struct device_node *i2c_handle;
struct fpc202_priv *priv;
int ret, port_id;
@@ -357,7 +356,7 @@ static int fpc202_probe(struct i2c_client *client)
bitmap_zero(priv->probed_ports, FPC202_NUM_PORTS);
- for_each_child_of_node(dev->of_node, i2c_handle) {
+ for_each_child_of_node_scoped(dev->of_node, i2c_handle) {
ret = of_property_read_u32(i2c_handle, "reg", &port_id);
if (ret) {
if (ret == -EINVAL)
diff --git a/drivers/most/core.c b/drivers/most/core.c
index da319d108ea1..40d63e38fef5 100644
--- a/drivers/most/core.c
+++ b/drivers/most/core.c
@@ -1282,19 +1282,28 @@ int most_register_interface(struct most_interface *iface)
int id;
struct most_channel *c;
- if (!iface || !iface->enqueue || !iface->configure ||
- !iface->poison_channel || (iface->num_channels > MAX_CHANNELS))
+ if (!iface)
return -EINVAL;
+ device_initialize(iface->dev);
+
+ if (!iface->enqueue || !iface->configure || !iface->poison_channel ||
+ (iface->num_channels > MAX_CHANNELS)) {
+ put_device(iface->dev);
+ return -EINVAL;
+ }
+
id = ida_alloc(&mdev_id, GFP_KERNEL);
if (id < 0) {
dev_err(iface->dev, "Failed to allocate device ID\n");
+ put_device(iface->dev);
return id;
}
iface->p = kzalloc(sizeof(*iface->p), GFP_KERNEL);
if (!iface->p) {
ida_free(&mdev_id, id);
+ put_device(iface->dev);
return -ENOMEM;
}
@@ -1304,7 +1313,7 @@ int most_register_interface(struct most_interface *iface)
iface->dev->bus = &mostbus;
iface->dev->groups = interface_attr_groups;
dev_set_drvdata(iface->dev, iface);
- if (device_register(iface->dev)) {
+ if (device_add(iface->dev)) {
dev_err(iface->dev, "Failed to register interface device\n");
kfree(iface->p);
put_device(iface->dev);
diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c
index 3409af1ffb80..0611ef28bb69 100644
--- a/drivers/mux/mmio.c
+++ b/drivers/mux/mmio.c
@@ -72,7 +72,7 @@ static int mux_mmio_probe(struct platform_device *pdev)
if (IS_ERR(base))
regmap = ERR_PTR(-ENODEV);
else
- regmap = regmap_init_mmio(dev, base, &mux_mmio_regmap_cfg);
+ regmap = devm_regmap_init_mmio(dev, base, &mux_mmio_regmap_cfg);
/* Fallback to checking the parent node on "real" errors. */
if (IS_ERR(regmap) && regmap != ERR_PTR(-EPROBE_DEFER)) {
regmap = dev_get_regmap(dev->parent, NULL);
diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
index acd76e9392d3..d2c44f7f9b62 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -34,7 +34,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
},
{
.num = 21,
@@ -48,7 +47,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = true,
},
};
@@ -99,7 +97,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
},
{
.num = 21,
@@ -113,7 +110,6 @@ static const struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = true,
},
};
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/mhi.c b/drivers/net/wireless/ath/ath12k/wifi7/mhi.c
index b8d972659314..988affafcfd1 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/mhi.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/mhi.c
@@ -20,7 +20,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_qcn9274[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
},
{
.num = 21,
@@ -34,7 +33,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_qcn9274[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = true,
},
};
@@ -85,7 +83,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_wcn7850[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = false,
},
{
.num = 21,
@@ -99,7 +96,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_wcn7850[] = {
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
- .auto_queue = true,
},
};
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index bf47a982cf62..74ddbd0f79b0 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -30,7 +30,7 @@ source "drivers/nvmem/layouts/Kconfig"
config NVMEM_AN8855_EFUSE
tristate "Airoha AN8855 eFuse support"
- depends on MFD_AIROHA_AN8855 || COMPILE_TEST
+ depends on COMPILE_TEST
help
Say y here to enable support for reading eFuses on Airoha AN8855
Switch. These are e.g. used to store factory programmed
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 387c88c55259..c6180cf1dd91 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -789,11 +789,10 @@ static int nvmem_validate_keepouts(struct nvmem_device *nvmem)
static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
{
struct device *dev = &nvmem->dev;
- struct device_node *child;
const __be32 *addr;
int len, ret;
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
struct nvmem_cell_info info = {0};
addr = of_get_property(child, "reg", &len);
@@ -801,7 +800,6 @@ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_nod
continue;
if (len < 2 * sizeof(u32)) {
dev_err(dev, "nvmem: invalid reg on %pOF\n", child);
- of_node_put(child);
return -EINVAL;
}
@@ -817,7 +815,6 @@ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_nod
info.nbits < 1 ||
info.bit_offset + info.nbits > BITS_PER_BYTE * info.bytes) {
dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
- of_node_put(child);
return -EINVAL;
}
}
@@ -830,7 +827,7 @@ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_nod
ret = nvmem_add_one_cell(nvmem, &info);
kfree(info.name);
if (ret) {
- of_node_put(child);
+ of_node_put(info.np);
return ret;
}
}
diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig
index b3f340ed3163..4ef02b3f2576 100644
--- a/drivers/pps/generators/Kconfig
+++ b/drivers/pps/generators/Kconfig
@@ -23,14 +23,6 @@ config PPS_GENERATOR_DUMMY
This driver can also be built as a module. If so, the module
will be called pps_gen-dummy.
-config PPS_GENERATOR_PARPORT
- tristate "Parallel port PPS signal generator"
- depends on PARPORT && BROKEN
- help
- If you say yes here you get support for a PPS signal generator which
- utilizes STROBE pin of a parallel port to send PPS signals. It uses
- parport abstraction layer and hrtimers to precisely control the signal.
-
config PPS_GENERATOR_TIO
tristate "TIO PPS signal generator"
depends on X86 && CPU_SUP_INTEL
diff --git a/drivers/pps/generators/Makefile b/drivers/pps/generators/Makefile
index e109920e8a2d..5d38774b4a56 100644
--- a/drivers/pps/generators/Makefile
+++ b/drivers/pps/generators/Makefile
@@ -7,7 +7,6 @@ pps_gen_core-y := pps_gen.o sysfs.o
obj-$(CONFIG_PPS_GENERATOR) := pps_gen_core.o
obj-$(CONFIG_PPS_GENERATOR_DUMMY) += pps_gen-dummy.o
-obj-$(CONFIG_PPS_GENERATOR_PARPORT) += pps_gen_parport.o
obj-$(CONFIG_PPS_GENERATOR_TIO) += pps_gen_tio.o
ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG
diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c
deleted file mode 100644
index 05bbf8d30ef1..000000000000
--- a/drivers/pps/generators/pps_gen_parport.c
+++ /dev/null
@@ -1,238 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * pps_gen_parport.c -- kernel parallel port PPS signal generator
- *
- * Copyright (C) 2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>
- */
-
-
-/*
- * TODO:
- * fix issues when realtime clock is adjusted in a leap
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/time.h>
-#include <linux/hrtimer.h>
-#include <linux/parport.h>
-
-#define SIGNAL 0
-#define NO_SIGNAL PARPORT_CONTROL_STROBE
-
-/* module parameters */
-
-#define SEND_DELAY_MAX 100000
-
-static unsigned int send_delay = 30000;
-MODULE_PARM_DESC(delay,
- "Delay between setting and dropping the signal (ns)");
-module_param_named(delay, send_delay, uint, 0);
-
-
-#define SAFETY_INTERVAL 3000 /* set the hrtimer earlier for safety (ns) */
-
-/* internal per port structure */
-struct pps_generator_pp {
- struct pardevice *pardev; /* parport device */
- struct hrtimer timer;
- long port_write_time; /* calibrated port write time (ns) */
-};
-
-static struct pps_generator_pp device = {
- .pardev = NULL,
-};
-
-static int attached;
-
-/* calibrated time between a hrtimer event and the reaction */
-static long hrtimer_error = SAFETY_INTERVAL;
-
-/* the kernel hrtimer event */
-static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
-{
- struct timespec64 expire_time, ts1, ts2, ts3, dts;
- struct pps_generator_pp *dev;
- struct parport *port;
- long lim, delta;
- unsigned long flags;
-
- /* We have to disable interrupts here. The idea is to prevent
- * other interrupts on the same processor to introduce random
- * lags while polling the clock. ktime_get_real_ts64() takes <1us on
- * most machines while other interrupt handlers can take much
- * more potentially.
- *
- * NB: approx time with blocked interrupts =
- * send_delay + 3 * SAFETY_INTERVAL
- */
- local_irq_save(flags);
-
- /* first of all we get the time stamp... */
- ktime_get_real_ts64(&ts1);
- expire_time = ktime_to_timespec64(hrtimer_get_softexpires(timer));
- dev = container_of(timer, struct pps_generator_pp, timer);
- lim = NSEC_PER_SEC - send_delay - dev->port_write_time;
-
- /* check if we are late */
- if (expire_time.tv_sec != ts1.tv_sec || ts1.tv_nsec > lim) {
- local_irq_restore(flags);
- pr_err("we are late this time %ptSp\n", &ts1);
- goto done;
- }
-
- /* busy loop until the time is right for an assert edge */
- do {
- ktime_get_real_ts64(&ts2);
- } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
-
- /* set the signal */
- port = dev->pardev->port;
- port->ops->write_control(port, SIGNAL);
-
- /* busy loop until the time is right for a clear edge */
- lim = NSEC_PER_SEC - dev->port_write_time;
- do {
- ktime_get_real_ts64(&ts2);
- } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
-
- /* unset the signal */
- port->ops->write_control(port, NO_SIGNAL);
-
- ktime_get_real_ts64(&ts3);
-
- local_irq_restore(flags);
-
- /* update calibrated port write time */
- dts = timespec64_sub(ts3, ts2);
- dev->port_write_time =
- (dev->port_write_time + timespec64_to_ns(&dts)) >> 1;
-
-done:
- /* update calibrated hrtimer error */
- dts = timespec64_sub(ts1, expire_time);
- delta = timespec64_to_ns(&dts);
- /* If the new error value is bigger then the old, use the new
- * value, if not then slowly move towards the new value. This
- * way it should be safe in bad conditions and efficient in
- * good conditions.
- */
- if (delta >= hrtimer_error)
- hrtimer_error = delta;
- else
- hrtimer_error = (3 * hrtimer_error + delta) >> 2;
-
- /* update the hrtimer expire time */
- hrtimer_set_expires(timer,
- ktime_set(expire_time.tv_sec + 1,
- NSEC_PER_SEC - (send_delay +
- dev->port_write_time + SAFETY_INTERVAL +
- 2 * hrtimer_error)));
-
- return HRTIMER_RESTART;
-}
-
-/* calibrate port write time */
-#define PORT_NTESTS_SHIFT 5
-static void calibrate_port(struct pps_generator_pp *dev)
-{
- struct parport *port = dev->pardev->port;
- int i;
- long acc = 0;
-
- for (i = 0; i < (1 << PORT_NTESTS_SHIFT); i++) {
- struct timespec64 a, b;
- unsigned long irq_flags;
-
- local_irq_save(irq_flags);
- ktime_get_real_ts64(&a);
- port->ops->write_control(port, NO_SIGNAL);
- ktime_get_real_ts64(&b);
- local_irq_restore(irq_flags);
-
- b = timespec64_sub(b, a);
- acc += timespec64_to_ns(&b);
- }
-
- dev->port_write_time = acc >> PORT_NTESTS_SHIFT;
- pr_info("port write takes %ldns\n", dev->port_write_time);
-}
-
-static inline ktime_t next_intr_time(struct pps_generator_pp *dev)
-{
- struct timespec64 ts;
-
- ktime_get_real_ts64(&ts);
-
- return ktime_set(ts.tv_sec +
- ((ts.tv_nsec > 990 * NSEC_PER_MSEC) ? 1 : 0),
- NSEC_PER_SEC - (send_delay +
- dev->port_write_time + 3 * SAFETY_INTERVAL));
-}
-
-static void parport_attach(struct parport *port)
-{
- struct pardev_cb pps_cb;
-
- if (send_delay > SEND_DELAY_MAX) {
- pr_err("delay value should be not greater then %d\n", SEND_DELAY_MAX);
- return;
- }
-
- if (attached) {
- /* we already have a port */
- return;
- }
-
- memset(&pps_cb, 0, sizeof(pps_cb));
- pps_cb.private = &device;
- pps_cb.flags = PARPORT_FLAG_EXCL;
- device.pardev = parport_register_dev_model(port, KBUILD_MODNAME,
- &pps_cb, 0);
- if (!device.pardev) {
- pr_err("couldn't register with %s\n", port->name);
- return;
- }
-
- if (parport_claim_or_block(device.pardev) < 0) {
- pr_err("couldn't claim %s\n", port->name);
- goto err_unregister_dev;
- }
-
- pr_info("attached to %s\n", port->name);
- attached = 1;
-
- calibrate_port(&device);
-
- hrtimer_setup(&device.timer, hrtimer_event, CLOCK_REALTIME, HRTIMER_MODE_ABS);
- hrtimer_start(&device.timer, next_intr_time(&device), HRTIMER_MODE_ABS);
-
- return;
-
-err_unregister_dev:
- parport_unregister_device(device.pardev);
-}
-
-static void parport_detach(struct parport *port)
-{
- if (port->cad != device.pardev)
- return; /* not our port */
-
- hrtimer_cancel(&device.timer);
- parport_release(device.pardev);
- parport_unregister_device(device.pardev);
-}
-
-static struct parport_driver pps_gen_parport_driver = {
- .name = KBUILD_MODNAME,
- .match_port = parport_attach,
- .detach = parport_detach,
-};
-module_parport_driver(pps_gen_parport_driver);
-
-MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
-MODULE_DESCRIPTION("parallel port PPS signal generator");
-MODULE_LICENSE("GPL");
diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index ba3d80d12605..7338b38697d0 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -1514,26 +1514,22 @@ static int of_qcom_slim_ngd_register(struct device *parent,
const struct ngd_reg_offset_data *data;
struct qcom_slim_ngd *ngd;
const struct of_device_id *match;
- struct device_node *node;
u32 id;
int ret;
match = of_match_node(qcom_slim_ngd_dt_match, parent->of_node);
data = match->data;
- for_each_available_child_of_node(parent->of_node, node) {
+ for_each_available_child_of_node_scoped(parent->of_node, node) {
if (of_property_read_u32(node, "reg", &id))
continue;
ngd = kzalloc(sizeof(*ngd), GFP_KERNEL);
- if (!ngd) {
- of_node_put(node);
+ if (!ngd)
return -ENOMEM;
- }
ngd->pdev = platform_device_alloc(QCOM_SLIM_NGD_DRV_NAME, id);
if (!ngd->pdev) {
kfree(ngd);
- of_node_put(node);
return -ENOMEM;
}
ngd->id = id;
@@ -1546,7 +1542,6 @@ static int of_qcom_slim_ngd_register(struct device *parent,
if (ret) {
platform_device_put(ngd->pdev);
kfree(ngd);
- of_node_put(node);
return ret;
}
ngd->pdev->dev.of_node = node;
@@ -1556,7 +1551,6 @@ static int of_qcom_slim_ngd_register(struct device *parent,
if (ret) {
platform_device_put(ngd->pdev);
kfree(ngd);
- of_node_put(node);
return ret;
}
ngd->base = ctrl->base + ngd->id * data->offset +
diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c
index 68276d195917..f6a75f0184c4 100644
--- a/drivers/spi/spi-fsi.c
+++ b/drivers/spi/spi-fsi.c
@@ -528,12 +528,12 @@ static size_t fsi_spi_max_transfer_size(struct spi_device *spi)
return SPI_FSI_MAX_RX_SIZE;
}
-static int fsi_spi_probe(struct device *dev)
+static int fsi_spi_probe(struct fsi_device *fsi)
{
int rc;
int num_controllers_registered = 0;
struct fsi2spi *bridge;
- struct fsi_device *fsi = to_fsi_dev(dev);
+ struct device *dev = &fsi->dev;
rc = fsi_spi_check_mux(fsi, dev);
if (rc)
@@ -590,10 +590,9 @@ MODULE_DEVICE_TABLE(fsi, fsi_spi_ids);
static struct fsi_driver fsi_spi_driver = {
.id_table = fsi_spi_ids,
+ .probe = fsi_spi_probe,
.drv = {
.name = "spi-fsi",
- .bus = &fsi_bus_type,
- .probe = fsi_spi_probe,
},
};
module_fsi_driver(fsi_spi_driver);
diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
index 697b3e8bb023..87e3ee9d4f2a 100644
--- a/drivers/spmi/spmi-apple-controller.c
+++ b/drivers/spmi/spmi-apple-controller.c
@@ -149,6 +149,7 @@ static int apple_spmi_probe(struct platform_device *pdev)
}
static const struct of_device_id apple_spmi_match_table[] = {
+ { .compatible = "apple,t8103-spmi", },
{ .compatible = "apple,spmi", },
{}
};
diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index 160d36f7d238..1048420b5afb 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -1,14 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2021 MediaTek Inc.
+// Copyright (c) 2025 Collabora Ltd
+// AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
#include <linux/clk.h>
+#include <linux/interrupt.h>
#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/spmi.h>
+#include <linux/irqchip/chained_irq.h>
#define SWINF_IDLE 0x00
#define SWINF_WFVLDCLR 0x06
@@ -20,11 +27,13 @@
#define PMIF_CMD_EXT_REG 2
#define PMIF_CMD_EXT_REG_LONG 3
-#define PMIF_DELAY_US 10
+#define PMIF_DELAY_US 2
#define PMIF_TIMEOUT_US (10 * 1000)
#define PMIF_CHAN_OFFSET 0x5
+#define PMIF_RCS_IRQ_MASK GENMASK(7, 0)
+#define PMIF_MAX_BUSES 2
#define PMIF_MAX_CLKS 3
#define SPMI_OP_ST_BUSY 1
@@ -41,16 +50,28 @@ struct pmif_data {
const u32 *regs;
const u32 *spmimst_regs;
u32 soc_chan;
+ u8 spmi_ver;
+ u32 num_spmi_buses;
};
-struct pmif {
+struct pmif_bus {
void __iomem *base;
void __iomem *spmimst_base;
- struct ch_reg chan;
+ struct spmi_controller *ctrl;
+ struct irq_domain *dom;
+ int irq;
struct clk_bulk_data clks[PMIF_MAX_CLKS];
size_t nclks;
+ u8 irq_min_sid;
+ u8 irq_max_sid;
+ u16 irq_en;
+ raw_spinlock_t lock;
+};
+
+struct pmif {
+ struct pmif_bus bus[PMIF_MAX_BUSES];
+ struct ch_reg chan;
const struct pmif_data *data;
- raw_spinlock_t lock;
};
static const char * const pmif_clock_names[] = {
@@ -262,33 +283,46 @@ static const u32 mt8195_spmi_regs[] = {
[SPMI_MST_DBG] = 0x00FC,
};
-static u32 pmif_readl(struct pmif *arb, enum pmif_regs reg)
+static inline struct pmif *to_mtk_pmif(struct spmi_controller *ctrl)
+{
+ return dev_get_drvdata(ctrl->dev.parent);
+}
+
+static u32 pmif_readl(struct pmif *arb, struct pmif_bus *pbus, enum pmif_regs reg)
{
- return readl(arb->base + arb->data->regs[reg]);
+ return readl(pbus->base + arb->data->regs[reg]);
}
-static void pmif_writel(struct pmif *arb, u32 val, enum pmif_regs reg)
+static void pmif_writel(struct pmif *arb, struct pmif_bus *pbus,
+ u32 val, enum pmif_regs reg)
{
- writel(val, arb->base + arb->data->regs[reg]);
+ writel(val, pbus->base + arb->data->regs[reg]);
}
-static void mtk_spmi_writel(struct pmif *arb, u32 val, enum spmi_regs reg)
+static u32 mtk_spmi_readl(struct pmif *arb, struct pmif_bus *pbus, enum spmi_regs reg)
{
- writel(val, arb->spmimst_base + arb->data->spmimst_regs[reg]);
+ return readl(pbus->spmimst_base + arb->data->spmimst_regs[reg]);
}
-static bool pmif_is_fsm_vldclr(struct pmif *arb)
+static void mtk_spmi_writel(struct pmif *arb, struct pmif_bus *pbus,
+ u32 val, enum spmi_regs reg)
+{
+ writel(val, pbus->spmimst_base + arb->data->spmimst_regs[reg]);
+}
+
+static bool pmif_is_fsm_vldclr(struct pmif *arb, struct pmif_bus *pbus)
{
u32 reg_rdata;
- reg_rdata = pmif_readl(arb, arb->chan.ch_sta);
+ reg_rdata = pmif_readl(arb, pbus, arb->chan.ch_sta);
return GET_SWINF(reg_rdata) == SWINF_WFVLDCLR;
}
static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
{
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif_bus *pbus = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = to_mtk_pmif(ctrl);
u32 rdata, cmd;
int ret;
@@ -298,8 +332,8 @@ static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
cmd = opc - SPMI_CMD_RESET;
- mtk_spmi_writel(arb, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL);
- ret = readl_poll_timeout_atomic(arb->spmimst_base + arb->data->spmimst_regs[SPMI_OP_ST_STA],
+ mtk_spmi_writel(arb, pbus, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL);
+ ret = readl_poll_timeout_atomic(pbus->spmimst_base + arb->data->spmimst_regs[SPMI_OP_ST_STA],
rdata, (rdata & SPMI_OP_ST_BUSY) == SPMI_OP_ST_BUSY,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0)
@@ -311,7 +345,8 @@ static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
u16 addr, u8 *buf, size_t len)
{
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif_bus *pbus = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = to_mtk_pmif(ctrl);
struct ch_reg *inf_reg;
int ret;
u32 data, cmd;
@@ -325,7 +360,6 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
if (len > 4) {
dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len);
-
return -EINVAL;
}
@@ -336,41 +370,43 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
else
return -EINVAL;
- raw_spin_lock_irqsave(&arb->lock, flags);
+ raw_spin_lock_irqsave(&pbus->lock, flags);
/* Wait for Software Interface FSM state to be IDLE. */
inf_reg = &arb->chan;
- ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
+ ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta],
data, GET_SWINF(data) == SWINF_IDLE,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0) {
/* set channel ready if the data has transferred */
- if (pmif_is_fsm_vldclr(arb))
- pmif_writel(arb, 1, inf_reg->ch_rdy);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ if (pmif_is_fsm_vldclr(arb, pbus))
+ pmif_writel(arb, pbus, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
return ret;
}
/* Send the command. */
cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr;
- pmif_writel(arb, cmd, inf_reg->ch_send);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ pmif_writel(arb, pbus, cmd, inf_reg->ch_send);
/*
* Wait for Software Interface FSM state to be WFVLDCLR,
* read the data and clear the valid flag.
*/
- ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
+ ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta],
data, GET_SWINF(data) == SWINF_WFVLDCLR,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0) {
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_WFVLDCLR\n");
return ret;
}
- data = pmif_readl(arb, inf_reg->rdata);
+ data = pmif_readl(arb, pbus, inf_reg->rdata);
+ pmif_writel(arb, pbus, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
+
memcpy(buf, &data, len);
- pmif_writel(arb, 1, inf_reg->ch_rdy);
return 0;
}
@@ -378,7 +414,8 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
u16 addr, const u8 *buf, size_t len)
{
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif_bus *pbus = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = to_mtk_pmif(ctrl);
struct ch_reg *inf_reg;
int ret;
u32 data, wdata, cmd;
@@ -409,31 +446,184 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
/* Set the write data. */
memcpy(&wdata, buf, len);
- raw_spin_lock_irqsave(&arb->lock, flags);
+ raw_spin_lock_irqsave(&pbus->lock, flags);
/* Wait for Software Interface FSM state to be IDLE. */
inf_reg = &arb->chan;
- ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
+ ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta],
data, GET_SWINF(data) == SWINF_IDLE,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0) {
/* set channel ready if the data has transferred */
- if (pmif_is_fsm_vldclr(arb))
- pmif_writel(arb, 1, inf_reg->ch_rdy);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ if (pmif_is_fsm_vldclr(arb, pbus))
+ pmif_writel(arb, pbus, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
return ret;
}
- pmif_writel(arb, wdata, inf_reg->wdata);
+ pmif_writel(arb, pbus, wdata, inf_reg->wdata);
/* Send the command. */
cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr;
- pmif_writel(arb, cmd, inf_reg->ch_send);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ pmif_writel(arb, pbus, cmd, inf_reg->ch_send);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
return 0;
}
+static void mtk_spmi_handle_chained_irq(struct irq_desc *desc)
+{
+ struct pmif_bus *pbus = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct pmif *arb = to_mtk_pmif(pbus->ctrl);
+ u8 regidx_min, regidx_max;
+ bool irq_handled = false;
+ unsigned int i;
+
+ regidx_min = pbus->irq_min_sid / 4;
+ regidx_min += SPMI_SLV_3_0_EINT;
+
+ regidx_max = pbus->irq_max_sid / 4;
+ regidx_max += SPMI_SLV_3_0_EINT;
+
+ chained_irq_enter(chip, desc);
+
+ for (i = regidx_min; i <= regidx_max; i++) {
+ u32 val = mtk_spmi_readl(arb, pbus, i);
+
+ while (val) {
+ u8 bit = __ffs(val);
+ u8 bank = bit / 7;
+ u8 sid = ((i - SPMI_SLV_3_0_EINT) * 4) + bank;
+
+ val &= ~(PMIF_RCS_IRQ_MASK << (8 * bank));
+
+ /* Check if IRQs for this SID are enabled */
+ if (!(pbus->irq_en & BIT(sid)))
+ continue;
+
+ generic_handle_domain_irq_safe(pbus->dom, sid);
+ irq_handled = true;
+ }
+ }
+
+ if (!irq_handled)
+ handle_bad_irq(desc);
+
+ chained_irq_exit(chip, desc);
+}
+
+static void mtk_spmi_rcs_irq_eoi(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+ struct pmif *arb = to_mtk_pmif(pbus->ctrl);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
+ unsigned int reg, shift;
+
+ /* There are four interrupts (8 bits each) per register */
+ reg = SPMI_SLV_3_0_EINT + d->hwirq / 4;
+ shift = (irq % 4) * 8;
+
+ mtk_spmi_writel(arb, pbus, PMIF_RCS_IRQ_MASK << shift, reg);
+}
+
+static void mtk_spmi_rcs_irq_enable(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
+
+ pbus->irq_en |= BIT(irq);
+}
+
+static void mtk_spmi_rcs_irq_disable(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
+
+ pbus->irq_en &= ~BIT(irq);
+}
+
+static int mtk_spmi_rcs_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+
+ return irq_set_irq_wake(pbus->irq, on);
+}
+
+static const struct irq_chip mtk_spmi_rcs_irq_chip = {
+ .name = "spmi_rcs",
+ .irq_eoi = mtk_spmi_rcs_irq_eoi,
+ .irq_enable = mtk_spmi_rcs_irq_enable,
+ .irq_disable = mtk_spmi_rcs_irq_disable,
+ .irq_set_wake = mtk_spmi_rcs_irq_set_wake,
+};
+
+static int mtk_spmi_rcs_irq_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ struct pmif_bus *pbus = d->host_data;
+ struct device *dev = &pbus->ctrl->dev;
+ u32 *intspec = fwspec->param;
+
+ if (intspec[0] > SPMI_MAX_SLAVE_ID)
+ return -EINVAL;
+
+ /*
+ * The IRQ number in intspec[1] is ignored on purpose here!
+ *
+ * The controller only has knowledge of which SID raised an interrupt
+ * and the type of irq, but doesn't know about any device irq number,
+ * hence that must be read from the SPMI device's registers.
+ */
+ *out_hwirq = intspec[0];
+ *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+
+ if (pbus->irq_min_sid > intspec[0])
+ pbus->irq_min_sid = intspec[0];
+
+ if (pbus->irq_max_sid < intspec[0])
+ pbus->irq_max_sid = intspec[0];
+
+ dev_dbg(dev, "Found SPMI IRQ %u (map: 0x%lx)\n", intspec[0], *out_hwirq);
+
+ return 0;
+}
+
+static struct lock_class_key mtk_spmi_rcs_irqlock_class, mtk_spmi_rcs_irqreq_class;
+
+static int mtk_spmi_rcs_irq_alloc(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct pmif_bus *pbus = d->host_data;
+ struct device *dev = &pbus->ctrl->dev;
+ struct irq_fwspec *fwspec = data;
+ irq_hw_number_t hwirq;
+ unsigned int irqtype;
+ int i, ret;
+
+ ret = mtk_spmi_rcs_irq_translate(d, fwspec, &hwirq, &irqtype);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ dev_dbg(dev, "Mapping IRQ%u (hwirq %lu) with type %u\n",
+ virq, hwirq, irqtype);
+
+ irq_set_lockdep_class(virq, &mtk_spmi_rcs_irqlock_class,
+ &mtk_spmi_rcs_irqreq_class);
+ irq_domain_set_info(d, virq, hwirq, &mtk_spmi_rcs_irq_chip,
+ pbus, handle_level_irq, NULL, NULL);
+ }
+
+ return 0;
+}
+
+static const struct irq_domain_ops mtk_spmi_rcs_irq_domain_ops = {
+ .alloc = mtk_spmi_rcs_irq_alloc,
+ .free = irq_domain_free_irqs_common,
+ .translate = mtk_spmi_rcs_irq_translate,
+};
+
static const struct pmif_data mt6873_pmif_arb = {
.regs = mt6873_regs,
.spmimst_regs = mt6873_spmi_regs,
@@ -446,84 +636,202 @@ static const struct pmif_data mt8195_pmif_arb = {
.soc_chan = 2,
};
-static int mtk_spmi_probe(struct platform_device *pdev)
+static const struct pmif_data mt8196_pmif_arb = {
+ .regs = mt8195_regs,
+ .spmimst_regs = mt8195_spmi_regs,
+ .soc_chan = 2,
+ .spmi_ver = 2,
+ .num_spmi_buses = 2,
+};
+
+static int mtk_spmi_irq_init(struct device_node *node,
+ const struct pmif_data *pdata,
+ struct pmif_bus *pbus)
+{
+ struct pmif *arb = to_mtk_pmif(pbus->ctrl);
+ unsigned int i;
+
+ /* No interrupts required for SPMI 1.x controller */
+ if (pdata->spmi_ver < 2) {
+ pbus->dom = NULL;
+ return 0;
+ }
+
+ pbus->irq = of_irq_get_byname(node, "rcs");
+ if (pbus->irq <= 0)
+ return pbus->irq ? : -ENXIO;
+
+ pbus->dom = irq_domain_create_tree(of_fwnode_handle(node),
+ &mtk_spmi_rcs_irq_domain_ops, pbus);
+ if (!pbus->dom)
+ return -ENOMEM;
+
+ /* Clear possible unhandled interrupts coming from bootloader SPMI init */
+ for (i = SPMI_SLV_3_0_EINT; i <= SPMI_SLV_F_C_EINT; i++)
+ mtk_spmi_writel(arb, pbus, GENMASK(31, 0), i);
+
+ return 0;
+}
+
+static void mtk_spmi_irq_remove(struct pmif_bus *pbus)
+{
+ if (!pbus->dom)
+ return;
+
+ irq_set_chained_handler_and_data(pbus->irq, NULL, NULL);
+ irq_domain_remove(pbus->dom);
+}
+
+static int mtk_spmi_bus_probe(struct platform_device *pdev,
+ struct device_node *node,
+ const struct pmif_data *pdata,
+ struct pmif_bus *pbus)
{
- struct pmif *arb;
struct spmi_controller *ctrl;
- int err, i;
- u32 chan_offset;
+ int err, idx, bus_id, i;
- ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*arb));
+ if (pdata->num_spmi_buses > 1)
+ bus_id = of_alias_get_id(node, "spmi");
+ else
+ bus_id = 0;
+
+ if (bus_id < 0)
+ return dev_err_probe(&pdev->dev, bus_id,
+ "Cannot find SPMI Bus alias ID\n");
+
+ ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*pbus));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
- arb = spmi_controller_get_drvdata(ctrl);
- arb->data = device_get_match_data(&pdev->dev);
- if (!arb->data) {
- dev_err(&pdev->dev, "Cannot get drv_data\n");
+ pbus = spmi_controller_get_drvdata(ctrl);
+ pbus->ctrl = ctrl;
+
+ idx = of_property_match_string(node, "reg-names", "pmif");
+ if (idx < 0)
return -EINVAL;
- }
- arb->base = devm_platform_ioremap_resource_byname(pdev, "pmif");
- if (IS_ERR(arb->base))
- return PTR_ERR(arb->base);
+ pbus->base = devm_of_iomap(&pdev->dev, node, idx, NULL);
+ if (IS_ERR(pbus->base))
+ return PTR_ERR(pbus->base);
- arb->spmimst_base = devm_platform_ioremap_resource_byname(pdev, "spmimst");
- if (IS_ERR(arb->spmimst_base))
- return PTR_ERR(arb->spmimst_base);
+ idx = of_property_match_string(node, "reg-names", "spmimst");
+ if (idx < 0)
+ return -EINVAL;
- arb->nclks = ARRAY_SIZE(pmif_clock_names);
- for (i = 0; i < arb->nclks; i++)
- arb->clks[i].id = pmif_clock_names[i];
+ pbus->spmimst_base = devm_of_iomap(&pdev->dev, node, idx, NULL);
+ if (IS_ERR(pbus->spmimst_base))
+ return PTR_ERR(pbus->spmimst_base);
+
+ pbus->nclks = ARRAY_SIZE(pmif_clock_names);
+ for (i = 0; i < pbus->nclks; i++) {
+ pbus->clks[i].id = pmif_clock_names[i];
+ pbus->clks[i].clk = of_clk_get_by_name(node, pbus->clks[i].id);
+ if (IS_ERR(pbus->clks[i].clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pbus->clks[i].clk),
+ "Failed to get clocks\n");
+ }
- err = clk_bulk_get(&pdev->dev, arb->nclks, arb->clks);
+ err = clk_bulk_prepare_enable(pbus->nclks, pbus->clks);
if (err) {
- dev_err(&pdev->dev, "Failed to get clocks: %d\n", err);
- return err;
+ dev_err_probe(&pdev->dev, err, "Failed to enable clocks\n");
+ goto err_put_clks;
}
- err = clk_bulk_prepare_enable(arb->nclks, arb->clks);
+ err = mtk_spmi_irq_init(node, pdata, pbus);
if (err) {
- dev_err(&pdev->dev, "Failed to enable clocks: %d\n", err);
- goto err_put_clks;
+ dev_err_probe(&pdev->dev, err, "Cannot initialize SPMI IRQs\n");
+ goto err_disable_clks;
}
ctrl->cmd = pmif_arb_cmd;
ctrl->read_cmd = pmif_spmi_read_cmd;
ctrl->write_cmd = pmif_spmi_write_cmd;
+ ctrl->dev.of_node = node;
+ dev_set_name(&ctrl->dev, "spmi-%d", bus_id);
- chan_offset = PMIF_CHAN_OFFSET * arb->data->soc_chan;
- arb->chan.ch_sta = PMIF_SWINF_0_STA + chan_offset;
- arb->chan.wdata = PMIF_SWINF_0_WDATA_31_0 + chan_offset;
- arb->chan.rdata = PMIF_SWINF_0_RDATA_31_0 + chan_offset;
- arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset;
- arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset;
-
- raw_spin_lock_init(&arb->lock);
-
- platform_set_drvdata(pdev, ctrl);
+ raw_spin_lock_init(&pbus->lock);
err = spmi_controller_add(ctrl);
if (err)
- goto err_domain_remove;
+ goto err_remove_irq;
+
+ if (pbus->dom)
+ irq_set_chained_handler_and_data(pbus->irq, mtk_spmi_handle_chained_irq, pbus);
return 0;
-err_domain_remove:
- clk_bulk_disable_unprepare(arb->nclks, arb->clks);
+err_remove_irq:
+ mtk_spmi_irq_remove(pbus);
+err_disable_clks:
+ clk_bulk_disable_unprepare(pbus->nclks, pbus->clks);
err_put_clks:
- clk_bulk_put(arb->nclks, arb->clks);
+ clk_bulk_put(pbus->nclks, pbus->clks);
return err;
}
+static int mtk_spmi_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct pmif *arb;
+ u32 chan_offset;
+ u8 cur_bus = 0;
+ int ret;
+
+ arb = devm_kzalloc(&pdev->dev, sizeof(*arb), GFP_KERNEL);
+ if (!arb)
+ return -ENOMEM;
+
+ arb->data = device_get_match_data(&pdev->dev);
+ if (!arb->data) {
+ dev_err(&pdev->dev, "Cannot get drv_data\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, arb);
+
+ if (!arb->data->num_spmi_buses) {
+ ret = mtk_spmi_bus_probe(pdev, node, arb->data, &arb->bus[cur_bus]);
+ if (ret)
+ return ret;
+ } else {
+ for_each_available_child_of_node_scoped(node, child) {
+ if (!of_node_name_eq(child, "spmi"))
+ continue;
+
+ ret = mtk_spmi_bus_probe(pdev, child, arb->data,
+ &arb->bus[cur_bus]);
+ if (ret)
+ return ret;
+ cur_bus++;
+ }
+ }
+
+ chan_offset = PMIF_CHAN_OFFSET * arb->data->soc_chan;
+ arb->chan.ch_sta = PMIF_SWINF_0_STA + chan_offset;
+ arb->chan.wdata = PMIF_SWINF_0_WDATA_31_0 + chan_offset;
+ arb->chan.rdata = PMIF_SWINF_0_RDATA_31_0 + chan_offset;
+ arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset;
+ arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset;
+
+ return 0;
+}
+
static void mtk_spmi_remove(struct platform_device *pdev)
{
- struct spmi_controller *ctrl = platform_get_drvdata(pdev);
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = platform_get_drvdata(pdev);
+ int i;
- spmi_controller_remove(ctrl);
- clk_bulk_disable_unprepare(arb->nclks, arb->clks);
- clk_bulk_put(arb->nclks, arb->clks);
+ for (i = 0; i < PMIF_MAX_BUSES; i++) {
+ struct pmif_bus *pbus = &arb->bus[i];
+
+ if (!pbus->ctrl)
+ continue;
+
+ mtk_spmi_irq_remove(pbus);
+ spmi_controller_remove(pbus->ctrl);
+ clk_bulk_disable_unprepare(pbus->nclks, pbus->clks);
+ clk_bulk_put(pbus->nclks, pbus->clks);
+ }
}
static const struct of_device_id mtk_spmi_match_table[] = {
@@ -534,6 +842,9 @@ static const struct of_device_id mtk_spmi_match_table[] = {
.compatible = "mediatek,mt8195-spmi",
.data = &mt8195_pmif_arb,
}, {
+ .compatible = "mediatek,mt8196-spmi",
+ .data = &mt8196_pmif_arb,
+ }, {
/* sentinel */
},
};
@@ -549,6 +860,7 @@ static struct platform_driver mtk_spmi_driver = {
};
module_platform_driver(mtk_spmi_driver);
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_AUTHOR("Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>");
MODULE_DESCRIPTION("MediaTek SPMI Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 91581974ef84..69f8d456324a 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -2,6 +2,8 @@
/*
* Copyright (c) 2012-2015, 2017, 2021, The Linux Foundation. All rights reserved.
*/
+
+#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/err.h>
@@ -25,12 +27,12 @@
#define PMIC_ARB_VERSION_V3_MIN 0x30000000
#define PMIC_ARB_VERSION_V5_MIN 0x50000000
#define PMIC_ARB_VERSION_V7_MIN 0x70000000
+#define PMIC_ARB_VERSION_V8_MIN 0x80000000
#define PMIC_ARB_INT_EN 0x0004
#define PMIC_ARB_FEATURES 0x0004
#define PMIC_ARB_FEATURES_PERIPH_MASK GENMASK(10, 0)
-
-#define PMIC_ARB_FEATURES1 0x0008
+#define PMIC_ARB_FEATURES_V8_PERIPH_MASK GENMASK(12, 0)
/* PMIC Arbiter channel registers offsets */
#define PMIC_ARB_CMD 0x00
@@ -50,9 +52,11 @@
#define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF)
#define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */
-#define PMIC_ARB_MAX_PPID BIT(12) /* PPID is 12bit */
+#define PMIC_ARB_MAX_PPID BIT(13)
#define PMIC_ARB_APID_VALID BIT(15)
-#define PMIC_ARB_CHAN_IS_IRQ_OWNER(reg) ((reg) & BIT(24))
+#define PMIC_ARB_CHAN_IS_IRQ_OWNER_MASK BIT(24)
+#define PMIC_ARB_V8_CHAN_IS_IRQ_OWNER_MASK BIT(31)
+
#define INVALID_EE 0xFF
/* Ownership Table */
@@ -96,30 +100,37 @@ enum pmic_arb_channel {
PMIC_ARB_CHANNEL_OBS,
};
-#define PMIC_ARB_MAX_BUSES 2
+#define PMIC_ARB_MAX_BUSES 4
/* Maximum number of support PMIC peripherals */
#define PMIC_ARB_MAX_PERIPHS 512
#define PMIC_ARB_MAX_PERIPHS_V7 1024
+#define PMIC_ARB_MAX_PERIPHS_V8 8192
#define PMIC_ARB_TIMEOUT_US 1000
#define PMIC_ARB_MAX_TRANS_BYTES (8)
#define PMIC_ARB_APID_MASK 0xFF
-#define PMIC_ARB_PPID_MASK 0xFFF
+#define PMIC_ARB_PPID_MASK GENMASK(11, 0)
+#define PMIC_ARB_V8_PPID_MASK GENMASK(12, 0)
/* interrupt enable bit */
#define SPMI_PIC_ACC_ENABLE_BIT BIT(0)
+#define HWIRQ_SID_MASK GENMASK(28, 24)
+#define HWIRQ_PID_MASK GENMASK(23, 16)
+#define HWIRQ_IRQID_MASK GENMASK(15, 13)
+#define HWIRQ_APID_MASK GENMASK(12, 0)
+
#define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \
- ((((slave_id) & 0xF) << 28) | \
- (((periph_id) & 0xFF) << 20) | \
- (((irq_id) & 0x7) << 16) | \
- (((apid) & 0x3FF) << 0))
+ (FIELD_PREP(HWIRQ_SID_MASK, (slave_id)) | \
+ FIELD_PREP(HWIRQ_PID_MASK, (periph_id)) | \
+ FIELD_PREP(HWIRQ_IRQID_MASK, (irq_id)) | \
+ FIELD_PREP(HWIRQ_APID_MASK, (apid)))
-#define hwirq_to_sid(hwirq) (((hwirq) >> 28) & 0xF)
-#define hwirq_to_per(hwirq) (((hwirq) >> 20) & 0xFF)
-#define hwirq_to_irq(hwirq) (((hwirq) >> 16) & 0x7)
-#define hwirq_to_apid(hwirq) (((hwirq) >> 0) & 0x3FF)
+#define hwirq_to_sid(hwirq) FIELD_GET(HWIRQ_SID_MASK, (hwirq))
+#define hwirq_to_per(hwirq) FIELD_GET(HWIRQ_PID_MASK, (hwirq))
+#define hwirq_to_irq(hwirq) FIELD_GET(HWIRQ_IRQID_MASK, (hwirq))
+#define hwirq_to_apid(hwirq) FIELD_GET(HWIRQ_APID_MASK, (hwirq))
struct pmic_arb_ver_ops;
@@ -138,11 +149,12 @@ struct spmi_pmic_arb;
* @domain: irq domain object for PMIC IRQ domain
* @intr: address of the SPMI interrupt control registers.
* @cnfg: address of the PMIC Arbiter configuration registers.
+ * @apid_owner: on v8: address of APID owner mapping table registers
* @spmic: spmi controller registered for this bus
* @lock: lock to synchronize accesses.
- * @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
+ * @base_apid: on v7 and v8: minimum APID associated with the
+ * particular SPMI bus instance
+ * @apid_count: on v5, v7 and v8: number of APIDs associated with the
* particular SPMI bus instance
* @mapping_table: in-memory copy of PPID -> APID mapping table.
* @mapping_table_valid:bitmap containing valid-only periphs
@@ -159,6 +171,7 @@ struct spmi_pmic_arb_bus {
struct irq_domain *domain;
void __iomem *intr;
void __iomem *cnfg;
+ void __iomem *apid_owner;
struct spmi_controller *spmic;
raw_spinlock_t lock;
u16 base_apid;
@@ -181,6 +194,7 @@ struct spmi_pmic_arb_bus {
* @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
+ * @apid_map: on v8, APID mapping table register base
* @channel: execution environment channel to use for accesses.
* @ee: the current Execution Environment
* @ver_ops: version dependent operations.
@@ -193,6 +207,7 @@ struct spmi_pmic_arb {
void __iomem *wr_base;
void __iomem *core;
resource_size_t core_size;
+ void __iomem *apid_map;
u8 channel;
u8 ee;
const struct pmic_arb_ver_ops *ver_ops;
@@ -206,6 +221,7 @@ struct spmi_pmic_arb {
*
* @ver_str: version string.
* @get_core_resources: initializes the core, observer and channels
+ * @get_bus_resources: requests per-SPMI bus register resources
* @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.
@@ -227,6 +243,9 @@ struct spmi_pmic_arb {
struct pmic_arb_ver_ops {
const char *ver_str;
int (*get_core_resources)(struct platform_device *pdev, void __iomem *core);
+ int (*get_bus_resources)(struct platform_device *pdev,
+ struct device_node *node,
+ struct spmi_pmic_arb_bus *bus);
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 */
@@ -656,7 +675,7 @@ static int periph_interrupt(struct spmi_pmic_arb_bus *bus, u16 apid)
unsigned int irq;
u32 status, id;
int handled = 0;
- u8 sid = (bus->apid_data[apid].ppid >> 8) & 0xF;
+ u8 sid = (bus->apid_data[apid].ppid >> 8) & 0x1F;
u8 per = bus->apid_data[apid].ppid & 0xFF;
status = readl_relaxed(pmic_arb->ver_ops->irq_status(bus, apid));
@@ -686,7 +705,7 @@ static void pmic_arb_chained_irq(struct irq_desc *desc)
int last = bus->max_apid;
/*
* acc_offset will be non-zero for the secondary SPMI bus instance on
- * v7 controllers.
+ * v7 and v8 controllers.
*/
int acc_offset = bus->base_apid >> 5;
u8 ee = pmic_arb->ee;
@@ -913,7 +932,8 @@ static int qpnpint_irq_domain_translate(struct irq_domain *d,
return -EINVAL;
if (fwspec->param_count != 4)
return -EINVAL;
- if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
+ if (intspec[0] > FIELD_MAX(HWIRQ_SID_MASK) || intspec[1] > FIELD_MAX(HWIRQ_PID_MASK) ||
+ intspec[2] > FIELD_MAX(HWIRQ_IRQID_MASK))
return -EINVAL;
ppid = intspec[0] << 8 | intspec[1];
@@ -1160,7 +1180,9 @@ static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb_bus *bus, u16 ppid)
return apid_valid & ~PMIC_ARB_APID_VALID;
}
-static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb_bus *bus)
+static int _pmic_arb_read_apid_map(struct spmi_pmic_arb_bus *bus,
+ void __iomem *ppid_base, unsigned long ppid_mask,
+ u8 ppid_shift, unsigned long irq_owner_mask)
{
struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
struct apid_data *apidd;
@@ -1171,7 +1193,7 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb_bus *bus)
/*
* In order to allow multiple EEs to write to a single PPID in arbiter
- * version 5 and 7, there is more than one APID mapped to each PPID.
+ * version 5,7 and 8, there can be more than one APID mapped to each PPID.
* The owner field for each of these mappings specifies the EE which is
* allowed to write to the APID. The owner of the last (highest) APID
* which has the IRQ owner bit set for a given PPID will receive
@@ -1183,19 +1205,30 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb_bus *bus)
* APID = N to N+M-1 are assigned to the secondary bus
* where N = number of APIDs supported by the primary bus and
* M = number of APIDs supported by the secondary bus
+ *
+ * In arbiter version 8, the APID numbering space is divided between
+ * the SPMI buses according to this mapping:
+ * APID = 0 to N-1 --> bus 0
+ * APID = N to N+M-1 --> bus 1
+ * APID = N+M to N+M+P-1 --> bus 2
+ * APID = N+M+P to N+M+P+Q-1 --> bus 3
+ * where N = number of APIDs supported by bus 0
+ * M = number of APIDs supported by bus 1
+ * P = number of APIDs supported by bus 2
+ * Q = number of APIDs supported by bus 3
*/
+
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;
-
- regval = readl_relaxed(pmic_arb->core + offset);
+ regval = readl_relaxed(ppid_base + offset);
if (!regval)
continue;
- ppid = (regval >> 8) & PMIC_ARB_PPID_MASK;
- is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval);
+ ppid = (regval >> ppid_shift) & ppid_mask;
+ is_irq_ee = regval & irq_owner_mask;
regval = readl_relaxed(pmic_arb->ver_ops->apid_owner(bus, i));
apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval);
@@ -1237,6 +1270,12 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb_bus *bus)
return 0;
}
+static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb_bus *bus)
+{
+ return _pmic_arb_read_apid_map(bus, bus->pmic_arb->core, PMIC_ARB_PPID_MASK,
+ 8, PMIC_ARB_CHAN_IS_IRQ_OWNER_MASK);
+}
+
static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb_bus *bus, u16 ppid)
{
if (!(bus->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID))
@@ -1345,37 +1384,46 @@ static int pmic_arb_get_core_resources_v7(struct platform_device *pdev,
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)
+static int _pmic_arb_init_apid_v7(struct spmi_pmic_arb_bus *bus, int index,
+ int max_buses, unsigned long periph_mask)
{
struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
- int ret;
+ int i;
- 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);
+ if (index < 0 || index >= max_buses) {
+ dev_err(&bus->spmic->dev, "Unsupported bus index %d detected\n",
+ index);
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 = 0;
+ bus->apid_count = 0;
+ for (i = 0; i <= index; i++) {
+ bus->base_apid += bus->apid_count;
+ bus->apid_count = readl_relaxed(pmic_arb->core +
+ PMIC_ARB_FEATURES + i * 4) &
+ periph_mask;
+ }
+
+ if (bus->apid_count == 0) {
+ dev_err(&bus->spmic->dev, "Bus %d not implemented\n", index);
+ return -EINVAL;
+ } else if (bus->base_apid + bus->apid_count > pmic_arb->max_periphs) {
+ dev_err(&bus->spmic->dev, "Unsupported max APID %d detected\n",
bus->base_apid + bus->apid_count);
return -EINVAL;
}
- ret = pmic_arb_init_apid_min_max(bus);
+ return pmic_arb_init_apid_min_max(bus);
+}
+
+/*
+ * Arbiter 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)
+{
+ int ret = _pmic_arb_init_apid_v7(bus, index, 2, PMIC_ARB_FEATURES_PERIPH_MASK);
if (ret)
return ret;
@@ -1424,6 +1472,102 @@ static int pmic_arb_offset_v7(struct spmi_pmic_arb_bus *bus, u8 sid, u16 addr,
return offset;
}
+static int pmic_arb_get_core_resources_v8(struct platform_device *pdev,
+ void __iomem *core)
+{
+ struct spmi_pmic_arb *pmic_arb = platform_get_drvdata(pdev);
+
+ pmic_arb->apid_map = devm_platform_ioremap_resource_byname(pdev, "chnl_map");
+ if (IS_ERR(pmic_arb->apid_map))
+ return PTR_ERR(pmic_arb->apid_map);
+
+ pmic_arb->core = core;
+
+ pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS_V8;
+
+ return pmic_arb_get_obsrvr_chnls_v2(pdev);
+}
+
+static int pmic_arb_get_bus_resources_v8(struct platform_device *pdev,
+ struct device_node *node,
+ struct spmi_pmic_arb_bus *bus)
+{
+ int index;
+
+ index = of_property_match_string(node, "reg-names", "chnl_owner");
+ if (index < 0) {
+ dev_err(&pdev->dev, "chnl_owner reg region missing\n");
+ return -EINVAL;
+ }
+
+ bus->apid_owner = devm_of_iomap(&pdev->dev, node, index, NULL);
+
+ return PTR_ERR_OR_ZERO(bus->apid_owner);
+}
+
+static int pmic_arb_read_apid_map_v8(struct spmi_pmic_arb_bus *bus)
+{
+ return _pmic_arb_read_apid_map(bus, bus->pmic_arb->apid_map,
+ PMIC_ARB_V8_PPID_MASK, 0,
+ PMIC_ARB_V8_CHAN_IS_IRQ_OWNER_MASK);
+}
+
+/*
+ * Arbiter v8 supports up to 4 buses. Each bus will get a different apid count, read
+ * from different registers.
+ */
+static int pmic_arb_init_apid_v8(struct spmi_pmic_arb_bus *bus, int index)
+{
+ int ret = _pmic_arb_init_apid_v7(bus, index, 4,
+ PMIC_ARB_FEATURES_V8_PERIPH_MASK);
+ if (ret)
+ return ret;
+
+ ret = pmic_arb_read_apid_map_v8(bus);
+ if (ret) {
+ dev_err(&bus->spmic->dev, "could not read APID->PPID mapping table, rc= %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * v8 offset per ee and per apid for observer channels and per apid for
+ * read/write channels.
+ */
+static int pmic_arb_offset_v8(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(bus, ppid);
+ if (rc < 0)
+ return rc;
+
+ apid = rc;
+ switch (ch_type) {
+ case PMIC_ARB_CHANNEL_OBS:
+ offset = 0x40000 * pmic_arb->ee + 0x20 * apid;
+ break;
+ case PMIC_ARB_CHANNEL_RW:
+ 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;
+ }
+ offset = 0x200 * apid;
+ break;
+ }
+
+ return offset;
+}
+
static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
{
return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
@@ -1491,6 +1635,14 @@ pmic_arb_acc_enable_v7(struct spmi_pmic_arb_bus *bus, u16 n)
}
static void __iomem *
+pmic_arb_acc_enable_v8(struct spmi_pmic_arb_bus *bus, u16 n)
+{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+
+ return pmic_arb->wr_base + 0x100 + 0x200 * n;
+}
+
+static void __iomem *
pmic_arb_irq_status_v1(struct spmi_pmic_arb_bus *bus, u16 n)
{
return bus->intr + 0x600 + 0x4 * n;
@@ -1517,6 +1669,14 @@ pmic_arb_irq_status_v7(struct spmi_pmic_arb_bus *bus, u16 n)
}
static void __iomem *
+pmic_arb_irq_status_v8(struct spmi_pmic_arb_bus *bus, u16 n)
+{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+
+ return pmic_arb->wr_base + 0x104 + 0x200 * n;
+}
+
+static void __iomem *
pmic_arb_irq_clear_v1(struct spmi_pmic_arb_bus *bus, u16 n)
{
return bus->intr + 0xA00 + 0x4 * n;
@@ -1542,6 +1702,14 @@ pmic_arb_irq_clear_v7(struct spmi_pmic_arb_bus *bus, u16 n)
return pmic_arb->wr_base + 0x108 + 0x1000 * n;
}
+static void __iomem *
+pmic_arb_irq_clear_v8(struct spmi_pmic_arb_bus *bus, u16 n)
+{
+ struct spmi_pmic_arb *pmic_arb = bus->pmic_arb;
+
+ return pmic_arb->wr_base + 0x108 + 0x200 * n;
+}
+
static u32 pmic_arb_apid_map_offset_v2(u16 n)
{
return 0x800 + 0x4 * n;
@@ -1557,6 +1725,12 @@ static u32 pmic_arb_apid_map_offset_v7(u16 n)
return 0x2000 + 0x4 * n;
}
+static u32 pmic_arb_apid_map_offset_v8(u16 n)
+{
+ /* For v8, offset is from "chnl_map" base register, not "core". */
+ return 0x4 * n;
+}
+
static void __iomem *
pmic_arb_apid_owner_v2(struct spmi_pmic_arb_bus *bus, u16 n)
{
@@ -1564,7 +1738,7 @@ pmic_arb_apid_owner_v2(struct spmi_pmic_arb_bus *bus, u16 n)
}
/*
- * For arbiter version 7, APID ownership table registers have independent
+ * For arbiter version 7 and 8, APID ownership table registers have independent
* numbering space for each SPMI bus instance, so each is indexed starting from
* 0.
*/
@@ -1574,6 +1748,12 @@ pmic_arb_apid_owner_v7(struct spmi_pmic_arb_bus *bus, u16 n)
return bus->cnfg + 0x4 * (n - bus->base_apid);
}
+static void __iomem *
+pmic_arb_apid_owner_v8(struct spmi_pmic_arb_bus *bus, u16 n)
+{
+ return bus->apid_owner + 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,
@@ -1654,6 +1834,23 @@ static const struct pmic_arb_ver_ops pmic_arb_v7 = {
.apid_owner = pmic_arb_apid_owner_v7,
};
+static const struct pmic_arb_ver_ops pmic_arb_v8 = {
+ .ver_str = "v8",
+ .get_core_resources = pmic_arb_get_core_resources_v8,
+ .get_bus_resources = pmic_arb_get_bus_resources_v8,
+ .init_apid = pmic_arb_init_apid_v8,
+ .ppid_to_apid = pmic_arb_ppid_to_apid_v5,
+ .non_data_cmd = pmic_arb_non_data_cmd_v2,
+ .offset = pmic_arb_offset_v8,
+ .fmt_cmd = pmic_arb_fmt_cmd_v2,
+ .owner_acc_status = pmic_arb_owner_acc_status_v7,
+ .acc_enable = pmic_arb_acc_enable_v8,
+ .irq_status = pmic_arb_irq_status_v8,
+ .irq_clear = pmic_arb_irq_clear_v8,
+ .apid_map_offset = pmic_arb_apid_map_offset_v8,
+ .apid_owner = pmic_arb_apid_owner_v8,
+};
+
static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
.activate = qpnpint_irq_domain_activate,
.alloc = qpnpint_irq_domain_alloc,
@@ -1731,6 +1928,12 @@ static int spmi_pmic_arb_bus_init(struct platform_device *pdev,
bus->spmic = ctrl;
bus->id = bus_index;
+ if (pmic_arb->ver_ops->get_bus_resources) {
+ ret = pmic_arb->ver_ops->get_bus_resources(pdev, node, bus);
+ if (ret)
+ return ret;
+ }
+
ret = pmic_arb->ver_ops->init_apid(bus, bus_index);
if (ret)
return ret;
@@ -1825,8 +2028,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
pmic_arb->ver_ops = &pmic_arb_v3;
else if (hw_ver < PMIC_ARB_VERSION_V7_MIN)
pmic_arb->ver_ops = &pmic_arb_v5;
- else
+ else if (hw_ver < PMIC_ARB_VERSION_V8_MIN)
pmic_arb->ver_ops = &pmic_arb_v7;
+ else
+ pmic_arb->ver_ops = &pmic_arb_v8;
err = pmic_arb->ver_ops->get_core_resources(pdev, core);
if (err)
@@ -1875,6 +2080,7 @@ static void spmi_pmic_arb_remove(struct platform_device *pdev)
static const struct of_device_id spmi_pmic_arb_match_table[] = {
{ .compatible = "qcom,spmi-pmic-arb", },
{ .compatible = "qcom,x1e80100-spmi-pmic-arb", },
+ { .compatible = "qcom,glymur-spmi-pmic-arb", },
{},
};
MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c
index f45968ef94ea..3bdaee925dee 100644
--- a/drivers/staging/iio/addac/adt7316-i2c.c
+++ b/drivers/staging/iio/addac/adt7316-i2c.c
@@ -136,7 +136,7 @@ static struct i2c_driver adt7316_driver = {
.driver = {
.name = "adt7316",
.of_match_table = adt7316_of_match,
- .pm = ADT7316_PM_OPS,
+ .pm = pm_sleep_ptr(&adt7316_pm_ops),
},
.probe = adt7316_i2c_probe,
.id_table = adt7316_i2c_id,
diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c
index af513e003da7..f91325d11394 100644
--- a/drivers/staging/iio/addac/adt7316-spi.c
+++ b/drivers/staging/iio/addac/adt7316-spi.c
@@ -142,7 +142,7 @@ static struct spi_driver adt7316_driver = {
.driver = {
.name = "adt7316",
.of_match_table = adt7316_of_spi_match,
- .pm = ADT7316_PM_OPS,
+ .pm = pm_sleep_ptr(&adt7316_pm_ops),
},
.probe = adt7316_spi_probe,
.id_table = adt7316_spi_id,
diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c
index 8a9a8262c2be..59fb3bd26bc1 100644
--- a/drivers/staging/iio/addac/adt7316.c
+++ b/drivers/staging/iio/addac/adt7316.c
@@ -2082,7 +2082,6 @@ static const struct attribute_group adt7516_event_attribute_group = {
.name = "events",
};
-#ifdef CONFIG_PM_SLEEP
static int adt7316_disable(struct device *dev)
{
struct iio_dev *dev_info = dev_get_drvdata(dev);
@@ -2098,9 +2097,8 @@ static int adt7316_enable(struct device *dev)
return _adt7316_store_enabled(chip, 1);
}
-EXPORT_SYMBOL_GPL(adt7316_pm_ops);
-SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
-#endif
+
+EXPORT_GPL_SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
static const struct iio_info adt7316_info = {
.attrs = &adt7316_attribute_group,
diff --git a/drivers/staging/iio/addac/adt7316.h b/drivers/staging/iio/addac/adt7316.h
index 8c2a92ae7157..f208f0d3583a 100644
--- a/drivers/staging/iio/addac/adt7316.h
+++ b/drivers/staging/iio/addac/adt7316.h
@@ -22,12 +22,8 @@ struct adt7316_bus {
int (*multi_write)(void *client, u8 first_reg, u8 count, u8 *data);
};
-#ifdef CONFIG_PM_SLEEP
extern const struct dev_pm_ops adt7316_pm_ops;
-#define ADT7316_PM_OPS (&adt7316_pm_ops)
-#else
-#define ADT7316_PM_OPS NULL
-#endif
+
int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
const char *name);
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
index 49388da5a684..b87ea1781b27 100644
--- a/drivers/staging/iio/frequency/ad9832.c
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -23,12 +23,9 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include "ad9832.h"
-
#include "dds.h"
/* Registers */
-
#define AD9832_FREQ0LL 0x0
#define AD9832_FREQ0HL 0x1
#define AD9832_FREQ0LM 0x2
@@ -45,14 +42,12 @@
#define AD9832_PHASE2H 0xD
#define AD9832_PHASE3L 0xE
#define AD9832_PHASE3H 0xF
-
#define AD9832_PHASE_SYM 0x10
#define AD9832_FREQ_SYM 0x11
#define AD9832_PINCTRL_EN 0x12
#define AD9832_OUTPUT_EN 0x13
/* Command Control Bits */
-
#define AD9832_CMD_PHA8BITSW 0x1
#define AD9832_CMD_PHA16BITSW 0x0
#define AD9832_CMD_FRE8BITSW 0x3
@@ -92,7 +87,6 @@
* @phase_data: tuning word spi transmit buffer
* @freq_data: tuning word spi transmit buffer
*/
-
struct ad9832_state {
struct spi_device *spi;
struct clk *mclk;
@@ -299,16 +293,10 @@ static const struct iio_info ad9832_info = {
static int ad9832_probe(struct spi_device *spi)
{
- struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev);
struct iio_dev *indio_dev;
struct ad9832_state *st;
int ret;
- if (!pdata) {
- dev_dbg(&spi->dev, "no platform data?\n");
- return -ENODEV;
- }
-
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
@@ -335,7 +323,6 @@ static int ad9832_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
/* Setup default messages */
-
st->xfer.tx_buf = &st->data;
st->xfer.len = 2;
@@ -379,30 +366,6 @@ static int ad9832_probe(struct spi_device *spi)
return ret;
}
- ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0);
- if (ret)
- return ret;
-
- ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3);
- if (ret)
- return ret;
-
return devm_iio_device_register(&spi->dev, indio_dev);
}
diff --git a/drivers/staging/iio/frequency/ad9832.h b/drivers/staging/iio/frequency/ad9832.h
deleted file mode 100644
index d0d840edb8d2..000000000000
--- a/drivers/staging/iio/frequency/ad9832.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * AD9832 SPI DDS driver
- *
- * Copyright 2011 Analog Devices Inc.
- */
-#ifndef IIO_DDS_AD9832_H_
-#define IIO_DDS_AD9832_H_
-
-/*
- * TODO: struct ad9832_platform_data needs to go into include/linux/iio
- */
-
-/**
- * struct ad9832_platform_data - platform specific information
- * @freq0: power up freq0 tuning word in Hz
- * @freq1: power up freq1 tuning word in Hz
- * @phase0: power up phase0 value [0..4095] correlates with 0..2PI
- * @phase1: power up phase1 value [0..4095] correlates with 0..2PI
- * @phase2: power up phase2 value [0..4095] correlates with 0..2PI
- * @phase3: power up phase3 value [0..4095] correlates with 0..2PI
- */
-
-struct ad9832_platform_data {
- unsigned long freq0;
- unsigned long freq1;
- unsigned short phase0;
- unsigned short phase1;
- unsigned short phase2;
- unsigned short phase3;
-};
-
-#endif /* IIO_DDS_AD9832_H_ */