summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-08-21 14:23:06 +0100
committerMark Brown <broonie@kernel.org>2026-08-21 14:23:08 +0100
commit1bb70cd208d68d056aac8dcde8b587e70d33c582 (patch)
tree589879dc2e22e55bbf89485fc4fbea3b35437bb8 /drivers
parent417c5a2b0966fccf49628301d1af61b23e0c390f (diff)
parent8992f32c57607bdfaf5de2a2cd26b3b71f3a9d55 (diff)
downloadlinux-next-1bb70cd208d68d056aac8dcde8b587e70d33c582.tar.gz
linux-next-1bb70cd208d68d056aac8dcde8b587e70d33c582.zip
Merge branch 'char-misc-next' of https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
Diffstat (limited to 'drivers')
-rw-r--r--drivers/accessibility/speakup/fakekey.c4
-rw-r--r--drivers/accessibility/speakup/genmap.c3
-rw-r--r--drivers/accessibility/speakup/i18n.c2
-rw-r--r--drivers/accessibility/speakup/keyhelp.c27
-rw-r--r--drivers/accessibility/speakup/kobjects.c14
-rw-r--r--drivers/accessibility/speakup/main.c75
-rw-r--r--drivers/accessibility/speakup/selection.c2
-rw-r--r--drivers/accessibility/speakup/speakup.h10
-rw-r--r--drivers/accessibility/speakup/speakup_acnt.h3
-rw-r--r--drivers/accessibility/speakup/speakup_decpc.c2
-rw-r--r--drivers/accessibility/speakup/speakup_dtlk.h5
-rw-r--r--drivers/accessibility/speakup/speakup_soft.c5
-rw-r--r--drivers/accessibility/speakup/spk_types.h2
-rw-r--r--drivers/accessibility/speakup/synth.c4
-rw-r--r--drivers/accessibility/speakup/utils.h2
-rw-r--r--drivers/android/Kconfig2
-rw-r--r--drivers/android/binder/allocation.rs4
-rw-r--r--drivers/android/binder/debug.rs76
-rw-r--r--drivers/android/binder/defs.rs8
-rw-r--r--drivers/android/binder/freeze.rs125
-rw-r--r--drivers/android/binder/netlink.rs117
-rw-r--r--drivers/android/binder/node.rs87
-rw-r--r--drivers/android/binder/node/wrapper.rs2
-rw-r--r--drivers/android/binder/page_range.rs8
-rw-r--r--drivers/android/binder/process.rs226
-rw-r--r--drivers/android/binder/rust_binder_main.rs39
-rw-r--r--drivers/android/binder/rust_binderfs.c3
-rw-r--r--drivers/android/binder/thread.rs296
-rw-r--r--drivers/android/binder/trace.rs4
-rw-r--r--drivers/android/binder/transaction.rs131
-rw-r--r--drivers/base/cacheinfo.c9
-rw-r--r--drivers/bus/mhi/ep/main.c56
-rw-r--r--drivers/bus/mhi/host/init.c4
-rw-r--r--drivers/bus/mhi/host/main.c6
-rw-r--r--drivers/bus/mhi/host/pci_generic.c5
-rw-r--r--drivers/bus/mhi/host/pm.c73
-rw-r--r--drivers/cdx/cdx.c7
-rw-r--r--drivers/char/powernv-op-panel.c2
-rw-r--r--drivers/char/ppdev.c10
-rw-r--r--drivers/char/tlclk.c36
-rw-r--r--drivers/char/virtio_console.c196
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c5
-rw-r--r--drivers/char/xillybus/xillybus_of.c5
-rw-r--r--drivers/char/xillybus/xillybus_pcie.c5
-rw-r--r--drivers/comedi/drivers/aio_iiro_16.c3
-rw-r--r--drivers/comedi/drivers/c6xdigio.c6
-rw-r--r--drivers/comedi/drivers/das6402.c10
-rw-r--r--drivers/comedi/drivers/dt2811.c7
-rw-r--r--drivers/comedi/drivers/ni_at_a2150.c13
-rw-r--r--drivers/comedi/drivers/ni_atmio.c14
-rw-r--r--drivers/comedi/drivers/ni_atmio16d.c3
-rw-r--r--drivers/comedi/drivers/ni_pcimio.c5
-rw-r--r--drivers/comedi/drivers/pcl711.c3
-rw-r--r--drivers/comedi/drivers/pcl816.c3
-rw-r--r--drivers/comedi/drivers/pcl818.c3
-rw-r--r--drivers/comedi/drivers/pcmmio.c3
-rw-r--r--drivers/comedi/drivers/pcmuio.c5
-rw-r--r--drivers/comedi/drivers/quatech_daqp_cs.c4
-rw-r--r--drivers/counter/stm32-timer-cnt.c10
-rw-r--r--drivers/counter/ti-ecap-capture.c2
-rw-r--r--drivers/counter/ti-eqep.c2
-rw-r--r--drivers/eisa/eisa-bus.c3
-rw-r--r--drivers/extcon/extcon-adc-jack.c1
-rw-r--r--drivers/firmware/stratix10-rsu.c421
-rw-r--r--drivers/firmware/stratix10-svc.c139
-rw-r--r--drivers/fpga/altera-cvp.c10
-rw-r--r--drivers/fpga/dfl-fme-perf.c2
-rw-r--r--drivers/fpga/stratix10-soc.c21
-rw-r--r--drivers/fpga/xilinx-pr-decoupler.c20
-rw-r--r--drivers/fpga/zynq-fpga.c1
-rw-r--r--drivers/gpib/agilent_82350b/agilent_82350b.c9
-rw-r--r--drivers/gpib/cb7210/cb7210.c8
-rw-r--r--drivers/gpib/cec/cec_gpib.c4
-rw-r--r--drivers/gpib/common/gpib_os.c21
-rw-r--r--drivers/gpib/common/iblib.c3
-rw-r--r--drivers/gpib/fmh_gpib/fmh_gpib.c2
-rw-r--r--drivers/gpib/hp_82341/hp_82341.c4
-rw-r--r--drivers/gpib/include/amccs5933.h10
-rw-r--r--drivers/gpib/ines/ines_gpib.c21
-rw-r--r--drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c13
-rw-r--r--drivers/gpib/tnt4882/mite.h4
-rw-r--r--drivers/gpib/tnt4882/tnt4882_gpib.c4
-rw-r--r--drivers/greybus/manifest.c2
-rw-r--r--drivers/hwmon/Kconfig12
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/iio_hwmon.c1
-rw-r--r--drivers/hwmon/ntc_thermistor.c1
-rw-r--r--drivers/hwmon/sbtsi_temp.c152
-rw-r--r--drivers/hwmon/socfpga-hwmon.c579
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-sysfs.c15
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-cfg.c2
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c59
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-sysfs.c6
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.h12
-rw-r--r--drivers/hwtracing/ptt/hisi_ptt.c25
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/TODO13
-rw-r--r--drivers/iio/accel/adxl345_spi.c4
-rw-r--r--drivers/iio/accel/adxl355_core.c3
-rw-r--r--drivers/iio/accel/adxl355_spi.c4
-rw-r--r--drivers/iio/accel/adxl367.c2
-rw-r--r--drivers/iio/accel/adxl367_spi.c2
-rw-r--r--drivers/iio/accel/adxl372_spi.c4
-rw-r--r--drivers/iio/accel/adxl380_spi.c8
-rw-r--r--drivers/iio/accel/bma180.c4
-rw-r--r--drivers/iio/accel/bma220_core.c3
-rw-r--r--drivers/iio/accel/bma220_spi.c2
-rw-r--r--drivers/iio/accel/bma400_core.c3
-rw-r--r--drivers/iio/accel/bma400_spi.c2
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c6
-rw-r--r--drivers/iio/accel/bmc150-accel-spi.c18
-rw-r--r--drivers/iio/accel/bmi088-accel-spi.c6
-rw-r--r--drivers/iio/accel/dmard09.c7
-rw-r--r--drivers/iio/accel/fxls8962af-core.c2
-rw-r--r--drivers/iio/accel/fxls8962af-spi.c4
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c68
-rw-r--r--drivers/iio/accel/kionix-kx022a.c2
-rw-r--r--drivers/iio/accel/kxsd9-spi.c2
-rw-r--r--drivers/iio/accel/mma7455_spi.c4
-rw-r--r--drivers/iio/accel/mma9551.c4
-rw-r--r--drivers/iio/accel/mma9553.c5
-rw-r--r--drivers/iio/accel/msa311.c2
-rw-r--r--drivers/iio/accel/mxc4005.c5
-rw-r--r--drivers/iio/accel/sca3000.c8
-rw-r--r--drivers/iio/accel/sca3300.c4
-rw-r--r--drivers/iio/accel/st_accel_spi.c40
-rw-r--r--drivers/iio/accel/stk8312.c17
-rw-r--r--drivers/iio/accel/stk8ba50.c14
-rw-r--r--drivers/iio/adc/88pm886-gpadc.c2
-rw-r--r--drivers/iio/adc/Kconfig78
-rw-r--r--drivers/iio/adc/Makefile6
-rw-r--r--drivers/iio/adc/ad4000.c62
-rw-r--r--drivers/iio/adc/ad4030.c14
-rw-r--r--drivers/iio/adc/ad4080.c40
-rw-r--r--drivers/iio/adc/ad4130.c12
-rw-r--r--drivers/iio/adc/ad4134.c2
-rw-r--r--drivers/iio/adc/ad4170-4.c6
-rw-r--r--drivers/iio/adc/ad4851.c18
-rw-r--r--drivers/iio/adc/ad7091r8.c6
-rw-r--r--drivers/iio/adc/ad7124.c4
-rw-r--r--drivers/iio/adc/ad7173.c26
-rw-r--r--drivers/iio/adc/ad7191.c2
-rw-r--r--drivers/iio/adc/ad7192.c10
-rw-r--r--drivers/iio/adc/ad7266.c4
-rw-r--r--drivers/iio/adc/ad7280a.c2
-rw-r--r--drivers/iio/adc/ad7292.c2
-rw-r--r--drivers/iio/adc/ad7298.c2
-rw-r--r--drivers/iio/adc/ad7380.c36
-rw-r--r--drivers/iio/adc/ad7476.c60
-rw-r--r--drivers/iio/adc/ad7606_spi.c22
-rw-r--r--drivers/iio/adc/ad7766.c12
-rw-r--r--drivers/iio/adc/ad7768-1.c8
-rw-r--r--drivers/iio/adc/ad7779.c6
-rw-r--r--drivers/iio/adc/ad7780.c8
-rw-r--r--drivers/iio/adc/ad7791.c10
-rw-r--r--drivers/iio/adc/ad7793.c18
-rw-r--r--drivers/iio/adc/ad7887.c2
-rw-r--r--drivers/iio/adc/ad7923.c14
-rw-r--r--drivers/iio/adc/ad7944.c6
-rw-r--r--drivers/iio/adc/ad7949.c6
-rw-r--r--drivers/iio/adc/ad9467.c14
-rw-r--r--drivers/iio/adc/ade9000.c4
-rw-r--r--drivers/iio/adc/at91_adc.c4
-rw-r--r--drivers/iio/adc/envelope-detector.c1
-rw-r--r--drivers/iio/adc/hi8435.c2
-rw-r--r--drivers/iio/adc/hx711.c98
-rw-r--r--drivers/iio/adc/ltc2378.c857
-rw-r--r--drivers/iio/adc/max1027.c12
-rw-r--r--drivers/iio/adc/max1118.c6
-rw-r--r--drivers/iio/adc/max11205.c4
-rw-r--r--drivers/iio/adc/max11410.c2
-rw-r--r--drivers/iio/adc/max1241.c9
-rw-r--r--drivers/iio/adc/max14001.c4
-rw-r--r--drivers/iio/adc/mcp320x.c26
-rw-r--r--drivers/iio/adc/mcp3564.c24
-rw-r--r--drivers/iio/adc/mcp3911.c14
-rw-r--r--drivers/iio/adc/mt6323-auxadc.c313
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c14
-rw-r--r--drivers/iio/adc/rohm-bd79112.c2
-rw-r--r--drivers/iio/adc/rtq6056.c4
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c12
-rw-r--r--drivers/iio/adc/ti-adc081c.c6
-rw-r--r--drivers/iio/adc/ti-adc0832.c8
-rw-r--r--drivers/iio/adc/ti-adc084s021.c8
-rw-r--r--drivers/iio/adc/ti-adc108s102.c2
-rw-r--r--drivers/iio/adc/ti-adc12138.c6
-rw-r--r--drivers/iio/adc/ti-adc128s052.c24
-rw-r--r--drivers/iio/adc/ti-adc161s626.c4
-rw-r--r--drivers/iio/adc/ti-ads1015.c44
-rw-r--r--drivers/iio/adc/ti-ads1018.c4
-rw-r--r--drivers/iio/adc/ti-ads112c14.c1454
-rw-r--r--drivers/iio/adc/ti-ads124s08.c4
-rw-r--r--drivers/iio/adc/ti-ads1298.c2
-rw-r--r--drivers/iio/adc/ti-ads131e08.c6
-rw-r--r--drivers/iio/adc/ti-ads131m02.c10
-rw-r--r--drivers/iio/adc/ti-ads7950.c24
-rw-r--r--drivers/iio/adc/ti-ads8688.c4
-rw-r--r--drivers/iio/adc/ti-lmp92064.c2
-rw-r--r--drivers/iio/adc/ti-tlc4541.c4
-rw-r--r--drivers/iio/adc/ti-tsc2046.c25
-rw-r--r--drivers/iio/adc/versal-sysmon-core.c1052
-rw-r--r--drivers/iio/adc/versal-sysmon-i2c.c134
-rw-r--r--drivers/iio/adc/versal-sysmon.c92
-rw-r--r--drivers/iio/adc/versal-sysmon.h120
-rw-r--r--drivers/iio/addac/ad74115.c2
-rw-r--r--drivers/iio/addac/ad74413r.c2
-rw-r--r--drivers/iio/afe/iio-rescale.c1
-rw-r--r--drivers/iio/amplifiers/ad8366.c26
-rw-r--r--drivers/iio/amplifiers/ada4250.c2
-rw-r--r--drivers/iio/buffer/industrialio-buffer-cb.c1
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c42
-rw-r--r--drivers/iio/buffer/industrialio-hw-consumer.c1
-rw-r--r--drivers/iio/chemical/bme680_i2c.c7
-rw-r--r--drivers/iio/chemical/bme680_spi.c9
-rw-r--r--drivers/iio/chemical/ccs811.c4
-rw-r--r--drivers/iio/chemical/ens160_core.c2
-rw-r--r--drivers/iio/chemical/ens160_spi.c2
-rw-r--r--drivers/iio/chemical/scd30_core.c12
-rw-r--r--drivers/iio/chemical/scd30_i2c.c4
-rw-r--r--drivers/iio/chemical/scd30_serial.c4
-rw-r--r--drivers/iio/chemical/sgp30.c3
-rw-r--r--drivers/iio/chemical/sps30_i2c.c4
-rw-r--r--drivers/iio/chemical/sps30_serial.c4
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c81
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c7
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.h2
-rw-r--r--drivers/iio/common/inv_sensors/inv_sensors_timestamp.c109
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c4
-rw-r--r--drivers/iio/dac/Kconfig61
-rw-r--r--drivers/iio/dac/Makefile2
-rw-r--r--drivers/iio/dac/ad3530r.c359
-rw-r--r--drivers/iio/dac/ad5064.c32
-rw-r--r--drivers/iio/dac/ad5360.c16
-rw-r--r--drivers/iio/dac/ad5380.c32
-rw-r--r--drivers/iio/dac/ad5446-spi.c62
-rw-r--r--drivers/iio/dac/ad5449.c14
-rw-r--r--drivers/iio/dac/ad5504.c4
-rw-r--r--drivers/iio/dac/ad5624r_spi.c12
-rw-r--r--drivers/iio/dac/ad5686-spi.c165
-rw-r--r--drivers/iio/dac/ad5686.c307
-rw-r--r--drivers/iio/dac/ad5686.h63
-rw-r--r--drivers/iio/dac/ad5696-i2c.c32
-rw-r--r--drivers/iio/dac/ad5706r.c2
-rw-r--r--drivers/iio/dac/ad5755.c10
-rw-r--r--drivers/iio/dac/ad5758.c2
-rw-r--r--drivers/iio/dac/ad5761.c8
-rw-r--r--drivers/iio/dac/ad5764.c8
-rw-r--r--drivers/iio/dac/ad5766.c4
-rw-r--r--drivers/iio/dac/ad5770r.c2
-rw-r--r--drivers/iio/dac/ad5791.c10
-rw-r--r--drivers/iio/dac/ad7293.c2
-rw-r--r--drivers/iio/dac/ad7303.c2
-rw-r--r--drivers/iio/dac/ad8460.c1
-rw-r--r--drivers/iio/dac/ad8801.c4
-rw-r--r--drivers/iio/dac/ad9739a.c2
-rw-r--r--drivers/iio/dac/dpot-dac.c1
-rw-r--r--drivers/iio/dac/ltc1660.c4
-rw-r--r--drivers/iio/dac/ltc2632.c44
-rw-r--r--drivers/iio/dac/ltc2664.c4
-rw-r--r--drivers/iio/dac/ltc2688.c2
-rw-r--r--drivers/iio/dac/max22007.c2
-rw-r--r--drivers/iio/dac/max5522.c31
-rw-r--r--drivers/iio/dac/mcf54415_dac.c180
-rw-r--r--drivers/iio/dac/mcp47a1.c166
-rw-r--r--drivers/iio/dac/mcp4821.c12
-rw-r--r--drivers/iio/dac/mcp4922.c8
-rw-r--r--drivers/iio/dac/rohm-bd79703.c8
-rw-r--r--drivers/iio/dac/ti-dac082s085.c12
-rw-r--r--drivers/iio/dac/ti-dac7311.c6
-rw-r--r--drivers/iio/dac/ti-dac7612.c2
-rw-r--r--drivers/iio/filter/admv8818.c2
-rw-r--r--drivers/iio/flow/Kconfig22
-rw-r--r--drivers/iio/flow/Makefile7
-rw-r--r--drivers/iio/flow/slf3s.c542
-rw-r--r--drivers/iio/frequency/Kconfig10
-rw-r--r--drivers/iio/frequency/Makefile1
-rw-r--r--drivers/iio/frequency/ad9523.c3
-rw-r--r--drivers/iio/frequency/adf41513.c1246
-rw-r--r--drivers/iio/frequency/adf4350.c4
-rw-r--r--drivers/iio/frequency/adf4371.c4
-rw-r--r--drivers/iio/frequency/adf4377.c85
-rw-r--r--drivers/iio/frequency/admv1013.c2
-rw-r--r--drivers/iio/frequency/admv1014.c2
-rw-r--r--drivers/iio/frequency/adrf6780.c2
-rw-r--r--drivers/iio/gyro/adis16080.c4
-rw-r--r--drivers/iio/gyro/adis16136.c8
-rw-r--r--drivers/iio/gyro/adis16260.c12
-rw-r--r--drivers/iio/gyro/adxrs290.c5
-rw-r--r--drivers/iio/gyro/adxrs450.c4
-rw-r--r--drivers/iio/gyro/bmg160_spi.c7
-rw-r--r--drivers/iio/gyro/fxas21002c_spi.c2
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c57
-rw-r--r--drivers/iio/gyro/st_gyro_spi.c18
-rw-r--r--drivers/iio/health/afe4403.c6
-rw-r--r--drivers/iio/health/afe4404.c4
-rw-r--r--drivers/iio/health/max30100.c4
-rw-r--r--drivers/iio/health/max30102.c4
-rw-r--r--drivers/iio/humidity/hdc3020.c3
-rw-r--r--drivers/iio/humidity/hid-sensor-humidity.c35
-rw-r--r--drivers/iio/humidity/hts221_buffer.c5
-rw-r--r--drivers/iio/humidity/hts221_spi.c2
-rw-r--r--drivers/iio/iio_core_trigger.h2
-rw-r--r--drivers/iio/imu/Kconfig1
-rw-r--r--drivers/iio/imu/Makefile1
-rw-r--r--drivers/iio/imu/adis16400.c30
-rw-r--r--drivers/iio/imu/adis16460.c2
-rw-r--r--drivers/iio/imu/adis16475.c54
-rw-r--r--drivers/iio/imu/adis16480.c44
-rw-r--r--drivers/iio/imu/adis16550.c89
-rw-r--r--drivers/iio/imu/bmi160/bmi160_core.c2
-rw-r--r--drivers/iio/imu/bmi160/bmi160_spi.c4
-rw-r--r--drivers/iio/imu/bmi270/bmi270_core.c2
-rw-r--r--drivers/iio/imu/bmi270/bmi270_spi.c5
-rw-r--r--drivers/iio/imu/bmi323/bmi323_core.c4
-rw-r--r--drivers/iio/imu/bmi323/bmi323_spi.c2
-rw-r--r--drivers/iio/imu/fxos8700_core.c2
-rw-r--r--drivers/iio/imu/fxos8700_spi.c2
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600.h10
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c18
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c45
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h3
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_core.c12
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c16
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c6
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c20
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c3
-rw-r--r--drivers/iio/imu/inv_icm42607/Kconfig30
-rw-r--r--drivers/iio/imu/inv_icm42607/Makefile13
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607.h421
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c316
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_core.c702
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c313
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c97
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c107
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c99
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h38
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600.h2
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c7
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_core.c2
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c2
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c16
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c4
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c34
-rw-r--r--drivers/iio/imu/smi240.c2
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c5
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c5
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c48
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c4
-rw-r--r--drivers/iio/industrialio-backend.c8
-rw-r--r--drivers/iio/industrialio-core.c52
-rw-r--r--drivers/iio/inkern.c54
-rw-r--r--drivers/iio/light/adux1020.c4
-rw-r--r--drivers/iio/light/apds9160.c7
-rw-r--r--drivers/iio/light/apds9300.c4
-rw-r--r--drivers/iio/light/apds9306.c3
-rw-r--r--drivers/iio/light/apds9960.c4
-rw-r--r--drivers/iio/light/bh1745.c3
-rw-r--r--drivers/iio/light/cm32181.c2
-rw-r--r--drivers/iio/light/cm3323.c17
-rw-r--r--drivers/iio/light/cm3605.c5
-rw-r--r--drivers/iio/light/gp2ap002.c5
-rw-r--r--drivers/iio/light/hid-sensor-als.c74
-rw-r--r--drivers/iio/light/hid-sensor-prox.c49
-rw-r--r--drivers/iio/light/isl29028.c22
-rw-r--r--drivers/iio/light/ltr390.c3
-rw-r--r--drivers/iio/light/ltr501.c5
-rw-r--r--drivers/iio/light/opt3001.c539
-rw-r--r--drivers/iio/light/opt4060.c2
-rw-r--r--drivers/iio/light/rpr0521.c10
-rw-r--r--drivers/iio/light/si1145.c4
-rw-r--r--drivers/iio/light/st_uvis25_core.c5
-rw-r--r--drivers/iio/light/st_uvis25_spi.c2
-rw-r--r--drivers/iio/light/stk3310.c5
-rw-r--r--drivers/iio/light/tcs3472.c319
-rw-r--r--drivers/iio/light/tsl2563.c2
-rw-r--r--drivers/iio/light/tsl2583.c44
-rw-r--r--drivers/iio/light/tsl2772.c7
-rw-r--r--drivers/iio/light/vcnl4000.c16
-rw-r--r--drivers/iio/light/vcnl4035.c23
-rw-r--r--drivers/iio/light/veml6030.c4
-rw-r--r--drivers/iio/light/vl6180.c2
-rw-r--r--drivers/iio/magnetometer/Kconfig22
-rw-r--r--drivers/iio/magnetometer/Makefile3
-rw-r--r--drivers/iio/magnetometer/ak8974.c11
-rw-r--r--drivers/iio/magnetometer/bmc150_magn.c50
-rw-r--r--drivers/iio/magnetometer/bmc150_magn_spi.c6
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c73
-rw-r--r--drivers/iio/magnetometer/hmc5843_spi.c6
-rw-r--r--drivers/iio/magnetometer/mmc5983.c2
-rw-r--r--drivers/iio/magnetometer/qmc5883l.c516
-rw-r--r--drivers/iio/magnetometer/qmc6308.c601
-rw-r--r--drivers/iio/magnetometer/rm3100-core.c4
-rw-r--r--drivers/iio/magnetometer/st_magn_spi.c12
-rw-r--r--drivers/iio/multiplexer/iio-mux.c1
-rw-r--r--drivers/iio/orientation/hid-sensor-incl-3d.c55
-rw-r--r--drivers/iio/orientation/hid-sensor-rotation.c48
-rw-r--r--drivers/iio/position/hid-sensor-custom-intel-hinge.c19
-rw-r--r--drivers/iio/potentiometer/max5481.c8
-rw-r--r--drivers/iio/potentiometer/max5487.c6
-rw-r--r--drivers/iio/potentiometer/mcp41010.c12
-rw-r--r--drivers/iio/potentiometer/mcp4131.c128
-rw-r--r--drivers/iio/potentiometer/x9250.c4
-rw-r--r--drivers/iio/potentiostat/lmp91000.c1
-rw-r--r--drivers/iio/pressure/abp2030pa.c1
-rw-r--r--drivers/iio/pressure/abp2030pa_spi.c2
-rw-r--r--drivers/iio/pressure/bmp280-core.c34
-rw-r--r--drivers/iio/pressure/bmp280-spi.c14
-rw-r--r--drivers/iio/pressure/dlhl60d.c4
-rw-r--r--drivers/iio/pressure/hid-sensor-press.c57
-rw-r--r--drivers/iio/pressure/hsc030pa_spi.c2
-rw-r--r--drivers/iio/pressure/mpl115.c11
-rw-r--r--drivers/iio/pressure/mpl115_spi.c2
-rw-r--r--drivers/iio/pressure/mprls0025pa_spi.c2
-rw-r--r--drivers/iio/pressure/ms5611_spi.c4
-rw-r--r--drivers/iio/pressure/rohm-bm1390.c25
-rw-r--r--drivers/iio/pressure/st_pressure_spi.c24
-rw-r--r--drivers/iio/pressure/zpa2326.c5
-rw-r--r--drivers/iio/pressure/zpa2326_spi.c2
-rw-r--r--drivers/iio/proximity/as3935.c6
-rw-r--r--drivers/iio/proximity/d3323aa.c2
-rw-r--r--drivers/iio/proximity/hx9023s.c2
-rw-r--r--drivers/iio/proximity/irsd200.c2
-rw-r--r--drivers/iio/proximity/mb1232.c4
-rw-r--r--drivers/iio/proximity/srf04.c4
-rw-r--r--drivers/iio/proximity/sx_common.c2
-rw-r--r--drivers/iio/proximity/vcnl3020.c6
-rw-r--r--drivers/iio/proximity/vl53l0x-i2c.c4
-rw-r--r--drivers/iio/proximity/vl53l1x-i2c.c43
-rw-r--r--drivers/iio/resolver/ad2s1200.c20
-rw-r--r--drivers/iio/resolver/ad2s1210.c32
-rw-r--r--drivers/iio/resolver/ad2s90.c2
-rw-r--r--drivers/iio/temperature/hid-sensor-temperature.c34
-rw-r--r--drivers/iio/temperature/ltc2983.c12
-rw-r--r--drivers/iio/temperature/max31856.c2
-rw-r--r--drivers/iio/temperature/max31865.c2
-rw-r--r--drivers/iio/temperature/maxim_thermocouple.c18
-rw-r--r--drivers/iio/temperature/tmp007.c4
-rw-r--r--drivers/iio/temperature/tmp117.c13
-rw-r--r--drivers/iio/test/iio-test-format.c97
-rw-r--r--drivers/iio/test/iio-test-rescale.c2
-rw-r--r--drivers/input/joystick/adc-joystick.c1
-rw-r--r--drivers/input/keyboard/adc-keys.c1
-rw-r--r--drivers/input/touchscreen/colibri-vf50-ts.c1
-rw-r--r--drivers/input/touchscreen/resistive-adc-touch.c1
-rw-r--r--drivers/interconnect/core.c7
-rw-r--r--drivers/interconnect/debugfs-client.c2
-rw-r--r--drivers/interconnect/qcom/Kconfig12
-rw-r--r--drivers/interconnect/qcom/Makefile2
-rw-r--r--drivers/interconnect/qcom/icc-rpm.c7
-rw-r--r--drivers/interconnect/qcom/icc-rpm.h8
-rw-r--r--drivers/interconnect/qcom/maili.c2091
-rw-r--r--drivers/interconnect/qcom/osm-l3.c16
-rw-r--r--drivers/interconnect/qcom/sc8280xp.c356
-rw-r--r--drivers/interconnect/qcom/x1e80100.c485
-rw-r--r--drivers/ipack/devices/ipoctal.c60
-rw-r--r--drivers/mailbox/mailbox-mchp-ipc-sbi.c2
-rw-r--r--drivers/misc/Kconfig67
-rw-r--r--drivers/misc/Makefile5
-rw-r--r--drivers/misc/ad525x_dpot-i2c.c53
-rw-r--r--drivers/misc/ad525x_dpot-spi.c59
-rw-r--r--drivers/misc/ad525x_dpot.c177
-rw-r--r--drivers/misc/ad525x_dpot.h3
-rw-r--r--drivers/misc/amd-sbi/Kconfig13
-rw-r--r--drivers/misc/amd-sbi/Makefile3
-rw-r--r--drivers/misc/amd-sbi/rmi-core.c4
-rw-r--r--drivers/misc/amd-sbi/rmi-i2c.c6
-rw-r--r--drivers/misc/amd-sbi/tsi-core.c207
-rw-r--r--drivers/misc/amd-sbi/tsi-core.h29
-rw-r--r--drivers/misc/amd-sbi/tsi.c234
-rw-r--r--drivers/misc/apds9802als.c2
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_dev.c17
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_msg.c24
-rw-r--r--drivers/misc/bh1770glc.c6
-rw-r--r--drivers/misc/cardreader/alcor_pci.c2
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.c2
-rw-r--r--drivers/misc/cb710/core.c5
-rw-r--r--drivers/misc/ds1682.c6
-rw-r--r--drivers/misc/eeprom/Kconfig93
-rw-r--r--drivers/misc/eeprom/Makefile5
-rw-r--r--drivers/misc/eeprom/idt_89hpesx.c220
-rw-r--r--drivers/misc/eeprom/max6875.c2
-rw-r--r--drivers/misc/enclosure.c4
-rw-r--r--drivers/misc/fastrpc.c260
-rw-r--r--drivers/misc/gehc-achc.c4
-rw-r--r--drivers/misc/genwqe/card_base.c88
-rw-r--r--drivers/misc/genwqe/card_utils.c2
-rw-r--r--drivers/misc/hi6421v600-irq.c7
-rw-r--r--drivers/misc/hisi_hikey_usb.c274
-rw-r--r--drivers/misc/hmc6352.c2
-rw-r--r--drivers/misc/hpilo.c13
-rw-r--r--drivers/misc/ibmasm/Makefile16
-rw-r--r--drivers/misc/ibmasm/command.c173
-rw-r--r--drivers/misc/ibmasm/dot_command.c138
-rw-r--r--drivers/misc/ibmasm/dot_command.h64
-rw-r--r--drivers/misc/ibmasm/event.c163
-rw-r--r--drivers/misc/ibmasm/heartbeat.c88
-rw-r--r--drivers/misc/ibmasm/i2o.h63
-rw-r--r--drivers/misc/ibmasm/ibmasm.h209
-rw-r--r--drivers/misc/ibmasm/ibmasmfs.c604
-rw-r--r--drivers/misc/ibmasm/lowlevel.c75
-rw-r--r--drivers/misc/ibmasm/lowlevel.h123
-rw-r--r--drivers/misc/ibmasm/module.c224
-rw-r--r--drivers/misc/ibmasm/r_heartbeat.c86
-rw-r--r--drivers/misc/ibmasm/remote.c273
-rw-r--r--drivers/misc/ibmasm/remote.h257
-rw-r--r--drivers/misc/ibmasm/uart.c58
-rw-r--r--drivers/misc/ibmvmc.c21
-rw-r--r--drivers/misc/ics932s401.c2
-rw-r--r--drivers/misc/isl29003.c4
-rw-r--r--drivers/misc/isl29020.c2
-rw-r--r--drivers/misc/issei/Kconfig29
-rw-r--r--drivers/misc/issei/Makefile15
-rw-r--r--drivers/misc/issei/cdev.c446
-rw-r--r--drivers/misc/issei/cdev.h16
-rw-r--r--drivers/misc/issei/dma.c154
-rw-r--r--drivers/misc/issei/dma.h69
-rw-r--r--drivers/misc/issei/fw_client.c240
-rw-r--r--drivers/misc/issei/fw_client.h45
-rw-r--r--drivers/misc/issei/ham.c169
-rw-r--r--drivers/misc/issei/ham.h20
-rw-r--r--drivers/misc/issei/host_client.c519
-rw-r--r--drivers/misc/issei/host_client.h75
-rw-r--r--drivers/misc/issei/hw_heci.c550
-rw-r--r--drivers/misc/issei/hw_heci.h47
-rw-r--r--drivers/misc/issei/hw_heci_regs.h35
-rw-r--r--drivers/misc/issei/hw_msg.h163
-rw-r--r--drivers/misc/issei/issei_dev.h163
-rw-r--r--drivers/misc/issei/main.c296
-rw-r--r--drivers/misc/issei/pci_heci.c151
-rw-r--r--drivers/misc/keba/cp500.c17
-rw-r--r--drivers/misc/keba/lan9252.c4
-rw-r--r--drivers/misc/lan966x_pci.c1
-rw-r--r--drivers/misc/lattice-ecp3-config.c4
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c8
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_spi.c2
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c2
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c4
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c4
-rw-r--r--drivers/misc/mei/gsc-me.c4
-rw-r--r--drivers/misc/mei/mei_lb.c4
-rw-r--r--drivers/misc/mei/pci-csc.c2
-rw-r--r--drivers/misc/mei/pci-me.c212
-rw-r--r--drivers/misc/mei/pci-txe.c6
-rw-r--r--drivers/misc/mei/platform-vsc.c2
-rw-r--r--drivers/misc/mei/vsc-tp.c10
-rw-r--r--drivers/misc/mrvl_cn10k_dpi.c6
-rw-r--r--drivers/misc/nsm.c7
-rw-r--r--drivers/misc/ntsync.c3
-rw-r--r--drivers/misc/ocxl/pci.c2
-rw-r--r--drivers/misc/open-dice.c53
-rw-r--r--drivers/misc/pch_phub.c59
-rw-r--r--drivers/misc/pci_endpoint_test.c16
-rw-r--r--drivers/misc/phantom.c6
-rw-r--r--drivers/misc/pvpanic/pvpanic-mmio.c8
-rw-r--r--drivers/misc/pvpanic/pvpanic-pci.c4
-rw-r--r--drivers/misc/qcom-coincell.c4
-rw-r--r--drivers/misc/rp1/rp1_pci.c29
-rw-r--r--drivers/misc/sgi-gru/Makefile6
-rw-r--r--drivers/misc/sgi-gru/gru.h76
-rw-r--r--drivers/misc/sgi-gru/gru_instructions.h726
-rw-r--r--drivers/misc/sgi-gru/grufault.c903
-rw-r--r--drivers/misc/sgi-gru/grufile.c540
-rw-r--r--drivers/misc/sgi-gru/gruhandles.c192
-rw-r--r--drivers/misc/sgi-gru/gruhandles.h517
-rw-r--r--drivers/misc/sgi-gru/grukdump.c223
-rw-r--r--drivers/misc/sgi-gru/grukservices.c1157
-rw-r--r--drivers/misc/sgi-gru/grukservices.h201
-rw-r--r--drivers/misc/sgi-gru/grulib.h153
-rw-r--r--drivers/misc/sgi-gru/grumain.c969
-rw-r--r--drivers/misc/sgi-gru/gruprocfs.c308
-rw-r--r--drivers/misc/sgi-gru/grutables.h659
-rw-r--r--drivers/misc/sgi-gru/grutlbpurge.c316
-rw-r--r--drivers/misc/sgi-xp/Makefile13
-rw-r--r--drivers/misc/sgi-xp/xp.h341
-rw-r--r--drivers/misc/sgi-xp/xp_main.c261
-rw-r--r--drivers/misc/sgi-xp/xp_uv.c151
-rw-r--r--drivers/misc/sgi-xp/xpc.h732
-rw-r--r--drivers/misc/sgi-xp/xpc_channel.c1011
-rw-r--r--drivers/misc/sgi-xp/xpc_main.c1309
-rw-r--r--drivers/misc/sgi-xp/xpc_partition.c545
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c1727
-rw-r--r--drivers/misc/sgi-xp/xpnet.c599
-rw-r--r--drivers/misc/sram.c2
-rw-r--r--drivers/misc/ti_fpc202.c2
-rw-r--r--drivers/misc/tps6594-esm.c2
-rw-r--r--drivers/misc/tps6594-pfsm.c2
-rw-r--r--drivers/misc/tsl2550.c2
-rw-r--r--drivers/misc/vcpu_stall_detector.c4
-rw-r--r--drivers/misc/vmw_vmci/vmci_event.c4
-rw-r--r--drivers/misc/vmw_vmci/vmci_guest.c4
-rw-r--r--drivers/misc/xilinx_sdfec.c78
-rw-r--r--drivers/nvmem/Kconfig110
-rw-r--r--drivers/nvmem/Makefile7
-rw-r--r--drivers/nvmem/airoha-smc-efuses.c125
-rw-r--r--drivers/nvmem/at24.c (renamed from drivers/misc/eeprom/at24.c)0
-rw-r--r--drivers/nvmem/at25.c (renamed from drivers/misc/eeprom/at25.c)0
-rw-r--r--drivers/nvmem/brcm_nvram.c15
-rw-r--r--drivers/nvmem/core.c213
-rw-r--r--drivers/nvmem/ee1004.c (renamed from drivers/misc/eeprom/ee1004.c)0
-rw-r--r--drivers/nvmem/eeprom_93xx46.c (renamed from drivers/misc/eeprom/eeprom_93xx46.c)0
-rw-r--r--drivers/nvmem/internals.h10
-rw-r--r--drivers/nvmem/lan9662-otpc.c12
-rw-r--r--drivers/nvmem/layouts/u-boot-env.c5
-rw-r--r--drivers/nvmem/m24lr.c (renamed from drivers/misc/eeprom/m24lr.c)0
-rw-r--r--drivers/nvmem/nintendo-otp.c9
-rw-r--r--drivers/nvmem/rockchip-otp.c9
-rw-r--r--drivers/parport/parport_pc.c204
-rw-r--r--drivers/parport/parport_serial.c280
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-mhi.c10
-rw-r--r--drivers/phy/motorola/phy-cpcap-usb.c1
-rw-r--r--drivers/power/supply/ab8500_btemp.c1
-rw-r--r--drivers/power/supply/ab8500_charger.c1
-rw-r--r--drivers/power/supply/ab8500_fg.c1
-rw-r--r--drivers/power/supply/axp20x_ac_power.c1
-rw-r--r--drivers/power/supply/axp20x_battery.c1
-rw-r--r--drivers/power/supply/axp20x_usb_power.c1
-rw-r--r--drivers/power/supply/axp288_fuel_gauge.c1
-rw-r--r--drivers/power/supply/cpcap-battery.c1
-rw-r--r--drivers/power/supply/cpcap-charger.c1
-rw-r--r--drivers/power/supply/da9150-charger.c1
-rw-r--r--drivers/power/supply/generic-adc-battery.c1
-rw-r--r--drivers/power/supply/ingenic-battery.c1
-rw-r--r--drivers/power/supply/intel_dc_ti_battery.c1
-rw-r--r--drivers/power/supply/lego_ev3_battery.c1
-rw-r--r--drivers/power/supply/lp8788-charger.c1
-rw-r--r--drivers/power/supply/max17040_battery.c1
-rw-r--r--drivers/power/supply/mp2629_charger.c1
-rw-r--r--drivers/power/supply/mt6370-charger.c1
-rw-r--r--drivers/power/supply/qcom_smbx.c1
-rw-r--r--drivers/power/supply/rn5t618_power.c1
-rw-r--r--drivers/power/supply/rx51_battery.c1
-rw-r--r--drivers/power/supply/sc27xx_fuel_gauge.c1
-rw-r--r--drivers/power/supply/twl4030_charger.c1
-rw-r--r--drivers/power/supply/twl4030_madc_battery.c1
-rw-r--r--drivers/power/supply/twl6030_charger.c1
-rw-r--r--drivers/staging/iio/adc/ad7816.c6
-rw-r--r--drivers/staging/iio/addac/adt7316-spi.c13
-rw-r--r--drivers/staging/iio/addac/adt7316.c5
-rw-r--r--drivers/staging/iio/frequency/ad9832.c24
-rw-r--r--drivers/staging/iio/frequency/ad9834.c30
-rw-r--r--drivers/staging/iio/frequency/dds.h50
-rw-r--r--drivers/thermal/qcom/qcom-spmi-adc-tm5.c1
-rw-r--r--drivers/thermal/qcom/qcom-spmi-temp-alarm.c1
-rw-r--r--drivers/thermal/renesas/rzg3s_thermal.c1
-rw-r--r--drivers/thermal/thermal-generic-adc.c1
-rw-r--r--drivers/uio/uio.c7
-rw-r--r--drivers/uio/uio_fsl_elbc_gpcm.c60
-rw-r--r--drivers/uio/uio_sercos3.c1
-rw-r--r--drivers/w1/masters/ds2482.c12
-rw-r--r--drivers/w1/slaves/w1_ds28e17.c8
-rw-r--r--drivers/w1/w1.c5
652 files changed, 24036 insertions, 21061 deletions
diff --git a/drivers/accessibility/speakup/fakekey.c b/drivers/accessibility/speakup/fakekey.c
index 868c47b2a59b..9c0b2403876a 100644
--- a/drivers/accessibility/speakup/fakekey.c
+++ b/drivers/accessibility/speakup/fakekey.c
@@ -71,9 +71,9 @@ void speakup_fake_down_arrow(void)
input_sync(virt_keyboard);
__this_cpu_write(reporting_keystroke, false);
- /* reenable preemption */
+ /* re-enable preemption */
preempt_enable();
- /* reenable keyboard interrupts */
+ /* re-enable keyboard interrupts */
local_irq_restore(flags);
}
diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c
index 0882bab10fb8..a9e308bdd0be 100644
--- a/drivers/accessibility/speakup/genmap.c
+++ b/drivers/accessibility/speakup/genmap.c
@@ -71,7 +71,6 @@ main(int argc, char *argv[])
open_input(NULL, argv[1]);
while (fgets(buffer, sizeof(buffer), infile)) {
- lc++;
value = shift_state = 0;
cp = strtok(buffer, delims);
@@ -153,7 +152,7 @@ main(int argc, char *argv[])
continue;
printf("\n\t%d,", lc);
for (i = 0; i < max_states; i++)
- printf(" %d,", (unsigned int)kp[i]);
+ printf(" %u,", (unsigned int)kp[i]);
}
printf("\n\t0, %d\n", map_ver);
diff --git a/drivers/accessibility/speakup/i18n.c b/drivers/accessibility/speakup/i18n.c
index d62079b1661f..554bf81f2c1c 100644
--- a/drivers/accessibility/speakup/i18n.c
+++ b/drivers/accessibility/speakup/i18n.c
@@ -31,7 +31,7 @@ static char *speakup_default_msgs[MSG_LAST_INDEX] = {
[MSG_CURSORING_OFF] = "cursoring off",
[MSG_CURSORING_ON] = "cursoring on",
[MSG_HIGHLIGHT_TRACKING] = "highlight tracking",
- [MSG_READ_WINDOW] = "read windo",
+ [MSG_READ_WINDOW] = "read window",
[MSG_READ_ALL] = "read all",
[MSG_EDIT_DONE] = "edit done",
[MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset",
diff --git a/drivers/accessibility/speakup/keyhelp.c b/drivers/accessibility/speakup/keyhelp.c
index 822ceac83068..0940f430ac0f 100644
--- a/drivers/accessibility/speakup/keyhelp.c
+++ b/drivers/accessibility/speakup/keyhelp.c
@@ -8,14 +8,15 @@
*/
#include <linux/keyboard.h>
+#include <linux/ctype.h>
#include "spk_priv.h"
#include "speakup.h"
#define MAXFUNCS 130
#define MAXKEYS 256
static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1;
-static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS];
-static u_short masks[] = { 32, 16, 8, 4, 2, 1 };
+static u16 key_offsets[MAXFUNCS], key_data[MAXKEYS];
+static u16 masks[] = { 32, 16, 8, 4, 2, 1 };
static short letter_offsets[26] = {
-1, -1, -1, -1, -1, -1, -1, -1,
@@ -49,7 +50,7 @@ static int cur_item, nstates;
static void build_key_data(void)
{
u_char *kp, counters[MAXFUNCS], ch, ch1;
- u_short *p_key, key;
+ u16 *p_key, key;
int i, offset = 1;
nstates = (int)(state_tbl[-1]);
@@ -111,7 +112,7 @@ static void say_key(int key)
spk_msg_get(MSG_KEYNAMES_START + (key - 1)));
}
-static int help_init(void)
+static void help_init(void)
{
char start = SPACE;
int i;
@@ -120,21 +121,27 @@ static int help_init(void)
state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2;
for (i = 0; i < num_funcs; i++) {
char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i);
+ char first_letter;
- if (start == *cur_funcname)
+ first_letter = tolower(*cur_funcname);
+
+ /* Accept only 'a'..'z' to index letter_offsets[] safely */
+ if (first_letter < 'a' || first_letter > 'z')
+ continue;
+
+ if (start == first_letter)
continue;
- start = *cur_funcname;
+ start = first_letter;
letter_offsets[(start & 31) - 1] = i;
}
- return 0;
}
-int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
+int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key)
{
int i, n;
char *name;
u_char func, *kp;
- u_short *p_keys, val;
+ u16 *p_keys, val;
if (letter_offsets[0] == -1)
help_init();
@@ -144,7 +151,7 @@ int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP));
return 1;
}
- ch |= 32; /* lower case */
+ ch = tolower(ch);
if (ch < 'a' || ch > 'z')
return -1;
if (letter_offsets[ch - 'a'] == -1) {
diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c
index 0dfdb6608e02..a73b478e06de 100644
--- a/drivers/accessibility/speakup/kobjects.c
+++ b/drivers/accessibility/speakup/kobjects.c
@@ -92,13 +92,13 @@ static void report_char_chartab_status(int reset, int received, int used,
if (reset) {
pr_info("%s reset to defaults\n", object_type[do_characters]);
} else if (received) {
- len = snprintf(buf, sizeof(buf),
- " updated %d of %d %s\n",
- used, received, object_type[do_characters]);
+ len = scnprintf(buf, sizeof(buf),
+ " updated %d of %d %s\n",
+ used, received, object_type[do_characters]);
if (rejected)
snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
" with %d reject%s\n",
- rejected, rejected > 1 ? "s" : "");
+ rejected, str_plural(rejected));
pr_info("%s", buf);
}
}
@@ -120,7 +120,7 @@ static ssize_t chars_chartab_store(struct kobject *kobj,
ssize_t retval = count;
unsigned long flags;
unsigned long index = 0;
- int charclass = 0;
+ u16 charclass = 0;
int received = 0;
int used = 0;
int rejected = 0;
@@ -461,7 +461,7 @@ static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
struct st_var_header *p_header;
struct punc_var_t *var;
struct st_bits_data *pb;
- short mask;
+ u16 mask;
unsigned long flags;
p_header = spk_var_header_by_name(attr->attr.name);
@@ -740,7 +740,7 @@ static void report_msg_status(int reset, int received, int used,
if (rejected)
snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
" with %d reject%s\n",
- rejected, rejected > 1 ? "s" : "");
+ rejected, str_plural(rejected));
pr_info("%s", buf);
}
}
diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c
index 78a77dd789a2..e9b7c2761f6f 100644
--- a/drivers/accessibility/speakup/main.c
+++ b/drivers/accessibility/speakup/main.c
@@ -63,7 +63,7 @@ int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
int spk_no_intr, spk_spell_delay;
int spk_key_echo, spk_say_word_ctl;
int spk_say_ctrl, spk_bell_pos;
-short spk_punc_mask;
+u16 spk_punc_mask;
int spk_punc_level, spk_reading_punc;
int spk_cur_phonetic;
char spk_str_caps_start[MAXVARLEN + 1] = "\0";
@@ -183,13 +183,13 @@ char *spk_default_chars[256] = {
/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
};
-/* array of 256 u_short (one for each character)
+/* array of 256 u16 (one for each character)
* initialized to default_chartab and user selectable via
* /sys/module/speakup/parameters/chartab
*/
-u_short spk_chartab[256];
+u16 spk_chartab[256];
-static u_short default_chartab[256] = {
+static u16 default_chartab[256] = {
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
@@ -267,10 +267,10 @@ static void speakup_date(struct vc_data *vc)
spk_y = spk_cy = vc->state.y;
spk_pos = spk_cp = vc->vc_pos;
spk_old_attr = spk_attr;
- spk_attr = get_attributes(vc, (u_short *)spk_pos);
+ spk_attr = get_attributes(vc, (u16 *)spk_pos);
}
-static void bleep(u_short val)
+static void bleep(u16 val)
{
static const short vals[] = {
350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
@@ -346,14 +346,14 @@ static void speakup_cut(struct vc_data *vc)
if (!mark_cut_flag) {
mark_cut_flag = 1;
- spk_xs = (u_short)spk_x;
- spk_ys = (u_short)spk_y;
+ spk_xs = (u16)spk_x;
+ spk_ys = (u16)spk_y;
spk_sel_cons = vc;
synth_printf("%s\n", spk_msg_get(MSG_MARK));
return;
}
- spk_xe = (u_short)spk_x;
- spk_ye = (u_short)spk_y;
+ spk_xe = (u16)spk_x;
+ spk_ye = (u16)spk_y;
mark_cut_flag = 0;
synth_printf("%s\n", spk_msg_get(MSG_CUT));
@@ -482,7 +482,7 @@ static void say_char(struct vc_data *vc)
u16 ch;
spk_old_attr = spk_attr;
- ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
+ ch = get_char(vc, (u16 *)spk_pos, &spk_attr);
if (spk_attr != spk_old_attr) {
if (spk_attrib_bleep & 1)
bleep(spk_y);
@@ -497,7 +497,7 @@ static void say_phonetic_char(struct vc_data *vc)
u16 ch;
spk_old_attr = spk_attr;
- ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
+ ch = get_char(vc, (u16 *)spk_pos, &spk_attr);
if (ch <= 0x7f && isalpha(ch)) {
ch &= 0x1f;
synth_printf("%s\n", phonetic[--ch]);
@@ -549,7 +549,7 @@ static u_long get_word(struct vc_data *vc)
u_char temp;
spk_old_attr = spk_attr;
- ch = get_char(vc, (u_short *)tmp_pos, &temp);
+ ch = get_char(vc, (u16 *)tmp_pos, &temp);
/* decided to take out the sayword if on a space (mis-information */
if (spk_say_word_ctl && ch == SPACE) {
@@ -558,26 +558,26 @@ static u_long get_word(struct vc_data *vc)
return 0;
} else if (tmpx < vc->vc_cols - 2 &&
(ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
- get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
+ get_char(vc, (u16 *)tmp_pos + 1, &temp) > SPACE) {
tmp_pos += 2;
tmpx++;
} else {
while (tmpx > 0) {
- ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
+ ch = get_char(vc, (u16 *)tmp_pos - 1, &temp);
if ((ch == SPACE || ch == 0 ||
(ch < 0x100 && IS_WDLM(ch))) &&
- get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
+ get_char(vc, (u16 *)tmp_pos, &temp) > SPACE)
break;
tmp_pos -= 2;
tmpx--;
}
}
- attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
+ attr_ch = get_char(vc, (u16 *)tmp_pos, &spk_attr);
buf[cnt++] = attr_ch;
while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) {
tmp_pos += 2;
tmpx++;
- ch = get_char(vc, (u_short *)tmp_pos, &temp);
+ ch = get_char(vc, (u16 *)tmp_pos, &temp);
if (ch == SPACE || ch == 0 ||
(buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
ch > SPACE))
@@ -591,7 +591,7 @@ static u_long get_word(struct vc_data *vc)
static void say_word(struct vc_data *vc)
{
u_long cnt = get_word(vc);
- u_short saved_punc_mask = spk_punc_mask;
+ u16 saved_punc_mask = spk_punc_mask;
if (cnt == 0)
return;
@@ -606,7 +606,7 @@ static void say_prev_word(struct vc_data *vc)
u_char temp;
u16 ch;
enum edge edge_said = edge_none;
- u_short last_state = 0, state = 0;
+ u16 last_state = 0, state = 0;
spk_parked |= 0x01;
@@ -635,7 +635,7 @@ static void say_prev_word(struct vc_data *vc)
spk_x--;
}
spk_pos -= 2;
- ch = get_char(vc, (u_short *)spk_pos, &temp);
+ ch = get_char(vc, (u16 *)spk_pos, &temp);
if (ch == SPACE || ch == 0)
state = 0;
else if (ch < 0x100 && IS_WDLM(ch))
@@ -661,7 +661,7 @@ static void say_next_word(struct vc_data *vc)
u_char temp;
u16 ch;
enum edge edge_said = edge_none;
- u_short last_state = 2, state = 0;
+ u16 last_state = 2, state = 0;
spk_parked |= 0x01;
if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
@@ -669,7 +669,7 @@ static void say_next_word(struct vc_data *vc)
return;
}
while (1) {
- ch = get_char(vc, (u_short *)spk_pos, &temp);
+ ch = get_char(vc, (u16 *)spk_pos, &temp);
if (ch == SPACE || ch == 0)
state = 0;
else if (ch < 0x100 && IS_WDLM(ch))
@@ -755,9 +755,9 @@ static int get_line(struct vc_data *vc)
u_char tmp2;
spk_old_attr = spk_attr;
- spk_attr = get_attributes(vc, (u_short *)spk_pos);
+ spk_attr = get_attributes(vc, (u16 *)spk_pos);
for (i = 0; i < vc->vc_cols; i++) {
- buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
+ buf[i] = get_char(vc, (u16 *)tmp, &tmp2);
tmp += 2;
}
for (--i; i >= 0; i--)
@@ -770,7 +770,7 @@ static void say_line(struct vc_data *vc)
{
int i = get_line(vc);
u16 *cp;
- u_short saved_punc_mask = spk_punc_mask;
+ u16 saved_punc_mask = spk_punc_mask;
if (i == 0) {
synth_printf("%s\n", spk_msg_get(MSG_BLANK));
@@ -817,12 +817,12 @@ static int say_from_to(struct vc_data *vc, u_long from, u_long to,
{
int i = 0;
u_char tmp;
- u_short saved_punc_mask = spk_punc_mask;
+ u16 saved_punc_mask = spk_punc_mask;
spk_old_attr = spk_attr;
- spk_attr = get_attributes(vc, (u_short *)from);
+ spk_attr = get_attributes(vc, (u16 *)from);
while (from < to) {
- buf[i++] = get_char(vc, (u_short *)from, &tmp);
+ buf[i++] = get_char(vc, (u16 *)from, &tmp);
from += 2;
if (i >= vc->vc_size_row)
break;
@@ -895,10 +895,10 @@ static int get_sentence_buf(struct vc_data *vc, int read_punc)
sentmarks[bn][0] = &sentbuf[bn][0];
i = 0;
spk_old_attr = spk_attr;
- spk_attr = get_attributes(vc, (u_short *)start);
+ spk_attr = get_attributes(vc, (u16 *)start);
while (start < end) {
- sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
+ sentbuf[bn][i] = get_char(vc, (u16 *)start, &tmp);
if (i > 0) {
if (sentbuf[bn][i] == SPACE &&
sentbuf[bn][i - 1] == '.' &&
@@ -1047,7 +1047,7 @@ static void say_position(struct vc_data *vc)
static void say_char_num(struct vc_data *vc)
{
u_char tmp;
- u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
+ u16 ch = get_char(vc, (u16 *)spk_pos, &tmp);
synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
}
@@ -1080,7 +1080,7 @@ static void spkup_write(const u16 *in_buf, int count)
{
static int rep_count;
static u16 ch = '\0', old_ch = '\0';
- static u_short char_type, last_type;
+ static u16 char_type, last_type;
int in_count = count;
spk_keydown = 0;
@@ -1325,9 +1325,9 @@ void spk_reset_default_chartab(void)
static const struct st_bits_data *pb_edit;
-static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
+static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u16 key)
{
- short mask = pb_edit->mask, ch_type = spk_chartab[ch];
+ u16 mask = pb_edit->mask, ch_type = spk_chartab[ch];
if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
return -1;
@@ -1947,7 +1947,7 @@ static void speakup_bits(struct vc_data *vc)
spk_special_handler = edit_bits;
}
-static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
+static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u16 key)
{
static u_char goto_buf[8];
static int num;
@@ -2105,7 +2105,7 @@ static void do_spkup(struct vc_data *vc, u_char value)
static const char *pad_chars = "0123456789+-*/\015,.?()";
static int
-speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
+speakup_key(struct vc_data *vc, int shift_state, int keycode, u16 keysym,
int up_flag)
{
unsigned long flags;
@@ -2444,6 +2444,7 @@ error_kbdnotifier:
mutex_lock(&spk_mutex);
synth_release();
mutex_unlock(&spk_mutex);
+ spk_ttyio_unregister_ldisc();
speakup_kobj_exit();
error_kobjects:
diff --git a/drivers/accessibility/speakup/selection.c b/drivers/accessibility/speakup/selection.c
index 7df7afad5ab4..1713ce4e0ba5 100644
--- a/drivers/accessibility/speakup/selection.c
+++ b/drivers/accessibility/speakup/selection.c
@@ -13,7 +13,7 @@
#include "speakup.h"
-unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
+u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
struct vc_data *spk_sel_cons;
struct speakup_selection_work {
diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h
index 54f1226ea061..984a729fd82d 100644
--- a/drivers/accessibility/speakup/speakup.h
+++ b/drivers/accessibility/speakup/speakup.h
@@ -62,7 +62,7 @@ int spk_set_num_var(int val, struct st_var_header *var, int how);
int spk_set_string_var(const char *page, struct st_var_header *var, int len);
int spk_set_mask_bits(const char *input, const int which, const int how);
extern special_func spk_special_handler;
-int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key);
+int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key);
int synth_init(char *name);
void synth_release(void);
@@ -82,7 +82,7 @@ void synth_writeu(const char *buf, size_t count);
int synth_supports_indexing(void);
extern struct vc_data *spk_sel_cons;
-extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
+extern u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
extern wait_queue_head_t speakup_event;
extern struct kobject *speakup_kobj;
@@ -95,20 +95,20 @@ extern struct st_spk_t *speakup_console[];
extern struct spk_synth *synth;
extern char spk_pitch_buff[];
extern u_char *spk_our_keys[];
-extern short spk_punc_masks[];
+extern u16 spk_punc_masks[];
extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[];
extern bool spk_paused;
extern const struct st_bits_data spk_punc_info[];
extern u_char spk_key_buf[600];
extern char *spk_characters[];
extern char *spk_default_chars[];
-extern u_short spk_chartab[];
+extern u16 spk_chartab[];
extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level;
extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps;
extern int spk_bleep_time, spk_bell_pos;
extern int spk_spell_delay, spk_key_echo;
extern int spk_cur_phonetic;
-extern short spk_punc_mask;
+extern u16 spk_punc_mask;
extern short spk_pitch_shift, synth_flags;
extern bool spk_quiet_boot;
extern char *synth_name;
diff --git a/drivers/accessibility/speakup/speakup_acnt.h b/drivers/accessibility/speakup/speakup_acnt.h
index cffa938ae580..cea05d770f6d 100644
--- a/drivers/accessibility/speakup/speakup_acnt.h
+++ b/drivers/accessibility/speakup/speakup_acnt.h
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* speakup_acntpc.h - header file for speakups Accent-PC driver. */
+#ifndef _SPEAKUP_ACNT_H
+#define _SPEAKUP_ACNT_H
#define SYNTH_IO_EXTENT 0x02
@@ -17,3 +19,4 @@
#define SYNTH_FULL 'F' /* synth is full. */
#define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */
#define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */
+#endif
diff --git a/drivers/accessibility/speakup/speakup_decpc.c b/drivers/accessibility/speakup/speakup_decpc.c
index 083ca9265805..1bf36d1e5477 100644
--- a/drivers/accessibility/speakup/speakup_decpc.c
+++ b/drivers/accessibility/speakup/speakup_decpc.c
@@ -41,7 +41,7 @@
#define STAT_new_index 0x0040 /* new last index ready */
#define STAT_new_status 0x0080 /* new status posted */
#define STAT_dma_state 0x0100 /* dma state toggle */
-#define STAT_index_valid 0x0200 /* indexs are valid */
+#define STAT_index_valid 0x0200 /* indexes are valid */
#define STAT_flushing 0x0400 /* flush in progress */
#define STAT_self_test 0x0800 /* module in self test */
#define MODE_ready 0xc000 /* module ready for next phase */
diff --git a/drivers/accessibility/speakup/speakup_dtlk.h b/drivers/accessibility/speakup/speakup_dtlk.h
index 9c378b58066e..9354e3382bba 100644
--- a/drivers/accessibility/speakup/speakup_dtlk.h
+++ b/drivers/accessibility/speakup/speakup_dtlk.h
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* speakup_dtlk.h - header file for speakups DoubleTalk driver. */
+#ifndef _SPEAKUP_DTLK_H
+#define _SPEAKUP_DTLK_H
#define SYNTH_IO_EXTENT 0x02
#define SYNTH_CLEAR 0x18 /* stops speech */
@@ -39,7 +41,7 @@
/* data returned by Interrogate command */
struct synth_settings {
- u_short serial_number; /* 0-7Fh:0-7Fh */
+ u16 serial_number; /* 0-7Fh:0-7Fh */
u_char rom_version[24]; /* null terminated string */
u_char mode; /* 0=Character; 1=Phoneme; 2=Text */
u_char punc_level; /* nB; 0-7 */
@@ -61,3 +63,4 @@ struct synth_settings {
*/
u_char has_indexing; /* nonzero if indexing is implemented */
};
+#endif
diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c
index 6d446824677b..57d83b82f1d1 100644
--- a/drivers/accessibility/speakup/speakup_soft.c
+++ b/drivers/accessibility/speakup/speakup_soft.c
@@ -446,8 +446,9 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var)
if (var->var_id != PUNC_LEVEL)
return 0;
- /* We want to set the the speech synthesis punctuation level
- * accordingly, so it properly tunes speaking A_PUNC characters */
+ /* We want to set the speech synthesis punctuation level
+ * accordingly, so it properly tunes speaking A_PUNC characters
+ */
var_data = var->data;
if (!var_data)
return 0;
diff --git a/drivers/accessibility/speakup/spk_types.h b/drivers/accessibility/speakup/spk_types.h
index 08011518a28a..a5762330e249 100644
--- a/drivers/accessibility/speakup/spk_types.h
+++ b/drivers/accessibility/speakup/spk_types.h
@@ -53,7 +53,7 @@ enum var_id_t {
};
typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch,
- u_short key);
+ u16 key);
#define COLOR_BUFFER_SIZE 160
diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c
index d8addbf3ad0d..11cba1fd8715 100644
--- a/drivers/accessibility/speakup/synth.c
+++ b/drivers/accessibility/speakup/synth.c
@@ -163,7 +163,7 @@ int spk_synth_is_alive_restart(struct spk_synth *synth)
/* restart */
synth->alive = 1;
synth_printf("%s", synth->init);
- return 2; /* reenabled */
+ return 2; /* re-enabled */
}
pr_warn("%s: can't restart synth\n", synth->long_name);
return 0;
@@ -574,4 +574,4 @@ struct spk_synth *synth_current(void)
}
EXPORT_SYMBOL_GPL(synth_current);
-short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
+u16 spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h
index 4ce9a12f7664..db00c962f8e2 100644
--- a/drivers/accessibility/speakup/utils.h
+++ b/drivers/accessibility/speakup/utils.h
@@ -36,7 +36,7 @@ static inline void open_input(const char *dir_name, const char *name)
else
snprintf(filename, sizeof(filename), "%s", name);
infile = fopen(filename, "r");
- if (infile == 0) {
+ if (!infile) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
index e2e402c9d175..606a9d07f774 100644
--- a/drivers/android/Kconfig
+++ b/drivers/android/Kconfig
@@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC
config ANDROID_BINDER_IPC_RUST
bool "Rust version of Android Binder IPC Driver"
- depends on RUST && MMU && !ANDROID_BINDER_IPC
+ depends on RUST && MMU && NET && !ANDROID_BINDER_IPC
help
This enables the Rust implementation of the Binder driver.
diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs
index ea5846e4da16..165cb797eb1e 100644
--- a/drivers/android/binder/allocation.rs
+++ b/drivers/android/binder/allocation.rs
@@ -384,8 +384,8 @@ impl<'a> AllocationView<'a> {
BINDER_TYPE_WEAK_BINDER
};
newobj.flags = obj.flags;
- newobj.__bindgen_anon_1.binder = ptr as _;
- newobj.cookie = cookie as _;
+ newobj.__bindgen_anon_1.binder = ptr as uapi::binder_uintptr_t;
+ newobj.cookie = cookie as uapi::binder_uintptr_t;
self.write(offset, &newobj)?;
// Increment the user ref count on the node. It will be decremented as part of the
// destruction of the buffer, when we see a binder or weak-binder object.
diff --git a/drivers/android/binder/debug.rs b/drivers/android/binder/debug.rs
new file mode 100644
index 000000000000..824b10c004c3
--- /dev/null
+++ b/drivers/android/binder/debug.rs
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2026 Google LLC.
+
+//! Binder debugging helpers.
+
+#![allow(dead_code)]
+
+use kernel::bits::bit_u32;
+use kernel::sync::atomic::Atomic;
+
+kernel::impl_flags!(
+ /// Represents multiple debug mask flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+ pub struct DebugMasks(u32);
+
+ /// Represents a single debug mask category.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum DebugMask {
+ UserError = bit_u32(0),
+ FailedTransaction = bit_u32(1),
+ DeadTransaction = bit_u32(2),
+ OpenClose = bit_u32(3),
+ DeadBinder = bit_u32(4),
+ DeathNotification = bit_u32(5),
+ ReadWrite = bit_u32(6),
+ UserRefs = bit_u32(7),
+ Threads = bit_u32(8),
+ Transaction = bit_u32(9),
+ TransactionComplete = bit_u32(10),
+ FreeBuffer = bit_u32(11),
+ InternalRefs = bit_u32(12),
+ PriorityCap = bit_u32(13),
+ Spinlocks = bit_u32(14),
+ }
+);
+
+#[no_mangle]
+pub(crate) static rust_binder_debug_mask: Atomic<u32> = Atomic::new(
+ (DebugMask::UserError as u32)
+ | (DebugMask::FailedTransaction as u32)
+ | (DebugMask::DeadTransaction as u32),
+);
+
+/// Checks if the given debug logging category is enabled in the mask.
+pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool {
+ let current_mask = rust_binder_debug_mask.load(kernel::sync::atomic::Relaxed);
+ DebugMasks(current_mask).contains(mask)
+}
+
+/// Prints a debug log if the specified mask category is enabled.
+#[macro_export]
+macro_rules! binder_debug {
+ // Rule to explicitly specify a PID (used in kworkers).
+ (pid=$pid:expr, $mask:ident, $($arg:tt)*) => {
+ if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) {
+ kernel::pr_info!(
+ "{}: {}\n",
+ $pid,
+ kernel::prelude::fmt!($($arg)*)
+ );
+ }
+ };
+
+ // Default rule (automatically prepends "PID:TID" of the current calling thread).
+ ($mask:ident, $($arg:tt)*) => {
+ if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) {
+ let thread = kernel::current!();
+ kernel::pr_info!(
+ "{}:{} {}\n",
+ thread.tgid(),
+ thread.pid(),
+ kernel::prelude::fmt!($($arg)*)
+ );
+ }
+ };
+}
diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs
index 33f51b4139c7..8ac9bdd7a499 100644
--- a/drivers/android/binder/defs.rs
+++ b/drivers/android/binder/defs.rs
@@ -4,6 +4,8 @@
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
+use core::ptr;
+
use kernel::{
transmute::{AsBytes, FromBytes},
uapi::{self, *},
@@ -146,7 +148,7 @@ decl_wrapper!(ExtendedError, uapi::binder_extended_error);
impl BinderVersion {
pub(crate) fn current() -> Self {
Self(MaybeUninit::new(uapi::binder_version {
- protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as _,
+ protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as i32,
}))
}
}
@@ -165,8 +167,8 @@ impl BinderTransactionDataSecctx {
pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData {
// SAFETY: Transparent wrapper is safe to transmute.
unsafe {
- &mut *(&mut self.transaction_data as *mut uapi::binder_transaction_data
- as *mut BinderTransactionData)
+ &mut *(ptr::from_mut::<uapi::binder_transaction_data>(&mut self.transaction_data)
+ .cast::<BinderTransactionData>())
}
}
}
diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs
index f4df14568b25..66912b4cb527 100644
--- a/drivers/android/binder/freeze.rs
+++ b/drivers/android/binder/freeze.rs
@@ -60,6 +60,7 @@ type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
/// Represents a notification that the freeze state has changed.
pub(crate) struct FreezeMessage {
cookie: FreezeCookie,
+ pid: i32,
}
kernel::list::impl_list_arc_safe! {
@@ -73,8 +74,8 @@ impl FreezeMessage {
UniqueArc::new_uninit(flags)
}
- fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> {
- match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) {
+ fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc<FreezeMessage> {
+ match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) {
Ok(msg) => ListArc::from(msg),
Err(err) => match err {},
}
@@ -127,7 +128,7 @@ impl DeliverToRead for FreezeMessage {
}
let mut state_info = BinderFrozenStateInfo::default();
- state_info.is_frozen = is_frozen as u32;
+ state_info.is_frozen = u32::from(is_frozen);
state_info.cookie = freeze.cookie.0;
freeze.is_pending = true;
freeze.last_is_frozen = Some(is_frozen);
@@ -140,7 +141,14 @@ impl DeliverToRead for FreezeMessage {
}
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ binder_debug!(
+ pid = self.pid,
+ DeadTransaction,
+ "undelivered freeze notification, {:016x}",
+ self.cookie.0
+ );
+ }
fn should_sync_wakeup(&self) -> bool {
false
@@ -180,36 +188,61 @@ impl Process {
let msg = FreezeMessage::new(GFP_KERNEL)?;
let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ let mut afl_vec_alloc = KVVec::new();
+ let mut info;
+ let mut freeze_entry;
let mut node_refs_guard = self.node_refs.lock();
- let node_refs = &mut *node_refs_guard;
- let Some(info) = node_refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
- return Err(EINVAL);
- };
- if info.freeze().is_some() {
- pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
- return Err(EINVAL);
- }
- let node_ref = info.node_ref();
- let freeze_entry = node_refs.freeze_listeners.entry(cookie);
-
- if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
- if !dupe.get().allow_duplicate(&node_ref.node) {
- pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
+ loop {
+ let node_refs = &mut *node_refs_guard;
+ info = match node_refs.by_handle.get_mut(&handle) {
+ Some(info) => info,
+ None => {
+ binder_debug!(
+ UserError,
+ "BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}"
+ );
+ return Err(EINVAL);
+ }
+ };
+ if info.freeze().is_some() {
+ binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set");
return Err(EINVAL);
}
- }
+ let node_ref = info.node_ref();
+ freeze_entry = node_refs.freeze_listeners.entry(cookie);
+
+ if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
+ if !dupe.get().allow_duplicate(&node_ref.node) {
+ binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie");
+ return Err(EINVAL);
+ }
+ }
- // All failure paths must come before this call, and all modifications must come after this
- // call.
- node_ref.node.add_freeze_listener(self, GFP_KERNEL)?;
+ // Now we add to the node's freeze listener list, with retry and re-allocate if the
+ // vector is full.
+ //
+ // To ensure that the node is added atomically, this is the first time we modify any
+ // state. When this call succeeds, all other modifications must occur without the
+ // possibility for any failure paths.
+ match node_ref
+ .node
+ .add_freeze_listener(self, &mut afl_vec_alloc)?
+ {
+ Ok(()) => break,
+ Err(resize_target) => {
+ drop(node_refs_guard);
+ afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?;
+ node_refs_guard = self.node_refs.lock();
+ }
+ }
+ }
match freeze_entry {
rbtree::Entry::Vacant(entry) => {
entry.insert(
FreezeListener {
cookie,
- node: node_ref.node.clone(),
+ node: info.node_ref().node.clone(),
last_is_frozen: None,
is_pending: false,
is_clearing: false,
@@ -233,7 +266,7 @@ impl Process {
}
*info.freeze() = Some(cookie);
- let msg = FreezeMessage::init(msg, cookie);
+ let msg = FreezeMessage::init(msg, cookie, self.task.pid());
drop(node_refs_guard);
let _ = self.push_work(msg);
Ok(())
@@ -245,18 +278,23 @@ impl Process {
let mut node_refs_guard = self.node_refs.lock();
let node_refs = &mut *node_refs_guard;
let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
- pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
+ binder_debug!(
+ UserError,
+ "BC_FREEZE_NOTIFICATION_DONE {:016x} not found",
+ cookie.0
+ );
return Err(EINVAL);
};
let mut clear_msg = None;
if freeze.num_pending_duplicates > 0 {
- clear_msg = Some(FreezeMessage::init(alloc, cookie));
+ clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
freeze.num_pending_duplicates -= 1;
freeze.num_cleared_duplicates += 1;
} else {
if !freeze.is_pending {
- pr_warn!(
- "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
+ binder_debug!(
+ UserError,
+ "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending",
cookie.0
);
return Err(EINVAL);
@@ -264,7 +302,7 @@ impl Process {
let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
// Immediately send another FreezeMessage.
- clear_msg = Some(FreezeMessage::init(alloc, cookie));
+ clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
}
freeze.is_pending = false;
}
@@ -280,31 +318,44 @@ impl Process {
let handle = hc.handle;
let cookie = FreezeCookie(hc.cookie);
+ let _to_free_fl;
let alloc = FreezeMessage::new(GFP_KERNEL)?;
let mut node_refs_guard = self.node_refs.lock();
let node_refs = &mut *node_refs_guard;
let Some(info) = node_refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}"
+ );
return Err(EINVAL);
};
let Some(info_cookie) = info.freeze() else {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active"
+ );
return Err(EINVAL);
};
if *info_cookie != cookie {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch"
+ );
return Err(EINVAL);
}
let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}"
+ );
return Err(EINVAL);
};
listener.is_clearing = true;
- listener.node.remove_freeze_listener(self);
+ _to_free_fl = listener.node.remove_freeze_listener(self);
*info.freeze() = None;
let mut msg = None;
if !listener.is_pending {
- msg = Some(FreezeMessage::init(alloc, cookie));
+ msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
}
drop(node_refs_guard);
@@ -384,7 +435,7 @@ impl Process {
continue;
};
let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
- let msg = FreezeMessage::init(msg_alloc, cookie);
+ let msg = FreezeMessage::init(msg_alloc, cookie, proc.task.pid());
batch.push((proc, msg), GFP_KERNEL)?;
}
diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/netlink.rs
new file mode 100644
index 000000000000..f34e1009432c
--- /dev/null
+++ b/drivers/android/binder/netlink.rs
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Based on: Documentation/netlink/specs/binder.yaml */
+
+#![allow(unreachable_pub, clippy::wrong_self_convention)]
+use kernel::{
+ net::netlink::{
+ Family,
+ GenlMsg,
+ MulticastGroup,
+ NetlinkSkBuff, //
+ },
+ prelude::*, //
+};
+
+pub static BINDER_NL_FAMILY: Family = Family::const_new(
+ kernel::module::this_module::<crate::LocalModule>(),
+ kernel::uapi::BINDER_FAMILY_NAME,
+ kernel::uapi::BINDER_FAMILY_VERSION,
+ &BINDER_NL_FAMILY_MCGRPS,
+);
+
+static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] = [MulticastGroup::const_new(c"report")];
+
+/// A multicast event sent to userspace subscribers to notify them about
+/// binder transaction failures. The generated report provides the full
+/// details of the specific transaction that failed. The intention is for
+/// programs to monitor these events and react to the failures as needed.
+pub struct Report {
+ skb: GenlMsg,
+}
+
+impl Report {
+ /// Create a new multicast message.
+ pub fn new(
+ size: usize,
+ portid: u32,
+ seq: u32,
+ flags: kernel::alloc::Flags,
+ ) -> Result<Self, kernel::alloc::AllocError> {
+ const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
+ let skb = NetlinkSkBuff::new(size, flags)?;
+ let skb = skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER_CMD_REPORT)?;
+ Ok(Self { skb })
+ }
+
+ /// Broadcast this message.
+ pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Result {
+ self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags)
+ }
+
+ /// Check if this message type has listeners.
+ pub fn has_listeners() -> bool {
+ BINDER_NL_FAMILY.has_listeners(0)
+ }
+
+ /// The enum binder_driver_return_protocol returned to the sender.
+ pub fn error(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_ERROR, val)
+ }
+
+ /// The binder context where the transaction occurred.
+ pub fn context(&mut self, val: &CStr) -> Result {
+ const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
+ self.skb.put_string(BINDER_A_REPORT_CONTEXT, val)
+ }
+
+ /// The PID of the sender process.
+ pub fn from_pid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val)
+ }
+
+ /// The TID of the sender thread.
+ pub fn from_tid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val)
+ }
+
+ /// The PID of the recipient process. This attribute may not be present
+ /// if the target could not be determined.
+ pub fn to_pid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_TO_PID, val)
+ }
+
+ /// The TID of the recipient thread. This attribute may not be present
+ /// if the target could not be determined.
+ pub fn to_tid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_TO_TID, val)
+ }
+
+ /// When present, indicates the failed transaction is a reply.
+ pub fn is_reply(&mut self) -> Result {
+ const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
+ self.skb.put_flag(BINDER_A_REPORT_IS_REPLY)
+ }
+
+ /// The bitmask of enum transaction_flags from the transaction.
+ pub fn flags(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_FLAGS, val)
+ }
+
+ /// The application-defined code from the transaction.
+ pub fn code(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_CODE, val)
+ }
+
+ /// The transaction payload size in bytes.
+ pub fn data_size(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val)
+ }
+}
diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
index c10148e9069f..0a82af14cda3 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -9,6 +9,7 @@ use kernel::{
seq_print,
sync::lock::{spinlock::SpinLockBackend, Guard},
sync::{Arc, LockedBy, SpinLock},
+ uapi,
};
use crate::{
@@ -21,6 +22,7 @@ use crate::{
};
use core::mem;
+use core::ptr;
mod wrapper;
pub(crate) use self::wrapper::CritIncrWrapper;
@@ -321,7 +323,7 @@ impl Node {
/// An id that is unique across all binder nodes on the system. Used as the key in the
/// `by_node` map.
pub(crate) fn global_id(&self) -> usize {
- self as *const Node as usize
+ ptr::from_ref(self).addr()
}
pub(crate) fn get_id(&self) -> (u64, u64) {
@@ -333,6 +335,10 @@ impl Node {
death: ListArc<DTRWrap<NodeDeath>, 1>,
guard: &mut Guard<'_, ProcessInner, SpinLockBackend>,
) {
+ assert!(
+ core::ptr::eq(self, &**death.node),
+ "attempt to add NodeDeath to the wrong death list"
+ );
self.inner.access_mut(guard).death_list.push_back(death);
}
@@ -343,7 +349,7 @@ impl Node {
) -> Option<DLArc<Node>> {
let inner = self.inner.access_mut(owner_inner);
if inner.active_inc_refs == 0 {
- pr_err!("inc_ref_done called when no active inc_refs");
+ binder_debug!(UserError, "inc_ref_done called when no active inc_refs");
return None;
}
@@ -464,7 +470,7 @@ impl Node {
owner_inner: &mut ProcessInner,
) -> Option<DLArc<dyn DeliverToRead>> {
match self.incr_refcount_allow_zero2one(strong, owner_inner) {
- Ok(Some(node)) => Some(node as _),
+ Ok(Some(node)) => Some(node as DLArc<dyn DeliverToRead>),
Ok(None) => None,
Err(CouldNotDeliverCriticalIncrement) => {
assert!(strong);
@@ -489,8 +495,8 @@ impl Node {
guard: &Guard<'_, ProcessInner, SpinLockBackend>,
) {
let inner = self.inner.access(guard);
- out.strong_count = inner.strong.count as _;
- out.weak_count = inner.weak.count as _;
+ out.strong_count = inner.strong.count as u32;
+ out.weak_count = inner.weak.count as u32;
}
pub(crate) fn populate_debug_info(
@@ -498,8 +504,8 @@ impl Node {
out: &mut BinderNodeDebugInfo,
guard: &Guard<'_, ProcessInner, SpinLockBackend>,
) {
- out.ptr = self.ptr as _;
- out.cookie = self.cookie as _;
+ out.ptr = self.ptr as uapi::binder_uintptr_t;
+ out.cookie = self.cookie as uapi::binder_uintptr_t;
let inner = self.inner.access(guard);
if inner.strong.has_count {
out.has_strong_ref = 1;
@@ -536,7 +542,7 @@ impl Node {
inner.oneway_todo.push_back(transaction);
} else {
inner.has_oneway_transaction = true;
- guard.push_work(transaction)?;
+ guard.push_work(&self.owner, transaction)?;
}
Ok(())
}
@@ -568,7 +574,7 @@ impl Node {
let transaction = inner.oneway_todo.pop_front();
inner.has_oneway_transaction = transaction.is_some();
if let Some(transaction) = transaction {
- match guard.push_work(transaction) {
+ match guard.push_work(&self.owner, transaction) {
Ok(()) => {}
Err((_err, work)) => {
// Process is dead.
@@ -657,29 +663,26 @@ impl Node {
pub(crate) fn add_freeze_listener(
&self,
process: &Arc<Process>,
- flags: kernel::alloc::Flags,
- ) -> Result {
- let mut vec_alloc = KVVec::<Arc<Process>>::new();
- loop {
- let mut guard = self.owner.inner.lock();
- // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the
- // listener, no the target.
- let inner = self.inner.access_mut(&mut guard);
- let len = inner.freeze_list.len();
- if len >= inner.freeze_list.capacity() {
- if len >= vec_alloc.capacity() {
- drop(guard);
- vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?;
- continue;
- }
- mem::swap(&mut inner.freeze_list, &mut vec_alloc);
- for elem in vec_alloc.drain_all() {
- inner.freeze_list.push_within_capacity(elem)?;
- }
+ // If the vector needs to be resized, it's done via this argument.
+ vec_alloc: &mut KVVec<Arc<Process>>,
+ ) -> Result<Result<(), usize>> {
+ let mut guard = self.owner.inner.lock();
+ // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the
+ // listener, not the target.
+ let inner = self.inner.access_mut(&mut guard);
+ let len = inner.freeze_list.len();
+ if len == inner.freeze_list.capacity() {
+ if len >= vec_alloc.capacity() {
+ // Request the caller to reallocate.
+ return Ok(Err((1 + len).next_power_of_two()));
+ }
+ mem::swap(&mut inner.freeze_list, vec_alloc);
+ for elem in vec_alloc.drain_all() {
+ inner.freeze_list.push_within_capacity(elem)?;
}
- inner.freeze_list.push_within_capacity(process.clone())?;
- return Ok(());
}
+ inner.freeze_list.push_within_capacity(process.clone())?;
+ Ok(Ok(()))
}
pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec<Arc<Process>> {
@@ -695,6 +698,8 @@ impl Node {
p.pid_in_current_ns()
);
}
+ // If the vector is empty it needs to be freed. However, we can't free it here because that
+ // might sleep, so return it to the caller.
if inner.freeze_list.is_empty() {
return mem::take(&mut inner.freeze_list);
}
@@ -820,6 +825,7 @@ impl NodeRef {
pub(crate) fn clone(&self, strong: bool) -> Result<NodeRef> {
if strong && self.strong_count == 0 {
+ binder_debug!(UserError, "tried to use weak ref as strong ref");
return Err(EINVAL);
}
Ok(self
@@ -860,9 +866,10 @@ impl NodeRef {
*count += 1;
} else {
if *count == 0 {
- pr_warn!(
- "pid {} performed invalid decrement on ref\n",
- kernel::current!().pid()
+ binder_debug!(
+ UserError,
+ "performed invalid {} decrement on ref",
+ if strong { "strong" } else { "weak" }
);
return false;
}
@@ -1104,6 +1111,11 @@ impl DeliverToRead for NodeDeath {
// We're still holding the inner lock, so it cannot be aborted while we insert it into
// the delivered list.
process_inner.death_delivered(self.clone());
+ binder_debug!(
+ DeathNotification,
+ "sending death notification, cookie {:016x}",
+ cookie
+ );
BR_DEAD_BINDER
};
@@ -1114,7 +1126,14 @@ impl DeliverToRead for NodeDeath {
Ok(cmd != BR_DEAD_BINDER)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ binder_debug!(
+ pid = self.process.task.pid(),
+ DeadTransaction,
+ "undelivered death notification, {:016x}",
+ self.cookie
+ );
+ }
fn should_sync_wakeup(&self) -> bool {
false
diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs
index 43294c050502..6e4ca01c941a 100644
--- a/drivers/android/binder/node/wrapper.rs
+++ b/drivers/android/binder/node/wrapper.rs
@@ -21,7 +21,7 @@ impl CritIncrWrapper {
pub(super) fn init(self, node: DArc<Node>) -> DLArc<dyn DeliverToRead> {
match self.inner.pin_init_with(DTRWrap::new(NodeWrapper { node })) {
- Ok(initialized) => ListArc::from(initialized) as _,
+ Ok(initialized) => ListArc::from(initialized) as DLArc<dyn DeliverToRead>,
Err(err) => match err {},
}
}
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index e82a5523804f..52ffbf3504e7 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -312,7 +312,7 @@ impl ShrinkablePageRange {
// SAFETY: This just initializes the pages array.
unsafe {
- let self_ptr = self as *const ShrinkablePageRange;
+ let self_ptr = ptr::from_ref(self);
for i in 0..num_pages {
let info = pages.as_mut_ptr().add(i);
(&raw mut (*info).range).write(self_ptr);
@@ -571,7 +571,7 @@ impl ShrinkablePageRange {
unsafe {
self.iterate(offset, size_of::<T>(), |page, offset, to_copy| {
// SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
- let obj_ptr = (out.as_mut_ptr() as *mut u8).add(out_offset);
+ let obj_ptr = out.as_mut_ptr().cast::<u8>().add(out_offset);
// SAFETY: The pointer points is in-bounds of the `out` variable, so it is valid.
page.read_raw(obj_ptr, offset, to_copy)?;
out_offset += to_copy;
@@ -593,7 +593,7 @@ impl ShrinkablePageRange {
unsafe {
self.iterate(offset, size_of_val(obj), |page, offset, to_copy| {
// SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
- let obj_ptr = (obj as *const T as *const u8).add(obj_offset);
+ let obj_ptr = ptr::from_ref(obj).cast::<u8>().add(obj_offset);
// SAFETY: We have a reference to the object, so the pointer is valid.
page.write_raw(obj_ptr, offset, to_copy)?;
obj_offset += to_copy;
@@ -712,7 +712,7 @@ unsafe extern "C" fn rust_shrink_free_page(
{
// CAST: The `list_head` field is first in `PageInfo`.
- let info = item as *mut PageInfo;
+ let info = item.cast::<PageInfo>();
// SAFETY: The `range` field of `PageInfo` is immutable.
range_ptr = unsafe { (*info).range };
// SAFETY: The `range` outlives its `PageInfo` values.
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index 5b8f73ec1931..5372bfbd93b3 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -16,6 +16,7 @@ use core::mem::take;
use kernel::{
bindings,
+ bits::bit_u8,
cred::Credential,
error::Error,
fs::file::{self, File},
@@ -30,9 +31,10 @@ use kernel::{
sync::{
aref::ARef,
lock::{spinlock::SpinLockBackend, Guard},
- Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc,
+ poll::PollCondVarBox,
+ Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SetOnce, SpinLock, UniqueArc,
},
- task::Task,
+ task::{Pid, Task},
uaccess::{UserSlice, UserSliceReader},
uapi,
workqueue::{self, Work},
@@ -70,9 +72,18 @@ impl Mapping {
}
}
-// bitflags for defer_work.
-const PROC_DEFER_FLUSH: u8 = 1;
-const PROC_DEFER_RELEASE: u8 = 2;
+kernel::impl_flags!(
+ /// Represents multiple deferred work flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+ pub struct DeferWorks(u8);
+
+ /// Represents a single deferred work category.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum DeferWork {
+ Flush = bit_u8(0),
+ Release = bit_u8(1),
+ }
+);
#[derive(Copy, Clone)]
pub(crate) enum IsFrozen {
@@ -121,7 +132,7 @@ pub(crate) struct ProcessInner {
started_thread_count: u32,
/// Bitmap of deferred work to do.
- defer_work: u8,
+ defer_work: DeferWorks,
/// Number of transactions to be transmitted before processes in freeze_wait
/// are woken up.
@@ -151,7 +162,7 @@ impl ProcessInner {
requested_thread_count: 0,
max_threads: 0,
started_thread_count: 0,
- defer_work: 0,
+ defer_work: DeferWorks::default(),
outstanding_txns: 0,
is_frozen: IsFrozen::No,
sync_recv: false,
@@ -172,21 +183,26 @@ impl ProcessInner {
/// taken while holding the inner process lock.
pub(crate) fn push_work(
&mut self,
+ proc: &Process,
work: DLArc<dyn DeliverToRead>,
) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> {
+ let sync = work.should_sync_wakeup();
+
// Try to find a ready thread to which to push the work.
if let Some(thread) = self.ready_threads.pop_front() {
// Push to thread while holding state lock. This prevents the thread from giving up
// (for example, because of a signal) when we're about to deliver work.
- match thread.push_work(work) {
+ match thread.push_work_inner(work, sync) {
PushWorkRes::Ok => Ok(()),
+ PushWorkRes::OkNotifyPoll => {
+ proc.notify_poll(sync);
+ Ok(())
+ }
PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)),
}
} else if self.is_dead {
Err((BinderError::new_dead(), work))
} else {
- let sync = work.should_sync_wakeup();
-
// Didn't find a thread waiting for proc work; this can happen
// in two scenarios:
// 1. All threads are busy handling transactions
@@ -194,17 +210,12 @@ impl ProcessInner {
// the kernel driver soon and pick up this work.
// 2. Threads are using the (e)poll interface, in which case
// they may be blocked on the waitqueue without having been
- // added to waiting_threads. For this case, we just iterate
- // over all threads not handling transaction work, and
- // wake them all up. We wake all because we don't know whether
- // a thread that called into (e)poll is handling non-binder
- // work currently.
+ // added to waiting_threads. For this case, we wake it up
+ // directly.
self.work.push_back(work);
// Wake up polling threads, if any.
- for thread in self.threads.values() {
- thread.notify_if_poll_ready(sync);
- }
+ proc.notify_poll(sync);
Ok(())
}
@@ -227,11 +238,11 @@ impl ProcessInner {
// If we decided that we need to push work, push either to the process or to a thread if
// one is specified.
- if let Some(node) = push {
+ if let Some(pnode) = push {
if let Some(thread) = othread {
- thread.push_work_deferred(node);
+ thread.push_work_deferred(pnode);
} else {
- let _ = self.push_work(node);
+ let _ = self.push_work(&node.owner, pnode);
// Nothing to do: `push_work` may fail if the process is dead, but that's ok as in
// that case, it doesn't care about the notification.
}
@@ -259,7 +270,7 @@ impl ProcessInner {
let push = match wrapper {
None => node
.incr_refcount_allow_zero2one(strong, self)?
- .map(|node| node as _),
+ .map(|node| node as DLArc<dyn DeliverToRead>),
Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self),
};
if let Some(node) = push {
@@ -455,7 +466,13 @@ pub(crate) struct Process {
// Node references are in a different lock to avoid recursive acquisition when
// incrementing/decrementing a node in another process.
#[pin]
- node_refs: Mutex<ProcessNodeRefs>,
+ node_refs: SpinLock<ProcessNodeRefs>,
+
+ // Synchronizes `register_wait` calls to the `PollCondVarBox`.
+ //
+ // The `PollCondVarBox` is not stored here because synchronization is
+ // done for `register_wait` only. Wakeups do not take this lock.
+ poll: SetOnce<PollCondVarBox>,
// Work node for deferred work item.
#[pin]
@@ -489,13 +506,13 @@ impl workqueue::WorkItem for Process {
{
let mut inner = me.inner.lock();
defer = inner.defer_work;
- inner.defer_work = 0;
+ inner.defer_work = DeferWorks::default();
}
- if defer & PROC_DEFER_FLUSH != 0 {
+ if defer.contains(DeferWork::Flush) {
me.deferred_flush();
}
- if defer & PROC_DEFER_RELEASE != 0 {
+ if defer.contains(DeferWork::Release) {
me.deferred_release();
}
}
@@ -510,12 +527,13 @@ impl Process {
cred,
inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"),
pages <- ShrinkablePageRange::new(&super::BINDER_SHRINKER),
- node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"),
+ node_refs <- kernel::new_spinlock!(ProcessNodeRefs::new(), "Process::node_refs"),
freeze_wait <- kernel::new_condvar!("Process::freeze_wait"),
task: current.group_leader().into(),
defer_work <- kernel::new_work!("Process::defer_work"),
links <- ListLinks::new(),
stats: BinderStats::new(),
+ poll: SetOnce::new(),
}),
GFP_KERNEL,
)?;
@@ -715,7 +733,7 @@ impl Process {
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
// If push_work fails, drop the work item outside the lock.
- let res = self.inner.lock().push_work(work);
+ let res = self.inner.lock().push_work(self, work);
match res {
Ok(()) => Ok(()),
Err((err, work)) => {
@@ -741,7 +759,7 @@ impl Process {
} else {
(0, 0, 0)
};
- let node_ref = self.get_node(ptr, cookie, flags as _, true, thread)?;
+ let node_ref = self.get_node(ptr, cookie, flags, true, thread)?;
let node = node_ref.node.clone();
self.ctx.set_manager_node(node_ref)?;
self.inner.lock().is_manager = true;
@@ -861,14 +879,17 @@ impl Process {
let handle = unused_id.as_u32();
// Do a lookup again as node may have been inserted before the lock was reacquired.
- if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) {
- let handle = *handle_ref;
- let info = refs.by_handle.get_mut(&handle).unwrap();
- info.node_ref().absorb(node_ref);
- return Ok(handle);
- }
+ let by_node_slot = match refs.by_node.entry(node_ref.node.global_id()) {
+ rbtree::Entry::Vacant(by_node_slot) => by_node_slot,
+ rbtree::Entry::Occupied(handle_ref) => {
+ // The node was inserted by another thread while we didn't hold the lock.
+ let handle = handle_ref.get();
+ let info = refs.by_handle.get_mut(handle).unwrap();
+ info.node_ref().absorb(node_ref);
+ return Ok(*handle);
+ }
+ };
- let gid = node_ref.node.global_id();
let (info_proc, info_node) = {
let info_init = NodeRefInfo::new(node_ref, handle, self.into());
match info.pin_init_with(info_init) {
@@ -884,6 +905,9 @@ impl Process {
// first thing in `deferred_release`, process cleanup will not miss the items inserted into
// `refs` below.
if self.inner.lock().is_dead {
+ // Explicitly drop the lock so that `info_proc` and `info_node` are dropped outside of
+ // the lock.
+ drop(refs_lock);
return Err(ESRCH);
}
@@ -891,7 +915,7 @@ impl Process {
// `info_node` into the right node's `refs` list.
unsafe { info_proc.node_ref2().node.insert_node_info(info_node) };
- refs.by_node.insert(reserve1.into_node(gid, handle));
+ by_node_slot.insert(handle, reserve1);
by_handle_slot.insert(info_proc, reserve2);
unused_id.acquire();
Ok(handle)
@@ -906,7 +930,13 @@ impl Process {
}
Ok(node_ref)
} else {
- Ok(self.get_node_from_handle(handle, true)?)
+ match self.get_node_from_handle(handle, true) {
+ Ok(node_ref) => Ok(node_ref),
+ Err(err) => {
+ binder_debug!(UserError, "got transaction to invalid handle {handle}");
+ Err(err.into())
+ }
+ }
}
}
@@ -946,15 +976,19 @@ impl Process {
// To preserve original binder behaviour, we only fail requests where the manager tries to
// increment references on itself.
+ let _to_free_by_handle;
+ let _to_free_by_node;
let _to_free_freeze_listener;
let _to_free_freeze_listener_cleanup;
let mut refs = self.node_refs.lock();
if let Some(info) = refs.by_handle.get_mut(&handle) {
if info.node_ref().update(inc, strong) {
// Clean up death if there is one attached to this node reference.
- if let Some(death) = info.death().take() {
+ //
+ // We remove the entire `info` below, so no need to remove `death` from `info`.
+ if let Some(death) = info.death().as_ref() {
death.set_cleared(true);
- self.remove_from_delivered_deaths(&death);
+ self.remove_from_delivered_deaths(death);
}
// Remove reference from process tables, and from the node's `refs` list.
@@ -971,8 +1005,8 @@ impl Process {
}
}
- refs.by_handle.remove(&handle);
- refs.by_node.remove(&id);
+ _to_free_by_handle = refs.by_handle.remove_node(&handle);
+ _to_free_by_node = refs.by_node.remove_node(&id);
refs.handle_is_present.release_id(handle as usize);
if let Some(shrink) = refs.handle_is_present.shrink_request() {
@@ -987,7 +1021,7 @@ impl Process {
} else {
// All refs are cleared in process exit, so this warning is expected in that case.
if !self.inner.lock().is_dead {
- pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns());
+ binder_debug!(UserError, "no such ref {handle}");
}
}
Ok(())
@@ -1008,7 +1042,7 @@ impl Process {
if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) {
if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) {
// This only fails if the process is dead.
- let _ = inner.push_work(node);
+ let _ = inner.push_work(self, node);
}
}
Ok(())
@@ -1237,16 +1271,26 @@ impl Process {
// Queue BR_ERROR if we can't allocate memory for the death notification.
let death = UniqueArc::new_uninit(GFP_KERNEL).inspect_err(|_| {
thread.push_return_work(BR_ERROR);
+ binder_debug!(
+ DeathNotification,
+ "BC_REQUEST_DEATH_NOTIFICATION failed due to memory allocation failure"
+ );
})?;
let mut refs = self.node_refs.lock();
let Some(info) = refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n");
+ binder_debug!(
+ UserError,
+ "BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}"
+ );
return Ok(());
};
// Nothing to do if there is already a death notification request for this handle.
if info.death().is_some() {
- pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n");
+ binder_debug!(
+ UserError,
+ "BC_REQUEST_DEATH_NOTIFICATION death notification already set"
+ );
return Ok(());
}
@@ -1274,6 +1318,11 @@ impl Process {
info.node_ref().node.add_death(death, &mut owner_inner);
}
}
+ binder_debug!(
+ DeathNotification,
+ "BC_REQUEST_DEATH_NOTIFICATION handle {handle} cookie {:016x}",
+ cookie
+ );
Ok(())
}
@@ -1283,28 +1332,45 @@ impl Process {
let mut refs = self.node_refs.lock();
let Some(info) = refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}"
+ );
return Ok(());
};
let Some(death) = info.death().take() else {
- pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_DEATH_NOTIFICATION death notification not active"
+ );
return Ok(());
};
if death.cookie != cookie {
*info.death() = Some(death);
- pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch"
+ );
return Ok(());
}
// Update state and determine if we need to queue a work item. We only need to do it when
// the node is not dead or if the user already completed the death notification.
- if death.set_cleared(false) {
+ let should_schedule = death.set_cleared(false);
+ drop(refs);
+
+ if should_schedule {
if let Some(death) = ListArc::try_from_arc_or_drop(death) {
let _ = thread.push_work_if_looper(death);
}
}
+ binder_debug!(
+ DeathNotification,
+ "BC_CLEAR_DEATH_NOTIFICATION handle {handle} cookie {:016x}",
+ cookie
+ );
Ok(())
}
@@ -1328,6 +1394,7 @@ impl Process {
}
fn deferred_flush(&self) {
+ binder_debug!(pid = self.task.pid(), OpenClose, "flushing process");
let inner = self.inner.lock();
for thread in inner.threads.values() {
thread.exit_looper();
@@ -1335,6 +1402,8 @@ impl Process {
}
fn deferred_release(self: Arc<Self>) {
+ binder_debug!(pid = self.task.pid(), OpenClose, "releasing process");
+
let is_manager = {
let mut inner = self.inner.lock();
inner.is_dead = true;
@@ -1382,13 +1451,11 @@ impl Process {
// SAFETY: We are removing the `NodeRefInfo` from the right node.
unsafe { info.node_ref2().node.remove_node_info(info) };
- // Remove all death notifications from the nodes (that belong to a different process).
- let death = if let Some(existing) = info.death().take() {
- existing
- } else {
- continue;
- };
- death.set_cleared(false);
+ // Clear death notifications from the nodes (that belong to a different process).
+ // No need to remove them from `info` as we clear info below.
+ if let Some(death) = info.death().as_ref() {
+ death.set_cleared(false);
+ }
}
// Clean up freeze listeners.
@@ -1524,6 +1591,15 @@ impl Process {
}
}
}
+
+ pub(crate) fn notify_poll(&self, sync: bool) {
+ if let Some(poll) = self.poll.as_ref() {
+ if sync {
+ poll.notify_sync();
+ }
+ poll.notify_all();
+ }
+ }
}
fn get_frozen_status(data: UserSlice) -> Result {
@@ -1536,13 +1612,13 @@ fn get_frozen_status(data: UserSlice) -> Result {
for ctx in crate::context::get_all_contexts()? {
ctx.for_each_proc(|proc| {
- if proc.task.pid() == info.pid as _ {
+ if proc.task.pid() == info.pid as Pid {
found = true;
let inner = proc.inner.lock();
let txns_pending = inner.txns_pending_locked();
- info.async_recv |= inner.async_recv as u32;
- info.sync_recv |= inner.sync_recv as u32;
- info.sync_recv |= (txns_pending as u32) << 1;
+ info.async_recv |= u32::from(inner.async_recv);
+ info.sync_recv |= u32::from(inner.sync_recv);
+ info.sync_recv |= u32::from(txns_pending) << 1;
}
});
}
@@ -1634,7 +1710,9 @@ impl Process {
/// The file operations supported by `Process`.
impl Process {
pub(crate) fn open(ctx: ArcBorrow<'_, Context>, file: &File) -> Result<Arc<Process>> {
- Self::new(ctx.into(), ARef::from(file.cred()))
+ let proc = Self::new(ctx.into(), ARef::from(file.cred()))?;
+ binder_debug!(OpenClose, "opened process");
+ Ok(proc)
}
pub(crate) fn release(this: Arc<Process>, _file: &File) {
@@ -1642,8 +1720,8 @@ impl Process {
let should_schedule;
{
let mut inner = this.inner.lock();
- should_schedule = inner.defer_work == 0;
- inner.defer_work |= PROC_DEFER_RELEASE;
+ should_schedule = inner.defer_work == DeferWorks::empty();
+ inner.defer_work |= DeferWork::Release;
binderfs_file = inner.binderfs_file.take();
}
@@ -1660,8 +1738,8 @@ impl Process {
let should_schedule;
{
let mut inner = this.inner.lock();
- should_schedule = inner.defer_work == 0;
- inner.defer_work |= PROC_DEFER_FLUSH;
+ should_schedule = inner.defer_work == DeferWorks::empty();
+ inner.defer_work |= DeferWork::Flush;
}
if should_schedule {
@@ -1719,7 +1797,21 @@ impl Process {
table: PollTable<'_>,
) -> Result<u32> {
let thread = this.get_current_thread()?;
- let (from_proc, mut mask) = thread.poll(file, table);
+ {
+ let poll = loop {
+ if let Some(poll) = this.poll.as_ref() {
+ break poll;
+ }
+
+ let poll = PollCondVarBox::new(c"Process::poll", kernel::static_lock_class!())?;
+ // Reuse our existing lock to synchronize callers initializing.
+ let _guard = this.node_refs.lock();
+ this.poll.populate(poll);
+ };
+
+ table.register_wait(file, poll);
+ }
+ let (from_proc, mut mask) = thread.poll()?;
if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
mask |= bindings::POLLIN;
}
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index d6ceebbd5f94..955c4c348f73 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -6,12 +6,6 @@
#![crate_name = "rust_binder"]
#![recursion_limit = "256"]
-#![allow(
- clippy::as_underscore,
- clippy::ref_as_ptr,
- clippy::ptr_as_ptr,
- clippy::cast_lossless
-)]
use kernel::{
bindings::{self, seq_file},
@@ -38,7 +32,10 @@ mod allocation;
mod context;
mod deferred_close;
mod defs;
+#[macro_use]
+mod debug;
mod error;
+mod netlink;
mod node;
mod page_range;
mod process;
@@ -226,6 +223,7 @@ impl<T: ListArcSafe> DTRWrap<T> {
struct DeliverCode {
code: u32,
skip: Atomic<bool>,
+ pid: i32,
}
kernel::list::impl_list_arc_safe! {
@@ -233,10 +231,11 @@ kernel::list::impl_list_arc_safe! {
}
impl DeliverCode {
- fn new(code: u32) -> Self {
+ fn new(code: u32, pid: i32) -> Self {
Self {
code,
skip: Atomic::new(false),
+ pid,
}
}
@@ -261,7 +260,15 @@ impl DeliverToRead for DeliverCode {
Ok(true)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ if !self.skip.load(Relaxed) {
+ binder_debug!(
+ pid = self.pid,
+ DeadTransaction,
+ "undelivered TRANSACTION_COMPLETE"
+ );
+ }
+ }
fn should_sync_wakeup(&self) -> bool {
false
@@ -289,19 +296,22 @@ fn ptr_align(value: usize) -> Option<usize> {
// SAFETY: We call register in `init`.
static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() };
-struct BinderModule {}
+struct BinderModule {
+ _netlink: kernel::net::netlink::Registration,
+}
impl kernel::Module for BinderModule {
fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
// SAFETY: The module initializer never runs twice, so we only call this once.
unsafe { crate::context::CONTEXTS.init() };
+ let netlink = crate::netlink::BINDER_NL_FAMILY.register()?;
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())? };
- Ok(Self {})
+ Ok(Self { _netlink: netlink })
}
}
@@ -315,9 +325,6 @@ unsafe impl<T> Sync for AssertSync<T> {}
#[no_mangle]
#[used]
pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
- // SAFETY: All zeroes is safe for the `file_operations` type.
- let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
-
let ops = kernel::bindings::file_operations {
owner: this_module::<LocalModule>().as_ptr(),
poll: Some(rust_binder_poll),
@@ -327,7 +334,7 @@ pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
open: Some(rust_binder_open),
release: Some(rust_binder_release),
flush: Some(rust_binder_flush),
- ..zeroed_ops
+ ..pin_init::zeroed()
};
AssertSync(ops)
};
@@ -418,7 +425,7 @@ unsafe extern "C" fn rust_binder_ioctl(
// SAFETY: We previously set `private_data` in `rust_binder_open`.
let f = unsafe { Arc::<Process>::borrow((*file).private_data) };
// SAFETY: The caller ensures that the file is valid.
- match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) {
+ match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd, arg) {
Ok(()) => 0,
Err(err) => err.to_errno() as isize,
}
@@ -512,7 +519,7 @@ unsafe extern "C" fn rust_binder_proc_show(
_: *mut kernel::ffi::c_void,
) -> kernel::ffi::c_int {
// SAFETY: Accessing the private field of `seq_file` is okay.
- let pid = (unsafe { (*ptr).private }) as usize as Pid;
+ let pid = unsafe { (*ptr).private }.addr() as Pid;
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
// this method is called.
let m = unsafe { SeqFile::from_raw(ptr) };
diff --git a/drivers/android/binder/rust_binderfs.c b/drivers/android/binder/rust_binderfs.c
index ade1c4d92499..300cc65562d1 100644
--- a/drivers/android/binder/rust_binderfs.c
+++ b/drivers/android/binder/rust_binderfs.c
@@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);
char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
module_param_named(rust_devices, rust_binder_devices_param, charp, 0444);
+extern u32 rust_binder_debug_mask;
+module_param_named(debug_mask, rust_binder_debug_mask, uint, 0644);
+
static dev_t binderfs_dev;
static DEFINE_MUTEX(binderfs_minors_mutex);
static DEFINE_IDA(binderfs_minors);
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index bc0ef8927905..18a14aa8a835 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -9,15 +9,15 @@
use kernel::{
bindings,
- fs::{File, LocalFile},
+ bits::bit_u32,
+ fs::LocalFile,
list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc},
prelude::*,
security,
seq_file::SeqFile,
seq_print,
sync::atomic::{ordering::Relaxed, Atomic},
- sync::poll::{PollCondVar, PollTable},
- sync::{aref::ARef, Arc, SpinLock},
+ sync::{aref::ARef, Arc, CondVar, SpinLock},
task::Task,
uaccess::{UserPtr, UserSlice, UserSliceReader},
uapi,
@@ -30,7 +30,7 @@ use crate::{
process::{GetWorkOrRegister, Process},
ptr_align,
stats::GLOBAL_STATS,
- transaction::{Transaction, TransactionInfo},
+ transaction::{Transaction, TransactionFlag, TransactionFlags, TransactionInfo},
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,
};
@@ -225,8 +225,10 @@ impl UnusedBufferSpace {
}
}
+#[must_use]
pub(crate) enum PushWorkRes {
Ok,
+ OkNotifyPoll,
FailedDead(DLArc<dyn DeliverToRead>),
}
@@ -234,6 +236,7 @@ impl PushWorkRes {
fn is_ok(&self) -> bool {
match self {
PushWorkRes::Ok => true,
+ PushWorkRes::OkNotifyPoll => true,
PushWorkRes::FailedDead(_) => false,
}
}
@@ -243,7 +246,7 @@ impl PushWorkRes {
struct InnerThread {
/// Determines the looper state of the thread. It is a bit-wise combination of the constants
/// prefixed with `LOOPER_`.
- looper_flags: u32,
+ looper_flags: LooperFlags,
/// Determines whether the looper should return.
looper_need_return: bool,
@@ -270,28 +273,38 @@ struct InnerThread {
extended_error: ExtendedError,
}
-const LOOPER_REGISTERED: u32 = 0x01;
-const LOOPER_ENTERED: u32 = 0x02;
-const LOOPER_EXITED: u32 = 0x04;
-const LOOPER_INVALID: u32 = 0x08;
-const LOOPER_WAITING: u32 = 0x10;
-const LOOPER_WAITING_PROC: u32 = 0x20;
-const LOOPER_POLL: u32 = 0x40;
+kernel::impl_flags!(
+ /// Represents multiple looper flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+ pub struct LooperFlags(u32);
+
+ /// Represents a single looper flag.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum LooperFlag {
+ Registered = bit_u32(0),
+ Entered = bit_u32(1),
+ Exited = bit_u32(2),
+ Invalid = bit_u32(3),
+ Waiting = bit_u32(4),
+ WaitingProc = bit_u32(5),
+ Poll = bit_u32(6),
+ }
+);
impl InnerThread {
- fn new() -> Result<Self> {
+ fn new(pid: i32) -> Result<Self> {
fn next_err_id() -> u32 {
static EE_ID: Atomic<u32> = Atomic::new(0);
EE_ID.fetch_add(1, Relaxed)
}
Ok(Self {
- looper_flags: 0,
+ looper_flags: LooperFlags::default(),
looper_need_return: false,
is_dead: false,
process_work_list: false,
- reply_work: ThreadError::try_new()?,
- return_work: ThreadError::try_new()?,
+ reply_work: ThreadError::try_new(pid)?,
+ return_work: ThreadError::try_new(pid)?,
work_list: List::new(),
current_transaction: None,
extended_error: ExtendedError::new(next_err_id(), BR_OK, 0),
@@ -310,27 +323,32 @@ impl InnerThread {
fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
if self.is_dead {
- PushWorkRes::FailedDead(work)
+ return PushWorkRes::FailedDead(work);
+ }
+ self.work_list.push_back(work);
+ self.process_work_list = true;
+ if self.looper_flags.contains(LooperFlag::Poll) {
+ PushWorkRes::OkNotifyPoll
} else {
- self.work_list.push_back(work);
- self.process_work_list = true;
PushWorkRes::Ok
}
}
- fn push_reply_work(&mut self, code: u32) {
+ fn push_reply_work(&mut self, code: u32) -> PushWorkRes {
if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) {
work.set_error_code(code);
- self.push_work(work);
+ self.push_work(work)
} else {
pr_warn!("Thread reply work is already in use.");
+ PushWorkRes::Ok
}
}
fn push_return_work(&mut self, reply: u32) {
if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) {
work.set_error_code(reply);
- self.push_work(work);
+ // Not notifying: Reply to current thread.
+ let _ = self.push_work(work);
} else {
pr_warn!("Thread return work is already in use.");
}
@@ -373,26 +391,27 @@ impl InnerThread {
}
fn looper_enter(&mut self) {
- self.looper_flags |= LOOPER_ENTERED;
- if self.looper_flags & LOOPER_REGISTERED != 0 {
- self.looper_flags |= LOOPER_INVALID;
+ self.looper_flags |= LooperFlag::Entered;
+ if self.looper_flags.contains(LooperFlag::Registered) {
+ self.looper_flags |= LooperFlag::Invalid;
}
}
fn looper_register(&mut self, valid: bool) {
- self.looper_flags |= LOOPER_REGISTERED;
- if !valid || self.looper_flags & LOOPER_ENTERED != 0 {
- self.looper_flags |= LOOPER_INVALID;
+ self.looper_flags |= LooperFlag::Registered;
+ if !valid || self.looper_flags.contains(LooperFlag::Entered) {
+ self.looper_flags |= LooperFlag::Invalid;
}
}
fn looper_exit(&mut self) {
- self.looper_flags |= LOOPER_EXITED;
+ self.looper_flags |= LooperFlag::Exited;
}
/// Determines whether the thread is part of a pool, i.e., if it is a looper.
fn is_looper(&self) -> bool {
- self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0
+ self.looper_flags
+ .contains_any(LooperFlag::Entered | LooperFlag::Registered)
}
/// Determines whether the thread should attempt to fetch work items from the process queue.
@@ -404,7 +423,7 @@ impl InnerThread {
}
fn poll(&mut self) -> u32 {
- self.looper_flags |= LOOPER_POLL;
+ self.looper_flags |= LooperFlag::Poll;
if self.process_work_list || self.looper_need_return {
bindings::POLLIN
} else {
@@ -422,7 +441,7 @@ pub(crate) struct Thread {
#[pin]
inner: SpinLock<InnerThread>,
#[pin]
- work_condvar: PollCondVar,
+ work_condvar: CondVar,
/// Used to insert this thread into the process' `ready_threads` list.
///
/// INVARIANT: May never be used for any other list than the `self.process.ready_threads`.
@@ -445,7 +464,7 @@ kernel::list::impl_list_item! {
impl Thread {
pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {
- let inner = InnerThread::new()?;
+ let inner = InnerThread::new(process.task.pid())?;
Arc::pin_init(
try_pin_init!(Thread {
@@ -453,7 +472,7 @@ impl Thread {
process,
task: ARef::from(&**kernel::current!()),
inner <- kernel::new_spinlock!(inner, "Thread::inner"),
- work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"),
+ work_condvar <- kernel::new_condvar!("Thread::work_condvar"),
links <- ListLinks::new(),
links_track <- AtomicTracker::new(),
}),
@@ -470,7 +489,7 @@ impl Thread {
m,
" thread {}: l {:02x} need_return {}\n",
self.id,
- inner.looper_flags,
+ u32::from(inner.looper_flags),
inner.looper_need_return,
);
}
@@ -543,9 +562,9 @@ impl Thread {
return Ok(Some(work));
}
- inner.looper_flags |= LOOPER_WAITING;
+ inner.looper_flags |= LooperFlag::Waiting;
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
- inner.looper_flags &= !LOOPER_WAITING;
+ inner.looper_flags &= !LooperFlag::Waiting;
if signal_pending {
return Err(EINTR);
@@ -597,9 +616,9 @@ impl Thread {
return Ok(Some(work));
}
- inner.looper_flags |= LOOPER_WAITING | LOOPER_WAITING_PROC;
+ inner.looper_flags |= LooperFlag::Waiting | LooperFlag::WaitingProc;
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
- inner.looper_flags &= !(LOOPER_WAITING | LOOPER_WAITING_PROC);
+ inner.looper_flags &= !(LooperFlag::Waiting | LooperFlag::WaitingProc);
if signal_pending || inner.looper_need_return {
// We need to return now. We need to pull the thread off the list of ready threads
@@ -624,7 +643,14 @@ impl Thread {
/// Returns whether the item was successfully pushed. This can only fail if the thread is dead.
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
let sync = work.should_sync_wakeup();
+ self.push_work_inner(work, sync)
+ }
+ pub(crate) fn push_work_inner(
+ &self,
+ work: DLArc<dyn DeliverToRead>,
+ sync: bool,
+ ) -> PushWorkRes {
let res = self.inner.lock().push_work(work);
if res.is_ok() {
@@ -643,7 +669,8 @@ impl Thread {
pub(crate) fn push_work_if_looper(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
let mut inner = self.inner.lock();
if inner.is_looper() && !inner.is_dead {
- inner.push_work(work);
+ // Not notifying: Reply to current thread.
+ let _ = inner.push_work(work);
Ok(())
} else {
drop(inner);
@@ -673,9 +700,9 @@ impl Thread {
let strong = obj.hdr.type_ == BINDER_TYPE_BINDER;
// SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid
// representation.
- let ptr = unsafe { obj.__bindgen_anon_1.binder } as _;
- let cookie = obj.cookie as _;
- let flags = obj.flags as _;
+ let ptr = unsafe { obj.__bindgen_anon_1.binder };
+ let cookie = obj.cookie;
+ let flags = obj.flags;
let node = self
.process
.as_arc_borrow()
@@ -686,7 +713,7 @@ impl Thread {
BinderObjectRef::Handle(obj) => {
let strong = obj.hdr.type_ == BINDER_TYPE_HANDLE;
// SAFETY: `handle` is a `u32`; any bit pattern is a valid representation.
- let handle = unsafe { obj.__bindgen_anon_1.handle } as _;
+ let handle = unsafe { obj.__bindgen_anon_1.handle };
let node = self.process.get_node_from_handle(handle, strong)?;
security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?;
view.transfer_binder_object(offset, obj, strong, node)?;
@@ -728,11 +755,12 @@ impl Thread {
let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) {
Ok(alloc_offset) => alloc_offset,
Err(err) => {
- pr_warn!(
- "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})",
+ binder_debug!(
+ UserError,
+ "failed to claim space for a BINDER_TYPE_PTR (offset: {}, limit: {}, size: {})",
sg_state.unused_buffer_space.offset,
sg_state.unused_buffer_space.limit,
- obj_length,
+ obj_length
);
return Err(err.into());
}
@@ -743,7 +771,7 @@ impl Thread {
ScatterGatherEntry {
obj_index,
offset: alloc_offset,
- sender_uaddr: obj.buffer as _,
+ sender_uaddr: obj.buffer as usize,
length: obj_length,
pointer_fixups: KVec::new(),
fixup_min_offset: 0,
@@ -811,6 +839,7 @@ impl Thread {
let fds_len = num_fds.checked_mul(size_of::<u32>()).ok_or(EINVAL)?;
if !is_aligned(parent_offset, size_of::<u32>()) {
+ binder_debug!(UserError, "FDA parent offset not aligned correctly");
return Err(EINVAL.into());
}
@@ -829,6 +858,7 @@ impl Thread {
};
if !is_aligned(parent_entry.sender_uaddr, size_of::<u32>()) {
+ binder_debug!(UserError, "FDA parent buffer not aligned correctly");
return Err(EINVAL.into());
}
@@ -850,7 +880,7 @@ impl Thread {
.ok_or(EINVAL)?;
let mut fda_bytes = KVec::new();
- UserSlice::new(UserPtr::from_addr(fda_uaddr as _), fds_len)
+ UserSlice::new(UserPtr::from_addr(fda_uaddr as usize), fds_len)
.read_all(&mut fda_bytes, GFP_KERNEL)?;
if fds_len != fda_bytes.len() {
@@ -912,12 +942,9 @@ impl Thread {
let target_offset_end = fixup_offset.checked_add(fixup_len).ok_or(EINVAL)?;
if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end {
- pr_warn!(
- "Fixups oob {} {} {} {}",
- fixup_offset,
- end_of_previous_fixup,
- offset_end,
- target_offset_end
+ binder_debug!(
+ UserError,
+ "fixups oob {fixup_offset} {end_of_previous_fixup} {offset_end} {target_offset_end}"
);
return Err(EINVAL.into());
}
@@ -925,18 +952,21 @@ impl Thread {
let copy_off = end_of_previous_fixup;
let copy_len = fixup_offset - end_of_previous_fixup;
if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
- pr_warn!("Failed copying into alloc: {:?}", err);
+ binder_debug!(UserError, "failed copying into alloc: {err:?}");
return Err(err.into());
}
if let PointerFixupEntry::Fixup { pointer_value, .. } = fixup {
let res = alloc.write::<u64>(fixup_offset, pointer_value);
if let Err(err) = res {
- pr_warn!("Failed copying ptr into alloc: {:?}", err);
+ binder_debug!(UserError, "failed copying ptr into alloc: {err:?}");
return Err(err.into());
}
}
if let Err(err) = reader.skip(fixup_len) {
- pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err);
+ binder_debug!(
+ UserError,
+ "failed skipping {fixup_len} from reader: {err:?}"
+ );
return Err(err.into());
}
end_of_previous_fixup = target_offset_end;
@@ -944,7 +974,7 @@ impl Thread {
let copy_off = end_of_previous_fixup;
let copy_len = offset_end - end_of_previous_fixup;
if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
- pr_warn!("Failed copying remainder into alloc: {:?}", err);
+ binder_debug!(UserError, "failed copying remainder into alloc: {err:?}");
return Err(err.into());
}
}
@@ -1048,7 +1078,7 @@ impl Thread {
let offset: usize = offset.try_into().map_err(|_| EINVAL)?;
if offset < end_of_previous_object || !is_aligned(offset, size_of::<u32>()) {
- pr_warn!("Got transaction with invalid offset.");
+ binder_debug!(UserError, "got transaction with invalid offset");
return Err(EINVAL.into());
}
@@ -1073,7 +1103,7 @@ impl Thread {
) {
Ok(()) => end_of_previous_object = offset + object.size(),
Err(err) => {
- pr_warn!("Error while translating object.");
+ binder_debug!(UserError, "error while translating object: {err:?}");
return Err(err);
}
}
@@ -1093,15 +1123,12 @@ impl Thread {
)?;
if let Some(sg_state) = sg_state.as_mut() {
- if let Err(err) = self.apply_sg(&mut alloc, sg_state) {
- pr_warn!("Failure in apply_sg: {:?}", err);
- return Err(err);
- }
+ self.apply_sg(&mut alloc, sg_state)?;
}
if let Some((off_out, secctx)) = secctx.as_mut() {
if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) {
- pr_warn!("Failed to write security context: {:?}", err);
+ binder_debug!(UserError, "failed to write security context: {err:?}");
return Err(err.into());
}
**off_out = secctx_off;
@@ -1115,6 +1142,12 @@ impl Thread {
let mut inner = thread.inner.lock();
inner.pop_transaction_to_reply(thread.as_ref())
} {
+ binder_debug!(
+ DeadTransaction,
+ "release transaction {} in, still active",
+ transaction.debug_id
+ );
+
let reply = Err(BR_DEAD_REPLY);
if !transaction
.from
@@ -1154,7 +1187,7 @@ impl Thread {
transaction.set_outstanding(&mut self.process.inner.lock());
}
- {
+ let ret = {
let mut inner = self.inner.lock();
if !inner.pop_transaction_replied(transaction) {
return false;
@@ -1171,15 +1204,16 @@ impl Thread {
}
match reply {
- Ok(work) => {
- inner.push_work(work);
- }
+ Ok(work) => inner.push_work(work),
Err(code) => inner.push_reply_work(code),
}
- }
+ };
// Notify the thread now that we've released the inner lock.
self.work_condvar.notify_sync();
+ if matches!(ret, PushWorkRes::OkNotifyPoll) {
+ self.process.notify_poll(true);
+ }
false
}
@@ -1232,7 +1266,7 @@ impl Thread {
info.from_pid = self.process.task.pid();
info.from_tid = self.id;
info.code = td.transaction_data.code;
- info.flags = td.transaction_data.flags;
+ info.flags = TransactionFlags::from_bits(td.transaction_data.flags);
info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize);
info.data_size = td.transaction_data.data_size as usize;
info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize);
@@ -1274,16 +1308,36 @@ impl Thread {
ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno());
}
- pr_warn!(
- "{}:{} transaction to {} failed: {err:?}",
- info.from_pid,
- info.from_tid,
- info.to_pid
+ binder_debug!(
+ FailedTransaction,
+ "transaction {} to {}:{} failed {:?}, code {} size {}-{}",
+ if info.is_reply {
+ "reply"
+ } else if info.is_oneway() {
+ "async"
+ } else {
+ "call"
+ },
+ info.to_pid,
+ info.to_tid,
+ err,
+ info.code,
+ info.data_size,
+ info.offsets_size
);
}
}
}
+ if info.oneway_spam_suspect {
+ // If this is both a oneway spam suspect and a failure, we report it twice. This is
+ // useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN.
+ info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx);
+ }
+ if info.reply != 0 {
+ info.report_netlink(info.reply, &self.process.ctx);
+ }
+
Ok(())
}
@@ -1294,7 +1348,10 @@ impl Thread {
// TODO: We need to ensure that there isn't a pending transaction in the work queue. How
// could this happen?
let top = self.top_of_transaction_stack()?;
- let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
+ let list_completion = DTRWrap::arc_try_new(DeliverCode::new(
+ BR_TRANSACTION_COMPLETE,
+ self.process.task.pid(),
+ ))?;
let completion = list_completion.clone_arc();
let transaction = Transaction::new(node_ref, top, self, info)?;
@@ -1303,7 +1360,7 @@ impl Thread {
{
let mut inner = self.inner.lock();
if !transaction.is_stacked_on(&inner.current_transaction) {
- pr_warn!("Transaction stack changed during transaction!");
+ binder_debug!(UserError, "got new transaction with bad transaction stack");
return Err(EINVAL.into());
}
inner.current_transaction = Some(transaction.clone_arc());
@@ -1326,8 +1383,18 @@ impl Thread {
}
fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
- let orig = self.inner.lock().pop_transaction_to_reply(self)?;
+ let orig = match self.inner.lock().pop_transaction_to_reply(self) {
+ Ok(orig) => orig,
+ Err(err) => {
+ binder_debug!(UserError, "got reply transaction with no transaction stack");
+ return Err(err.into());
+ }
+ };
if !orig.from.is_current_transaction(&orig) {
+ binder_debug!(
+ UserError,
+ "got reply transaction with bad transaction stack"
+ );
return Err(EINVAL.into());
}
@@ -1336,11 +1403,15 @@ impl Thread {
// We need to complete the transaction even if we cannot complete building the reply.
let out = (|| -> BinderResult<_> {
- let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
+ let completion = DTRWrap::arc_try_new(DeliverCode::new(
+ BR_TRANSACTION_COMPLETE,
+ self.process.task.pid(),
+ ))?;
let process = orig.from.process.clone();
- let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
+ let allow_fds = orig.flags.contains(TransactionFlag::AcceptFds);
let reply = Transaction::new_reply(self, process, info, allow_fds)?;
- self.inner.lock().push_work(completion);
+ // Not notifying: Reply to current thread.
+ let _ = self.inner.lock().push_work(completion);
orig.from.deliver_reply(Ok(reply), &orig, None);
Ok(())
})()
@@ -1354,11 +1425,11 @@ impl Thread {
info.from_tid,
info.to_pid
);
-
let param = err.source.as_ref().map_or(0, |e| e.to_errno());
let ee = ExtendedError::new(info.debug_id as u32, err.reply, param);
orig.from
.deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee));
+ info.reply = BR_FAILED_REPLY;
err.reply = BR_TRANSACTION_COMPLETE;
err
});
@@ -1376,9 +1447,11 @@ impl Thread {
} else {
BR_TRANSACTION_COMPLETE
};
- let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
+ let list_completion =
+ DTRWrap::arc_try_new(DeliverCode::new(code, self.process.task.pid()))?;
let completion = list_completion.clone_arc();
- self.inner.lock().push_work(list_completion);
+ // Not notifying: Reply to current thread.
+ let _ = self.inner.lock().push_work(list_completion);
match transaction.submit(info) {
Ok(()) => Ok(()),
Err(err) => {
@@ -1392,7 +1465,7 @@ impl Thread {
let write_start = req.write_buffer.wrapping_add(req.write_consumed);
let write_len = req.write_size.saturating_sub(req.write_consumed);
let mut reader =
- UserSlice::new(UserPtr::from_addr(write_start as _), write_len as _).reader();
+ UserSlice::new(UserPtr::from_addr(write_start as usize), write_len as usize).reader();
while reader.len() >= size_of::<u32>() && self.inner.lock().return_work.is_unused() {
let before = reader.len();
@@ -1463,7 +1536,7 @@ impl Thread {
let read_start = req.read_buffer.wrapping_add(req.read_consumed);
let read_len = req.read_size.saturating_sub(req.read_consumed);
let mut writer = BinderReturnWriter::new(
- UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(),
+ UserSlice::new(UserPtr::from_addr(read_start as usize), read_len as usize).writer(),
self,
);
let (in_pool, has_transaction, thread_todo, use_proc_queue) = {
@@ -1527,9 +1600,11 @@ impl Thread {
// Write BR_SPAWN_LOOPER if the process needs more threads for its pool.
if has_noop_placeholder && in_pool && self.process.needs_thread() {
- let mut writer =
- UserSlice::new(UserPtr::from_addr(req.read_buffer as _), req.read_size as _)
- .writer();
+ let mut writer = UserSlice::new(
+ UserPtr::from_addr(req.read_buffer as usize),
+ req.read_size as usize,
+ )
+ .writer();
writer.write(&BR_SPAWN_LOOPER)?;
}
Ok(())
@@ -1578,16 +1653,15 @@ impl Thread {
ret
}
- pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) {
- table.register_wait(file, &self.work_condvar);
+ pub(crate) fn poll(&self) -> Result<(bool, u32)> {
let mut inner = self.inner.lock();
- (inner.should_use_process_work_queue(), inner.poll())
+ Ok((inner.should_use_process_work_queue(), inner.poll()))
}
/// Make the call to `get_work` or `get_work_local` return immediately, if any.
pub(crate) fn exit_looper(&self) {
let mut inner = self.inner.lock();
- let should_notify = inner.looper_flags & LOOPER_WAITING != 0;
+ let should_notify = inner.looper_flags.contains(LooperFlag::Waiting);
if should_notify {
inner.looper_need_return = true;
}
@@ -1598,26 +1672,9 @@ impl Thread {
}
}
- pub(crate) fn notify_if_poll_ready(&self, sync: bool) {
- // Determine if we need to notify. This requires the lock.
- let inner = self.inner.lock();
- let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue();
- drop(inner);
-
- // Now that the lock is no longer held, notify the waiters if we have to.
- if notify {
- if sync {
- self.work_condvar.notify_sync();
- } else {
- self.work_condvar.notify_one();
- }
- }
- }
-
pub(crate) fn release(self: &Arc<Self>) {
self.inner.lock().is_dead = true;
- //self.work_condvar.clear();
self.unwind_transaction_stack();
// Cancel all pending work items.
@@ -1630,14 +1687,16 @@ impl Thread {
#[pin_data]
struct ThreadError {
error_code: Atomic<u32>,
+ pid: i32,
#[pin]
links_track: AtomicTracker,
}
impl ThreadError {
- fn try_new() -> Result<DArc<Self>> {
+ fn try_new(pid: i32) -> Result<DArc<Self>> {
DTRWrap::arc_pin_init(pin_init!(Self {
error_code: Atomic::new(BR_OK),
+ pid,
links_track <- AtomicTracker::new(),
}))
.map(ListArc::into_arc)
@@ -1664,7 +1723,16 @@ impl DeliverToRead for ThreadError {
Ok(true)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ let code = self.error_code.load(Relaxed);
+ if code != BR_OK {
+ binder_debug!(
+ pid = self.pid,
+ DeadTransaction,
+ "undelivered TRANSACTION_ERROR: {code}"
+ );
+ }
+ }
fn should_sync_wakeup(&self) -> bool {
false
diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs
index 5539672d7285..06aabb3cc2f1 100644
--- a/drivers/android/binder/trace.rs
+++ b/drivers/android/binder/trace.rs
@@ -4,6 +4,8 @@
use crate::transaction::Transaction;
+use core::ptr;
+
use kernel::bindings::{rust_binder_transaction, task_struct};
use kernel::error::Result;
use kernel::ffi::{c_int, c_uint, c_ulong};
@@ -26,7 +28,7 @@ declare_trace! {
#[inline]
fn raw_transaction(t: &Transaction) -> rust_binder_transaction {
- t as *const Transaction as rust_binder_transaction
+ ptr::from_ref(t).cast_mut().cast()
}
#[inline]
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 0e5d07b7e6f0..245f1556b5db 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -3,6 +3,7 @@
// Copyright (C) 2025 Google LLC.
use kernel::{
+ net::netlink::GENLMSG_DEFAULT_SIZE,
prelude::*,
seq_file::SeqFile,
seq_print,
@@ -11,12 +12,14 @@ use kernel::{
task::{Kuid, Pid},
time::{Instant, Monotonic},
types::ScopeGuard,
+ uapi,
};
use crate::{
allocation::{Allocation, TranslatedFds},
defs::*,
error::{BinderError, BinderResult},
+ netlink::Report,
node::{Node, NodeRef},
process::{Process, ProcessInner},
ptr_align,
@@ -24,6 +27,33 @@ use crate::{
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
};
+kernel::impl_flags!(
+ /// Represents multiple transaction flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Zeroable)]
+ pub struct TransactionFlags(u32);
+
+ /// Represents a single transaction flag.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum TransactionFlag {
+ OneWay = TF_ONE_WAY,
+ AcceptFds = TF_ACCEPT_FDS,
+ ClearBuf = TF_CLEAR_BUF,
+ UpdateTxn = TF_UPDATE_TXN,
+ }
+);
+
+impl TransactionFlags {
+ /// Creates a `TransactionFlags` from a raw `u32` value.
+ pub(crate) fn from_bits(bits: u32) -> Self {
+ Self(bits)
+ }
+
+ /// Checks if the Oneway flag is set.
+ pub(crate) fn is_oneway(self) -> bool {
+ self.contains(TransactionFlag::OneWay)
+ }
+}
+
#[derive(Zeroable)]
pub(crate) struct TransactionInfo {
pub(crate) from_pid: Pid,
@@ -31,7 +61,7 @@ pub(crate) struct TransactionInfo {
pub(crate) to_pid: Pid,
pub(crate) to_tid: Pid,
pub(crate) code: u32,
- pub(crate) flags: u32,
+ pub(crate) flags: TransactionFlags,
pub(crate) data_ptr: UserPtr,
pub(crate) data_size: usize,
pub(crate) offsets_ptr: UserPtr,
@@ -48,7 +78,45 @@ pub(crate) struct TransactionInfo {
impl TransactionInfo {
#[inline]
pub(crate) fn is_oneway(&self) -> bool {
- self.flags & TF_ONE_WAY != 0
+ self.flags.is_oneway()
+ }
+
+ pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) {
+ if let Err(err) = self.report_netlink_inner(reply, ctx) {
+ pr_warn!(
+ "{}:{} netlink report failed: {err:?}\n",
+ self.from_pid,
+ self.from_tid
+ );
+ }
+ }
+
+ fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result {
+ if !Report::has_listeners() {
+ return Ok(());
+ }
+ let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?;
+
+ report.error(reply)?;
+ report.context(&ctx.name)?;
+ report.from_pid(self.from_pid as u32)?;
+ report.from_tid(self.from_tid as u32)?;
+ if self.to_pid != 0 {
+ report.to_pid(self.to_pid as u32)?;
+ }
+ if self.to_tid != 0 {
+ report.to_tid(self.to_tid as u32)?;
+ }
+
+ if self.is_reply {
+ report.is_reply()?;
+ }
+ report.flags(u32::from(self.flags))?;
+ report.code(self.code)?;
+ report.data_size(self.data_size as u32)?;
+
+ report.multicast(0, GFP_KERNEL)?;
+ Ok(())
}
}
@@ -74,7 +142,7 @@ pub(crate) struct Transaction {
allocation: SpinLock<Option<Allocation>>,
is_outstanding: Atomic<bool>,
code: u32,
- pub(crate) flags: u32,
+ pub(crate) flags: TransactionFlags,
data_size: usize,
offsets_size: usize,
data_address: usize,
@@ -120,7 +188,7 @@ impl Transaction {
}
alloc.set_info_oneway_node(node_ref.node.clone());
}
- if info.flags & TF_CLEAR_BUF != 0 {
+ if info.flags.contains(TransactionFlag::ClearBuf) {
alloc.set_info_clear_on_drop();
}
let target_node = node_ref.node.clone();
@@ -160,7 +228,7 @@ impl Transaction {
return Err(err);
}
};
- if info.flags & TF_CLEAR_BUF != 0 {
+ if info.flags.contains(TransactionFlag::ClearBuf) {
alloc.set_info_clear_on_drop();
}
Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
@@ -193,7 +261,7 @@ impl Transaction {
self.from.id,
self.to.task.pid(),
self.code,
- self.flags,
+ u32::from(self.flags),
self.start_time.elapsed().as_millis(),
);
if let Some(target_node) = &self.target_node {
@@ -272,7 +340,7 @@ impl Transaction {
let _t_outdated;
let _oneway_node;
- let oneway = self.flags & TF_ONE_WAY != 0;
+ let oneway = self.flags.is_oneway();
let process = self.to.clone();
let mut process_inner = process.inner.lock();
@@ -283,7 +351,7 @@ impl Transaction {
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 {
+ if self.flags.contains(TransactionFlag::UpdateTxn) {
if let Some(t_outdated) =
target_node.take_outdated_transaction(&self, &mut process_inner)
{
@@ -330,11 +398,15 @@ impl Transaction {
crate::trace::trace_transaction(false, &self, Some(&thread.task));
match thread.push_work(self) {
PushWorkRes::Ok => Ok(()),
+ PushWorkRes::OkNotifyPoll => {
+ process.notify_poll(true);
+ Ok(())
+ }
PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
}
} else {
crate::trace::trace_transaction(false, &self, None);
- process_inner.push_work(self)
+ process_inner.push_work(&process, self)
};
drop(process_inner);
@@ -354,7 +426,8 @@ impl Transaction {
return false;
}
- if self.flags & old.flags & (TF_ONE_WAY | TF_UPDATE_TXN) != (TF_ONE_WAY | TF_UPDATE_TXN) {
+ let required = TransactionFlag::OneWay | TransactionFlag::UpdateTxn;
+ if !(self.flags.contains_all(required) && old.flags.contains_all(required)) {
return false;
}
@@ -391,7 +464,7 @@ impl DeliverToRead for Transaction {
writer: &mut BinderReturnWriter<'_>,
) -> Result<bool> {
let send_failed_reply = ScopeGuard::new(|| {
- if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ if self.target_node.is_some() && !self.flags.is_oneway() {
let reply = Err(BR_FAILED_REPLY);
self.from.deliver_reply(reply, &self, None);
}
@@ -403,6 +476,14 @@ impl DeliverToRead for Transaction {
} else {
// On failure to process the list, we send a reply back to the sender and ignore the
// transaction on the recipient.
+ binder_debug!(
+ FailedTransaction,
+ "transaction {} to {} failed, fd fixups failed, size {}-{}",
+ self.debug_id,
+ self.to.task.pid(),
+ self.data_size,
+ self.offsets_size
+ );
return Ok(true);
};
@@ -410,20 +491,21 @@ impl DeliverToRead for Transaction {
let tr = tr_sec.tr_data();
if let Some(target_node) = &self.target_node {
let (ptr, cookie) = target_node.get_id();
- tr.target.ptr = ptr as _;
- tr.cookie = cookie as _;
+ tr.target.ptr = ptr as uapi::binder_uintptr_t;
+ tr.cookie = cookie as uapi::binder_uintptr_t;
};
tr.code = self.code;
- tr.flags = self.flags;
- tr.data_size = self.data_size as _;
- tr.data.ptr.buffer = self.data_address as _;
- tr.offsets_size = self.offsets_size as _;
+ tr.flags = u32::from(self.flags);
+ tr.data_size = self.data_size as uapi::binder_size_t;
+ tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t;
+ tr.offsets_size = self.offsets_size as uapi::binder_size_t;
if tr.offsets_size > 0 {
- tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size).unwrap()) as _;
+ tr.data.ptr.offsets =
+ (self.data_address + ptr_align(self.data_size).unwrap()) as uapi::binder_uintptr_t;
}
tr.sender_euid = self.sender_euid.into_uid_in_current_ns();
tr.sender_pid = 0;
- if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ if self.target_node.is_some() && !self.flags.is_oneway() {
// Not a reply and not one-way.
tr.sender_pid = self.from.process.pid_in_current_ns();
}
@@ -475,16 +557,23 @@ impl DeliverToRead for Transaction {
drop(allocation);
// If this is not a reply or oneway transaction, then send a dead reply.
- if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ if self.target_node.is_some() && !self.flags.is_oneway() {
let reply = Err(BR_DEAD_REPLY);
self.from.deliver_reply(reply, &self, None);
+ } else {
+ binder_debug!(
+ pid = self.to.task.pid(),
+ DeadTransaction,
+ "undelivered transaction {}, process died",
+ self.debug_id
+ );
}
self.drop_outstanding_txn();
}
fn should_sync_wakeup(&self) -> bool {
- self.flags & TF_ONE_WAY == 0
+ !self.flags.is_oneway()
}
fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> {
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index 70701d3bc81c..9f9c72727a05 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -401,9 +401,14 @@ static int cache_setup_properties(unsigned int cpu)
else if (!acpi_disabled)
ret = cache_setup_acpi(cpu);
- // Assume there is no cache information available in DT/ACPI from now.
- if (ret && use_arch_cache_info())
+ /*
+ * No DT/ACPI cache nodes; fall back to arch-derived topology (e.g.
+ * arm64 CLIDR_EL1) and clear the error to avoid a spurious warning.
+ */
+ if (ret && use_arch_cache_info()) {
use_arch_info = true;
+ ret = 0;
+ }
return ret;
}
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index b1213786f72c..db7ee6547eef 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -1026,26 +1026,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
struct mhi_ep_chan *mhi_chan;
int i;
- /* Stop all the channels */
+ /* Disable all the channels to prevent new transfers */
+ for (i = 0; i < mhi_cntrl->max_chan; i++) {
+ mhi_chan = &mhi_cntrl->mhi_chan[i];
+ if (!mhi_chan->ring.started)
+ continue;
+
+ mutex_lock(&mhi_chan->lock);
+ mhi_chan->state = MHI_CH_STATE_DISABLED;
+ mutex_unlock(&mhi_chan->lock);
+ }
+
+ /* Drain ring workers and in-flight transfers before notifying disconnect */
+ flush_workqueue(mhi_cntrl->wq);
+ if (mhi_cntrl->flush_async)
+ mhi_cntrl->flush_async(mhi_cntrl);
+
+ /* Send channel disconnect status to client drivers */
for (i = 0; i < mhi_cntrl->max_chan; i++) {
mhi_chan = &mhi_cntrl->mhi_chan[i];
if (!mhi_chan->ring.started)
continue;
mutex_lock(&mhi_chan->lock);
- /* Send channel disconnect status to client drivers */
if (mhi_chan->xfer_cb) {
result.transaction_status = -ENOTCONN;
result.bytes_xferd = 0;
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
}
-
- mhi_chan->state = MHI_CH_STATE_DISABLED;
mutex_unlock(&mhi_chan->lock);
}
- flush_workqueue(mhi_cntrl->wq);
-
/* Destroy devices associated with all channels */
device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
@@ -1340,14 +1351,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
ret = dev_set_name(&mhi_dev->dev, "%s_%s",
dev_name(&mhi_cntrl->mhi_dev->dev),
mhi_dev->name);
- if (ret) {
- put_device(&mhi_dev->dev);
- return ret;
- }
+ if (ret)
+ goto err_put_channels;
ret = device_add(&mhi_dev->dev);
if (ret)
- put_device(&mhi_dev->dev);
+ goto err_put_channels;
+
+ return 0;
+
+err_put_channels:
+ put_device(&mhi_dev->dev); /* DL channel reference */
+ put_device(&mhi_dev->dev); /* UL channel reference */
+ put_device(&mhi_dev->dev); /* device_initialize() reference */
return ret;
}
@@ -1614,6 +1630,7 @@ 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);
+ struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
struct mhi_result result = {};
struct mhi_ep_chan *mhi_chan;
int dir;
@@ -1622,6 +1639,22 @@ static void mhi_ep_remove(struct device *dev)
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
return;
+ /* Disable the channels to prevent new transfers */
+ for (dir = 0; dir < 2; dir++) {
+ mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
+
+ if (!mhi_chan)
+ continue;
+
+ mutex_lock(&mhi_chan->lock);
+ mhi_chan->state = MHI_CH_STATE_DISABLED;
+ mutex_unlock(&mhi_chan->lock);
+ }
+
+ /* Flush in-flight transfers before notifying disconnect */
+ if (mhi_cntrl->flush_async)
+ mhi_cntrl->flush_async(mhi_cntrl);
+
/* Disconnect the channels associated with the driver */
for (dir = 0; dir < 2; dir++) {
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
@@ -1637,7 +1670,6 @@ static void mhi_ep_remove(struct device *dev)
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
}
- mhi_chan->state = MHI_CH_STATE_DISABLED;
mhi_chan->xfer_cb = NULL;
mutex_unlock(&mhi_chan->lock);
}
diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c
index 12dcb1a2753c..fd3050889412 100644
--- a/drivers/bus/mhi/host/init.c
+++ b/drivers/bus/mhi/host/init.c
@@ -1029,7 +1029,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
if (mhi_cntrl->edl_trigger) {
ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
if (ret)
- goto err_release_dev;
+ goto err_del_dev;
}
mhi_cntrl->mhi_dev = mhi_dev;
@@ -1038,6 +1038,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
return 0;
+err_del_dev:
+ device_del(&mhi_dev->dev);
err_release_dev:
put_device(&mhi_dev->dev);
error_setup_irq:
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index 53c0ffe30070..4d458396233a 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -170,6 +170,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state);
void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
{
+ int __maybe_unused ret;
+ u32 tmp;
+
if (mhi_cntrl->reset) {
mhi_cntrl->reset(mhi_cntrl);
return;
@@ -178,6 +181,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
/* Generic MHI SoC reset */
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
MHI_SOC_RESET_REQ);
+ /* Flush the posted write to the device (ignore return value) */
+ ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
+ &tmp);
}
EXPORT_SYMBOL_GPL(mhi_soc_reset);
diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index 0d0d9c7ffa4b..b636e2c23b4d 100644
--- a/drivers/bus/mhi/host/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -491,6 +491,8 @@ static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = {
static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
+ MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
+ MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
@@ -506,6 +508,8 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
+ MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
+ MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
@@ -1395,6 +1399,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width);
mhi_cntrl->fw_image = info->fw;
mhi_cntrl->edl_image = info->edl;
+ mhi_cntrl->no_m3 = info->no_m3;
mhi_cntrl->read_reg = mhi_pci_read_reg;
mhi_cntrl->write_reg = mhi_pci_write_reg;
diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c
index f799503c8f36..ced83fbb1a51 100644
--- a/drivers/bus/mhi/host/pm.c
+++ b/drivers/bus/mhi/host/pm.c
@@ -651,21 +651,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
/* Trigger MHI RESET so that the device will not access host memory */
if (reset_device) {
- u32 in_reset = -1;
- unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms);
-
dev_dbg(dev, "Triggering MHI Reset in device\n");
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
/* Wait for the reset bit to be cleared by the device */
- ret = wait_event_timeout(mhi_cntrl->state_event,
- mhi_read_reg_field(mhi_cntrl,
- mhi_cntrl->regs,
- MHICTRL,
- MHICTRL_RESET_MASK,
- &in_reset) ||
- !in_reset, timeout);
- if (!ret || in_reset) {
+ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
+ MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms);
+ if (ret) {
dev_err(dev, "Device failed to exit MHI Reset state\n");
write_lock_irq(&mhi_cntrl->pm_lock);
cur_state = mhi_tryset_pm_state(mhi_cntrl,
@@ -922,22 +914,39 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
return -EIO;
}
- /* Set MHI to M3 and wait for completion */
- mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
- write_unlock_irq(&mhi_cntrl->pm_lock);
- dev_dbg(dev, "Waiting for M3 completion\n");
-
- ret = wait_event_timeout(mhi_cntrl->state_event,
- mhi_cntrl->dev_state == MHI_STATE_M3 ||
- MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
- msecs_to_jiffies(mhi_cntrl->timeout_ms));
+ /*
+ * For devices without M3 support, just set the host state to M3. This
+ * host transition is needed to prevent the client drivers from
+ * accessing the device during suspend.
+ */
+ if (mhi_cntrl->no_m3) {
+ new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3);
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ if (new_state != MHI_PM_M3) {
+ dev_err(dev,
+ "Error setting to PM state: %s from: %s\n",
+ to_mhi_pm_state_str(MHI_PM_M3),
+ to_mhi_pm_state_str(mhi_cntrl->pm_state));
+ return -EIO;
+ }
+ } else {
+ /* Set MHI to M3 and wait for completion */
+ mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ dev_dbg(dev, "Waiting for M3 completion\n");
- if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
- dev_err(dev,
- "Did not enter M3 state, MHI state: %s, PM state: %s\n",
- mhi_state_str(mhi_cntrl->dev_state),
- to_mhi_pm_state_str(mhi_cntrl->pm_state));
- return -EIO;
+ ret = wait_event_timeout(mhi_cntrl->state_event,
+ mhi_cntrl->dev_state == MHI_STATE_M3 ||
+ MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
+ msecs_to_jiffies(mhi_cntrl->timeout_ms));
+
+ if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
+ dev_err(dev,
+ "Did not enter M3 state, MHI state: %s, PM state: %s\n",
+ mhi_state_str(mhi_cntrl->dev_state),
+ to_mhi_pm_state_str(mhi_cntrl->pm_state));
+ return -EIO;
+ }
}
/* Notify clients about entering LPM */
@@ -969,7 +978,8 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
return -EIO;
- if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) {
+ if (!mhi_cntrl->no_m3 &&
+ mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) {
dev_warn(dev, "Resuming from non M3 state (%s)\n",
mhi_state_str(mhi_get_mhi_state(mhi_cntrl)));
if (!force)
@@ -995,6 +1005,15 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
return -EIO;
}
+ /*
+ * For devices without M3 support, just move the host back to M0
+ * directly.
+ */
+ if (mhi_cntrl->no_m3) {
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ return mhi_pm_m0_transition(mhi_cntrl);
+ }
+
/* Set MHI to M0 and wait for completion */
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
write_unlock_irq(&mhi_cntrl->pm_lock);
diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c
index d3d230247262..a118a89a7779 100644
--- a/drivers/cdx/cdx.c
+++ b/drivers/cdx/cdx.c
@@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
sysfs_bin_attr_init(res_attr);
- cdx_dev->res_attr[num] = res_attr;
sprintf(res_attr_name, "resource%d", num);
res_attr->mmap = cdx_mmap_resource;
@@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
res_attr->size = cdx_resource_len(cdx_dev, num);
res_attr->private = (void *)(unsigned long)num;
ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr);
- if (ret)
+ if (ret) {
kfree(res_attr);
+ return ret;
+ }
+
+ cdx_dev->res_attr[num] = res_attr;
return ret;
}
diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c
index 63175b765c90..a98acb199805 100644
--- a/drivers/char/powernv-op-panel.c
+++ b/drivers/char/powernv-op-panel.c
@@ -89,7 +89,6 @@ static int __op_panel_update_display(void)
static ssize_t oppanel_write(struct file *filp, const char __user *userbuf,
size_t len, loff_t *f_pos)
{
- loff_t f_pos_prev = *f_pos;
ssize_t ret;
int rc;
@@ -105,7 +104,6 @@ static ssize_t oppanel_write(struct file *filp, const char __user *userbuf,
if (rc != OPAL_SUCCESS) {
pr_err_ratelimited("OPAL call failed to write to op panel display [rc=%d]\n",
rc);
- *f_pos = f_pos_prev;
return -EIO;
}
}
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 6da817b9849f..8803268b4cdc 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -340,15 +340,17 @@ static enum ieee1284_phase init_phase(int mode)
return IEEE1284_PH_FWD_IDLE;
}
-static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec)
+static int pp_set_timeout(struct pardevice *pdev, s64 tv_sec, s64 tv_usec)
{
+ struct timespec64 ts;
long to_jiffies;
- if ((tv_sec < 0) || (tv_usec < 0))
+ if (tv_sec < 0 || tv_usec < 0 || tv_usec >= USEC_PER_SEC)
return -EINVAL;
- to_jiffies = usecs_to_jiffies(tv_usec);
- to_jiffies += tv_sec * HZ;
+ ts.tv_sec = tv_sec;
+ ts.tv_nsec = tv_usec * NSEC_PER_USEC;
+ to_jiffies = timespec64_to_jiffies(&ts);
if (to_jiffies <= 0)
return -EINVAL;
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index dd45fe5eb6f2..255f69123af5 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -328,7 +328,7 @@ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static ssize_t store_received_ref_clk3a(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -350,7 +350,7 @@ static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_received_ref_clk3b(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -372,7 +372,7 @@ static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_enable_clk3b_output(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -394,7 +394,7 @@ static ssize_t store_enable_clk3a_output(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long flags;
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
sscanf(buf, "%lX", &tmp);
@@ -415,7 +415,7 @@ static ssize_t store_enable_clkb1_output(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long flags;
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
sscanf(buf, "%lX", &tmp);
@@ -437,7 +437,7 @@ static ssize_t store_enable_clka1_output(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long flags;
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
sscanf(buf, "%lX", &tmp);
@@ -458,7 +458,7 @@ static ssize_t store_enable_clkb0_output(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long flags;
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
sscanf(buf, "%lX", &tmp);
@@ -479,7 +479,7 @@ static ssize_t store_enable_clka0_output(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long flags;
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
sscanf(buf, "%lX", &tmp);
@@ -500,7 +500,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long flags;
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
sscanf(buf, "%lX", &tmp);
@@ -541,7 +541,7 @@ static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_select_amcb1_transmit_clock(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -583,7 +583,7 @@ static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_select_redundant_clock(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -604,7 +604,7 @@ static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_select_ref_frequency(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -625,7 +625,7 @@ static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_filter_select(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -645,7 +645,7 @@ static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select);
static ssize_t store_hardware_switching_mode(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -666,7 +666,7 @@ static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_hardware_switching(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -687,7 +687,7 @@ static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL,
static ssize_t store_refalign (struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned long flags;
sscanf(buf, "%lX", &tmp);
@@ -706,7 +706,7 @@ static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign);
static ssize_t store_mode_select (struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
@@ -726,7 +726,7 @@ static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select);
static ssize_t store_reset (struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
- unsigned long tmp;
+ unsigned long tmp = 0;
unsigned char val;
unsigned long flags;
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 198b97314168..62eecfa61646 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -157,6 +157,12 @@ struct ports_device {
/* Major number for this device. Ports will be created as minors. */
int chr_major;
+
+ /*
+ * Set to true during PM freeze to block TX paths that may race
+ * with virtqueue teardown (e.g. hvc put_chars with no_console_suspend).
+ */
+ bool pm_freezing;
};
struct port_stats {
@@ -304,6 +310,12 @@ out:
return port;
}
+/*
+ * Finds a port by the virtqueue and returns a pointer to struct port
+ * with the reference count incremented.
+ *
+ * Callers MUST decrement it when finished.
+ */
static struct port *find_port_by_vq(struct ports_device *portdev,
struct virtqueue *vq)
{
@@ -312,8 +324,10 @@ static struct port *find_port_by_vq(struct ports_device *portdev,
spin_lock_irqsave(&portdev->ports_lock, flags);
list_for_each_entry(port, &portdev->ports, list)
- if (port->in_vq == vq || port->out_vq == vq)
+ if (port->in_vq == vq || port->out_vq == vq) {
+ kref_get(&port->kref);
goto out;
+ }
port = NULL;
out:
spin_unlock_irqrestore(&portdev->ports_lock, flags);
@@ -402,7 +416,7 @@ static void reclaim_dma_bufs(void)
}
static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size,
- int pages)
+ int pages, gfp_t gfp)
{
struct port_buffer *buf;
@@ -436,11 +450,10 @@ static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size
/* Increase device refcnt to avoid freeing it */
get_device(buf->dev);
- buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma,
- GFP_KERNEL);
+ buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, gfp);
} else {
buf->dev = NULL;
- buf->buf = kmalloc(buf_size, GFP_KERNEL);
+ buf->buf = kmalloc(buf_size, gfp);
}
if (!buf->buf)
@@ -595,27 +608,46 @@ static void reclaim_consumed_buffers(struct port *port)
static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
int nents, size_t in_count,
- void *data, bool nonblock)
+ struct port_buffer *buf, bool nonblock)
{
struct virtqueue *out_vq;
int err;
unsigned long flags;
unsigned int len;
-
- out_vq = port->out_vq;
+ struct ports_device *portdev;
spin_lock_irqsave(&port->outvq_lock, flags);
+ portdev = READ_ONCE(port->portdev);
+
+ if (!portdev) {
+ in_count = 0;
+ goto free_and_done;
+ }
+
+ /*
+ * Check freeze flag under the lock so that the flag check and
+ * virtqueue_add_outbuf() are atomic with respect to
+ * remove_port_data() which also takes outvq_lock. This
+ * guarantees that once remove_port_data() returns, no new
+ * buffers can be added before remove_vqs() tears down the vq.
+ * Pairs with smp_store_release() in virtcons_freeze/restore.
+ */
+ if (smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */
+ goto free_and_done;
+
+ out_vq = port->out_vq;
+
reclaim_consumed_buffers(port);
- err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC);
+ err = virtqueue_add_outbuf(out_vq, sg, nents, buf, GFP_ATOMIC);
/* Tell Host to go! */
virtqueue_kick(out_vq);
if (err) {
in_count = 0;
- goto done;
+ goto free_and_done;
}
if (out_vq->num_free == 0)
@@ -632,10 +664,19 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
* buffer and relax the spinning requirement. The downside is
* we need to kmalloc a GFP_ATOMIC buffer each time the
* console driver writes something out.
+ *
+ * Spin until host returns the buffer.
+ * Capture the returned buf so we can free it.
+ * If broken, buf == NULL and buf stays in the vq;
+ * remove_vqs() will call virtqueue_detach_unused_buf() -> free_buf().
*/
- while (!virtqueue_get_buf(out_vq, &len)
+ while (!(buf = virtqueue_get_buf(out_vq, &len))
&& !virtqueue_is_broken(out_vq))
cpu_relax();
+
+free_and_done:
+ if (buf)
+ free_buf(buf, false);
done:
spin_unlock_irqrestore(&port->outvq_lock, flags);
@@ -816,14 +857,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
count = min((size_t)(32 * 1024), count);
- buf = alloc_buf(port->portdev->vdev, count, 0);
+ buf = alloc_buf(port->portdev->vdev, count, 0, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = copy_from_user(buf->buf, ubuf, count);
if (ret) {
- ret = -EFAULT;
- goto free_buf;
+ free_buf(buf, true);
+ return -EFAULT;
}
/*
@@ -835,15 +876,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
*/
nonblock = true;
sg_init_one(sg, buf->buf, count);
- ret = __send_to_port(port, sg, 1, count, buf, nonblock);
-
- if (nonblock && ret > 0)
- goto out;
-
-free_buf:
- free_buf(buf, true);
-out:
- return ret;
+ return __send_to_port(port, sg, 1, count, buf, nonblock);
}
struct sg_list {
@@ -932,7 +965,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
goto error_out;
occupancy = pipe_buf_usage(pipe);
- buf = alloc_buf(port->portdev->vdev, 0, occupancy);
+ buf = alloc_buf(port->portdev->vdev, 0, occupancy, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
@@ -946,11 +979,12 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
sg_init_table(sgl.sg, sgl.size);
ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
pipe_unlock(pipe);
+
if (likely(ret > 0))
ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);
-
- if (unlikely(ret <= 0))
+ else
free_buf(buf, true);
+
return ret;
error_out:
@@ -1108,21 +1142,50 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count)
{
struct port *port;
struct scatterlist sg[1];
- void *data;
- int ret;
+ struct port_buffer *pbuf;
+ struct ports_device *portdev;
port = find_port_by_vtermno(vtermno);
if (!port)
return -EPIPE;
- data = kmemdup(buf, count, GFP_ATOMIC);
- if (!data)
+ /*
+ * Silently drop output in two cases, both by returning count so
+ * that the hvc layer does not spin-retry:
+ *
+ * 1. Device hot-unplug (!portdev): portdev was NULLed by
+ * unplug_port() after hvc_remove() was already called, so
+ * the hvc layer will stop invoking put_chars() very soon.
+ * Returning count avoids a pointless retry loop in the
+ * interim.
+ *
+ * 2. PM freeze (pm_freezing): the hvc console stays active
+ * under no_console_suspend but virtqueues are being torn
+ * down. Drop the output silently so the hvc layer does not
+ * stall suspend.
+ *
+ * This early check avoids a pointless GFP_ATOMIC allocation;
+ * __send_to_port() rechecks under outvq_lock for correctness.
+ * Pairs with smp_store_release() in virtcons_freeze/restore.
+ */
+ portdev = READ_ONCE(port->portdev);
+ if (!portdev ||
+ smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */
+ return count;
+
+ pbuf = alloc_buf(portdev->vdev, count, 0, GFP_ATOMIC);
+ if (!pbuf)
return -ENOMEM;
- sg_init_one(sg, data, count);
- ret = __send_to_port(port, sg, 1, count, data, false);
- kfree(data);
- return ret;
+ memcpy(pbuf->buf, buf, count);
+ pbuf->len = count;
+ sg_init_one(sg, pbuf->buf, count);
+
+ /*
+ * Ownership of pbuf is transferred to __send_to_port().
+ * Do not touch or free pbuf after this call.
+ */
+ return __send_to_port(port, sg, 1, count, pbuf, false);
}
/*
@@ -1295,7 +1358,7 @@ static int fill_queue(struct virtqueue *vq, spinlock_t *lock)
nr_added_bufs = 0;
do {
- buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
+ buf = alloc_buf(vq->vdev, PAGE_SIZE, 0, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -1498,11 +1561,16 @@ static void unplug_port(struct port *port)
remove_port_data(port);
/*
- * We should just assume the device itself has gone off --
- * else a close on an open port later will try to send out a
- * control message.
+ * Null out portdev under outvq_lock so that __send_to_port()
+ * cannot race: it checks port->portdev inside the same lock
+ * and bails out if NULL, preventing any buffer from being
+ * enqueued to an already torn-down virtqueue. Also prevents
+ * a close on an open port later from sending a stale control
+ * message.
*/
+ spin_lock_irq(&port->outvq_lock);
port->portdev = NULL;
+ spin_unlock_irq(&port->outvq_lock);
sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
device_destroy(&port_class, port->dev->devt);
@@ -1536,7 +1604,8 @@ static void handle_control_message(struct virtio_device *vdev,
cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) {
/* No valid header at start of buffer. Drop it. */
dev_dbg(&portdev->vdev->dev,
- "Invalid index %u in control packet\n", cpkt->id);
+ "Invalid index %u in control packet\n",
+ virtio32_to_cpu(vdev, cpkt->id));
return;
}
@@ -1553,7 +1622,8 @@ static void handle_control_message(struct virtio_device *vdev,
dev_warn(&portdev->vdev->dev,
"Request for adding port with "
"out-of-bound id %u, max. supported id: %u\n",
- cpkt->id, portdev->max_nr_ports - 1);
+ virtio32_to_cpu(vdev, cpkt->id),
+ portdev->max_nr_ports - 1);
break;
}
add_port(portdev, virtio32_to_cpu(vdev, cpkt->id));
@@ -1706,6 +1776,7 @@ static void out_intr(struct virtqueue *vq)
}
wake_up_interruptible(&port->waitqueue);
+ kref_put(&port->kref, remove_port);
}
static void in_intr(struct virtqueue *vq)
@@ -1751,6 +1822,8 @@ static void in_intr(struct virtqueue *vq)
if (is_console_port(port) && hvc_poll(port->cons.hvc))
hvc_kick();
+
+ kref_put(&port->kref, remove_port);
}
static void control_intr(struct virtqueue *vq)
@@ -1980,6 +2053,7 @@ static int virtcons_probe(struct virtio_device *vdev)
/* Attach this portdev to this virtio_device, and vice-versa. */
portdev->vdev = vdev;
vdev->priv = portdev;
+ portdev->pm_freezing = false;
portdev->chr_major = register_chrdev(0, "virtio-portsdev",
&portdev_fops);
@@ -2099,9 +2173,30 @@ static int virtcons_freeze(struct virtio_device *vdev)
{
struct ports_device *portdev;
struct port *port;
+ unsigned long flags;
portdev = vdev->priv;
+ /*
+ * Block TX paths (put_chars, __send_to_port) before resetting the
+ * device and tearing down virtqueues. This prevents races with
+ * hvc console writes that remain active under no_console_suspend.
+ */
+ smp_store_release(&portdev->pm_freezing, true);
+
+ /*
+ * Synchronize with any concurrent __send_to_port() that may have
+ * passed the pm_freezing check. By acquiring and releasing the
+ * outvq_lock for each port, we ensure all active TX paths have
+ * completed before we reset the device.
+ */
+ spin_lock_irqsave(&portdev->ports_lock, flags);
+ list_for_each_entry(port, &portdev->ports, list) {
+ spin_lock(&port->outvq_lock);
+ spin_unlock(&port->outvq_lock);
+ }
+ spin_unlock_irqrestore(&portdev->ports_lock, flags);
+
virtio_reset_device(vdev);
if (use_multiport(portdev))
@@ -2144,9 +2239,6 @@ static int virtcons_restore(struct virtio_device *vdev)
virtio_device_ready(portdev->vdev);
- if (use_multiport(portdev))
- fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
-
list_for_each_entry(port, &portdev->ports, list) {
port->in_vq = portdev->in_vqs[port->id];
port->out_vq = portdev->out_vqs[port->id];
@@ -2163,6 +2255,24 @@ static int virtcons_restore(struct virtio_device *vdev)
if (port->guest_connected)
send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
}
+
+ /*
+ * Populate the control receive queue only after the list iteration
+ * is complete. If we fill this queue before iterating, the host could
+ * immediately deliver a VIRTIO_CONSOLE_PORT_REMOVE message.
+ * This would trigger the control workqueue, which modifies the
+ * portdev->ports list concurrently with the unprotected loop above,
+ * leading to a Use-After-Free and list corruption.
+ */
+ if (use_multiport(portdev))
+ fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
+
+ /*
+ * Allow TX paths only after all port->out_vq pointers have
+ * been reassigned to the newly allocated virtqueues.
+ */
+ smp_store_release(&portdev->pm_freezing, false);
+
return 0;
}
#endif
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index 34a345dc5e72..9bb5fa642fd8 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -760,7 +760,7 @@ static int __init hwicap_module_init(void)
HWICAP_DEVICES,
DRIVER_NAME);
if (retval < 0)
- return retval;
+ goto failed_class;
retval = platform_driver_register(&hwicap_platform_driver);
if (retval)
@@ -771,6 +771,9 @@ static int __init hwicap_module_init(void)
failed:
unregister_chrdev_region(devt, HWICAP_DEVICES);
+ failed_class:
+ class_unregister(&icap_class);
+
return retval;
}
diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c
index 1a1e64133315..46e1046abfca 100644
--- a/drivers/char/xillybus/xillybus_of.c
+++ b/drivers/char/xillybus/xillybus_of.c
@@ -55,11 +55,8 @@ static int xilly_drv_probe(struct platform_device *op)
rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint);
- if (rc) {
- dev_err(endpoint->dev,
- "Failed to register IRQ handler. Aborting.\n");
+ if (rc)
return -ENODEV;
- }
return xillybus_endpoint_discovery(endpoint);
}
diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c
index 9858711e3e79..32064b6c7627 100644
--- a/drivers/char/xillybus/xillybus_pcie.c
+++ b/drivers/char/xillybus/xillybus_pcie.c
@@ -83,11 +83,8 @@ static int xilly_probe(struct pci_dev *pdev,
}
rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0,
xillyname, endpoint);
- if (rc) {
- dev_err(endpoint->dev,
- "Failed to register MSI handler. Aborting.\n");
+ if (rc)
return -ENODEV;
- }
/*
* Some (old and buggy?) hardware drops 64-bit addressed PCIe packets,
diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c
index d5d18fa2c638..52385b14a9a4 100644
--- a/drivers/comedi/drivers/aio_iiro_16.c
+++ b/drivers/comedi/drivers/aio_iiro_16.c
@@ -59,6 +59,9 @@ static irqreturn_t aio_iiro_16_cos(int irq, void *d)
unsigned int status;
unsigned int val;
+ if (!dev->attached)
+ return IRQ_NONE;
+
status = inb(dev->iobase + AIO_IIRO_16_STATUS);
if (!(status & AIO_IIRO_16_STATUS_IRQE))
return IRQ_NONE;
diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c
index b6563a48ada6..fd36f820979c 100644
--- a/drivers/comedi/drivers/c6xdigio.c
+++ b/drivers/comedi/drivers/c6xdigio.c
@@ -224,10 +224,10 @@ static void c6xdigio_init(struct comedi_device *dev)
static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
/* Standard LPT Printer Port */
- {.id = "PNP0400", .driver_data = 0},
+ { .id = "PNP0400" },
/* ECP Printer Port */
- {.id = "PNP0401", .driver_data = 0},
- {}
+ { .id = "PNP0401" },
+ { }
};
static struct pnp_driver c6xdigio_pnp_driver = {
diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c
index 516a5d5a2840..384e9ba8d5df 100644
--- a/drivers/comedi/drivers/das6402.c
+++ b/drivers/comedi/drivers/das6402.c
@@ -173,10 +173,16 @@ static irqreturn_t das6402_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
struct comedi_subdevice *s = dev->read_subdev;
- struct comedi_async *async = s->async;
- struct comedi_cmd *cmd = &async->cmd;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
unsigned int status;
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ async = s->async;
+ cmd = &async->cmd;
+
status = inb(dev->iobase + DAS6402_STATUS_REG);
if ((status & DAS6402_STATUS_INT) == 0)
return IRQ_NONE;
diff --git a/drivers/comedi/drivers/dt2811.c b/drivers/comedi/drivers/dt2811.c
index bcc4b5ef48e8..0438b8c90e44 100644
--- a/drivers/comedi/drivers/dt2811.c
+++ b/drivers/comedi/drivers/dt2811.c
@@ -193,13 +193,16 @@ static irqreturn_t dt2811_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
struct comedi_subdevice *s = dev->read_subdev;
- struct comedi_async *async = s->async;
- struct comedi_cmd *cmd = &async->cmd;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
unsigned int status;
if (!dev->attached)
return IRQ_NONE;
+ async = s->async;
+ cmd = &async->cmd;
+
status = inb(dev->iobase + DT2811_ADCSR_REG);
if (status & DT2811_ADCSR_ADERROR) {
diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c
index 44221c928e32..86629b495fed 100644
--- a/drivers/comedi/drivers/ni_at_a2150.c
+++ b/drivers/comedi/drivers/ni_at_a2150.c
@@ -132,11 +132,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d)
struct comedi_device *dev = d;
struct a2150_private *devpriv = dev->private;
struct comedi_isadma *dma = devpriv->dma;
- struct comedi_isadma_desc *desc = &dma->desc[0];
+ struct comedi_isadma_desc *desc;
struct comedi_subdevice *s = dev->read_subdev;
- struct comedi_async *async = s->async;
- struct comedi_cmd *cmd = &async->cmd;
- unsigned short *buf = desc->virt_addr;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ unsigned short *buf;
unsigned int max_points, num_points, residue, leftover;
unsigned short dpnt;
int status;
@@ -145,6 +145,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d)
if (!dev->attached)
return IRQ_HANDLED;
+ desc = &dma->desc[0];
+ async = s->async;
+ cmd = &async->cmd;
+ buf = desc->virt_addr;
+
status = inw(dev->iobase + STATUS_REG);
if ((status & INTR_BIT) == 0)
return IRQ_NONE;
diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c
index 7bc336333ace..537301eee9bf 100644
--- a/drivers/comedi/drivers/ni_atmio.c
+++ b/drivers/comedi/drivers/ni_atmio.c
@@ -215,13 +215,13 @@ static const int ni_irqpin[] = {
#include "ni_mio_common.c"
-static const struct pnp_device_id __maybe_unused device_ids[] = {
- {.id = "NIC1900", .driver_data = 0},
- {.id = "NIC2400", .driver_data = 0},
- {.id = "NIC2500", .driver_data = 0},
- {.id = "NIC2600", .driver_data = 0},
- {.id = "NIC2700", .driver_data = 0},
- {.id = ""}
+static const struct pnp_device_id device_ids[] = {
+ { .id = "NIC1900" },
+ { .id = "NIC2400" },
+ { .id = "NIC2500" },
+ { .id = "NIC2600" },
+ { .id = "NIC2700" },
+ { }
};
MODULE_DEVICE_TABLE(pnp, device_ids);
diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c
index b2772d909896..6765cdc276ca 100644
--- a/drivers/comedi/drivers/ni_atmio16d.c
+++ b/drivers/comedi/drivers/ni_atmio16d.c
@@ -223,6 +223,9 @@ static irqreturn_t atmio16d_interrupt(int irq, void *d)
struct comedi_subdevice *s = dev->read_subdev;
unsigned short val;
+ if (!dev->attached)
+ return IRQ_NONE;
+
val = inw(dev->iobase + AD_FIFO_REG);
comedi_buf_write_samples(s, &val, 1);
comedi_handle_events(dev, s);
diff --git a/drivers/comedi/drivers/ni_pcimio.c b/drivers/comedi/drivers/ni_pcimio.c
index 4be9ca4f4828..299e2a0e9474 100644
--- a/drivers/comedi/drivers/ni_pcimio.c
+++ b/drivers/comedi/drivers/ni_pcimio.c
@@ -685,17 +685,18 @@ static const struct ni_board_struct ni_boards[] = {
.name = "pci-6220",
.n_adchan = 16,
.ai_maxdata = 0xffff,
- .ai_fifo_depth = 512, /* FIXME: guess */
+ .ai_fifo_depth = 4095,
.gainlkup = ai_gain_622x,
.ai_speed = 4000,
.reg_type = ni_reg_622x,
.caldac = { caldac_none },
+ .dio_speed = 1000,
},
[BOARD_PXI6220] = {
.name = "pxi-6220",
.n_adchan = 16,
.ai_maxdata = 0xffff,
- .ai_fifo_depth = 512, /* FIXME: guess */
+ .ai_fifo_depth = 4095,
.gainlkup = ai_gain_622x,
.ai_speed = 4000,
.reg_type = ni_reg_622x,
diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c
index 5d2c4b2aa3bb..8ad5789966f0 100644
--- a/drivers/comedi/drivers/pcl711.c
+++ b/drivers/comedi/drivers/pcl711.c
@@ -184,7 +184,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
struct comedi_subdevice *s = dev->read_subdev;
- struct comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd;
unsigned short data;
if (!dev->attached) {
@@ -192,6 +192,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d)
return IRQ_HANDLED;
}
+ cmd = &s->async->cmd;
data = pcl711_ai_get_sample(dev, s);
outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG);
diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c
index 1fcb2f798c7a..b0d30cb1cfe9 100644
--- a/drivers/comedi/drivers/pcl816.c
+++ b/drivers/comedi/drivers/pcl816.c
@@ -242,7 +242,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d)
struct comedi_subdevice *s = dev->read_subdev;
struct pcl816_private *devpriv = dev->private;
struct comedi_isadma *dma = devpriv->dma;
- struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ struct comedi_isadma_desc *desc;
unsigned int nsamples;
unsigned int bufptr;
@@ -257,6 +257,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d)
return IRQ_HANDLED;
}
+ desc = &dma->desc[dma->cur_dma];
nsamples = comedi_bytes_to_samples(s, desc->size) -
devpriv->ai_poll_ptr;
bufptr = devpriv->ai_poll_ptr;
diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c
index aa775a024fc7..89f32c493caa 100644
--- a/drivers/comedi/drivers/pcl818.c
+++ b/drivers/comedi/drivers/pcl818.c
@@ -534,13 +534,14 @@ static irqreturn_t pcl818_interrupt(int irq, void *d)
struct comedi_device *dev = d;
struct pcl818_private *devpriv = dev->private;
struct comedi_subdevice *s = dev->read_subdev;
- struct comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd;
if (!dev->attached || !devpriv->ai_cmd_running) {
pcl818_ai_clear_eoc(dev);
return IRQ_HANDLED;
}
+ cmd = &s->async->cmd;
if (devpriv->ai_cmd_canceled) {
/*
* The cleanup from ai_cancel() has been delayed
diff --git a/drivers/comedi/drivers/pcmmio.c b/drivers/comedi/drivers/pcmmio.c
index d38202c8a12b..f42b7343b4e4 100644
--- a/drivers/comedi/drivers/pcmmio.c
+++ b/drivers/comedi/drivers/pcmmio.c
@@ -362,6 +362,9 @@ static irqreturn_t interrupt_pcmmio(int irq, void *d)
unsigned int triggered;
unsigned char int_pend;
+ if (!dev->attached)
+ return IRQ_NONE;
+
/* are there any interrupts pending */
int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
if (!int_pend)
diff --git a/drivers/comedi/drivers/pcmuio.c b/drivers/comedi/drivers/pcmuio.c
index 0995911a3ea3..d9995cbeecb6 100644
--- a/drivers/comedi/drivers/pcmuio.c
+++ b/drivers/comedi/drivers/pcmuio.c
@@ -362,12 +362,15 @@ static irqreturn_t pcmuio_interrupt(int irq, void *d)
struct pcmuio_private *devpriv = dev->private;
int handled = 0;
+ if (!dev->attached)
+ return IRQ_NONE;
+
if (irq == dev->irq)
handled += pcmuio_handle_asic_interrupt(dev, 0);
if (irq == devpriv->irq2)
handled += pcmuio_handle_asic_interrupt(dev, 1);
- return handled ? IRQ_HANDLED : IRQ_NONE;
+ return IRQ_RETVAL(handled);
}
/* chip->spinlock is already locked */
diff --git a/drivers/comedi/drivers/quatech_daqp_cs.c b/drivers/comedi/drivers/quatech_daqp_cs.c
index 2a76c75c513b..d5e3e213e233 100644
--- a/drivers/comedi/drivers/quatech_daqp_cs.c
+++ b/drivers/comedi/drivers/quatech_daqp_cs.c
@@ -211,13 +211,15 @@ static irqreturn_t daqp_interrupt(int irq, void *dev_id)
{
struct comedi_device *dev = dev_id;
struct comedi_subdevice *s = dev->read_subdev;
- struct comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd;
int loop_limit = 10000;
int status;
if (!dev->attached)
return IRQ_NONE;
+ cmd = &s->async->cmd;
+
status = inb(dev->iobase + DAQP_STATUS_REG);
if (!(status & DAQP_STATUS_EVENTS))
return IRQ_NONE;
diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
index a3d8f7a5874e..9eee281350d0 100644
--- a/drivers/counter/stm32-timer-cnt.c
+++ b/drivers/counter/stm32-timer-cnt.c
@@ -759,11 +759,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
/* All events reported through the global interrupt */
ret = devm_request_irq(&pdev->dev, ddata->irq[0], stm32_timer_cnt_isr,
0, dev_name(dev), counter);
- if (ret) {
- dev_err(dev, "Failed to request irq %d (err %d)\n",
- ddata->irq[0], ret);
+ if (ret)
return ret;
- }
} else {
for (i = 0; i < priv->nr_irqs; i++) {
/*
@@ -775,11 +772,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, ddata->irq[i], stm32_timer_cnt_isr,
0, dev_name(dev), counter);
- if (ret) {
- dev_err(dev, "Failed to request irq %d (err %d)\n",
- ddata->irq[i], ret);
+ if (ret)
return ret;
- }
}
}
diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c
index f69b6920463f..d77a256f0ed3 100644
--- a/drivers/counter/ti-ecap-capture.c
+++ b/drivers/counter/ti-ecap-capture.c
@@ -513,7 +513,7 @@ static int ecap_cnt_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, ret, ecap_cnt_isr, 0, pdev->name, counter_dev);
if (ret)
- return dev_err_probe(dev, ret, "failed to request irq\n");
+ return ret;
platform_set_drvdata(pdev, counter_dev);
diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
index d9302ec21163..0dda3f2b14c7 100644
--- a/drivers/counter/ti-eqep.c
+++ b/drivers/counter/ti-eqep.c
@@ -526,7 +526,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
err = devm_request_threaded_irq(dev, irq, NULL, ti_eqep_irq_handler,
IRQF_ONESHOT, dev_name(dev), counter);
if (err < 0)
- return dev_err_probe(dev, err, "failed to request IRQ\n");
+ return err;
counter->name = dev_name(dev);
counter->parent = dev;
diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c
index 79ab44fe26aa..73c4ba3dc0bc 100644
--- a/drivers/eisa/eisa-bus.c
+++ b/drivers/eisa/eisa-bus.c
@@ -12,6 +12,7 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/ioport.h>
#include <asm/io.h>
@@ -393,7 +394,7 @@ static int __init eisa_probe(struct eisa_root_device *root)
}
}
- dev_info(root->dev, "EISA: Detected %d card%s\n", c, c == 1 ? "" : "s");
+ dev_info(root->dev, "EISA: Detected %d card%s\n", c, str_plural(c));
return 0;
}
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
index 7e3c9f38297b..e735f43dcdeb 100644
--- a/drivers/extcon/extcon-adc-jack.c
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -210,3 +210,4 @@ module_platform_driver(adc_jack_driver);
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_DESCRIPTION("ADC Jack extcon driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c
index daddb5224794..b360f752d191 100644
--- a/drivers/firmware/stratix10-rsu.c
+++ b/drivers/firmware/stratix10-rsu.c
@@ -7,17 +7,28 @@
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/firmware/intel/stratix10-svc-client.h>
#include <linux/string.h>
#include <linux/sysfs.h>
-#include <linux/delay.h>
-#define RSU_ERASE_SIZE_MASK GENMASK_ULL(63, 32)
+#define RSU_STATE_MASK GENMASK_ULL(31, 0)
+#define RSU_VERSION_MASK GENMASK_ULL(63, 32)
+#define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0)
+#define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32)
+/*
+ * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO packs each flash word as:
+ * [63:32] erase_size, [31:0] size (see stratix10-smc.h).
+ */
+#define RSU_DEVICE_INFO_SIZE_MASK GENMASK_ULL(31, 0)
+#define RSU_DEVICE_INFO_ERASE_SIZE_MASK GENMASK_ULL(63, 32)
+
#define RSU_DCMF0_MASK GENMASK_ULL(31, 0)
#define RSU_DCMF1_MASK GENMASK_ULL(63, 32)
#define RSU_DCMF2_MASK GENMASK_ULL(31, 0)
@@ -33,11 +44,32 @@
#define INVALID_DCMF_VERSION 0xFF
#define INVALID_DCMF_STATUS 0xFFFFFFFF
#define INVALID_SPT_ADDRESS 0x0
+#define INVALID_DEVICE_INFO (~0U)
#define RSU_RETRY_SLEEP_MS (1U)
#define RSU_ASYNC_MSG_RETRY (3U)
+#define RSU_GET_SPT_CMD 0x5A
#define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int))
+struct flash_device_info {
+ unsigned int size;
+ unsigned int erase_size;
+};
+
+/**
+ * rsu_device_info_set_from_packed() - Decode one RSU device-info SMC word
+ * @di: slot to fill
+ * @packed: register value: [63:32] erase_size, [31:0] size
+ * (INTEL_SIP_SMC_RSU_GET_DEVICE_INFO)
+ */
+static void rsu_device_info_set_from_packed(struct flash_device_info *di,
+ unsigned long packed)
+{
+ di->size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_SIZE_MASK, packed);
+ di->erase_size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_ERASE_SIZE_MASK,
+ packed);
+}
+
typedef void (*rsu_callback)(struct stratix10_svc_client *client,
struct stratix10_svc_cb_data *data);
/**
@@ -46,6 +78,7 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client,
* @client: active service client
* @completion: state for callback completion
* @lock: a mutex to protect callback completion state
+ * @async: supports async operations
* @status.current_image: address of image currently running in flash
* @status.fail_image: address of failed image in flash
* @status.version: the interface version number of RSU firmware
@@ -60,16 +93,20 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client,
* @dcmf_status.dcmf1: dcmf1 status
* @dcmf_status.dcmf2: dcmf2 status
* @dcmf_status.dcmf3: dcmf3 status
+ * @device_info: per-device flash information array; each entry contains
+ * size and erase size for one flash device
* @retry_counter: the current image's retry counter
* @max_retry: the preset max retry value
* @spt0_address: address of spt0
* @spt1_address: address of spt1
+ * @get_spt_response_buf: response from sdm for get_spt command
*/
struct stratix10_rsu_priv {
struct stratix10_svc_chan *chan;
struct stratix10_svc_client client;
struct completion completion;
struct mutex lock;
+ bool async;
struct {
unsigned long current_image;
unsigned long fail_image;
@@ -93,17 +130,74 @@ struct stratix10_rsu_priv {
unsigned int dcmf3;
} dcmf_status;
+ struct flash_device_info device_info[4];
+
unsigned int retry_counter;
unsigned int max_retry;
unsigned long spt0_address;
unsigned long spt1_address;
+
+ unsigned int *get_spt_response_buf;
};
+/**
+ * rsu_device_info_invalidate() - Mark all cached QSPI device slots invalid
+ * @priv: RSU private data
+ */
+static void rsu_device_info_invalidate(struct stratix10_rsu_priv *priv)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->device_info); i++) {
+ priv->device_info[i].size = INVALID_DEVICE_INFO;
+ priv->device_info[i].erase_size = INVALID_DEVICE_INFO;
+ }
+}
+
typedef void (*rsu_async_callback)(struct device *dev,
struct stratix10_rsu_priv *priv, struct stratix10_svc_cb_data *data);
/**
+ * rsu_status_callback() - Status callback from Intel Service Layer
+ * @client: pointer to service client
+ * @data: pointer to callback data structure
+ *
+ * Callback from Intel service layer for RSU status request. Status is
+ * only updated after a system reboot, so a get updated status call is
+ * made during driver probe.
+ */
+static void rsu_status_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ priv->status.version = FIELD_GET(RSU_VERSION_MASK,
+ res->a2);
+ priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2);
+ priv->status.fail_image = res->a1;
+ priv->status.current_image = res->a0;
+ priv->status.error_location =
+ FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3);
+ priv->status.error_details =
+ FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3);
+ } else {
+ dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n",
+ res->a0);
+ priv->status.version = 0;
+ priv->status.state = 0;
+ priv->status.fail_image = 0;
+ priv->status.current_image = 0;
+ priv->status.error_location = 0;
+ priv->status.error_details = 0;
+ }
+
+ complete(&priv->completion);
+}
+
+/**
* rsu_async_status_callback() - Status callback from rsu_async_send()
* @dev: pointer to device object
* @priv: pointer to priv object
@@ -147,6 +241,32 @@ static void rsu_command_callback(struct stratix10_svc_client *client,
complete(&priv->completion);
}
+/**
+ * rsu_retry_callback() - Callback from Intel service layer for getting
+ * the current image's retry counter from the firmware
+ * @client: pointer to client
+ * @data: pointer to callback data structure
+ *
+ * Callback from Intel service layer for retry counter, which is used by
+ * user to know how many times the images is still allowed to reload
+ * itself before giving up and starting RSU fail-over flow.
+ */
+static void rsu_retry_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ unsigned int *counter = (unsigned int *)data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK))
+ priv->retry_counter = *counter;
+ else if (data->status == BIT(SVC_STATUS_NO_SUPPORT))
+ dev_warn(client->dev, "Secure FW doesn't support retry\n");
+ else
+ dev_err(client->dev, "Failed to get retry counter %lu\n",
+ BIT(data->status));
+
+ complete(&priv->completion);
+}
/**
* rsu_max_retry_callback() - Callback from Intel service layer for getting
@@ -229,8 +349,57 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client,
}
/**
- * rsu_async_get_spt_table_callback() - Callback to be used by the rsu_async_send()
- * to retrieve the SPT table information.
+ * rsu_get_device_info_callback() - Callback from Intel service layer for
+ * getting the QSPI device info
+ * @client: pointer to client
+ * @data: pointer to callback data structure
+ *
+ * Callback from Intel service layer for QSPI device info.
+ * @data->kaddr1 points to struct arm_smccc_1_2_regs on SVC_STATUS_OK or
+ * SVC_STATUS_ERROR; it is NULL on SVC_STATUS_NO_SUPPORT (unsupported command).
+ */
+static void rsu_get_device_info_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ struct arm_smccc_1_2_regs *res = data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ if (!res) {
+ dev_err(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO: missing result payload\n");
+ rsu_device_info_invalidate(priv);
+ complete(&priv->completion);
+ return;
+ }
+
+ rsu_device_info_set_from_packed(&priv->device_info[0], res->a1);
+ rsu_device_info_set_from_packed(&priv->device_info[1], res->a2);
+ rsu_device_info_set_from_packed(&priv->device_info[2], res->a3);
+ rsu_device_info_set_from_packed(&priv->device_info[3], res->a4);
+
+ } else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) {
+ dev_warn(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO not supported by firmware\n");
+ rsu_device_info_invalidate(priv);
+ } else {
+ if (res)
+ dev_err(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO returned 0x%lX\n",
+ res->a0);
+ else
+ dev_err(client->dev,
+ "COMMAND_RSU_GET_DEVICE_INFO failed with status 0x%X\n",
+ data->status);
+ rsu_device_info_invalidate(priv);
+ }
+
+ complete(&priv->completion);
+}
+
+/**
+ * rsu_async_get_spt_table_callback() - Callback to be used by the
+ * rsu_async_send() to retrieve the SPT table information.
* @dev: pointer to device object
* @priv: pointer to priv object
* @data: pointer to callback data structure
@@ -243,6 +412,38 @@ static void rsu_async_get_spt_table_callback(struct device *dev,
priv->spt1_address = *((unsigned long *)data->kaddr2);
}
+static void rsu_get_spt_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct stratix10_rsu_priv *priv = client->priv;
+ unsigned long *mbox_err = (unsigned long *)data->kaddr1;
+ unsigned long *resp_len = (unsigned long *)data->kaddr2;
+
+ if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) ||
+ (*resp_len != RSU_GET_SPT_RESP_LEN))
+ goto error;
+
+ priv->spt0_address = priv->get_spt_response_buf[0];
+ priv->spt0_address <<= 32;
+ priv->spt0_address |= priv->get_spt_response_buf[1];
+ priv->spt1_address = priv->get_spt_response_buf[2];
+ priv->spt1_address <<= 32;
+ priv->spt1_address |= priv->get_spt_response_buf[3];
+
+ goto complete;
+
+error:
+ dev_err(priv->client.dev,
+ "failed to get SPTs (status=%#x, mbox_err=%lu, resp_len=%lu)\n",
+ data->status, mbox_err ? *mbox_err : 0,
+ resp_len ? *resp_len : 0);
+
+complete:
+ stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf);
+ priv->get_spt_response_buf = NULL;
+ complete(&priv->completion);
+}
+
/**
* __rsu_send_msg_locked() - send a message to Intel service layer
* @priv: pointer to rsu private data
@@ -259,7 +460,7 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv,
unsigned long arg,
rsu_callback callback)
{
- struct stratix10_svc_client_msg msg;
+ struct stratix10_svc_client_msg msg = {0};
int ret;
lockdep_assert_held(&priv->lock);
@@ -271,6 +472,14 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv,
if (arg)
msg.arg[0] = arg;
+ if (command == COMMAND_MBOX_SEND_CMD) {
+ msg.arg[1] = 0;
+ msg.payload = NULL;
+ msg.payload_length = 0;
+ msg.payload_output = priv->get_spt_response_buf;
+ msg.payload_length_output = RSU_GET_SPT_RESP_LEN;
+ }
+
ret = stratix10_svc_send(priv->chan, &msg);
if (ret < 0)
goto status_done;
@@ -395,6 +604,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri
if (status && !handle) {
dev_err(dev, "Failed to send async message\n");
+ if (msg.payload_output)
+ stratix10_svc_free_memory(priv->chan, msg.payload_output);
return -ETIMEDOUT;
}
@@ -434,6 +645,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri
}
status_done:
+ if (msg.payload_output)
+ stratix10_svc_free_memory(priv->chan, msg.payload_output);
stratix10_svc_async_done(priv->chan, handle);
return ret;
}
@@ -681,15 +894,30 @@ static ssize_t notify_store(struct device *dev,
if (ret)
return ret;
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL);
+ if (priv->async)
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL);
+ else
+ ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY, status, rsu_command_callback);
if (ret) {
dev_err(dev, "Error, RSU notify returned %i\n", ret);
return ret;
}
/* to get the updated state */
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
- rsu_async_status_callback);
+ if (priv->async) {
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
+ rsu_async_status_callback);
+ } else {
+ ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0,
+ rsu_status_callback);
+ /*
+ * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS;
+ * issue it separately only in synchronous mode.
+ */
+ if (!ret)
+ ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0,
+ rsu_retry_callback);
+ }
if (ret) {
dev_err(dev, "Error, getting RSU status %i\n", ret);
return ret;
@@ -698,6 +926,75 @@ static ssize_t notify_store(struct device *dev,
return count;
}
+static ssize_t rsu_device_info_show(struct device *dev, char *buf,
+ unsigned int index, bool erase_size)
+{
+ struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
+ unsigned int value;
+
+ if (!priv)
+ return -ENODEV;
+
+ if (index >= ARRAY_SIZE(priv->device_info))
+ return -EINVAL;
+
+ value = erase_size ? priv->device_info[index].erase_size :
+ priv->device_info[index].size;
+
+ if (value == INVALID_DEVICE_INFO)
+ return -EIO;
+
+ return sysfs_emit(buf, "0x%08x\n", value);
+}
+
+static ssize_t size0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 0, false);
+}
+
+static ssize_t size1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 1, false);
+}
+
+static ssize_t size2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 2, false);
+}
+
+static ssize_t size3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 3, false);
+}
+
+static ssize_t erase_size0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 0, true);
+}
+
+static ssize_t erase_size1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 1, true);
+}
+
+static ssize_t erase_size2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 2, true);
+}
+
+static ssize_t erase_size3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rsu_device_info_show(dev, buf, 3, true);
+}
+
static ssize_t spt0_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -742,6 +1039,14 @@ static DEVICE_ATTR_RO(dcmf0_status);
static DEVICE_ATTR_RO(dcmf1_status);
static DEVICE_ATTR_RO(dcmf2_status);
static DEVICE_ATTR_RO(dcmf3_status);
+static DEVICE_ATTR_RO(size0);
+static DEVICE_ATTR_RO(size1);
+static DEVICE_ATTR_RO(size2);
+static DEVICE_ATTR_RO(size3);
+static DEVICE_ATTR_RO(erase_size0);
+static DEVICE_ATTR_RO(erase_size1);
+static DEVICE_ATTR_RO(erase_size2);
+static DEVICE_ATTR_RO(erase_size3);
static DEVICE_ATTR_WO(reboot_image);
static DEVICE_ATTR_WO(notify);
static DEVICE_ATTR_RO(spt0_address);
@@ -764,6 +1069,14 @@ static struct attribute *rsu_attrs[] = {
&dev_attr_dcmf1_status.attr,
&dev_attr_dcmf2_status.attr,
&dev_attr_dcmf3_status.attr,
+ &dev_attr_size0.attr,
+ &dev_attr_size1.attr,
+ &dev_attr_size2.attr,
+ &dev_attr_size3.attr,
+ &dev_attr_erase_size0.attr,
+ &dev_attr_erase_size1.attr,
+ &dev_attr_erase_size2.attr,
+ &dev_attr_erase_size3.attr,
&dev_attr_reboot_image.attr,
&dev_attr_notify.attr,
&dev_attr_spt0_address.attr,
@@ -795,7 +1108,12 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS;
- /* spt0/1_address and status fields default to 0 from kzalloc */
+ priv->max_retry = INVALID_RETRY_COUNTER;
+ priv->spt0_address = INVALID_SPT_ADDRESS;
+ priv->spt1_address = INVALID_SPT_ADDRESS;
+ priv->get_spt_response_buf = NULL;
+ priv->async = false;
+ rsu_device_info_invalidate(priv);
mutex_init(&priv->lock);
init_completion(&priv->completion);
@@ -809,19 +1127,39 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
}
ret = stratix10_svc_add_async_client(priv->chan, false);
- if (ret) {
- dev_err(dev, "failed to add async client\n");
- goto free_channel;
+ if (ret < 0) {
+ dev_dbg(dev, "Async operations not supported, fallback to non-async mode\n");
+ priv->async = false;
+ } else {
+ priv->async = true;
}
platform_set_drvdata(pdev, priv);
/* get the initial state from firmware */
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
- rsu_async_status_callback);
+ if (priv->async)
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0,
+ rsu_async_status_callback);
+ else
+ ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0,
+ rsu_status_callback);
if (ret) {
dev_err(dev, "Error, getting RSU status %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
+ }
+
+ /*
+ * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS;
+ * issue it separately only in synchronous mode.
+ */
+ if (!priv->async) {
+ ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback);
+ if (ret) {
+ dev_err(dev, "Error, getting RSU retry %i\n", ret);
+ goto free_channel;
+ }
}
/* get DCMF version from firmware */
@@ -829,28 +1167,64 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
rsu_dcmf_version_callback);
if (ret) {
dev_err(dev, "Error, getting DCMF version %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
}
ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0,
rsu_dcmf_status_callback);
if (ret) {
dev_err(dev, "Error, getting DCMF status %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
}
ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0,
rsu_max_retry_callback);
if (ret) {
dev_err(dev, "Error, getting RSU max retry %i\n", ret);
- goto remove_async_client;
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
+ }
+
+ /* get QSPI device info from firmware */
+ ret = rsu_send_msg(priv, COMMAND_RSU_GET_DEVICE_INFO, 0,
+ rsu_get_device_info_callback);
+ if (ret) {
+ dev_err(dev, "Error, getting QSPI Device Info %i\n", ret);
+ if (priv->async)
+ goto remove_async_client;
+ goto free_channel;
}
- ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0,
- rsu_async_get_spt_table_callback);
+ if (priv->async) {
+ ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE,
+ 0, rsu_async_get_spt_table_callback);
+ } else {
+ priv->get_spt_response_buf =
+ stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN);
+ if (IS_ERR(priv->get_spt_response_buf)) {
+ ret = PTR_ERR(priv->get_spt_response_buf);
+ priv->get_spt_response_buf = NULL;
+ dev_err(dev, "failed to allocate get spt buffer\n");
+ } else {
+ ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD,
+ RSU_GET_SPT_CMD, rsu_get_spt_callback);
+ }
+ }
if (ret) {
dev_err(dev, "Error, getting SPT table %i\n", ret);
- goto remove_async_client;
+ if (priv->async) {
+ goto remove_async_client;
+ } else if (!IS_ERR_OR_NULL(priv->get_spt_response_buf)) {
+ stratix10_svc_free_memory(priv->chan,
+ priv->get_spt_response_buf);
+ priv->get_spt_response_buf = NULL;
+ }
+ goto free_channel;
}
return 0;
@@ -866,6 +1240,9 @@ static void stratix10_rsu_remove(struct platform_device *pdev)
{
struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev);
+ if (priv->async)
+ stratix10_svc_remove_async_client(priv->chan);
+
stratix10_svc_free_channel(priv->chan);
}
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index 5e20057ee344..f8c2da207cb4 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include <linux/firmware/intel/stratix10-smc.h>
#include <linux/firmware/intel/stratix10-svc-client.h>
#include <linux/types.h>
@@ -45,6 +46,7 @@
/* stratix10 service layer clients */
#define STRATIX10_RSU "stratix10-rsu"
+#define SOCFPGA_HWMON "socfpga-hwmon"
/* Maximum number of SDM client IDs. */
#define MAX_SDM_CLIENT_IDS 16
@@ -104,9 +106,11 @@ struct stratix10_svc_chan;
/**
* struct stratix10_svc - svc private data
* @stratix10_svc_rsu: pointer to stratix10 RSU device
+ * @stratix10_svc_hwmon: pointer to stratix10 HWMON device
*/
struct stratix10_svc {
struct platform_device *stratix10_svc_rsu;
+ struct platform_device *stratix10_svc_hwmon;
};
/**
@@ -445,13 +449,15 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl,
* svc_thread_recv_status_ok() - handle the successful status
* @p_data: pointer to service data structure
* @cb_data: pointer to callback data structure to service client
- * @res: result from SMC or HVC call
+ * @res: result from SMC or HVC call (a0-a3; used for routing and most commands)
+ * @res12: full v1.2 result for %COMMAND_RSU_GET_DEVICE_INFO, else NULL
*
* Send back the correspond status to the service clients.
*/
static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
struct stratix10_svc_cb_data *cb_data,
- struct arm_smccc_res res)
+ struct arm_smccc_res res,
+ struct arm_smccc_1_2_regs *res12)
{
cb_data->kaddr1 = NULL;
cb_data->kaddr2 = NULL;
@@ -513,6 +519,16 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
res.a2 = res.a2 * BYTE_TO_WORD_SIZE;
cb_data->kaddr2 = &res.a2;
break;
+ case COMMAND_RSU_GET_DEVICE_INFO:
+ if (WARN_ON(!res12)) {
+ cb_data->status = BIT(SVC_STATUS_ERROR);
+ break;
+ }
+ cb_data->status = BIT(SVC_STATUS_OK);
+ cb_data->kaddr1 = res12;
+ cb_data->kaddr2 = NULL;
+ cb_data->kaddr3 = NULL;
+ break;
default:
pr_warn("it shouldn't happen\n");
break;
@@ -522,6 +538,10 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data);
}
+static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl,
+ const struct arm_smccc_1_2_regs *args,
+ struct arm_smccc_1_2_regs *res);
+
/**
* svc_normal_to_secure_thread() - the function to run in the kthread
* @data: data pointer for kthread function
@@ -539,6 +559,7 @@ static int svc_normal_to_secure_thread(void *data)
struct stratix10_svc_data *pdata = NULL;
struct stratix10_svc_cb_data *cbdata = NULL;
struct arm_smccc_res res;
+ struct arm_smccc_1_2_regs res12 = { 0 };
unsigned long a0, a1, a2, a3, a4, a5, a6, a7;
int ret_fifo = 0;
@@ -727,6 +748,16 @@ static int svc_normal_to_secure_thread(void *data)
a5 = (unsigned long)pdata->paddr_output;
a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE;
break;
+ case COMMAND_RSU_GET_DEVICE_INFO:
+ a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO;
+ a1 = 0;
+ a2 = 0;
+ a3 = 0;
+ a4 = 0;
+ a5 = 0;
+ a6 = 0;
+ a7 = 0;
+ break;
default:
pr_warn("it shouldn't happen\n");
mutex_unlock(&ctrl->sdm_lock);
@@ -740,7 +771,18 @@ static int svc_normal_to_secure_thread(void *data)
pr_debug(" a3=0x%016x\n", (unsigned int)a3);
pr_debug(" a4=0x%016x\n", (unsigned int)a4);
pr_debug(" a5=0x%016x\n", (unsigned int)a5);
- ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
+ if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) {
+ struct arm_smccc_1_2_regs args12 = { 0 };
+
+ args12.a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO;
+ svc_smccc_1_2_full(ctrl, &args12, &res12);
+ res.a0 = res12.a0;
+ res.a1 = res12.a1;
+ res.a2 = res12.a2;
+ res.a3 = res12.a3;
+ } else {
+ ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
+ }
pr_debug("%s: %s: after SMC call -- res.a0=0x%016x",
__func__, chan->name, (unsigned int)res.a0);
@@ -763,9 +805,15 @@ static int svc_normal_to_secure_thread(void *data)
}
switch (res.a0) {
- case INTEL_SIP_SMC_STATUS_OK:
- svc_thread_recv_status_ok(pdata, cbdata, res);
+ case INTEL_SIP_SMC_STATUS_OK: {
+ struct arm_smccc_1_2_regs *devinfo_res =
+ (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) ?
+ &res12 : NULL;
+
+ svc_thread_recv_status_ok(pdata, cbdata, res,
+ devinfo_res);
break;
+ }
case INTEL_SIP_SMC_STATUS_BUSY:
switch (pdata->command) {
case COMMAND_RECONFIG_DATA_SUBMIT:
@@ -806,10 +854,16 @@ static int svc_normal_to_secure_thread(void *data)
case INTEL_SIP_SMC_RSU_ERROR:
pr_err("%s: STATUS_ERROR\n", __func__);
cbdata->status = BIT(SVC_STATUS_ERROR);
- cbdata->kaddr1 = &res.a1;
- cbdata->kaddr2 = (res.a2) ?
- svc_pa_to_va(res.a2) : NULL;
- cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL;
+ if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) {
+ cbdata->kaddr1 = &res12;
+ cbdata->kaddr2 = NULL;
+ cbdata->kaddr3 = NULL;
+ } else {
+ cbdata->kaddr1 = &res.a1;
+ cbdata->kaddr2 = (res.a2) ?
+ svc_pa_to_va(res.a2) : NULL;
+ cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL;
+ }
pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata);
break;
default:
@@ -1026,6 +1080,31 @@ static void svc_smccc_hvc(unsigned long a0, unsigned long a1,
}
/**
+ * svc_smccc_1_2_full() - SMC/HVC v1.2 call matching the sync channel method
+ * @ctrl: service controller (selects SMC vs HVC)
+ * @args: arguments
+ * @res: full register-file result (a0-a17)
+ */
+static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl,
+ const struct arm_smccc_1_2_regs *args,
+ struct arm_smccc_1_2_regs *res)
+{
+ if (ctrl->invoke_fn == svc_smccc_smc) {
+ arm_smccc_1_2_smc(args, res);
+ } else if (ctrl->invoke_fn == svc_smccc_hvc) {
+ arm_smccc_1_2_hvc(args, res);
+ } else {
+ WARN_ON_ONCE(1);
+ /*
+ * INTEL_SIP_SMC_STATUS_OK is 0; zero-filled res would be misrouted
+ * as success. Force an error path and clear fabricated payload.
+ */
+ memset(res, 0, sizeof(*res));
+ res->a0 = INTEL_SIP_SMC_STATUS_ERROR;
+ }
+}
+
+/**
* get_invoke_func() - invoke SMC or HVC call
* @dev: pointer to device
*
@@ -1329,6 +1408,14 @@ int stratix10_svc_async_send(struct stratix10_svc_chan *chan, void *msg,
args.a0 = INTEL_SIP_SMC_ASYNC_RSU_NOTIFY;
args.a2 = p_msg->arg[0];
break;
+ case COMMAND_HWMON_READTEMP:
+ args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READTEMP;
+ args.a2 = p_msg->arg[0];
+ break;
+ case COMMAND_HWMON_READVOLT:
+ args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READVOLT;
+ args.a2 = p_msg->arg[0];
+ break;
default:
dev_err(ctrl->dev, "Invalid command ,%d\n", p_msg->command);
ret = -EINVAL;
@@ -1422,6 +1509,10 @@ static int stratix10_svc_async_prepare_response(struct stratix10_svc_chan *chan,
*/
data->kaddr1 = (void *)&handle->res;
break;
+ case COMMAND_HWMON_READTEMP:
+ case COMMAND_HWMON_READVOLT:
+ data->kaddr1 = (void *)&handle->res.a2;
+ break;
default:
dev_alert(ctrl->dev, "Invalid command\n ,%d", p_msg->command);
@@ -2016,16 +2107,38 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
if (ret)
goto err_put_device;
+ if (IS_ENABLED(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON)) {
+ svc->stratix10_svc_hwmon =
+ platform_device_alloc(SOCFPGA_HWMON, 0);
+ if (!svc->stratix10_svc_hwmon) {
+ dev_err(dev, "failed to allocate %s device\n",
+ SOCFPGA_HWMON);
+ } else {
+ svc->stratix10_svc_hwmon->dev.parent = dev;
+
+ ret = platform_device_add(svc->stratix10_svc_hwmon);
+ if (ret) {
+ dev_err(dev, "failed to add %s device: %d\n",
+ SOCFPGA_HWMON, ret);
+ platform_device_put(svc->stratix10_svc_hwmon);
+ svc->stratix10_svc_hwmon = NULL;
+ }
+ }
+ }
+
ret = of_platform_default_populate(dev_of_node(dev), NULL, dev);
if (ret)
- goto err_unregister_rsu_dev;
+ goto err_unregister_clients;
pr_info("Intel Service Layer Driver Initialized\n");
return 0;
-err_unregister_rsu_dev:
- platform_device_unregister(svc->stratix10_svc_rsu);
+err_unregister_clients:
+ if (svc->stratix10_svc_hwmon)
+ platform_device_unregister(svc->stratix10_svc_hwmon);
+ if (svc->stratix10_svc_rsu)
+ platform_device_unregister(svc->stratix10_svc_rsu);
goto err_free_fifos;
err_put_device:
platform_device_put(svc->stratix10_svc_rsu);
@@ -2050,6 +2163,8 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev)
struct stratix10_svc *svc = ctrl->svc;
platform_device_unregister(svc->stratix10_svc_rsu);
+ if (svc->stratix10_svc_hwmon)
+ platform_device_unregister(svc->stratix10_svc_hwmon);
stratix10_svc_async_exit(ctrl);
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 44badfd11e1b..070c025ee215 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sizes.h>
+#include <linux/string.h>
#define CVP_BAR 0 /* BAR used for data transfer in memory mode */
#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */
@@ -261,7 +262,7 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr,
static int altera_cvp_send_block(struct altera_cvp_conf *conf,
const u32 *data, size_t len)
{
- u32 mask, words = len / sizeof(u32);
+ u32 words = len / sizeof(u32);
int i, remainder;
for (i = 0; i < words; i++)
@@ -270,9 +271,10 @@ static int altera_cvp_send_block(struct altera_cvp_conf *conf,
/* write up to 3 trailing bytes, if any */
remainder = len % sizeof(u32);
if (remainder) {
- mask = BIT(remainder * 8) - 1;
- if (mask)
- conf->write_data(conf, *data & mask);
+ u32 word = 0;
+
+ memcpy(&word, data, remainder);
+ conf->write_data(conf, word);
}
return 0;
diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c
index 7aa4983ab67d..495edc448b68 100644
--- a/drivers/fpga/dfl-fme-perf.c
+++ b/drivers/fpga/dfl-fme-perf.c
@@ -925,6 +925,8 @@ static int fme_perf_pmu_register(struct platform_device *pdev,
PERF_PMU_CAP_NO_EXCLUDE;
name = devm_kasprintf(priv->dev, GFP_KERNEL, "dfl_fme%d", pdev->id);
+ if (!name)
+ return -ENOMEM;
ret = perf_pmu_register(pmu, name, -1);
if (ret)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 0a295ccf1644..b8ec2e6f615f 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
&ctype, sizeof(ctype));
if (ret < 0)
- goto init_done;
+ goto init_error;
- ret = wait_for_completion_timeout(
- &priv->status_return_completion, S10_RECONFIG_TIMEOUT);
- if (!ret) {
+ if (!wait_for_completion_timeout(&priv->status_return_completion,
+ S10_RECONFIG_TIMEOUT)) {
dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
- ret = 0;
if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
ret = -ETIMEDOUT;
- goto init_done;
+ goto init_error;
}
/* Allocate buffers from the service layer's pool. */
@@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
if (IS_ERR(kbuf)) {
s10_free_buffers(mgr);
ret = PTR_ERR(kbuf);
- goto init_done;
+ goto init_error;
}
priv->svc_bufs[i].buf = kbuf;
priv->svc_bufs[i].lock = 0;
}
-init_done:
+ return 0;
+
+init_error:
stratix10_svc_done(priv->chan);
return ret;
}
@@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
if (!s10_free_buffers(mgr))
dev_err(dev, "%s not all buffers were freed\n", __func__);
+ if (ret < 0)
+ stratix10_svc_done(priv->chan);
+
return ret;
}
diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
index 6994d68e9036..45b65a3264af 100644
--- a/drivers/fpga/xilinx-pr-decoupler.c
+++ b/drivers/fpga/xilinx-pr-decoupler.c
@@ -118,46 +118,30 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
- priv->clk = devm_clk_get(&pdev->dev, "aclk");
+ priv->clk = devm_clk_get_prepared(&pdev->dev, "aclk");
if (IS_ERR(priv->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
"input clock not found\n");
- err = clk_prepare_enable(priv->clk);
- if (err) {
- dev_err(&pdev->dev, "unable to enable clock\n");
- return err;
- }
-
- clk_disable(priv->clk);
-
br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
&xlnx_pr_decoupler_br_ops, priv);
if (IS_ERR(br)) {
err = PTR_ERR(br);
dev_err(&pdev->dev, "unable to register %s",
priv->ipconfig->name);
- goto err_clk;
+ return err;
}
platform_set_drvdata(pdev, br);
return 0;
-
-err_clk:
- clk_unprepare(priv->clk);
-
- return err;
}
static void xlnx_pr_decoupler_remove(struct platform_device *pdev)
{
struct fpga_bridge *bridge = platform_get_drvdata(pdev);
- struct xlnx_pr_decoupler_data *p = bridge->priv;
fpga_bridge_unregister(bridge);
-
- clk_unprepare(p->clk);
}
static struct platform_driver xlnx_pr_decoupler_driver = {
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 9d1d599ef718..2f8ba32e4002 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -598,7 +598,6 @@ static int zynq_fpga_probe(struct platform_device *pdev)
err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0, dev_name(dev),
priv);
if (err) {
- dev_err(dev, "unable to request IRQ\n");
clk_disable_unprepare(priv->clk);
return err;
}
diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.c b/drivers/gpib/agilent_82350b/agilent_82350b.c
index 9787c09faad8..490d44ad1b50 100644
--- a/drivers/gpib/agilent_82350b/agilent_82350b.c
+++ b/drivers/gpib/agilent_82350b/agilent_82350b.c
@@ -839,11 +839,10 @@ static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device
}
static const struct pci_device_id agilent_82350b_pci_table[] = {
- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP,
- PCI_SUBDEVICE_ID_82350A, 0, 0, 0 },
- { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { 0 }
+ { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, PCI_SUBDEVICE_ID_82350A) },
+ { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82350B) },
+ { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82351A) },
+ { }
};
MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table);
diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c
index 6dd8637c5964..c62c30fc3472 100644
--- a/drivers/gpib/cb7210/cb7210.c
+++ b/drivers/gpib/cb7210/cb7210.c
@@ -1093,10 +1093,10 @@ static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
static const struct pci_device_id cb7210_pci_table[] = {
- {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { 0 }
+ { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB) },
+ { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB) },
+ { PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB) },
+ { }
};
MODULE_DEVICE_TABLE(pci, cb7210_pci_table);
diff --git a/drivers/gpib/cec/cec_gpib.c b/drivers/gpib/cec/cec_gpib.c
index c13bc302d9e9..530a777b1384 100644
--- a/drivers/gpib/cec/cec_gpib.c
+++ b/drivers/gpib/cec/cec_gpib.c
@@ -353,8 +353,8 @@ static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
static const struct pci_device_id cec_pci_table[] = {
- {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 },
- {0}
+ { PCI_DEVICE_SUB(CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID) },
+ { }
};
MODULE_DEVICE_TABLE(pci, cec_pci_table);
diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c
index 69f6aa73ab9a..384800e6bf97 100644
--- a/drivers/gpib/common/gpib_os.c
+++ b/drivers/gpib/common/gpib_os.c
@@ -289,18 +289,19 @@ int autopoll_all_devices(struct gpib_board *board)
dev_dbg(board->gpib_dev, "autopoll has board lock\n");
retval = serial_poll_all(board, serial_timeout);
- if (retval < 0) {
- mutex_unlock(&board->big_gpib_mutex);
- mutex_unlock(&board->user_mutex);
- return retval;
+ if (retval >= 0) {
+ dev_dbg(board->gpib_dev, "complete\n");
+ /*
+ * need to wake wait queue in case someone is
+ * waiting on RQS
+ */
+ wake_up_interruptible(&board->wait);
}
- dev_dbg(board->gpib_dev, "complete\n");
- /*
- * need to wake wait queue in case someone is
- * waiting on RQS
- */
- wake_up_interruptible(&board->wait);
+ if (retval <= 0) {
+ atomic_set(&board->stuck_srq, 1);
+ set_bit(SRQI_NUM, &board->status);
+ }
mutex_unlock(&board->big_gpib_mutex);
mutex_unlock(&board->user_mutex);
diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c
index b672dd6aad25..511e1d61c1fb 100644
--- a/drivers/gpib/common/iblib.c
+++ b/drivers/gpib/common/iblib.c
@@ -193,9 +193,6 @@ static int autospoll_thread(void *board_void)
}
if (retval <= 0) {
dev_err(board->gpib_dev, "stuck SRQ\n");
-
- atomic_set(&board->stuck_srq, 1); // XXX could be better
- set_bit(SRQI_NUM, &board->status);
}
}
return retval;
diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c
index fcafdc02ea2e..5e10e9353fed 100644
--- a/drivers/gpib/fmh_gpib/fmh_gpib.c
+++ b/drivers/gpib/fmh_gpib/fmh_gpib.c
@@ -1338,7 +1338,7 @@ static int fmh_gpib_init(struct fmh_priv *e_priv, struct gpib_board *board, int
write_byte(nec_priv, AUX_LO_SPEED, AUXMR);
nec7210_set_handshake_mode(board, nec_priv, handshake_mode);
- /* Hueristically check if hardware supports fifo half full/empty interrupts */
+ /* Heuristically check if hardware supports fifo half full/empty interrupts */
fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG);
e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) &&
(fifo_status_bits & TX_FIFO_HALF_EMPTY);
diff --git a/drivers/gpib/hp_82341/hp_82341.c b/drivers/gpib/hp_82341/hp_82341.c
index 46175ba2ac36..e98cf5aea99f 100644
--- a/drivers/gpib/hp_82341/hp_82341.c
+++ b/drivers/gpib/hp_82341/hp_82341.c
@@ -803,8 +803,8 @@ static void hp_82341_detach(struct gpib_board *board)
#if 0
/* unused, will be needed when the driver is turned into a pnp_driver */
static const struct pnp_device_id hp_82341_pnp_table[] = {
- {.id = "HWP1411"},
- {.id = ""}
+ { .id = "HWP1411" },
+ { }
};
MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table);
#endif
diff --git a/drivers/gpib/include/amccs5933.h b/drivers/gpib/include/amccs5933.h
index d7f63c795096..f8a80bdc70dc 100644
--- a/drivers/gpib/include/amccs5933.h
+++ b/drivers/gpib/include/amccs5933.h
@@ -13,7 +13,7 @@ enum {
};
// incoming mailbox 0-3 register offsets
-extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox)
+static inline int INCOMING_MAILBOX_REG(unsigned int mailbox)
{
return (0x10 + 4 * mailbox);
};
@@ -29,25 +29,25 @@ enum {
};
// select byte 0 to 3 of incoming mailbox
-extern inline int INBOX_BYTE_BITS(unsigned int byte)
+static inline int INBOX_BYTE_BITS(unsigned int byte)
{
return (byte & 0x3) << 8;
};
// select incoming mailbox 0 to 3
-extern inline int INBOX_SELECT_BITS(unsigned int mailbox)
+static inline int INBOX_SELECT_BITS(unsigned int mailbox)
{
return (mailbox & 0x3) << 10;
};
// select byte 0 to 3 of outgoing mailbox
-extern inline int OUTBOX_BYTE_BITS(unsigned int byte)
+static inline int OUTBOX_BYTE_BITS(unsigned int byte)
{
return (byte & 0x3);
};
// select outgoing mailbox 0 to 3
-extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox)
+static inline int OUTBOX_SELECT_BITS(unsigned int mailbox)
{
return (mailbox & 0x3) << 2;
};
diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c
index 3562f3184c28..6cd6ff596fda 100644
--- a/drivers/gpib/ines/ines_gpib.c
+++ b/drivers/gpib/ines/ines_gpib.c
@@ -371,14 +371,19 @@ enum ines_pci_subdevice_ids {
};
static struct pci_device_id ines_pci_table[] = {
- {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX,
- PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0},
- {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC,
- PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0},
- {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030,
- PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0},
- {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)},
- {0}
+ {
+ PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050,
+ PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_INES_GPIB),
+ }, {
+ PCI_VDEVICE_SUB(AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC,
+ PCI_VENDOR_ID_AMCC, PCI_SUBDEVICE_ID_INES_GPIB),
+ }, {
+ PCI_VDEVICE_SUB(INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030,
+ PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030),
+ }, {
+ PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB),
+ },
+ { }
};
MODULE_DEVICE_TABLE(pci, ines_pci_table);
diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
index e6ea9422d6f2..dfb8f3ebc27f 100644
--- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
+++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
@@ -68,7 +68,7 @@ MODULE_DEVICE_TABLE(usb, lpvo_table);
* (about twice the log volume of [1])
* To switch debug level:
* At module loading: modprobe lpvo_usb_gpib debug={0,1,2}
- * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug
+ * On the fly: echo {0,1,2} > /sys/module/lpvo_usb_gpib/parameters/debug
*/
static int debug;
@@ -1811,14 +1811,9 @@ static ssize_t lpvo_write(struct file *file, const char __user *user_buffer,
dev = file->private_data;
- buf = kmalloc(count, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, user_buffer, count)) {
- kfree(buf);
- return -EFAULT;
- }
+ buf = memdup_user(user_buffer, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
rv = lpvo_do_write(dev, buf, count);
kfree(buf);
diff --git a/drivers/gpib/tnt4882/mite.h b/drivers/gpib/tnt4882/mite.h
index a1fdba9672a0..dd251afa90e3 100644
--- a/drivers/gpib/tnt4882/mite.h
+++ b/drivers/gpib/tnt4882/mite.h
@@ -45,12 +45,12 @@ struct mite_struct {
extern struct mite_struct *mite_devices;
-extern inline unsigned int mite_irq(struct mite_struct *mite)
+static inline unsigned int mite_irq(struct mite_struct *mite)
{
return mite->pcidev->irq;
};
-extern inline unsigned int mite_device_id(struct mite_struct *mite)
+static inline unsigned int mite_device_id(struct mite_struct *mite)
{
return mite->pcidev->device;
};
diff --git a/drivers/gpib/tnt4882/tnt4882_gpib.c b/drivers/gpib/tnt4882/tnt4882_gpib.c
index 51a920e1d9a4..3cd13f637ed4 100644
--- a/drivers/gpib/tnt4882/tnt4882_gpib.c
+++ b/drivers/gpib/tnt4882/tnt4882_gpib.c
@@ -1373,8 +1373,8 @@ static struct pci_driver tnt4882_pci_driver = {
#if 0
/* unused, will be needed when the driver is turned into a pnp_driver */
static const struct pnp_device_id tnt4882_pnp_table[] = {
- {.id = "NICC601"},
- {.id = ""}
+ { .id = "NICC601" },
+ { }
};
MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table);
#endif
diff --git a/drivers/greybus/manifest.c b/drivers/greybus/manifest.c
index 9be5d95da587..3b685ace2036 100644
--- a/drivers/greybus/manifest.c
+++ b/drivers/greybus/manifest.c
@@ -122,6 +122,8 @@ static int identify_descriptor(struct gb_interface *intf,
switch (desc_header->type) {
case GREYBUS_TYPE_STRING:
expected_size += sizeof(struct greybus_descriptor_string);
+ if (desc_size < expected_size)
+ break;
expected_size += desc->string.length;
/* String descriptors are padded to 4 byte boundaries */
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 942282614c48..fecff8610ea8 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1993,7 +1993,7 @@ config SENSORS_SL28CPLD
config SENSORS_SBTSI
tristate "Emulated SB-TSI temperature sensor"
- depends on I2C
+ depends on AMD_SBTSI
help
If you say yes here you get support for emulated temperature
sensors on AMD SoCs with SB-TSI interface connected to a BMC device.
@@ -2189,6 +2189,16 @@ config SENSORS_SMSC47M192
This driver can also be built as a module. If so, the module
will be called smsc47m192.
+config SENSORS_ALTERA_SOCFPGA_HWMON
+ tristate "Altera SoC FPGA hardware monitoring features"
+ depends on INTEL_STRATIX10_SERVICE
+ help
+ If you say yes here you get support for the temperature and
+ voltage sensors of Altera SoC FPGA devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called socfpga-hwmon.
+
config SENSORS_SMSC47B397
tristate "SMSC LPC47B397-NC"
depends on HAS_IOPORT
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 0cad7e21634c..1229b6b3996d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -224,6 +224,7 @@ obj-$(CONFIG_SENSORS_SMPRO) += smpro-hwmon.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+obj-$(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON) += socfpga-hwmon.o
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
index fc17ad93a376..6502e20517a8 100644
--- a/drivers/hwmon/iio_hwmon.c
+++ b/drivers/hwmon/iio_hwmon.c
@@ -221,3 +221,4 @@ module_platform_driver(iio_hwmon_driver);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("IIO to hwmon driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index 1ac0288fbbd8..4fa7f522f245 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -748,3 +748,4 @@ MODULE_DESCRIPTION("NTC Thermistor Driver");
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ntc-thermistor");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c
index c28f8625cd3a..11c8108d69b2 100644
--- a/drivers/hwmon/sbtsi_temp.c
+++ b/drivers/hwmon/sbtsi_temp.c
@@ -7,13 +7,12 @@
* Copyright (c) 2020, Kun Yi <kunyi@google.com>
*/
+#include <linux/auxiliary_bus.h>
#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/hwmon.h>
+#include <linux/init.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/bitfield.h>
+#include <linux/misc/tsi.h>
/*
* SB-TSI registers only support SMBus byte data access. "_INT" registers are
@@ -22,39 +21,17 @@
*/
#define SBTSI_REG_TEMP_INT 0x01 /* RO */
#define SBTSI_REG_STATUS 0x02 /* RO */
-#define SBTSI_REG_CONFIG 0x03 /* RO */
#define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */
#define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */
#define SBTSI_REG_TEMP_DEC 0x10 /* RW */
#define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */
#define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */
-/*
- * Bit for reporting value with temperature measurement range.
- * bit == 0: Use default temperature range (0C to 255.875C).
- * bit == 1: Use extended temperature range (-49C to +206.875C).
- */
-#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2
-/*
- * ReadOrder bit specifies the reading order of integer and decimal part of
- * CPU temperature for atomic reads. If bit == 0, reading integer part triggers
- * latching of the decimal part, so integer part should be read first.
- * If bit == 1, read order should be reversed.
- */
-#define SBTSI_CONFIG_READ_ORDER_SHIFT 5
-
#define SBTSI_TEMP_EXT_RANGE_ADJ 49000
#define SBTSI_TEMP_MIN 0
#define SBTSI_TEMP_MAX 255875
-/* Each client has this additional data */
-struct sbtsi_data {
- struct i2c_client *client;
- bool ext_range_mode;
- bool read_order;
-};
-
/*
* From SB-TSI spec: CPU temperature readings and limit registers encode the
* temperature in increments of 0.125 from 0 to 255.875. The "high byte"
@@ -84,40 +61,79 @@ static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal)
*decimal = (temp & 0x7) << 5;
}
+/*
+ * Read integer and decimal parts of an SB-TSI temperature register pair
+ * The read order is determined by the ReadOrder bit to ensure atomic latching.
+ */
+static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2,
+ u8 *val1, u8 *val2)
+{
+ int ret;
+
+ guard(sbtsi)(data);
+ ret = sbtsi_xfer(data, reg1, val1, true);
+ if (!ret)
+ ret = sbtsi_xfer(data, reg2, val2, true);
+ return ret;
+}
+
+/*
+ * Write integer and decimal parts of an SB-TSI temperature register pair.
+ */
+static int sbtsi_temp_write(struct sbtsi_data *data, u8 reg_int, u8 reg_dec,
+ u8 val_int, u8 val_dec)
+{
+ int ret;
+
+ guard(sbtsi)(data);
+ ret = sbtsi_xfer(data, reg_int, &val_int, false);
+ if (!ret)
+ ret = sbtsi_xfer(data, reg_dec, &val_dec, false);
+ return ret;
+}
+
static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct sbtsi_data *data = dev_get_drvdata(dev);
s32 temp_int, temp_dec;
+ int err;
+ u8 val_int, val_dec;
switch (attr) {
case hwmon_temp_input:
- if (data->read_order) {
- temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
- temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
- } else {
- temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
- temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
- }
+ if (data->read_order)
+ err = sbtsi_temp_read(data,
+ SBTSI_REG_TEMP_DEC, SBTSI_REG_TEMP_INT,
+ &val_dec, &val_int);
+ else
+ err = sbtsi_temp_read(data,
+ SBTSI_REG_TEMP_INT, SBTSI_REG_TEMP_DEC,
+ &val_int, &val_dec);
+ if (err < 0)
+ return err;
break;
case hwmon_temp_max:
- temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT);
- temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC);
+ err = sbtsi_temp_read(data,
+ SBTSI_REG_TEMP_HIGH_INT, SBTSI_REG_TEMP_HIGH_DEC,
+ &val_int, &val_dec);
+ if (err < 0)
+ return err;
break;
case hwmon_temp_min:
- temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT);
- temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC);
+ err = sbtsi_temp_read(data,
+ SBTSI_REG_TEMP_LOW_INT, SBTSI_REG_TEMP_LOW_DEC,
+ &val_int, &val_dec);
+
+ if (err < 0)
+ return err;
break;
default:
return -EINVAL;
}
-
- if (temp_int < 0)
- return temp_int;
- if (temp_dec < 0)
- return temp_dec;
-
+ temp_int = val_int;
+ temp_dec = val_dec;
*val = sbtsi_reg_to_mc(temp_int, temp_dec);
if (data->ext_range_mode)
*val -= SBTSI_TEMP_EXT_RANGE_ADJ;
@@ -129,7 +145,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct sbtsi_data *data = dev_get_drvdata(dev);
- int reg_int, reg_dec, err;
+ int reg_int, reg_dec;
u8 temp_int, temp_dec;
switch (attr) {
@@ -150,11 +166,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type,
val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX);
sbtsi_mc_to_reg(val, &temp_int, &temp_dec);
- err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int);
- if (err)
- return err;
-
- return i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec);
+ return sbtsi_temp_write(data, reg_int, reg_dec, temp_int, temp_dec);
}
static umode_t sbtsi_is_visible(const void *data,
@@ -195,55 +207,33 @@ static const struct hwmon_chip_info sbtsi_chip_info = {
.info = sbtsi_info,
};
-static int sbtsi_probe(struct i2c_client *client)
+static int sbtsi_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
{
- struct device *dev = &client->dev;
+ struct sbtsi_data *data = dev_get_drvdata(adev->dev.parent);
+ struct device *dev = &adev->dev;
struct device *hwmon_dev;
- struct sbtsi_data *data;
- int err;
-
- data = devm_kzalloc(dev, sizeof(struct sbtsi_data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->client = client;
-
- err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG);
- if (err < 0)
- return err;
- data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), err);
- data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), err);
- hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbtsi", data,
&sbtsi_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
-static const struct i2c_device_id sbtsi_id[] = {
- { .name = "sbtsi" },
+static const struct auxiliary_device_id sbtsi_id[] = {
+ { .name = AMD_SBTSI_ADEV "." AMD_SBTSI_AUX_HWMON },
{ }
};
-MODULE_DEVICE_TABLE(i2c, sbtsi_id);
+MODULE_DEVICE_TABLE(auxiliary, sbtsi_id);
-static const struct of_device_id __maybe_unused sbtsi_of_match[] = {
- {
- .compatible = "amd,sbtsi",
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, sbtsi_of_match);
-
-static struct i2c_driver sbtsi_driver = {
+static struct auxiliary_driver sbtsi_driver = {
.driver = {
.name = "sbtsi",
- .of_match_table = of_match_ptr(sbtsi_of_match),
},
.probe = sbtsi_probe,
.id_table = sbtsi_id,
};
-
-module_i2c_driver(sbtsi_driver);
+module_auxiliary_driver(sbtsi_driver);
MODULE_AUTHOR("Kun Yi <kunyi@google.com>");
MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor");
diff --git a/drivers/hwmon/socfpga-hwmon.c b/drivers/hwmon/socfpga-hwmon.c
new file mode 100644
index 000000000000..5b43274d0aa2
--- /dev/null
+++ b/drivers/hwmon/socfpga-hwmon.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Altera SoC FPGA hardware monitoring driver
+ *
+ * Copyright (c) 2026 Altera Corporation
+ *
+ * Authors:
+ * Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
+ * Tze Yee Ng <tze.yee.ng@altera.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define HWMON_TIMEOUT msecs_to_jiffies(SVC_HWMON_REQUEST_TIMEOUT_MS)
+#define HWMON_RETRY_SLEEP_US 1000U
+#define HWMON_ASYNC_MSG_RETRY 3U
+#define SOCFPGA_HWMON_MAXSENSORS 16
+#define SOCFPGA_HWMON_CHANNEL_MASK GENMASK(15, 0)
+#define SOCFPGA_HWMON_PAGE_SHIFT 16
+#define SOCFPGA_HWMON_CHAN(page, channel) \
+ (((page) << SOCFPGA_HWMON_PAGE_SHIFT) | \
+ ((channel) & SOCFPGA_HWMON_CHANNEL_MASK))
+#define SOCFPGA_HWMON_ATTR_VISIBLE 0444
+/* Temperature from SDM is signed Q8.8 degrees Celsius (8 fractional bits). */
+#define SOCFPGA_HWMON_TEMP_FRAC_BITS 8
+#define SOCFPGA_HWMON_TEMP_FRAC_DIV BIT(SOCFPGA_HWMON_TEMP_FRAC_BITS)
+#define SOCFPGA_HWMON_TEMP_MDEG_SCALE 1000
+/* Voltage from SDM is unsigned Q16 volts (16 fractional bits). */
+#define SOCFPGA_HWMON_VOLT_FRAC_BITS 16
+#define SOCFPGA_HWMON_VOLT_FRAC_DIV BIT(SOCFPGA_HWMON_VOLT_FRAC_BITS)
+#define SOCFPGA_HWMON_VOLT_MV_SCALE 1000
+
+#define ETEMP_INACTIVE 0x80000000U
+#define ETEMP_TOO_OLD 0x80000001U
+#define ETEMP_NOT_PRESENT 0x80000002U
+#define ETEMP_TIMEOUT 0x80000003U
+#define ETEMP_CORRUPT 0x80000004U
+#define ETEMP_BUSY 0x80000005U
+#define ETEMP_NOT_INITIALIZED 0x800000FFU
+
+struct socfpga_hwmon_channel {
+ u32 reg;
+ const char *label;
+};
+
+struct socfpga_hwmon_board_data {
+ const struct socfpga_hwmon_channel *temp;
+ unsigned int num_temp;
+ const struct socfpga_hwmon_channel *volt;
+ unsigned int num_volt;
+};
+
+struct socfpga_hwmon_priv {
+ struct stratix10_svc_chan *chan;
+ struct stratix10_svc_client client;
+ struct completion completion;
+ struct mutex lock; /* protect SVC calls */
+ bool async;
+ int last_err; /* sync-mode SVC result; 0 on success */
+ u32 temperature;
+ u32 voltage;
+ int temperature_channels;
+ int voltage_channels;
+ const char *temp_chan_names[SOCFPGA_HWMON_MAXSENSORS];
+ const char *volt_chan_names[SOCFPGA_HWMON_MAXSENSORS];
+ u32 temp_chan[SOCFPGA_HWMON_MAXSENSORS];
+ u32 volt_chan[SOCFPGA_HWMON_MAXSENSORS];
+};
+
+static umode_t socfpga_hwmon_is_visible(const void *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int chan)
+{
+ const struct socfpga_hwmon_priv *priv = dev;
+
+ switch (type) {
+ case hwmon_temp:
+ if (chan < priv->temperature_channels)
+ return SOCFPGA_HWMON_ATTR_VISIBLE;
+ return 0;
+ case hwmon_in:
+ if (chan < priv->voltage_channels)
+ return SOCFPGA_HWMON_ATTR_VISIBLE;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static void socfpga_hwmon_readtemp_cb(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct socfpga_hwmon_priv *priv = client->priv;
+
+ priv->last_err = -EIO;
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ priv->last_err = 0;
+ priv->temperature = (u32)*(unsigned long *)data->kaddr1;
+ } else if (data->kaddr1) {
+ dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n",
+ __func__, data->status,
+ *(unsigned long *)data->kaddr1);
+ } else {
+ dev_err(client->dev, "%s failed with status 0x%x\n",
+ __func__, data->status);
+ }
+
+ complete(&priv->completion);
+}
+
+static void socfpga_hwmon_readvolt_cb(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct socfpga_hwmon_priv *priv = client->priv;
+
+ priv->last_err = -EIO;
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ priv->last_err = 0;
+ priv->voltage = (u32)*(unsigned long *)data->kaddr1;
+ } else if (data->kaddr1) {
+ dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n",
+ __func__, data->status,
+ *(unsigned long *)data->kaddr1);
+ } else {
+ dev_err(client->dev, "%s failed with status 0x%x\n",
+ __func__, data->status);
+ }
+
+ complete(&priv->completion);
+}
+
+static int socfpga_hwmon_parse_temp(long *val, u32 temperature)
+{
+ switch (temperature) {
+ case ETEMP_INACTIVE:
+ case ETEMP_NOT_PRESENT:
+ case ETEMP_CORRUPT:
+ case ETEMP_NOT_INITIALIZED:
+ return -EOPNOTSUPP;
+ case ETEMP_TIMEOUT:
+ case ETEMP_BUSY:
+ case ETEMP_TOO_OLD:
+ return -EAGAIN;
+ default:
+ /* SDM returns a 16-bit signed Q8.8 value in the low 16 bits. */
+ *val = (long)(s16)(temperature & SOCFPGA_HWMON_CHANNEL_MASK) *
+ SOCFPGA_HWMON_TEMP_MDEG_SCALE / SOCFPGA_HWMON_TEMP_FRAC_DIV;
+ return 0;
+ }
+}
+
+static int socfpga_hwmon_encode_temp_arg(u32 reg, u64 *arg)
+{
+ u32 page = (reg >> SOCFPGA_HWMON_PAGE_SHIFT) & SOCFPGA_HWMON_CHANNEL_MASK;
+ u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK;
+
+ if (channel >= SOCFPGA_HWMON_MAXSENSORS)
+ return -EINVAL;
+
+ *arg = (1ULL << channel) | ((u64)page << SOCFPGA_HWMON_PAGE_SHIFT);
+ return 0;
+}
+
+static int socfpga_hwmon_encode_volt_arg(u32 reg, u64 *arg)
+{
+ u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK;
+
+ if (channel >= SOCFPGA_HWMON_MAXSENSORS)
+ return -EINVAL;
+
+ *arg = 1ULL << channel;
+ return 0;
+}
+
+static int socfpga_hwmon_async_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ struct stratix10_svc_client_msg *msg)
+{
+ struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev);
+ struct stratix10_svc_cb_data data = {};
+ unsigned long deadline = jiffies + HWMON_TIMEOUT;
+ void *handle = NULL;
+ int status, index, ret;
+
+ for (index = 0; index < HWMON_ASYNC_MSG_RETRY; index++) {
+ status = stratix10_svc_async_send(priv->chan, msg, &handle,
+ NULL, NULL);
+ if (status == 0)
+ break;
+ dev_warn(dev, "Failed to send async message: %d\n", status);
+ usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2);
+ }
+
+ if (status && !handle) {
+ dev_err(dev, "Failed to send async message after %u retries: %d\n",
+ HWMON_ASYNC_MSG_RETRY, status);
+ return status;
+ }
+
+ ret = -ETIMEDOUT;
+ while (!time_after(jiffies, deadline)) {
+ status = stratix10_svc_async_poll(priv->chan, handle, &data);
+ if (status == -EAGAIN) {
+ /* still in progress */
+ } else if (status < 0) {
+ ret = status;
+ break;
+ } else if (status == 0) {
+ ret = 0;
+ break;
+ }
+ usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2);
+ }
+
+ if (ret) {
+ dev_err(dev, "Failed to get async response\n");
+ goto done;
+ }
+
+ if (data.status) {
+ dev_err(dev, "%s returned 0x%x from SDM\n", __func__,
+ data.status);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (type == hwmon_temp)
+ priv->temperature = (u32)*(unsigned long *)data.kaddr1;
+ else
+ priv->voltage = (u32)*(unsigned long *)data.kaddr1;
+
+ ret = 0;
+
+done:
+ stratix10_svc_async_done(priv->chan, handle);
+ return ret;
+}
+
+static int socfpga_hwmon_sync_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ struct stratix10_svc_client_msg *msg)
+{
+ struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ reinit_completion(&priv->completion);
+
+ if (type == hwmon_temp)
+ priv->client.receive_cb = socfpga_hwmon_readtemp_cb;
+ else
+ priv->client.receive_cb = socfpga_hwmon_readvolt_cb;
+
+ ret = stratix10_svc_send(priv->chan, msg);
+ if (ret < 0)
+ goto status_done;
+
+ ret = wait_for_completion_timeout(&priv->completion, HWMON_TIMEOUT);
+ if (!ret) {
+ dev_err(priv->client.dev, "timeout waiting for SMC call\n");
+ ret = -ETIMEDOUT;
+ goto status_done;
+ }
+
+ ret = priv->last_err;
+
+status_done:
+ stratix10_svc_done(priv->chan);
+ return ret;
+}
+
+static int socfpga_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int chan, long *val)
+{
+ struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev);
+ struct stratix10_svc_client_msg msg = {0};
+ int ret;
+
+ if (chan >= SOCFPGA_HWMON_MAXSENSORS)
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case hwmon_temp:
+ ret = socfpga_hwmon_encode_temp_arg(priv->temp_chan[chan],
+ &msg.arg[0]);
+ if (ret)
+ return ret;
+ msg.command = COMMAND_HWMON_READTEMP;
+ break;
+ case hwmon_in:
+ ret = socfpga_hwmon_encode_volt_arg(priv->volt_chan[chan],
+ &msg.arg[0]);
+ if (ret)
+ return ret;
+ msg.command = COMMAND_HWMON_READVOLT;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ guard(mutex)(&priv->lock);
+ if (priv->async)
+ ret = socfpga_hwmon_async_read(dev, type, &msg);
+ else
+ ret = socfpga_hwmon_sync_read(dev, type, &msg);
+ if (ret)
+ return ret;
+
+ if (type == hwmon_temp)
+ ret = socfpga_hwmon_parse_temp(val, priv->temperature);
+ else
+ /* SDM returns Q16 volts; convert to hwmon millivolts. */
+ *val = (long)priv->voltage * SOCFPGA_HWMON_VOLT_MV_SCALE /
+ SOCFPGA_HWMON_VOLT_FRAC_DIV;
+ return ret;
+}
+
+static int socfpga_hwmon_read_string(struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ int chan, const char **str)
+{
+ struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_in:
+ *str = priv->volt_chan_names[chan];
+ return 0;
+ case hwmon_temp:
+ *str = priv->temp_chan_names[chan];
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_ops socfpga_hwmon_ops = {
+ .is_visible = socfpga_hwmon_is_visible,
+ .read = socfpga_hwmon_read,
+ .read_string = socfpga_hwmon_read_string,
+};
+
+static const struct hwmon_channel_info *socfpga_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL),
+ NULL
+};
+
+static const struct hwmon_chip_info socfpga_hwmon_chip_info = {
+ .ops = &socfpga_hwmon_ops,
+ .info = socfpga_hwmon_info,
+};
+
+static const struct socfpga_hwmon_channel s10_hwmon_volt_channels[] = {
+ { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" },
+ { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" },
+ { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCERAM" },
+};
+
+static const struct socfpga_hwmon_channel s10_hwmon_temp_channels[] = {
+ { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" },
+};
+
+static const struct socfpga_hwmon_board_data s10_hwmon_board = {
+ .temp = s10_hwmon_temp_channels,
+ .num_temp = ARRAY_SIZE(s10_hwmon_temp_channels),
+ .volt = s10_hwmon_volt_channels,
+ .num_volt = ARRAY_SIZE(s10_hwmon_volt_channels),
+};
+
+static const struct socfpga_hwmon_channel agilex_hwmon_volt_channels[] = {
+ { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" },
+ { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" },
+ { SOCFPGA_HWMON_CHAN(0, 4), "1.8V VCCPT" },
+ { SOCFPGA_HWMON_CHAN(0, 5), "1.2V VCCCRCORE" },
+ { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCH" },
+ { SOCFPGA_HWMON_CHAN(0, 7), "0.8V VCCL" },
+};
+
+static const struct socfpga_hwmon_channel agilex_hwmon_temp_channels[] = {
+ { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" },
+ { SOCFPGA_HWMON_CHAN(1, 0), "Main Die corner bottom left max" },
+ { SOCFPGA_HWMON_CHAN(2, 0), "Main Die corner top left max" },
+ { SOCFPGA_HWMON_CHAN(3, 0), "Main Die corner bottom right max" },
+ { SOCFPGA_HWMON_CHAN(4, 0), "Main Die corner top right max" },
+};
+
+static const struct socfpga_hwmon_board_data agilex_hwmon_board = {
+ .temp = agilex_hwmon_temp_channels,
+ .num_temp = ARRAY_SIZE(agilex_hwmon_temp_channels),
+ .volt = agilex_hwmon_volt_channels,
+ .num_volt = ARRAY_SIZE(agilex_hwmon_volt_channels),
+};
+
+static const struct socfpga_hwmon_board_data *
+socfpga_hwmon_get_board(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return NULL;
+
+ if (of_device_is_compatible(np, "intel,stratix10-svc"))
+ return &s10_hwmon_board;
+ if (of_device_is_compatible(np, "intel,agilex-svc"))
+ return &agilex_hwmon_board;
+
+ return NULL;
+}
+
+static int socfpga_hwmon_init_channels(struct device *dev,
+ const struct socfpga_hwmon_board_data *board,
+ struct socfpga_hwmon_priv *priv)
+{
+ unsigned int i;
+
+ if (board->num_temp > SOCFPGA_HWMON_MAXSENSORS ||
+ board->num_volt > SOCFPGA_HWMON_MAXSENSORS)
+ return -EINVAL;
+
+ for (i = 0; i < board->num_temp; i++) {
+ priv->temp_chan_names[i] = board->temp[i].label;
+ priv->temp_chan[i] = board->temp[i].reg;
+ }
+ priv->temperature_channels = board->num_temp;
+
+ for (i = 0; i < board->num_volt; i++) {
+ priv->volt_chan_names[i] = board->volt[i].label;
+ priv->volt_chan[i] = board->volt[i].reg;
+ }
+ priv->voltage_channels = board->num_volt;
+
+ return 0;
+}
+
+static void socfpga_hwmon_release_svc(void *data)
+{
+ struct socfpga_hwmon_priv *priv = data;
+
+ if (priv->async)
+ stratix10_svc_remove_async_client(priv->chan);
+ stratix10_svc_free_channel(priv->chan);
+}
+
+static int socfpga_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+ const struct socfpga_hwmon_board_data *board;
+ struct socfpga_hwmon_priv *priv;
+ struct device *hwmon_dev;
+ int ret;
+
+ if (!parent || !parent->of_node) {
+ dev_err(dev, "missing parent device node\n");
+ return -ENODEV;
+ }
+
+ board = socfpga_hwmon_get_board(parent);
+ if (!board) {
+ dev_err(dev, "unsupported service layer compatible\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client.dev = dev;
+ priv->client.priv = priv;
+ init_completion(&priv->completion);
+ mutex_init(&priv->lock);
+
+ ret = socfpga_hwmon_init_channels(dev, board, priv);
+ if (ret)
+ return ret;
+
+ priv->chan = stratix10_svc_request_channel_byname(&priv->client,
+ SVC_CLIENT_HWMON);
+ if (IS_ERR(priv->chan)) {
+ ret = PTR_ERR(priv->chan);
+ if (ret == -EPROBE_DEFER)
+ dev_dbg(dev, "service channel %s not ready, deferring probe\n",
+ SVC_CLIENT_HWMON);
+ else
+ dev_err(dev, "couldn't get service channel %s: %d\n",
+ SVC_CLIENT_HWMON, ret);
+ return ret;
+ }
+
+ ret = stratix10_svc_add_async_client(priv->chan, false);
+ switch (ret) {
+ case 0:
+ priv->async = true;
+ break;
+ case -EINVAL:
+ case -EOPNOTSUPP:
+ /*
+ * stratix10_svc_add_async_client() returns -EINVAL when the
+ * async controller is not initialized; fall back to sync mode.
+ */
+ dev_dbg(dev, "async operations not supported, using sync mode\n");
+ priv->async = false;
+ break;
+ default:
+ dev_err(dev, "failed to add async client: %d\n", ret);
+ stratix10_svc_free_channel(priv->chan);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, socfpga_hwmon_release_svc, priv);
+ if (ret)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "socfpga_hwmon",
+ priv,
+ &socfpga_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ platform_set_drvdata(pdev, priv);
+ return 0;
+}
+
+static struct platform_driver socfpga_hwmon_driver = {
+ .probe = socfpga_hwmon_probe,
+ .driver = {
+ .name = "socfpga-hwmon",
+ },
+};
+module_platform_driver(socfpga_hwmon_driver);
+
+MODULE_AUTHOR("Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>");
+MODULE_AUTHOR("Tze Yee Ng <tze.yee.ng@altera.com>");
+MODULE_DESCRIPTION("Altera SoC FPGA hardware monitoring driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:socfpga-hwmon");
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 762109307b86..b3c67e96a82a 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -717,26 +717,19 @@ static DEVICE_ATTR_RW(cntr_rld_event);
static ssize_t cntr_val_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int i, ret = 0;
u32 val;
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etm_config *config = &drvdata->config;
if (!coresight_get_mode(drvdata->csdev)) {
spin_lock(&drvdata->spinlock);
- for (i = 0; i < drvdata->nr_cntr; i++)
- ret += sprintf(buf, "counter %d: %x\n",
- i, config->cntr_val[i]);
+ val = config->cntr_val[config->cntr_idx];
spin_unlock(&drvdata->spinlock);
- return ret;
- }
-
- for (i = 0; i < drvdata->nr_cntr; i++) {
- val = etm_readl(drvdata, ETMCNTVRn(i));
- ret += sprintf(buf, "counter %d: %x\n", i, val);
+ } else {
+ val = etm_readl(drvdata, ETMCNTVRn(config->cntr_idx));
}
- return ret;
+ return sysfs_emit(buf, "%#x\n", val);
}
static ssize_t cntr_val_store(struct device *dev,
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
index c302072b293a..e1a59b434505 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
@@ -76,7 +76,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
- if (idx < ETM_MAX_SEQ_STATES) {
+ if (idx < ETM_MAX_SEQ_TRANSITIONS) {
reg_csdev->driver_regval = &drvcfg->seq_ctrl[idx];
err = 0;
}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 14bb31bd6a0b..2247ad55d444 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -93,7 +93,7 @@ static int etm4_probe_cpu(unsigned int cpu);
static bool etm4x_sspcicrn_present(struct etmv4_drvdata *drvdata, int n)
{
return (n < drvdata->nr_ss_cmp) &&
- drvdata->nr_pe &&
+ drvdata->nr_pe_cmp &&
(drvdata->config.ss_status[n] & TRCSSCSRn_PC);
}
@@ -542,7 +542,8 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, config->vissctlr, TRCVISSCTLR);
if (drvdata->nr_pe_cmp)
etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR);
- for (i = 0; i < drvdata->nrseqstate - 1; i++)
+
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i));
if (drvdata->nrseqstate) {
etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR);
@@ -756,8 +757,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
.ATTR_CFG_FLD_timestamp_CFG = U64_MAX,
};
struct perf_event_attr *attr = &event->attr;
- unsigned long cfg_hash;
- int preset, cc_threshold;
+ int cc_threshold;
u8 ts_level;
/* Clear configuration from previous run */
@@ -843,16 +843,6 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
/* bit[12], Return stack enable bit */
config->cfg |= TRCCONFIGR_RS;
- /*
- * Set any selected configuration and preset. A zero configid means no
- * configuration active, preset = 0 means no preset selected.
- */
- 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_CFG_GET_FLD(attr, branch_broadcast)) {
if (!drvdata->trcbb) {
@@ -876,7 +866,9 @@ static int etm4_enable_perf(struct coresight_device *csdev,
struct coresight_path *path)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- int ret;
+ struct perf_event_attr *attr = &event->attr;
+ unsigned long cfg_hash;
+ int ret, preset;
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
return -EINVAL;
@@ -887,7 +879,19 @@ static int etm4_enable_perf(struct coresight_device *csdev,
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(csdev, event);
if (ret)
- goto out;
+ goto err;
+
+ /*
+ * Set any selected configuration and preset. A zero configid means no
+ * configuration active, preset = 0 means no preset selected.
+ */
+ 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);
+ if (ret)
+ goto err;
+ }
drvdata->trcid = path->trace_id;
@@ -896,16 +900,19 @@ static int etm4_enable_perf(struct coresight_device *csdev,
/* And enable it */
ret = etm4_enable_hw(drvdata);
-
-out:
- /* Failed to start tracer; roll back to DISABLED mode */
if (ret) {
- coresight_set_mode(csdev, CS_MODE_DISABLED);
- return ret;
+ if (cfg_hash)
+ cscfg_csdev_disable_active_config(csdev);
+ goto err;
}
csdev->path = path;
return 0;
+
+err:
+ /* Failed to start tracer; roll back to DISABLED mode */
+ coresight_set_mode(csdev, CS_MODE_DISABLED);
+ return ret;
}
static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
@@ -919,8 +926,10 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa
cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset);
if (cfg_hash) {
ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
- if (ret)
+ if (ret) {
+ etm4_release_trace_id(drvdata);
return ret;
+ }
}
raw_spin_lock(&drvdata->spinlock);
@@ -1508,6 +1517,8 @@ static void etm4_init_arch_data(void *info)
drvdata->lpoverride = (etmidr5 & TRCIDR5_LPOVERRIDE) && (!drvdata->skip_power_up);
/* NUMSEQSTATE, bits[27:25] number of sequencer states implemented */
drvdata->nrseqstate = FIELD_GET(TRCIDR5_NUMSEQSTATE_MASK, etmidr5);
+ if (drvdata->nrseqstate)
+ drvdata->nr_seq_ctrls = ETM_MAX_SEQ_TRANSITIONS;
/* NUMCNTR, bits[30:28] number of counters available for tracing */
drvdata->nr_cntr = FIELD_GET(TRCIDR5_NUMCNTR_MASK, etmidr5);
@@ -1896,7 +1907,7 @@ static int etm4_cpu_save(struct coresight_device *csdev)
if (drvdata->nr_pe_cmp)
state->trcvipcssctlr = etm4x_read32(csa, TRCVIPCSSCTLR);
- for (i = 0; i < drvdata->nrseqstate - 1; i++)
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i));
if (drvdata->nrseqstate) {
@@ -2009,7 +2020,7 @@ static void etm4_cpu_restore(struct coresight_device *csdev)
if (drvdata->nr_pe_cmp)
etm4x_relaxed_write32(csa, state->trcvipcssctlr, TRCVIPCSSCTLR);
- for (i = 0; i < drvdata->nrseqstate - 1; i++)
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i));
if (drvdata->nrseqstate) {
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index e9eeea6240d5..cc6cdd3ae29d 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
@@ -223,7 +223,7 @@ static ssize_t reset_store(struct device *dev,
config->vipcssctlr = 0x0;
/* Disable seq events */
- for (i = 0; i < drvdata->nrseqstate-1; i++)
+ for (i = 0; i < drvdata->nr_seq_ctrls; i++)
config->seq_ctrl[i] = 0x0;
config->seq_rst = 0x0;
config->seq_state = 0x0;
@@ -1395,9 +1395,11 @@ static ssize_t seq_idx_store(struct device *dev,
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
+ if (!drvdata->nr_seq_ctrls)
+ return -ENOTSUPP;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val >= drvdata->nrseqstate - 1)
+ if (val >= drvdata->nr_seq_ctrls)
return -EINVAL;
/*
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index 89d81ce4e04e..df9e0748d71e 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -614,6 +614,7 @@ static inline u32 etm4_res_sel_pair(u8 res_sel_idx)
#define ETM_MAX_NR_PE 8
#define ETMv4_MAX_CNTR 4
#define ETM_MAX_SEQ_STATES 4
+#define ETM_MAX_SEQ_TRANSITIONS 3
#define ETM_MAX_EXT_INP_SEL 4
#define ETM_MAX_EXT_INP 256
#define ETM_MAX_EXT_OUT 4
@@ -877,7 +878,7 @@ struct etmv4_config {
u32 vipcssctlr;
u8 seq_idx;
u8 syncfreq;
- u32 seq_ctrl[ETM_MAX_SEQ_STATES];
+ u32 seq_ctrl[ETM_MAX_SEQ_TRANSITIONS];
u32 seq_rst;
u32 seq_state;
u8 cntr_idx;
@@ -928,7 +929,7 @@ struct etmv4_save_state {
u32 trcvissctlr;
u32 trcvipcssctlr;
- u32 trcseqevr[ETM_MAX_SEQ_STATES];
+ u32 trcseqevr[ETM_MAX_SEQ_TRANSITIONS];
u32 trcseqrstevr;
u32 trcseqstr;
u32 trcextinselr;
@@ -953,11 +954,6 @@ struct etmv4_save_state {
u32 trcclaimset;
- u32 cntr_val[ETMv4_MAX_CNTR];
- u32 seq_state;
- u32 vinst_ctrl;
- u32 ss_status[ETM_MAX_SS_CMP];
-
u32 trcpdcr;
};
@@ -981,6 +977,7 @@ struct etmv4_save_state {
* @numcidc: Number of contextID comparators.
* @numvmidc: Number of VMID comparators.
* @nrseqstate: The number of sequencer states that are implemented.
+ * @nr_seq_ctrls: The number of sequence state transition control registers.
* @nr_event: Indicates how many events the trace unit support.
* @nr_resource:The number of resource selection pairs available for tracing.
* @nr_ss_cmp: Number of single-shot comparator controls that are available.
@@ -1046,6 +1043,7 @@ struct etmv4_drvdata {
u8 numextinsel;
u8 numvmidc;
u8 nrseqstate;
+ u8 nr_seq_ctrls;
u8 nr_event;
u8 nr_resource;
u8 nr_ss_cmp;
diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c
index 233c4c32513c..52f168f8b459 100644
--- a/drivers/hwtracing/ptt/hisi_ptt.c
+++ b/drivers/hwtracing/ptt/hisi_ptt.c
@@ -171,13 +171,13 @@ static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt)
HISI_PTT_WAIT_TRACE_TIMEOUT_US);
}
-static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt)
+static bool hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt)
{
u32 val;
- readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS,
- val, !val, HISI_PTT_RESET_POLL_INTERVAL_US,
- HISI_PTT_RESET_TIMEOUT_US);
+ return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS,
+ val, !val, HISI_PTT_RESET_POLL_INTERVAL_US,
+ HISI_PTT_RESET_TIMEOUT_US);
}
static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt)
@@ -194,7 +194,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt)
{
struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl;
u32 val;
- int i;
/* Check device idle before start trace */
if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) {
@@ -202,14 +201,18 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt)
return -EBUSY;
}
- ctrl->started = true;
-
/* Reset the DMA before start tracing */
val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
val |= HISI_PTT_TRACE_CTRL_RST;
writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
- hisi_ptt_wait_dma_reset_done(hisi_ptt);
+ if (!hisi_ptt_wait_dma_reset_done(hisi_ptt)) {
+ pci_err(hisi_ptt->pdev, "timed out waiting for DMA reset\n");
+ val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
+ val &= ~HISI_PTT_TRACE_CTRL_RST;
+ writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
+ return -ETIMEDOUT;
+ }
val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
val &= ~HISI_PTT_TRACE_CTRL_RST;
@@ -218,10 +221,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt)
/* Reset the index of current buffer */
hisi_ptt->trace_ctrl.buf_index = 0;
- /* Zero the trace buffers */
- for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++)
- memset(ctrl->trace_buf[i].addr, 0, HISI_PTT_TRACE_BUF_SIZE);
-
/* Clear the interrupt status */
writel(HISI_PTT_TRACE_INT_STAT_MASK, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT);
writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK);
@@ -234,6 +233,8 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt)
if (!hisi_ptt->trace_ctrl.is_port)
val |= HISI_PTT_TRACE_CTRL_FILTER_MODE;
+ ctrl->started = true;
+
/* Start the Trace */
val |= HISI_PTT_TRACE_CTRL_EN;
writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 661127aed2f9..652557a5b851 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -92,6 +92,7 @@ source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/filter/Kconfig"
+source "drivers/iio/flow/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/gyro/Kconfig"
source "drivers/iio/health/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index cb80ef837e84..f03a4100c800 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -29,6 +29,7 @@ obj-y += dac/
obj-y += dummy/
obj-y += gyro/
obj-y += filter/
+obj-y += flow/
obj-y += frequency/
obj-y += health/
obj-y += humidity/
diff --git a/drivers/iio/TODO b/drivers/iio/TODO
index 2ace27d1ac62..7b05308248d7 100644
--- a/drivers/iio/TODO
+++ b/drivers/iio/TODO
@@ -5,12 +5,17 @@ Documentation
tree
- Yaml conversions for abandoned drivers
- ABI Documentation
- - Audit driviers/iio/staging/Documentation
+ - Audit drivers/staging/iio/Documentation
-- Converting drivers from device tree centric to more generic
-property handlers.
+- Converting drivers from device tree centric to generic property handlers.
- Refactor old platform_data constructs from drivers and convert it
-to state struct and using property handlers and readers.
+ to state struct and using property handlers and readers.
+
+- Convert selected IIO drivers to use fully device managed resource APIs.
+
+- Evaluate and possibly adopt cleanup.h helpers (guard()(), __free(), et cetera)
+ in drivers where manual resource management is still used and there is a
+ significant improvement to the code flow and complexity to be had.
Mailing list: linux-iio@vger.kernel.org
diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c
index 0315f4bfd69a..4fa1b04f75b6 100644
--- a/drivers/iio/accel/adxl345_spi.c
+++ b/drivers/iio/accel/adxl345_spi.c
@@ -60,8 +60,8 @@ static const struct adxl345_chip_info adxl375_spi_info = {
};
static const struct spi_device_id adxl345_spi_id[] = {
- { "adxl345", (kernel_ulong_t)&adxl345_spi_info },
- { "adxl375", (kernel_ulong_t)&adxl375_spi_info },
+ { .name = "adxl345", .driver_data = (kernel_ulong_t)&adxl345_spi_info },
+ { .name = "adxl375", .driver_data = (kernel_ulong_t)&adxl375_spi_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl345_spi_id);
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 68cb2557f390..080c9f6f9a11 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -768,8 +768,7 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq)
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);
+ return ret;
ret = devm_iio_trigger_register(data->dev, data->dready_trig);
if (ret)
diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c
index 437be2f1d53c..d2209ca8ca6e 100644
--- a/drivers/iio/accel/adxl355_spi.c
+++ b/drivers/iio/accel/adxl355_spi.c
@@ -40,8 +40,8 @@ static int adxl355_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id adxl355_spi_id[] = {
- { "adxl355", (kernel_ulong_t)&adxl35x_chip_info[ADXL355] },
- { "adxl359", (kernel_ulong_t)&adxl35x_chip_info[ADXL359] },
+ { .name = "adxl355", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL355] },
+ { .name = "adxl359", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL359] },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl355_spi_id);
diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c
index 8c3de11a10a3..4ff1c7a0988b 100644
--- a/drivers/iio/accel/adxl367.c
+++ b/drivers/iio/accel/adxl367.c
@@ -1486,7 +1486,7 @@ int adxl367_probe(struct device *dev, const struct adxl367_ops *ops,
adxl367_irq_handler, IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
- return dev_err_probe(st->dev, ret, "Failed to request irq\n");
+ return ret;
return devm_iio_device_register(dev, indio_dev);
}
diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c
index 785652c7fc92..1d1418452bdc 100644
--- a/drivers/iio/accel/adxl367_spi.c
+++ b/drivers/iio/accel/adxl367_spi.c
@@ -137,7 +137,7 @@ static int adxl367_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id adxl367_spi_id[] = {
- { "adxl367", 0 },
+ { .name = "adxl367" },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl367_spi_id);
diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c
index 25fdb4254372..128e148c4e96 100644
--- a/drivers/iio/accel/adxl372_spi.c
+++ b/drivers/iio/accel/adxl372_spi.c
@@ -34,8 +34,8 @@ static int adxl372_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id adxl372_spi_id[] = {
- { "adxl371", (kernel_ulong_t)&adxl371_chip_info },
- { "adxl372", (kernel_ulong_t)&adxl372_chip_info },
+ { .name = "adxl371", .driver_data = (kernel_ulong_t)&adxl371_chip_info },
+ { .name = "adxl372", .driver_data = (kernel_ulong_t)&adxl372_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl372_spi_id);
diff --git a/drivers/iio/accel/adxl380_spi.c b/drivers/iio/accel/adxl380_spi.c
index ee3e6338b53d..ae8467f4e6c0 100644
--- a/drivers/iio/accel/adxl380_spi.c
+++ b/drivers/iio/accel/adxl380_spi.c
@@ -34,10 +34,10 @@ static int adxl380_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id adxl380_spi_id[] = {
- { "adxl318", (kernel_ulong_t)&adxl318_chip_info },
- { "adxl319", (kernel_ulong_t)&adxl319_chip_info },
- { "adxl380", (kernel_ulong_t)&adxl380_chip_info },
- { "adxl382", (kernel_ulong_t)&adxl382_chip_info },
+ { .name = "adxl318", .driver_data = (kernel_ulong_t)&adxl318_chip_info },
+ { .name = "adxl319", .driver_data = (kernel_ulong_t)&adxl319_chip_info },
+ { .name = "adxl380", .driver_data = (kernel_ulong_t)&adxl380_chip_info },
+ { .name = "adxl382", .driver_data = (kernel_ulong_t)&adxl382_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl380_spi_id);
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index 62bda8d76691..e7dbbd4766dd 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -988,10 +988,8 @@ static int bma180_probe(struct i2c_client *client)
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");
+ if (ret)
goto err_trigger_free;
- }
data->trig->ops = &bma180_trigger_ops;
iio_trigger_set_drvdata(data->trig, indio_dev);
diff --git a/drivers/iio/accel/bma220_core.c b/drivers/iio/accel/bma220_core.c
index 269e2b720ddb..b83abfdd84fd 100644
--- a/drivers/iio/accel/bma220_core.c
+++ b/drivers/iio/accel/bma220_core.c
@@ -544,8 +544,7 @@ int bma220_common_probe(struct device *dev, struct regmap *regmap, int irq)
&bma220_irq_handler, IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
- return dev_err_probe(dev, ret,
- "request irq %d failed\n", irq);
+ return ret;
}
ret = devm_add_action_or_reset(dev, bma220_deinit, data);
diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c
index d897aec1c9f2..d6c1087cc3c0 100644
--- a/drivers/iio/accel/bma220_spi.c
+++ b/drivers/iio/accel/bma220_spi.c
@@ -25,7 +25,7 @@ static int bma220_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id bma220_spi_id[] = {
- { "bma220", 0 },
+ { .name = "bma220" },
{ }
};
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 05f72707f830..7e3cff4cca91 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -1789,8 +1789,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
- return dev_err_probe(data->dev, ret,
- "request irq %d failed\n", irq);
+ return ret;
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c
index 4b89e313704f..7ac0a7881b59 100644
--- a/drivers/iio/accel/bma400_spi.c
+++ b/drivers/iio/accel/bma400_spi.c
@@ -87,7 +87,7 @@ static int bma400_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id bma400_spi_ids[] = {
- { "bma400", 0 },
+ { .name = "bma400" },
{ }
};
MODULE_DEVICE_TABLE(spi, bma400_spi_ids);
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index dc8a6285cf3d..46bb36d9366e 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -988,8 +988,10 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
do_div(sample_period, count);
tstamp = data->timestamp - (count - 1) * sample_period;
- if (samples && count > samples)
- count = samples;
+ if (samples)
+ count = min3(count, samples, BMC150_ACCEL_FIFO_LENGTH);
+ else
+ count = min(count, BMC150_ACCEL_FIFO_LENGTH);
count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH);
diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c
index 7d2be6f63538..405bd7b2d199 100644
--- a/drivers/iio/accel/bmc150-accel-spi.c
+++ b/drivers/iio/accel/bmc150-accel-spi.c
@@ -52,15 +52,15 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
static const struct spi_device_id bmc150_accel_id[] = {
- {"bma222"},
- {"bma222e"},
- {"bma250e"},
- {"bma253"},
- {"bma255"},
- {"bma280"},
- {"bmc150_accel"},
- {"bmc156_accel", BOSCH_BMC156},
- {"bmi055_accel"},
+ { .name = "bma222", .driver_data = BOSCH_UNKNOWN },
+ { .name = "bma222e", .driver_data = BOSCH_UNKNOWN },
+ { .name = "bma250e", .driver_data = BOSCH_UNKNOWN },
+ { .name = "bma253", .driver_data = BOSCH_UNKNOWN },
+ { .name = "bma255", .driver_data = BOSCH_UNKNOWN },
+ { .name = "bma280", .driver_data = BOSCH_UNKNOWN },
+ { .name = "bmc150_accel", .driver_data = BOSCH_UNKNOWN },
+ { .name = "bmc156_accel", .driver_data = BOSCH_BMC156 },
+ { .name = "bmi055_accel", .driver_data = BOSCH_UNKNOWN },
{ }
};
MODULE_DEVICE_TABLE(spi, bmc150_accel_id);
diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c
index 44cb50c76cb1..ae89e162e097 100644
--- a/drivers/iio/accel/bmi088-accel-spi.c
+++ b/drivers/iio/accel/bmi088-accel-spi.c
@@ -72,9 +72,9 @@ static const struct of_device_id bmi088_of_match[] = {
MODULE_DEVICE_TABLE(of, bmi088_of_match);
static const struct spi_device_id bmi088_accel_id[] = {
- {"bmi085-accel", BOSCH_BMI085},
- {"bmi088-accel", BOSCH_BMI088},
- {"bmi090l-accel", BOSCH_BMI090L},
+ { .name = "bmi085-accel", .driver_data = BOSCH_BMI085 },
+ { .name = "bmi088-accel", .driver_data = BOSCH_BMI088 },
+ { .name = "bmi090l-accel", .driver_data = BOSCH_BMI090L },
{ }
};
MODULE_DEVICE_TABLE(spi, bmi088_accel_id);
diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c
index fe35a1270786..6f0497ab6133 100644
--- a/drivers/iio/accel/dmard09.c
+++ b/drivers/iio/accel/dmard09.c
@@ -8,6 +8,7 @@
#include <linux/unaligned.h>
#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/units.h>
#include <linux/iio/iio.h>
#define DMARD09_DRV_NAME "dmard09"
@@ -79,6 +80,12 @@ static int dmard09_read_raw(struct iio_dev *indio_dev,
*val = accel;
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ /* 1 g / 32 LSB, in m/s^2 */
+ *val2 = IIO_G_TO_M_S_2(NANO / 32);
+
+ return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c
index d0c2a8daef0d..18d7b09bddd2 100644
--- a/drivers/iio/accel/fxls8962af-core.c
+++ b/drivers/iio/accel/fxls8962af-core.c
@@ -969,6 +969,8 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev)
if (!count)
return 0;
+ count = min(count, FXLS8962AF_FIFO_LENGTH);
+
data->old_timestamp = data->timestamp;
data->timestamp = iio_get_time_ns(indio_dev);
diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c
index 8936032526a0..baf3c4352f14 100644
--- a/drivers/iio/accel/fxls8962af-spi.c
+++ b/drivers/iio/accel/fxls8962af-spi.c
@@ -34,8 +34,8 @@ static const struct of_device_id fxls8962af_spi_of_match[] = {
MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match);
static const struct spi_device_id fxls8962af_spi_id_table[] = {
- { "fxls8962af", fxls8962af },
- { "fxls8964af", fxls8964af },
+ { .name = "fxls8962af", .driver_data = fxls8962af },
+ { .name = "fxls8964af", .driver_data = fxls8964af },
{ }
};
MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table);
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index 737572bb44d9..eb521125f4eb 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -121,9 +121,8 @@ static const struct iio_chan_spec gravity_channels[] = {
/* Channel read_raw handler */
static int accel_3d_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct accel_3d_state *accel_state = iio_priv(indio_dev);
int report_id = -1;
@@ -150,7 +149,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
else {
*val = 0;
hid_sensor_power_state(&accel_state->common_attributes,
- false);
+ false);
return -EINVAL;
}
hid_sensor_power_state(&accel_state->common_attributes, false);
@@ -183,10 +182,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int accel_3d_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct accel_3d_state *accel_state = iio_priv(indio_dev);
int ret = 0;
@@ -222,8 +219,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
/* Callback handler to send event after all samples are received and captured */
static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct accel_3d_state *accel_state = iio_priv(indio_dev);
@@ -246,9 +242,9 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- size_t raw_len, char *raw_data,
- void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct accel_3d_state *accel_state = iio_priv(indio_dev);
@@ -280,10 +276,10 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id*/
static int accel_3d_parse_report(struct platform_device *pdev,
- struct hid_sensor_hub_device *hsdev,
- struct iio_chan_spec *channels,
- unsigned usage_id,
- struct accel_3d_state *st)
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ u32 usage_id,
+ struct accel_3d_state *st)
{
int ret;
@@ -302,10 +298,10 @@ static int accel_3d_parse_report(struct platform_device *pdev,
};
}
dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
- st->accel[0].index,
- st->accel[0].report_id,
- st->accel[1].index, st->accel[1].report_id,
- st->accel[2].index, st->accel[2].report_id);
+ st->accel[0].index,
+ st->accel[0].report_id,
+ st->accel[1].index, st->accel[1].report_id,
+ st->accel[2].index, st->accel[2].report_id);
st->scale_precision = hid_sensor_format_scale(
hsdev->usage,
@@ -328,7 +324,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct accel_3d_state));
- if (indio_dev == NULL)
+ if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
@@ -365,8 +361,8 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
return -ENOMEM;
}
ret = accel_3d_parse_report(pdev, hsdev,
- (struct iio_chan_spec *)indio_dev->channels,
- hsdev->usage, accel_state);
+ (struct iio_chan_spec *)indio_dev->channels,
+ hsdev->usage, accel_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
return ret;
@@ -379,32 +375,32 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
atomic_set(&accel_state->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &accel_state->common_attributes);
+ &accel_state->common_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
accel_state->callbacks.send_event = accel_3d_proc_event;
accel_state->callbacks.capture_sample = accel_3d_capture_sample;
accel_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
- &accel_state->callbacks);
+ &accel_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return ret;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
return ret;
@@ -417,8 +413,8 @@ static void hid_accel_3d_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct accel_3d_state *accel_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
}
diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c
index 8a13f78aeab0..f671fab3dc31 100644
--- a/drivers/iio/accel/kionix-kx022a.c
+++ b/drivers/iio/accel/kionix-kx022a.c
@@ -1435,7 +1435,7 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi
&kx022a_irq_thread_handler,
IRQF_ONESHOT, name, idev);
if (ret)
- return dev_err_probe(data->dev, ret, "Could not request IRQ\n");
+ return ret;
ret = devm_iio_trigger_register(dev, indio_trig);
if (ret)
diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c
index 31a26baba9f7..71da958d4051 100644
--- a/drivers/iio/accel/kxsd9-spi.c
+++ b/drivers/iio/accel/kxsd9-spi.c
@@ -36,7 +36,7 @@ static void kxsd9_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id kxsd9_spi_id[] = {
- {"kxsd9", 0},
+ { .name = "kxsd9" },
{ }
};
MODULE_DEVICE_TABLE(spi, kxsd9_spi_id);
diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c
index aca02e83f789..872c47acf882 100644
--- a/drivers/iio/accel/mma7455_spi.c
+++ b/drivers/iio/accel/mma7455_spi.c
@@ -28,8 +28,8 @@ static void mma7455_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id mma7455_spi_ids[] = {
- { "mma7455", 0 },
- { "mma7456", 0 },
+ { .name = "mma7455" },
+ { .name = "mma7456" },
{ }
};
MODULE_DEVICE_TABLE(spi, mma7455_spi_ids);
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
index 7d9cbfa01360..7e01427fd9c4 100644
--- a/drivers/iio/accel/mma9551.c
+++ b/drivers/iio/accel/mma9551.c
@@ -420,10 +420,8 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev)
NULL, mma9551_event_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"mma9551_event", indio_dev);
- if (ret < 0) {
- dev_err(dev, "request irq %d failed\n", data->irqs[i]);
+ if (ret)
return ret;
- }
dev_dbg(dev, "gpio resource, no:%d irq:%d\n",
desc_to_gpio(gpio), data->irqs[i]);
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
index ab43b1e0ff04..8e7aaac89d47 100644
--- a/drivers/iio/accel/mma9553.c
+++ b/drivers/iio/accel/mma9553.c
@@ -1099,11 +1099,8 @@ static int mma9553_probe(struct i2c_client *client)
mma9553_event_handler,
IRQF_TRIGGER_RISING,
"mma9553_event", indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "request irq %d failed\n",
- client->irq);
+ if (ret)
goto out_poweroff;
- }
}
ret = pm_runtime_set_active(&client->dev);
diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c
index e0e73b87cba8..caf9b1b6397f 100644
--- a/drivers/iio/accel/msa311.c
+++ b/drivers/iio/accel/msa311.c
@@ -1059,7 +1059,7 @@ static int msa311_setup_interrupts(struct msa311_priv *msa311)
msa311_irq_thread, IRQF_ONESHOT,
msa311->chip_name, indio_dev);
if (err)
- return dev_err_probe(dev, err, "failed to request IRQ\n");
+ return err;
trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name);
if (!trig)
diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
index 2034fe92bae3..a66fee828ec1 100644
--- a/drivers/iio/accel/mxc4005.c
+++ b/drivers/iio/accel/mxc4005.c
@@ -489,11 +489,8 @@ static int mxc4005_probe(struct i2c_client *client)
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");
+ if (ret)
return ret;
- }
data->dready_trig->ops = &mxc4005_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
index 573831199bba..a92a563f6a4b 100644
--- a/drivers/iio/accel/sca3000.c
+++ b/drivers/iio/accel/sca3000.c
@@ -1504,10 +1504,10 @@ static int sca3000_probe(struct spi_device *spi)
}
static const struct spi_device_id sca3000_id[] = {
- { "sca3000_d01", (kernel_ulong_t)&sca3000_chip_info_d01 },
- { "sca3000_e02", (kernel_ulong_t)&sca3000_chip_info_e02 },
- { "sca3000_e04", (kernel_ulong_t)&sca3000_chip_info_e04 },
- { "sca3000_e05", (kernel_ulong_t)&sca3000_chip_info_e05 },
+ { .name = "sca3000_d01", .driver_data = (kernel_ulong_t)&sca3000_chip_info_d01 },
+ { .name = "sca3000_e02", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e02 },
+ { .name = "sca3000_e04", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e04 },
+ { .name = "sca3000_e05", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e05 },
{ }
};
MODULE_DEVICE_TABLE(spi, sca3000_id);
diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c
index 8380b237831c..67582cc34149 100644
--- a/drivers/iio/accel/sca3300.c
+++ b/drivers/iio/accel/sca3300.c
@@ -664,8 +664,8 @@ static const struct of_device_id sca3300_dt_ids[] = {
MODULE_DEVICE_TABLE(of, sca3300_dt_ids);
static const struct spi_device_id sca3300_ids[] = {
- { "sca3300" },
- { "scl3300" },
+ { .name = "sca3300" },
+ { .name = "scl3300" },
{ }
};
MODULE_DEVICE_TABLE(spi, sca3300_ids);
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index e6bf8d0d4ff5..171f241c0a30 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -146,26 +146,26 @@ static int st_accel_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id st_accel_id_table[] = {
- { LIS3DH_ACCEL_DEV_NAME },
- { LSM330D_ACCEL_DEV_NAME },
- { LSM330DL_ACCEL_DEV_NAME },
- { LSM330DLC_ACCEL_DEV_NAME },
- { LIS331DLH_ACCEL_DEV_NAME },
- { LSM330_ACCEL_DEV_NAME },
- { LSM303AGR_ACCEL_DEV_NAME },
- { LIS2DH12_ACCEL_DEV_NAME },
- { LIS2DS12_ACCEL_DEV_NAME },
- { LIS3L02DQ_ACCEL_DEV_NAME },
- { LNG2DM_ACCEL_DEV_NAME },
- { H3LIS331DL_ACCEL_DEV_NAME },
- { LIS331DL_ACCEL_DEV_NAME },
- { LIS3LV02DL_ACCEL_DEV_NAME },
- { LIS2DW12_ACCEL_DEV_NAME },
- { LIS3DHH_ACCEL_DEV_NAME },
- { LIS3DE_ACCEL_DEV_NAME },
- { LIS302DL_ACCEL_DEV_NAME },
- { LSM303C_ACCEL_DEV_NAME },
- { IIS328DQ_ACCEL_DEV_NAME },
+ { .name = LIS3DH_ACCEL_DEV_NAME },
+ { .name = LSM330D_ACCEL_DEV_NAME },
+ { .name = LSM330DL_ACCEL_DEV_NAME },
+ { .name = LSM330DLC_ACCEL_DEV_NAME },
+ { .name = LIS331DLH_ACCEL_DEV_NAME },
+ { .name = LSM330_ACCEL_DEV_NAME },
+ { .name = LSM303AGR_ACCEL_DEV_NAME },
+ { .name = LIS2DH12_ACCEL_DEV_NAME },
+ { .name = LIS2DS12_ACCEL_DEV_NAME },
+ { .name = LIS3L02DQ_ACCEL_DEV_NAME },
+ { .name = LNG2DM_ACCEL_DEV_NAME },
+ { .name = H3LIS331DL_ACCEL_DEV_NAME },
+ { .name = LIS331DL_ACCEL_DEV_NAME },
+ { .name = LIS3LV02DL_ACCEL_DEV_NAME },
+ { .name = LIS2DW12_ACCEL_DEV_NAME },
+ { .name = LIS3DHH_ACCEL_DEV_NAME },
+ { .name = LIS3DE_ACCEL_DEV_NAME },
+ { .name = LIS302DL_ACCEL_DEV_NAME },
+ { .name = LSM303C_ACCEL_DEV_NAME },
+ { .name = IIS328DQ_ACCEL_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
index 8b5a3e0489e9..50840c3440c4 100644
--- a/drivers/iio/accel/stk8312.c
+++ b/drivers/iio/accel/stk8312.c
@@ -7,12 +7,20 @@
* IIO driver for STK8312; 7-bit I2C address: 0x3D.
*/
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/sysfs.h>
#include <linux/types.h>
+
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -542,11 +550,8 @@ static int stk8312_probe(struct i2c_client *client)
IRQF_ONESHOT,
"stk8312_event",
indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "request irq %d failed\n",
- client->irq);
+ if (ret)
goto err_power_off;
- }
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d",
diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c
index d0c53b8ac850..6c1e286c0a1d 100644
--- a/drivers/iio/accel/stk8ba50.c
+++ b/drivers/iio/accel/stk8ba50.c
@@ -7,11 +7,18 @@
* STK8BA50 7-bit I2C address: 0x18.
*/
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/sysfs.h>
#include <linux/types.h>
+
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -431,11 +438,8 @@ static int stk8ba50_probe(struct i2c_client *client)
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);
+ if (ret)
goto err_power_off;
- }
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d",
diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c
index ff9bc5f06c18..938ba43c2c35 100644
--- a/drivers/iio/adc/88pm886-gpadc.c
+++ b/drivers/iio/adc/88pm886-gpadc.c
@@ -279,7 +279,7 @@ static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const
static int pm886_gpadc_hw_enable(struct regmap *map)
{
- const u8 config[] = {
+ static const u8 config[] = {
PM886_GPADC_CONFIG1_EN_ALL,
PM886_GPADC_CONFIG2_EN_ALL,
PM886_GPADC_GND_DET2_EN,
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index c6d3cf68b1f7..415e519ad4eb 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -33,7 +33,8 @@ config AB8500_GPADC
and USB voltages integral to the U8500 platform.
config AD_SIGMA_DELTA
- tristate
+ tristate "Analog Devices Sigma-Delta Modulator support" if COMPILE_TEST
+ depends on SPI
select IIO_BUFFER
select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
@@ -945,6 +946,25 @@ config LTC2309
This driver can also be built as a module. If so, the module will
be called ltc2309.
+config LTC2378
+ tristate "Analog Devices LTC2378 ADC driver"
+ depends on SPI
+ depends on REGULATOR || COMPILE_TEST
+ depends on GPIOLIB
+ depends on PWM
+ select IIO_BUFFER
+ select IIO_BUFFER_DMA
+ select IIO_BUFFER_DMAENGINE
+ select IIO_TRIGGERED_BUFFER
+ select SPI_OFFLOAD
+ select SPI_OFFLOAD_TRIGGER_PWM
+ help
+ Say yes here to build support for Analog Devices LTC2378-20 and
+ similar analog to digital converters.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2378.
+
config LTC2471
tristate "Linear Technology LTC2471 and LTC2473 ADC driver"
depends on I2C
@@ -1167,6 +1187,17 @@ config MCP3911
This driver can also be built as a module. If so, the module will be
called mcp3911.
+config MEDIATEK_MT6323_AUXADC
+ tristate "MediaTek MT6323 PMIC AUXADC driver"
+ depends on MFD_MT6397
+ help
+ Say yes here to enable support for MediaTek MT6323 PMIC Auxiliary ADC.
+ This driver provides multiple channels for system monitoring,
+ such as battery voltage, PMIC temperature, and others.
+
+ This driver can also be built as a module. If so, the module will be
+ called mt6323-auxadc.
+
config MEDIATEK_MT6359_AUXADC
tristate "MediaTek MT6359 PMIC AUXADC driver"
depends on MFD_MT6397
@@ -1791,6 +1822,18 @@ config TI_ADS1119
This driver can also be built as a module. If so, the module will be
called ti-ads1119.
+config TI_ADS112C14
+ tristate "Texas Instruments ADS112C14/ADS122C14"
+ depends on I2C
+ select CRC8
+ select REGMAP
+ help
+ If you say yes here you get support for Texas Instruments ADS112C14,
+ ADS122C14 ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads112c14.
+
config TI_ADS124S08
tristate "Texas Instruments ADS124S08"
depends on SPI
@@ -1966,6 +2009,39 @@ config TWL6030_GPADC
This driver can also be built as a module. If so, the module will be
called twl6030-gpadc.
+config VERSAL_SYSMON_CORE
+ tristate
+ select REGMAP
+
+config VERSAL_SYSMON
+ tristate "AMD Versal SysMon driver"
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ depends on HAS_IOMEM
+ select VERSAL_SYSMON_CORE
+ help
+ Say yes here to have support for the AMD/Xilinx Versal System
+ Monitor (SysMon). This driver provides voltage and temperature
+ monitoring through the IIO subsystem.
+
+ The SysMon measures up to 160 supply voltages and reads up to
+ 64 temperature satellites distributed across the SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called versal-sysmon.
+
+config VERSAL_SYSMON_I2C
+ tristate "AMD Versal SysMon I2C driver"
+ depends on I2C
+ select VERSAL_SYSMON_CORE
+ help
+ Say yes here to have support for the AMD/Xilinx Versal System
+ Monitor (SysMon) via I2C interface. This driver enables voltage
+ and temperature monitoring when the Versal chip has SysMon
+ configured with I2C access.
+
+ To compile this driver as a module, choose M here: the module
+ will be called versal-sysmon-i2c.
+
config VF610_ADC
tristate "Freescale vf610 ADC driver"
depends on HAS_IOMEM
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 707dd708912f..dcec0abb03b7 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
obj-$(CONFIG_LTC2309) += ltc2309.o
+obj-$(CONFIG_LTC2378) += ltc2378.o
obj-$(CONFIG_LTC2471) += ltc2471.o
obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
@@ -100,6 +101,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MCP3564) += mcp3564.o
obj-$(CONFIG_MCP3911) += mcp3911.o
+obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o
obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o
obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o
@@ -153,6 +155,7 @@ 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_ADS112C14) += ti-ads112c14.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
@@ -168,6 +171,9 @@ obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
+obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o
+obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o
+obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c
index c2f6f5f9812c..a33896b2cf97 100644
--- a/drivers/iio/adc/ad4000.c
+++ b/drivers/iio/adc/ad4000.c
@@ -1176,37 +1176,37 @@ static int ad4000_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4000_id[] = {
- { "ad4000", (kernel_ulong_t)&ad4000_chip_info },
- { "ad4001", (kernel_ulong_t)&ad4001_chip_info },
- { "ad4002", (kernel_ulong_t)&ad4002_chip_info },
- { "ad4003", (kernel_ulong_t)&ad4003_chip_info },
- { "ad4004", (kernel_ulong_t)&ad4004_chip_info },
- { "ad4005", (kernel_ulong_t)&ad4005_chip_info },
- { "ad4006", (kernel_ulong_t)&ad4006_chip_info },
- { "ad4007", (kernel_ulong_t)&ad4007_chip_info },
- { "ad4008", (kernel_ulong_t)&ad4008_chip_info },
- { "ad4010", (kernel_ulong_t)&ad4010_chip_info },
- { "ad4011", (kernel_ulong_t)&ad4011_chip_info },
- { "ad4020", (kernel_ulong_t)&ad4020_chip_info },
- { "ad4021", (kernel_ulong_t)&ad4021_chip_info },
- { "ad4022", (kernel_ulong_t)&ad4022_chip_info },
- { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info },
- { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info },
- { "ad7685", (kernel_ulong_t)&ad7685_chip_info },
- { "ad7686", (kernel_ulong_t)&ad7686_chip_info },
- { "ad7687", (kernel_ulong_t)&ad7687_chip_info },
- { "ad7688", (kernel_ulong_t)&ad7688_chip_info },
- { "ad7690", (kernel_ulong_t)&ad7690_chip_info },
- { "ad7691", (kernel_ulong_t)&ad7691_chip_info },
- { "ad7693", (kernel_ulong_t)&ad7693_chip_info },
- { "ad7942", (kernel_ulong_t)&ad7942_chip_info },
- { "ad7946", (kernel_ulong_t)&ad7946_chip_info },
- { "ad7980", (kernel_ulong_t)&ad7980_chip_info },
- { "ad7982", (kernel_ulong_t)&ad7982_chip_info },
- { "ad7983", (kernel_ulong_t)&ad7983_chip_info },
- { "ad7984", (kernel_ulong_t)&ad7984_chip_info },
- { "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info },
- { "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info },
+ { .name = "ad4000", .driver_data = (kernel_ulong_t)&ad4000_chip_info },
+ { .name = "ad4001", .driver_data = (kernel_ulong_t)&ad4001_chip_info },
+ { .name = "ad4002", .driver_data = (kernel_ulong_t)&ad4002_chip_info },
+ { .name = "ad4003", .driver_data = (kernel_ulong_t)&ad4003_chip_info },
+ { .name = "ad4004", .driver_data = (kernel_ulong_t)&ad4004_chip_info },
+ { .name = "ad4005", .driver_data = (kernel_ulong_t)&ad4005_chip_info },
+ { .name = "ad4006", .driver_data = (kernel_ulong_t)&ad4006_chip_info },
+ { .name = "ad4007", .driver_data = (kernel_ulong_t)&ad4007_chip_info },
+ { .name = "ad4008", .driver_data = (kernel_ulong_t)&ad4008_chip_info },
+ { .name = "ad4010", .driver_data = (kernel_ulong_t)&ad4010_chip_info },
+ { .name = "ad4011", .driver_data = (kernel_ulong_t)&ad4011_chip_info },
+ { .name = "ad4020", .driver_data = (kernel_ulong_t)&ad4020_chip_info },
+ { .name = "ad4021", .driver_data = (kernel_ulong_t)&ad4021_chip_info },
+ { .name = "ad4022", .driver_data = (kernel_ulong_t)&ad4022_chip_info },
+ { .name = "adaq4001", .driver_data = (kernel_ulong_t)&adaq4001_chip_info },
+ { .name = "adaq4003", .driver_data = (kernel_ulong_t)&adaq4003_chip_info },
+ { .name = "ad7685", .driver_data = (kernel_ulong_t)&ad7685_chip_info },
+ { .name = "ad7686", .driver_data = (kernel_ulong_t)&ad7686_chip_info },
+ { .name = "ad7687", .driver_data = (kernel_ulong_t)&ad7687_chip_info },
+ { .name = "ad7688", .driver_data = (kernel_ulong_t)&ad7688_chip_info },
+ { .name = "ad7690", .driver_data = (kernel_ulong_t)&ad7690_chip_info },
+ { .name = "ad7691", .driver_data = (kernel_ulong_t)&ad7691_chip_info },
+ { .name = "ad7693", .driver_data = (kernel_ulong_t)&ad7693_chip_info },
+ { .name = "ad7942", .driver_data = (kernel_ulong_t)&ad7942_chip_info },
+ { .name = "ad7946", .driver_data = (kernel_ulong_t)&ad7946_chip_info },
+ { .name = "ad7980", .driver_data = (kernel_ulong_t)&ad7980_chip_info },
+ { .name = "ad7982", .driver_data = (kernel_ulong_t)&ad7982_chip_info },
+ { .name = "ad7983", .driver_data = (kernel_ulong_t)&ad7983_chip_info },
+ { .name = "ad7984", .driver_data = (kernel_ulong_t)&ad7984_chip_info },
+ { .name = "ad7988-1", .driver_data = (kernel_ulong_t)&ad7988_1_chip_info },
+ { .name = "ad7988-5", .driver_data = (kernel_ulong_t)&ad7988_5_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4000_id);
diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
index 9c5f19321e3b..e97400a1aa48 100644
--- a/drivers/iio/adc/ad4030.c
+++ b/drivers/iio/adc/ad4030.c
@@ -1763,13 +1763,13 @@ static const struct ad4030_chip_info adaq4224_chip_info = {
};
static const struct spi_device_id ad4030_id_table[] = {
- { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info },
- { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info },
- { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info },
- { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info },
- { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info },
- { "adaq4216", (kernel_ulong_t)&adaq4216_chip_info },
- { "adaq4224", (kernel_ulong_t)&adaq4224_chip_info },
+ { .name = "ad4030-24", .driver_data = (kernel_ulong_t)&ad4030_24_chip_info },
+ { .name = "ad4630-16", .driver_data = (kernel_ulong_t)&ad4630_16_chip_info },
+ { .name = "ad4630-24", .driver_data = (kernel_ulong_t)&ad4630_24_chip_info },
+ { .name = "ad4632-16", .driver_data = (kernel_ulong_t)&ad4632_16_chip_info },
+ { .name = "ad4632-24", .driver_data = (kernel_ulong_t)&ad4632_24_chip_info },
+ { .name = "adaq4216", .driver_data = (kernel_ulong_t)&adaq4216_chip_info },
+ { .name = "adaq4224", .driver_data = (kernel_ulong_t)&adaq4224_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4030_id_table);
diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
index 967144eee3eb..04cd6628ebff 100644
--- a/drivers/iio/adc/ad4080.c
+++ b/drivers/iio/adc/ad4080.c
@@ -135,6 +135,7 @@
#define AD4087_CHIP_ID 0x0057
#define AD4088_CHIP_ID 0x0058
#define AD4880_CHIP_ID 0x0059
+#define AD4883_CHIP_ID 0x005B
#define AD4884_CHIP_ID 0x005C
#define AD4080_MAX_CHANNELS 2
@@ -541,6 +542,11 @@ static const struct iio_chan_spec ad4880_channels[] = {
AD4880_CHANNEL_DEFINE(20, 32, 1),
};
+static const struct iio_chan_spec ad4883_channels[] = {
+ AD4880_CHANNEL_DEFINE(16, 16, 0),
+ AD4880_CHANNEL_DEFINE(16, 16, 1),
+};
+
static const struct iio_chan_spec ad4884_channels[] = {
AD4880_CHANNEL_DEFINE(16, 16, 0),
AD4880_CHANNEL_DEFINE(16, 16, 1),
@@ -646,6 +652,16 @@ static const struct ad4080_chip_info ad4880_chip_info = {
.lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX,
};
+static const struct ad4080_chip_info ad4883_chip_info = {
+ .name = "ad4883",
+ .product_id = AD4883_CHIP_ID,
+ .scale_table = ad4080_scale_table,
+ .num_scales = ARRAY_SIZE(ad4080_scale_table),
+ .num_channels = 2,
+ .channels = ad4883_channels,
+ .lvds_cnv_clk_cnt_max = 5,
+};
+
static const struct ad4080_chip_info ad4884_chip_info = {
.name = "ad4884",
.product_id = AD4884_CHIP_ID,
@@ -853,17 +869,18 @@ static int ad4080_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4080_id[] = {
- { "ad4080", (kernel_ulong_t)&ad4080_chip_info },
- { "ad4081", (kernel_ulong_t)&ad4081_chip_info },
- { "ad4082", (kernel_ulong_t)&ad4082_chip_info },
- { "ad4083", (kernel_ulong_t)&ad4083_chip_info },
- { "ad4084", (kernel_ulong_t)&ad4084_chip_info },
- { "ad4085", (kernel_ulong_t)&ad4085_chip_info },
- { "ad4086", (kernel_ulong_t)&ad4086_chip_info },
- { "ad4087", (kernel_ulong_t)&ad4087_chip_info },
- { "ad4088", (kernel_ulong_t)&ad4088_chip_info },
- { "ad4880", (kernel_ulong_t)&ad4880_chip_info },
- { "ad4884", (kernel_ulong_t)&ad4884_chip_info },
+ { .name = "ad4080", .driver_data = (kernel_ulong_t)&ad4080_chip_info },
+ { .name = "ad4081", .driver_data = (kernel_ulong_t)&ad4081_chip_info },
+ { .name = "ad4082", .driver_data = (kernel_ulong_t)&ad4082_chip_info },
+ { .name = "ad4083", .driver_data = (kernel_ulong_t)&ad4083_chip_info },
+ { .name = "ad4084", .driver_data = (kernel_ulong_t)&ad4084_chip_info },
+ { .name = "ad4085", .driver_data = (kernel_ulong_t)&ad4085_chip_info },
+ { .name = "ad4086", .driver_data = (kernel_ulong_t)&ad4086_chip_info },
+ { .name = "ad4087", .driver_data = (kernel_ulong_t)&ad4087_chip_info },
+ { .name = "ad4088", .driver_data = (kernel_ulong_t)&ad4088_chip_info },
+ { .name = "ad4880", .driver_data = (kernel_ulong_t)&ad4880_chip_info },
+ { .name = "ad4883", .driver_data = (kernel_ulong_t)&ad4883_chip_info },
+ { .name = "ad4884", .driver_data = (kernel_ulong_t)&ad4884_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4080_id);
@@ -879,6 +896,7 @@ static const struct of_device_id ad4080_of_match[] = {
{ .compatible = "adi,ad4087", &ad4087_chip_info },
{ .compatible = "adi,ad4088", &ad4088_chip_info },
{ .compatible = "adi,ad4880", &ad4880_chip_info },
+ { .compatible = "adi,ad4883", &ad4883_chip_info },
{ .compatible = "adi,ad4884", &ad4884_chip_info },
{ }
};
diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c
index 7f39f3484062..3ffc4b765e0e 100644
--- a/drivers/iio/adc/ad4130.c
+++ b/drivers/iio/adc/ad4130.c
@@ -2425,12 +2425,12 @@ static const struct of_device_id ad4130_of_match[] = {
MODULE_DEVICE_TABLE(of, ad4130_of_match);
static const struct spi_device_id ad4130_id_table[] = {
- { "ad4129-4", (kernel_ulong_t)&ad4129_4_chip_info },
- { "ad4129-8", (kernel_ulong_t)&ad4129_8_chip_info },
- { "ad4130-4", (kernel_ulong_t)&ad4130_4_chip_info },
- { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info },
- { "ad4131-4", (kernel_ulong_t)&ad4131_4_chip_info },
- { "ad4131-8", (kernel_ulong_t)&ad4131_8_chip_info },
+ { .name = "ad4129-4", .driver_data = (kernel_ulong_t)&ad4129_4_chip_info },
+ { .name = "ad4129-8", .driver_data = (kernel_ulong_t)&ad4129_8_chip_info },
+ { .name = "ad4130-4", .driver_data = (kernel_ulong_t)&ad4130_4_chip_info },
+ { .name = "ad4130", .driver_data = (kernel_ulong_t)&ad4130_8_chip_info },
+ { .name = "ad4131-4", .driver_data = (kernel_ulong_t)&ad4131_4_chip_info },
+ { .name = "ad4131-8", .driver_data = (kernel_ulong_t)&ad4131_8_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4130_id_table);
diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c
index cb9d74316f6a..0490218bb0e9 100644
--- a/drivers/iio/adc/ad4134.c
+++ b/drivers/iio/adc/ad4134.c
@@ -472,7 +472,7 @@ static int ad4134_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4134_id[] = {
- { "ad4134" },
+ { .name = "ad4134" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4134_id);
diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c
index 627cbf5a37b0..f7757b3a931c 100644
--- a/drivers/iio/adc/ad4170-4.c
+++ b/drivers/iio/adc/ad4170-4.c
@@ -2979,9 +2979,9 @@ static int ad4170_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4170_id_table[] = {
- { "ad4170-4", (kernel_ulong_t)&ad4170_chip_info },
- { "ad4190-4", (kernel_ulong_t)&ad4190_chip_info },
- { "ad4195-4", (kernel_ulong_t)&ad4195_chip_info },
+ { .name = "ad4170-4", .driver_data = (kernel_ulong_t)&ad4170_chip_info },
+ { .name = "ad4190-4", .driver_data = (kernel_ulong_t)&ad4190_chip_info },
+ { .name = "ad4195-4", .driver_data = (kernel_ulong_t)&ad4195_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4170_id_table);
diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c
index 940b042d743a..480d1798f17b 100644
--- a/drivers/iio/adc/ad4851.c
+++ b/drivers/iio/adc/ad4851.c
@@ -1285,15 +1285,15 @@ static const struct of_device_id ad4851_of_match[] = {
};
static const struct spi_device_id ad4851_spi_id[] = {
- { "ad4851", (kernel_ulong_t)&ad4851_info },
- { "ad4852", (kernel_ulong_t)&ad4852_info },
- { "ad4853", (kernel_ulong_t)&ad4853_info },
- { "ad4854", (kernel_ulong_t)&ad4854_info },
- { "ad4855", (kernel_ulong_t)&ad4855_info },
- { "ad4856", (kernel_ulong_t)&ad4856_info },
- { "ad4857", (kernel_ulong_t)&ad4857_info },
- { "ad4858", (kernel_ulong_t)&ad4858_info },
- { "ad4858i", (kernel_ulong_t)&ad4858i_info },
+ { .name = "ad4851", .driver_data = (kernel_ulong_t)&ad4851_info },
+ { .name = "ad4852", .driver_data = (kernel_ulong_t)&ad4852_info },
+ { .name = "ad4853", .driver_data = (kernel_ulong_t)&ad4853_info },
+ { .name = "ad4854", .driver_data = (kernel_ulong_t)&ad4854_info },
+ { .name = "ad4855", .driver_data = (kernel_ulong_t)&ad4855_info },
+ { .name = "ad4856", .driver_data = (kernel_ulong_t)&ad4856_info },
+ { .name = "ad4857", .driver_data = (kernel_ulong_t)&ad4857_info },
+ { .name = "ad4858", .driver_data = (kernel_ulong_t)&ad4858_info },
+ { .name = "ad4858i", .driver_data = (kernel_ulong_t)&ad4858i_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4851_spi_id);
diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c
index e93b8bb60e8e..465a8ba01a22 100644
--- a/drivers/iio/adc/ad7091r8.c
+++ b/drivers/iio/adc/ad7091r8.c
@@ -249,9 +249,9 @@ static const struct of_device_id ad7091r8_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7091r8_of_match);
static const struct spi_device_id ad7091r8_spi_id[] = {
- { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info },
- { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info },
- { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info },
+ { .name = "ad7091r2", .driver_data = (kernel_ulong_t)&ad7091r2_init_info },
+ { .name = "ad7091r4", .driver_data = (kernel_ulong_t)&ad7091r4_init_info },
+ { .name = "ad7091r8", .driver_data = (kernel_ulong_t)&ad7091r8_init_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id);
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 19058d081418..fc5dbc624856 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -1683,8 +1683,8 @@ static const struct of_device_id ad7124_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7124_of_match);
static const struct spi_device_id ad71124_ids[] = {
- { "ad7124-4", (kernel_ulong_t)&ad7124_4_chip_info },
- { "ad7124-8", (kernel_ulong_t)&ad7124_8_chip_info },
+ { .name = "ad7124-4", .driver_data = (kernel_ulong_t)&ad7124_4_chip_info },
+ { .name = "ad7124-8", .driver_data = (kernel_ulong_t)&ad7124_8_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad71124_ids);
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index f78e758706ed..eb47175a0528 100644
--- a/drivers/iio/adc/ad7173.c
+++ b/drivers/iio/adc/ad7173.c
@@ -2065,19 +2065,19 @@ static const struct of_device_id ad7173_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7173_of_match);
static const struct spi_device_id ad7173_id_table[] = {
- { "ad4111", (kernel_ulong_t)&ad4111_device_info },
- { "ad4112", (kernel_ulong_t)&ad4112_device_info },
- { "ad4113", (kernel_ulong_t)&ad4113_device_info },
- { "ad4114", (kernel_ulong_t)&ad4114_device_info },
- { "ad4115", (kernel_ulong_t)&ad4115_device_info },
- { "ad4116", (kernel_ulong_t)&ad4116_device_info },
- { "ad7172-2", (kernel_ulong_t)&ad7172_2_device_info },
- { "ad7172-4", (kernel_ulong_t)&ad7172_4_device_info },
- { "ad7173-8", (kernel_ulong_t)&ad7173_8_device_info },
- { "ad7175-2", (kernel_ulong_t)&ad7175_2_device_info },
- { "ad7175-8", (kernel_ulong_t)&ad7175_8_device_info },
- { "ad7176-2", (kernel_ulong_t)&ad7176_2_device_info },
- { "ad7177-2", (kernel_ulong_t)&ad7177_2_device_info },
+ { .name = "ad4111", .driver_data = (kernel_ulong_t)&ad4111_device_info },
+ { .name = "ad4112", .driver_data = (kernel_ulong_t)&ad4112_device_info },
+ { .name = "ad4113", .driver_data = (kernel_ulong_t)&ad4113_device_info },
+ { .name = "ad4114", .driver_data = (kernel_ulong_t)&ad4114_device_info },
+ { .name = "ad4115", .driver_data = (kernel_ulong_t)&ad4115_device_info },
+ { .name = "ad4116", .driver_data = (kernel_ulong_t)&ad4116_device_info },
+ { .name = "ad7172-2", .driver_data = (kernel_ulong_t)&ad7172_2_device_info },
+ { .name = "ad7172-4", .driver_data = (kernel_ulong_t)&ad7172_4_device_info },
+ { .name = "ad7173-8", .driver_data = (kernel_ulong_t)&ad7173_8_device_info },
+ { .name = "ad7175-2", .driver_data = (kernel_ulong_t)&ad7175_2_device_info },
+ { .name = "ad7175-8", .driver_data = (kernel_ulong_t)&ad7175_8_device_info },
+ { .name = "ad7176-2", .driver_data = (kernel_ulong_t)&ad7176_2_device_info },
+ { .name = "ad7177-2", .driver_data = (kernel_ulong_t)&ad7177_2_device_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7173_id_table);
diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c
index 94b172dce866..071d26191b3c 100644
--- a/drivers/iio/adc/ad7191.c
+++ b/drivers/iio/adc/ad7191.c
@@ -535,7 +535,7 @@ static const struct of_device_id ad7191_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7191_of_match);
static const struct spi_device_id ad7191_id_table[] = {
- { "ad7191" },
+ { .name = "ad7191" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7191_id_table);
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
index 2cfad0bda752..d8f5886fcf69 100644
--- a/drivers/iio/adc/ad7192.c
+++ b/drivers/iio/adc/ad7192.c
@@ -1448,11 +1448,11 @@ static const struct of_device_id ad7192_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7192_of_match);
static const struct spi_device_id ad7192_ids[] = {
- { "ad7190", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] },
- { "ad7192", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] },
- { "ad7193", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] },
- { "ad7194", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] },
- { "ad7195", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] },
+ { .name = "ad7190", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] },
+ { .name = "ad7192", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] },
+ { .name = "ad7193", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] },
+ { .name = "ad7194", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] },
+ { .name = "ad7195", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7192_ids);
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 0ef36c249ab8..594a070c3745 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -453,8 +453,8 @@ static int ad7266_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7266_id[] = {
- { "ad7265", 0 },
- { "ad7266", 0 },
+ { .name = "ad7265" },
+ { .name = "ad7266" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7266_id);
diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c
index 2972e706de92..505fa68ee539 100644
--- a/drivers/iio/adc/ad7280a.c
+++ b/drivers/iio/adc/ad7280a.c
@@ -1082,7 +1082,7 @@ static int ad7280_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7280_id[] = {
- { "ad7280a", 0 },
+ { .name = "ad7280a" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7280_id);
diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c
index 0ba0fbad4f70..1d7bf46b80e2 100644
--- a/drivers/iio/adc/ad7292.c
+++ b/drivers/iio/adc/ad7292.c
@@ -299,7 +299,7 @@ static int ad7292_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7292_id_table[] = {
- { "ad7292", 0 },
+ { .name = "ad7292" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7292_id_table);
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index 5fa76aec360e..c669022b5dec 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -353,7 +353,7 @@ static const struct acpi_device_id ad7298_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids);
static const struct spi_device_id ad7298_id[] = {
- { "ad7298", 0 },
+ { .name = "ad7298" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7298_id);
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 9f77990a03f9..2e90f125a797 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -2116,24 +2116,24 @@ static const struct of_device_id ad7380_of_match_table[] = {
};
static const struct spi_device_id ad7380_id_table[] = {
- { "ad7380", (kernel_ulong_t)&ad7380_chip_info },
- { "ad7381", (kernel_ulong_t)&ad7381_chip_info },
- { "ad7383", (kernel_ulong_t)&ad7383_chip_info },
- { "ad7384", (kernel_ulong_t)&ad7384_chip_info },
- { "ad7386", (kernel_ulong_t)&ad7386_chip_info },
- { "ad7387", (kernel_ulong_t)&ad7387_chip_info },
- { "ad7388", (kernel_ulong_t)&ad7388_chip_info },
- { "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info },
- { "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info },
- { "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info },
- { "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info },
- { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info },
- { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info },
- { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info },
- { "ad7389-4", (kernel_ulong_t)&ad7389_4_chip_info },
- { "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info },
- { "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info },
- { "adaq4381-4", (kernel_ulong_t)&adaq4381_4_chip_info },
+ { .name = "ad7380", .driver_data = (kernel_ulong_t)&ad7380_chip_info },
+ { .name = "ad7381", .driver_data = (kernel_ulong_t)&ad7381_chip_info },
+ { .name = "ad7383", .driver_data = (kernel_ulong_t)&ad7383_chip_info },
+ { .name = "ad7384", .driver_data = (kernel_ulong_t)&ad7384_chip_info },
+ { .name = "ad7386", .driver_data = (kernel_ulong_t)&ad7386_chip_info },
+ { .name = "ad7387", .driver_data = (kernel_ulong_t)&ad7387_chip_info },
+ { .name = "ad7388", .driver_data = (kernel_ulong_t)&ad7388_chip_info },
+ { .name = "ad7380-4", .driver_data = (kernel_ulong_t)&ad7380_4_chip_info },
+ { .name = "ad7381-4", .driver_data = (kernel_ulong_t)&ad7381_4_chip_info },
+ { .name = "ad7383-4", .driver_data = (kernel_ulong_t)&ad7383_4_chip_info },
+ { .name = "ad7384-4", .driver_data = (kernel_ulong_t)&ad7384_4_chip_info },
+ { .name = "ad7386-4", .driver_data = (kernel_ulong_t)&ad7386_4_chip_info },
+ { .name = "ad7387-4", .driver_data = (kernel_ulong_t)&ad7387_4_chip_info },
+ { .name = "ad7388-4", .driver_data = (kernel_ulong_t)&ad7388_4_chip_info },
+ { .name = "ad7389-4", .driver_data = (kernel_ulong_t)&ad7389_4_chip_info },
+ { .name = "adaq4370-4", .driver_data = (kernel_ulong_t)&adaq4370_4_chip_info },
+ { .name = "adaq4380-4", .driver_data = (kernel_ulong_t)&adaq4380_4_chip_info },
+ { .name = "adaq4381-4", .driver_data = (kernel_ulong_t)&adaq4381_4_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7380_id_table);
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index 21d3f6aae972..c007f453ae18 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -411,42 +411,42 @@ static int ad7476_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7476_id[] = {
- { "ad7091", (kernel_ulong_t)&ad7091_chip_info },
- { "ad7091r", (kernel_ulong_t)&ad7091r_chip_info },
- { "ad7273", (kernel_ulong_t)&ad7273_chip_info },
- { "ad7274", (kernel_ulong_t)&ad7274_chip_info },
- { "ad7276", (kernel_ulong_t)&ad7276_chip_info },
- { "ad7277", (kernel_ulong_t)&ad7277_chip_info },
- { "ad7278", (kernel_ulong_t)&ad7278_chip_info },
- { "ad7466", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7467", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7468", (kernel_ulong_t)&ad7468_chip_info },
- { "ad7475", (kernel_ulong_t)&ad7475_chip_info },
- { "ad7476", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7476a", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7477", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7477a", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7478", (kernel_ulong_t)&ad7468_chip_info },
- { "ad7478a", (kernel_ulong_t)&ad7468_chip_info },
- { "ad7495", (kernel_ulong_t)&ad7495_chip_info },
- { "ad7910", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7920", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7940", (kernel_ulong_t)&ad7940_chip_info },
- { "adc081s", (kernel_ulong_t)&adc081s_chip_info },
- { "adc101s", (kernel_ulong_t)&adc101s_chip_info },
- { "adc121s", (kernel_ulong_t)&adc121s_chip_info },
- { "ads7866", (kernel_ulong_t)&ads7866_chip_info },
- { "ads7867", (kernel_ulong_t)&ads7867_chip_info },
- { "ads7868", (kernel_ulong_t)&ads7868_chip_info },
- { "bd79105", (kernel_ulong_t)&bd79105_chip_info },
+ { .name = "ad7091", .driver_data = (kernel_ulong_t)&ad7091_chip_info },
+ { .name = "ad7091r", .driver_data = (kernel_ulong_t)&ad7091r_chip_info },
+ { .name = "ad7273", .driver_data = (kernel_ulong_t)&ad7273_chip_info },
+ { .name = "ad7274", .driver_data = (kernel_ulong_t)&ad7274_chip_info },
+ { .name = "ad7276", .driver_data = (kernel_ulong_t)&ad7276_chip_info },
+ { .name = "ad7277", .driver_data = (kernel_ulong_t)&ad7277_chip_info },
+ { .name = "ad7278", .driver_data = (kernel_ulong_t)&ad7278_chip_info },
+ { .name = "ad7466", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7467", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7468", .driver_data = (kernel_ulong_t)&ad7468_chip_info },
+ { .name = "ad7475", .driver_data = (kernel_ulong_t)&ad7475_chip_info },
+ { .name = "ad7476", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7476a", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7477", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7477a", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7478", .driver_data = (kernel_ulong_t)&ad7468_chip_info },
+ { .name = "ad7478a", .driver_data = (kernel_ulong_t)&ad7468_chip_info },
+ { .name = "ad7495", .driver_data = (kernel_ulong_t)&ad7495_chip_info },
+ { .name = "ad7910", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7920", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7940", .driver_data = (kernel_ulong_t)&ad7940_chip_info },
+ { .name = "adc081s", .driver_data = (kernel_ulong_t)&adc081s_chip_info },
+ { .name = "adc101s", .driver_data = (kernel_ulong_t)&adc101s_chip_info },
+ { .name = "adc121s", .driver_data = (kernel_ulong_t)&adc121s_chip_info },
+ { .name = "ads7866", .driver_data = (kernel_ulong_t)&ads7866_chip_info },
+ { .name = "ads7867", .driver_data = (kernel_ulong_t)&ads7867_chip_info },
+ { .name = "ads7868", .driver_data = (kernel_ulong_t)&ads7868_chip_info },
+ { .name = "bd79105", .driver_data = (kernel_ulong_t)&bd79105_chip_info },
/*
* The ROHM BU79100G is identical to the TI's ADS7866 from the software
* point of view. The binding document mandates the ADS7866 to be
* marked as a fallback for the BU79100G, but we still need the SPI ID
* here to make the module loading work.
*/
- { "bu79100g", (kernel_ulong_t)&ads7866_chip_info },
- { "ltc2314-14", (kernel_ulong_t)&ltc2314_14_chip_info },
+ { .name = "bu79100g", .driver_data = (kernel_ulong_t)&ads7866_chip_info },
+ { .name = "ltc2314-14", .driver_data = (kernel_ulong_t)&ltc2314_14_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index 7e17ccbcedd0..1ebdfb8580ab 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -463,17 +463,17 @@ static int ad7606_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7606_id_table[] = {
- { "ad7605-4", (kernel_ulong_t)&ad7605_4_bus_info },
- { "ad7606-4", (kernel_ulong_t)&ad7606_4_bus_info },
- { "ad7606-6", (kernel_ulong_t)&ad7606_6_bus_info },
- { "ad7606-8", (kernel_ulong_t)&ad7606_8_bus_info },
- { "ad7606b", (kernel_ulong_t)&ad7606b_bus_info },
- { "ad7606c-16", (kernel_ulong_t)&ad7606c_16_bus_info },
- { "ad7606c-18", (kernel_ulong_t)&ad7606c_18_bus_info },
- { "ad7607", (kernel_ulong_t)&ad7607_bus_info },
- { "ad7608", (kernel_ulong_t)&ad7608_bus_info },
- { "ad7609", (kernel_ulong_t)&ad7609_bus_info },
- { "ad7616", (kernel_ulong_t)&ad7616_bus_info },
+ { .name = "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_bus_info },
+ { .name = "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_bus_info },
+ { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_bus_info },
+ { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_bus_info },
+ { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_bus_info },
+ { .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_bus_info },
+ { .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_bus_info },
+ { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_bus_info },
+ { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_bus_info },
+ { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_bus_info },
+ { .name = "ad7616", .driver_data = (kernel_ulong_t)&ad7616_bus_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7606_id_table);
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 9e4a66477d2d..92957196de59 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -285,12 +285,12 @@ static int ad7766_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7766_id[] = {
- { "ad7766", ID_AD7766 },
- { "ad7766-1", ID_AD7766_1 },
- { "ad7766-2", ID_AD7766_2 },
- { "ad7767", ID_AD7766 },
- { "ad7767-1", ID_AD7766_1 },
- { "ad7767-2", ID_AD7766_2 },
+ { .name = "ad7766", .driver_data = ID_AD7766 },
+ { .name = "ad7766-1", .driver_data = ID_AD7766_1 },
+ { .name = "ad7766-2", .driver_data = ID_AD7766_2 },
+ { .name = "ad7767", .driver_data = ID_AD7766 },
+ { .name = "ad7767-1", .driver_data = ID_AD7766_1 },
+ { .name = "ad7767-2", .driver_data = ID_AD7766_2 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7766_id);
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 598936e47fd2..e9060c1bbe6f 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -1918,10 +1918,10 @@ static int ad7768_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7768_id_table[] = {
- { "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 },
+ { .name = "ad7768-1", .driver_data = (kernel_ulong_t)&ad7768_chip_info },
+ { .name = "adaq7767-1", .driver_data = (kernel_ulong_t)&adaq7767_chip_info },
+ { .name = "adaq7768-1", .driver_data = (kernel_ulong_t)&adaq7768_chip_info },
+ { .name = "adaq7769-1", .driver_data = (kernel_ulong_t)&adaq7769_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7768_id_table);
diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c
index 003e23d6e242..79fb38d0f400 100644
--- a/drivers/iio/adc/ad7779.c
+++ b/drivers/iio/adc/ad7779.c
@@ -142,7 +142,6 @@ struct ad7779_state {
const struct ad7779_chip_info *chip_info;
struct clk *mclk;
struct iio_trigger *trig;
- struct completion completion;
unsigned int sampling_freq;
enum ad7779_filter filter_enabled;
struct iio_backend *back;
@@ -842,8 +841,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev
IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name,
st->trig);
if (ret)
- return dev_err_probe(dev, ret, "request IRQ %d failed\n",
- st->spi->irq);
+ return ret;
ret = devm_iio_trigger_register(dev, st->trig);
if (ret)
@@ -851,8 +849,6 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev
indio_dev->trig = iio_trigger_get(st->trig);
- init_completion(&st->completion);
-
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad7779_trigger_handler,
diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c
index 26382b1f8c9e..1cfad70320fe 100644
--- a/drivers/iio/adc/ad7780.c
+++ b/drivers/iio/adc/ad7780.c
@@ -347,10 +347,10 @@ static int ad7780_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7780_id[] = {
- { "ad7170", ID_AD7170 },
- { "ad7171", ID_AD7171 },
- { "ad7780", ID_AD7780 },
- { "ad7781", ID_AD7781 },
+ { .name = "ad7170", .driver_data = ID_AD7170 },
+ { .name = "ad7171", .driver_data = ID_AD7171 },
+ { .name = "ad7780", .driver_data = ID_AD7780 },
+ { .name = "ad7781", .driver_data = ID_AD7781 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7780_id);
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
index bcdc19e799aa..c2688307bbba 100644
--- a/drivers/iio/adc/ad7791.c
+++ b/drivers/iio/adc/ad7791.c
@@ -458,11 +458,11 @@ static int ad7791_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7791_spi_ids[] = {
- { "ad7787", AD7787 },
- { "ad7788", AD7788 },
- { "ad7789", AD7789 },
- { "ad7790", AD7790 },
- { "ad7791", AD7791 },
+ { .name = "ad7787", .driver_data = AD7787 },
+ { .name = "ad7788", .driver_data = AD7788 },
+ { .name = "ad7789", .driver_data = AD7789 },
+ { .name = "ad7790", .driver_data = AD7790 },
+ { .name = "ad7791", .driver_data = AD7791 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index ea1c192e2142..b0f44c144f20 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -829,15 +829,15 @@ static int ad7793_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7793_id[] = {
- { "ad7785", ID_AD7785 },
- { "ad7792", ID_AD7792 },
- { "ad7793", ID_AD7793 },
- { "ad7794", ID_AD7794 },
- { "ad7795", ID_AD7795 },
- { "ad7796", ID_AD7796 },
- { "ad7797", ID_AD7797 },
- { "ad7798", ID_AD7798 },
- { "ad7799", ID_AD7799 },
+ { .name = "ad7785", .driver_data = ID_AD7785 },
+ { .name = "ad7792", .driver_data = ID_AD7792 },
+ { .name = "ad7793", .driver_data = ID_AD7793 },
+ { .name = "ad7794", .driver_data = ID_AD7794 },
+ { .name = "ad7795", .driver_data = ID_AD7795 },
+ { .name = "ad7796", .driver_data = ID_AD7796 },
+ { .name = "ad7797", .driver_data = ID_AD7797 },
+ { .name = "ad7798", .driver_data = ID_AD7798 },
+ { .name = "ad7799", .driver_data = ID_AD7799 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7793_id);
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
index 068171d54596..62aaedbce748 100644
--- a/drivers/iio/adc/ad7887.c
+++ b/drivers/iio/adc/ad7887.c
@@ -328,7 +328,7 @@ static int ad7887_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7887_id[] = {
- { "ad7887", ID_AD7887 },
+ { .name = "ad7887", .driver_data = ID_AD7887 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7887_id);
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index acc87d486aa4..44e014da9722 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -360,13 +360,13 @@ static int ad7923_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7923_id[] = {
- { "ad7904", AD7904 },
- { "ad7914", AD7914 },
- { "ad7923", AD7924 },
- { "ad7924", AD7924 },
- { "ad7908", AD7908 },
- { "ad7918", AD7918 },
- { "ad7928", AD7928 },
+ { .name = "ad7904", .driver_data = AD7904 },
+ { .name = "ad7914", .driver_data = AD7914 },
+ { .name = "ad7923", .driver_data = AD7924 },
+ { .name = "ad7924", .driver_data = AD7924 },
+ { .name = "ad7908", .driver_data = AD7908 },
+ { .name = "ad7918", .driver_data = AD7918 },
+ { .name = "ad7928", .driver_data = AD7928 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7923_id);
diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c
index 7722cf9e8214..12656d3cdcec 100644
--- a/drivers/iio/adc/ad7944.c
+++ b/drivers/iio/adc/ad7944.c
@@ -865,9 +865,9 @@ static const struct of_device_id ad7944_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7944_of_match);
static const struct spi_device_id ad7944_spi_id[] = {
- { "ad7944", (kernel_ulong_t)&ad7944_chip_info },
- { "ad7985", (kernel_ulong_t)&ad7985_chip_info },
- { "ad7986", (kernel_ulong_t)&ad7986_chip_info },
+ { .name = "ad7944", .driver_data = (kernel_ulong_t)&ad7944_chip_info },
+ { .name = "ad7985", .driver_data = (kernel_ulong_t)&ad7985_chip_info },
+ { .name = "ad7986", .driver_data = (kernel_ulong_t)&ad7986_chip_info },
{ }
};
diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c
index ebc629bcfd4d..93a57a3d59b8 100644
--- a/drivers/iio/adc/ad7949.c
+++ b/drivers/iio/adc/ad7949.c
@@ -410,9 +410,9 @@ static const struct of_device_id ad7949_spi_of_id[] = {
MODULE_DEVICE_TABLE(of, ad7949_spi_of_id);
static const struct spi_device_id ad7949_spi_id[] = {
- { "ad7949", ID_AD7949 },
- { "ad7682", ID_AD7682 },
- { "ad7689", ID_AD7689 },
+ { .name = "ad7949", .driver_data = ID_AD7949 },
+ { .name = "ad7682", .driver_data = ID_AD7682 },
+ { .name = "ad7689", .driver_data = ID_AD7689 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7949_spi_id);
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 0c377f9a7f25..34b7b1256f0d 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -1406,13 +1406,13 @@ 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 },
- { "ad9643", (kernel_ulong_t)&ad9643_chip_tbl },
- { "ad9649", (kernel_ulong_t)&ad9649_chip_tbl, },
- { "ad9652", (kernel_ulong_t)&ad9652_chip_tbl, },
+ { .name = "ad9211", .driver_data = (kernel_ulong_t)&ad9211_chip_tbl },
+ { .name = "ad9265", .driver_data = (kernel_ulong_t)&ad9265_chip_tbl },
+ { .name = "ad9434", .driver_data = (kernel_ulong_t)&ad9434_chip_tbl },
+ { .name = "ad9467", .driver_data = (kernel_ulong_t)&ad9467_chip_tbl },
+ { .name = "ad9643", .driver_data = (kernel_ulong_t)&ad9643_chip_tbl },
+ { .name = "ad9649", .driver_data = (kernel_ulong_t)&ad9649_chip_tbl },
+ { .name = "ad9652", .driver_data = (kernel_ulong_t)&ad9652_chip_tbl },
{ }
};
MODULE_DEVICE_TABLE(spi, ad9467_ids);
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
index e57e24b6acdf..da6caabfe2a4 100644
--- a/drivers/iio/adc/ade9000.c
+++ b/drivers/iio/adc/ade9000.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
* ADE9000 driver
*
* Copyright 2025 Analog Devices Inc.
@@ -1769,7 +1769,7 @@ static int ade9000_probe(struct spi_device *spi)
};
static const struct spi_device_id ade9000_id[] = {
- { "ade9000", 0 },
+ { .name = "ade9000" },
{ }
};
MODULE_DEVICE_TABLE(spi, ade9000_id);
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index f610ad729bf3..0ac74ad92fca 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -988,7 +988,7 @@ static int at91_adc_probe(struct platform_device *pdev)
struct iio_dev *idev;
struct at91_adc_state *st;
u32 reg, prop;
- char *s;
+ const char *s;
idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state));
if (!idev)
@@ -1023,7 +1023,7 @@ static int at91_adc_probe(struct platform_device *pdev)
st->res = st->caps->high_res_bits;
if (st->caps->low_res_bits &&
- !of_property_read_string(node, "atmel,adc-use-res", (const char **)&s)
+ !of_property_read_string(node, "atmel,adc-use-res", &s)
&& !strcmp(s, "lowres"))
st->res = st->caps->low_res_bits;
diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
index 30672e584c10..aace7e103770 100644
--- a/drivers/iio/adc/envelope-detector.c
+++ b/drivers/iio/adc/envelope-detector.c
@@ -405,3 +405,4 @@ module_platform_driver(envelope_detector_driver);
MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
index b01ba86c2945..9fa63de4b9ca 100644
--- a/drivers/iio/adc/hi8435.c
+++ b/drivers/iio/adc/hi8435.c
@@ -526,7 +526,7 @@ static const struct of_device_id hi8435_dt_ids[] = {
MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
static const struct spi_device_id hi8435_id[] = {
- { "hi8435", 0 },
+ { .name = "hi8435" },
{ }
};
MODULE_DEVICE_TABLE(spi, hi8435_id);
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index 17e9badf1d1c..00adafa7cbd7 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -4,6 +4,8 @@
*
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
*/
+#include <linux/array_size.h>
+#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -20,6 +22,8 @@
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
+#define HX711_DATA_BITS 24
+
/* gain to pulse and scale conversion */
#define HX711_GAIN_MAX 3
#define HX711_RESET_GAIN 128
@@ -73,6 +77,20 @@ static int hx711_get_scale_to_gain(const int *gain_scale, int scale)
return -EINVAL;
}
+/**
+ * struct hx711_chip_info - per-variant static configuration
+ * @name: IIO device name
+ * @channels: channel specification array
+ * @num_channels: number of entries in @channels
+ * @iio_info: IIO info ops for this variant
+ */
+struct hx711_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels __counted_by_ptr(num_channels);
+ unsigned int num_channels;
+ const struct iio_info *iio_info;
+};
+
struct hx711_data {
struct device *dev;
struct gpio_desc *gpiod_pd_sck;
@@ -80,6 +98,7 @@ struct hx711_data {
int gain_set; /* gain set on device */
int gain_chan_a; /* gain for channel A */
int gain_scale[HX711_GAIN_MAX];
+ const struct hx711_chip_info *chip_info;
struct mutex lock;
/*
* triggered buffer
@@ -137,17 +156,18 @@ static int hx711_cycle(struct hx711_data *hx711_data)
return gpiod_get_value(hx711_data->gpiod_dout);
}
-static int hx711_read(struct hx711_data *hx711_data)
+static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses)
{
- int i, ret;
int value = 0;
- int val = gpiod_get_value(hx711_data->gpiod_dout);
+ int val;
+ int ret;
/* we double check if it's really down */
+ val = gpiod_get_value(hx711_data->gpiod_dout);
if (val)
return -EIO;
- for (i = 0; i < 24; i++) {
+ for (int i = 0; i < HX711_DATA_BITS; i++) {
value <<= 1;
ret = hx711_cycle(hx711_data);
if (ret)
@@ -156,7 +176,7 @@ static int hx711_read(struct hx711_data *hx711_data)
value ^= 0x800000;
- for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++)
+ for (int i = 0; i < trailing_pulses; i++)
hx711_cycle(hx711_data);
return value;
@@ -186,8 +206,9 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data)
static int hx711_reset(struct hx711_data *hx711_data)
{
- int val = hx711_wait_for_ready(hx711_data);
+ int val;
+ val = hx711_wait_for_ready(hx711_data);
if (val) {
/*
* an examination with the oszilloscope indicated
@@ -219,7 +240,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
if (hx711_data->gain_set == 32) {
hx711_data->gain_set = hx711_data->gain_chan_a;
- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0)
return ret;
@@ -231,7 +253,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
if (hx711_data->gain_set != 32) {
hx711_data->gain_set = 32;
- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0)
return ret;
@@ -244,10 +267,26 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
return 0;
}
-static int hx711_reset_read(struct hx711_data *hx711_data, int chan)
+static int hx711_set_hx711_channel(struct hx711_data *hx711_data,
+ const struct iio_chan_spec *chan,
+ int *trailing_pulses)
{
int ret;
- int val;
+
+ ret = hx711_set_gain_for_channel(hx711_data, chan->channel);
+ if (ret < 0)
+ return ret;
+
+ *trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set);
+
+ return 0;
+}
+
+static int hx711_reset_read(struct hx711_data *hx711_data,
+ const struct iio_chan_spec *chan)
+{
+ int trailing_pulses;
+ int ret;
/*
* hx711_reset() must be called from here
@@ -258,13 +297,11 @@ static int hx711_reset_read(struct hx711_data *hx711_data, int chan)
return -EIO;
}
- ret = hx711_set_gain_for_channel(hx711_data, chan);
+ ret = hx711_set_hx711_channel(hx711_data, chan, &trailing_pulses);
if (ret < 0)
return ret;
- val = hx711_read(hx711_data);
-
- return val;
+ return hx711_read(hx711_data, trailing_pulses);
}
static int hx711_read_raw(struct iio_dev *indio_dev,
@@ -277,7 +314,7 @@ static int hx711_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
mutex_lock(&hx711_data->lock);
- *val = hx711_reset_read(hx711_data, chan->channel);
+ *val = hx711_reset_read(hx711_data, chan);
mutex_unlock(&hx711_data->lock);
@@ -331,7 +368,8 @@ static int hx711_write_raw(struct iio_dev *indio_dev,
if (gain != 32)
hx711_data->gain_chan_a = gain;
- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0) {
mutex_unlock(&hx711_data->lock);
return ret;
@@ -366,8 +404,8 @@ static irqreturn_t hx711_trigger(int irq, void *p)
memset(&hx711_data->buffer, 0, sizeof(hx711_data->buffer));
iio_for_each_active_channel(indio_dev, i) {
- hx711_data->buffer.channel[j] = hx711_reset_read(hx711_data,
- indio_dev->channels[i].channel);
+ hx711_data->buffer.channel[j] =
+ hx711_reset_read(hx711_data, &indio_dev->channels[i]);
j++;
}
@@ -455,8 +493,16 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
IIO_CHAN_SOFT_TIMESTAMP(2),
};
+static const struct hx711_chip_info hx711_chip = {
+ .name = "hx711",
+ .channels = hx711_chan_spec,
+ .iio_info = &hx711_iio_info,
+ .num_channels = ARRAY_SIZE(hx711_chan_spec),
+};
+
static int hx711_probe(struct platform_device *pdev)
{
+ const struct hx711_chip_info *chip_info;
struct device *dev = &pdev->dev;
struct hx711_data *hx711_data;
struct iio_dev *indio_dev;
@@ -472,6 +518,12 @@ static int hx711_probe(struct platform_device *pdev)
mutex_init(&hx711_data->lock);
+ chip_info = device_get_match_data(dev);
+ if (!chip_info)
+ return dev_err_probe(dev, -ENODEV, "missing driver data\n");
+
+ hx711_data->chip_info = chip_info;
+
/*
* PD_SCK stands for power down and serial clock input of HX711
* in the driver it is an output
@@ -533,11 +585,11 @@ static int hx711_probe(struct platform_device *pdev)
hx711_data->data_ready_delay_ns =
1000000000 / hx711_data->clock_frequency;
- indio_dev->name = "hx711";
- indio_dev->info = &hx711_iio_info;
+ indio_dev->name = chip_info->name;
+ indio_dev->info = chip_info->iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = hx711_chan_spec;
- indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec);
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
@@ -554,7 +606,7 @@ static int hx711_probe(struct platform_device *pdev)
}
static const struct of_device_id of_hx711_match[] = {
- { .compatible = "avia,hx711", },
+ { .compatible = "avia,hx711", .data = &hx711_chip },
{ }
};
diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
new file mode 100644
index 000000000000..b0ff2be1aee0
--- /dev/null
+++ b/drivers/iio/adc/ltc2378.c
@@ -0,0 +1,857 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices LTC2378 ADC series driver
+ *
+ * Copyright (C) 2026 Analog Devices Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/device-id/spi.h>
+#include <linux/device-id/of.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pwm.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/offload/consumer.h>
+#include <linux/spi/offload/types.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/types.h>
+
+#define LTC2378_TDSDOBUSYL_NS 5
+#define LTC2378_TBUSYLH_NS 13
+#define LTC2378_TCNV_HIGH_NS 20
+#define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */
+
+#define LTC2378_CHANNEL(_sign, _real_bits, _storage_bits) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = _sign, \
+ .channel = 0, \
+ .channel2 = _sign ? 1 : 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = 0, \
+ .scan_type = { \
+ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \
+ IIO_SCAN_FORMAT_UNSIGNED_INT, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = _storage_bits - _real_bits, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define LTC2378_DIFF_CHANNEL(_real_bits) \
+ LTC2378_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16))
+
+#define LTC2378_PSEUDO_DIFF_CHANNEL(_real_bits) \
+ LTC2378_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16))
+
+#define LTC2378_OFFLOAD_CHANNEL(_sign, _real_bits, _storage_bits) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = _sign, \
+ .channel = 0, \
+ .channel2 = _sign ? 1 : 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = 0, \
+ .scan_type = { \
+ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \
+ IIO_SCAN_FORMAT_UNSIGNED_INT, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = 0, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+/*
+ * Currently, the available offload hardware + DMA configuration only supports
+ * pushing 32-bit data elements to DMA IIO buffers in CPU endianness. For 16-bit
+ * precision parts, those 32-bit elements (in CPU endianness) contain 2 bytes
+ * with data and 2 bytes always zeroed out. Nevertheless, for the offload use
+ * case, the IIO buffer is configured for 32 storage bits in CPU endianness so
+ * data is correctly aligned in user space despite 2 out of the 4 bytes being
+ * zeros.
+ */
+#define LTC2378_OFFLOAD_DIFF_CHANNEL(_real_bits) \
+ LTC2378_OFFLOAD_CHANNEL(1, (_real_bits), 32)
+
+#define LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(_real_bits) \
+ LTC2378_OFFLOAD_CHANNEL(0, (_real_bits), 32)
+
+struct ltc2378_chip_info {
+ const char *name;
+ unsigned int internal_ref_uV;
+ struct u32_fract internal_div;
+ struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */
+ struct iio_chan_spec offload_chan;
+ unsigned int max_sample_rate_Hz;
+ unsigned int tconv_ns;
+};
+
+struct ltc2378_state {
+ const struct ltc2378_chip_info *info;
+ struct gpio_desc *cnv_gpio;
+ struct spi_device *spi;
+ struct mutex lock; /* Protect data acquisition cycle */
+ int ref_uV;
+ struct spi_transfer xfer;
+ struct spi_transfer offload_xfer;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
+ struct pwm_waveform cnv_wf;
+ struct spi_message offload_msg;
+ struct spi_offload_trigger_config offload_trigger_config;
+ struct pwm_device *cnv_trigger;
+ unsigned int cnv_Hz;
+ unsigned int sample_freq_range[3];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the transfer buffers
+ * to live in their own cache lines.
+ */
+ struct {
+ union {
+ __be16 sample_buf16_be;
+ __be32 sample_buf32_be;
+ u16 sample_buf16;
+ u32 sample_buf32;
+ } data;
+ aligned_s64 timestamp;
+ } scan __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct ltc2378_chip_info ltc2338_18_chip_info = {
+ .name = "ltc2338-18",
+ .internal_ref_uV = 2048000,
+ .internal_div = { .numerator = 5, .denominator = 2 },
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2364_16_chip_info = {
+ .name = "ltc2364-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2364_18_chip_info = {
+ .name = "ltc2364-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2367_16_chip_info = {
+ .name = "ltc2367-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2367_18_chip_info = {
+ .name = "ltc2367-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2368_16_chip_info = {
+ .name = "ltc2368-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2368_18_chip_info = {
+ .name = "ltc2368-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2369_18_chip_info = {
+ .name = "ltc2369-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
+};
+
+static const struct ltc2378_chip_info ltc2370_16_chip_info = {
+ .name = "ltc2370-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
+};
+
+static const struct ltc2378_chip_info ltc2376_16_chip_info = {
+ .name = "ltc2376-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2376_18_chip_info = {
+ .name = "ltc2376-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2376_20_chip_info = {
+ .name = "ltc2376-20",
+ .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2377_16_chip_info = {
+ .name = "ltc2377-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2377_18_chip_info = {
+ .name = "ltc2377-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2377_20_chip_info = {
+ .name = "ltc2377-20",
+ .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2378_16_chip_info = {
+ .name = "ltc2378-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2378_18_chip_info = {
+ .name = "ltc2378-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2378_20_chip_info = {
+ .name = "ltc2378-20",
+ .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 675,
+};
+
+static const struct ltc2378_chip_info ltc2379_18_chip_info = {
+ .name = "ltc2379-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
+};
+
+static const struct ltc2378_chip_info ltc2380_16_chip_info = {
+ .name = "ltc2380-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
+};
+
+static int ltc2378_convert_and_acquire(struct ltc2378_state *st)
+{
+ int ret;
+
+ /* Cause a rising edge of CNV to initiate a new ADC conversion */
+ gpiod_set_value_cansleep(st->cnv_gpio, 1);
+ fsleep(LTC2378_MAX_DATA_WAIT_US);
+ ret = spi_sync_transfer(st->spi, &st->xfer, 1);
+ gpiod_set_value_cansleep(st->cnv_gpio, 0);
+
+ return ret;
+}
+
+static irqreturn_t ltc2378_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret < 0)
+ goto err_out;
+
+ iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
+ pf->timestamp);
+
+err_out:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int ltc2378_channel_single_read(const struct iio_chan_spec *chan,
+ struct ltc2378_state *st, int *val)
+{
+ const struct iio_scan_type *scan_type = &chan->scan_type;
+ u32 sample;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret)
+ return ret;
+
+ if (chan->scan_type.endianness == IIO_BE) {
+ if (chan->scan_type.realbits > 16)
+ sample = be32_to_cpu(st->scan.data.sample_buf32_be);
+ else
+ sample = be16_to_cpu(st->scan.data.sample_buf16_be);
+ } else { /* IIO_CPU */
+ if (chan->scan_type.realbits > 16)
+ sample = st->scan.data.sample_buf32;
+ else
+ sample = st->scan.data.sample_buf16;
+ }
+
+ sample >>= chan->scan_type.shift;
+
+ if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT)
+ *val = sign_extend32(sample, scan_type->realbits - 1);
+ else
+ *val = sample;
+
+ return 0;
+}
+
+static int ltc2378_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ltc2378_channel_single_read(chan, st, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE: {
+ struct u32_fract fract = st->info->internal_div;
+ *val = st->ref_uV / MILLI;
+ if (fract.numerator && fract.denominator)
+ *val = mult_frac(*val, fract.numerator, fract.denominator);
+ /*
+ * For all LTC2378-like devices, the amount of bits that express
+ * voltage magnitude depend on the polarity / output code format:
+ * - straight binary: All precision/resolution bits are used.
+ * - 2's complement: One of the precision bits is used for sign.
+ */
+ if (chan->scan_type.format == IIO_SCAN_FORMAT_SIGNED_INT)
+ *val2 = chan->scan_type.realbits - 1;
+ else
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->cnv_Hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2378_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = st->sample_freq_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * SPI offload wiring schema
+ *
+ * +-------------+ +-------------+
+ * | CNV |<-----+--| GPIO |
+ * | | +--| PWM0 |
+ * | | | |
+ * | | +--| PWM1 |
+ * | | | +-------------+
+ * | | +->| TRIGGER |
+ * | | | |
+ * | ADC | | SPI |
+ * | | | controller |
+ * | | | |
+ * | SDI |<--------| SDO |
+ * | SDO |-------->| SDI |
+ * | SCLK |<--------| SCLK |
+ * +-------------+ +-------------+
+ *
+ */
+static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz)
+{
+ struct spi_offload_trigger_config config = st->offload_trigger_config;
+ unsigned int min_read_offset, offload_period_ns;
+ struct pwm_waveform cnv_wf = { };
+ u64 target = LTC2378_TCNV_HIGH_NS;
+ unsigned int count;
+ u64 offload_offset_ns;
+ int ret;
+
+ if (freq_Hz == 0)
+ return -EINVAL;
+
+ if (!in_range(freq_Hz, 1, st->info->max_sample_rate_Hz))
+ return -ERANGE;
+
+ /* Configure CNV PWM waveform */
+ cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz);
+
+ /*
+ * Ensure CNV high time meets minimum requirement (20ns). The PWM
+ * hardware may round the duty cycle, so iterate until we get at least
+ * the minimum required high time (or reach a try count limit).
+ */
+ count = 100;
+ do {
+ cnv_wf.duty_length_ns = target;
+ ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf);
+ if (ret)
+ return ret;
+ target += 10; /* Increment by PWM duty cycle period */
+ } while (count-- && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS);
+
+ /* Check the minimum CNV high time is met */
+ if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS)
+ return -EDOM;
+
+ /*
+ * Configure SPI offload PWM trigger.
+ * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL.
+ * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns)
+ *
+ * Use the same period as CNV PWM to avoid timing issues.
+ * Convert back from period to frequency for the SPI offload API.
+ */
+ offload_period_ns = cnv_wf.period_length_ns;
+ config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns);
+ min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS;
+ offload_offset_ns = min_read_offset;
+ count = 100;
+ do {
+ config.periodic.offset_ns = offload_offset_ns;
+ ret = spi_offload_trigger_validate(st->offload_trigger, &config);
+ if (ret)
+ return ret;
+ offload_offset_ns += 10;
+ } while (count-- && config.periodic.offset_ns < min_read_offset);
+
+ /* Check the minimum CNV to SCLK delay is met */
+ if (config.periodic.offset_ns < min_read_offset)
+ return -EDOM;
+
+ /* Check the PWM periods remain the same */
+ offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz);
+ if (cnv_wf.period_length_ns != offload_period_ns)
+ return -EDOM;
+
+ st->offload_trigger_config = config;
+ st->cnv_wf = cnv_wf;
+ st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns);
+
+ return 0;
+}
+
+static int ltc2378_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ltc2378_update_conversion_rate(st, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ltc2378_iio_info = {
+ .read_raw = &ltc2378_read_raw,
+};
+
+static const struct iio_info ltc2378_offload_iio_info = {
+ .read_raw = &ltc2378_read_raw,
+ .read_avail = &ltc2378_read_avail,
+ .write_raw = &ltc2378_write_raw,
+};
+
+static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, true);
+ if (ret)
+ return ret;
+
+ ret = spi_offload_trigger_enable(st->offload, st->offload_trigger,
+ &st->offload_trigger_config);
+ if (ret)
+ goto out_pwm_disable;
+
+ return 0;
+
+out_pwm_disable:
+ pwm_disable(st->cnv_trigger);
+ return ret;
+}
+
+static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = {
+ .postenable = &ltc2378_offload_buffer_postenable,
+ .predisable = &ltc2378_offload_buffer_predisable,
+};
+
+static int ltc2378_prepare_offload_message(struct device *dev,
+ struct ltc2378_state *st)
+{
+ unsigned int resolution = st->info->offload_chan.scan_type.realbits;
+
+ st->offload_xfer.bits_per_word = resolution;
+ st->offload_xfer.len = spi_bpw_to_bytes(resolution);
+ st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+
+ /* Initialize message with offload */
+ spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1);
+ st->offload_msg.offload = st->offload;
+
+ return devm_spi_optimize_message(dev, st->spi, &st->offload_msg);
+}
+
+static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev,
+ struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct dma_chan *rx_dma;
+
+ indio_dev->setup_ops = &ltc2378_offload_buffer_ops;
+
+ st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
+ SPI_OFFLOAD_TRIGGER_PERIODIC);
+ if (IS_ERR(st->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
+ "failed to get offload trigger\n");
+
+ st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC;
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n");
+
+ return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
+ IIO_BUFFER_DIRECTION_IN);
+}
+
+static int ltc2378_pwm_get(struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+
+ st->cnv_trigger = devm_pwm_get(dev, NULL);
+ if (IS_ERR(st->cnv_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_trigger),
+ "failed to get cnv pwm\n");
+
+ /*
+ * Disable the PWM connected to CNV in case it was left running by
+ * something else.
+ */
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct spi_offload_config ltc2378_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
+};
+
+static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st)
+{
+ int ret;
+
+ /*
+ * The internal reference buffer amplifies both the internal reference
+ * and REFIN by a factor of 2.
+ */
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret == -ENODEV) { /* refin is optional */
+ st->ref_uV = st->info->internal_ref_uV * 2;
+ return 0;
+ }
+
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+
+ st->ref_uV = ret * 2;
+
+ return 0;
+}
+
+static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st)
+{
+ int ret;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read ref regulator\n");
+
+ st->ref_uV = ret;
+
+ return 0;
+}
+
+static int ltc2378_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ltc2378_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -EINVAL;
+
+ if (st->info->internal_ref_uV)
+ ret = ltc2378_refin_setup(dev, st);
+ else
+ ret = ltc2378_ref_setup(dev, st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(st->cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
+ "failed to get CNV GPIO");
+
+ st->offload = devm_spi_offload_get(dev, spi, &ltc2378_offload_config);
+ ret = PTR_ERR_OR_ZERO(st->offload);
+ /* Fall back to low speed usage when no SPI offload is available. */
+ if (ret == -ENODEV) {
+ indio_dev->info = &ltc2378_iio_info;
+ indio_dev->channels = st->info->chan;
+ indio_dev->num_channels = ARRAY_SIZE(st->info->chan);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ltc2378_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
+ } else if (ret) {
+ return dev_err_probe(dev, ret, "failed to get offload\n");
+ } else {
+ indio_dev->info = &ltc2378_offload_iio_info;
+ indio_dev->channels = &st->info->offload_chan;
+ indio_dev->num_channels = 1;
+ ret = ltc2378_spi_offload_setup(indio_dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to setup SPI offload\n");
+
+ ret = ltc2378_pwm_get(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get PWM\n");
+
+ st->sample_freq_range[0] = 1; /* min */
+ st->sample_freq_range[1] = 1; /* step */
+ st->sample_freq_range[2] = st->info->max_sample_rate_Hz; /* max */
+
+ /*
+ * Start with a slower sampling rate so there is some room for
+ * adjusting the sample averaging and the sampling frequency
+ * without hitting the maximum conversion rate.
+ */
+ ret = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_Hz >> 4);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set offload samp freq\n");
+
+ ret = ltc2378_prepare_offload_message(&spi->dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to optimize SPI message\n");
+
+ /*
+ * Set single-read transfer bits_per_word so the SPI subsystem
+ * rearranges data to CPU endianness, enabling us to reuse
+ * offload_chan specifications for single-shot reads.
+ */
+ st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits;
+ }
+
+ st->xfer.rx_buf = &st->scan.data;
+ st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ltc2378_of_match[] = {
+ { .compatible = "adi,ltc2338-18", .data = &ltc2338_18_chip_info },
+ { .compatible = "adi,ltc2364-16", .data = &ltc2364_16_chip_info },
+ { .compatible = "adi,ltc2364-18", .data = &ltc2364_18_chip_info },
+ { .compatible = "adi,ltc2367-16", .data = &ltc2367_16_chip_info },
+ { .compatible = "adi,ltc2367-18", .data = &ltc2367_18_chip_info },
+ { .compatible = "adi,ltc2368-16", .data = &ltc2368_16_chip_info },
+ { .compatible = "adi,ltc2368-18", .data = &ltc2368_18_chip_info },
+ { .compatible = "adi,ltc2369-18", .data = &ltc2369_18_chip_info },
+ { .compatible = "adi,ltc2370-16", .data = &ltc2370_16_chip_info },
+ { .compatible = "adi,ltc2376-16", .data = &ltc2376_16_chip_info },
+ { .compatible = "adi,ltc2376-18", .data = &ltc2376_18_chip_info },
+ { .compatible = "adi,ltc2376-20", .data = &ltc2376_20_chip_info },
+ { .compatible = "adi,ltc2377-16", .data = &ltc2377_16_chip_info },
+ { .compatible = "adi,ltc2377-18", .data = &ltc2377_18_chip_info },
+ { .compatible = "adi,ltc2377-20", .data = &ltc2377_20_chip_info },
+ { .compatible = "adi,ltc2378-16", .data = &ltc2378_16_chip_info },
+ { .compatible = "adi,ltc2378-18", .data = &ltc2378_18_chip_info },
+ { .compatible = "adi,ltc2378-20", .data = &ltc2378_20_chip_info },
+ { .compatible = "adi,ltc2379-18", .data = &ltc2379_18_chip_info },
+ { .compatible = "adi,ltc2380-16", .data = &ltc2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc2378_of_match);
+
+static const struct spi_device_id ltc2378_spi_id[] = {
+ { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)&ltc2338_18_chip_info },
+ { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)&ltc2364_16_chip_info },
+ { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)&ltc2364_18_chip_info },
+ { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)&ltc2367_16_chip_info },
+ { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)&ltc2367_18_chip_info },
+ { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)&ltc2368_16_chip_info },
+ { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)&ltc2368_18_chip_info },
+ { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)&ltc2369_18_chip_info },
+ { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)&ltc2370_16_chip_info },
+ { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)&ltc2376_16_chip_info },
+ { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)&ltc2376_18_chip_info },
+ { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)&ltc2376_20_chip_info },
+ { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)&ltc2377_16_chip_info },
+ { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)&ltc2377_18_chip_info },
+ { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)&ltc2377_20_chip_info },
+ { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)&ltc2378_16_chip_info },
+ { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)&ltc2378_18_chip_info },
+ { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)&ltc2378_20_chip_info },
+ { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)&ltc2379_18_chip_info },
+ { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)&ltc2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ltc2378_spi_id);
+
+static struct spi_driver ltc2378_driver = {
+ .driver = {
+ .name = "ltc2378",
+ .of_match_table = ltc2378_of_match
+ },
+ .probe = ltc2378_probe,
+ .id_table = ltc2378_spi_id,
+};
+module_spi_driver(ltc2378_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
+MODULE_IMPORT_NS("SPI_OFFLOAD");
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 8b2c7b2a9c8f..fb6f12316724 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -72,12 +72,12 @@ enum max1027_id {
};
static const struct spi_device_id max1027_id[] = {
- { "max1027", max1027 },
- { "max1029", max1029 },
- { "max1031", max1031 },
- { "max1227", max1227 },
- { "max1229", max1229 },
- { "max1231", max1231 },
+ { .name = "max1027", .driver_data = max1027 },
+ { .name = "max1029", .driver_data = max1029 },
+ { .name = "max1031", .driver_data = max1031 },
+ { .name = "max1227", .driver_data = max1227 },
+ { .name = "max1229", .driver_data = max1229 },
+ { .name = "max1231", .driver_data = max1231 },
{ }
};
MODULE_DEVICE_TABLE(spi, max1027_id);
diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c
index d394e03cc4e8..75e51ce3077e 100644
--- a/drivers/iio/adc/max1118.c
+++ b/drivers/iio/adc/max1118.c
@@ -256,9 +256,9 @@ static int max1118_probe(struct spi_device *spi)
}
static const struct spi_device_id max1118_id[] = {
- { "max1117", max1117 },
- { "max1118", max1118 },
- { "max1119", max1119 },
+ { .name = "max1117", .driver_data = max1117 },
+ { .name = "max1118", .driver_data = max1118 },
+ { .name = "max1119", .driver_data = max1119 },
{ }
};
MODULE_DEVICE_TABLE(spi, max1118_id);
diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c
index 6c803df220b6..63b6561dcf47 100644
--- a/drivers/iio/adc/max11205.c
+++ b/drivers/iio/adc/max11205.c
@@ -145,8 +145,8 @@ static int max11205_probe(struct spi_device *spi)
}
static const struct spi_device_id max11205_spi_ids[] = {
- { "max11205a", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] },
- { "max11205b", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] },
+ { .name = "max11205a", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] },
+ { .name = "max11205b", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] },
{ }
};
MODULE_DEVICE_TABLE(spi, max11205_spi_ids);
diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c
index dc1b96356592..23ae684cf08b 100644
--- a/drivers/iio/adc/max11410.c
+++ b/drivers/iio/adc/max11410.c
@@ -1025,7 +1025,7 @@ static const struct of_device_id max11410_spi_of_id[] = {
MODULE_DEVICE_TABLE(of, max11410_spi_of_id);
static const struct spi_device_id max11410_id[] = {
- { "max11410" },
+ { .name = "max11410" },
{ }
};
MODULE_DEVICE_TABLE(spi, max11410_id);
diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c
index d62c1a011659..b3cecbf9a89e 100644
--- a/drivers/iio/adc/max1241.c
+++ b/drivers/iio/adc/max1241.c
@@ -15,10 +15,6 @@
#define MAX1241_VAL_MASK GENMASK(11, 0)
#define MAX1241_SHUTDOWN_DELAY_USEC 4
-enum max1241_id {
- max1241,
-};
-
struct max1241 {
struct spi_device *spi;
struct mutex lock;
@@ -166,7 +162,7 @@ static int max1241_probe(struct spi_device *spi)
else
dev_dbg(dev, "no shutdown pin passed, low-power mode disabled");
- indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->name = "max1241";
indio_dev->info = &max1241_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = max1241_channels;
@@ -176,9 +172,10 @@ static int max1241_probe(struct spi_device *spi)
}
static const struct spi_device_id max1241_id[] = {
- { "max1241", max1241 },
+ { .name = "max1241" },
{ }
};
+MODULE_DEVICE_TABLE(spi, max1241_id);
static const struct of_device_id max1241_dt_ids[] = {
{ .compatible = "maxim,max1241" },
diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c
index 09017163b191..58adeac62dde 100644
--- a/drivers/iio/adc/max14001.c
+++ b/drivers/iio/adc/max14001.c
@@ -362,8 +362,8 @@ static struct max14001_chip_info max14002_chip_info = {
};
static const struct spi_device_id max14001_id_table[] = {
- { "max14001", (kernel_ulong_t)&max14001_chip_info },
- { "max14002", (kernel_ulong_t)&max14002_chip_info },
+ { .name = "max14001", .driver_data = (kernel_ulong_t)&max14001_chip_info },
+ { .name = "max14002", .driver_data = (kernel_ulong_t)&max14002_chip_info },
{ }
};
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 686c519aa3d2..18cb957e7095 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -476,19 +476,19 @@ static const struct of_device_id mcp320x_dt_ids[] = {
MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
static const struct spi_device_id mcp320x_id[] = {
- { "mcp3001", mcp3001 },
- { "mcp3002", mcp3002 },
- { "mcp3004", mcp3004 },
- { "mcp3008", mcp3008 },
- { "mcp3201", mcp3201 },
- { "mcp3202", mcp3202 },
- { "mcp3204", mcp3204 },
- { "mcp3208", mcp3208 },
- { "mcp3301", mcp3301 },
- { "mcp3550-50", mcp3550_50 },
- { "mcp3550-60", mcp3550_60 },
- { "mcp3551", mcp3551 },
- { "mcp3553", mcp3553 },
+ { .name = "mcp3001", .driver_data = mcp3001 },
+ { .name = "mcp3002", .driver_data = mcp3002 },
+ { .name = "mcp3004", .driver_data = mcp3004 },
+ { .name = "mcp3008", .driver_data = mcp3008 },
+ { .name = "mcp3201", .driver_data = mcp3201 },
+ { .name = "mcp3202", .driver_data = mcp3202 },
+ { .name = "mcp3204", .driver_data = mcp3204 },
+ { .name = "mcp3208", .driver_data = mcp3208 },
+ { .name = "mcp3301", .driver_data = mcp3301 },
+ { .name = "mcp3550-50", .driver_data = mcp3550_50 },
+ { .name = "mcp3550-60", .driver_data = mcp3550_60 },
+ { .name = "mcp3551", .driver_data = mcp3551 },
+ { .name = "mcp3553", .driver_data = mcp3553 },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp320x_id);
diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c
index 36675563829e..259ac4ebdedf 100644
--- a/drivers/iio/adc/mcp3564.c
+++ b/drivers/iio/adc/mcp3564.c
@@ -1451,18 +1451,18 @@ static const struct of_device_id mcp3564_dt_ids[] = {
MODULE_DEVICE_TABLE(of, mcp3564_dt_ids);
static const struct spi_device_id mcp3564_id[] = {
- { "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] },
- { "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] },
- { "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] },
- { "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] },
- { "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] },
- { "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] },
- { "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] },
- { "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] },
- { "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] },
- { "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] },
- { "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] },
- { "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] },
+ { .name = "mcp3461", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] },
+ { .name = "mcp3462", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] },
+ { .name = "mcp3464", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] },
+ { .name = "mcp3561", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] },
+ { .name = "mcp3562", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] },
+ { .name = "mcp3564", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] },
+ { .name = "mcp3461r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] },
+ { .name = "mcp3462r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] },
+ { .name = "mcp3464r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] },
+ { .name = "mcp3561r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] },
+ { .name = "mcp3562r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] },
+ { .name = "mcp3564r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp3564_id);
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
index 5bf74b49bdb5..797424f59f74 100644
--- a/drivers/iio/adc/mcp3911.c
+++ b/drivers/iio/adc/mcp3911.c
@@ -927,13 +927,13 @@ static const struct of_device_id mcp3911_dt_ids[] = {
MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
static const struct spi_device_id mcp3911_id[] = {
- { "mcp3910", (kernel_ulong_t)&mcp3911_chip_info[MCP3910] },
- { "mcp3911", (kernel_ulong_t)&mcp3911_chip_info[MCP3911] },
- { "mcp3912", (kernel_ulong_t)&mcp3911_chip_info[MCP3912] },
- { "mcp3913", (kernel_ulong_t)&mcp3911_chip_info[MCP3913] },
- { "mcp3914", (kernel_ulong_t)&mcp3911_chip_info[MCP3914] },
- { "mcp3918", (kernel_ulong_t)&mcp3911_chip_info[MCP3918] },
- { "mcp3919", (kernel_ulong_t)&mcp3911_chip_info[MCP3919] },
+ { .name = "mcp3910", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3910] },
+ { .name = "mcp3911", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3911] },
+ { .name = "mcp3912", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3912] },
+ { .name = "mcp3913", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3913] },
+ { .name = "mcp3914", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3914] },
+ { .name = "mcp3918", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3918] },
+ { .name = "mcp3919", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3919] },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp3911_id);
diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c
new file mode 100644
index 000000000000..4f73ac42abb7
--- /dev/null
+++ b/drivers/iio/adc/mt6323-auxadc.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Roman Vivchar <rva333@protonmail.com>
+ *
+ * Based on drivers/iio/adc/mt6359-auxadc.c
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/stringify.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <linux/mfd/mt6323/registers.h>
+
+#include <dt-bindings/iio/adc/mediatek,mt6323-auxadc.h>
+
+#define AUXADC_STRUP_CON10_RSTB_SEL BIT(7)
+#define AUXADC_STRUP_CON10_RSTB_SW BIT(5)
+
+#define AUXADC_TOP_CKPDN2_CTL_CK BIT(5)
+
+#define AUXADC_TRIM_CH2_MASK GENMASK(11, 10)
+#define AUXADC_TRIM_CH4_MASK GENMASK(9, 8)
+#define AUXADC_TRIM_CH5_MASK GENMASK(5, 4)
+#define AUXADC_TRIM_CH6_MASK GENMASK(3, 2)
+
+#define AUXADC_CON27_VREF18_ENB_MD BIT(15)
+#define AUXADC_CON27_MD_STATUS BIT(0)
+
+#define AUXADC_CON19_GPS_STATUS BIT(1)
+
+#define AUXADC_CON26_VREF18_SELB BIT(1)
+#define AUXADC_CON26_DECI_GDLY_SEL BIT(0)
+
+#define AUXADC_CON11_VBUF_EN BIT(4)
+
+#define AUXADC_CON19_DECI_GDLY_MASK GENMASK(15, 14)
+#define AUXADC_ADC19_BUSY_MASK GENMASK(15, 1)
+#define AUXADC_READY_MASK BIT(15)
+#define AUXADC_DATA_MASK GENMASK(14, 0)
+
+#define AUXADC_CON9_OSR_MASK GENMASK(12, 10)
+#define AUXADC_DEFAULT_OSR 3
+
+#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _chan, \
+ .address = _addr, \
+ .datasheet_name = __stringify(_name), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+/*
+ * AUXADC reports everything in mV, including temperature and
+ * current channels. Channel macros are mapped such that their
+ * ID matches their respective hardware bit position in CON22.
+ */
+static const struct iio_chan_spec mt6323_auxadc_channels[] = {
+ MTK_PMIC_IIO_CHAN(baton2, MT6323_AUXADC_BATON2, MT6323_AUXADC_ADC6),
+ MTK_PMIC_IIO_CHAN(ch6, MT6323_AUXADC_CH6, MT6323_AUXADC_ADC11),
+ MTK_PMIC_IIO_CHAN(bat_temp, MT6323_AUXADC_BAT_TEMP, MT6323_AUXADC_ADC5),
+ MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4),
+ MTK_PMIC_IIO_CHAN(vcdt, MT6323_AUXADC_VCDT, MT6323_AUXADC_ADC2),
+ MTK_PMIC_IIO_CHAN(baton1, MT6323_AUXADC_BATON1, MT6323_AUXADC_ADC3),
+ MTK_PMIC_IIO_CHAN(isense, MT6323_AUXADC_ISENSE, MT6323_AUXADC_ADC1),
+ MTK_PMIC_IIO_CHAN(batsns, MT6323_AUXADC_BATSNS, MT6323_AUXADC_ADC0),
+ MTK_PMIC_IIO_CHAN(accdet, MT6323_AUXADC_ACCDET, MT6323_AUXADC_ADC7),
+};
+
+/*
+ * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy:
+ * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM)
+ *
+ * Therefore, PWRAP regmap should be obtained using dev->parent->parent.
+ */
+struct mt6323_auxadc {
+ struct regmap *regmap;
+ /* AUXADC doesn't support reading multiple channels simultaneously. */
+ struct mutex lock;
+};
+
+static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc)
+{
+ struct regmap *map = auxadc->regmap;
+ u32 val;
+ int ret;
+
+ ret = regmap_read(map, MT6323_AUXADC_CON19, &val);
+ if (ret)
+ return ret;
+
+ /* The ADC is idle. */
+ if (!(val & AUXADC_CON19_DECI_GDLY_MASK))
+ return 0;
+
+ ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19,
+ val, !(val & AUXADC_ADC19_BUSY_MASK),
+ 10, 500);
+ if (ret)
+ return ret;
+
+ return regmap_clear_bits(map, MT6323_AUXADC_CON19,
+ AUXADC_CON19_DECI_GDLY_MASK);
+}
+
+static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc,
+ unsigned long channel)
+{
+ struct regmap *map = auxadc->regmap;
+ int ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel));
+}
+
+static int mt6323_auxadc_release(struct mt6323_auxadc *auxadc,
+ unsigned long channel)
+{
+ struct regmap *map = auxadc->regmap;
+ int ret;
+
+ ret = regmap_clear_bits(map, MT6323_AUXADC_CON22, BIT(channel));
+ if (ret)
+ return ret;
+
+ return regmap_clear_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN);
+}
+
+static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc,
+ const struct iio_chan_spec *chan, int *out)
+{
+ struct regmap *map = auxadc->regmap;
+ u32 val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(map, chan->address,
+ val, (val & AUXADC_READY_MASK),
+ 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ *out = FIELD_GET(AUXADC_DATA_MASK, val);
+
+ return 0;
+}
+
+static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct mt6323_auxadc *auxadc = iio_priv(indio_dev);
+ int ret, mult;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->channel == MT6323_AUXADC_ISENSE ||
+ chan->channel == MT6323_AUXADC_BATSNS)
+ mult = 4;
+ else
+ mult = 1;
+
+ /* 1800mV full range with 15-bit resolution. */
+ *val = mult * 1800;
+ *val2 = 15;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_RAW: {
+ guard(mutex)(&auxadc->lock);
+
+ ret = mt6323_auxadc_prepare_channel(auxadc);
+ if (ret)
+ return ret;
+
+ ret = mt6323_auxadc_request(auxadc, chan->channel);
+ if (ret)
+ return ret;
+
+ /* Hardware limitation: the AUXADC needs a delay to become ready. */
+ fsleep(300);
+
+ ret = mt6323_auxadc_read(auxadc, chan, val);
+
+ if (mt6323_auxadc_release(auxadc, chan->channel))
+ dev_err(&indio_dev->dev,
+ "failed to release channel %d\n", chan->channel);
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt6323_auxadc_init(struct mt6323_auxadc *auxadc)
+{
+ struct regmap *map = auxadc->regmap;
+ int ret;
+
+ ret = regmap_set_bits(map, MT6323_STRUP_CON10,
+ AUXADC_STRUP_CON10_RSTB_SW |
+ AUXADC_STRUP_CON10_RSTB_SEL);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_TOP_CKPDN2, AUXADC_TOP_CKPDN2_CTL_CK);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(map, MT6323_AUXADC_CON10,
+ AUXADC_TRIM_CH2_MASK | AUXADC_TRIM_CH4_MASK |
+ AUXADC_TRIM_CH5_MASK | AUXADC_TRIM_CH6_MASK,
+ FIELD_PREP(AUXADC_TRIM_CH2_MASK, 1) |
+ FIELD_PREP(AUXADC_TRIM_CH4_MASK, 1) |
+ FIELD_PREP(AUXADC_TRIM_CH5_MASK, 1) |
+ FIELD_PREP(AUXADC_TRIM_CH6_MASK, 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON27,
+ AUXADC_CON27_VREF18_ENB_MD |
+ AUXADC_CON27_MD_STATUS);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON19, AUXADC_CON19_GPS_STATUS);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON26,
+ AUXADC_CON26_VREF18_SELB |
+ AUXADC_CON26_DECI_GDLY_SEL);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(map, MT6323_AUXADC_CON9, AUXADC_CON9_OSR_MASK,
+ FIELD_PREP(AUXADC_CON9_OSR_MASK, AUXADC_DEFAULT_OSR));
+}
+
+static const struct iio_info mt6323_auxadc_iio_info = {
+ .read_raw = mt6323_auxadc_read_raw,
+};
+
+static int mt6323_auxadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mt6323_auxadc *auxadc;
+ struct regmap *regmap;
+ struct iio_dev *iio;
+ int ret;
+
+ regmap = dev_get_regmap(dev->parent->parent, NULL);
+ if (!regmap)
+ return dev_err_probe(dev, -ENODEV, "failed to get regmap\n");
+
+ iio = devm_iio_device_alloc(dev, sizeof(*auxadc));
+ if (!iio)
+ return -ENOMEM;
+
+ auxadc = iio_priv(iio);
+ auxadc->regmap = regmap;
+
+ ret = devm_mutex_init(dev, &auxadc->lock);
+ if (ret)
+ return ret;
+
+ ret = mt6323_auxadc_init(auxadc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize auxadc\n");
+
+ iio->name = "mt6323-auxadc";
+ iio->info = &mt6323_auxadc_iio_info;
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->channels = mt6323_auxadc_channels;
+ iio->num_channels = ARRAY_SIZE(mt6323_auxadc_channels);
+
+ return devm_iio_device_register(dev, iio);
+}
+
+static const struct of_device_id mt6323_auxadc_of_match[] = {
+ { .compatible = "mediatek,mt6323-auxadc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt6323_auxadc_of_match);
+
+static struct platform_driver mt6323_auxadc_driver = {
+ .driver = {
+ .name = "mt6323-auxadc",
+ .of_match_table = mt6323_auxadc_of_match,
+ },
+ .probe = mt6323_auxadc_probe,
+};
+module_platform_driver(mt6323_auxadc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek MT6323 PMIC AUXADC Driver");
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index 3a17b3898bf6..d38cf3caeae0 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -159,16 +159,6 @@ static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
RCAR_GYROADC_CHAN(7),
};
-static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on)
-{
- struct device *dev = priv->dev;
-
- if (on)
- return pm_runtime_resume_and_get(dev);
-
- return pm_runtime_put_autosuspend(dev);
-}
-
static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -200,7 +190,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
- ret = rcar_gyroadc_set_power(priv, true);
+ ret = pm_runtime_resume_and_get(priv->dev);
if (ret < 0) {
iio_device_release_direct(indio_dev);
return ret;
@@ -209,7 +199,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
*val = readl(priv->regs + datareg);
*val &= BIT(priv->sample_width) - 1;
- ret = rcar_gyroadc_set_power(priv, false);
+ ret = pm_runtime_put_autosuspend(priv->dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/rohm-bd79112.c b/drivers/iio/adc/rohm-bd79112.c
index c4b0bec80794..2506fec7be84 100644
--- a/drivers/iio/adc/rohm-bd79112.c
+++ b/drivers/iio/adc/rohm-bd79112.c
@@ -529,7 +529,7 @@ static const struct of_device_id bd79112_of_match[] = {
MODULE_DEVICE_TABLE(of, bd79112_of_match);
static const struct spi_device_id bd79112_id[] = {
- { "bd79112" },
+ { .name = "bd79112" },
{ }
};
MODULE_DEVICE_TABLE(spi, bd79112_id);
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
index ba525a3c5cc2..2083c4351e43 100644
--- a/drivers/iio/adc/rtq6056.c
+++ b/drivers/iio/adc/rtq6056.c
@@ -871,8 +871,8 @@ static const struct richtek_dev_data rtq6059_devdata = {
};
static const struct i2c_device_id rtq6056_id[] = {
- { "rtq6056", (kernel_ulong_t)&rtq6056_devdata },
- { "rtq6059", (kernel_ulong_t)&rtq6059_devdata },
+ { .name = "rtq6056", .driver_data = (kernel_ulong_t)&rtq6056_devdata },
+ { .name = "rtq6059", .driver_data = (kernel_ulong_t)&rtq6059_devdata },
{ }
};
MODULE_DEVICE_TABLE(i2c, rtq6056_id);
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 9664b9bd75d4..00f05e167afc 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -660,11 +660,8 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
}
df_ch->src = val;
- ret = of_property_read_u32_index(indio_dev->dev.of_node,
- "st,adc-alt-channel", chan_idx,
- &df_ch->alt_si);
- if (ret < 0)
- df_ch->alt_si = 0;
+ df_ch->alt_si = of_property_present(indio_dev->dev.of_node,
+ "st,adc-alt-channel");
return 0;
}
@@ -1815,9 +1812,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
adc->dfsdm->fl_list[adc->fl_id].ford = val;
- ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
- if (!ret)
- adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
+ adc->dfsdm->fl_list[adc->fl_id].sync_mode =
+ of_property_read_bool(dev->of_node, "st,filter0-sync");
adc->dev_data = dev_data;
ret = dev_data->init(dev, iio);
diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
index e33a2f4bf66c..2df8f1c21c2a 100644
--- a/drivers/iio/adc/ti-adc081c.c
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -189,10 +189,8 @@ static int adc081c_probe(struct i2c_client *client)
err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL,
adc081c_trigger_handler, NULL);
- if (err < 0) {
- dev_err(&client->dev, "iio triggered buffer setup failed\n");
- return err;
- }
+ if (err < 0)
+ return dev_err_probe(&client->dev, err, "iio triggered buffer setup failed\n");
return devm_iio_device_register(&client->dev, iio);
}
diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c
index 63d712d5d111..a522d8676034 100644
--- a/drivers/iio/adc/ti-adc0832.c
+++ b/drivers/iio/adc/ti-adc0832.c
@@ -313,10 +313,10 @@ static const struct of_device_id adc0832_dt_ids[] = {
MODULE_DEVICE_TABLE(of, adc0832_dt_ids);
static const struct spi_device_id adc0832_id[] = {
- { "adc0831", adc0831 },
- { "adc0832", adc0832 },
- { "adc0834", adc0834 },
- { "adc0838", adc0838 },
+ { .name = "adc0831", .driver_data = adc0831 },
+ { .name = "adc0832", .driver_data = adc0832 },
+ { .name = "adc0834", .driver_data = adc0834 },
+ { .name = "adc0838", .driver_data = adc0838 },
{ }
};
MODULE_DEVICE_TABLE(spi, adc0832_id);
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
index 51596b500a90..cf2f6ee59411 100644
--- a/drivers/iio/adc/ti-adc084s021.c
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -228,10 +228,8 @@ static int adc084s021_probe(struct spi_device *spi)
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
adc084s021_buffer_trigger_handler,
&adc084s021_buffer_setup_ops);
- if (ret) {
- dev_err(&spi->dev, "Failed to setup triggered buffer\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n");
return devm_iio_device_register(&spi->dev, indio_dev);
}
@@ -243,7 +241,7 @@ static const struct of_device_id adc084s021_of_match[] = {
MODULE_DEVICE_TABLE(of, adc084s021_of_match);
static const struct spi_device_id adc084s021_id[] = {
- { ADC084S021_DRIVER_NAME, 0 },
+ { .name = ADC084S021_DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, adc084s021_id);
diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c
index d1f61b440959..10959e835cb8 100644
--- a/drivers/iio/adc/ti-adc108s102.c
+++ b/drivers/iio/adc/ti-adc108s102.c
@@ -276,7 +276,7 @@ static const struct acpi_device_id adc108s102_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids);
static const struct spi_device_id adc108s102_id[] = {
- { "adc108s102", 0 },
+ { .name = "adc108s102" },
{ }
};
MODULE_DEVICE_TABLE(spi, adc108s102_id);
diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c
index e5ec4b073daa..279e4999eb49 100644
--- a/drivers/iio/adc/ti-adc12138.c
+++ b/drivers/iio/adc/ti-adc12138.c
@@ -516,9 +516,9 @@ static const struct of_device_id adc12138_dt_ids[] = {
MODULE_DEVICE_TABLE(of, adc12138_dt_ids);
static const struct spi_device_id adc12138_id[] = {
- { "adc12130", adc12130 },
- { "adc12132", adc12132 },
- { "adc12138", adc12138 },
+ { .name = "adc12130", .driver_data = adc12130 },
+ { .name = "adc12132", .driver_data = adc12132 },
+ { .name = "adc12138", .driver_data = adc12138 },
{ }
};
MODULE_DEVICE_TABLE(spi, adc12138_id);
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 2cb68582ee6a..1899813c1aee 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -247,18 +247,18 @@ static const struct of_device_id adc128_of_match[] = {
MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
- { "adc128s052", (kernel_ulong_t)&adc128s_config },
- { "adc122s021", (kernel_ulong_t)&adc122s_config },
- { "adc122s051", (kernel_ulong_t)&adc122s_config },
- { "adc122s101", (kernel_ulong_t)&adc122s_config },
- { "adc124s021", (kernel_ulong_t)&adc124s_config },
- { "adc124s051", (kernel_ulong_t)&adc124s_config },
- { "adc124s101", (kernel_ulong_t)&adc124s_config },
- { "bd79100", (kernel_ulong_t)&bd79100_config },
- { "bd79101", (kernel_ulong_t)&bd79101_config },
- { "bd79102", (kernel_ulong_t)&bd79102_config },
- { "bd79103", (kernel_ulong_t)&bd79104_config },
- { "bd79104", (kernel_ulong_t)&bd79104_config },
+ { .name = "adc128s052", .driver_data = (kernel_ulong_t)&adc128s_config },
+ { .name = "adc122s021", .driver_data = (kernel_ulong_t)&adc122s_config },
+ { .name = "adc122s051", .driver_data = (kernel_ulong_t)&adc122s_config },
+ { .name = "adc122s101", .driver_data = (kernel_ulong_t)&adc122s_config },
+ { .name = "adc124s021", .driver_data = (kernel_ulong_t)&adc124s_config },
+ { .name = "adc124s051", .driver_data = (kernel_ulong_t)&adc124s_config },
+ { .name = "adc124s101", .driver_data = (kernel_ulong_t)&adc124s_config },
+ { .name = "bd79100", .driver_data = (kernel_ulong_t)&bd79100_config },
+ { .name = "bd79101", .driver_data = (kernel_ulong_t)&bd79101_config },
+ { .name = "bd79102", .driver_data = (kernel_ulong_t)&bd79102_config },
+ { .name = "bd79103", .driver_data = (kernel_ulong_t)&bd79104_config },
+ { .name = "bd79104", .driver_data = (kernel_ulong_t)&bd79104_config },
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
index 08aa32bd5e4b..422df0685580 100644
--- a/drivers/iio/adc/ti-adc161s626.c
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -229,8 +229,8 @@ static const struct of_device_id ti_adc_dt_ids[] = {
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
static const struct spi_device_id ti_adc_id[] = {
- { "adc141s626", TI_ADC141S626 },
- { "adc161s626", TI_ADC161S626 },
+ { .name = "adc141s626", .driver_data = TI_ADC141S626 },
+ { .name = "adc161s626", .driver_data = TI_ADC161S626 },
{ }
};
MODULE_DEVICE_TABLE(spi, ti_adc_id);
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index 8a272af69f7d..0fbfa4e499aa 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -930,6 +930,17 @@ static int ads1015_set_conv_mode(struct ads1015_data *data, int mode)
mode << ADS1015_CFG_MOD_SHIFT);
}
+static void ads1015_power_down(void *p)
+{
+ struct ads1015_data *data = p;
+ int ret;
+
+ ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT);
+ if (ret)
+ dev_warn(regmap_get_device(data->regmap),
+ "Failed to power down (%pe)\n", ERR_PTR(ret));
+}
+
static int ads1015_probe(struct i2c_client *client)
{
const struct ads1015_chip_data *chip;
@@ -1030,6 +1041,10 @@ static int ads1015_probe(struct i2c_client *client)
if (ret)
return ret;
+ ret = devm_add_action_or_reset(&client->dev, ads1015_power_down, data);
+ if (ret)
+ return ret;
+
data->conv_invalid = true;
ret = pm_runtime_set_active(&client->dev);
@@ -1037,33 +1052,11 @@ static int ads1015_probe(struct i2c_client *client)
return ret;
pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
- pm_runtime_enable(&client->dev);
-
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "Failed to register IIO device\n");
+ ret = devm_pm_runtime_enable(&client->dev);
+ if (ret)
return ret;
- }
-
- return 0;
-}
-static void ads1015_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct ads1015_data *data = iio_priv(indio_dev);
- int ret;
-
- iio_device_unregister(indio_dev);
-
- pm_runtime_disable(&client->dev);
- pm_runtime_set_suspended(&client->dev);
-
- /* power down single shot mode */
- ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT);
- if (ret)
- dev_warn(&client->dev, "Failed to power down (%pe)\n",
- ERR_PTR(ret));
+ return devm_iio_device_register(&client->dev, indio_dev);
}
#ifdef CONFIG_PM
@@ -1150,7 +1143,6 @@ static struct i2c_driver ads1015_driver = {
.pm = &ads1015_pm_ops,
},
.probe = ads1015_probe,
- .remove = ads1015_remove,
.id_table = ads1015_id,
};
diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c
index d6624c71a374..d9ec4a8d7ce5 100644
--- a/drivers/iio/adc/ti-ads1018.c
+++ b/drivers/iio/adc/ti-ads1018.c
@@ -717,8 +717,8 @@ static const struct of_device_id ads1018_of_match[] = {
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 },
+ { .name = "ads1018", .driver_data = (kernel_ulong_t)&ads1018_chip_info },
+ { .name = "ads1118", .driver_data = (kernel_ulong_t)&ads1118_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ads1018_spi_match);
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
new file mode 100644
index 000000000000..60eab4852ba2
--- /dev/null
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -0,0 +1,1454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IIO driver for Texas Instruments ADS112C14 and similar ADCs.
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Copyright (C) 2026 Baylibre Inc.
+ *
+ * Datasheet: https://www.ti.com/lit/ds/symlink/ads122c14.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/i2c.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+/* Arbitrary limit since channels are dynamic. */
+#define ADS112C14_MAX_MEASUREMENT_CHANNELS 16
+
+/* Datasheet t_d(RST) - time to wait after reset before next I2C use. */
+#define ADS112C14_DELAY_RESET_US 500
+
+#define ADS112C14_CMD_RDATA 0x00
+#define ADS112C14_CMD_RREG 0x40
+#define ADS112C14_CMD_WREG 0x80
+
+#define ADS112C14_REG_DEVICE_ID 0x00
+#define ADS112C14_DEVICE_ID_BITS GENMASK(3, 0)
+
+#define ADS112C14_REG_REVISION_ID 0x01
+
+#define ADS112C14_REG_STATUS_MSB 0x02
+#define ADS112C14_STATUS_MSB_RESETN BIT(7)
+#define ADS112C14_STATUS_MSB_AVDD_UVN BIT(6)
+#define ADS112C14_STATUS_MSB_REF_UVN BIT(5)
+#define ADS112C14_STATUS_MSB_REG_MAP_CRC_FAULTN BIT(3)
+#define ADS112C14_STATUS_MSB_MEM_FAULTN BIT(2)
+#define ADS112C14_STATUS_MSB_REG_WRITE_FAULTN BIT(1)
+#define ADS112C14_STATUS_MSB_DRDY BIT(0)
+
+#define ADS112C14_REG_STATUS_LSB 0x03
+#define ADS112C14_STATUS_LSB_CONV_COUNT GENMASK(7, 4)
+#define ADS112C14_STATUS_LSB_GPIO3_DAT_IN BIT(3)
+#define ADS112C14_STATUS_LSB_GPIO2_DAT_IN BIT(2)
+#define ADS112C14_STATUS_LSB_GPIO1_DAT_IN BIT(1)
+#define ADS112C14_STATUS_LSB_GPIO0_DAT_IN BIT(0)
+
+#define ADS112C14_REG_CONVERSION_CTRL 0x04
+#define ADS112C14_CONVERSION_CTRL_RESET GENMASK(7, 2)
+#define ADS112C14_CONVERSION_CTRL_START BIT(1)
+#define ADS112C14_CONVERSION_CTRL_STOP BIT(0)
+
+#define ADS112C14_REG_DEVICE_CFG 0x05
+#define ADS112C14_DEVICE_CFG_PWDN BIT(7)
+#define ADS112C14_DEVICE_CFG_STBY_MODE BIT(6)
+#define ADS112C14_DEVICE_CFG_BOCS GENMASK(5, 4)
+#define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3)
+#define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2)
+#define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0
+#define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1
+#define ADS112C14_DEVICE_CFG_SPEED_MODE GENMASK(1, 0)
+
+#define ADS112C14_REG_DATA_RATE_CFG 0x06
+#define ADS112C14_DATA_RATE_CFG_DELAY GENMASK(7, 4)
+#define ADS112C14_DATA_RATE_CFG_GC_EN BIT(3)
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR GENMASK(2, 0)
+
+#define ADS112C14_REG_MUX_CFG 0x07
+#define ADS112C14_MUX_CFG_AINP GENMASK(7, 4)
+#define ADS112C14_MUX_CFG_AINN GENMASK(3, 0)
+#define ADS112C14_MUX_CFG_AIN_GND 8
+
+#define ADS112C14_REG_GAIN_CFG 0x08
+#define ADS112C14_GAIN_CFG_SPARE BIT(7)
+#define ADS112C14_GAIN_CFG_SYS_MON GENMASK(6, 4)
+#define ADS112C14_GAIN_CFG_GAIN GENMASK(3, 0)
+
+#define ADS112C14_REG_REFERENCE_CFG 0x09
+#define ADS112C14_REFERENCE_CFG_REF_UV_EN BIT(7)
+#define ADS112C14_REFERENCE_CFG_REFP_BUF_EN BIT(5)
+#define ADS112C14_REFERENCE_CFG_REFN_BUF_EN BIT(4)
+#define ADS112C14_REFERENCE_CFG_REF_VAL BIT(2)
+#define ADS112C14_REFERENCE_CFG_REF_VAL_1_25V 0
+#define ADS112C14_REFERENCE_CFG_REF_VAL_2_5V 1
+#define ADS112C14_REFERENCE_CFG_REF_SEL GENMASK(1, 0)
+#define ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL 0
+#define ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL 1
+#define ADS112C14_REFERENCE_CFG_REF_SEL_AVDD 2
+
+#define ADS112C14_REG_DIGITAL_CFG 0x0A
+#define ADS112C14_DIGITAL_CFG_REG_MAP_CRC_EN BIT(6)
+#define ADS112C14_DIGITAL_CFG_I2C_CRC_EN BIT(5)
+#define ADS112C14_DIGITAL_CFG_STATUS_EN BIT(4)
+#define ADS112C14_DIGITAL_CFG_FAULT_PIN_BEHAVIOR BIT(3)
+#define ADS112C14_DIGITAL_CFG_CODING BIT(1)
+
+#define ADS112C14_REG_GPIO_CFG 0x0B
+#define ADS112C14_GPIO_CFG_GPIO3_CFG GENMASK(7, 6)
+#define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4)
+#define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2)
+#define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0)
+
+#define ADS112C14_REG_GPIO_DATA_OUTPUT 0x0C
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC BIT(7)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO1_DAT_OUT BIT(1)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO0_DAT_OUT BIT(0)
+
+#define ADS112C14_REG_IDAC_MAG_CFG 0x0D
+#define ADS112C14_IDAC_MAG_CFG_I2MAG GENMASK(7, 4)
+#define ADS112C14_IDAC_MAG_CFG_I1MAG GENMASK(3, 0)
+
+#define ADS112C14_REG_IDAC_MUX_CFG 0x0E
+#define ADS112C14_IDAC_MUX_CFG_IUNIT BIT(7)
+#define ADS112C14_IDAC_MUX_CFG_I2MUX GENMASK(6, 4)
+#define ADS112C14_IDAC_MUX_CFG_I1MUX GENMASK(2, 0)
+
+#define ADS112C14_REG_REG_MAP_CRC 0x0F
+
+#define ADS112C14_INT_REF0_mV 1250
+#define ADS112C14_INT_REF1_mV 2500
+
+enum {
+ ADS112C14_VREF_SOURCE_INTERNAL_2_5V,
+ ADS112C14_VREF_SOURCE_INTERNAL_1_25V,
+ ADS112C14_VREF_SOURCE_EXTERNAL,
+ ADS112C14_VREF_SOURCE_AVDD,
+};
+
+static const char * const ads112c14_vref_source_names[] = {
+ [ADS112C14_VREF_SOURCE_INTERNAL_2_5V] = "internal-2.5v",
+ [ADS112C14_VREF_SOURCE_INTERNAL_1_25V] = "internal-1.25v",
+ [ADS112C14_VREF_SOURCE_EXTERNAL] = "external",
+ [ADS112C14_VREF_SOURCE_AVDD] = "avdd",
+};
+
+/*
+ * Available gains as tenths (e.g. value 5 == 0.5 gain). Indexes correspond to
+ * ADS112C14_GAIN_CFG_GAIN values.
+ */
+static const u32 ads112c14_pga_gains_x10[] = {
+ 5, 10, 20, 40, 50, 80, 100, 160, /* 0 - 7 */
+ 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */
+};
+
+#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07
+DECLARE_CRC8_TABLE(ads112c14_crc8_table);
+
+struct ads112c14_chip_info {
+ const char *name;
+ u8 device_id;
+ u32 resolution_bits;
+};
+
+/* Fixed channels for system monitor measurements. */
+
+#define ADS112C14_SYS_MON_CHANNEL_BASE 100
+
+enum {
+ ADS112C14_SYS_MON_CHANNEL_TEMP = ADS112C14_SYS_MON_CHANNEL_BASE,
+ ADS112C14_SYS_MON_CHANNEL_EXT_REF,
+ ADS112C14_SYS_MON_CHANNEL_AVDD,
+ ADS112C14_SYS_MON_CHANNEL_DVDD,
+ ADS112C14_SYS_MON_CHANNEL_SHORT,
+};
+
+static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_TEMP,
+ .address = 2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE)
+ | BIT(IIO_CHAN_INFO_OFFSET),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_EXT_REF,
+ .address = 3,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_AVDD,
+ .address = 4,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_DVDD,
+ .address = 5,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_SHORT,
+ .channel2 = ADS112C14_SYS_MON_CHANNEL_SHORT,
+ .differential = 1,
+ .address = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+struct ads112c14_measurement {
+ const char *label;
+ u32 vref_source;
+ u8 iunit;
+ u8 idac1_mag;
+ u8 idac2_mag;
+ u8 idac1_mux;
+ u8 idac2_mux;
+ u8 iadc_count;
+ u8 gain_val;
+ bool global_chop;
+ bool bipolar;
+ int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
+};
+
+struct ads112c14_data {
+ const struct ads112c14_chip_info *chip_info;
+ struct regmap *regmap;
+ /* Synchronizes access to register value fields. */
+ struct mutex lock;
+ bool i2c_crc_enabled;
+ u32 avdd_uV;
+ u32 ext_ref_uV;
+ bool refp_is_avdd;
+ bool refn_is_gnd;
+ u32 ext_ref_ohms;
+ struct ads112c14_measurement *measurements;
+ u32 num_measurements;
+ u8 sys_mon_chan_short_gain_val;
+ int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
+ IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS +
+ ARRAY_SIZE(ads112c14_sys_mon_channels));
+};
+
+static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADS112C14_REG_DEVICE_ID:
+ case ADS112C14_REG_REVISION_ID:
+ case ADS112C14_REG_STATUS_LSB:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool ads112c14_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADS112C14_REG_STATUS_MSB:
+ case ADS112C14_REG_STATUS_LSB:
+ case ADS112C14_REG_CONVERSION_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default ads112c14_reg_defaults[] = {
+ { ADS112C14_REG_DEVICE_CFG, 0 },
+ { ADS112C14_REG_DATA_RATE_CFG, 0 },
+ { ADS112C14_REG_MUX_CFG, 0 },
+ { ADS112C14_REG_GAIN_CFG, FIELD_PREP_CONST(ADS112C14_GAIN_CFG_GAIN, 1) },
+ { ADS112C14_REG_REFERENCE_CFG, 0 },
+ { ADS112C14_REG_DIGITAL_CFG, 0 },
+ { ADS112C14_REG_GPIO_CFG, 0 },
+ { ADS112C14_REG_GPIO_DATA_OUTPUT, 0 },
+ { ADS112C14_REG_IDAC_MAG_CFG, 0 },
+ { ADS112C14_REG_IDAC_MUX_CFG, FIELD_PREP_CONST(ADS112C14_IDAC_MUX_CFG_I2MUX, 1) },
+};
+
+/**
+ * ads112c14_i2c_read_bytes() - Read bytes from the device over I2C
+ * @client: I2C client for the device
+ * @cmd: Command to send to the device before reading
+ * @buf: Buffer to store the read bytes
+ * @len: Number of bytes to read
+ * @use_crc: Whether to use CRC8 for data integrity check
+ *
+ * If I2C_CRC is enabled, @use_crc may be set to true to perform a CRC8 check
+ * on the received data.
+ */
+static int ads112c14_i2c_read_bytes(struct i2c_client *client, u8 cmd,
+ u8 *buf, u8 len, bool use_crc)
+{
+ u8 rx_buf[4]; /* Up to 3 data bytes + 1 CRC byte. */
+ u8 rx_len;
+ int ret;
+
+ rx_len = len + (use_crc ? 1 : 0);
+
+ if (rx_len > sizeof(rx_buf))
+ return -EINVAL;
+
+ ret = i2c_smbus_read_i2c_block_data(client, cmd, rx_len, rx_buf);
+ if (ret < 0)
+ return ret;
+
+ if (use_crc) {
+ u8 crc = crc8(ads112c14_crc8_table, rx_buf, len, CRC8_INIT_VALUE);
+
+ if (crc != rx_buf[len])
+ return -EBADMSG;
+ }
+
+ memcpy(buf, rx_buf, len);
+
+ return 0;
+}
+
+/**
+ * ads112c14_regmap_bus_read() - Read a register from the device
+ * @context: Pointer to the device context
+ * @reg_buf: Register address to read
+ * @reg_size: Size of the register address (should be 1)
+ * @val_buf: Buffer to store the read value
+ * @val_size: Size of the value to read
+ *
+ * Custom regmap read function that also does CRC check when enabled.
+ */
+static int ads112c14_regmap_bus_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct ads112c14_data *data = context;
+ struct device *dev = regmap_get_device(data->regmap);
+ struct i2c_client *client = to_i2c_client(dev);
+ const u8 *cmd = reg_buf;
+
+ if (reg_size != 1)
+ return -EINVAL;
+
+ return ads112c14_i2c_read_bytes(client, cmd[0], val_buf, val_size,
+ data->i2c_crc_enabled);
+}
+
+/**
+ * ads112c14_regmap_bus_write() - Write a register to the device
+ * @context: Pointer to the device context
+ * @data_buf: Buffer containing the register address and value to write
+ * @count: Number of bytes to write
+ *
+ * Custom regmap write function that also does readback with CRC check of
+ * nonvolatile registers when CRC is enabled.
+ */
+static int ads112c14_regmap_bus_write(void *context, const void *data_buf,
+ size_t count)
+{
+ struct ads112c14_data *data = context;
+ struct device *dev = regmap_get_device(data->regmap);
+ struct i2c_client *client = to_i2c_client(dev);
+ const u8 *tx = data_buf;
+ u8 reg, readback;
+ int ret;
+
+ if (count != 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(client, tx[0], tx[1]);
+ if (ret)
+ return ret;
+
+ reg = tx[0] & ~ADS112C14_CMD_WREG;
+
+ if (!data->i2c_crc_enabled || ads112c14_volatile_reg(dev, reg))
+ return 0;
+
+ ret = ads112c14_i2c_read_bytes(client, reg | ADS112C14_CMD_RREG,
+ &readback, sizeof(readback), true);
+ if (ret)
+ return ret;
+
+ if (readback != tx[1])
+ return -EIO;
+
+ return 0;
+}
+
+static const struct regmap_bus ads112c14_regmap_bus = {
+ .read = ads112c14_regmap_bus_read,
+ .write = ads112c14_regmap_bus_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config ads112c14_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = ADS112C14_CMD_RREG,
+ .write_flag_mask = ADS112C14_CMD_WREG,
+ .max_register = ADS112C14_REG_REG_MAP_CRC,
+ .writeable_reg = ads112c14_writeable_reg,
+ .volatile_reg = ads112c14_volatile_reg,
+ .reg_defaults = ads112c14_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ads112c14_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
+ const struct iio_chan_spec *chan)
+{
+ struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index];
+ u32 refp_buf_en, refn_buf_en, ref_val, ref_sel;
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_MUX_CFG,
+ ADS112C14_MUX_CFG_AINP | ADS112C14_MUX_CFG_AINN,
+ FIELD_PREP(ADS112C14_MUX_CFG_AINP, chan->channel) |
+ FIELD_PREP(ADS112C14_MUX_CFG_AINN, chan->channel2));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG,
+ ADS112C14_DIGITAL_CFG_CODING,
+ !measurement->bipolar);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG,
+ ADS112C14_GAIN_CFG_SYS_MON |
+ ADS112C14_GAIN_CFG_GAIN,
+ FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, 0) |
+ FIELD_PREP(ADS112C14_GAIN_CFG_GAIN,
+ measurement->gain_val));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MAG_CFG,
+ ADS112C14_IDAC_MAG_CFG_I2MAG |
+ ADS112C14_IDAC_MAG_CFG_I1MAG,
+ FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I2MAG,
+ measurement->idac2_mag) |
+ FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I1MAG,
+ measurement->idac1_mag));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MUX_CFG,
+ ADS112C14_IDAC_MUX_CFG_IUNIT |
+ ADS112C14_IDAC_MUX_CFG_I2MUX |
+ ADS112C14_IDAC_MUX_CFG_I1MUX,
+ FIELD_PREP(ADS112C14_IDAC_MUX_CFG_IUNIT,
+ measurement->iunit) |
+ FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I2MUX,
+ measurement->idac2_mux) |
+ FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I1MUX,
+ measurement->idac1_mux));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
+ ADS112C14_DATA_RATE_CFG_GC_EN,
+ FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN,
+ measurement->global_chop));
+ if (ret)
+ return ret;
+
+ refp_buf_en = !data->refp_is_avdd &&
+ measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL;
+ refn_buf_en = !data->refn_is_gnd &&
+ measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL;
+
+ ref_val = measurement->vref_source == ADS112C14_VREF_SOURCE_INTERNAL_2_5V ?
+ ADS112C14_REFERENCE_CFG_REF_VAL_2_5V :
+ ADS112C14_REFERENCE_CFG_REF_VAL_1_25V;
+
+ switch (measurement->vref_source) {
+ case ADS112C14_VREF_SOURCE_AVDD:
+ ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_AVDD;
+ break;
+ case ADS112C14_VREF_SOURCE_EXTERNAL:
+ ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL;
+ break;
+ default:
+ ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL;
+ break;
+ }
+
+ return regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG,
+ ADS112C14_REFERENCE_CFG_REFP_BUF_EN |
+ ADS112C14_REFERENCE_CFG_REFN_BUF_EN |
+ ADS112C14_REFERENCE_CFG_REF_VAL |
+ ADS112C14_REFERENCE_CFG_REF_SEL,
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REFP_BUF_EN,
+ refp_buf_en) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REFN_BUF_EN,
+ refn_buf_en) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL,
+ ref_val) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL,
+ ref_sel));
+}
+
+static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
+ const struct iio_chan_spec *chan)
+{
+ u32 gain_val;
+ int ret;
+
+ /*
+ * NB: IDAC registers are left as-is in case they are generating current
+ * needed for the external reference measurement.
+ */
+
+ /*
+ * All SYS_MON channels use GAIN of 1 to keep it simple. Other than
+ * the internal short channel, where it is useful in practice.
+ */
+ gain_val = chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT ?
+ data->sys_mon_chan_short_gain_val : 1;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG,
+ ADS112C14_GAIN_CFG_SYS_MON |
+ ADS112C14_GAIN_CFG_GAIN,
+ FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, chan->address) |
+ FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, gain_val));
+ if (ret)
+ return ret;
+
+ /* All SYS_MON channels use signed data to keep it simple. */
+ ret = regmap_clear_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG,
+ ADS112C14_DIGITAL_CFG_CODING);
+ if (ret)
+ return ret;
+
+ /*
+ * REVISIT: if we implement regulator support for the REFOUT pin, we
+ * might need to make this voltage match what is required by that. In
+ * that case, we could also adjust GAIN so that we still get the same
+ * range.
+ */
+ /*
+ * NB: SYS_MON channels ignore REF_SEL except for the shorted input
+ * channel, so we set it here to internal reference to be consistent.
+ * If we ever need to make a measurement of shorted input with other
+ * reference source, we could add additional channels for that.
+ */
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG,
+ ADS112C14_REFERENCE_CFG_REF_VAL |
+ ADS112C14_REFERENCE_CFG_REF_SEL,
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL,
+ ADS112C14_REFERENCE_CFG_REF_VAL_2_5V) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL,
+ ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ads112c14_single_conversion(struct ads112c14_data *data,
+ const struct iio_chan_spec *chan,
+ u8 *buf, bool for_scan)
+{
+ struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
+ u32 reg_val;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ ret = ads112c14_prepare_measurement_channel(data, chan);
+ if (ret)
+ return ret;
+ } else {
+ ret = ads112c14_prepare_sys_mon_channel(data, chan);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ ADS112C14_CONVERSION_CTRL_START);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(data->regmap,
+ ADS112C14_REG_STATUS_MSB, reg_val,
+ FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val),
+ 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ /*
+ * When doing buffered read, we don't check the CRC, but rather pass it
+ * along with the raw data. This way, we don't silently drop samples
+ * with CRC errors, but rather leave it to userspace to decide what to
+ * do.
+ */
+ if (for_scan) {
+ u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) +
+ (data->i2c_crc_enabled ? 1 : 0);
+
+ ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA,
+ len, buf);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ }
+
+ return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf,
+ BITS_TO_BYTES(data->chip_info->resolution_bits),
+ data->i2c_crc_enabled);
+}
+
+static int ads112c14_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_measurement *measurement = NULL;
+ const int *scale_avail;
+ u32 vref_uV, fsr_bits;
+
+ /* Selecting V_REF source is not implemented yet. */
+ vref_uV = ADS112C14_INT_REF1_mV * (MICRO / MILLI);
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ measurement = &data->measurements[chan->scan_index];
+ fsr_bits = data->chip_info->resolution_bits - measurement->bipolar;
+ } else {
+ /* All SYS_MON channels are using signed coding. */
+ fsr_bits = data->chip_info->resolution_bits - 1;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ u8 buf[3];
+ int ret;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ads112c14_single_conversion(data, chan, buf, false);
+ if (ret)
+ return ret;
+
+ switch (data->chip_info->resolution_bits) {
+ case 16:
+ *val = get_unaligned_be16(buf);
+ break;
+ case 24:
+ *val = get_unaligned_be24(buf);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!measurement || measurement->bipolar)
+ *val = sign_extend32(*val, fsr_bits);
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ /* TS_TC (typical) = 405 uV/°C */
+ *val = MILLI * vref_uV / 405;
+ *val2 = fsr_bits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ guard(mutex)(&data->lock);
+
+ scale_avail = measurement->scale_available[measurement->gain_val];
+ *val = scale_avail[0];
+ *val2 = scale_avail[1];
+
+ return IIO_VAL_DECIMAL64_PICO;
+ }
+
+ if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) {
+ u8 idx;
+
+ guard(mutex)(&data->lock);
+
+ idx = data->sys_mon_chan_short_gain_val;
+ scale_avail = data->sys_mon_chan_short_scale_available[idx];
+ *val = scale_avail[0];
+ *val2 = scale_avail[1];
+
+ return IIO_VAL_DECIMAL64_PICO;
+ }
+
+ *val = vref_uV / (MICRO / MILLI);
+
+ /*
+ * Some SYS_MON channels (ext ref, AVDD, DVDD) need to be
+ * multiplied by 8 to account for internal attenuation of / 8.
+ */
+ switch (chan->address) {
+ case 3 ... 5:
+ *val2 = fsr_bits - 3;
+ break;
+ default:
+ *val2 = fsr_bits;
+ break;
+ }
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ /* Only the temperature channel has an offset. */
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+ /*
+ * Die temperature [°C] = 25°C + (Measured voltage – TS_Offset) / TS_TC
+ * TS_TC (typical) = 405 uV/°C
+ * TS_Offset (typical) = 119.5 mV
+ */
+ *val = div_s64((s64)(25 * 405 - 119500) * BIT(fsr_bits), vref_uV);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_read_avail(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ struct ads112c14_measurement *measurement;
+
+ guard(mutex)(&data->lock);
+
+ measurement = &data->measurements[chan->scan_index];
+ *vals = (const int *)measurement->scale_available;
+ *length = 2 * ARRAY_SIZE(measurement->scale_available);
+ *type = IIO_VAL_DECIMAL64_PICO;
+ return IIO_AVAIL_LIST;
+ }
+
+ if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) {
+ guard(mutex)(&data->lock);
+
+ *vals = (const int *)data->sys_mon_chan_short_scale_available;
+ *length = 2 * ARRAY_SIZE(data->sys_mon_chan_short_scale_available);
+ *type = IIO_VAL_DECIMAL64_PICO;
+ return IIO_AVAIL_LIST;
+ }
+
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ const int (*scale_avail)[2];
+ u8 *gain_val;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&data->lock);
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ struct ads112c14_measurement *measurement;
+
+ measurement = &data->measurements[chan->scan_index];
+ scale_avail = measurement->scale_available;
+ gain_val = &measurement->gain_val;
+ } else if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) {
+ scale_avail = data->sys_mon_chan_short_scale_available;
+ gain_val = &data->sys_mon_chan_short_gain_val;
+ } else {
+ return -EINVAL;
+ }
+
+ for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) {
+ if (iio_val_s64_compose(val, val2) ==
+ iio_val_s64_compose(scale_avail[i][0], scale_avail[i][1])) {
+ *gain_val = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_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_DECIMAL64_PICO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(data->regmap, reg, readval);
+
+ return regmap_write(data->regmap, reg, writeval);
+}
+
+static int ads112c14_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, char *label)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ const char *label_source;
+
+ /* measurement channels */
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ struct ads112c14_measurement *measurement;
+
+ measurement = &data->measurements[chan->scan_index];
+ if (!measurement->label)
+ return -EINVAL;
+
+ return sysfs_emit(label, "%s\n", measurement->label);
+ }
+
+ /* System monitor channels. */
+ switch (chan->channel) {
+ case ADS112C14_SYS_MON_CHANNEL_TEMP:
+ label_source = "Internal temperature sensor";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_EXT_REF:
+ label_source = "External reference";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_AVDD:
+ label_source = "AVDD";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_DVDD:
+ label_source = "DVDD";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_SHORT:
+ label_source = "Internal short (internal reference source)";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(label, "%s\n", label_source);
+}
+
+static irqreturn_t ads112c14_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ u32 offset = 0;
+ u32 i;
+ int ret;
+
+ iio_for_each_active_channel(indio_dev, i) {
+ const struct iio_chan_spec *chan = &indio_dev->channels[i];
+
+ ret = ads112c14_single_conversion(data, chan,
+ (u8 *)&data->scan[offset++],
+ true);
+ if (ret) {
+ dev_err_once(indio_dev->dev.parent,
+ "failed to read channel %d: %pe; additional errors will be suppressed\n",
+ chan->channel, ERR_PTR(ret));
+ goto out;
+ }
+ }
+
+ iio_push_to_buffers_with_ts(indio_dev, data->scan,
+ sizeof(data->scan), pf->timestamp);
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info ads112c14_info = {
+ .read_raw = ads112c14_read_raw,
+ .read_avail = ads112c14_read_avail,
+ .write_raw = ads112c14_write_raw,
+ .write_raw_get_fmt = ads112c14_write_raw_get_fmt,
+ .debugfs_reg_access = ads112c14_debugfs_reg_access,
+ .read_label = ads112c14_read_label,
+};
+
+static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag)
+{
+ u32 current_uA = current_nA / (NANO / MICRO);
+
+ /* Convert microamps to IMAG bits */
+ if (current_uA == 1)
+ *idac_mag = 1;
+ else if (in_range(current_uA, 10, 100) && current_uA % 10 == 0)
+ *idac_mag = current_uA / 10 + 1;
+ else
+ return dev_err_probe(NULL, -EINVAL,
+ "invalid excitation-current-nanoamp value\n");
+
+ return 0;
+}
+
+static int ads112c14_parse_channels(struct iio_dev *indio_dev,
+ bool *need_avdd_ref, bool *need_ext_ref)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_chan_spec *channels;
+ u32 num_child_nodes, i, pair[2];
+ int ret;
+
+ *need_avdd_ref = false;
+ *need_ext_ref = false;
+
+ num_child_nodes = device_get_named_child_node_count(dev, "channel");
+
+ data->measurements = devm_kcalloc(dev, num_child_nodes,
+ sizeof(*data->measurements), GFP_KERNEL);
+ if (!data->measurements)
+ return -ENOMEM;
+
+ channels = devm_kcalloc(dev, num_child_nodes +
+ ARRAY_SIZE(ads112c14_sys_mon_channels) + 1,
+ sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ i = 0;
+ device_for_each_named_child_node_scoped(dev, child, "channel") {
+ struct ads112c14_measurement *measurement = &data->measurements[i];
+ struct iio_chan_spec *spec = &channels[i];
+
+ spec->indexed = 1;
+ spec->scan_index = i;
+ measurement->gain_val = 1;
+
+ if (fwnode_property_present(child, "label")) {
+ ret = fwnode_property_read_string(child, "label", &measurement->label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read label property\n");
+ }
+
+ if (fwnode_property_present(child, "single-channel")) {
+ ret = fwnode_property_read_u32(child, "single-channel",
+ &pair[0]);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read single-channel property\n");
+
+ if (pair[0] >= 8)
+ return dev_err_probe(dev, -EINVAL,
+ "single-channel value must be between 0 and 7\n");
+
+ spec->channel = pair[0];
+ /*
+ * NB: channel2 is unused by iio core code in this case.
+ * Let's us avoid special case for negative input mux
+ * for single-ended channels when taking measurements.
+ */
+ spec->channel2 = ADS112C14_MUX_CFG_AIN_GND;
+ } else if (fwnode_property_present(child, "diff-channels")) {
+ ret = fwnode_property_read_u32_array(child, "diff-channels",
+ pair, ARRAY_SIZE(pair));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read diff-channels property\n");
+
+ if (pair[0] >= 8 || pair[1] >= 8)
+ return dev_err_probe(dev, -EINVAL,
+ "diff-channels values must be between 0 and 7\n");
+
+ spec->differential = 1;
+ spec->channel = pair[0];
+ spec->channel2 = pair[1];
+ } else {
+ return dev_err_probe(dev, -EINVAL,
+ "channel node missing channel type property\n");
+ }
+
+ if (fwnode_property_present(child, "excitation-channels")) {
+ ret = fwnode_property_count_u32(child, "excitation-channels");
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to read excitation-channels property\n");
+
+ if (ret < 1 || ret > 2)
+ return dev_err_probe(dev, -EINVAL,
+ "excitation-channels property must have 1 or 2 values\n");
+
+ measurement->iadc_count = ret;
+ pair[1] = 0;
+
+ ret = fwnode_property_read_u32_array(child, "excitation-channels",
+ pair, measurement->iadc_count);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read excitation-channels property\n");
+
+ if (pair[0] >= 8 || pair[1] >= 8)
+ return dev_err_probe(dev, -EINVAL,
+ "excitation-channels values must be between 0 and 7\n");
+
+ measurement->idac1_mux = pair[0];
+ measurement->idac2_mux = measurement->iadc_count > 1 ? pair[1] : 0;
+
+ ret = fwnode_property_read_u32_array(child, "excitation-current-nanoamp",
+ pair, measurement->iadc_count);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read excitation-current-nanoamp property\n");
+
+ if (pair[0] <= 100 * (NANO / MICRO) &&
+ (measurement->iadc_count == 1 || pair[1] <= 100 * (NANO / MICRO))) {
+ /*
+ * If both values are 100µA or less, then we can
+ * use IUNIT = 1µA for better precision.
+ */
+ ret = ads112c14_populate_idac_mag(pair[0],
+ &measurement->idac1_mag);
+ if (ret)
+ return ret;
+
+ if (measurement->iadc_count > 1) {
+ ret = ads112c14_populate_idac_mag(pair[1],
+ &measurement->idac2_mag);
+ if (ret)
+ return ret;
+ }
+ } else {
+ /*
+ * Otherwise, IUINT is 10µA (flag set) and so
+ * IxMAG is 1/10 of the actual current.
+ */
+ measurement->iunit = 1;
+
+ ret = ads112c14_populate_idac_mag(pair[0] / 10,
+ &measurement->idac1_mag);
+ if (ret)
+ return ret;
+
+ if (measurement->iadc_count > 1) {
+ ret = ads112c14_populate_idac_mag(pair[1] / 10,
+ &measurement->idac2_mag);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ measurement->bipolar = fwnode_property_read_bool(child, "bipolar");
+ measurement->global_chop = fwnode_property_read_bool(child,
+ "input-chopping");
+
+ if (fwnode_property_present(child, "reference-sources")) {
+ ret = fwnode_property_match_property_string(child,
+ "reference-sources", ads112c14_vref_source_names,
+ ARRAY_SIZE(ads112c14_vref_source_names));
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "invalid reference-sources value\n");
+
+ measurement->vref_source = ret;
+ }
+
+ if (measurement->vref_source == ADS112C14_VREF_SOURCE_AVDD)
+ *need_avdd_ref = true;
+ if (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL)
+ *need_ext_ref = true;
+
+ spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE);
+ spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE);
+
+ /*
+ * If reference source is resistor rather than voltage supply,
+ * then the measurement is effectively a resistance measurement.
+ */
+ spec->type = (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL &&
+ data->ext_ref_ohms) ? IIO_RESISTANCE : IIO_VOLTAGE;
+
+ if (spec->type == IIO_RESISTANCE)
+ spec->differential = 0;
+
+ spec->scan_type = (struct iio_scan_type){
+ .format = measurement->bipolar ?
+ IIO_SCAN_FORMAT_SIGNED_INT :
+ IIO_SCAN_FORMAT_UNSIGNED_INT,
+ .realbits = data->chip_info->resolution_bits,
+ .storagebits = 32,
+ .shift = 32 - data->chip_info->resolution_bits,
+ .endianness = IIO_BE,
+ };
+
+ i++;
+ }
+
+ data->num_measurements = i;
+ if (data->num_measurements > ADS112C14_MAX_MEASUREMENT_CHANNELS)
+ return dev_err_probe(dev, -EINVAL,
+ "too many measurement channels defined\n");
+
+ memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels));
+
+ for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) {
+ struct iio_chan_spec *spec = &channels[i];
+
+ /* Update the template that was already copied with dynamic values. */
+ spec->scan_index = i;
+ spec->scan_type = (struct iio_scan_type){
+ .format = IIO_SCAN_FORMAT_SIGNED_INT,
+ .realbits = data->chip_info->resolution_bits,
+ .storagebits = 32,
+ .shift = 32 - data->chip_info->resolution_bits,
+ .endianness = IIO_BE,
+ };
+
+ i++;
+ }
+
+ channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i);
+ i++;
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = i;
+
+ return 0;
+}
+
+static void ads112c14_populate_scale_available(int (*scale_avail)[2],
+ u32 full_scale, u32 fsr_bits)
+{
+ for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) {
+ u64 gain_x10 = ads112c14_pga_gains_x10[i];
+ s64 scale;
+
+ scale = div64_u64((u64)PICO * 10U * full_scale,
+ gain_x10 * BIT(fsr_bits));
+
+ iio_val_s64_decompose(scale, &scale_avail[i][0],
+ &scale_avail[i][1]);
+ }
+}
+
+static void ads112c14_populate_tables(struct ads112c14_data *data)
+{
+ u32 full_scale, fsr_bits;
+
+ for (u32 i = 0; i < data->num_measurements; i++) {
+ struct ads112c14_measurement *measurement = &data->measurements[i];
+
+ switch (measurement->vref_source) {
+ case ADS112C14_VREF_SOURCE_EXTERNAL:
+ if (data->ext_ref_ohms)
+ full_scale = data->ext_ref_ohms;
+ else
+ full_scale = data->ext_ref_uV / (MICRO / MILLI);
+ break;
+ case ADS112C14_VREF_SOURCE_AVDD:
+ full_scale = data->avdd_uV / (MICRO / MILLI);
+ break;
+ case ADS112C14_VREF_SOURCE_INTERNAL_1_25V:
+ full_scale = ADS112C14_INT_REF0_mV;
+ break;
+ default:
+ full_scale = ADS112C14_INT_REF1_mV;
+ break;
+ }
+
+ fsr_bits = data->chip_info->resolution_bits - measurement->bipolar;
+
+ ads112c14_populate_scale_available(measurement->scale_available,
+ full_scale, fsr_bits);
+ }
+
+ /* For now, assuming all sys_mon channels are using 2.5V reference. */
+ full_scale = ADS112C14_INT_REF1_mV;
+ fsr_bits = data->chip_info->resolution_bits - 1;
+
+ ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available,
+ full_scale, fsr_bits);
+}
+
+static int ads112c14_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ const struct ads112c14_chip_info *info;
+ struct iio_dev *indio_dev;
+ struct ads112c14_data *data;
+ bool need_avdd_ref, need_ext_ref;
+ u32 refp_uV = 0;
+ u32 refn_uV = 0;
+ u32 reg_val;
+ int ret;
+
+ info = i2c_get_match_data(client);
+ if (!info)
+ return dev_err_probe(dev, -ENODEV, "missing match data\n");
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->chip_info = info;
+
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ if (device_property_present(dev, "ti,refp-refn-resistor-ohms")) {
+ ret = device_property_read_u32(dev, "ti,refp-refn-resistor-ohms",
+ &data->ext_ref_ohms);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read ti,refp-refn-resistor-ohms property\n");
+ }
+
+ ret = ads112c14_parse_channels(indio_dev, &need_avdd_ref, &need_ext_ref);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_get_enable(dev, "dvdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get dvdd regulator\n");
+
+ if (need_avdd_ref) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "avdd");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get avdd voltage\n");
+
+ data->avdd_uV = ret;
+ } else {
+ ret = devm_regulator_get_enable(dev, "avdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get avdd regulator\n");
+ }
+
+ if (device_property_present(dev, "refp-supply")) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "refp");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get refp voltage\n");
+
+ refp_uV = ret;
+
+ struct fwnode_handle *refp_fwnode __free(fwnode_handle) =
+ fwnode_find_reference(dev->fwnode, "refp-supply", 0);
+ if (IS_ERR(refp_fwnode))
+ return dev_err_probe(dev, PTR_ERR(refp_fwnode),
+ "failed to get refp fwnode\n");
+
+ struct fwnode_handle *avdd_fwnode __free(fwnode_handle) =
+ fwnode_find_reference(dev->fwnode, "avdd-supply", 0);
+ if (IS_ERR(avdd_fwnode))
+ return dev_err_probe(dev, PTR_ERR(avdd_fwnode),
+ "failed to get avdd fwnode\n");
+
+ /* REFP buffer should not be enabled when connected to AVDD */
+ data->refp_is_avdd = refp_fwnode == avdd_fwnode;
+ }
+
+ if (device_property_present(dev, "refn-supply")) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "refn");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get refn voltage\n");
+
+ refn_uV = ret;
+ } else {
+ data->refn_is_gnd = true;
+ }
+
+ data->ext_ref_uV = refp_uV - refn_uV;
+
+ if (data->ext_ref_uV && data->ext_ref_ohms)
+ return dev_err_probe(dev, -EINVAL,
+ "ti,refp-refn-resistor-ohms property should not be present when refp-supply or refn-supply is present\n");
+
+ if (need_ext_ref && !data->ext_ref_uV && !data->ext_ref_ohms)
+ return dev_err_probe(dev, -EINVAL,
+ "external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n");
+
+ /* It takes some time for the internal reference to stabilize. */
+ fsleep(10 * USEC_PER_MSEC);
+
+ data->regmap = devm_regmap_init(dev, &ads112c14_regmap_bus, data,
+ &ads112c14_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "failed to init regmap\n");
+
+ /*
+ * Write magic reset value (0x16) to ensure known state. The reset may
+ * cause an error because of failing to get the I2C ACK at the end of
+ * the message. The device still gets reset so it is safe to ignore the
+ * return value here. If something else is wrong, later read/write will
+ * likely have the same error.
+ */
+ regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ FIELD_PREP(ADS112C14_CONVERSION_CTRL_RESET, 0x16));
+
+ fsleep(ADS112C14_DELAY_RESET_US);
+
+ ret = regmap_read(data->regmap, ADS112C14_REG_STATUS_MSB, &reg_val);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(ADS112C14_STATUS_MSB_RESETN, reg_val))
+ return dev_err_probe(dev, -EIO, "reset failed\n");
+
+ /* Default gain after reset is 1. */
+ data->sys_mon_chan_short_gain_val = 1;
+
+ /*
+ * Clear reset bit to prepare for next probe. And clear AVDD fault since
+ * that happens on every reset.
+ */
+ ret = regmap_write(data->regmap, ADS112C14_REG_STATUS_MSB,
+ ADS112C14_STATUS_MSB_RESETN |
+ ADS112C14_STATUS_MSB_AVDD_UVN);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG,
+ ADS112C14_DIGITAL_CFG_I2C_CRC_EN);
+ if (ret)
+ return ret;
+
+ data->i2c_crc_enabled = true;
+
+ ret = regmap_read(data->regmap, ADS112C14_REG_DEVICE_ID, &reg_val);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val) != info->device_id)
+ dev_info(dev, "device ID mismatch, expected 0x%X, got 0x%lX\n",
+ info->device_id,
+ FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val));
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_CONV_MODE,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE,
+ ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT));
+ if (ret)
+ return ret;
+
+ ads112c14_populate_tables(data);
+
+ indio_dev->name = info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads112c14_info;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads112c14_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct ads112c14_chip_info ads112c14_chip_info = {
+ .name = "ads112c14",
+ .device_id = 0xE,
+ .resolution_bits = 16,
+};
+
+static const struct ads112c14_chip_info ads122c14_chip_info = {
+ .name = "ads122c14",
+ .device_id = 0xF,
+ .resolution_bits = 24,
+};
+
+static const struct of_device_id ads112c14_of_match[] = {
+ { .compatible = "ti,ads112c14", .data = &ads112c14_chip_info },
+ { .compatible = "ti,ads122c14", .data = &ads122c14_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads112c14_of_match);
+
+static const struct i2c_device_id ads112c14_id[] = {
+ { .name = "ads112c14", .driver_data = (kernel_ulong_t)&ads112c14_chip_info },
+ { .name = "ads122c14", .driver_data = (kernel_ulong_t)&ads122c14_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ads112c14_id);
+
+static int ads112c14_i2c_add_driver(struct i2c_driver *driver)
+{
+ crc8_populate_msb(ads112c14_crc8_table, ADS112C14_I2C_CRC8_POLYNOMIAL);
+
+ return i2c_add_driver(driver);
+}
+
+static struct i2c_driver ads112c14_driver = {
+ .driver = {
+ .name = "ads112c14",
+ .of_match_table = ads112c14_of_match,
+ },
+ .probe = ads112c14_probe,
+ .id_table = ads112c14_id,
+};
+module_driver(ads112c14_driver, ads112c14_i2c_add_driver, i2c_del_driver);
+
+MODULE_AUTHOR("David Lechner (TI) <dlechner@baylibre.com>");
+MODULE_DESCRIPTION("TI ADS112C14 I2C ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
index 45de47778809..49fbc63508ac 100644
--- a/drivers/iio/adc/ti-ads124s08.c
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -348,8 +348,8 @@ static int ads124s_probe(struct spi_device *spi)
}
static const struct spi_device_id ads124s_id[] = {
- { "ads124s06", ADS124S06_ID },
- { "ads124s08", ADS124S08_ID },
+ { .name = "ads124s06", .driver_data = ADS124S06_ID },
+ { .name = "ads124s08", .driver_data = ADS124S08_ID },
{ }
};
MODULE_DEVICE_TABLE(spi, ads124s_id);
diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c
index 9e47ace16acd..06c6353aa1b2 100644
--- a/drivers/iio/adc/ti-ads1298.c
+++ b/drivers/iio/adc/ti-ads1298.c
@@ -743,7 +743,7 @@ static int ads1298_probe(struct spi_device *spi)
}
static const struct spi_device_id ads1298_id[] = {
- { "ads1298" },
+ { .name = "ads1298" },
{ }
};
MODULE_DEVICE_TABLE(spi, ads1298_id);
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index a585621b0bc3..1d25f887cadb 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -917,9 +917,9 @@ static const struct of_device_id ads131e08_of_match[] = {
MODULE_DEVICE_TABLE(of, ads131e08_of_match);
static const struct spi_device_id ads131e08_ids[] = {
- { "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] },
- { "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] },
- { "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] },
+ { .name = "ads131e04", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] },
+ { .name = "ads131e06", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] },
+ { .name = "ads131e08", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] },
{ }
};
MODULE_DEVICE_TABLE(spi, ads131e08_ids);
diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c
index 36203ce37c89..2f8f75c8216b 100644
--- a/drivers/iio/adc/ti-ads131m02.c
+++ b/drivers/iio/adc/ti-ads131m02.c
@@ -943,11 +943,11 @@ static const struct of_device_id ads131m_of_match[] = {
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 },
+ { .name = "ads131m02", .driver_data = (kernel_ulong_t)&ads131m02_config },
+ { .name = "ads131m03", .driver_data = (kernel_ulong_t)&ads131m03_config },
+ { .name = "ads131m04", .driver_data = (kernel_ulong_t)&ads131m04_config },
+ { .name = "ads131m06", .driver_data = (kernel_ulong_t)&ads131m06_config },
+ { .name = "ads131m08", .driver_data = (kernel_ulong_t)&ads131m08_config },
{ }
};
MODULE_DEVICE_TABLE(spi, ads131m_id);
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
index 39a074bce6d5..d27c1f2885fd 100644
--- a/drivers/iio/adc/ti-ads7950.c
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -619,18 +619,18 @@ static int ti_ads7950_probe(struct spi_device *spi)
}
static const struct spi_device_id ti_ads7950_id[] = {
- { "ads7950", (kernel_ulong_t)&ti_ads7950_chip_info },
- { "ads7951", (kernel_ulong_t)&ti_ads7951_chip_info },
- { "ads7952", (kernel_ulong_t)&ti_ads7952_chip_info },
- { "ads7953", (kernel_ulong_t)&ti_ads7953_chip_info },
- { "ads7954", (kernel_ulong_t)&ti_ads7954_chip_info },
- { "ads7955", (kernel_ulong_t)&ti_ads7955_chip_info },
- { "ads7956", (kernel_ulong_t)&ti_ads7956_chip_info },
- { "ads7957", (kernel_ulong_t)&ti_ads7957_chip_info },
- { "ads7958", (kernel_ulong_t)&ti_ads7958_chip_info },
- { "ads7959", (kernel_ulong_t)&ti_ads7959_chip_info },
- { "ads7960", (kernel_ulong_t)&ti_ads7960_chip_info },
- { "ads7961", (kernel_ulong_t)&ti_ads7961_chip_info },
+ { .name = "ads7950", .driver_data = (kernel_ulong_t)&ti_ads7950_chip_info },
+ { .name = "ads7951", .driver_data = (kernel_ulong_t)&ti_ads7951_chip_info },
+ { .name = "ads7952", .driver_data = (kernel_ulong_t)&ti_ads7952_chip_info },
+ { .name = "ads7953", .driver_data = (kernel_ulong_t)&ti_ads7953_chip_info },
+ { .name = "ads7954", .driver_data = (kernel_ulong_t)&ti_ads7954_chip_info },
+ { .name = "ads7955", .driver_data = (kernel_ulong_t)&ti_ads7955_chip_info },
+ { .name = "ads7956", .driver_data = (kernel_ulong_t)&ti_ads7956_chip_info },
+ { .name = "ads7957", .driver_data = (kernel_ulong_t)&ti_ads7957_chip_info },
+ { .name = "ads7958", .driver_data = (kernel_ulong_t)&ti_ads7958_chip_info },
+ { .name = "ads7959", .driver_data = (kernel_ulong_t)&ti_ads7959_chip_info },
+ { .name = "ads7960", .driver_data = (kernel_ulong_t)&ti_ads7960_chip_info },
+ { .name = "ads7961", .driver_data = (kernel_ulong_t)&ti_ads7961_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ti_ads7950_id);
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
index ba5e240aa41a..8ad070b5ec4c 100644
--- a/drivers/iio/adc/ti-ads8688.c
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -453,8 +453,8 @@ static int ads8688_probe(struct spi_device *spi)
}
static const struct spi_device_id ads8688_id[] = {
- { "ads8684", ID_ADS8684 },
- { "ads8688", ID_ADS8688 },
+ { .name = "ads8684", .driver_data = ID_ADS8684 },
+ { .name = "ads8688", .driver_data = ID_ADS8688 },
{ }
};
MODULE_DEVICE_TABLE(spi, ads8688_id);
diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
index 7e57006a8a12..d248bbea0d31 100644
--- a/drivers/iio/adc/ti-lmp92064.c
+++ b/drivers/iio/adc/ti-lmp92064.c
@@ -357,7 +357,7 @@ static int lmp92064_adc_probe(struct spi_device *spi)
}
static const struct spi_device_id lmp92064_id_table[] = {
- { "lmp92064" },
+ { .name = "lmp92064" },
{ }
};
MODULE_DEVICE_TABLE(spi, lmp92064_id_table);
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
index 94bbf5afe30e..ea776f80de72 100644
--- a/drivers/iio/adc/ti-tlc4541.c
+++ b/drivers/iio/adc/ti-tlc4541.c
@@ -240,8 +240,8 @@ static const struct of_device_id tlc4541_dt_ids[] = {
MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
static const struct spi_device_id tlc4541_id[] = {
- { "tlc3541", TLC3541 },
- { "tlc4541", TLC4541 },
+ { .name = "tlc3541", .driver_data = TLC3541 },
+ { .name = "tlc4541", .driver_data = TLC4541 },
{ }
};
MODULE_DEVICE_TABLE(spi, tlc4541_id);
diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c
index aba4b10a17ac..125f6d7e2d84 100644
--- a/drivers/iio/adc/ti-tsc2046.c
+++ b/drivers/iio/adc/ti-tsc2046.c
@@ -121,11 +121,6 @@ struct tsc2046_adc_group_layout {
unsigned int skip;
};
-struct tsc2046_adc_dcfg {
- const struct iio_chan_spec *channels;
- unsigned int num_channels;
-};
-
struct tsc2046_adc_ch_cfg {
unsigned int settling_time_us;
unsigned int oversampling_ratio;
@@ -141,7 +136,6 @@ enum tsc2046_state {
struct tsc2046_adc_priv {
struct spi_device *spi;
- const struct tsc2046_adc_dcfg *dcfg;
bool internal_vref;
struct iio_trigger *trig;
@@ -214,11 +208,6 @@ const struct iio_chan_spec name ## _channels[] = { \
static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12);
-static const struct tsc2046_adc_dcfg tsc2046_adc_dcfg_tsc2046e = {
- .channels = tsc2046_adc_channels,
- .num_channels = ARRAY_SIZE(tsc2046_adc_channels),
-};
-
/*
* Convert time to a number of samples which can be transferred within this
* time.
@@ -739,7 +728,6 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv)
static int tsc2046_adc_probe(struct spi_device *spi)
{
- const struct tsc2046_adc_dcfg *dcfg;
struct device *dev = &spi->dev;
struct tsc2046_adc_priv *priv;
struct iio_dev *indio_dev;
@@ -752,10 +740,6 @@ static int tsc2046_adc_probe(struct spi_device *spi)
return -EINVAL;
}
- dcfg = spi_get_device_match_data(spi);
- if (!dcfg)
- return -EINVAL;
-
spi->mode &= ~SPI_MODE_X_MASK;
spi->mode |= SPI_MODE_0;
ret = spi_setup(spi);
@@ -767,14 +751,13 @@ static int tsc2046_adc_probe(struct spi_device *spi)
return -ENOMEM;
priv = iio_priv(indio_dev);
- priv->dcfg = dcfg;
priv->spi = spi;
indio_dev->name = TI_TSC2046_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = dcfg->channels;
- indio_dev->num_channels = dcfg->num_channels;
+ indio_dev->channels = tsc2046_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(tsc2046_adc_channels);
indio_dev->info = &tsc2046_adc_info;
ret = devm_regulator_get_enable_read_voltage(dev, "vref");
@@ -829,13 +812,13 @@ static int tsc2046_adc_probe(struct spi_device *spi)
}
static const struct of_device_id ads7950_of_table[] = {
- { .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e },
+ { .compatible = "ti,tsc2046e-adc" },
{ }
};
MODULE_DEVICE_TABLE(of, ads7950_of_table);
static const struct spi_device_id tsc2046_adc_spi_ids[] = {
- { "tsc2046e-adc", (unsigned long)&tsc2046_adc_dcfg_tsc2046e },
+ { .name = "tsc2046e-adc" },
{ }
};
MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids);
diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c
new file mode 100644
index 000000000000..1b55d343982e
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon-core.c
@@ -0,0 +1,1052 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon core driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/limits.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/units.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+
+#include "versal-sysmon.h"
+
+/*
+ * Oversampling ratio values exposed to userspace via IIO.
+ * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x.
+ */
+static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 };
+
+/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */
+#define SYSMON_TEMP_HYST_MASK BIT(1)
+
+/* Compute alarm register offset from a channel address */
+#define SYSMON_ALARM_OFFSET(addr) \
+ (SYSMON_ALARM_REG + ((addr) / SYSMON_ALARM_BITS_PER_REG) * SYSMON_REG_STRIDE)
+
+#define SYSMON_CHAN_TEMP(_chan, _address, _name) \
+{ \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .address = _address, \
+ .channel = _chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .datasheet_name = _name, \
+}
+
+enum sysmon_alarm_bit {
+ SYSMON_BIT_ALARM0 = 0,
+ SYSMON_BIT_ALARM1 = 1,
+ SYSMON_BIT_ALARM2 = 2,
+ SYSMON_BIT_ALARM3 = 3,
+ SYSMON_BIT_ALARM4 = 4,
+ SYSMON_BIT_TEMP = 9,
+};
+
+/* Temperature event specification: rising threshold + hysteresis only */
+static const struct iio_event_spec sysmon_temp_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+};
+
+/* Supply event specifications */
+static const struct iio_event_spec sysmon_supply_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+/*
+ * Static temperature channels (always present).
+ *
+ * These are hardware-computed aggregate registers across all active
+ * temperature satellites:
+ * temp: current max temperature across all active satellites
+ * min: current min temperature across all active satellites
+ * max_max: highest peak recorded since last hardware reset
+ * min_min: lowest trough recorded since last hardware reset
+ */
+static const struct iio_chan_spec temp_channels[] = {
+ SYSMON_CHAN_TEMP(0, SYSMON_TEMP_MAX, "temp"),
+ SYSMON_CHAN_TEMP(1, SYSMON_TEMP_MIN, "min"),
+ SYSMON_CHAN_TEMP(2, SYSMON_TEMP_MAX_MAX, "max_max"),
+ SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"),
+};
+
+static void sysmon_q8p7_to_millicelsius(s16 raw_data, int *val)
+{
+ *val = (raw_data * MILLIDEGREE_PER_DEGREE) >> SYSMON_FRACTIONAL_SHIFT;
+}
+
+static void sysmon_millicelsius_to_q8p7(u32 *raw_data, int val)
+{
+ *raw_data = (val << SYSMON_FRACTIONAL_SHIFT) / MILLIDEGREE_PER_DEGREE;
+}
+
+static void sysmon_supply_rawtoprocessed(int raw_data, int *val)
+{
+ int mantissa, format, exponent;
+
+ mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data);
+ exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data);
+ format = FIELD_GET(SYSMON_FMT_MASK, raw_data);
+ /*
+ * When format bit is set the mantissa is two's complement
+ * (per hardware spec); sign-extend to int for correct arithmetic.
+ */
+ if (format)
+ mantissa = sign_extend32(mantissa, 15);
+
+ *val = (mantissa * (int)MILLI) >> exponent;
+}
+
+static void sysmon_supply_processedtoraw(int val, u32 reg_val, u32 *raw_data)
+{
+ int exponent = FIELD_GET(SYSMON_MODE_MASK, reg_val);
+ int format = FIELD_GET(SYSMON_FMT_MASK, reg_val);
+ int scale, tmp;
+
+ scale = BIT(SYSMON_SUPPLY_MANTISSA_BITS - exponent);
+ tmp = (val * scale) / (int)MILLI;
+
+ if (format)
+ tmp = clamp(tmp, S16_MIN, S16_MAX);
+ else
+ tmp = clamp(tmp, 0, U16_MAX);
+
+ *raw_data = (u16)tmp;
+}
+
+static int sysmon_supply_thresh_offset(unsigned long address, enum iio_event_direction dir)
+{
+ if (dir == IIO_EV_DIR_RISING)
+ return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_UP;
+ if (dir == IIO_EV_DIR_FALLING)
+ return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_LOW;
+
+ return -EINVAL;
+}
+
+static int sysmon_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int regval;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
+ *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling :
+ sysmon->supply_oversampling;
+ return IIO_VAL_INT;
+ }
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ if (mask == IIO_CHAN_INFO_SCALE) {
+ /* Q8.7 to millicelsius: raw * 1000 / 128 */
+ *val = MILLIDEGREE_PER_DEGREE;
+ *val2 = BIT(SYSMON_FRACTIONAL_SHIFT);
+ return IIO_VAL_FRACTIONAL;
+ }
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ ret = regmap_read(sysmon->regmap, chan->address, &regval);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(regval, 15);
+ return IIO_VAL_INT;
+
+ case IIO_VOLTAGE:
+ if (mask != IIO_CHAN_INFO_PROCESSED)
+ return -EINVAL;
+
+ ret = regmap_read(sysmon->regmap,
+ chan->address * SYSMON_REG_STRIDE +
+ SYSMON_SUPPLY_BASE, &regval);
+ if (ret)
+ return ret;
+
+ sysmon_supply_rawtoprocessed(regval, val);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static u32 sysmon_get_event_mask(const struct iio_chan_spec *chan)
+{
+ if (chan->type == IIO_TEMP)
+ return BIT(SYSMON_BIT_TEMP);
+
+ return BIT(chan->address / SYSMON_ALARM_BITS_PER_REG);
+}
+
+static int sysmon_read_alarm_config(struct sysmon *sysmon,
+ unsigned long address)
+{
+ u32 shift = address % SYSMON_ALARM_BITS_PER_REG;
+ u32 offset = SYSMON_ALARM_OFFSET(address);
+
+ return regmap_test_bits(sysmon->regmap, offset, BIT(shift));
+}
+
+static int sysmon_write_alarm_config(struct sysmon *sysmon,
+ unsigned long address, bool enable)
+{
+ u32 shift = address % SYSMON_ALARM_BITS_PER_REG;
+ u32 offset = SYSMON_ALARM_OFFSET(address);
+
+ return regmap_assign_bits(sysmon->regmap, offset, BIT(shift), enable);
+}
+
+static int sysmon_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 sysmon *sysmon = iio_priv(indio_dev);
+ u32 mask = sysmon_get_event_mask(chan);
+ unsigned int imr;
+ int config_value;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr);
+ if (ret)
+ return ret;
+
+ /* IMR bits are 1=masked, invert to get 1=enabled */
+ imr = ~imr;
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ config_value = sysmon_read_alarm_config(sysmon, chan->address);
+ if (config_value < 0)
+ return config_value;
+ return config_value && (imr & mask);
+
+ case IIO_TEMP:
+ /*
+ * Return the administrative state, not the hardware IMR.
+ * The IRQ handler temporarily masks the interrupt during
+ * the polling window; reading IMR would show it as disabled.
+ * temp_mask bit is set when administratively disabled.
+ */
+ return !(sysmon->temp_mask & mask);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_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)
+{
+ u32 offset = SYSMON_ALARM_OFFSET(chan->address);
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ u32 mask = sysmon_get_event_mask(chan);
+ unsigned int alarm_config;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ ret = sysmon_write_alarm_config(sysmon, chan->address, state);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(sysmon->regmap, offset, &alarm_config);
+ if (ret)
+ return ret;
+
+ if (alarm_config)
+ return regmap_write(sysmon->regmap, SYSMON_IER, mask);
+
+ return regmap_write(sysmon->regmap, SYSMON_IDR, mask);
+
+ case IIO_TEMP:
+ if (state) {
+ ret = regmap_write(sysmon->regmap, SYSMON_IER, mask);
+ if (ret)
+ return ret;
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->temp_mask &= ~mask;
+ } else {
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, mask);
+ if (ret)
+ return ret;
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->temp_mask |= mask;
+ }
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Recompute the lower threshold register from upper threshold and
+ * cached hysteresis. Called when either upper threshold or hysteresis
+ * is written.
+ */
+static int sysmon_update_temp_lower(struct sysmon *sysmon)
+{
+ unsigned int upper_reg;
+ int upper_mc, lower_mc;
+ u32 raw_val;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc);
+ lower_mc = clamp(upper_mc - sysmon->temp_hysteresis, -256000, 255992);
+ sysmon_millicelsius_to_q8p7(&raw_val, lower_mc);
+
+ return regmap_write(sysmon->regmap, SYSMON_TEMP_TH_LOW, raw_val);
+}
+
+static int sysmon_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int reg_val;
+ int offset;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &reg_val);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(reg_val, val);
+
+ return IIO_VAL_INT;
+
+ case IIO_EV_INFO_HYSTERESIS:
+ *val = sysmon->temp_hysteresis;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_VOLTAGE:
+ offset = sysmon_supply_thresh_offset(chan->address, dir);
+ if (offset < 0)
+ return offset;
+
+ ret = regmap_read(sysmon->regmap, offset, &reg_val);
+ if (ret)
+ return ret;
+
+ sysmon_supply_rawtoprocessed(reg_val, val);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int reg_val;
+ u32 raw_val;
+ int offset;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ /* Q8.7 signed range: -256000 to +255992 mC */
+ if (val < -256000 || val > 255992)
+ return -EINVAL;
+
+ sysmon_millicelsius_to_q8p7(&raw_val, val);
+
+ ret = regmap_write(sysmon->regmap, SYSMON_TEMP_TH_UP, raw_val);
+ if (ret)
+ return ret;
+
+ /* Recompute lower = upper - hysteresis */
+ return sysmon_update_temp_lower(sysmon);
+
+ case IIO_EV_INFO_HYSTERESIS:
+ if (val < 0)
+ return -EINVAL;
+
+ sysmon->temp_hysteresis = val;
+
+ return sysmon_update_temp_lower(sysmon);
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_VOLTAGE:
+ offset = sysmon_supply_thresh_offset(chan->address, dir);
+ if (offset < 0)
+ return offset;
+
+ ret = regmap_read(sysmon->regmap, offset, &reg_val);
+ if (ret)
+ return ret;
+
+ /* Clamp to prevent overflow in processedtoraw conversion */
+ if (val < -32768 || val > 32767)
+ return -EINVAL;
+
+ sysmon_supply_processedtoraw(val, reg_val, &raw_val);
+
+ /*
+ * The hardware threshold register returns FMT and MODE
+ * bits in the upper 16 bits on read, but only the lower
+ * 16-bit mantissa is used on write.
+ */
+ return regmap_write(sysmon->regmap, offset, raw_val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_set_avg_enable(struct sysmon *sysmon,
+ u32 base, u32 count, u32 val)
+{
+ struct regmap *map = sysmon->regmap;
+ int ret;
+
+ for (unsigned int i = 0; i < count; i++) {
+ ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val)
+{
+ /*
+ * HW register encoding is sample_count / 2:
+ * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based).
+ */
+ unsigned int hw_val = val >> 1;
+ unsigned int readback;
+ int ret;
+
+ ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
+ SYSMON_CONFIG_TEMP_SAT_OSR,
+ FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val));
+ if (ret)
+ return ret;
+
+ /*
+ * Readback fence: the SysMon CONFIG register resides in the
+ * PMC domain behind the NoC. A posted write may not reach the
+ * hardware before the next MMIO access. Reading the register
+ * back forces the interconnect to complete the write, preventing
+ * a bus hang on the subsequent access.
+ */
+ regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
+
+ return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE,
+ SYSMON_TEMP_EN_AVG_COUNT,
+ hw_val ? ~0 : 0);
+}
+
+static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val)
+{
+ /* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */
+ unsigned int hw_val = val >> 1;
+ unsigned int readback;
+ int ret;
+
+ ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
+ SYSMON_CONFIG_SUPPLY_OSR,
+ FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val));
+ if (ret)
+ return ret;
+
+ /* Readback fence -- see sysmon_osr_write_temp for details */
+ regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
+
+ return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE,
+ SYSMON_SUPPLY_EN_AVG_COUNT,
+ hw_val ? ~0 : 0);
+}
+
+static int sysmon_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int i;
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) {
+ if (val == sysmon_oversampling_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(sysmon_oversampling_avail))
+ return -EINVAL;
+
+ guard(mutex)(&sysmon->lock);
+
+ if (chan->type == IIO_TEMP) {
+ ret = sysmon_osr_write_temp(sysmon, val);
+ if (ret)
+ return ret;
+ sysmon->temp_oversampling = val;
+ } else {
+ ret = sysmon_osr_write_supply(sysmon, val);
+ if (ret)
+ return ret;
+ sysmon->supply_oversampling = val;
+ }
+
+ return 0;
+}
+
+static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return IIO_VAL_INT;
+
+ return -EINVAL;
+}
+
+static int sysmon_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+
+ *vals = sysmon_oversampling_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(sysmon_oversampling_avail);
+
+ return IIO_AVAIL_LIST;
+}
+
+static int sysmon_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ char *label)
+{
+ if (chan->datasheet_name)
+ return sysfs_emit(label, "%s\n", chan->datasheet_name);
+
+ return -EINVAL;
+}
+
+static const struct iio_info sysmon_iio_info = {
+ .read_raw = sysmon_read_raw,
+ .write_raw = sysmon_write_raw,
+ .write_raw_get_fmt = sysmon_write_raw_get_fmt,
+ .read_avail = sysmon_read_avail,
+ .read_label = sysmon_read_label,
+ .read_event_config = sysmon_read_event_config,
+ .write_event_config = sysmon_write_event_config,
+ .read_event_value = sysmon_read_event_value,
+ .write_event_value = sysmon_write_event_value,
+};
+
+static void sysmon_push_event(struct iio_dev *indio_dev, u32 address)
+{
+ const struct iio_chan_spec *chan;
+ enum iio_event_direction dir;
+
+ for (unsigned int i = 0; i < indio_dev->num_channels; i++) {
+ if (indio_dev->channels[i].address != address)
+ continue;
+
+ chan = &indio_dev->channels[i];
+ /* Temp uses hysteresis mode (rising only), voltage uses window */
+ dir = (chan->type == IIO_TEMP) ? IIO_EV_DIR_RISING :
+ IIO_EV_DIR_EITHER;
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(chan->type,
+ chan->channel,
+ IIO_EV_TYPE_THRESH,
+ dir),
+ iio_get_time_ns(indio_dev));
+ }
+}
+
+static int sysmon_handle_event(struct iio_dev *indio_dev, u32 event)
+{
+ u32 alarm_flag_offset = SYSMON_ALARM_FLAG + event * SYSMON_REG_STRIDE;
+ u32 alarm_reg_offset = SYSMON_ALARM_REG + event * SYSMON_REG_STRIDE;
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned long alarm_flag_reg;
+ unsigned int reg_val;
+ u32 address, bit;
+ int ret;
+
+ switch (event) {
+ case SYSMON_BIT_TEMP:
+ sysmon_push_event(indio_dev, SYSMON_TEMP_MAX);
+
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, BIT(SYSMON_BIT_TEMP));
+ if (ret)
+ return ret;
+
+ sysmon->masked_temp |= BIT(SYSMON_BIT_TEMP);
+ return 0;
+
+ case SYSMON_BIT_ALARM0:
+ case SYSMON_BIT_ALARM1:
+ case SYSMON_BIT_ALARM2:
+ case SYSMON_BIT_ALARM3:
+ case SYSMON_BIT_ALARM4:
+ ret = regmap_read(sysmon->regmap, alarm_flag_offset, &reg_val);
+ if (ret)
+ return ret;
+
+ alarm_flag_reg = reg_val;
+
+ for_each_set_bit(bit, &alarm_flag_reg, SYSMON_ALARM_BITS_PER_REG) {
+ address = bit + SYSMON_ALARM_BITS_PER_REG * event;
+ sysmon_push_event(indio_dev, address);
+ ret = regmap_clear_bits(sysmon->regmap, alarm_reg_offset, BIT(bit));
+ if (ret)
+ return ret;
+ }
+
+ return regmap_write(sysmon->regmap, alarm_flag_offset, alarm_flag_reg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void sysmon_handle_events(struct iio_dev *indio_dev,
+ unsigned long events)
+{
+ unsigned int bit;
+
+ for_each_set_bit(bit, &events, SYSMON_NO_OF_EVENTS)
+ sysmon_handle_event(indio_dev, bit);
+}
+
+static void sysmon_unmask_temp(struct sysmon *sysmon, unsigned int isr)
+{
+ unsigned int status;
+ u32 ier;
+
+ status = isr & SYSMON_TEMP_INTR_MASK;
+
+ ier = ~status & sysmon->masked_temp;
+ sysmon->masked_temp &= status;
+
+ /* Only unmask if not administratively disabled by userspace */
+ ier &= ~sysmon->temp_mask;
+
+ regmap_write(sysmon->regmap, SYSMON_IER, ier);
+}
+
+/*
+ * Versal threshold interrupts are level-sensitive. Active threshold
+ * interrupts are masked in the handler and polled via delayed work
+ * until the condition clears, then unmasked.
+ */
+static void sysmon_unmask_worker(struct work_struct *work)
+{
+ struct sysmon *sysmon =
+ container_of(work, struct sysmon, sysmon_unmask_work.work);
+ unsigned int isr;
+
+ /*
+ * If the ISR read fails, skip processing to avoid acting
+ * on undefined data.
+ */
+ scoped_guard(spinlock_irq, &sysmon->irq_lock) {
+ if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr))
+ break;
+ regmap_write(sysmon->regmap, SYSMON_ISR, isr);
+ sysmon_unmask_temp(sysmon, isr);
+ }
+
+ if (sysmon->masked_temp)
+ schedule_delayed_work(&sysmon->sysmon_unmask_work,
+ msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS));
+ else
+ regmap_write(sysmon->regmap, SYSMON_STATUS_RESET, 1);
+}
+
+static irqreturn_t sysmon_iio_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int isr, imr;
+
+ guard(spinlock)(&sysmon->irq_lock);
+
+ if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr) ||
+ regmap_read(sysmon->regmap, SYSMON_IMR, &imr))
+ return IRQ_NONE;
+
+ isr &= ~imr;
+ if (!isr)
+ return IRQ_NONE;
+
+ regmap_write(sysmon->regmap, SYSMON_ISR, isr);
+
+ sysmon_handle_events(indio_dev, isr);
+ schedule_delayed_work(&sysmon->sysmon_unmask_work,
+ msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS));
+
+ return IRQ_HANDLED;
+}
+
+static void sysmon_disable_interrupts(void *data)
+{
+ struct sysmon *sysmon = data;
+
+ regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK);
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->masked_temp = 0;
+
+ cancel_delayed_work_sync(&sysmon->sysmon_unmask_work);
+}
+
+static int sysmon_init_interrupt(struct sysmon *sysmon,
+ struct device *dev,
+ struct iio_dev *indio_dev,
+ int irq)
+{
+ unsigned int imr;
+ int ret;
+
+ /* Events not supported without IRQ (e.g. I2C path) */
+ if (!irq)
+ return 0;
+
+ INIT_DELAYED_WORK(&sysmon->sysmon_unmask_work, sysmon_unmask_worker);
+
+ ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr);
+ if (ret)
+ return ret;
+ sysmon->temp_mask = imr & SYSMON_TEMP_INTR_MASK;
+
+ ret = devm_request_irq(dev, irq, sysmon_iio_irq, 0, "sysmon-irq", indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, sysmon_disable_interrupts, sysmon);
+}
+
+/*
+ * Initialize the cached hysteresis for a temperature channel from the
+ * current hardware threshold registers: hysteresis = upper - lower.
+ */
+static int sysmon_init_hysteresis(struct sysmon *sysmon, int *hysteresis)
+{
+ unsigned int upper_reg, lower_reg;
+ int upper_mc, lower_mc;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_LOW, &lower_reg);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc);
+ sysmon_q8p7_to_millicelsius(lower_reg, &lower_mc);
+ *hysteresis = upper_mc - lower_mc;
+
+ return 0;
+}
+
+/**
+ * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels.
+ * @indio_dev: IIO device instance
+ * @dev: Parent device
+ * @irq: IRQ number (positive enables event channels, 0 disables)
+ *
+ * Reads voltage-channels and temperature-channels container nodes from
+ * firmware and builds the IIO channel array. Static temperature channels
+ * and event channels are prepended, followed by supply and satellite
+ * channels from DT.
+ *
+ * Event channels and per-channel event specs are only added when the
+ * device has an IRQ. I2C devices have no interrupt line, and the I2C
+ * regmap cannot be called from atomic context, so events are not
+ * supported on that path.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq)
+{
+ unsigned int num_chan, num_static, num_supply, num_temp;
+ unsigned int idx, temp_chan_idx, volt_chan_idx;
+ struct iio_chan_spec *sysmon_channels;
+ const char *label;
+ u32 reg;
+ int ret;
+
+ struct fwnode_handle *supply_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "voltage-channels");
+ num_supply = fwnode_get_child_node_count(supply_node);
+
+ struct fwnode_handle *temp_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "temperature-channels");
+ num_temp = fwnode_get_child_node_count(temp_node);
+
+ num_static = ARRAY_SIZE(temp_channels);
+ num_chan = size_add(num_temp, size_add(num_static, num_supply));
+ sysmon_channels = devm_kcalloc(dev, num_chan, sizeof(*sysmon_channels), GFP_KERNEL);
+ if (!sysmon_channels)
+ return -ENOMEM;
+
+ memcpy(sysmon_channels, temp_channels, sizeof(temp_channels));
+
+ /* Attach event spec to channel 0 when IRQ is available */
+ if (irq > 0) {
+ sysmon_channels[0].event_spec = sysmon_temp_events;
+ sysmon_channels[0].num_event_specs = ARRAY_SIZE(sysmon_temp_events);
+ }
+
+ idx = num_static;
+
+ /* Supply channels from DT */
+ fwnode_for_each_child_node_scoped(supply_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing reg for supply channel\n");
+
+ if (reg > SYSMON_SUPPLY_IDX_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "supply reg %u exceeds max %u\n",
+ reg, SYSMON_SUPPLY_IDX_MAX);
+
+ ret = fwnode_property_read_string(child, "label", &label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing label for supply channel\n");
+
+ sysmon_channels[idx++] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .address = reg,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .event_spec = irq > 0 ?
+ sysmon_supply_events : NULL,
+ .num_event_specs = irq > 0 ?
+ ARRAY_SIZE(sysmon_supply_events) : 0,
+ .datasheet_name = label,
+ };
+ }
+
+ /* Temperature satellite channels from DT */
+ fwnode_for_each_child_node_scoped(temp_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing reg for temp channel\n");
+
+ if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "temp reg %u out of range [1..%u]\n",
+ reg, SYSMON_TEMP_SAT_MAX);
+
+ ret = fwnode_property_read_string(child, "label", &label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing label for temp channel\n");
+
+ sysmon_channels[idx++] = (struct iio_chan_spec) {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .address = SYSMON_TEMP_SAT_BASE +
+ (reg - 1) * SYSMON_REG_STRIDE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .datasheet_name = label,
+ };
+ }
+
+ indio_dev->num_channels = idx;
+ indio_dev->info = &sysmon_iio_info;
+
+ /*
+ * Assign per-type sequential channel numbers.
+ * IIO sysfs uses type prefix (in_tempN, in_voltageN)
+ * so numbers only need to be unique within each type.
+ */
+ temp_chan_idx = 0;
+ volt_chan_idx = 0;
+ for (unsigned int idx = 0; idx < indio_dev->num_channels; idx++) {
+ if (sysmon_channels[idx].type == IIO_TEMP)
+ sysmon_channels[idx].channel = temp_chan_idx++;
+ else
+ sysmon_channels[idx].channel = volt_chan_idx++;
+ }
+
+ indio_dev->channels = sysmon_channels;
+
+ return 0;
+}
+
+/**
+ * devm_versal_sysmon_core_probe() - Initialize Versal SysMon core
+ * @dev: Parent device
+ * @regmap: Register map for hardware access
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
+{
+ struct iio_dev *indio_dev;
+ struct sysmon *sysmon;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ sysmon = iio_priv(indio_dev);
+ sysmon->regmap = regmap;
+ sysmon->temp_oversampling = 1;
+ sysmon->supply_oversampling = 1;
+
+ ret = devm_mutex_init(dev, &sysmon->lock);
+ if (ret)
+ return ret;
+ spin_lock_init(&sysmon->irq_lock);
+
+ /* Disable all interrupts and clear pending status */
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK);
+ if (ret)
+ return ret;
+ ret = regmap_write(sysmon->regmap, SYSMON_ISR, SYSMON_INTR_ALL_MASK);
+ if (ret)
+ return ret;
+
+ irq = fwnode_irq_get(dev_fwnode(dev), 0);
+ if (irq == -EPROBE_DEFER)
+ return dev_err_probe(dev, irq, "failed to get IRQ\n");
+
+ indio_dev->name = "versal-sysmon";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = sysmon_parse_fw(indio_dev, dev, irq);
+ if (ret)
+ return ret;
+
+ if (irq > 0) {
+ /* Set hysteresis mode for temperature threshold */
+ ret = regmap_set_bits(sysmon->regmap, SYSMON_TEMP_EV_CFG,
+ SYSMON_TEMP_HYST_MASK);
+ if (ret)
+ return ret;
+
+ /* Initialize cached hysteresis from hardware registers */
+ ret = sysmon_init_hysteresis(sysmon, &sysmon->temp_hysteresis);
+ if (ret)
+ return ret;
+
+ ret = sysmon_init_interrupt(sysmon, dev, indio_dev, irq);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon Core Driver");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c
new file mode 100644
index 000000000000..e9a7629159ab
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon-i2c.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon I2C driver
+ *
+ * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "versal-sysmon.h"
+
+#define SYSMON_I2C_INSTR_READ BIT(2)
+#define SYSMON_I2C_INSTR_WRITE BIT(3)
+
+/*
+ * I2C command frame layout (8 bytes):
+ * [0..3] data payload (little-endian u32)
+ * [4..5] register offset >> 2 (little-endian u16)
+ * [6] instruction (read/write)
+ * [7] reserved
+ */
+#define SYSMON_I2C_DATA_OFS 0
+#define SYSMON_I2C_REG_OFS 4
+#define SYSMON_I2C_INSTR_OFS 6
+
+static int sysmon_i2c_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct i2c_client *client = context;
+ u8 write_buf[8] = { };
+ u8 read_buf[4];
+ int ret;
+
+ put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]);
+ write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ;
+
+ ret = i2c_master_send(client, write_buf, sizeof(write_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(write_buf))
+ return -EIO;
+
+ ret = i2c_master_recv(client, read_buf, sizeof(read_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(read_buf))
+ return -EIO;
+
+ *val = get_unaligned_le32(read_buf);
+
+ return 0;
+}
+
+static int sysmon_i2c_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *client = context;
+ u8 write_buf[8] = { };
+ int ret;
+
+ put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]);
+ put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]);
+ write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE;
+
+ ret = i2c_master_send(client, write_buf, sizeof(write_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(write_buf))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Almost all registers are volatile (live ADC readings, interrupt
+ * status). The rest are not accessed often enough to benefit from
+ * caching.
+ */
+static const struct regmap_config sysmon_i2c_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = SYSMON_REG_STRIDE,
+ .max_register = SYSMON_MAX_REG,
+ .reg_read = sysmon_i2c_reg_read,
+ .reg_write = sysmon_i2c_reg_write,
+};
+
+static int sysmon_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* I2C has no IRQ connection; events are not supported */
+ return devm_versal_sysmon_core_probe(dev, regmap);
+}
+
+static const struct of_device_id sysmon_i2c_of_match_table[] = {
+ { .compatible = "xlnx,versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table);
+
+static const struct i2c_device_id sysmon_i2c_id_table[] = {
+ { .name = "versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table);
+
+static struct i2c_driver sysmon_i2c_driver = {
+ .probe = sysmon_i2c_probe,
+ .driver = {
+ .name = "versal-sysmon-i2c",
+ .of_match_table = sysmon_i2c_of_match_table,
+ },
+ .id_table = sysmon_i2c_id_table,
+};
+module_i2c_driver(sysmon_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver");
+MODULE_IMPORT_NS("VERSAL_SYSMON");
+MODULE_AUTHOR("Conall O'Griofa <conall.ogriofa@amd.com>");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c
new file mode 100644
index 000000000000..529d0486c9f9
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon MMIO platform driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "versal-sysmon.h"
+
+struct sysmon_mmio {
+ void __iomem *base;
+};
+
+static int sysmon_mmio_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct sysmon_mmio *mmio = context;
+
+ *val = readl(mmio->base + reg);
+ return 0;
+}
+
+static int sysmon_mmio_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct sysmon_mmio *mmio = context;
+
+ /* NPI must be unlocked before any register write except to NPI_LOCK */
+ if (reg != SYSMON_NPI_LOCK)
+ writel(SYSMON_NPI_UNLOCK_CODE, mmio->base + SYSMON_NPI_LOCK);
+ writel(val, mmio->base + reg);
+
+ return 0;
+}
+
+static const struct regmap_config sysmon_mmio_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = SYSMON_REG_STRIDE,
+ .max_register = SYSMON_MAX_REG,
+ .reg_read = sysmon_mmio_reg_read,
+ .reg_write = sysmon_mmio_reg_write,
+ .fast_io = true,
+};
+
+static int sysmon_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sysmon_mmio *mmio;
+ struct regmap *regmap;
+
+ mmio = devm_kzalloc(dev, sizeof(*mmio), GFP_KERNEL);
+ if (!mmio)
+ return -ENOMEM;
+
+ mmio->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mmio->base))
+ return PTR_ERR(mmio->base);
+
+ regmap = devm_regmap_init(dev, NULL, mmio, &sysmon_mmio_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return devm_versal_sysmon_core_probe(dev, regmap);
+}
+
+static const struct of_device_id sysmon_of_match_table[] = {
+ { .compatible = "xlnx,versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sysmon_of_match_table);
+
+static struct platform_driver sysmon_platform_driver = {
+ .probe = sysmon_platform_probe,
+ .driver = {
+ .name = "versal-sysmon",
+ .of_match_table = sysmon_of_match_table,
+ },
+};
+module_platform_driver(sysmon_platform_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon Platform Driver");
+MODULE_IMPORT_NS("VERSAL_SYSMON");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h
new file mode 100644
index 000000000000..bb9a75bf71c0
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon.h
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AMD Versal SysMon driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _VERSAL_SYSMON_H_
+#define _VERSAL_SYSMON_H_
+
+#include <linux/bits.h>
+#include <linux/mutex.h>
+#include <linux/spinlock_types.h>
+#include <linux/workqueue.h>
+
+struct device;
+struct regmap;
+
+/* Register offsets (sorted by address) */
+#define SYSMON_NPI_LOCK 0x000C
+#define SYSMON_ISR 0x0044
+#define SYSMON_IMR 0x0048
+#define SYSMON_IER 0x004C
+#define SYSMON_IDR 0x0050
+#define SYSMON_CONFIG 0x0100
+#define SYSMON_TEMP_MAX 0x1030
+#define SYSMON_TEMP_MIN 0x1034
+#define SYSMON_SUPPLY_BASE 0x1040
+#define SYSMON_ALARM_FLAG 0x1018
+#define SYSMON_ALARM_REG 0x1940
+#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958
+#define SYSMON_TEMP_TH_LOW 0x1970
+#define SYSMON_TEMP_TH_UP 0x1974
+#define SYSMON_SUPPLY_TH_LOW 0x1980
+#define SYSMON_SUPPLY_TH_UP 0x1C80
+#define SYSMON_TEMP_EV_CFG 0x1F84
+#define SYSMON_TEMP_MIN_MIN 0x1F8C
+#define SYSMON_TEMP_MAX_MAX 0x1F90
+#define SYSMON_STATUS_RESET 0x1F94
+#define SYSMON_TEMP_SAT_BASE 0x1FAC
+#define SYSMON_TEMP_EN_AVG_BASE 0x24B4
+#define SYSMON_MAX_REG 0x24C0
+
+/* NPI unlock value written to SYSMON_NPI_LOCK */
+#define SYSMON_NPI_UNLOCK_CODE 0xF9E8D7C6
+
+/* Register stride: 4 bytes per 32-bit register */
+#define SYSMON_REG_STRIDE 4
+
+#define SYSMON_SUPPLY_IDX_MAX 159
+#define SYSMON_TEMP_SAT_MAX 64
+#define SYSMON_NO_OF_EVENTS 32
+#define SYSMON_INTR_ALL_MASK GENMASK(31, 0)
+
+/* ISR/IMR temperature alarm mask (bit 9) */
+#define SYSMON_TEMP_INTR_MASK BIT(9)
+
+/* SYSMON_CONFIG: supply oversampling ratio */
+#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14)
+
+/* SYSMON_CONFIG: temperature satellite oversampling ratio */
+#define SYSMON_CONFIG_TEMP_SAT_OSR GENMASK(27, 24)
+
+/* Per-channel averaging enable register counts */
+#define SYSMON_SUPPLY_EN_AVG_COUNT 5
+#define SYSMON_TEMP_EN_AVG_COUNT 2
+
+/* Supply voltage conversion register fields */
+#define SYSMON_MANTISSA_MASK GENMASK(15, 0)
+#define SYSMON_FMT_MASK BIT(16)
+#define SYSMON_MODE_MASK GENMASK(18, 17)
+
+/* Q8.7 fractional shift */
+#define SYSMON_FRACTIONAL_SHIFT 7U
+#define SYSMON_SUPPLY_MANTISSA_BITS 16
+
+/* Bits per alarm register */
+#define SYSMON_ALARM_BITS_PER_REG 32
+
+#define SYSMON_UNMASK_WORK_DELAY_MS 500
+
+/**
+ * struct sysmon - Driver data for Versal SysMon
+ * @regmap: register map for hardware access
+ * @lock: protects read-modify-write sequences on threshold registers
+ * and cached state that spans multiple regmap calls
+ * @irq_lock: protects interrupt mask register updates (MMIO path only)
+ * @masked_temp: currently masked temperature alarm bits
+ * @temp_mask: temperature interrupt configuration mask
+ * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius
+ * @sysmon_unmask_work: re-enables events after alarm condition clears
+ * @temp_oversampling: current temp oversampling ratio
+ * @supply_oversampling: current supply oversampling ratio
+ */
+struct sysmon {
+ struct regmap *regmap;
+ /*
+ * Protects read-modify-write sequences on threshold registers
+ * and cached state (oversampling ratios, hysteresis values)
+ * that spans multiple regmap calls.
+ */
+ struct mutex lock;
+ /*
+ * Protects interrupt mask register updates. Only used on the
+ * MMIO path (fast_io regmap); I2C has no IRQ and never reaches
+ * the event code that takes this lock.
+ */
+ spinlock_t irq_lock;
+ unsigned int masked_temp;
+ unsigned int temp_mask;
+ int temp_hysteresis;
+ struct delayed_work sysmon_unmask_work;
+ unsigned int temp_oversampling;
+ unsigned int supply_oversampling;
+};
+
+int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap);
+
+#endif /* _VERSAL_SYSMON_H_ */
diff --git a/drivers/iio/addac/ad74115.c b/drivers/iio/addac/ad74115.c
index 41e0b1d334cc..9b9d7a12ce87 100644
--- a/drivers/iio/addac/ad74115.c
+++ b/drivers/iio/addac/ad74115.c
@@ -1904,7 +1904,7 @@ static int __init ad74115_register_driver(struct spi_driver *spi)
}
static const struct spi_device_id ad74115_spi_id[] = {
- { "ad74115h" },
+ { .name = "ad74115h" },
{ }
};
diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c
index 43bd2079cf6d..2e5a58e48a89 100644
--- a/drivers/iio/addac/ad74413r.c
+++ b/drivers/iio/addac/ad74413r.c
@@ -1457,7 +1457,7 @@ static int ad74413r_probe(struct spi_device *spi)
ret = devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt,
0, st->chip_info->name, indio_dev);
if (ret)
- return dev_err_probe(st->dev, ret, "Failed to request irq\n");
+ return ret;
ret = devm_iio_triggered_buffer_setup(st->dev, indio_dev,
&iio_pollfunc_store_time,
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
index 654a3a50eb4f..3a6a09ffa526 100644
--- a/drivers/iio/afe/iio-rescale.c
+++ b/drivers/iio/afe/iio-rescale.c
@@ -608,3 +608,4 @@ module_platform_driver(rescale_driver);
MODULE_DESCRIPTION("IIO rescale driver");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c
index affc9c9d8488..e12f8604f59f 100644
--- a/drivers/iio/amplifiers/ad8366.c
+++ b/drivers/iio/amplifiers/ad8366.c
@@ -351,19 +351,19 @@ static int ad8366_probe(struct spi_device *spi)
}
static const struct spi_device_id ad8366_id[] = {
- { "ad8366", (kernel_ulong_t)&ad8366_chip_info },
- { "ada4961", (kernel_ulong_t)&ada4961_chip_info },
- { "adl5240", (kernel_ulong_t)&adl5240_chip_info },
- { "adrf5702", (kernel_ulong_t)&adrf5702_chip_info },
- { "adrf5703", (kernel_ulong_t)&adrf5703_chip_info },
- { "adrf5720", (kernel_ulong_t)&adrf5720_chip_info },
- { "adrf5730", (kernel_ulong_t)&adrf5730_chip_info },
- { "adrf5731", (kernel_ulong_t)&adrf5731_chip_info },
- { "hmc271a", (kernel_ulong_t)&hmc271_chip_info },
- { "hmc792a", (kernel_ulong_t)&hmc792_chip_info },
- { "hmc1018a", (kernel_ulong_t)&hmc1018_chip_info },
- { "hmc1019a", (kernel_ulong_t)&hmc1019_chip_info },
- { "hmc1119", (kernel_ulong_t)&hmc1119_chip_info },
+ { .name = "ad8366", .driver_data = (kernel_ulong_t)&ad8366_chip_info },
+ { .name = "ada4961", .driver_data = (kernel_ulong_t)&ada4961_chip_info },
+ { .name = "adl5240", .driver_data = (kernel_ulong_t)&adl5240_chip_info },
+ { .name = "adrf5702", .driver_data = (kernel_ulong_t)&adrf5702_chip_info },
+ { .name = "adrf5703", .driver_data = (kernel_ulong_t)&adrf5703_chip_info },
+ { .name = "adrf5720", .driver_data = (kernel_ulong_t)&adrf5720_chip_info },
+ { .name = "adrf5730", .driver_data = (kernel_ulong_t)&adrf5730_chip_info },
+ { .name = "adrf5731", .driver_data = (kernel_ulong_t)&adrf5731_chip_info },
+ { .name = "hmc271a", .driver_data = (kernel_ulong_t)&hmc271_chip_info },
+ { .name = "hmc792a", .driver_data = (kernel_ulong_t)&hmc792_chip_info },
+ { .name = "hmc1018a", .driver_data = (kernel_ulong_t)&hmc1018_chip_info },
+ { .name = "hmc1019a", .driver_data = (kernel_ulong_t)&hmc1019_chip_info },
+ { .name = "hmc1119", .driver_data = (kernel_ulong_t)&hmc1119_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad8366_id);
diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c
index 71e361af2074..9702de3a5074 100644
--- a/drivers/iio/amplifiers/ada4250.c
+++ b/drivers/iio/amplifiers/ada4250.c
@@ -358,7 +358,7 @@ static int ada4250_probe(struct spi_device *spi)
}
static const struct spi_device_id ada4250_id[] = {
- { "ada4250", 0 },
+ { .name = "ada4250" },
{ }
};
MODULE_DEVICE_TABLE(spi, ada4250_id);
diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
index 0c266c216525..b58263bcf3cb 100644
--- a/drivers/iio/buffer/industrialio-buffer-cb.c
+++ b/drivers/iio/buffer/industrialio-buffer-cb.c
@@ -154,3 +154,4 @@ EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Industrial I/O callback buffer");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 98acce909854..ecc02a427b92 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -56,6 +56,23 @@ static void iio_dmaengine_buffer_block_done(void *data,
iio_dma_buffer_block_done(block);
}
+/*
+ * Cyclic transfers run until abort. Cap active cyclic blocks to one until there
+ * is a use case for more.
+ */
+static bool iio_dmaengine_buffer_has_active_cyclic(struct dmaengine_buffer *dmaengine_buffer)
+{
+ struct iio_dma_buffer_block *block;
+
+ guard(spinlock_irqsave)(&dmaengine_buffer->queue.list_lock);
+ list_for_each_entry(block, &dmaengine_buffer->active, head) {
+ if (block->cyclic)
+ return true;
+ }
+
+ return false;
+}
+
static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
struct iio_dma_buffer_block *block)
{
@@ -79,7 +96,14 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
else
dma_dir = DMA_MEM_TO_DEV;
+ if (block->cyclic && iio_dmaengine_buffer_has_active_cyclic(dmaengine_buffer)) {
+ dev_err(queue->dev, "cyclic DMA transfer already active\n");
+ return -EBUSY;
+ }
+
if (block->sg_table) {
+ unsigned long flags;
+
sgl = block->sg_table->sgl;
nents = sg_nents_for_len(sgl, block->bytes_used);
if (nents < 0)
@@ -99,9 +123,19 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
sgl = sg_next(sgl);
}
+ if (block->cyclic)
+ flags = DMA_PREP_REPEAT;
+ else
+ flags = DMA_PREP_INTERRUPT;
+
+ /*
+ * A new transfer may need to end an already active cyclic transfer
+ * before it can run, so always set the EOT flag.
+ */
+ flags |= DMA_PREP_LOAD_EOT;
desc = dmaengine_prep_peripheral_dma_vec(dmaengine_buffer->chan,
vecs, nents, dma_dir,
- DMA_PREP_INTERRUPT);
+ flags);
kfree(vecs);
} else {
max_size = min(block->size, dmaengine_buffer->max_size);
@@ -122,8 +156,10 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
if (!desc)
return -ENOMEM;
- desc->callback_result = iio_dmaengine_buffer_block_done;
- desc->callback_param = block;
+ if (!block->cyclic) {
+ desc->callback_result = iio_dmaengine_buffer_block_done;
+ desc->callback_param = block;
+ }
cookie = dmaengine_submit(desc);
if (dma_submit_error(cookie))
diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c
index 10e912bbf0c5..d035aa33b357 100644
--- a/drivers/iio/buffer/industrialio-hw-consumer.c
+++ b/drivers/iio/buffer/industrialio-hw-consumer.c
@@ -216,3 +216,4 @@ EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Hardware consumer buffer the IIO framework");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c
index 4cc0e491621a..c34d67bcb2a3 100644
--- a/drivers/iio/chemical/bme680_i2c.c
+++ b/drivers/iio/chemical/bme680_i2c.c
@@ -24,10 +24,9 @@ static int bme680_i2c_probe(struct i2c_client *client)
const char *name = NULL;
regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "Failed to register i2c regmap\n");
if (id)
name = id->name;
diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c
index 785200a6fd65..2175b9564fa1 100644
--- a/drivers/iio/chemical/bme680_spi.c
+++ b/drivers/iio/chemical/bme680_spi.c
@@ -121,16 +121,15 @@ static int bme680_spi_probe(struct spi_device *spi)
regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
bus_context, &bme680_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
+ if (IS_ERR(regmap))
+ return dev_err_probe(&spi->dev, PTR_ERR(regmap),
+ "Failed to register spi regmap\n");
return bme680_core_probe(&spi->dev, regmap, id->name);
}
static const struct spi_device_id bme680_spi_id[] = {
- {"bme680", 0},
+ { .name = "bme680" },
{ }
};
MODULE_DEVICE_TABLE(spi, bme680_spi_id);
diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c
index ce7187ccd706..d126ee12ffe5 100644
--- a/drivers/iio/chemical/ccs811.c
+++ b/drivers/iio/chemical/ccs811.c
@@ -484,10 +484,8 @@ static int ccs811_probe(struct i2c_client *client)
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"ccs811_irq", indio_dev);
- if (ret) {
- dev_err(&client->dev, "irq request error %d\n", -ret);
+ if (ret)
goto err_poweroff;
- }
data->drdy_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d",
diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c
index bbc96c4c6283..7c1ffda38b99 100644
--- a/drivers/iio/chemical/ens160_core.c
+++ b/drivers/iio/chemical/ens160_core.c
@@ -320,7 +320,7 @@ static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq)
IRQF_NO_THREAD, indio_dev->name,
indio_dev->trig);
if (ret)
- return dev_err_probe(dev, ret, "failed to request irq\n");
+ return ret;
return 0;
}
diff --git a/drivers/iio/chemical/ens160_spi.c b/drivers/iio/chemical/ens160_spi.c
index a674c0e1bf4b..7c11d19291c9 100644
--- a/drivers/iio/chemical/ens160_spi.c
+++ b/drivers/iio/chemical/ens160_spi.c
@@ -39,7 +39,7 @@ static const struct of_device_id ens160_spi_of_match[] = {
MODULE_DEVICE_TABLE(of, ens160_spi_of_match);
static const struct spi_device_id ens160_spi_id[] = {
- { "ens160" },
+ { .name = "ens160" },
{ }
};
MODULE_DEVICE_TABLE(spi, ens160_spi_id);
diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c
index c5425136814b..770571c21521 100644
--- a/drivers/iio/chemical/scd30_core.c
+++ b/drivers/iio/chemical/scd30_core.c
@@ -2,7 +2,7 @@
/*
* Sensirion SCD30 carbon dioxide sensor core driver
*
- * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
+ * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com>
*/
#include <linux/bitfield.h>
@@ -686,7 +686,7 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev)
IRQF_NO_AUTOEN,
indio_dev->name, indio_dev);
if (ret)
- return dev_err_probe(dev, ret, "failed to request irq\n");
+ return ret;
return 0;
}
@@ -711,7 +711,11 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT;
state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT;
state->command = command;
- mutex_init(&state->lock);
+
+ ret = devm_mutex_init(dev, &state->lock);
+ if (ret)
+ return ret;
+
init_completion(&state->meas_ready);
dev_set_drvdata(dev, indio_dev);
@@ -770,6 +774,6 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
}
EXPORT_SYMBOL_NS(scd30_probe, "IIO_SCD30");
-MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c
index abceccdddc71..bcef1e473e34 100644
--- a/drivers/iio/chemical/scd30_i2c.c
+++ b/drivers/iio/chemical/scd30_i2c.c
@@ -2,7 +2,7 @@
/*
* Sensirion SCD30 carbon dioxide sensor i2c driver
*
- * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
+ * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com>
*
* I2C slave address: 0x61
*/
@@ -136,7 +136,7 @@ static struct i2c_driver scd30_i2c_driver = {
};
module_i2c_driver(scd30_i2c_driver);
-MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_SCD30");
diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c
index 7fefdadbfa0a..31b0fc957b08 100644
--- a/drivers/iio/chemical/scd30_serial.c
+++ b/drivers/iio/chemical/scd30_serial.c
@@ -2,7 +2,7 @@
/*
* Sensirion SCD30 carbon dioxide sensor serial driver
*
- * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
+ * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com>
*/
#include <linux/crc16.h>
#include <linux/device.h>
@@ -257,7 +257,7 @@ static struct serdev_device_driver scd30_serdev_driver = {
};
module_serdev_device_driver(scd30_serdev_driver);
-MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_SCD30");
diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c
index f10bbebc29e4..379c1c4af8d8 100644
--- a/drivers/iio/chemical/sgp30.c
+++ b/drivers/iio/chemical/sgp30.c
@@ -548,6 +548,9 @@ static int sgp_probe(struct i2c_client *client)
data->iaq_thread = kthread_run(sgp_iaq_threadfn, data,
"%s-iaq", data->client->name);
+ if (IS_ERR(data->iaq_thread))
+ return dev_err_probe(dev, PTR_ERR(data->iaq_thread),
+ "failed to start IAQ thread\n");
return 0;
}
diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c
index 90f1adb8c89f..2d3a876cc06e 100644
--- a/drivers/iio/chemical/sps30_i2c.c
+++ b/drivers/iio/chemical/sps30_i2c.c
@@ -2,7 +2,7 @@
/*
* Sensirion SPS30 particulate matter sensor i2c driver
*
- * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
+ * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com>
*
* I2C slave address: 0x69
*/
@@ -252,7 +252,7 @@ static struct i2c_driver sps30_i2c_driver = {
};
module_i2c_driver(sps30_i2c_driver);
-MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_SPS30");
diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c
index 80eab9b2e4bf..0cdba8703e93 100644
--- a/drivers/iio/chemical/sps30_serial.c
+++ b/drivers/iio/chemical/sps30_serial.c
@@ -2,7 +2,7 @@
/*
* Sensirion SPS30 particulate matter sensor serial driver
*
- * Copyright (c) 2021 Tomasz Duszynski <tomasz.duszynski@octakon.com>
+ * Copyright (c) 2021 Tomasz Duszynski <tduszyns@gmail.com>
*/
#include <linux/completion.h>
#include <linux/device.h>
@@ -425,7 +425,7 @@ static struct serdev_device_driver sps30_serial_driver = {
};
module_serdev_device_driver(sps30_serial_driver);
-MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_SPS30");
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index c115a72832b2..9e7c26e37880 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -72,8 +72,7 @@ static const struct {
{HID_USAGE_SENSOR_HUMAN_ATTENTION, 0, 1, 0},
};
-static void simple_div(int dividend, int divisor, int *whole,
- int *micro_frac)
+static void simple_div(int dividend, int divisor, int *whole, int *micro_frac)
{
int rem;
int exp = 0;
@@ -111,7 +110,7 @@ for 10^-2.
Negative numbers are 2's complement
*/
static void convert_from_vtf_format(u32 value, int size, int exp,
- int *val1, int *val2)
+ int *val1, int *val2)
{
int sign = 1;
@@ -176,7 +175,7 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st)
EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, "IIO_HID_ATTRIBUTES");
int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
- int *val1, int *val2)
+ int *val1, int *val2)
{
s32 value;
int ret;
@@ -203,7 +202,7 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, "IIO_HID");
int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
- int val1, int val2)
+ int val1, int val2)
{
s32 value;
int ret;
@@ -238,15 +237,15 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, "IIO_HID");
int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,
- int *val1, int *val2)
+ int *val1, int *val2)
{
s32 value;
int ret;
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity.report_id,
- st->sensitivity.index, sizeof(value),
- &value);
+ st->sensitivity.index,
+ sizeof(value), &value);
if (ret < 0 || value < 0) {
*val1 = *val2 = 0;
return -EINVAL;
@@ -268,8 +267,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1,
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity_rel.report_id,
- st->sensitivity_rel.index, sizeof(value),
- &value);
+ st->sensitivity_rel.index,
+ sizeof(value), &value);
if (ret < 0 || value < 0) {
*val1 = *val2 = 0;
return -EINVAL;
@@ -282,9 +281,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1,
}
EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, "IIO_HID");
-
int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
- int val1, int val2)
+ int val1, int val2)
{
s32 value;
int ret;
@@ -293,8 +291,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
return -EINVAL;
value = convert_to_vtf_format(st->sensitivity.size,
- st->sensitivity.unit_expo,
- val1, val2);
+ st->sensitivity.unit_expo,
+ val1, val2);
ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id,
st->sensitivity.index, sizeof(value),
&value);
@@ -303,8 +301,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity.report_id,
- st->sensitivity.index, sizeof(value),
- &value);
+ st->sensitivity.index,
+ sizeof(value), &value);
if (ret < 0 || value < 0)
return -EINVAL;
@@ -324,8 +322,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st,
return -EINVAL;
value = convert_to_vtf_format(st->sensitivity_rel.size,
- st->sensitivity_rel.unit_expo,
- val1, val2);
+ st->sensitivity_rel.unit_expo,
+ val1, val2);
ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id,
st->sensitivity_rel.index, sizeof(value),
&value);
@@ -334,8 +332,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st,
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity_rel.report_id,
- st->sensitivity_rel.index, sizeof(value),
- &value);
+ st->sensitivity_rel.index,
+ sizeof(value), &value);
if (ret < 0 || value < 0)
return -EINVAL;
@@ -356,8 +354,8 @@ EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, "IIO_HID");
* 1.001745329 ->exp:4-> val0[10017]val1[453290000]
* 9.806650000 ->exp:-2-> val0[0]val1[98066500]
*/
-static void adjust_exponent_nano(int *val0, int *val1, int scale0,
- int scale1, int exp)
+static void adjust_exponent_nano(int *val0, int *val1,
+ int scale0, int scale1, int exp)
{
int divisor;
int i;
@@ -404,8 +402,8 @@ static void adjust_exponent_nano(int *val0, int *val1, int scale0,
}
int hid_sensor_format_scale(u32 usage_id,
- struct hid_sensor_hub_attribute_info *attr_info,
- int *val0, int *val1)
+ struct hid_sensor_hub_attribute_info *attr_info,
+ int *val0, int *val1)
{
int i;
int exp;
@@ -415,12 +413,11 @@ int hid_sensor_format_scale(u32 usage_id,
for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) {
if (unit_conversion[i].usage_id == usage_id &&
- unit_conversion[i].unit == attr_info->units) {
- exp = hid_sensor_convert_exponent(
- attr_info->unit_expo);
+ unit_conversion[i].unit == attr_info->units) {
+ exp = hid_sensor_convert_exponent(attr_info->unit_expo);
adjust_exponent_nano(val0, val1,
- unit_conversion[i].scale_val0,
- unit_conversion[i].scale_val1, exp);
+ unit_conversion[i].scale_val0,
+ unit_conversion[i].scale_val1, exp);
break;
}
}
@@ -438,8 +435,8 @@ EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, "IIO_HID");
static
int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,
- u32 usage_id,
- struct hid_sensor_common *st)
+ u32 usage_id,
+ struct hid_sensor_common *st)
{
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
@@ -473,9 +470,10 @@ int hid_sensor_get_report_latency(struct hid_sensor_common *st)
int ret;
int value;
- ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id,
- st->report_latency.index, sizeof(value),
- &value);
+ ret = sensor_hub_get_feature(st->hsdev,
+ st->report_latency.report_id,
+ st->report_latency.index,
+ sizeof(value), &value);
if (ret < 0)
return ret;
@@ -498,10 +496,10 @@ bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st)
EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, "IIO_HID_ATTRIBUTES");
int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
- u32 usage_id,
- struct hid_sensor_common *st,
- const u32 *sensitivity_addresses,
- u32 sensitivity_addresses_len)
+ u32 usage_id,
+ struct hid_sensor_common *st,
+ const u32 *sensitivity_addresses,
+ u32 sensitivity_addresses_len)
{
struct hid_sensor_hub_attribute_info timestamp;
@@ -527,7 +525,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
- &st->sensitivity);
+ &st->sensitivity);
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
@@ -578,8 +576,9 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
timestamp.index, timestamp.report_id);
ret = sensor_hub_get_feature(hsdev,
- st->power_state.report_id,
- st->power_state.index, sizeof(value), &value);
+ st->power_state.report_id,
+ st->power_state.index,
+ sizeof(value), &value);
if (ret < 0)
return ret;
if (value < 0)
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 417c4ab8c1b2..60808e2430ca 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -233,7 +233,7 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev,
EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, "IIO_HID");
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
- struct hid_sensor_common *attrb)
+ struct hid_sensor_common *attrb)
{
const struct iio_dev_attr **fifo_attrs;
int ret;
@@ -266,7 +266,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
trig = iio_trigger_alloc(indio_dev->dev.parent,
"%s-dev%d", name, iio_device_id(indio_dev));
- if (trig == NULL) {
+ if (!trig) {
dev_err(&indio_dev->dev, "Trigger Allocate Failed\n");
return -ENOMEM;
}
@@ -314,7 +314,9 @@ static int __maybe_unused hid_sensor_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
+
schedule_work(&attrb->work);
+
return 0;
}
@@ -322,6 +324,7 @@ static int __maybe_unused hid_sensor_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
+
return _hid_sensor_power_state(attrb, true);
}
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
index f94fca4f1edf..589de858e369 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
@@ -15,7 +15,7 @@ struct iio_dev;
extern const struct dev_pm_ops hid_sensor_pm_ops;
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
- struct hid_sensor_common *attrb);
+ struct hid_sensor_common *attrb);
void hid_sensor_remove_trigger(struct iio_dev *indio_dev,
struct hid_sensor_common *attrb);
int hid_sensor_power_state(struct hid_sensor_common *st, bool state);
diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
index e0b10366ed2b..88a82d1370c5 100644
--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
+++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
@@ -10,18 +10,19 @@
#include <linux/iio/common/inv_sensors_timestamp.h>
-/* compute jitter, min and max following jitter in per mille */
-#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \
- (div_s64((_val) * (_jitter), 1000))
+/* compute min and max following jitter in per mille */
#define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \
(((_val) * (1000 - (_jitter))) / 1000)
#define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \
(((_val) * (1000 + (_jitter))) / 1000)
+/* minimum timestamp delta between 2 interrupts for measuring period (20ms) */
+#define INV_SENSORS_MIN_IT_DELTA (20 * NSEC_PER_MSEC)
+
/* Add a new value inside an accumulator and update the estimate value */
-static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val)
+static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, u32 val)
{
- uint64_t sum = 0;
+ u64 sum = 0;
size_t i;
acc->values[acc->idx++] = val;
@@ -58,9 +59,9 @@ void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, "IIO_INV_SENSORS_TIMESTAMP");
int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
- uint32_t period, bool fifo)
+ u32 period, bool fifo)
{
- uint32_t mult;
+ u32 mult;
/* when FIFO is on, prevent odr change if one is already pending */
if (fifo && ts->new_mult != 0)
@@ -78,9 +79,9 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, "IIO_INV_SENSORS_TIMESTAMP");
-static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period)
+static bool inv_validate_period(struct inv_sensors_timestamp *ts, u32 period)
{
- uint32_t period_min, period_max;
+ u32 period_min, period_max;
/* check that period is acceptable */
period_min = ts->min_period * ts->mult;
@@ -92,9 +93,9 @@ static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t perio
}
static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
- uint32_t period)
+ u32 period)
{
- uint32_t new_chip_period;
+ u32 new_chip_period;
if (!inv_validate_period(ts, period))
return false;
@@ -102,78 +103,84 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
/* update chip internal period estimation */
new_chip_period = period / ts->mult;
inv_update_acc(&ts->chip_period, new_chip_period);
- ts->period = ts->mult * ts->chip_period.val;
return true;
}
-static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts)
+static u32 inv_align_timestamp_it(struct inv_sensors_timestamp *ts,
+ unsigned int sample_nb)
{
- const int64_t period_min = (int64_t)ts->min_period * ts->mult;
- const int64_t period_max = (int64_t)ts->max_period * ts->mult;
- int64_t add_max, sub_max;
- int64_t delta, jitter;
- int64_t adjust;
-
- /* delta time between last sample and last interrupt */
- delta = ts->it.lo - ts->timestamp;
-
- /* adjust timestamp while respecting jitter */
- add_max = period_max - (int64_t)ts->period;
- sub_max = period_min - (int64_t)ts->period;
- jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter);
- if (delta > jitter)
- adjust = add_max;
- else if (delta < -jitter)
- adjust = sub_max;
- else
- adjust = 0;
+ const s64 period_min = (s64)ts->min_period * ts->mult;
+ const s64 period_max = (s64)ts->max_period * ts->mult;
+ s64 new_period;
- ts->timestamp += adjust;
+ /* compute new period aligning last timestamp with interrupt timestamp */
+ new_period = div_s64(ts->it.up - ts->timestamp, sample_nb);
+
+ /* ensure that period never overflows the jitter */
+ return clamp(new_period, period_min, period_max);
}
void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
- size_t sample_nb, int64_t timestamp)
+ size_t sample_nb, s64 timestamp)
{
struct inv_sensors_timestamp_interval *it;
- int64_t delta, interval;
- uint32_t period;
+ s64 delta, delta_threshold, interval;
+ u32 period;
bool valid = false;
if (sample_nb == 0)
return;
+ /* no previous data, compute theoretical value from interrupt */
+ if (ts->timestamp == 0) {
+ /* elapsed time: sensor period * sensor samples number */
+ interval = (s64)ts->period * (s64)sample_nb;
+ ts->timestamp = timestamp - interval;
+ }
+
+ /* update delta timestamps and estimated period */
+ it = &ts->delta;
+ ts->delta_counter += sample_nb;
+ delta = timestamp - it->up;
+ delta_threshold = INV_SENSORS_TIMESTAMP_MIN(INV_SENSORS_MIN_IT_DELTA, ts->chip.jitter);
+ if (delta >= delta_threshold) {
+ it->lo = it->up;
+ it->up = timestamp;
+ if (it->lo != 0) {
+ /* compute period: delta time divided by number of samples */
+ delta = it->up - it->lo;
+ period = div_s64(delta, ts->delta_counter);
+ inv_update_chip_period(ts, period);
+ }
+ ts->delta_counter = 0;
+ }
+
/* update interrupt timestamp and compute chip and sensor periods */
it = &ts->it;
it->lo = it->up;
it->up = timestamp;
delta = it->up - it->lo;
if (it->lo != 0) {
- /* compute period: delta time divided by number of samples */
+ /* compute period and check validity */
period = div_s64(delta, sample_nb);
- valid = inv_update_chip_period(ts, period);
- }
-
- /* no previous data, compute theoretical value from interrupt */
- if (ts->timestamp == 0) {
- /* elapsed time: sensor period * sensor samples number */
- interval = (int64_t)ts->period * (int64_t)sample_nb;
- ts->timestamp = it->up - interval;
- return;
+ valid = inv_validate_period(ts, period);
}
/* if interrupt interval is valid, sync with interrupt timestamp */
if (valid)
- inv_align_timestamp_it(ts);
+ ts->period = inv_align_timestamp_it(ts, sample_nb);
+ else
+ ts->period = ts->mult * ts->chip_period.val;
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, "IIO_INV_SENSORS_TIMESTAMP");
void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
- uint32_t fifo_period, size_t fifo_nb,
+ u32 fifo_period, size_t fifo_nb,
unsigned int fifo_no)
{
- int64_t interval;
- uint32_t fifo_mult;
+ s64 interval;
+ u32 fifo_mult;
if (ts->new_mult == 0)
return;
@@ -194,7 +201,7 @@ void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
fifo_mult = fifo_period / ts->chip.clock_period;
fifo_period = fifo_mult * ts->chip_period.val;
/* computes time interval between interrupt and this sample */
- interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
+ interval = (s64)(fifo_nb - fifo_no) * (s64)fifo_period;
ts->timestamp = ts->it.up - interval;
}
}
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
index 8a8ab688d798..7b3e9732cc3e 100644
--- a/drivers/iio/common/st_sensors/st_sensors_trigger.c
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -211,10 +211,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
irq_trig,
sdata->trig->name,
sdata->trig);
- if (err) {
- dev_err(parent, "failed to request trigger IRQ.\n");
+ if (err)
return err;
- }
err = devm_iio_trigger_register(parent, sdata->trig);
if (err < 0) {
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 14a246729d2b..3b3c77d8b3e1 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -11,8 +11,11 @@ config AD3530R
depends on SPI
select REGMAP_SPI
help
- Say yes here to build support for Analog Devices AD3530R, AD3531R
- Digital to Analog Converter.
+ Say yes here to build support for the following Analog Devices
+ Digital to Analog Converters:
+ - AD3530/AD3530R (8-channel)
+ - AD3531/AD3531R (4-channel)
+ - AD3532/AD3532R (16-channel)
To compile this driver as a module, choose M here: the
module will be called ad3530r.
@@ -240,15 +243,26 @@ config LTC2688
config AD5686
tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
config AD5686_SPI
tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)"
depends on SPI
select AD5686
help
- Say yes here to build support for Analog Devices AD5672R, AD5674R,
- AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686,
- AD5686R Voltage Output Digital to Analog Converter.
+ Say yes here to build support for Analog Devices Voltage Output
+ Digital to Analog Converters:
+ - Single-channel:
+ AD5310R, AD5681R, AD5682R, AD5683, AD5683R
+ - Dual-channel:
+ AD5313R, AD5687, AD5687R, AD5689, AD5689R
+ - Quad-channel:
+ AD5317R, AD5684, AD5684R, AD5685R, AD5686, AD5686R
+ - 8-channel:
+ AD5672R, AD5676, AD5676R
+ - 16-channel:
+ AD5674, AD5674R, AD5679, AD5679R
To compile this driver as a module, choose M here: the
module will be called ad5686.
@@ -258,10 +272,18 @@ config AD5696_I2C
depends on I2C
select AD5686
help
- Say yes here to build support for Analog Devices AD5311R, AD5337,
- AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693,
- AD5693R, AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to
- Analog converters.
+ Say yes here to build support for Analog Devices Voltage Output
+ Digital to Analog Converters:
+ - Single-channel:
+ AD5311R, AD5691R, AD5692R, AD5693, AD5693R
+ - Dual-channel:
+ AD5338R, AD5697R
+ - Quad-channel:
+ AD5316R, AD5694, AD5694R, AD5695R, AD5696, AD5696R
+ - 8-channel:
+ AD5671R, AD5675, AD5675R
+ - 16-channel:
+ AD5673R, AD5677R
To compile this driver as a module, choose M here: the module will be
called ad5696.
@@ -527,6 +549,17 @@ config MAX5821
Say yes here to build support for Maxim MAX5821
10 bits DAC.
+config MCF54415_DAC
+ tristate "NXP MCF54415 DAC driver"
+ depends on M5441x || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ Say yes here if you want to build support for NXP ColdFire
+ MCF54415/6/7/8 12-bit DAC module.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcf54415_dac.
+
config MCP4725
tristate "MCP4725/6 DAC driver"
depends on I2C
@@ -549,6 +582,16 @@ config MCP4728
To compile this driver as a module, choose M here: the module
will be called mcp4728.
+config MCP47A1
+ tristate "MCP47A1 DAC driver"
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Microchip
+ MCP47A1 digital-to-analog converter with an I2C interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcp47a1.
+
config MCP47FEB02
tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver"
depends on I2C
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 003431798498..992f8930f95c 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -52,8 +52,10 @@ obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX22007) += max22007.o
obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
+obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
+obj-$(CONFIG_MCP47A1) += mcp47a1.o
obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o
obj-$(CONFIG_MCP4821) += mcp4821.o
obj-$(CONFIG_MCP4922) += mcp4922.o
diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c
index 4e911bfb6fc5..06b7f280f61b 100644
--- a/drivers/iio/dac/ad3530r.c
+++ b/drivers/iio/dac/ad3530r.c
@@ -2,6 +2,7 @@
/*
* AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver
* AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver
+ * AD3532R/AD3532 16-channel, 16-bit Voltage Output DAC Driver
*
* Copyright 2025 Analog Devices Inc.
*/
@@ -38,6 +39,25 @@
#define AD3531R_SW_LDAC_TRIG_A 0xDD
#define AD3531R_INPUT_CH 0xE3
+/* AD3532R/AD3532 bank 0 registers (channels 0-7) */
+#define AD3532R_INTERFACE_CONFIG_A_0 0x1000
+#define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020
+#define AD3532R_OUTPUT_OPERATING_MODE_1 0x1021
+#define AD3532R_OUTPUT_CONTROL_0 0x102A
+#define AD3532R_REFERENCE_CONTROL_0 0x103C
+#define AD3532R_SW_LDAC_TRIG_0 0x10E5
+#define AD3532R_INPUT_CH_0 0x10EB
+
+/* AD3532R/AD3532 bank 1 registers (channels 8-15) */
+#define AD3532R_INTERFACE_CONFIG_A_1 0x3000
+#define AD3532R_OUTPUT_OPERATING_MODE_2 0x3020
+#define AD3532R_OUTPUT_OPERATING_MODE_3 0x3021
+#define AD3532R_OUTPUT_CONTROL_1 0x302A
+#define AD3532R_REFERENCE_CONTROL_1 0x303C
+#define AD3532R_SW_LDAC_TRIG_1 0x30E5
+#define AD3532R_INPUT_CH_1 0x30EB
+#define AD3532R_MAX_REG_ADDR 0x30F9
+
#define AD3530R_SLD_TRIG_A BIT(7)
#define AD3530R_OUTPUT_CONTROL_RANGE BIT(2)
#define AD3530R_REFERENCE_CONTROL_SEL BIT(0)
@@ -49,8 +69,10 @@
#define AD3530R_LDAC_PULSE_US 100
#define AD3530R_DAC_MAX_VAL GENMASK(15, 0)
-#define AD3530R_MAX_CHANNELS 8
+#define AD3530R_CH_PER_REG 4
+#define AD3530R_CH_PER_BANK 8
#define AD3531R_MAX_CHANNELS 4
+#define AD3532R_MAX_CHANNELS 16
enum ad3530r_mode {
AD3530R_NORMAL_OP,
@@ -67,9 +89,16 @@ struct ad3530r_chan {
struct ad3530r_chip_info {
const char *name;
const struct iio_chan_spec *channels;
+ const struct regmap_config *regmap_config;
int (*input_ch_reg)(unsigned int channel);
+ int (*sw_ldac_trig_reg)(unsigned int channel);
+ const unsigned int *interface_config_a;
+ const unsigned int *output_control;
+ const unsigned int *reference_control;
+ const unsigned int *op_mode;
unsigned int num_channels;
- unsigned int sw_ldac_trig_reg;
+ unsigned int num_banks;
+ unsigned int num_op_mode_regs;
bool internal_ref_support;
};
@@ -77,7 +106,7 @@ struct ad3530r_state {
struct regmap *regmap;
/* lock to protect against multiple access to the device and shared data */
struct mutex lock;
- struct ad3530r_chan chan[AD3530R_MAX_CHANNELS];
+ struct ad3530r_chan chan[AD3532R_MAX_CHANNELS];
const struct ad3530r_chip_info *chip_info;
struct gpio_desc *ldac_gpio;
int vref_mV;
@@ -98,6 +127,14 @@ static int ad3531r_input_ch_reg(unsigned int channel)
return 2 * channel + AD3531R_INPUT_CH;
}
+static int ad3532r_input_ch_reg(unsigned int channel)
+{
+ unsigned int bank = channel / AD3530R_CH_PER_BANK;
+ unsigned int local_ch = channel % AD3530R_CH_PER_BANK;
+
+ return 2 * local_ch + (bank ? AD3532R_INPUT_CH_1 : AD3532R_INPUT_CH_0);
+}
+
static const char * const ad3530r_powerdown_modes[] = {
"1kohm_to_gnd",
"7.7kohm_to_gnd",
@@ -110,6 +147,12 @@ static const char * const ad3531r_powerdown_modes[] = {
"16kohm_to_gnd",
};
+static const char * const ad3532r_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "10kohm_to_gnd",
+ "three_state",
+};
+
static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@@ -145,6 +188,13 @@ static const struct iio_enum ad3531r_powerdown_mode_enum = {
.set = ad3530r_set_powerdown_mode,
};
+static const struct iio_enum ad3532r_powerdown_mode_enum = {
+ .items = ad3532r_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad3532r_powerdown_modes),
+ .get = ad3530r_get_powerdown_mode,
+ .set = ad3530r_set_powerdown_mode,
+};
+
static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
@@ -189,6 +239,62 @@ static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev,
return len;
}
+static ssize_t ad3532r_set_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad3530r_state *st = iio_priv(indio_dev);
+ unsigned int bank, local_ch, reg_in_bank, ch_in_reg;
+ unsigned int reg, mask, val;
+ bool powerdown;
+ int ret;
+
+ ret = kstrtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ bank = chan->channel / AD3530R_CH_PER_BANK;
+ local_ch = chan->channel % AD3530R_CH_PER_BANK;
+ reg_in_bank = local_ch / AD3530R_CH_PER_REG;
+ ch_in_reg = local_ch % AD3530R_CH_PER_REG;
+
+ reg = reg_in_bank + (bank ? AD3532R_OUTPUT_OPERATING_MODE_2 :
+ AD3532R_OUTPUT_OPERATING_MODE_0);
+ mask = AD3530R_OP_MODE_CHAN_MSK(ch_in_reg);
+
+ guard(mutex)(&st->lock);
+ if (powerdown) {
+ val = field_prep(mask, st->chan[chan->channel].powerdown_mode);
+ ret = regmap_update_bits(st->regmap, reg, mask, val);
+ } else {
+ ret = regmap_clear_bits(st->regmap, reg, mask);
+ }
+ if (ret)
+ return ret;
+
+ st->chan[chan->channel].powerdown = powerdown;
+
+ return len;
+}
+
+static int ad3530r_trigger_sw_ldac_reg(unsigned int channel)
+{
+ return AD3530R_SW_LDAC_TRIG_A;
+}
+
+static int ad3531r_trigger_sw_ldac_reg(unsigned int channel)
+{
+ return AD3531R_SW_LDAC_TRIG_A;
+}
+
+static int ad3532r_trigger_sw_ldac_reg(unsigned int channel)
+{
+ unsigned int bank = channel / AD3530R_CH_PER_BANK;
+
+ return bank ? AD3532R_SW_LDAC_TRIG_1 : AD3532R_SW_LDAC_TRIG_0;
+}
+
static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio)
{
gpiod_set_value_cansleep(ldac_gpio, 1);
@@ -214,7 +320,7 @@ static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan,
if (st->ldac_gpio)
return ad3530r_trigger_hw_ldac(st->ldac_gpio);
- return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg,
+ return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg(chan),
AD3530R_SLD_TRIG_A);
}
@@ -301,6 +407,19 @@ static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = {
{ }
};
+static const struct iio_chan_spec_ext_info ad3532r_ext_info[] = {
+ {
+ .name = "powerdown",
+ .shared = IIO_SEPARATE,
+ .read = ad3530r_get_dac_powerdown,
+ .write = ad3532r_set_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3532r_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE,
+ &ad3532r_powerdown_mode_enum),
+ { }
+};
+
#define AD3530R_CHAN(_chan, _ext_info) \
{ \
.type = IIO_VOLTAGE, \
@@ -330,48 +449,215 @@ static const struct iio_chan_spec ad3531r_channels[] = {
AD3530R_CHAN(3, ad3531r_ext_info),
};
+static const struct iio_chan_spec ad3532r_channels[] = {
+ AD3530R_CHAN(0, ad3532r_ext_info),
+ AD3530R_CHAN(1, ad3532r_ext_info),
+ AD3530R_CHAN(2, ad3532r_ext_info),
+ AD3530R_CHAN(3, ad3532r_ext_info),
+ AD3530R_CHAN(4, ad3532r_ext_info),
+ AD3530R_CHAN(5, ad3532r_ext_info),
+ AD3530R_CHAN(6, ad3532r_ext_info),
+ AD3530R_CHAN(7, ad3532r_ext_info),
+ AD3530R_CHAN(8, ad3532r_ext_info),
+ AD3530R_CHAN(9, ad3532r_ext_info),
+ AD3530R_CHAN(10, ad3532r_ext_info),
+ AD3530R_CHAN(11, ad3532r_ext_info),
+ AD3530R_CHAN(12, ad3532r_ext_info),
+ AD3530R_CHAN(13, ad3532r_ext_info),
+ AD3530R_CHAN(14, ad3532r_ext_info),
+ AD3530R_CHAN(15, ad3532r_ext_info),
+};
+
+static const unsigned int ad3530r_if_config[] = {
+ AD3530R_INTERFACE_CONFIG_A,
+};
+
+static const unsigned int ad3530r_out_ctrl[] = {
+ AD3530R_OUTPUT_CONTROL_0,
+};
+
+static const unsigned int ad3530r_ref_ctrl[] = {
+ AD3530R_REFERENCE_CONTROL_0,
+};
+
+static const unsigned int ad3530r_op_mode[] = {
+ AD3530R_OUTPUT_OPERATING_MODE_0,
+ AD3530R_OUTPUT_OPERATING_MODE_1,
+};
+
+static const unsigned int ad3531r_op_mode[] = {
+ AD3530R_OUTPUT_OPERATING_MODE_0,
+};
+
+static const unsigned int ad3532r_if_config[] = {
+ AD3532R_INTERFACE_CONFIG_A_0,
+ AD3532R_INTERFACE_CONFIG_A_1,
+};
+
+static const unsigned int ad3532r_out_ctrl[] = {
+ AD3532R_OUTPUT_CONTROL_0,
+ AD3532R_OUTPUT_CONTROL_1,
+};
+
+static const unsigned int ad3532r_ref_ctrl[] = {
+ AD3532R_REFERENCE_CONTROL_0,
+ AD3532R_REFERENCE_CONTROL_1,
+};
+
+static const unsigned int ad3532r_op_mode[] = {
+ AD3532R_OUTPUT_OPERATING_MODE_0,
+ AD3532R_OUTPUT_OPERATING_MODE_1,
+ AD3532R_OUTPUT_OPERATING_MODE_2,
+ AD3532R_OUTPUT_OPERATING_MODE_3,
+};
+
+static const struct regmap_config ad3530r_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = AD3530R_MAX_REG_ADDR,
+};
+
+static const struct regmap_config ad3532r_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = AD3532R_MAX_REG_ADDR,
+};
+
static const struct ad3530r_chip_info ad3530_chip = {
.name = "ad3530",
.channels = ad3530r_channels,
+ .regmap_config = &ad3530r_regmap_config,
.num_channels = ARRAY_SIZE(ad3530r_channels),
- .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A,
+ .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg,
.input_ch_reg = ad3530r_input_ch_reg,
+ .interface_config_a = ad3530r_if_config,
+ .output_control = ad3530r_out_ctrl,
+ .reference_control = ad3530r_ref_ctrl,
+ .op_mode = ad3530r_op_mode,
+ .num_banks = ARRAY_SIZE(ad3530r_if_config),
+ .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode),
.internal_ref_support = false,
};
static const struct ad3530r_chip_info ad3530r_chip = {
.name = "ad3530r",
.channels = ad3530r_channels,
+ .regmap_config = &ad3530r_regmap_config,
.num_channels = ARRAY_SIZE(ad3530r_channels),
- .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A,
+ .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg,
.input_ch_reg = ad3530r_input_ch_reg,
+ .interface_config_a = ad3530r_if_config,
+ .output_control = ad3530r_out_ctrl,
+ .reference_control = ad3530r_ref_ctrl,
+ .op_mode = ad3530r_op_mode,
+ .num_banks = ARRAY_SIZE(ad3530r_if_config),
+ .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode),
.internal_ref_support = true,
};
static const struct ad3530r_chip_info ad3531_chip = {
.name = "ad3531",
.channels = ad3531r_channels,
+ .regmap_config = &ad3530r_regmap_config,
.num_channels = ARRAY_SIZE(ad3531r_channels),
- .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A,
+ .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg,
.input_ch_reg = ad3531r_input_ch_reg,
+ .interface_config_a = ad3530r_if_config,
+ .output_control = ad3530r_out_ctrl,
+ .reference_control = ad3530r_ref_ctrl,
+ .op_mode = ad3531r_op_mode,
+ .num_banks = ARRAY_SIZE(ad3530r_if_config),
+ .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode),
.internal_ref_support = false,
};
static const struct ad3530r_chip_info ad3531r_chip = {
.name = "ad3531r",
.channels = ad3531r_channels,
+ .regmap_config = &ad3530r_regmap_config,
.num_channels = ARRAY_SIZE(ad3531r_channels),
- .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A,
+ .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg,
.input_ch_reg = ad3531r_input_ch_reg,
+ .interface_config_a = ad3530r_if_config,
+ .output_control = ad3530r_out_ctrl,
+ .reference_control = ad3530r_ref_ctrl,
+ .op_mode = ad3531r_op_mode,
+ .num_banks = ARRAY_SIZE(ad3530r_if_config),
+ .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode),
+ .internal_ref_support = true,
+};
+
+static const struct ad3530r_chip_info ad3532_chip = {
+ .name = "ad3532",
+ .channels = ad3532r_channels,
+ .regmap_config = &ad3532r_regmap_config,
+ .num_channels = ARRAY_SIZE(ad3532r_channels),
+ .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg,
+ .input_ch_reg = ad3532r_input_ch_reg,
+ .interface_config_a = ad3532r_if_config,
+ .output_control = ad3532r_out_ctrl,
+ .reference_control = ad3532r_ref_ctrl,
+ .op_mode = ad3532r_op_mode,
+ .num_banks = ARRAY_SIZE(ad3532r_if_config),
+ .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode),
+ .internal_ref_support = false,
+};
+
+static const struct ad3530r_chip_info ad3532r_chip = {
+ .name = "ad3532r",
+ .channels = ad3532r_channels,
+ .regmap_config = &ad3532r_regmap_config,
+ .num_channels = ARRAY_SIZE(ad3532r_channels),
+ .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg,
+ .input_ch_reg = ad3532r_input_ch_reg,
+ .interface_config_a = ad3532r_if_config,
+ .output_control = ad3532r_out_ctrl,
+ .reference_control = ad3532r_ref_ctrl,
+ .op_mode = ad3532r_op_mode,
+ .num_banks = ARRAY_SIZE(ad3532r_if_config),
+ .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode),
.internal_ref_support = true,
};
+static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st,
+ const unsigned int *regs,
+ unsigned int num_regs,
+ unsigned int mask)
+{
+ int ret;
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ ret = regmap_set_bits(st->regmap, regs[i], mask);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad3530r_write_reg_banks(const struct ad3530r_state *st,
+ const unsigned int *regs,
+ unsigned int num_regs,
+ unsigned int val)
+{
+ int ret;
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ ret = regmap_write(st->regmap, regs[i], val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV)
{
+ const struct ad3530r_chip_info *chip_info = st->chip_info;
struct device *dev = regmap_get_device(st->regmap);
struct gpio_desc *reset_gpio;
- int i, ret;
u8 range_multiplier, val;
+ int ret;
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio))
@@ -384,8 +670,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV)
gpiod_set_value_cansleep(reset_gpio, 0);
} else {
/* Perform software reset */
- ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A,
- AD3530R_SW_RESET, AD3530R_SW_RESET);
+ ret = ad3530r_set_reg_bank_bits(st, chip_info->interface_config_a,
+ chip_info->num_banks,
+ AD3530R_SW_RESET);
if (ret)
return ret;
}
@@ -394,8 +681,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV)
range_multiplier = 1;
if (device_property_read_bool(dev, "adi,range-double")) {
- ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0,
- AD3530R_OUTPUT_CONTROL_RANGE);
+ ret = ad3530r_set_reg_bank_bits(st, chip_info->output_control,
+ chip_info->num_banks,
+ AD3530R_OUTPUT_CONTROL_RANGE);
if (ret)
return ret;
@@ -405,8 +693,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV)
if (external_vref_uV) {
st->vref_mV = range_multiplier * external_vref_uV / MILLI;
} else {
- ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0,
- AD3530R_REFERENCE_CONTROL_SEL);
+ ret = ad3530r_set_reg_bank_bits(st, chip_info->reference_control,
+ chip_info->num_banks,
+ AD3530R_REFERENCE_CONTROL_SEL);
if (ret)
return ret;
@@ -419,18 +708,12 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV)
FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) |
FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP);
- ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val);
+ ret = ad3530r_write_reg_banks(st, st->chip_info->op_mode,
+ st->chip_info->num_op_mode_regs, val);
if (ret)
return ret;
- if (st->chip_info->num_channels > 4) {
- ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1,
- val);
- if (ret)
- return ret;
- }
-
- for (i = 0; i < st->chip_info->num_channels; i++)
+ for (unsigned int i = 0; i < st->chip_info->num_channels; i++)
st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K;
st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW);
@@ -441,12 +724,6 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV)
return 0;
}
-static const struct regmap_config ad3530r_regmap_config = {
- .reg_bits = 16,
- .val_bits = 8,
- .max_register = AD3530R_MAX_REG_ADDR,
-};
-
static const struct iio_info ad3530r_info = {
.read_raw = ad3530r_read_raw,
.write_raw = ad3530r_write_raw,
@@ -467,7 +744,11 @@ static int ad3530r_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
- st->regmap = devm_regmap_init_spi(spi, &ad3530r_regmap_config);
+ st->chip_info = spi_get_device_match_data(spi);
+ if (!st->chip_info)
+ return -ENODEV;
+
+ st->regmap = devm_regmap_init_spi(spi, st->chip_info->regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(dev, PTR_ERR(st->regmap),
"Failed to init regmap");
@@ -476,10 +757,6 @@ static int ad3530r_probe(struct spi_device *spi)
if (ret)
return ret;
- st->chip_info = spi_get_device_match_data(spi);
- if (!st->chip_info)
- return -ENODEV;
-
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators),
regulators);
if (ret)
@@ -509,10 +786,12 @@ static int ad3530r_probe(struct spi_device *spi)
}
static const struct spi_device_id ad3530r_id[] = {
- { "ad3530", (kernel_ulong_t)&ad3530_chip },
- { "ad3530r", (kernel_ulong_t)&ad3530r_chip },
- { "ad3531", (kernel_ulong_t)&ad3531_chip },
- { "ad3531r", (kernel_ulong_t)&ad3531r_chip },
+ { .name = "ad3530", .driver_data = (kernel_ulong_t)&ad3530_chip },
+ { .name = "ad3530r", .driver_data = (kernel_ulong_t)&ad3530r_chip },
+ { .name = "ad3531", .driver_data = (kernel_ulong_t)&ad3531_chip },
+ { .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip },
+ { .name = "ad3532", .driver_data = (kernel_ulong_t)&ad3532_chip },
+ { .name = "ad3532r", .driver_data = (kernel_ulong_t)&ad3532r_chip },
{ }
};
MODULE_DEVICE_TABLE(spi, ad3530r_id);
@@ -522,6 +801,8 @@ static const struct of_device_id ad3530r_of_match[] = {
{ .compatible = "adi,ad3530r", .data = &ad3530r_chip },
{ .compatible = "adi,ad3531", .data = &ad3531_chip },
{ .compatible = "adi,ad3531r", .data = &ad3531r_chip },
+ { .compatible = "adi,ad3532", .data = &ad3532_chip },
+ { .compatible = "adi,ad3532r", .data = &ad3532r_chip },
{ }
};
MODULE_DEVICE_TABLE(of, ad3530r_of_match);
diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c
index b5ec33f5c317..298f3a703571 100644
--- a/drivers/iio/dac/ad5064.c
+++ b/drivers/iio/dac/ad5064.c
@@ -920,22 +920,22 @@ static int ad5064_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5064_spi_ids[] = {
- {"ad5024", ID_AD5024},
- {"ad5025", ID_AD5025},
- {"ad5044", ID_AD5044},
- {"ad5045", ID_AD5045},
- {"ad5064", ID_AD5064},
- {"ad5064-1", ID_AD5064_1},
- {"ad5065", ID_AD5065},
- {"ad5628-1", ID_AD5628_1},
- {"ad5628-2", ID_AD5628_2},
- {"ad5648-1", ID_AD5648_1},
- {"ad5648-2", ID_AD5648_2},
- {"ad5666-1", ID_AD5666_1},
- {"ad5666-2", ID_AD5666_2},
- {"ad5668-1", ID_AD5668_1},
- {"ad5668-2", ID_AD5668_2},
- {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
+ { .name = "ad5024", .driver_data = ID_AD5024 },
+ { .name = "ad5025", .driver_data = ID_AD5025 },
+ { .name = "ad5044", .driver_data = ID_AD5044 },
+ { .name = "ad5045", .driver_data = ID_AD5045 },
+ { .name = "ad5064", .driver_data = ID_AD5064 },
+ { .name = "ad5064-1", .driver_data = ID_AD5064_1 },
+ { .name = "ad5065", .driver_data = ID_AD5065 },
+ { .name = "ad5628-1", .driver_data = ID_AD5628_1 },
+ { .name = "ad5628-2", .driver_data = ID_AD5628_2 },
+ { .name = "ad5648-1", .driver_data = ID_AD5648_1 },
+ { .name = "ad5648-2", .driver_data = ID_AD5648_2 },
+ { .name = "ad5666-1", .driver_data = ID_AD5666_1 },
+ { .name = "ad5666-2", .driver_data = ID_AD5666_2 },
+ { .name = "ad5668-1", .driver_data = ID_AD5668_1 },
+ { .name = "ad5668-2", .driver_data = ID_AD5668_2 },
+ { .name = "ad5668-3", .driver_data = ID_AD5668_2 }, /* similar enough to ad5668-2 */
{ }
};
MODULE_DEVICE_TABLE(spi, ad5064_spi_ids);
diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c
index 20316fd568e6..c1553f18a26a 100644
--- a/drivers/iio/dac/ad5360.c
+++ b/drivers/iio/dac/ad5360.c
@@ -523,14 +523,14 @@ static void ad5360_remove(struct spi_device *spi)
}
static const struct spi_device_id ad5360_ids[] = {
- { "ad5360", ID_AD5360 },
- { "ad5361", ID_AD5361 },
- { "ad5362", ID_AD5362 },
- { "ad5363", ID_AD5363 },
- { "ad5370", ID_AD5370 },
- { "ad5371", ID_AD5371 },
- { "ad5372", ID_AD5372 },
- { "ad5373", ID_AD5373 },
+ { .name = "ad5360", .driver_data = ID_AD5360 },
+ { .name = "ad5361", .driver_data = ID_AD5361 },
+ { .name = "ad5362", .driver_data = ID_AD5362 },
+ { .name = "ad5363", .driver_data = ID_AD5363 },
+ { .name = "ad5370", .driver_data = ID_AD5370 },
+ { .name = "ad5371", .driver_data = ID_AD5371 },
+ { .name = "ad5372", .driver_data = ID_AD5372 },
+ { .name = "ad5373", .driver_data = ID_AD5373 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5360_ids);
diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c
index 2e587bdd3214..4a5ad419f4ea 100644
--- a/drivers/iio/dac/ad5380.c
+++ b/drivers/iio/dac/ad5380.c
@@ -446,22 +446,22 @@ static int ad5380_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5380_spi_ids[] = {
- { "ad5380-3", ID_AD5380_3 },
- { "ad5380-5", ID_AD5380_5 },
- { "ad5381-3", ID_AD5381_3 },
- { "ad5381-5", ID_AD5381_5 },
- { "ad5382-3", ID_AD5382_3 },
- { "ad5382-5", ID_AD5382_5 },
- { "ad5383-3", ID_AD5383_3 },
- { "ad5383-5", ID_AD5383_5 },
- { "ad5384-3", ID_AD5380_3 },
- { "ad5384-5", ID_AD5380_5 },
- { "ad5390-3", ID_AD5390_3 },
- { "ad5390-5", ID_AD5390_5 },
- { "ad5391-3", ID_AD5391_3 },
- { "ad5391-5", ID_AD5391_5 },
- { "ad5392-3", ID_AD5392_3 },
- { "ad5392-5", ID_AD5392_5 },
+ { .name = "ad5380-3", .driver_data = ID_AD5380_3 },
+ { .name = "ad5380-5", .driver_data = ID_AD5380_5 },
+ { .name = "ad5381-3", .driver_data = ID_AD5381_3 },
+ { .name = "ad5381-5", .driver_data = ID_AD5381_5 },
+ { .name = "ad5382-3", .driver_data = ID_AD5382_3 },
+ { .name = "ad5382-5", .driver_data = ID_AD5382_5 },
+ { .name = "ad5383-3", .driver_data = ID_AD5383_3 },
+ { .name = "ad5383-5", .driver_data = ID_AD5383_5 },
+ { .name = "ad5384-3", .driver_data = ID_AD5380_3 },
+ { .name = "ad5384-5", .driver_data = ID_AD5380_5 },
+ { .name = "ad5390-3", .driver_data = ID_AD5390_3 },
+ { .name = "ad5390-5", .driver_data = ID_AD5390_5 },
+ { .name = "ad5391-3", .driver_data = ID_AD5391_3 },
+ { .name = "ad5391-5", .driver_data = ID_AD5391_5 },
+ { .name = "ad5392-3", .driver_data = ID_AD5392_3 },
+ { .name = "ad5392-5", .driver_data = ID_AD5392_5 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5380_spi_ids);
diff --git a/drivers/iio/dac/ad5446-spi.c b/drivers/iio/dac/ad5446-spi.c
index 54bd0e113f40..85bb8829046a 100644
--- a/drivers/iio/dac/ad5446-spi.c
+++ b/drivers/iio/dac/ad5446-spi.c
@@ -163,38 +163,38 @@ static const struct ad5446_chip_info ad5662_chip_info = {
};
static const struct spi_device_id ad5446_spi_ids[] = {
- {"ad5300", (kernel_ulong_t)&ad5300_chip_info},
- {"ad5310", (kernel_ulong_t)&ad5310_chip_info},
- {"ad5320", (kernel_ulong_t)&ad5320_chip_info},
- {"ad5444", (kernel_ulong_t)&ad5444_chip_info},
- {"ad5446", (kernel_ulong_t)&ad5446_chip_info},
- {"ad5450", (kernel_ulong_t)&ad5450_chip_info},
- {"ad5451", (kernel_ulong_t)&ad5451_chip_info},
- {"ad5452", (kernel_ulong_t)&ad5444_chip_info}, /* ad5452 is compatible to the ad5444 */
- {"ad5453", (kernel_ulong_t)&ad5446_chip_info}, /* ad5453 is compatible to the ad5446 */
- {"ad5512a", (kernel_ulong_t)&ad5512a_chip_info},
- {"ad5541a", (kernel_ulong_t)&ad5541a_chip_info},
- {"ad5542", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542 are compatible */
- {"ad5542a", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542a are compatible */
- {"ad5543", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5543 are compatible */
- {"ad5553", (kernel_ulong_t)&ad5553_chip_info},
- {"ad5600", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5600 are compatible */
- {"ad5601", (kernel_ulong_t)&ad5601_chip_info},
- {"ad5611", (kernel_ulong_t)&ad5611_chip_info},
- {"ad5621", (kernel_ulong_t)&ad5621_chip_info},
- {"ad5641", (kernel_ulong_t)&ad5641_chip_info},
- {"ad5620-2500", (kernel_ulong_t)&ad5620_2500_chip_info}, /* AD5620/40/60: */
+ { .name = "ad5300", .driver_data = (kernel_ulong_t)&ad5300_chip_info },
+ { .name = "ad5310", .driver_data = (kernel_ulong_t)&ad5310_chip_info },
+ { .name = "ad5320", .driver_data = (kernel_ulong_t)&ad5320_chip_info },
+ { .name = "ad5444", .driver_data = (kernel_ulong_t)&ad5444_chip_info },
+ { .name = "ad5446", .driver_data = (kernel_ulong_t)&ad5446_chip_info },
+ { .name = "ad5450", .driver_data = (kernel_ulong_t)&ad5450_chip_info },
+ { .name = "ad5451", .driver_data = (kernel_ulong_t)&ad5451_chip_info },
+ { .name = "ad5452", .driver_data = (kernel_ulong_t)&ad5444_chip_info }, /* ad5452 is compatible to the ad5444 */
+ { .name = "ad5453", .driver_data = (kernel_ulong_t)&ad5446_chip_info }, /* ad5453 is compatible to the ad5446 */
+ { .name = "ad5512a", .driver_data = (kernel_ulong_t)&ad5512a_chip_info },
+ { .name = "ad5541a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info },
+ { .name = "ad5542", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542 are compatible */
+ { .name = "ad5542a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542a are compatible */
+ { .name = "ad5543", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5543 are compatible */
+ { .name = "ad5553", .driver_data = (kernel_ulong_t)&ad5553_chip_info },
+ { .name = "ad5600", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5600 are compatible */
+ { .name = "ad5601", .driver_data = (kernel_ulong_t)&ad5601_chip_info },
+ { .name = "ad5611", .driver_data = (kernel_ulong_t)&ad5611_chip_info },
+ { .name = "ad5621", .driver_data = (kernel_ulong_t)&ad5621_chip_info },
+ { .name = "ad5641", .driver_data = (kernel_ulong_t)&ad5641_chip_info },
+ { .name = "ad5620-2500", .driver_data = (kernel_ulong_t)&ad5620_2500_chip_info }, /* AD5620/40/60: */
/* part numbers may look differently */
- {"ad5620-1250", (kernel_ulong_t)&ad5620_1250_chip_info},
- {"ad5640-2500", (kernel_ulong_t)&ad5640_2500_chip_info},
- {"ad5640-1250", (kernel_ulong_t)&ad5640_1250_chip_info},
- {"ad5660-2500", (kernel_ulong_t)&ad5660_2500_chip_info},
- {"ad5660-1250", (kernel_ulong_t)&ad5660_1250_chip_info},
- {"ad5662", (kernel_ulong_t)&ad5662_chip_info},
- {"dac081s101", (kernel_ulong_t)&ad5300_chip_info}, /* compatible Texas Instruments chips */
- {"dac101s101", (kernel_ulong_t)&ad5310_chip_info},
- {"dac121s101", (kernel_ulong_t)&ad5320_chip_info},
- {"dac7512", (kernel_ulong_t)&ad5320_chip_info},
+ { .name = "ad5620-1250", .driver_data = (kernel_ulong_t)&ad5620_1250_chip_info },
+ { .name = "ad5640-2500", .driver_data = (kernel_ulong_t)&ad5640_2500_chip_info },
+ { .name = "ad5640-1250", .driver_data = (kernel_ulong_t)&ad5640_1250_chip_info },
+ { .name = "ad5660-2500", .driver_data = (kernel_ulong_t)&ad5660_2500_chip_info },
+ { .name = "ad5660-1250", .driver_data = (kernel_ulong_t)&ad5660_1250_chip_info },
+ { .name = "ad5662", .driver_data = (kernel_ulong_t)&ad5662_chip_info },
+ { .name = "dac081s101", .driver_data = (kernel_ulong_t)&ad5300_chip_info }, /* compatible Texas Instruments chips */
+ { .name = "dac101s101", .driver_data = (kernel_ulong_t)&ad5310_chip_info },
+ { .name = "dac121s101", .driver_data = (kernel_ulong_t)&ad5320_chip_info },
+ { .name = "dac7512", .driver_data = (kernel_ulong_t)&ad5320_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c
index d8c325260259..19e66bc69676 100644
--- a/drivers/iio/dac/ad5449.c
+++ b/drivers/iio/dac/ad5449.c
@@ -330,13 +330,13 @@ static void ad5449_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id ad5449_spi_ids[] = {
- { "ad5415", ID_AD5449 },
- { "ad5426", ID_AD5426 },
- { "ad5429", ID_AD5429 },
- { "ad5432", ID_AD5432 },
- { "ad5439", ID_AD5439 },
- { "ad5443", ID_AD5443 },
- { "ad5449", ID_AD5449 },
+ { .name = "ad5415", .driver_data = ID_AD5449 },
+ { .name = "ad5426", .driver_data = ID_AD5426 },
+ { .name = "ad5429", .driver_data = ID_AD5429 },
+ { .name = "ad5432", .driver_data = ID_AD5432 },
+ { .name = "ad5439", .driver_data = ID_AD5439 },
+ { .name = "ad5443", .driver_data = ID_AD5443 },
+ { .name = "ad5449", .driver_data = ID_AD5449 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5449_spi_ids);
diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c
index 5e586185d857..ad1a74678cf2 100644
--- a/drivers/iio/dac/ad5504.c
+++ b/drivers/iio/dac/ad5504.c
@@ -319,8 +319,8 @@ static int ad5504_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5504_id[] = {
- {"ad5504", ID_AD5504},
- {"ad5501", ID_AD5501},
+ { .name = "ad5504", .driver_data = ID_AD5504 },
+ { .name = "ad5501", .driver_data = ID_AD5501 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5504_id);
diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c
index 13aefe769bad..56aea4b1c167 100644
--- a/drivers/iio/dac/ad5624r_spi.c
+++ b/drivers/iio/dac/ad5624r_spi.c
@@ -260,12 +260,12 @@ static int ad5624r_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5624r_id[] = {
- {"ad5624r3", ID_AD5624R3},
- {"ad5644r3", ID_AD5644R3},
- {"ad5664r3", ID_AD5664R3},
- {"ad5624r5", ID_AD5624R5},
- {"ad5644r5", ID_AD5644R5},
- {"ad5664r5", ID_AD5664R5},
+ { .name = "ad5624r3", .driver_data = ID_AD5624R3 },
+ { .name = "ad5644r3", .driver_data = ID_AD5644R3 },
+ { .name = "ad5664r3", .driver_data = ID_AD5664R3 },
+ { .name = "ad5624r5", .driver_data = ID_AD5624R5 },
+ { .name = "ad5644r5", .driver_data = ID_AD5644R5 },
+ { .name = "ad5664r5", .driver_data = ID_AD5664R5 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5624r_id);
diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
index 8abfaf8f0c46..69cd9753f339 100644
--- a/drivers/iio/dac/ad5686-spi.c
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -1,69 +1,109 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * AD5672R, AD5674R, AD5676, AD5676R, AD5679R,
- * AD5681R, AD5682R, AD5683, AD5683R, AD5684,
- * AD5684R, AD5685R, AD5686, AD5686R
- * Digital to analog converters driver
+ * SPI driver for AD5686 and similar Digital to Analog Converters
*
* Copyright 2018 Analog Devices Inc.
*/
#include <linux/array_size.h>
+#include <linux/bitfield.h>
#include <linux/errno.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/spi/spi.h>
#include <asm/byteorder.h>
#include "ad5686.h"
+/**
+ * struct ad5686_spi_data - SPI bus specific data
+ * @msg: SPI message used for transfers
+ * @size: number of transfers currently in the message
+ * @capacity: maximum number of transfers that can be added to the message
+ * @xfers: array of SPI transfers, allocated with the provided capacity
+ */
+struct ad5686_spi_data {
+ struct spi_message msg;
+ unsigned int size;
+ unsigned int capacity;
+ struct spi_transfer xfers[] __counted_by(capacity);
+};
+
static int ad5686_spi_write(struct ad5686_state *st,
u8 cmd, u8 addr, u16 val)
{
- struct spi_device *spi = to_spi_device(st->dev);
- u8 tx_len, *buf;
-
+ struct ad5686_spi_data *bus_data = st->bus_data;
+ struct spi_transfer *xfer;
+
+ if (bus_data->size >= bus_data->capacity)
+ return -E2BIG;
+
+ /*
+ * This function stores spi transfers to a spi message to be sent over
+ * the bus when sync() op is called. If there are already transfers in
+ * the spi message, set the cs_change flag on the last transfer to
+ * ensure that the chip select is deasserted between transfers. If this
+ * is the first transfer, initialize the spi message. Later on, the
+ * current transfer is added to the message with spi_message_add_tail().
+ */
+ if (bus_data->size)
+ bus_data->xfers[bus_data->size - 1].cs_change = 1;
+ else
+ spi_message_init(&bus_data->msg);
+
+ xfer = &bus_data->xfers[bus_data->size];
+ auto buf = &st->data[bus_data->size];
switch (st->chip_info->regmap_type) {
case AD5310_REGMAP:
- st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) |
- val);
- buf = &st->data[0].d8[0];
- tx_len = 2;
+ buf->d16 = cpu_to_be16(FIELD_PREP(AD5310_CMD_MSK, cmd) |
+ FIELD_PREP(AD5310_DATA_MSK, val));
+ *xfer = (struct spi_transfer) {
+ .tx_buf = &buf->d16,
+ .len = sizeof(buf->d16),
+ };
break;
case AD5683_REGMAP:
- st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
- AD5683_DATA(val));
- buf = &st->data[0].d8[1];
- tx_len = 3;
+ buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) |
+ FIELD_PREP(AD5683_DATA_MSK, val));
+ *xfer = (struct spi_transfer) {
+ .tx_buf = &buf->d8[1],
+ .len = sizeof(buf->d8) - 1,
+ };
break;
case AD5686_REGMAP:
- st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
- AD5686_ADDR(addr) |
- val);
- buf = &st->data[0].d8[1];
- tx_len = 3;
+ buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) |
+ FIELD_PREP(AD5686_ADDR_MSK, addr) |
+ FIELD_PREP(AD5686_DATA_MSK, val));
+ *xfer = (struct spi_transfer) {
+ .tx_buf = &buf->d8[1],
+ .len = sizeof(buf->d8) - 1,
+ };
break;
default:
return -EINVAL;
}
- return spi_write(spi, buf, tx_len);
+ spi_message_add_tail(xfer, &bus_data->msg);
+ bus_data->size++;
+
+ return 0;
+}
+
+static int ad5686_spi_sync(struct ad5686_state *st)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ struct ad5686_spi_data *bus_data = st->bus_data;
+
+ bus_data->size = 0; /* always reset, even on sync failure */
+ return spi_sync(spi, &bus_data->msg);
}
static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
{
- struct spi_transfer t[] = {
- {
- .tx_buf = &st->data[0].d8[1],
- .len = 3,
- .cs_change = 1,
- }, {
- .tx_buf = &st->data[1].d8[1],
- .rx_buf = &st->data[2].d8[1],
- .len = 3,
- },
- };
struct spi_device *spi = to_spi_device(st->dev);
+ struct ad5686_spi_data *bus_data = st->bus_data;
+ struct spi_transfer *xfer = &bus_data->xfers[0];
u8 cmd = 0;
int ret;
@@ -80,12 +120,25 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
return -EINVAL;
}
- st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
- AD5686_ADDR(addr));
- st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
+ st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) |
+ FIELD_PREP(AD5686_ADDR_MSK, addr));
+ st->data[1].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP));
+
+ xfer[0] = (struct spi_transfer) {
+ .tx_buf = &st->data[0].d8[1],
+ .len = sizeof(st->data[0].d8) - 1,
+ .cs_change = 1,
+ };
+ xfer[1] = (struct spi_transfer) {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[2].d8[1],
+ .len = sizeof(st->data[1].d8) - 1,
+ };
+
+ spi_message_init_with_transfers(&bus_data->msg, xfer, 2);
- ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
- if (ret < 0)
+ ret = spi_sync(spi, &bus_data->msg);
+ if (ret)
return ret;
return be32_to_cpu(st->data[2].d32);
@@ -94,20 +147,42 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
static const struct ad5686_bus_ops ad5686_spi_ops = {
.write = ad5686_spi_write,
.read = ad5686_spi_read,
+ .sync = ad5686_spi_sync,
};
static int ad5686_spi_probe(struct spi_device *spi)
{
- return ad5686_probe(&spi->dev, spi_get_device_match_data(spi),
- spi->modalias, &ad5686_spi_ops);
+ const struct ad5686_chip_info *info;
+ struct ad5686_spi_data *bus_data;
+ struct device *dev = &spi->dev;
+ unsigned int capacity;
+
+ info = spi_get_device_match_data(spi);
+ if (!info)
+ return -ENODATA;
+
+ /* read operation requires at least 2 transfers */
+ capacity = max(info->num_channels, 2);
+ bus_data = devm_kzalloc(dev, struct_size(bus_data, xfers, capacity),
+ GFP_KERNEL);
+ if (!bus_data)
+ return -ENOMEM;
+
+ bus_data->capacity = capacity;
+
+ return ad5686_probe(dev, info, spi->modalias, &ad5686_spi_ops, bus_data);
}
static const struct spi_device_id ad5686_spi_id[] = {
{ .name = "ad5310r", .driver_data = (kernel_ulong_t)&ad5310r_chip_info },
+ { .name = "ad5313r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info },
+ { .name = "ad5317r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info },
{ .name = "ad5672r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info },
+ { .name = "ad5674", .driver_data = (kernel_ulong_t)&ad5674_chip_info },
{ .name = "ad5674r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info },
{ .name = "ad5676", .driver_data = (kernel_ulong_t)&ad5676_chip_info },
{ .name = "ad5676r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info },
+ { .name = "ad5679", .driver_data = (kernel_ulong_t)&ad5679_chip_info },
{ .name = "ad5679r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info },
{ .name = "ad5681r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info },
{ .name = "ad5682r", .driver_data = (kernel_ulong_t)&ad5682r_chip_info },
@@ -119,16 +194,24 @@ static const struct spi_device_id ad5686_spi_id[] = {
{ .name = "ad5685r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info },
{ .name = "ad5686", .driver_data = (kernel_ulong_t)&ad5686_chip_info },
{ .name = "ad5686r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info },
+ { .name = "ad5687", .driver_data = (kernel_ulong_t)&ad5687_chip_info },
+ { .name = "ad5687r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info },
+ { .name = "ad5689", .driver_data = (kernel_ulong_t)&ad5689_chip_info },
+ { .name = "ad5689r", .driver_data = (kernel_ulong_t)&ad5689r_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5686_spi_id);
static const struct of_device_id ad5686_of_match[] = {
{ .compatible = "adi,ad5310r", .data = &ad5310r_chip_info },
+ { .compatible = "adi,ad5313r", .data = &ad5338r_chip_info },
+ { .compatible = "adi,ad5317r", .data = &ad5317r_chip_info },
{ .compatible = "adi,ad5672r", .data = &ad5672r_chip_info },
+ { .compatible = "adi,ad5674", .data = &ad5674_chip_info },
{ .compatible = "adi,ad5674r", .data = &ad5674r_chip_info },
{ .compatible = "adi,ad5676", .data = &ad5676_chip_info },
{ .compatible = "adi,ad5676r", .data = &ad5676r_chip_info },
+ { .compatible = "adi,ad5679", .data = &ad5679_chip_info },
{ .compatible = "adi,ad5679r", .data = &ad5679r_chip_info },
{ .compatible = "adi,ad5681r", .data = &ad5681r_chip_info },
{ .compatible = "adi,ad5682r", .data = &ad5682r_chip_info },
@@ -139,6 +222,10 @@ static const struct of_device_id ad5686_of_match[] = {
{ .compatible = "adi,ad5685r", .data = &ad5685r_chip_info },
{ .compatible = "adi,ad5686", .data = &ad5686_chip_info },
{ .compatible = "adi,ad5686r", .data = &ad5686r_chip_info },
+ { .compatible = "adi,ad5687", .data = &ad5687_chip_info },
+ { .compatible = "adi,ad5687r", .data = &ad5687r_chip_info },
+ { .compatible = "adi,ad5689", .data = &ad5689_chip_info },
+ { .compatible = "adi,ad5689r", .data = &ad5689r_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad5686_of_match);
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 5840fda4b011..0cc1c9c22a28 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * AD5686R, AD5685R, AD5684R Digital to analog converters driver
+ * Core driver for AD5686 and similar Digital to Analog Converters
*
* Copyright 2011 Analog Devices Inc.
*/
@@ -8,15 +8,27 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/export.h>
+#include <linux/gpio/consumer.h>
#include <linux/kstrtox.h>
+#include <linux/math64.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/sysfs.h>
+#include <linux/units.h>
#include <linux/wordpart.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>
#include "ad5686.h"
@@ -31,8 +43,9 @@ static int ad5310_control_sync(struct ad5686_state *st)
unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode;
return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0,
- FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) |
- FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1));
+ FIELD_PREP(AD5310_DATA_PD_MSK, pd_val & AD5686_PD_MSK) |
+ FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) |
+ FIELD_PREP(AD5310_DATA_GAIN_MSK, st->double_scale ? 1 : 0));
}
static int ad5683_control_sync(struct ad5686_state *st)
@@ -40,8 +53,9 @@ static int ad5683_control_sync(struct ad5686_state *st)
unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode;
return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0,
- FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) |
- FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1));
+ FIELD_PREP(AD5683_DATA_PD_MSK, pd_val & AD5686_PD_MSK) |
+ FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) |
+ FIELD_PREP(AD5683_DATA_GAIN_MSK, st->double_scale ? 1 : 0));
}
static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan)
@@ -173,20 +187,25 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
struct ad5686_state *st = iio_priv(indio_dev);
int ret;
+ guard(mutex)(&st->lock);
+
switch (m) {
case IIO_CHAN_INFO_RAW:
- mutex_lock(&st->lock);
ret = ad5686_read(st, chan->address);
- mutex_unlock(&st->lock);
if (ret < 0)
return ret;
*val = (ret >> chan->scan_type.shift) &
GENMASK(chan->scan_type.realbits - 1, 0);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = st->vref_mv;
- *val2 = chan->scan_type.realbits;
- return IIO_VAL_FRACTIONAL_LOG2;
+ if (st->double_scale) {
+ *val = st->scale_avail[2];
+ *val2 = st->scale_avail[3];
+ } else {
+ *val = st->scale_avail[0];
+ *val2 = st->scale_avail[1];
+ }
+ return IIO_VAL_INT_PLUS_NANO;
}
return -EINVAL;
}
@@ -198,28 +217,104 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad5686_state *st = iio_priv(indio_dev);
+ bool double_scale;
int ret;
+ guard(mutex)(&st->lock);
+
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (val >= (1 << chan->scan_type.realbits) || val < 0)
return -EINVAL;
- mutex_lock(&st->lock);
- ret = ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
- chan->address, val << chan->scan_type.shift);
- mutex_unlock(&st->lock);
- break;
+ return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address, val << chan->scan_type.shift);
+ case IIO_CHAN_INFO_SCALE:
+ if (val == st->scale_avail[0] && val2 == st->scale_avail[1])
+ double_scale = false;
+ else if (val == st->scale_avail[2] && val2 == st->scale_avail[3])
+ double_scale = true;
+ else
+ return -EINVAL;
+
+ if (st->double_scale == double_scale)
+ return 0; /* no change */
+
+ if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio)
+ return -EINVAL; /* GAIN pin is board-strapped */
+
+ st->double_scale = double_scale;
+ switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ ret = ad5310_control_sync(st);
+ break;
+ case AD5683_REGMAP:
+ ret = ad5683_control_sync(st);
+ break;
+ case AD5686_REGMAP:
+ ret = gpiod_set_value_cansleep(st->gain_gpio,
+ st->double_scale ? 1 : 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ st->double_scale = !double_scale; /* revert on failure */
+ return ret;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
+}
- return ret;
+static int ad5686_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5686_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *type = IIO_VAL_INT_PLUS_NANO;
+
+ if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) {
+ /*
+ * GAIN pin is board-strapped, so only the current
+ * scale is available.
+ */
+ *vals = st->double_scale ? &st->scale_avail[2] :
+ &st->scale_avail[0];
+ *length = 2;
+ return IIO_AVAIL_LIST;
+ }
+
+ *vals = st->scale_avail;
+ *length = ARRAY_SIZE(st->scale_avail);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
}
static const struct iio_info ad5686_info = {
.read_raw = ad5686_read_raw,
.write_raw = ad5686_write_raw,
+ .write_raw_get_fmt = ad5686_write_raw_get_fmt,
+ .read_avail = ad5686_read_avail,
};
static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
@@ -241,7 +336,9 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),\
.address = addr, \
+ .scan_index = chan, \
.scan_type = { \
.sign = 'u', \
.realbits = (bits), \
@@ -312,8 +409,11 @@ DECLARE_AD5683_CHANNELS(ad5683r_channels, 16, 0);
/* dual-channel */
DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8);
DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6);
+DECLARE_AD5338_CHANNELS(ad5687r_channels, 12, 4);
+DECLARE_AD5338_CHANNELS(ad5689r_channels, 16, 0);
/* quad-channel */
+DECLARE_AD5686_CHANNELS(ad5317r_channels, 10, 6);
DECLARE_AD5686_CHANNELS(ad5684r_channels, 12, 4);
DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2);
DECLARE_AD5686_CHANNELS(ad5686r_channels, 16, 0);
@@ -389,6 +489,44 @@ const struct ad5686_chip_info ad5338r_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(ad5338r_chip_info, "IIO_AD5686");
+const struct ad5686_chip_info ad5687_chip_info = {
+ .channels = ad5687r_channels,
+ .num_channels = 2,
+ .regmap_type = AD5686_REGMAP,
+};
+EXPORT_SYMBOL_NS_GPL(ad5687_chip_info, "IIO_AD5686");
+
+const struct ad5686_chip_info ad5687r_chip_info = {
+ .channels = ad5687r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 2,
+ .regmap_type = AD5686_REGMAP,
+};
+EXPORT_SYMBOL_NS_GPL(ad5687r_chip_info, "IIO_AD5686");
+
+const struct ad5686_chip_info ad5689_chip_info = {
+ .channels = ad5689r_channels,
+ .num_channels = 2,
+ .regmap_type = AD5686_REGMAP,
+};
+EXPORT_SYMBOL_NS_GPL(ad5689_chip_info, "IIO_AD5686");
+
+const struct ad5686_chip_info ad5689r_chip_info = {
+ .channels = ad5689r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 2,
+ .regmap_type = AD5686_REGMAP,
+};
+EXPORT_SYMBOL_NS_GPL(ad5689r_chip_info, "IIO_AD5686");
+
+const struct ad5686_chip_info ad5317r_chip_info = {
+ .channels = ad5317r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+};
+EXPORT_SYMBOL_NS_GPL(ad5317r_chip_info, "IIO_AD5686");
+
const struct ad5686_chip_info ad5684_chip_info = {
.channels = ad5684r_channels,
.num_channels = 4,
@@ -450,6 +588,13 @@ const struct ad5686_chip_info ad5676r_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(ad5676r_chip_info, "IIO_AD5686");
+const struct ad5686_chip_info ad5674_chip_info = {
+ .channels = ad5674r_channels,
+ .num_channels = 16,
+ .regmap_type = AD5686_REGMAP,
+};
+EXPORT_SYMBOL_NS_GPL(ad5674_chip_info, "IIO_AD5686");
+
const struct ad5686_chip_info ad5674r_chip_info = {
.channels = ad5674r_channels,
.int_vref_mv = 2500,
@@ -458,6 +603,13 @@ const struct ad5686_chip_info ad5674r_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(ad5674r_chip_info, "IIO_AD5686");
+const struct ad5686_chip_info ad5679_chip_info = {
+ .channels = ad5679r_channels,
+ .num_channels = 16,
+ .regmap_type = AD5686_REGMAP,
+};
+EXPORT_SYMBOL_NS_GPL(ad5679_chip_info, "IIO_AD5686");
+
const struct ad5686_chip_info ad5679r_chip_info = {
.channels = ad5679r_channels,
.int_vref_mv = 2500,
@@ -466,10 +618,74 @@ const struct ad5686_chip_info ad5679r_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686");
+static void ad5686_init_scale_avail(struct ad5686_state *st)
+{
+ int realbits = st->chip_info->channels[0].scan_type.realbits;
+ s64 tmp = 2ULL * st->vref_mv * NANO >> realbits;
+
+ st->scale_avail[2] = div_s64_rem(tmp, NANO, &st->scale_avail[3]);
+ st->scale_avail[0] = div_s64_rem(tmp >> 1, NANO, &st->scale_avail[1]);
+}
+
+static void do_ad5686_trigger_handler(struct iio_dev *indio_dev)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+ u16 val[AD5686_MAX_CHANNELS] = { };
+ unsigned int scan_count, ch, i;
+ bool async_update;
+ u8 cmd;
+
+ if (iio_pop_from_buffer(indio_dev->buffer, val))
+ return;
+
+ guard(mutex)(&st->lock);
+
+ scan_count = bitmap_weight(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ async_update = st->ldac_gpio && scan_count > 1;
+ if (async_update) {
+ /* use LDAC to update all channels simultaneously */
+ cmd = AD5686_CMD_WRITE_INPUT_N;
+ gpiod_set_value_cansleep(st->ldac_gpio, 0);
+ } else {
+ cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N;
+ }
+
+ i = 0;
+ iio_for_each_active_channel(indio_dev, ch) {
+ if (st->ops->write(st, cmd, indio_dev->channels[ch].address, val[i++]))
+ break;
+ }
+
+ /*
+ * If sync() is available, it is called here regardless of write
+ * failure to allow bus implementation to reset. In that case, partial
+ * writes are unlikely as the write operations would just queue up
+ * the transfers.
+ */
+ if (st->ops->sync)
+ st->ops->sync(st);
+
+ if (async_update)
+ gpiod_set_value_cansleep(st->ldac_gpio, 1);
+}
+
+static irqreturn_t ad5686_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+
+ do_ad5686_trigger_handler(indio_dev);
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
int ad5686_probe(struct device *dev,
const struct ad5686_chip_info *chip_info,
- const char *name, const struct ad5686_bus_ops *ops)
+ const char *name, const struct ad5686_bus_ops *ops,
+ void *bus_data)
{
+ struct reset_control *rstc;
struct ad5686_state *st;
struct iio_dev *indio_dev;
int ret, i;
@@ -482,14 +698,56 @@ int ad5686_probe(struct device *dev,
st->dev = dev;
st->ops = ops;
+ st->bus_data = bus_data;
st->chip_info = chip_info;
- ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
+ 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 control\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable vdd supply\n");
+
+ ret = devm_regulator_get_enable(dev, "vlogic");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable vlogic supply\n");
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */
+ ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
if (ret < 0 && ret != -ENODEV)
- return ret;
+ return dev_err_probe(dev, ret, "failed to read vref voltage\n");
st->use_internal_vref = ret == -ENODEV;
st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000;
+ if (!st->vref_mv)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid or not provided vref voltage\n");
+
+ /* 4.5us power-up time: Datasheet Table 4: Timing Characteristics */
+ fsleep(5);
+
+ /* 1us >> 30ns reset pulse activation time: Datasheet Table 4 */
+ reset_control_assert(rstc);
+ fsleep(1);
+ reset_control_deassert(rstc);
+
+ st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH);
+ if (IS_ERR(st->ldac_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->ldac_gpio),
+ "Failed to get LDAC GPIO\n");
+
+ st->double_scale = device_property_read_bool(dev, "adi,range-double");
+ st->gain_gpio = devm_gpiod_get_optional(dev, "gain",
+ st->double_scale ? GPIOD_OUT_HIGH :
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gain_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->gain_gpio),
+ "Failed to get GAIN GPIO\n");
+
+ ad5686_init_scale_avail(st);
/* Initialize masks to all ones */
st->pwr_down_mask = ~0;
@@ -526,7 +784,7 @@ int ad5686_probe(struct device *dev,
break;
case AD5686_REGMAP:
ret = ad5686_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0,
- st->use_internal_vref ? 0 : AD5686_REF_BIT_MSK);
+ st->use_internal_vref ? 0 : AD5686_DATA_REF_MSK);
if (ret)
return ret;
break;
@@ -534,6 +792,13 @@ int ad5686_probe(struct device *dev,
return -EINVAL;
}
+ ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL,
+ &ad5686_trigger_handler,
+ IIO_BUFFER_DIRECTION_OUT,
+ NULL, NULL);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686");
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index a06fe7d89305..482bc70515f7 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -14,15 +14,18 @@
#include <linux/iio/iio.h>
-#define AD5310_CMD(x) ((x) << 12)
+#define AD5310_CMD_MSK GENMASK(15, 12)
+#define AD5310_DATA_MSK GENMASK(11, 0)
-#define AD5683_DATA(x) ((x) << 4)
+#define AD5683_DATA_MSK GENMASK(19, 4)
-#define AD5686_ADDR(x) ((x) << 16)
-#define AD5686_CMD(x) ((x) << 20)
+#define AD5686_CMD_MSK GENMASK(23, 20)
+#define AD5686_ADDR_MSK GENMASK(19, 16)
+#define AD5686_DATA_MSK GENMASK(15, 0)
#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
#define AD5686_ADDR_ALL_DAC 0xF
+#define AD5686_MAX_CHANNELS 16
#define AD5686_CMD_NOOP 0x0
#define AD5686_CMD_WRITE_INPUT_N 0x1
@@ -38,15 +41,17 @@
#define AD5686_CMD_CONTROL_REG 0x4
#define AD5686_CMD_READBACK_ENABLE_V2 0x5
-#define AD5310_REF_BIT_MSK BIT(8)
-#define AD5310_PD_MSK GENMASK(10, 9)
+#define AD5310_DATA_GAIN_MSK BIT(7)
+#define AD5310_DATA_REF_MSK BIT(8)
+#define AD5310_DATA_PD_MSK GENMASK(10, 9)
-#define AD5683_REF_BIT_MSK BIT(12)
-#define AD5683_PD_MSK GENMASK(14, 13)
+#define AD5683_DATA_GAIN_MSK BIT(11) /* DB15 */
+#define AD5683_DATA_REF_MSK BIT(12) /* DB16 */
+#define AD5683_DATA_PD_MSK GENMASK(14, 13) /* DB18:DB17 */
-#define AD5686_REF_BIT_MSK BIT(0)
-#define AD5686_PD_MSK GENMASK(1, 0)
+#define AD5686_DATA_REF_MSK BIT(0)
+#define AD5686_PD_MSK GENMASK(1, 0)
#define AD5686_PD_MODE_1K_TO_GND 0x1
#define AD5686_PD_MODE_100K_TO_GND 0x2
#define AD5686_PD_MODE_THREE_STATE 0x3
@@ -60,16 +65,20 @@ enum ad5686_regmap_type {
AD5686_REGMAP,
};
+struct gpio_desc;
+
struct ad5686_state;
/**
* struct ad5686_bus_ops - bus specific read/write operations
* @read: read a register value at the given address
* @write: write a command, address and value to the device
+ * @sync: ensure the completion of the write operation (optional)
*/
struct ad5686_bus_ops {
int (*read)(struct ad5686_state *st, u8 addr);
int (*write)(struct ad5686_state *st, u8 cmd, u8 addr, u16 val);
+ int (*sync)(struct ad5686_state *st);
};
/**
@@ -97,8 +106,13 @@ extern const struct ad5686_chip_info ad5683r_chip_info;
/* dual-channel instances */
extern const struct ad5686_chip_info ad5337r_chip_info;
extern const struct ad5686_chip_info ad5338r_chip_info;
+extern const struct ad5686_chip_info ad5687_chip_info;
+extern const struct ad5686_chip_info ad5687r_chip_info;
+extern const struct ad5686_chip_info ad5689_chip_info;
+extern const struct ad5686_chip_info ad5689r_chip_info;
/* quad-channel instances */
+extern const struct ad5686_chip_info ad5317r_chip_info;
extern const struct ad5686_chip_info ad5684_chip_info;
extern const struct ad5686_chip_info ad5684r_chip_info;
extern const struct ad5686_chip_info ad5685r_chip_info;
@@ -111,7 +125,9 @@ extern const struct ad5686_chip_info ad5676_chip_info;
extern const struct ad5686_chip_info ad5676r_chip_info;
/* 16-channel instances */
+extern const struct ad5686_chip_info ad5674_chip_info;
extern const struct ad5686_chip_info ad5674r_chip_info;
+extern const struct ad5686_chip_info ad5679_chip_info;
extern const struct ad5686_chip_info ad5679r_chip_info;
/**
@@ -119,23 +135,33 @@ extern const struct ad5686_chip_info ad5679r_chip_info;
* @dev: device instance
* @chip_info: chip model specific constants, available modes etc
* @ops: bus specific operations
- * @vref_mv: actual reference voltage used
+ * @ldac_gpio: LDAC pin GPIO descriptor
+ * @gain_gpio: GAIN pin GPIO descriptor
* @pwr_down_mask: power down mask
* @pwr_down_mode: current power down mode
+ * @scale_avail: pre-calculated available scale values
+ * @vref_mv: actual reference voltage used
+ * @double_scale: flag to indicate the gain multiplier is applied
* @use_internal_vref: set to true if the internal reference voltage is used
* @lock: lock to protect access to state fields, which includes
* the data buffer during regmap ops
+ * @bus_data: bus specific data
* @data: transfer buffers
*/
struct ad5686_state {
struct device *dev;
const struct ad5686_chip_info *chip_info;
const struct ad5686_bus_ops *ops;
- unsigned short vref_mv;
+ struct gpio_desc *ldac_gpio;
+ struct gpio_desc *gain_gpio;
unsigned int pwr_down_mask;
unsigned int pwr_down_mode;
+ int scale_avail[4];
+ unsigned short vref_mv;
+ bool double_scale;
bool use_internal_vref;
struct mutex lock;
+ void *bus_data;
/*
* DMA (thus cache coherency maintenance) may require the
@@ -146,17 +172,24 @@ struct ad5686_state {
__be32 d32;
__be16 d16;
u8 d8[4];
- } data[3] __aligned(IIO_DMA_MINALIGN);
+ } data[AD5686_MAX_CHANNELS] __aligned(IIO_DMA_MINALIGN);
};
int ad5686_probe(struct device *dev,
const struct ad5686_chip_info *chip_info,
- const char *name, const struct ad5686_bus_ops *ops);
+ const char *name, const struct ad5686_bus_ops *ops,
+ void *bus_data);
static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val)
{
- return st->ops->write(st, cmd, addr, val);
+ int ret;
+
+ ret = st->ops->write(st, cmd, addr, val);
+ if (ret)
+ return ret;
+
+ return st->ops->sync ? st->ops->sync(st) : 0;
}
static inline int ad5686_read(struct ad5686_state *st, u8 addr)
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
index d49946adbde3..f91abfcdbada 100644
--- a/drivers/iio/dac/ad5696-i2c.c
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -1,12 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693,
- * AD5693R, AD5694, AD5694R, AD5695R, AD5696, AD5696R
- * Digital to analog converters driver
+ * I2C driver for AD5696 and similar Digital to Analog Converters
*
* Copyright 2018 Analog Devices Inc.
*/
+#include <linux/bitfield.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/module.h>
@@ -34,9 +33,8 @@ static int ad5686_i2c_read(struct ad5686_state *st, u8 addr)
};
int ret;
- st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) |
- AD5686_ADDR(addr) |
- 0x00);
+ st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP) |
+ FIELD_PREP(AD5686_ADDR_MSK, addr));
ret = i2c_transfer(i2c->adapter, msg, 2);
if (ret < 0)
@@ -51,8 +49,9 @@ static int ad5686_i2c_write(struct ad5686_state *st,
struct i2c_client *i2c = to_i2c_client(st->dev);
int ret;
- st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr)
- | val);
+ st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) |
+ FIELD_PREP(AD5686_ADDR_MSK, addr) |
+ FIELD_PREP(AD5686_DATA_MSK, val));
ret = i2c_master_send(i2c, &st->data[0].d8[1], 3);
if (ret < 0)
@@ -68,16 +67,23 @@ static const struct ad5686_bus_ops ad5686_i2c_ops = {
static int ad5686_i2c_probe(struct i2c_client *i2c)
{
- return ad5686_probe(&i2c->dev, i2c_get_match_data(i2c),
- i2c->name, &ad5686_i2c_ops);
+ const struct ad5686_chip_info *info;
+
+ info = i2c_get_match_data(i2c);
+ if (!info)
+ return -ENODATA;
+
+ return ad5686_probe(&i2c->dev, info, i2c->name, &ad5686_i2c_ops, NULL);
}
static const struct i2c_device_id ad5686_i2c_id[] = {
{ .name = "ad5311r", .driver_data = (kernel_ulong_t)&ad5311r_chip_info },
+ { .name = "ad5316r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info },
{ .name = "ad5337r", .driver_data = (kernel_ulong_t)&ad5337r_chip_info },
{ .name = "ad5338r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info },
{ .name = "ad5671r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info },
{ .name = "ad5673r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info },
+ { .name = "ad5675", .driver_data = (kernel_ulong_t)&ad5676_chip_info },
{ .name = "ad5675r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info },
{ .name = "ad5677r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info },
{ .name = "ad5691r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info },
@@ -89,16 +95,21 @@ static const struct i2c_device_id ad5686_i2c_id[] = {
{ .name = "ad5695r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info },
{ .name = "ad5696", .driver_data = (kernel_ulong_t)&ad5686_chip_info },
{ .name = "ad5696r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info },
+ { .name = "ad5697r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id);
static const struct of_device_id ad5686_of_match[] = {
{ .compatible = "adi,ad5311r", .data = &ad5311r_chip_info },
+ { .compatible = "adi,ad5316r", .data = &ad5317r_chip_info },
{ .compatible = "adi,ad5337r", .data = &ad5337r_chip_info },
{ .compatible = "adi,ad5338r", .data = &ad5338r_chip_info },
{ .compatible = "adi,ad5671r", .data = &ad5672r_chip_info },
+ { .compatible = "adi,ad5673r", .data = &ad5674r_chip_info },
+ { .compatible = "adi,ad5675", .data = &ad5676_chip_info },
{ .compatible = "adi,ad5675r", .data = &ad5676r_chip_info },
+ { .compatible = "adi,ad5677r", .data = &ad5679r_chip_info },
{ .compatible = "adi,ad5691r", .data = &ad5681r_chip_info },
{ .compatible = "adi,ad5692r", .data = &ad5682r_chip_info },
{ .compatible = "adi,ad5693", .data = &ad5683_chip_info },
@@ -108,6 +119,7 @@ static const struct of_device_id ad5686_of_match[] = {
{ .compatible = "adi,ad5695r", .data = &ad5685r_chip_info },
{ .compatible = "adi,ad5696", .data = &ad5686_chip_info },
{ .compatible = "adi,ad5696r", .data = &ad5686r_chip_info },
+ { .compatible = "adi,ad5697r", .data = &ad5687r_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad5686_of_match);
diff --git a/drivers/iio/dac/ad5706r.c b/drivers/iio/dac/ad5706r.c
index e4e48ca7ad34..06c4860a7eba 100644
--- a/drivers/iio/dac/ad5706r.c
+++ b/drivers/iio/dac/ad5706r.c
@@ -232,7 +232,7 @@ static const struct of_device_id ad5706r_of_match[] = {
MODULE_DEVICE_TABLE(of, ad5706r_of_match);
static const struct spi_device_id ad5706r_id[] = {
- { "ad5706r" },
+ { .name = "ad5706r" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5706r_id);
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
index cc6d56140d66..0828b9909099 100644
--- a/drivers/iio/dac/ad5755.c
+++ b/drivers/iio/dac/ad5755.c
@@ -849,11 +849,11 @@ static int ad5755_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5755_id[] = {
- { "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
- { "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
- { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] },
- { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] },
- { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] },
+ { .name = "ad5755", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
+ { .name = "ad5755-1", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
+ { .name = "ad5757", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] },
+ { .name = "ad5735", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] },
+ { .name = "ad5737", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5755_id);
diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c
index bb30842e7080..44efa373cd80 100644
--- a/drivers/iio/dac/ad5758.c
+++ b/drivers/iio/dac/ad5758.c
@@ -878,7 +878,7 @@ static int ad5758_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5758_id[] = {
- { "ad5758", 0 },
+ { .name = "ad5758" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5758_id);
diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c
index b5d20f04f070..c49568b943d6 100644
--- a/drivers/iio/dac/ad5761.c
+++ b/drivers/iio/dac/ad5761.c
@@ -342,10 +342,10 @@ static int ad5761_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5761_id[] = {
- {"ad5721", ID_AD5721},
- {"ad5721r", ID_AD5721R},
- {"ad5761", ID_AD5761},
- {"ad5761r", ID_AD5761R},
+ { .name = "ad5721", .driver_data = ID_AD5721 },
+ { .name = "ad5721r", .driver_data = ID_AD5721R },
+ { .name = "ad5761", .driver_data = ID_AD5761 },
+ { .name = "ad5761r", .driver_data = ID_AD5761R },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5761_id);
diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c
index fbbd7105a80c..2be4bee7eb01 100644
--- a/drivers/iio/dac/ad5764.c
+++ b/drivers/iio/dac/ad5764.c
@@ -342,10 +342,10 @@ static void ad5764_remove(struct spi_device *spi)
}
static const struct spi_device_id ad5764_ids[] = {
- { "ad5744", ID_AD5744 },
- { "ad5744r", ID_AD5744R },
- { "ad5764", ID_AD5764 },
- { "ad5764r", ID_AD5764R },
+ { .name = "ad5744", .driver_data = ID_AD5744 },
+ { .name = "ad5744r", .driver_data = ID_AD5744R },
+ { .name = "ad5764", .driver_data = ID_AD5764 },
+ { .name = "ad5764r", .driver_data = ID_AD5764R },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5764_ids);
diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c
index f6a0a0d84fef..061b43fc0345 100644
--- a/drivers/iio/dac/ad5766.c
+++ b/drivers/iio/dac/ad5766.c
@@ -651,8 +651,8 @@ static const struct of_device_id ad5766_dt_match[] = {
MODULE_DEVICE_TABLE(of, ad5766_dt_match);
static const struct spi_device_id ad5766_spi_ids[] = {
- { "ad5766", ID_AD5766 },
- { "ad5767", ID_AD5767 },
+ { .name = "ad5766", .driver_data = ID_AD5766 },
+ { .name = "ad5767", .driver_data = ID_AD5767 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5766_spi_ids);
diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c
index 6027e8d88b27..bdc48e57013f 100644
--- a/drivers/iio/dac/ad5770r.c
+++ b/drivers/iio/dac/ad5770r.c
@@ -642,7 +642,7 @@ static const struct of_device_id ad5770r_of_id[] = {
MODULE_DEVICE_TABLE(of, ad5770r_of_id);
static const struct spi_device_id ad5770r_id[] = {
- { "ad5770r", 0 },
+ { .name = "ad5770r" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5770r_id);
diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c
index ae7297f08398..a5eda135c9bd 100644
--- a/drivers/iio/dac/ad5791.c
+++ b/drivers/iio/dac/ad5791.c
@@ -586,11 +586,11 @@ static const struct of_device_id ad5791_of_match[] = {
MODULE_DEVICE_TABLE(of, ad5791_of_match);
static const struct spi_device_id ad5791_id[] = {
- { "ad5760", (kernel_ulong_t)&ad5760_chip_info },
- { "ad5780", (kernel_ulong_t)&ad5780_chip_info },
- { "ad5781", (kernel_ulong_t)&ad5781_chip_info },
- { "ad5790", (kernel_ulong_t)&ad5790_chip_info },
- { "ad5791", (kernel_ulong_t)&ad5791_chip_info },
+ { .name = "ad5760", .driver_data = (kernel_ulong_t)&ad5760_chip_info },
+ { .name = "ad5780", .driver_data = (kernel_ulong_t)&ad5780_chip_info },
+ { .name = "ad5781", .driver_data = (kernel_ulong_t)&ad5781_chip_info },
+ { .name = "ad5790", .driver_data = (kernel_ulong_t)&ad5790_chip_info },
+ { .name = "ad5791", .driver_data = (kernel_ulong_t)&ad5791_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5791_id);
diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c
index 03acf7c114b0..33a49813ece0 100644
--- a/drivers/iio/dac/ad7293.c
+++ b/drivers/iio/dac/ad7293.c
@@ -869,7 +869,7 @@ static int ad7293_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7293_id[] = {
- { "ad7293", 0 },
+ { .name = "ad7293" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7293_id);
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c
index 6451fc586a67..83153e7d7fc8 100644
--- a/drivers/iio/dac/ad7303.c
+++ b/drivers/iio/dac/ad7303.c
@@ -270,7 +270,7 @@ static const struct of_device_id ad7303_spi_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7303_spi_of_match);
static const struct spi_device_id ad7303_spi_ids[] = {
- { "ad7303", 0 },
+ { .name = "ad7303" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c
index ddec62b6e57b..a2292810e4cf 100644
--- a/drivers/iio/dac/ad8460.c
+++ b/drivers/iio/dac/ad8460.c
@@ -954,3 +954,4 @@ MODULE_AUTHOR("Mariel Tinaco <mariel.tinaco@analog.com");
MODULE_DESCRIPTION("AD8460 DAC driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c
index 60e663af1cc1..08d7d74e999e 100644
--- a/drivers/iio/dac/ad8801.c
+++ b/drivers/iio/dac/ad8801.c
@@ -151,8 +151,8 @@ static int ad8801_probe(struct spi_device *spi)
}
static const struct spi_device_id ad8801_ids[] = {
- {"ad8801", ID_AD8801},
- {"ad8803", ID_AD8803},
+ { .name = "ad8801", .driver_data = ID_AD8801 },
+ { .name = "ad8803", .driver_data = ID_AD8803 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad8801_ids);
diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
index ccd6a3b1e891..5263a0c968f5 100644
--- a/drivers/iio/dac/ad9739a.c
+++ b/drivers/iio/dac/ad9739a.c
@@ -446,7 +446,7 @@ static const struct of_device_id ad9739a_of_match[] = {
MODULE_DEVICE_TABLE(of, ad9739a_of_match);
static const struct spi_device_id ad9739a_id[] = {
- {"ad9739a"},
+ { .name = "ad9739a" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad9739a_id);
diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c
index cf6d94e7af84..de36e2e27330 100644
--- a/drivers/iio/dac/dpot-dac.c
+++ b/drivers/iio/dac/dpot-dac.c
@@ -253,3 +253,4 @@ module_platform_driver(dpot_dac_driver);
MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c
index 6e80b49f4665..90ffb58e7927 100644
--- a/drivers/iio/dac/ltc1660.c
+++ b/drivers/iio/dac/ltc1660.c
@@ -224,8 +224,8 @@ static const struct of_device_id ltc1660_dt_ids[] = {
MODULE_DEVICE_TABLE(of, ltc1660_dt_ids);
static const struct spi_device_id ltc1660_id[] = {
- {"ltc1660", ID_LTC1660},
- {"ltc1665", ID_LTC1665},
+ { .name = "ltc1660", .driver_data = ID_LTC1660 },
+ { .name = "ltc1665", .driver_data = ID_LTC1665 },
{ }
};
MODULE_DEVICE_TABLE(spi, ltc1660_id);
diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c
index d6a3d290e7a8..3800aee8dd6e 100644
--- a/drivers/iio/dac/ltc2632.c
+++ b/drivers/iio/dac/ltc2632.c
@@ -362,28 +362,28 @@ static int ltc2632_probe(struct spi_device *spi)
}
static const struct spi_device_id ltc2632_id[] = {
- { "ltc2632-l12", (kernel_ulong_t)&ltc2632l12_chip_info },
- { "ltc2632-l10", (kernel_ulong_t)&ltc2632l10_chip_info },
- { "ltc2632-l8", (kernel_ulong_t)&ltc2632l8_chip_info },
- { "ltc2632-h12", (kernel_ulong_t)&ltc2632h12_chip_info },
- { "ltc2632-h10", (kernel_ulong_t)&ltc2632h10_chip_info },
- { "ltc2632-h8", (kernel_ulong_t)&ltc2632h8_chip_info },
- { "ltc2634-l12", (kernel_ulong_t)&ltc2634l12_chip_info },
- { "ltc2634-l10", (kernel_ulong_t)&ltc2634l10_chip_info },
- { "ltc2634-l8", (kernel_ulong_t)&ltc2634l8_chip_info },
- { "ltc2634-h12", (kernel_ulong_t)&ltc2634h12_chip_info },
- { "ltc2634-h10", (kernel_ulong_t)&ltc2634h10_chip_info },
- { "ltc2634-h8", (kernel_ulong_t)&ltc2634h8_chip_info },
- { "ltc2636-l12", (kernel_ulong_t)&ltc2636l12_chip_info },
- { "ltc2636-l10", (kernel_ulong_t)&ltc2636l10_chip_info },
- { "ltc2636-l8", (kernel_ulong_t)&ltc2636l8_chip_info },
- { "ltc2636-h12", (kernel_ulong_t)&ltc2636h12_chip_info },
- { "ltc2636-h10", (kernel_ulong_t)&ltc2636h10_chip_info },
- { "ltc2636-h8", (kernel_ulong_t)&ltc2636h8_chip_info },
- { "ltc2654-l16", (kernel_ulong_t)&ltc2654l16_chip_info },
- { "ltc2654-l12", (kernel_ulong_t)&ltc2634l12_chip_info },
- { "ltc2654-h16", (kernel_ulong_t)&ltc2654h16_chip_info },
- { "ltc2654-h12", (kernel_ulong_t)&ltc2634h12_chip_info },
+ { .name = "ltc2632-l12", .driver_data = (kernel_ulong_t)&ltc2632l12_chip_info },
+ { .name = "ltc2632-l10", .driver_data = (kernel_ulong_t)&ltc2632l10_chip_info },
+ { .name = "ltc2632-l8", .driver_data = (kernel_ulong_t)&ltc2632l8_chip_info },
+ { .name = "ltc2632-h12", .driver_data = (kernel_ulong_t)&ltc2632h12_chip_info },
+ { .name = "ltc2632-h10", .driver_data = (kernel_ulong_t)&ltc2632h10_chip_info },
+ { .name = "ltc2632-h8", .driver_data = (kernel_ulong_t)&ltc2632h8_chip_info },
+ { .name = "ltc2634-l12", .driver_data = (kernel_ulong_t)&ltc2634l12_chip_info },
+ { .name = "ltc2634-l10", .driver_data = (kernel_ulong_t)&ltc2634l10_chip_info },
+ { .name = "ltc2634-l8", .driver_data = (kernel_ulong_t)&ltc2634l8_chip_info },
+ { .name = "ltc2634-h12", .driver_data = (kernel_ulong_t)&ltc2634h12_chip_info },
+ { .name = "ltc2634-h10", .driver_data = (kernel_ulong_t)&ltc2634h10_chip_info },
+ { .name = "ltc2634-h8", .driver_data = (kernel_ulong_t)&ltc2634h8_chip_info },
+ { .name = "ltc2636-l12", .driver_data = (kernel_ulong_t)&ltc2636l12_chip_info },
+ { .name = "ltc2636-l10", .driver_data = (kernel_ulong_t)&ltc2636l10_chip_info },
+ { .name = "ltc2636-l8", .driver_data = (kernel_ulong_t)&ltc2636l8_chip_info },
+ { .name = "ltc2636-h12", .driver_data = (kernel_ulong_t)&ltc2636h12_chip_info },
+ { .name = "ltc2636-h10", .driver_data = (kernel_ulong_t)&ltc2636h10_chip_info },
+ { .name = "ltc2636-h8", .driver_data = (kernel_ulong_t)&ltc2636h8_chip_info },
+ { .name = "ltc2654-l16", .driver_data = (kernel_ulong_t)&ltc2654l16_chip_info },
+ { .name = "ltc2654-l12", .driver_data = (kernel_ulong_t)&ltc2634l12_chip_info },
+ { .name = "ltc2654-h16", .driver_data = (kernel_ulong_t)&ltc2654h16_chip_info },
+ { .name = "ltc2654-h12", .driver_data = (kernel_ulong_t)&ltc2634h12_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ltc2632_id);
diff --git a/drivers/iio/dac/ltc2664.c b/drivers/iio/dac/ltc2664.c
index c48b9efc8280..35bec55ebde1 100644
--- a/drivers/iio/dac/ltc2664.c
+++ b/drivers/iio/dac/ltc2664.c
@@ -708,8 +708,8 @@ static int ltc2664_probe(struct spi_device *spi)
}
static const struct spi_device_id ltc2664_id[] = {
- { "ltc2664", (kernel_ulong_t)&ltc2664_chip },
- { "ltc2672", (kernel_ulong_t)&ltc2672_chip },
+ { .name = "ltc2664", .driver_data = (kernel_ulong_t)&ltc2664_chip },
+ { .name = "ltc2672", .driver_data = (kernel_ulong_t)&ltc2672_chip },
{ }
};
MODULE_DEVICE_TABLE(spi, ltc2664_id);
diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c
index a575ef8371dc..0301bb7dce31 100644
--- a/drivers/iio/dac/ltc2688.c
+++ b/drivers/iio/dac/ltc2688.c
@@ -991,7 +991,7 @@ static const struct of_device_id ltc2688_of_id[] = {
MODULE_DEVICE_TABLE(of, ltc2688_of_id);
static const struct spi_device_id ltc2688_id[] = {
- { "ltc2688" },
+ { .name = "ltc2688" },
{ }
};
MODULE_DEVICE_TABLE(spi, ltc2688_id);
diff --git a/drivers/iio/dac/max22007.c b/drivers/iio/dac/max22007.c
index d747901df5a3..e2c7e2ad37ba 100644
--- a/drivers/iio/dac/max22007.c
+++ b/drivers/iio/dac/max22007.c
@@ -464,7 +464,7 @@ static int max22007_probe(struct spi_device *spi)
}
static const struct spi_device_id max22007_id[] = {
- { "max22007" },
+ { .name = "max22007" },
{ }
};
MODULE_DEVICE_TABLE(spi, max22007_id);
diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c
index 1459ba132df6..f3415748c765 100644
--- a/drivers/iio/dac/max5522.c
+++ b/drivers/iio/dac/max5522.c
@@ -24,15 +24,8 @@
#define MAX5522_REG_DATA(x) ((x) + MAX5522_CTRL_LOAD_IN_A)
-struct max5522_chip_info {
- const char *name;
- const struct iio_chan_spec *channels;
- unsigned int num_channels;
-};
-
struct max5522_state {
struct regmap *regmap;
- const struct max5522_chip_info *chip_info;
unsigned short dac_cache[2];
int vref_mV;
};
@@ -57,18 +50,6 @@ static const struct iio_chan_spec max5522_channels[] = {
MAX5522_CHANNEL(1),
};
-enum max5522_type {
- ID_MAX5522,
-};
-
-static const struct max5522_chip_info max5522_chip_info_tbl[] = {
- [ID_MAX5522] = {
- .name = "max5522",
- .channels = max5522_channels,
- .num_channels = 2,
- },
-};
-
static inline int max5522_info_to_reg(struct iio_chan_spec const *chan)
{
return MAX5522_REG_DATA(chan->channel);
@@ -139,9 +120,6 @@ static int max5522_spi_probe(struct spi_device *spi)
}
state = iio_priv(indio_dev);
- state->chip_info = spi_get_device_match_data(spi);
- if (!state->chip_info)
- return -EINVAL;
ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefin");
if (ret < 0)
@@ -158,22 +136,19 @@ static int max5522_spi_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = max5522_channels;
indio_dev->num_channels = ARRAY_SIZE(max5522_channels);
- indio_dev->name = max5522_chip_info_tbl[ID_MAX5522].name;
+ indio_dev->name = "max5522";
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id max5522_ids[] = {
- { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] },
+ { .name = "max5522" },
{ }
};
MODULE_DEVICE_TABLE(spi, max5522_ids);
static const struct of_device_id max5522_of_match[] = {
- {
- .compatible = "maxim,max5522",
- .data = &max5522_chip_info_tbl[ID_MAX5522],
- },
+ { .compatible = "maxim,max5522" },
{ }
};
MODULE_DEVICE_TABLE(of, max5522_of_match);
diff --git a/drivers/iio/dac/mcf54415_dac.c b/drivers/iio/dac/mcf54415_dac.c
new file mode 100644
index 000000000000..e2c12241a534
--- /dev/null
+++ b/drivers/iio/dac/mcf54415_dac.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NXP mcf54415 DAC driver
+ *
+ * Copyright 2026 BayLibre - adureghello@baylibre.com
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define MCF54415_DAC_CR 0x00
+#define MCF54415_DAC_CR_PDN BIT(0)
+#define MCF54415_DAC_CR_HSLS BIT(6)
+#define MCF54415_DAC_CR_WMLVL GENMASK(9, 8)
+#define MCF54415_DAC_CR_FILT BIT(12)
+
+#define MCF54415_DAC_DATA 0x02
+
+struct mcf54415_dac {
+ struct regmap *map;
+ struct clk *clk;
+};
+
+static const struct regmap_config mcf54415_dac_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x0c, /* DACX_FILTCNT, R.M. Table 30-2 */
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int mcf54415_dac_init(struct mcf54415_dac *info)
+{
+ u16 val = MCF54415_DAC_CR_FILT | FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1);
+ int ret;
+
+ /* Fixed defaults and enable DAC (bit 0 set to 0) */
+ ret = regmap_write(info->map, MCF54415_DAC_CR, val);
+ if (ret)
+ return ret;
+
+ /* DAC is ready after 12us, from RM table 40-3 */
+ fsleep(12);
+
+ return 0;
+}
+
+static void mcf54415_dac_exit(void *data)
+{
+ struct mcf54415_dac *info = data;
+
+ regmap_set_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN);
+}
+
+static const struct iio_chan_spec mcf54415_dac_iio_channel = {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static int mcf54415_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mcf54415_dac *info = iio_priv(indio_dev);
+ unsigned int reg;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(info->map, MCF54415_DAC_DATA, &reg);
+ if (ret)
+ return ret;
+ *val = reg & GENMASK(11, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* Reference voltage as per ColdFire datasheet is 3.3V */
+ *val = 3300 /* mV */;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcf54415_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mcf54415_dac *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* Check based on RM 30.3.2 (DACn_DATA) reg. resolution */
+ if (val < 0 || val > 4095)
+ return -EINVAL;
+ return regmap_write(info->map, MCF54415_DAC_DATA, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mcf54415_dac_iio_info = {
+ .read_raw = &mcf54415_read_raw,
+ .write_raw = &mcf54415_write_raw,
+};
+
+static int mcf54415_dac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct mcf54415_dac *info;
+ void __iomem *regs;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return dev_err_probe(dev, PTR_ERR(regs), "failed to get io regs\n");
+
+ info->map = devm_regmap_init_mmio(dev, regs, &mcf54415_dac_regmap_config);
+ if (IS_ERR(info->map))
+ return PTR_ERR(info->map);
+
+ info->clk = devm_clk_get_enabled(dev, "dac");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n");
+
+ indio_dev->name = "mcf54415";
+ indio_dev->info = &mcf54415_dac_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &mcf54415_dac_iio_channel;
+ indio_dev->num_channels = 1;
+
+ ret = mcf54415_dac_init(info);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct platform_device_id mcf54415_dac_ids[] = {
+ { .name = "mcfdac" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, mcf54415_dac_ids);
+
+static struct platform_driver mcf54415_dac_driver = {
+ .driver = {
+ .name = "mcf54415_dac",
+ },
+ .probe = mcf54415_dac_probe,
+ .id_table = mcf54415_dac_ids,
+};
+module_platform_driver(mcf54415_dac_driver);
+
+MODULE_AUTHOR("Angelo Dureghello <angelo@kernel-space.org>");
+MODULE_DESCRIPTION("NXP MCF54415 DAC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/mcp47a1.c b/drivers/iio/dac/mcp47a1.c
new file mode 100644
index 000000000000..0bf994aa0e4f
--- /dev/null
+++ b/drivers/iio/dac/mcp47a1.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Microchip MCP47A1 DAC driver
+ *
+ * Copyright (c) 2026 Joshua Crofts <joshua.crofts1@gmail.com>
+ *
+ * Datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+#define MCP47A1_CMD_CODE 0x00
+#define MCP47A1_MAX_STEPS 64
+
+struct mcp47a1_data {
+ struct i2c_client *client;
+ int vref_mV;
+};
+
+static const int mcp47a1_raw_avail[] = { 0, 1, MCP47A1_MAX_STEPS - 1 };
+
+static const struct iio_chan_spec mcp47a1_channel = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static int mcp47a1_write(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mcp47a1_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!in_range(val, 0, MCP47A1_MAX_STEPS))
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(data->client, MCP47A1_CMD_CODE,
+ val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp47a1_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mcp47a1_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_byte_data(data->client, MCP47A1_CMD_CODE);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = data->vref_mV;
+ *val2 = MCP47A1_MAX_STEPS;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp47a1_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *vals = mcp47a1_raw_avail;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mcp47a1_info = {
+ .write_raw = mcp47a1_write,
+ .read_raw = mcp47a1_read,
+ .read_avail = mcp47a1_read_avail,
+};
+
+static int mcp47a1_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct mcp47a1_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->client = client;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+ /* Delay after device exits reset state (see AC/DC characteristics) */
+ fsleep(20);
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read vref\n");
+
+ data->vref_mV = ret / (MICRO / MILLI);
+
+ indio_dev->name = "mcp47a1";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp47a1_info;
+ indio_dev->channels = &mcp47a1_channel;
+ indio_dev->num_channels = 1;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id mcp47a1_of_match[] = {
+ { .compatible = "microchip,mcp47a1" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp47a1_of_match);
+
+static const struct i2c_device_id mcp47a1_id[] = {
+ { .name = "mcp47a1" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp47a1_id);
+
+static struct i2c_driver mcp47a1_driver = {
+ .driver = {
+ .name = "mcp47a1",
+ .of_match_table = mcp47a1_of_match,
+ },
+ .probe = mcp47a1_probe,
+ .id_table = mcp47a1_id,
+};
+module_i2c_driver(mcp47a1_driver);
+
+MODULE_AUTHOR("Joshua Crofts <joshua.crofts1@gmail.com>");
+MODULE_DESCRIPTION("Microchip MCP47A1 DAC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c
index d2e7c930c848..8d8c00cd60e9 100644
--- a/drivers/iio/dac/mcp4821.c
+++ b/drivers/iio/dac/mcp4821.c
@@ -297,12 +297,12 @@ static const struct of_device_id mcp4821_of_table[] = {
MODULE_DEVICE_TABLE(of, mcp4821_of_table);
static const struct spi_device_id mcp4821_id_table[] = {
- { "mcp4801", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801]},
- { "mcp4802", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802]},
- { "mcp4811", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811]},
- { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]},
- { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]},
- { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]},
+ { .name = "mcp4801", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801] },
+ { .name = "mcp4802", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802] },
+ { .name = "mcp4811", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811] },
+ { .name = "mcp4812", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812] },
+ { .name = "mcp4821", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821] },
+ { .name = "mcp4822", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822] },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp4821_id_table);
diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c
index 74f338afcab9..15072a9081ff 100644
--- a/drivers/iio/dac/mcp4922.c
+++ b/drivers/iio/dac/mcp4922.c
@@ -157,10 +157,10 @@ static int mcp4922_probe(struct spi_device *spi)
static const struct spi_device_id mcp4922_id[] = {
- {"mcp4902", ID_MCP4902},
- {"mcp4912", ID_MCP4912},
- {"mcp4921", ID_MCP4921},
- {"mcp4922", ID_MCP4922},
+ { .name = "mcp4902", .driver_data = ID_MCP4902 },
+ { .name = "mcp4912", .driver_data = ID_MCP4912 },
+ { .name = "mcp4921", .driver_data = ID_MCP4921 },
+ { .name = "mcp4922", .driver_data = ID_MCP4922 },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp4922_id);
diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c
index 68e66e7286d8..127ccc2c4a22 100644
--- a/drivers/iio/dac/rohm-bd79703.c
+++ b/drivers/iio/dac/rohm-bd79703.c
@@ -217,10 +217,10 @@ static int bd79703_probe(struct spi_device *spi)
}
static const struct spi_device_id bd79703_id[] = {
- { "bd79700", (kernel_ulong_t)&bd79700_chip_data },
- { "bd79701", (kernel_ulong_t)&bd79701_chip_data },
- { "bd79702", (kernel_ulong_t)&bd79702_chip_data },
- { "bd79703", (kernel_ulong_t)&bd79703_chip_data },
+ { .name = "bd79700", .driver_data = (kernel_ulong_t)&bd79700_chip_data },
+ { .name = "bd79701", .driver_data = (kernel_ulong_t)&bd79701_chip_data },
+ { .name = "bd79702", .driver_data = (kernel_ulong_t)&bd79702_chip_data },
+ { .name = "bd79703", .driver_data = (kernel_ulong_t)&bd79703_chip_data },
{ }
};
MODULE_DEVICE_TABLE(spi, bd79703_id);
diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c
index 6e62c1f302c8..afa7d9bad5de 100644
--- a/drivers/iio/dac/ti-dac082s085.c
+++ b/drivers/iio/dac/ti-dac082s085.c
@@ -334,12 +334,12 @@ static const struct of_device_id ti_dac_of_id[] = {
MODULE_DEVICE_TABLE(of, ti_dac_of_id);
static const struct spi_device_id ti_dac_spi_id[] = {
- { "dac082s085", dual_8bit },
- { "dac102s085", dual_10bit },
- { "dac122s085", dual_12bit },
- { "dac084s085", quad_8bit },
- { "dac104s085", quad_10bit },
- { "dac124s085", quad_12bit },
+ { .name = "dac082s085", .driver_data = dual_8bit },
+ { .name = "dac102s085", .driver_data = dual_10bit },
+ { .name = "dac122s085", .driver_data = dual_12bit },
+ { .name = "dac084s085", .driver_data = quad_8bit },
+ { .name = "dac104s085", .driver_data = quad_10bit },
+ { .name = "dac124s085", .driver_data = quad_12bit },
{ }
};
MODULE_DEVICE_TABLE(spi, ti_dac_spi_id);
diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c
index 5c1c5213962f..e5843bd35259 100644
--- a/drivers/iio/dac/ti-dac7311.c
+++ b/drivers/iio/dac/ti-dac7311.c
@@ -311,9 +311,9 @@ static const struct of_device_id ti_dac_of_id[] = {
MODULE_DEVICE_TABLE(of, ti_dac_of_id);
static const struct spi_device_id ti_dac_spi_id[] = {
- { "dac5311", ID_DAC5311 },
- { "dac6311", ID_DAC6311 },
- { "dac7311", ID_DAC7311 },
+ { .name = "dac5311", .driver_data = ID_DAC5311 },
+ { .name = "dac6311", .driver_data = ID_DAC6311 },
+ { .name = "dac7311", .driver_data = ID_DAC7311 },
{ }
};
MODULE_DEVICE_TABLE(spi, ti_dac_spi_id);
diff --git a/drivers/iio/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c
index c308eca02b88..9065663cd609 100644
--- a/drivers/iio/dac/ti-dac7612.c
+++ b/drivers/iio/dac/ti-dac7612.c
@@ -165,7 +165,7 @@ static int dac7612_probe(struct spi_device *spi)
}
static const struct spi_device_id dac7612_id[] = {
- {"ti-dac7612"},
+ { .name = "ti-dac7612" },
{ }
};
MODULE_DEVICE_TABLE(spi, dac7612_id);
diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c
index 8bcd298e1f3d..b88689e60cc8 100644
--- a/drivers/iio/filter/admv8818.c
+++ b/drivers/iio/filter/admv8818.c
@@ -789,7 +789,7 @@ static int admv8818_probe(struct spi_device *spi)
}
static const struct spi_device_id admv8818_id[] = {
- { "admv8818", 0 },
+ { .name = "admv8818" },
{ }
};
MODULE_DEVICE_TABLE(spi, admv8818_id);
diff --git a/drivers/iio/flow/Kconfig b/drivers/iio/flow/Kconfig
new file mode 100644
index 000000000000..e0e1a8e3654a
--- /dev/null
+++ b/drivers/iio/flow/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Liquid / gas flow sensor drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Flow sensors"
+
+config SENSIRION_SLF3S
+ tristate "Sensirion SLF3S liquid flow sensor"
+ depends on I2C
+ select CRC8
+ help
+ Say yes here to build support for the Sensirion SLF3S family of
+ digital liquid-flow sensors (SLF3S-0600F, SLF3S-1300F and
+ SLF3S-4000B). The driver reports the volumetric flow rate and the
+ embedded temperature reading via the standard IIO interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called slf3s.
+
+endmenu
diff --git a/drivers/iio/flow/Makefile b/drivers/iio/flow/Makefile
new file mode 100644
index 000000000000..3cf4ab95c69c
--- /dev/null
+++ b/drivers/iio/flow/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for industrial I/O flow sensor drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_SENSIRION_SLF3S) += slf3s.o
diff --git a/drivers/iio/flow/slf3s.c b/drivers/iio/flow/slf3s.c
new file mode 100644
index 000000000000..dfa7c1409045
--- /dev/null
+++ b/drivers/iio/flow/slf3s.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sensirion SLF3S liquid flow sensor driver.
+ *
+ * Supports the SLF3S-0600F, SLF3S-1300F and SLF3S-4000B liquid-flow
+ * sensors over I2C. Each measurement frame returns a 16-bit signed
+ * flow value, a 16-bit signed temperature value and a status word,
+ * each protected by a CRC-8 byte.
+ *
+ * The active calibration medium (water or isopropyl alcohol) is
+ * runtime-switchable via the in_volumeflow_medium sysfs attribute and
+ * defaults to water.
+ *
+ * Datasheet: https://sensirion.com/products/catalog/SLF3S-0600F/
+ *
+ * Copyright (C) 2026 CMBlu Energy GmbH
+ * Author: Wadim Mueller <wafgo01@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+#define SLF3S_CRC8_POLY 0x31
+#define SLF3S_CRC8_INIT 0xff
+
+#define SLF3S_PRODUCT_ID_LEN 18
+#define SLF3S_PRODUCT_FAMILY_BYTE 1
+#define SLF3S_PRODUCT_SUBTYPE_BYTE 3
+#define SLF3S_PRODUCT_FAMILY_ID 0x03
+
+/* Datasheet section 2.2: tPU = 25 ms max from power-on to first cmd. */
+#define SLF3S_POWER_UP_DELAY_US (25 * USEC_PER_MSEC)
+/* Datasheet section 2.2: tw = 60 ms typical until first valid sample. */
+#define SLF3S_MEAS_START_DELAY_US (60 * USEC_PER_MSEC)
+
+static const u8 slf3s_cmd_prep_pid[] = { 0x36, 0x7c };
+static const u8 slf3s_cmd_read_pid[] = { 0xe1, 0x02 };
+static const u8 slf3s_cmd_start_water[] = { 0x36, 0x08 };
+static const u8 slf3s_cmd_start_ipa[] = { 0x36, 0x15 };
+static const u8 slf3s_cmd_stop_meas[] = { 0x3f, 0xf9 };
+
+enum slf3s_medium {
+ SLF3S_MEDIUM_WATER,
+ SLF3S_MEDIUM_IPA,
+};
+
+static const char * const slf3s_medium_modes[] = {
+ [SLF3S_MEDIUM_WATER] = "water",
+ [SLF3S_MEDIUM_IPA] = "ipa",
+};
+
+enum slf3s_variant_id {
+ SLF3S_0600F,
+ SLF3S_1300F,
+ SLF3S_4000B,
+};
+
+/**
+ * struct slf3s_variant - per-variant calibration constants
+ * @sub_type: product-info sub-type byte returned by the sensor
+ * @name: name reported via @iio_dev.name
+ * @scale: flow scale in l/s per LSB
+ */
+struct slf3s_variant {
+ u8 sub_type;
+ const char *name;
+ struct s32_fract scale;
+};
+
+static const struct slf3s_variant slf3s_variants[] = {
+ [SLF3S_0600F] = {
+ .sub_type = 0x03,
+ .name = "slf3s-0600f",
+ .scale = { .numerator = 1, .denominator = 600 * MICRO },
+ },
+ [SLF3S_1300F] = {
+ .sub_type = 0x02,
+ .name = "slf3s-1300f",
+ .scale = { .numerator = 1, .denominator = 30 * MICRO },
+ },
+ [SLF3S_4000B] = {
+ .sub_type = 0x05,
+ .name = "slf3s-4000b",
+ .scale = { .numerator = 1, .denominator = 1920 * MILLI },
+ },
+};
+
+/**
+ * struct slf3s_data - per-device state
+ * @client: I2C client this instance is bound to
+ * @vdd: supply regulator, disabled while suspended
+ * @variant: pointer into @slf3s_variants for the detected device
+ * @medium: currently active calibration medium
+ * @lock: serialises the multi-step command/response exchanges
+ * @crc_table: pre-computed CRC-8 lookup table for SLF3S_CRC8_POLY
+ */
+struct slf3s_data {
+ struct i2c_client *client;
+ struct regulator *vdd;
+ const struct slf3s_variant *variant;
+ enum slf3s_medium medium;
+ struct mutex lock;
+ u8 crc_table[CRC8_TABLE_SIZE];
+};
+
+static int slf3s_send_cmd(struct i2c_client *client, const u8 *cmd)
+{
+ int ret;
+
+ ret = i2c_master_send(client, cmd, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ return 0;
+}
+
+/* Start continuous measurement and wait until the first sample is valid. */
+static int slf3s_start_meas(struct slf3s_data *sf, enum slf3s_medium medium)
+{
+ const u8 *cmd = (medium == SLF3S_MEDIUM_IPA) ? slf3s_cmd_start_ipa
+ : slf3s_cmd_start_water;
+ int ret;
+
+ ret = slf3s_send_cmd(sf->client, cmd);
+ if (ret)
+ return ret;
+
+ fsleep(SLF3S_MEAS_START_DELAY_US);
+
+ return 0;
+}
+
+static bool slf3s_crc_valid(const struct slf3s_data *sf, const u8 *block)
+{
+ return crc8(sf->crc_table, block, 2, SLF3S_CRC8_INIT) == block[2];
+}
+
+/*
+ * Read the product-info block and pick the matching variant. The
+ * sub-type byte returned by the sensor is the source of truth; a
+ * DT-supplied compatible only seeds an initial guess and is overridden
+ * on mismatch (with an informational message so misconfigured device
+ * trees are easy to spot).
+ *
+ * Bus / CRC failures are real errors and fail probe. An unknown
+ * sub-type byte falls back to the variant named in the device tree /
+ * I2C table, so a drop-in replacement part that lists one of the known
+ * compatibles keeps working on an older kernel that does not know its
+ * sub-type yet. Without any match data probe fails since no
+ * meaningful scale can be published.
+ */
+static int slf3s_detect_variant(struct slf3s_data *sf)
+{
+ struct i2c_client *client = sf->client;
+ u8 buf[SLF3S_PRODUCT_ID_LEN];
+ int ret;
+
+ ret = slf3s_send_cmd(client, slf3s_cmd_prep_pid);
+ if (ret)
+ return ret;
+
+ ret = slf3s_send_cmd(client, slf3s_cmd_read_pid);
+ if (ret)
+ return ret;
+
+ ret = i2c_master_recv(client, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(buf))
+ return -EIO;
+
+ for (unsigned int i = 0; i < SLF3S_PRODUCT_ID_LEN; i += 3) {
+ if (!slf3s_crc_valid(sf, &buf[i]))
+ return -EIO;
+ }
+
+ if (buf[SLF3S_PRODUCT_FAMILY_BYTE] != SLF3S_PRODUCT_FAMILY_ID)
+ dev_info(&client->dev,
+ "unexpected family byte 0x%02x (expected 0x%02x)\n",
+ buf[SLF3S_PRODUCT_FAMILY_BYTE],
+ SLF3S_PRODUCT_FAMILY_ID);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(slf3s_variants); i++) {
+ if (buf[SLF3S_PRODUCT_SUBTYPE_BYTE] !=
+ slf3s_variants[i].sub_type)
+ continue;
+
+ if (sf->variant && sf->variant != &slf3s_variants[i])
+ dev_info(&client->dev,
+ "DT compatible says %s but sensor reports %s; using the latter\n",
+ sf->variant->name,
+ slf3s_variants[i].name);
+
+ sf->variant = &slf3s_variants[i];
+
+ return 0;
+ }
+
+ if (sf->variant) {
+ dev_warn(&client->dev,
+ "unknown SLF3S sub-type 0x%02x, assuming %s\n",
+ buf[SLF3S_PRODUCT_SUBTYPE_BYTE], sf->variant->name);
+ return 0;
+ }
+
+ dev_err(&client->dev, "unknown SLF3S sub-type 0x%02x\n",
+ buf[SLF3S_PRODUCT_SUBTYPE_BYTE]);
+
+ return -ENODEV;
+}
+
+static int slf3s_read_sample(struct slf3s_data *sf, int *flow, int *temp)
+{
+ /*
+ * A measurement frame is flow, temperature and a signaling-flags
+ * word, each followed by a CRC byte. Only flow and temperature are
+ * used, so the read is stopped after their two words (6 bytes).
+ */
+ u8 buf[6];
+ int ret;
+
+ ret = i2c_master_recv(sf->client, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(buf))
+ return -EIO;
+
+ for (unsigned int i = 0; i < sizeof(buf); i += 3) {
+ if (!slf3s_crc_valid(sf, &buf[i]))
+ return -EIO;
+ }
+
+ *flow = sign_extend32(get_unaligned_be16(&buf[0]), 15);
+ *temp = sign_extend32(get_unaligned_be16(&buf[3]), 15);
+
+ return 0;
+}
+
+static int slf3s_get_medium(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct slf3s_data *sf = iio_priv(indio_dev);
+
+ return sf->medium;
+}
+
+static int slf3s_set_medium(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&sf->lock);
+
+ ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas);
+ if (ret)
+ return ret;
+
+ ret = slf3s_start_meas(sf, mode);
+ if (ret) {
+ /*
+ * Try to restart with the previous medium so the sensor is
+ * not left idle, which would fail all subsequent reads.
+ */
+ if (slf3s_start_meas(sf, sf->medium))
+ dev_warn(&sf->client->dev,
+ "failed to restart measurement, reads will fail until a medium is set\n");
+ return ret;
+ }
+
+ sf->medium = mode;
+
+ return 0;
+}
+
+static const struct iio_enum slf3s_medium_enum = {
+ .items = slf3s_medium_modes,
+ .num_items = ARRAY_SIZE(slf3s_medium_modes),
+ .get = slf3s_get_medium,
+ .set = slf3s_set_medium,
+};
+
+static const struct iio_chan_spec_ext_info slf3s_ext_info[] = {
+ IIO_ENUM("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum),
+ IIO_ENUM_AVAILABLE("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum),
+ { }
+};
+
+static const struct iio_chan_spec slf3s_channels[] = {
+ {
+ .type = IIO_VOLUMEFLOW,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = slf3s_ext_info,
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int slf3s_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int flow, temp, ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ scoped_guard(mutex, &sf->lock)
+ ret = slf3s_read_sample(sf, &flow, &temp);
+ if (ret)
+ return ret;
+
+ *val = (chan->type == IIO_VOLUMEFLOW) ? flow : temp;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLUMEFLOW) {
+ /*
+ * The variant scale is the flow per LSB in l/s, but
+ * IIO reports volume flow in m^3/s (1 l = 1e-3 m^3).
+ * These values are tiny (~1.67e-12 m^3/s for the
+ * SLF3S-0600F), so emit a 64-bit fixed-point value with
+ * femto (1e-15) resolution to preserve precision.
+ * Converting l/s to m^3/s (/ MILLI) and scaling to femto
+ * (* FEMTO) leaves a net * (FEMTO / MILLI) factor.
+ */
+ const struct slf3s_variant *v = sf->variant;
+ s64 num = (s64)v->scale.numerator * (FEMTO / MILLI);
+ s64 scale = DIV_S64_ROUND_CLOSEST(num,
+ v->scale.denominator);
+
+ iio_val_s64_decompose(scale, val, val2);
+
+ return IIO_VAL_DECIMAL64_FEMTO;
+ }
+ /* Temperature LSB = 1/200 degC; IIO_TEMP wants milli-degC. */
+ *val = MILLIDEGREE_PER_DEGREE / 200;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info slf3s_info = {
+ .read_raw = slf3s_read_raw,
+};
+
+static void slf3s_stop_meas(void *data)
+{
+ struct slf3s_data *sf = data;
+
+ slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas);
+}
+
+static void slf3s_disable_vdd(void *data)
+{
+ struct slf3s_data *sf = data;
+
+ regulator_disable(sf->vdd);
+}
+
+static int slf3s_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct slf3s_data *sf;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sf));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ sf = iio_priv(indio_dev);
+ sf->client = client;
+ i2c_set_clientdata(client, indio_dev);
+ sf->variant = i2c_get_match_data(client);
+ sf->medium = SLF3S_MEDIUM_WATER;
+ crc8_populate_msb(sf->crc_table, SLF3S_CRC8_POLY);
+
+ ret = devm_mutex_init(dev, &sf->lock);
+ if (ret)
+ return ret;
+
+ sf->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(sf->vdd))
+ return dev_err_probe(dev, PTR_ERR(sf->vdd),
+ "failed to get vdd supply\n");
+
+ ret = regulator_enable(sf->vdd);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable vdd supply\n");
+
+ ret = devm_add_action_or_reset(dev, slf3s_disable_vdd, sf);
+ if (ret)
+ return ret;
+
+ fsleep(SLF3S_POWER_UP_DELAY_US);
+
+ /*
+ * The sensor may still be in continuous measurement mode from a
+ * previous boot (warm reboot / kexec); in that case it would NACK
+ * the product-id command below. Stop it first and ignore the error
+ * if it was already idle.
+ */
+ slf3s_send_cmd(client, slf3s_cmd_stop_meas);
+
+ ret = slf3s_detect_variant(sf);
+ if (ret)
+ return dev_err_probe(dev, ret, "product info read failed\n");
+
+ ret = slf3s_start_meas(sf, sf->medium);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to start measurement\n");
+
+ ret = devm_add_action_or_reset(dev, slf3s_stop_meas, sf);
+ if (ret)
+ return ret;
+
+ indio_dev->name = sf->variant->name;
+ indio_dev->channels = slf3s_channels;
+ indio_dev->num_channels = ARRAY_SIZE(slf3s_channels);
+ indio_dev->info = &slf3s_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+/*
+ * The sensor has no low-power state of its own, so stop the measurement
+ * and cut the supply while suspended. Resume powers it back up, waits
+ * out the power-up time and restarts with the medium that was active
+ * before.
+ */
+static int slf3s_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&sf->lock);
+
+ ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas);
+ if (ret)
+ return ret;
+
+ return regulator_disable(sf->vdd);
+}
+
+static int slf3s_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct slf3s_data *sf = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&sf->lock);
+
+ ret = regulator_enable(sf->vdd);
+ if (ret)
+ return ret;
+
+ fsleep(SLF3S_POWER_UP_DELAY_US);
+
+ return slf3s_start_meas(sf, sf->medium);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(slf3s_pm_ops, slf3s_suspend, slf3s_resume);
+
+static const struct i2c_device_id slf3s_id[] = {
+ {
+ .name = "slf3s-0600f",
+ .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_0600F],
+ },
+ {
+ .name = "slf3s-1300f",
+ .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_1300F],
+ },
+ {
+ .name = "slf3s-4000b",
+ .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_4000B],
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, slf3s_id);
+
+static const struct of_device_id slf3s_of_match[] = {
+ {
+ .compatible = "sensirion,slf3s-0600f",
+ .data = &slf3s_variants[SLF3S_0600F],
+ },
+ {
+ .compatible = "sensirion,slf3s-1300f",
+ .data = &slf3s_variants[SLF3S_1300F],
+ },
+ {
+ .compatible = "sensirion,slf3s-4000b",
+ .data = &slf3s_variants[SLF3S_4000B],
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, slf3s_of_match);
+
+static struct i2c_driver slf3s_driver = {
+ .driver = {
+ .name = "slf3s",
+ .of_match_table = slf3s_of_match,
+ .pm = pm_sleep_ptr(&slf3s_pm_ops),
+ },
+ .probe = slf3s_probe,
+ .id_table = slf3s_id,
+};
+module_i2c_driver(slf3s_driver);
+
+MODULE_AUTHOR("Wadim Mueller <wafgo01@gmail.com>");
+MODULE_DESCRIPTION("Sensirion SLF3S liquid flow sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index 583cbdf4e8cd..90c6304c4bcd 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -29,6 +29,16 @@ endmenu
menu "Phase-Locked Loop (PLL) frequency synthesizers"
+config ADF41513
+ tristate "Analog Devices ADF41513 PLL Frequency Synthesizer"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADF41513
+ 26.5 GHz Integer-N/Fractional-N PLL Frequency Synthesizer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adf41513.
+
config ADF4350
tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers"
depends on SPI
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index 70d0e0b70e80..53b4d01414d8 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -5,6 +5,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD9523) += ad9523.o
+obj-$(CONFIG_ADF41513) += adf41513.o
obj-$(CONFIG_ADF4350) += adf4350.o
obj-$(CONFIG_ADF4371) += adf4371.o
obj-$(CONFIG_ADF4377) += adf4377.o
diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
index ea4d2763564a..1e393f40a8c8 100644
--- a/drivers/iio/frequency/ad9523.c
+++ b/drivers/iio/frequency/ad9523.c
@@ -990,7 +990,6 @@ static int ad9523_probe(struct spi_device *spi)
if (IS_ERR(st->sync_gpio))
return PTR_ERR(st->sync_gpio);
- spi_set_drvdata(spi, indio_dev);
st->spi = spi;
st->pdata = pdata;
@@ -1009,7 +1008,7 @@ static int ad9523_probe(struct spi_device *spi)
}
static const struct spi_device_id ad9523_id[] = {
- {"ad9523-1", 9523},
+ { .name = "ad9523-1" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad9523_id);
diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41513.c
new file mode 100644
index 000000000000..c497027012ba
--- /dev/null
+++ b/drivers/iio/frequency/adf41513.c
@@ -0,0 +1,1246 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADF41513 SPI PLL Frequency Synthesizer driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kstrtox.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+/* Registers */
+#define ADF41513_REG0 0
+#define ADF41513_REG1 1
+#define ADF41513_REG2 2
+#define ADF41513_REG3 3
+#define ADF41513_REG4 4
+#define ADF41513_REG5 5
+#define ADF41513_REG6 6
+#define ADF41513_REG7 7
+#define ADF41513_REG8 8
+#define ADF41513_REG9 9
+#define ADF41513_REG10 10
+#define ADF41513_REG11 11
+#define ADF41513_REG12 12
+#define ADF41513_REG13 13
+#define ADF41513_REG_NUM 14
+
+#define ADF41513_SYNC_REG0 BIT(ADF41513_REG0)
+#define ADF41513_SYNC_REG1 BIT(ADF41513_REG1)
+#define ADF41513_SYNC_REG2 BIT(ADF41513_REG2)
+#define ADF41513_SYNC_REG3 BIT(ADF41513_REG3)
+#define ADF41513_SYNC_REG4 BIT(ADF41513_REG4)
+#define ADF41513_SYNC_REG5 BIT(ADF41513_REG5)
+#define ADF41513_SYNC_REG6 BIT(ADF41513_REG6)
+#define ADF41513_SYNC_REG7 BIT(ADF41513_REG7)
+#define ADF41513_SYNC_REG9 BIT(ADF41513_REG9)
+#define ADF41513_SYNC_REG11 BIT(ADF41513_REG11)
+#define ADF41513_SYNC_REG12 BIT(ADF41513_REG12)
+#define ADF41513_SYNC_REG13 BIT(ADF41513_REG13)
+#define ADF41513_SYNC_DIFF 0
+#define ADF41513_SYNC_ALL GENMASK(ADF41513_REG13, ADF41513_REG0)
+
+/* REG0 Bit Definitions */
+#define ADF41513_REG0_CTRL_BITS_MSK GENMASK(3, 0)
+#define ADF41513_REG0_INT_MSK GENMASK(19, 4)
+#define ADF41513_REG0_VAR_MOD_MSK BIT(28)
+
+/* REG1 Bit Definitions */
+#define ADF41513_REG1_FRAC1_MSK GENMASK(28, 4)
+#define ADF41513_REG1_DITHER2_MSK BIT(31)
+
+/* REG2 Bit Definitions */
+#define ADF41513_REG2_PHASE_VAL_MSK GENMASK(15, 4)
+#define ADF41513_REG2_PHASE_ADJ_MSK BIT(31)
+
+/* REG3 Bit Definitions */
+#define ADF41513_REG3_FRAC2_MSK GENMASK(27, 4)
+
+/* REG4 Bit Definitions */
+#define ADF41513_REG4_MOD2_MSK GENMASK(27, 4)
+
+/* REG5 Bit Definitions */
+#define ADF41513_REG5_CLK1_DIV_MSK GENMASK(15, 4)
+#define ADF41513_REG5_R_CNT_MSK GENMASK(20, 16)
+#define ADF41513_REG5_REF_DOUBLER_MSK BIT(21)
+#define ADF41513_REG5_RDIV2_MSK BIT(22)
+#define ADF41513_REG5_PRESCALER_MSK BIT(23)
+#define ADF41513_REG5_LSB_P1_MSK BIT(24)
+#define ADF41513_REG5_CP_CURRENT_MSK GENMASK(28, 25)
+#define ADF41513_REG5_DLD_MODES_MSK GENMASK(31, 30)
+
+/* REG6 Bit Definitions */
+#define ADF41513_REG6_COUNTER_RESET_MSK BIT(4)
+#define ADF41513_REG6_CP_TRISTATE_MSK BIT(5)
+#define ADF41513_REG6_POWER_DOWN_MSK BIT(6)
+#define ADF41513_REG6_PD_POLARITY_MSK BIT(7)
+#define ADF41513_REG6_LDP_MSK GENMASK(9, 8)
+#define ADF41513_REG6_CP_TRISTATE_PD_ON_MSK BIT(16)
+#define ADF41513_REG6_SD_RESET_MSK BIT(17)
+#define ADF41513_REG6_LOL_ENABLE_MSK BIT(18)
+#define ADF41513_REG6_ABP_MSK BIT(19)
+#define ADF41513_REG6_INT_MODE_MSK BIT(20)
+#define ADF41513_REG6_BLEED_ENABLE_MSK BIT(22)
+#define ADF41513_REG6_BLEED_POLARITY_MSK BIT(23)
+#define ADF41513_REG6_BLEED_CURRENT_MSK GENMASK(31, 24)
+
+/* REG7 Bit Definitions */
+#define ADF41513_REG7_CLK2_DIV_MSK GENMASK(17, 6)
+#define ADF41513_REG7_CLK_DIV_MODE_MSK GENMASK(19, 18)
+#define ADF41513_REG7_PS_BIAS_MSK GENMASK(21, 20)
+#define ADF41513_REG7_N_DELAY_MSK GENMASK(23, 22)
+#define ADF41513_REG7_LD_CLK_SEL_MSK BIT(26)
+#define ADF41513_REG7_LD_COUNT_MSK GENMASK(29, 27)
+
+/* REG9 Bit Definitions */
+#define ADF41513_REG9_LD_BIAS_MSK GENMASK(31, 30)
+
+/* REG11 Bit Definitions */
+#define ADF41513_REG11_POWER_DOWN_SEL_MSK BIT(31)
+
+/* REG12 Bit Definitions */
+#define ADF41513_REG12_READBACK_SEL_MSK GENMASK(19, 14)
+#define ADF41513_REG12_LE_SELECT_MSK BIT(20)
+#define ADF41513_REG12_MASTER_RESET_MSK BIT(22)
+#define ADF41513_REG12_LOGIC_LEVEL_MSK BIT(27)
+#define ADF41513_REG12_MUXOUT_MSK GENMASK(31, 28)
+
+/* MUXOUT Selection */
+#define ADF41513_MUXOUT_TRISTATE 0x0
+#define ADF41513_MUXOUT_DVDD 0x1
+#define ADF41513_MUXOUT_DGND 0x2
+#define ADF41513_MUXOUT_R_DIV 0x3
+#define ADF41513_MUXOUT_N_DIV 0x4
+#define ADF41513_MUXOUT_DIG_LD 0x6
+#define ADF41513_MUXOUT_SDO 0x7
+#define ADF41513_MUXOUT_READBACK 0x8
+#define ADF41513_MUXOUT_CLK1_DIV 0xA
+#define ADF41513_MUXOUT_R_DIV2 0xD
+#define ADF41513_MUXOUT_N_DIV2 0xE
+
+/* DLD Mode Selection */
+#define ADF41513_DLD_TRISTATE 0x0
+#define ADF41513_DLD_DIG_LD 0x1
+#define ADF41513_DLD_LOW 0x2
+#define ADF41513_DLD_HIGH 0x3
+
+/* Prescaler Selection */
+#define ADF41513_PRESCALER_4_5 0
+#define ADF41513_PRESCALER_8_9 1
+#define ADF41513_PRESCALER_AUTO 2
+
+/* CLK Divider mode */
+#define ADF41513_CLK_DIV_MODE_OFF 0
+#define ADF41513_CLK_DIV_MODE_PHASE_RESYNC 2
+
+/* Specifications */
+#define ADF41510_MAX_RF_FREQ_HZ (10ULL * HZ_PER_GHZ)
+#define ADF41513_MIN_RF_FREQ_HZ (1ULL * HZ_PER_GHZ)
+#define ADF41513_MAX_RF_FREQ_HZ (26500ULL * HZ_PER_MHZ)
+
+#define ADF41513_MIN_REF_FREQ_HZ (10 * HZ_PER_MHZ)
+#define ADF41513_MAX_REF_FREQ_HZ (800 * HZ_PER_MHZ)
+#define ADF41513_MAX_REF_FREQ_DOUBLER_HZ (225 * HZ_PER_MHZ)
+
+#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ (250ULL * MEGA * MICROHZ_PER_HZ)
+#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ (125ULL * MEGA * MICROHZ_PER_HZ)
+#define ADF41513_MAX_FREQ_RESOLUTION_UHZ (100ULL * KILO * MICROHZ_PER_HZ)
+
+#define ADF41513_MIN_INT_4_5 20
+#define ADF41513_MAX_INT_4_5 511
+#define ADF41513_MIN_INT_8_9 64
+#define ADF41513_MAX_INT_8_9 1023
+
+#define ADF41513_MIN_INT_FRAC_4_5 23
+#define ADF41513_MIN_INT_FRAC_8_9 75
+
+#define ADF41513_MIN_R_CNT 1
+#define ADF41513_MAX_R_CNT 32
+
+#define ADF41513_MIN_R_SET 1800
+#define ADF41513_DEFAULT_R_SET 2700
+#define ADF41513_MAX_R_SET 10000
+
+#define ADF41513_MIN_CP_VOLTAGE_mV 810
+#define ADF41513_DEFAULT_CP_VOLTAGE_mV 6480
+#define ADF41513_MAX_CP_VOLTAGE_mV 12960
+
+#define ADF41513_MIN_CP_CURRENT_uA 81
+#define ADF41513_MAX_CP_CURRENT_uA 7200
+
+#define ADF41513_LD_COUNT_FAST_MIN 2
+#define ADF41513_LD_COUNT_FAST_LIMIT 64
+#define ADF41513_LD_COUNT_MIN 64
+#define ADF41513_LD_COUNT_MAX 8192
+
+#define ADF41513_FIXED_MODULUS BIT(25)
+#define ADF41513_MAX_MOD2 (BIT(24) - 1)
+#define ADF41513_MAX_PHASE_VAL (BIT(12) - 1)
+#define ADF41513_MAX_CLK_DIVIDER (BIT(12) - 1)
+
+#define ADF41513_HZ_DECIMAL_SCALE 6
+#define ADF41513_PS_BIAS_INIT 0x2
+#define ADF41513_MAX_PHASE_MICRORAD ((2 * 314159265UL) / 100)
+
+enum adf41513_pll_mode {
+ ADF41513_MODE_INVALID,
+ ADF41513_MODE_INTEGER_N,
+ ADF41513_MODE_FIXED_MODULUS,
+ ADF41513_MODE_VARIABLE_MODULUS,
+};
+
+struct adf41513_chip_info {
+ const char *name;
+ u64 max_rf_freq_hz;
+ bool has_prescaler_8_9;
+};
+
+struct adf41513_data {
+ u64 power_up_frequency_hz;
+ u64 freq_resolution_uhz;
+ u32 phase_resync_period_ns;
+ u32 charge_pump_voltage_mv;
+ u32 lock_detect_count;
+
+ u8 ref_div_factor;
+ bool ref_doubler_en;
+ bool ref_div2_en;
+ bool phase_detector_polarity;
+
+ bool logic_lvl_1v8_en;
+ bool le_sync_en;
+};
+
+struct adf41513_pll_settings {
+ enum adf41513_pll_mode mode;
+
+ /* reference path parameters */
+ u8 r_counter;
+ u8 ref_doubler;
+ u8 ref_div2;
+ u8 prescaler;
+
+ /* frequency parameters */
+ u64 target_frequency_uhz;
+ u64 actual_frequency_uhz;
+ u64 pfd_frequency_uhz;
+
+ /* pll parameters */
+ u32 frac1;
+ u32 frac2;
+ u32 mod2;
+ u16 int_val;
+};
+
+struct adf41513_state {
+ const struct adf41513_chip_info *chip_info;
+ struct spi_device *spi;
+ struct gpio_desc *lock_detect;
+ struct clk *ref_clk;
+ u32 ref_freq_hz;
+
+ /*
+ * Lock for accessing device registers. Some operations require
+ * multiple consecutive R/W operations, during which the device
+ * shouldn't be interrupted. The buffers are also shared across
+ * all operations so need to be protected on stand alone reads and
+ * writes.
+ */
+ struct mutex lock;
+
+ /* Cached register values */
+ u32 regs[ADF41513_REG_NUM];
+ u32 regs_hw[ADF41513_REG_NUM];
+
+ struct adf41513_data data;
+ struct adf41513_pll_settings settings;
+
+ bool powerdown;
+};
+
+static const u16 adf41513_ld_window_x10_ns[] = {
+ 9, 12, 16, 17, 21, 28, 29, 35, /* 0 - 7 */
+ 43, 47, 49, 52, 70, 79, 115, /* 8 - 14 */
+};
+
+static const u8 adf41513_ldp_bias[] = {
+ 0xC, 0xD, 0xE, 0x8, 0x9, 0x4, 0xA, 0x5, /* 0 - 7 */
+ 0x0, 0x6, 0xB, 0x1, 0x2, 0x7, 0x3, /* 8 - 14 */
+};
+
+static const char * const adf41513_power_supplies[] = {
+ "avdd1", "avdd2", "avdd3", "avdd4", "avdd5", "vp",
+};
+
+static int adf41513_sync_config(struct adf41513_state *st, u16 sync_mask)
+{
+ __be32 d32;
+ int ret;
+
+ /* write registers in reverse order (R13 to R0)*/
+ for (int i = ADF41513_REG13; i >= ADF41513_REG0; i--) {
+ if (st->regs_hw[i] == st->regs[i] && !(sync_mask & BIT(i)))
+ continue;
+
+ d32 = cpu_to_be32(st->regs[i] | i);
+ ret = spi_write_then_read(st->spi, &d32, sizeof(d32), NULL, 0);
+ if (ret < 0)
+ return ret;
+ st->regs_hw[i] = st->regs[i];
+ dev_dbg(&st->spi->dev, "REG%d <= 0x%08X\n", i, st->regs[i] | i);
+ }
+
+ return 0;
+}
+
+static u64 adf41513_pll_get_rate(struct adf41513_state *st)
+{
+ struct adf41513_pll_settings *cfg = &st->settings;
+
+ if (cfg->mode != ADF41513_MODE_INVALID)
+ return cfg->actual_frequency_uhz;
+
+ /* get pll settings from regs_hw */
+ cfg->int_val = FIELD_GET(ADF41513_REG0_INT_MSK, st->regs_hw[ADF41513_REG0]);
+ cfg->frac1 = FIELD_GET(ADF41513_REG1_FRAC1_MSK, st->regs_hw[ADF41513_REG1]);
+ cfg->frac2 = FIELD_GET(ADF41513_REG3_FRAC2_MSK, st->regs_hw[ADF41513_REG3]);
+ cfg->mod2 = FIELD_GET(ADF41513_REG4_MOD2_MSK, st->regs_hw[ADF41513_REG4]);
+ cfg->r_counter = FIELD_GET(ADF41513_REG5_R_CNT_MSK, st->regs_hw[ADF41513_REG5]);
+ cfg->ref_doubler = FIELD_GET(ADF41513_REG5_REF_DOUBLER_MSK, st->regs_hw[ADF41513_REG5]);
+ cfg->ref_div2 = FIELD_GET(ADF41513_REG5_RDIV2_MSK, st->regs_hw[ADF41513_REG5]);
+ cfg->prescaler = FIELD_GET(ADF41513_REG5_PRESCALER_MSK, st->regs_hw[ADF41513_REG5]);
+
+ if (!cfg->mod2)
+ cfg->mod2 = 1;
+ if (!cfg->r_counter)
+ cfg->r_counter = ADF41513_MAX_R_CNT;
+
+ /* calculate pfd frequency */
+ cfg->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO;
+ if (cfg->ref_doubler)
+ cfg->pfd_frequency_uhz <<= 1;
+ if (cfg->ref_div2)
+ cfg->pfd_frequency_uhz >>= 1;
+ cfg->pfd_frequency_uhz = div_u64(cfg->pfd_frequency_uhz, cfg->r_counter);
+ cfg->actual_frequency_uhz = (u64)cfg->int_val * cfg->pfd_frequency_uhz;
+
+ /* check if int mode is selected */
+ if (FIELD_GET(ADF41513_REG6_INT_MODE_MSK, st->regs_hw[ADF41513_REG6])) {
+ cfg->mode = ADF41513_MODE_INTEGER_N;
+ } else {
+ cfg->actual_frequency_uhz += mul_u64_u32_div(cfg->pfd_frequency_uhz,
+ cfg->frac1,
+ ADF41513_FIXED_MODULUS);
+
+ /* check if variable modulus is selected */
+ if (FIELD_GET(ADF41513_REG0_VAR_MOD_MSK, st->regs_hw[ADF41513_REG0])) {
+ cfg->actual_frequency_uhz +=
+ mul_u64_u64_div_u64(cfg->frac2,
+ cfg->pfd_frequency_uhz,
+ (u64)cfg->mod2 * ADF41513_FIXED_MODULUS);
+
+ cfg->mode = ADF41513_MODE_VARIABLE_MODULUS;
+ } else {
+ /* LSB_P1 offset */
+ if (!FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs_hw[ADF41513_REG5]))
+ cfg->actual_frequency_uhz +=
+ div_u64(cfg->pfd_frequency_uhz,
+ 2 * ADF41513_FIXED_MODULUS);
+ cfg->mode = ADF41513_MODE_FIXED_MODULUS;
+ }
+ }
+
+ cfg->target_frequency_uhz = cfg->actual_frequency_uhz;
+
+ return cfg->actual_frequency_uhz;
+}
+
+static int adf41513_calc_pfd_frequency(struct adf41513_state *st,
+ struct adf41513_pll_settings *result,
+ u64 fpfd_limit_uhz)
+{
+ result->ref_div2 = st->data.ref_div2_en;
+ result->ref_doubler = st->data.ref_doubler_en;
+ result->r_counter = st->data.ref_div_factor - 1;
+
+ do {
+ result->r_counter++;
+ /* f_PFD = REF_IN × ((1 + D)/(R × (1 + T))) */
+ result->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO;
+ if (result->ref_doubler)
+ result->pfd_frequency_uhz <<= 1;
+ if (result->ref_div2)
+ result->pfd_frequency_uhz >>= 1;
+ result->pfd_frequency_uhz = div_u64(result->pfd_frequency_uhz,
+ result->r_counter);
+ } while (result->pfd_frequency_uhz > fpfd_limit_uhz);
+
+ if (result->r_counter > ADF41513_MAX_R_CNT) {
+ dev_err(&st->spi->dev, "Cannot optimize PFD frequency\n");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static int adf41513_calc_integer_n(struct adf41513_state *st,
+ struct adf41513_pll_settings *result)
+{
+ u32 max_int = st->chip_info->has_prescaler_8_9 ?
+ ADF41513_MAX_INT_8_9 : ADF41513_MAX_INT_4_5;
+ u64 freq_error_uhz;
+ u32 int_val = div64_u64_rem(result->target_frequency_uhz, result->pfd_frequency_uhz,
+ &freq_error_uhz);
+
+ /* check if freq error is within a tolerance of 1/2 resolution */
+ if (freq_error_uhz > (result->pfd_frequency_uhz >> 1) && int_val < max_int) {
+ int_val++;
+ freq_error_uhz = result->pfd_frequency_uhz - freq_error_uhz;
+ }
+
+ if (freq_error_uhz > st->data.freq_resolution_uhz)
+ return -ERANGE;
+
+ /* set prescaler */
+ if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_8_9 &&
+ int_val <= ADF41513_MAX_INT_8_9)
+ result->prescaler = 1;
+ else if (int_val >= ADF41513_MIN_INT_4_5 && int_val <= ADF41513_MAX_INT_4_5)
+ result->prescaler = 0;
+ else
+ return -ERANGE;
+
+ result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz;
+ result->mode = ADF41513_MODE_INTEGER_N;
+ result->int_val = int_val;
+ result->frac1 = 0;
+ result->frac2 = 0;
+ result->mod2 = 0;
+
+ return 0;
+}
+
+static int adf41513_calc_fixed_mod(struct adf41513_state *st,
+ struct adf41513_pll_settings *result)
+{
+ u64 resolution_uhz = div_u64(result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS);
+ u64 target_frequency_uhz = result->target_frequency_uhz;
+ u64 freq_error_uhz;
+ u32 int_val, frac1;
+ bool lsb_p1_offset = !FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs[ADF41513_REG5]);
+
+ /* LSB_P1 adds a frequency offset of f_pfd/2^26 */
+ if (lsb_p1_offset)
+ target_frequency_uhz -= resolution_uhz >> 1;
+
+ int_val = div64_u64_rem(target_frequency_uhz, result->pfd_frequency_uhz,
+ &freq_error_uhz);
+
+ if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 &&
+ int_val <= ADF41513_MAX_INT_8_9)
+ result->prescaler = 1;
+ else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5)
+ result->prescaler = 0;
+ else
+ return -ERANGE;
+
+ /* compute frac1 and fixed modulus error */
+ frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS,
+ result->pfd_frequency_uhz);
+ freq_error_uhz -= mul_u64_u32_div(result->pfd_frequency_uhz, frac1,
+ ADF41513_FIXED_MODULUS);
+
+ /* check if freq error is within a tolerance of 1/2 resolution */
+ if (freq_error_uhz > (resolution_uhz >> 1) && frac1 < (ADF41513_FIXED_MODULUS - 1)) {
+ frac1++;
+ freq_error_uhz = freq_error_uhz < resolution_uhz ?
+ resolution_uhz - freq_error_uhz : 0;
+ }
+
+ if (freq_error_uhz > st->data.freq_resolution_uhz)
+ return -ERANGE;
+
+ /* integer part */
+ result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz;
+ /* fractional part */
+ if (lsb_p1_offset)
+ result->actual_frequency_uhz += (resolution_uhz >> 1);
+ result->actual_frequency_uhz += mul_u64_u32_div(result->pfd_frequency_uhz, frac1,
+ ADF41513_FIXED_MODULUS);
+ result->mode = ADF41513_MODE_FIXED_MODULUS;
+ result->int_val = int_val;
+ result->frac1 = frac1;
+ result->frac2 = 0;
+ result->mod2 = 0;
+
+ return 0;
+}
+
+static int adf41513_calc_variable_mod(struct adf41513_state *st,
+ struct adf41513_pll_settings *result)
+{
+ u64 freq_error_uhz, mod2;
+ u32 frac1, frac2;
+ u32 int_val = div64_u64_rem(result->target_frequency_uhz,
+ result->pfd_frequency_uhz, &freq_error_uhz);
+
+ if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 &&
+ int_val <= ADF41513_MAX_INT_8_9)
+ result->prescaler = 1;
+ else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5)
+ result->prescaler = 0;
+ else
+ return -ERANGE;
+
+ /* calculate required mod2 based on target resolution / 2 */
+ mod2 = DIV64_U64_ROUND_CLOSEST(result->pfd_frequency_uhz << 1,
+ st->data.freq_resolution_uhz * ADF41513_FIXED_MODULUS);
+ /* ensure mod2 is at least 2 for meaningful operation */
+ mod2 = clamp(mod2, 2, ADF41513_MAX_MOD2);
+
+ /* calculate frac1 and frac2 */
+ frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS,
+ result->pfd_frequency_uhz);
+ frac2 = mul_u64_u64_div_u64(freq_error_uhz, mod2 * ADF41513_FIXED_MODULUS,
+ result->pfd_frequency_uhz) - mod2 * frac1;
+
+ /* integer part */
+ result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz;
+ /* fractional part */
+ result->actual_frequency_uhz += mul_u64_u64_div_u64(mod2 * frac1 + frac2,
+ result->pfd_frequency_uhz,
+ mod2 * ADF41513_FIXED_MODULUS);
+ result->mode = ADF41513_MODE_VARIABLE_MODULUS;
+ result->int_val = int_val;
+ result->frac1 = frac1;
+ result->frac2 = frac2;
+ result->mod2 = mod2;
+
+ return 0;
+}
+
+static int adf41513_calc_pll_settings(struct adf41513_state *st,
+ struct adf41513_pll_settings *result,
+ u64 rf_out_uhz)
+{
+ u64 max_rf_freq_uhz = st->chip_info->max_rf_freq_hz * MICRO;
+ u64 min_rf_freq_uhz = ADF41513_MIN_RF_FREQ_HZ * MICRO;
+ u64 pfd_freq_limit_uhz;
+ int ret;
+
+ if (rf_out_uhz < min_rf_freq_uhz || rf_out_uhz > max_rf_freq_uhz) {
+ dev_err(&st->spi->dev, "RF frequency %llu uHz out of range [%llu, %llu] uHz\n",
+ rf_out_uhz, min_rf_freq_uhz, max_rf_freq_uhz);
+ return -EINVAL;
+ }
+
+ result->target_frequency_uhz = rf_out_uhz;
+
+ /* try integer-N first (best phase noise performance) */
+ pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_4_5),
+ ADF41513_MAX_PFD_FREQ_INT_N_UHZ);
+ ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz);
+ if (ret)
+ return ret;
+
+ if (adf41513_calc_integer_n(st, result) == 0)
+ return 0;
+
+ /* try fractional-N: recompute pfd frequency if necessary */
+ pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_FRAC_4_5),
+ ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ);
+ if (pfd_freq_limit_uhz < result->pfd_frequency_uhz) {
+ ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz);
+ if (ret)
+ return ret;
+ }
+
+ /* fixed-modulus attempt */
+ if (adf41513_calc_fixed_mod(st, result) == 0)
+ return 0;
+
+ /* variable-modulus attempt */
+ ret = adf41513_calc_variable_mod(st, result);
+ if (ret) {
+ dev_err(&st->spi->dev,
+ "no valid PLL configuration found for %llu uHz\n",
+ rf_out_uhz);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void adf41513_set_bleed_val(struct adf41513_state *st)
+{
+ u32 bleed_value, cp_index;
+
+ if (!(st->regs[ADF41513_REG6] & ADF41513_REG6_BLEED_ENABLE_MSK))
+ return;
+
+ if (st->data.phase_detector_polarity)
+ bleed_value = 90;
+ else
+ bleed_value = 144;
+
+ cp_index = 1 + FIELD_GET(ADF41513_REG5_CP_CURRENT_MSK,
+ st->regs[ADF41513_REG5]);
+ bleed_value = div64_u64(st->settings.pfd_frequency_uhz * cp_index * bleed_value,
+ 1600ULL * MEGA * MICROHZ_PER_HZ);
+
+ FIELD_MODIFY(ADF41513_REG6_BLEED_CURRENT_MSK, &st->regs[ADF41513_REG6],
+ bleed_value);
+}
+
+static void adf41513_set_ld_window(struct adf41513_state *st)
+{
+ /*
+ * The ideal lock detector window size is halfway between the max
+ * window, set by the phase comparison period t_PFD = (1 / f_PFD),
+ * and the minimum is set by (I_BLEED/I_CP) × t_PFD
+ */
+ u16 ld_window_10x_ns = div64_u64(10ULL * NSEC_PER_SEC * MICROHZ_PER_HZ,
+ st->settings.pfd_frequency_uhz << 1);
+ u8 ld_idx, ldp, ld_bias;
+
+ if (st->settings.mode != ADF41513_MODE_INTEGER_N) {
+ /* account for bleed current (deduced from eq.6 and eq.7) */
+ if (st->data.phase_detector_polarity)
+ ld_window_10x_ns += 4;
+ else
+ ld_window_10x_ns += 6;
+ }
+
+ ld_idx = find_closest(ld_window_10x_ns, adf41513_ld_window_x10_ns,
+ ARRAY_SIZE(adf41513_ld_window_x10_ns));
+ ldp = (adf41513_ldp_bias[ld_idx] >> 2) & 0x3;
+ ld_bias = adf41513_ldp_bias[ld_idx] & 0x3;
+
+ FIELD_MODIFY(ADF41513_REG6_LDP_MSK, &st->regs[ADF41513_REG6], ldp);
+ FIELD_MODIFY(ADF41513_REG9_LD_BIAS_MSK, &st->regs[ADF41513_REG9], ld_bias);
+}
+
+static void adf41513_set_phase_resync(struct adf41513_state *st)
+{
+ u32 total_div, clk1_div, clk2_div;
+
+ if (!st->data.phase_resync_period_ns)
+ return;
+
+ /* assuming both clock dividers hold similar values */
+ total_div = mul_u64_u64_div_u64(st->settings.pfd_frequency_uhz,
+ st->data.phase_resync_period_ns,
+ 1ULL * MICROHZ_PER_HZ * NSEC_PER_SEC);
+ clk1_div = clamp(int_sqrt(total_div), 1,
+ ADF41513_MAX_CLK_DIVIDER);
+ clk2_div = clamp(DIV_ROUND_CLOSEST(total_div, clk1_div), 1,
+ ADF41513_MAX_CLK_DIVIDER);
+
+ FIELD_MODIFY(ADF41513_REG5_CLK1_DIV_MSK, &st->regs[ADF41513_REG5],
+ clk1_div);
+ FIELD_MODIFY(ADF41513_REG7_CLK2_DIV_MSK, &st->regs[ADF41513_REG7],
+ clk2_div);
+
+ /* enable phase resync */
+ FIELD_MODIFY(ADF41513_REG7_CLK_DIV_MODE_MSK, &st->regs[ADF41513_REG7],
+ ADF41513_CLK_DIV_MODE_PHASE_RESYNC);
+}
+
+static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 sync_mask)
+{
+ struct adf41513_pll_settings result;
+ bool pfd_change = false;
+ bool mode_change = false;
+ int ret;
+
+ ret = adf41513_calc_pll_settings(st, &result, freq_uhz);
+ if (ret < 0)
+ return ret;
+
+ /* apply computed results to pll settings */
+ pfd_change = st->settings.pfd_frequency_uhz != result.pfd_frequency_uhz;
+ mode_change = st->settings.mode != result.mode;
+ st->settings = result;
+
+ dev_dbg(&st->spi->dev,
+ "%s mode: int=%u, frac1=%u, frac2=%u, mod2=%u, fpdf=%llu Hz, prescaler=%s\n",
+ (result.mode == ADF41513_MODE_INTEGER_N) ? "integer-n" :
+ (result.mode == ADF41513_MODE_FIXED_MODULUS) ? "fixed-modulus" : "variable-modulus",
+ result.int_val, result.frac1, result.frac2, result.mod2,
+ div64_u64(result.pfd_frequency_uhz, MICRO),
+ result.prescaler ? "8/9" : "4/5");
+
+ st->regs[ADF41513_REG0] = FIELD_PREP(ADF41513_REG0_INT_MSK,
+ st->settings.int_val);
+ if (st->settings.mode == ADF41513_MODE_VARIABLE_MODULUS)
+ st->regs[ADF41513_REG0] |= ADF41513_REG0_VAR_MOD_MSK;
+
+ st->regs[ADF41513_REG1] = FIELD_PREP(ADF41513_REG1_FRAC1_MSK,
+ st->settings.frac1);
+ if (st->settings.mode != ADF41513_MODE_INTEGER_N)
+ st->regs[ADF41513_REG1] |= ADF41513_REG1_DITHER2_MSK;
+
+ st->regs[ADF41513_REG3] = FIELD_PREP(ADF41513_REG3_FRAC2_MSK,
+ st->settings.frac2);
+ FIELD_MODIFY(ADF41513_REG4_MOD2_MSK, &st->regs[ADF41513_REG4],
+ st->settings.mod2);
+ FIELD_MODIFY(ADF41513_REG5_R_CNT_MSK, &st->regs[ADF41513_REG5],
+ st->settings.r_counter % ADF41513_MAX_R_CNT);
+ FIELD_MODIFY(ADF41513_REG5_REF_DOUBLER_MSK, &st->regs[ADF41513_REG5],
+ st->settings.ref_doubler);
+ FIELD_MODIFY(ADF41513_REG5_RDIV2_MSK, &st->regs[ADF41513_REG5],
+ st->settings.ref_div2);
+ FIELD_MODIFY(ADF41513_REG5_PRESCALER_MSK, &st->regs[ADF41513_REG5],
+ st->settings.prescaler);
+
+ if (st->settings.mode == ADF41513_MODE_INTEGER_N) {
+ st->regs[ADF41513_REG6] |= ADF41513_REG6_INT_MODE_MSK;
+ st->regs[ADF41513_REG6] &= ~ADF41513_REG6_BLEED_ENABLE_MSK;
+ } else {
+ st->regs[ADF41513_REG6] &= ~ADF41513_REG6_INT_MODE_MSK;
+ st->regs[ADF41513_REG6] |= ADF41513_REG6_BLEED_ENABLE_MSK;
+ }
+
+ if (pfd_change)
+ adf41513_set_phase_resync(st);
+
+ if (pfd_change || mode_change) {
+ adf41513_set_bleed_val(st);
+ adf41513_set_ld_window(st);
+ }
+
+ return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0);
+}
+
+static int adf41513_suspend(struct adf41513_state *st)
+{
+ st->regs[ADF41513_REG6] |= FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, 1);
+ st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK;
+ return adf41513_sync_config(st, ADF41513_SYNC_DIFF);
+}
+
+static int adf41513_resume(struct adf41513_state *st)
+{
+ int ret;
+
+ st->regs[ADF41513_REG6] &= ~ADF41513_REG6_POWER_DOWN_MSK;
+ st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK;
+ ret = adf41513_sync_config(st, ADF41513_SYNC_ALL);
+ if (ret)
+ return ret;
+
+ if (st->data.le_sync_en) {
+ st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK;
+ ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t adf41513_read_resolution(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct adf41513_state *st = iio_priv(indio_dev);
+ int vals[2];
+
+ guard(mutex)(&st->lock);
+
+ iio_val_s64_decompose(st->data.freq_resolution_uhz, &vals[0], &vals[1]);
+ return iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(vals), vals);
+}
+
+static ssize_t adf41513_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct adf41513_state *st = iio_priv(indio_dev);
+ u32 val;
+
+ guard(mutex)(&st->lock);
+
+ val = FIELD_GET(ADF41513_REG6_POWER_DOWN_MSK, st->regs_hw[ADF41513_REG6]);
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t adf41513_write_resolution(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct adf41513_state *st = iio_priv(indio_dev);
+ u64 freq_uhz;
+ int ret;
+
+ ret = kstrtoudec64(buf, ADF41513_HZ_DECIMAL_SCALE, &freq_uhz);
+ if (ret)
+ return ret;
+
+ if (freq_uhz == 0 || freq_uhz > ADF41513_MAX_FREQ_RESOLUTION_UHZ)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ st->data.freq_resolution_uhz = freq_uhz;
+ return len;
+}
+
+static ssize_t adf41513_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct adf41513_state *st = iio_priv(indio_dev);
+ bool val;
+ int ret;
+
+ ret = kstrtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ if (val)
+ ret = adf41513_suspend(st);
+ else
+ ret = adf41513_resume(st);
+ if (ret)
+ return ret;
+
+ st->powerdown = val;
+ return len;
+}
+
+static const struct iio_chan_spec_ext_info adf41513_ext_info[] = {
+ {
+ .name = "frequency_resolution",
+ .read = adf41513_read_resolution,
+ .write = adf41513_write_resolution,
+ .shared = IIO_SEPARATE,
+ },
+ {
+ .name = "powerdown",
+ .read = adf41513_read_powerdown,
+ .write = adf41513_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ { }
+};
+
+static const struct iio_chan_spec adf41513_chan = {
+ .type = IIO_ALTVOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY) |
+ BIT(IIO_CHAN_INFO_PHASE),
+ .ext_info = adf41513_ext_info,
+};
+
+static int adf41513_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct adf41513_state *st = iio_priv(indio_dev);
+ u64 tmp64;
+
+ guard(mutex)(&st->lock);
+
+ switch (info) {
+ case IIO_CHAN_INFO_FREQUENCY:
+ if (st->lock_detect &&
+ !gpiod_get_value_cansleep(st->lock_detect)) {
+ dev_dbg(&st->spi->dev, "PLL un-locked\n");
+ return -EBUSY;
+ }
+ tmp64 = adf41513_pll_get_rate(st);
+ iio_val_s64_decompose(tmp64, val, val2);
+ return IIO_VAL_DECIMAL64_MICRO;
+ case IIO_CHAN_INFO_PHASE:
+ tmp64 = FIELD_GET(ADF41513_REG2_PHASE_VAL_MSK,
+ st->regs_hw[ADF41513_REG2]);
+ tmp64 = (tmp64 * ADF41513_MAX_PHASE_MICRORAD) >> 12;
+ iio_val_s64_decompose(tmp64, val, val2);
+ return IIO_VAL_DECIMAL64_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adf41513_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct adf41513_state *st = iio_priv(indio_dev);
+ u64 tmp64 = iio_val_s64_compose(val, val2);
+ u16 phase_val;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ switch (info) {
+ case IIO_CHAN_INFO_FREQUENCY:
+ return adf41513_set_frequency(st, tmp64, ADF41513_SYNC_DIFF);
+ case IIO_CHAN_INFO_PHASE:
+ if (tmp64 >= ADF41513_MAX_PHASE_MICRORAD)
+ return -EINVAL;
+
+ phase_val = DIV_U64_ROUND_CLOSEST(tmp64 << 12,
+ ADF41513_MAX_PHASE_MICRORAD);
+ phase_val = min(phase_val, ADF41513_MAX_PHASE_VAL);
+ st->regs[ADF41513_REG2] |= ADF41513_REG2_PHASE_ADJ_MSK;
+ FIELD_MODIFY(ADF41513_REG2_PHASE_VAL_MSK,
+ &st->regs[ADF41513_REG2], phase_val);
+ ret = adf41513_sync_config(st, ADF41513_SYNC_REG0);
+ /* clear phase adjust for the next sync */
+ st->regs[ADF41513_REG2] &= ~ADF41513_REG2_PHASE_ADJ_MSK;
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adf41513_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_FREQUENCY:
+ case IIO_CHAN_INFO_PHASE:
+ return IIO_VAL_DECIMAL64_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adf41513_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct adf41513_state *st = iio_priv(indio_dev);
+
+ if (reg > ADF41513_REG13)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ if (!readval) {
+ if (reg <= ADF41513_REG6)
+ st->settings.mode = ADF41513_MODE_INVALID;
+ st->regs[reg] = writeval & ~0xF; /* Clear control bits */
+ return adf41513_sync_config(st, BIT(reg));
+ }
+
+ *readval = st->regs_hw[reg];
+ return 0;
+}
+
+static const struct iio_info adf41513_info = {
+ .read_raw = adf41513_read_raw,
+ .write_raw = adf41513_write_raw,
+ .write_raw_get_fmt = adf41513_write_raw_get_fmt,
+ .debugfs_reg_access = &adf41513_reg_access,
+};
+
+static int adf41513_parse_fw(struct adf41513_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ u32 tmp, cp_resistance, cp_current;
+ int ret;
+
+ tmp = ADF41510_MAX_RF_FREQ_HZ / MEGA;
+ device_property_read_u32(dev, "adi,power-up-frequency-mhz", &tmp);
+ st->data.power_up_frequency_hz = (u64)tmp * MEGA;
+ if (st->data.power_up_frequency_hz < ADF41513_MIN_RF_FREQ_HZ ||
+ st->data.power_up_frequency_hz > st->chip_info->max_rf_freq_hz)
+ return dev_err_probe(dev, -ERANGE,
+ "power-up frequency %llu Hz out of range\n",
+ st->data.power_up_frequency_hz);
+
+ tmp = ADF41513_MIN_R_CNT;
+ device_property_read_u32(dev, "adi,reference-div-factor", &tmp);
+ if (tmp < ADF41513_MIN_R_CNT || tmp > ADF41513_MAX_R_CNT)
+ return dev_err_probe(dev, -ERANGE,
+ "invalid reference div factor %u\n", tmp);
+ st->data.ref_div_factor = tmp;
+
+ st->data.ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable");
+ st->data.ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable");
+
+ if (st->data.ref_doubler_en &&
+ st->ref_freq_hz > ADF41513_MAX_REF_FREQ_DOUBLER_HZ) {
+ return dev_err_probe(dev, -ERANGE,
+ "Ref frequency not supported with doubler enabled\n");
+ }
+
+ cp_resistance = ADF41513_DEFAULT_R_SET;
+ device_property_read_u32(dev, "adi,charge-pump-resistor-ohms", &cp_resistance);
+ if (cp_resistance < ADF41513_MIN_R_SET || cp_resistance > ADF41513_MAX_R_SET)
+ return dev_err_probe(dev, -ERANGE, "R_SET %u Ohms out of range\n", cp_resistance);
+
+ st->data.charge_pump_voltage_mv = ADF41513_DEFAULT_CP_VOLTAGE_mV;
+ ret = device_property_read_u32(dev, "adi,charge-pump-current-microamp", &cp_current);
+ if (!ret) {
+ if (cp_current < ADF41513_MIN_CP_CURRENT_uA ||
+ cp_current > ADF41513_MAX_CP_CURRENT_uA)
+ return dev_err_probe(dev, -ERANGE,
+ "I_CP %u uA out of range\n", cp_current);
+
+ tmp = DIV_ROUND_CLOSEST(cp_current * cp_resistance, MILLI);
+ if (tmp < ADF41513_MIN_CP_VOLTAGE_mV || tmp > ADF41513_MAX_CP_VOLTAGE_mV)
+ return dev_err_probe(dev, -ERANGE, "I_CP %u uA (%u Ohms) out of range\n",
+ cp_current, cp_resistance);
+ st->data.charge_pump_voltage_mv = tmp;
+ }
+
+ st->data.phase_detector_polarity =
+ device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable");
+
+ st->data.phase_resync_period_ns = 0;
+ ret = device_property_read_u32(dev, "adi,phase-resync-period-ns", &tmp);
+ if (!ret)
+ st->data.phase_resync_period_ns = tmp;
+
+ st->data.logic_lvl_1v8_en = device_property_read_bool(dev, "adi,logic-level-1v8-enable");
+
+ tmp = ADF41513_LD_COUNT_MIN;
+ device_property_read_u32(dev, "adi,lock-detector-count", &tmp);
+ if (tmp < ADF41513_LD_COUNT_FAST_MIN || tmp > ADF41513_LD_COUNT_MAX ||
+ !is_power_of_2(tmp))
+ return dev_err_probe(dev, -ERANGE,
+ "invalid lock detect count: %u\n", tmp);
+ st->data.lock_detect_count = tmp;
+
+ /* load enable sync */
+ st->data.le_sync_en = device_property_read_bool(dev, "adi,le-sync-enable");
+ st->data.freq_resolution_uhz = MICROHZ_PER_HZ;
+
+ return 0;
+}
+
+static void adf41513_chip_disable(void *data)
+{
+ gpiod_set_value_cansleep(data, 0);
+}
+
+static void adf41513_close(void *data)
+{
+ adf41513_suspend(data);
+}
+
+static int adf41513_setup(struct device *dev, struct adf41513_state *st)
+{
+ u32 tmp;
+ int ret;
+
+ memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
+
+ /* assuming DLD pin is used for lock detection */
+ st->regs[ADF41513_REG5] = FIELD_PREP(ADF41513_REG5_DLD_MODES_MSK,
+ ADF41513_DLD_DIG_LD);
+
+ tmp = DIV_ROUND_CLOSEST(st->data.charge_pump_voltage_mv, ADF41513_MIN_CP_VOLTAGE_mV);
+ st->regs[ADF41513_REG5] |= FIELD_PREP(ADF41513_REG5_CP_CURRENT_MSK, tmp - 1);
+
+ st->regs[ADF41513_REG6] = ADF41513_REG6_ABP_MSK |
+ ADF41513_REG6_LOL_ENABLE_MSK |
+ ADF41513_REG6_SD_RESET_MSK;
+ if (st->data.phase_detector_polarity)
+ st->regs[ADF41513_REG6] |= ADF41513_REG6_PD_POLARITY_MSK;
+
+ st->regs[ADF41513_REG7] = FIELD_PREP(ADF41513_REG7_PS_BIAS_MSK,
+ ADF41513_PS_BIAS_INIT);
+ tmp = ilog2(st->data.lock_detect_count);
+ if (st->data.lock_detect_count < ADF41513_LD_COUNT_FAST_LIMIT) {
+ tmp -= const_ilog2(ADF41513_LD_COUNT_FAST_MIN);
+ st->regs[ADF41513_REG7] |= ADF41513_REG7_LD_CLK_SEL_MSK;
+ } else {
+ tmp -= const_ilog2(ADF41513_LD_COUNT_MIN);
+ }
+ st->regs[ADF41513_REG7] |= FIELD_PREP(ADF41513_REG7_LD_COUNT_MSK, tmp);
+
+ st->regs[ADF41513_REG11] = ADF41513_REG11_POWER_DOWN_SEL_MSK;
+ st->regs[ADF41513_REG12] = FIELD_PREP(ADF41513_REG12_LOGIC_LEVEL_MSK,
+ st->data.logic_lvl_1v8_en ? 0 : 1);
+
+ /* perform initialization sequence with power-up frequency */
+ ret = adf41513_set_frequency(st, st->data.power_up_frequency_hz * MICRO,
+ ADF41513_SYNC_ALL);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, adf41513_close, st);
+ if (ret)
+ return ret;
+
+ if (st->data.le_sync_en) {
+ st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK;
+ ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adf41513_pm_suspend(struct device *dev)
+{
+ struct adf41513_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+ return adf41513_suspend(st);
+}
+
+static int adf41513_pm_resume(struct device *dev)
+{
+ struct adf41513_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+ if (st->powerdown)
+ return 0; /* nothing to do */
+
+ return adf41513_resume(st);
+}
+
+static const struct adf41513_chip_info adf41510_chip_info = {
+ .name = "adf41510",
+ .max_rf_freq_hz = ADF41510_MAX_RF_FREQ_HZ,
+ .has_prescaler_8_9 = false,
+};
+
+static const struct adf41513_chip_info adf41513_chip_info = {
+ .name = "adf41513",
+ .max_rf_freq_hz = ADF41513_MAX_RF_FREQ_HZ,
+ .has_prescaler_8_9 = true,
+};
+
+static int adf41513_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct gpio_desc *chip_enable;
+ struct iio_dev *indio_dev;
+ struct adf41513_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;
+ st->chip_info = spi_get_device_match_data(spi);
+ if (!st->chip_info)
+ return -EINVAL;
+
+ spi_set_drvdata(spi, st);
+
+ st->ref_clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(st->ref_clk))
+ return PTR_ERR(st->ref_clk);
+
+ st->ref_freq_hz = clk_get_rate(st->ref_clk);
+ if (st->ref_freq_hz < ADF41513_MIN_REF_FREQ_HZ ||
+ st->ref_freq_hz > ADF41513_MAX_REF_FREQ_HZ)
+ return dev_err_probe(dev, -ERANGE,
+ "reference frequency %u Hz out of range\n",
+ st->ref_freq_hz);
+
+ ret = adf41513_parse_fw(st);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(adf41513_power_supplies),
+ adf41513_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable regulators\n");
+
+ st->lock_detect = devm_gpiod_get_optional(dev, "lock-detect", GPIOD_IN);
+ if (IS_ERR(st->lock_detect))
+ return dev_err_probe(dev, PTR_ERR(st->lock_detect),
+ "fail to request lock detect GPIO\n");
+
+ chip_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(chip_enable))
+ return dev_err_probe(dev, PTR_ERR(chip_enable),
+ "fail to request chip enable GPIO\n");
+
+ ret = devm_add_action_or_reset(dev, adf41513_chip_disable, chip_enable);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add disable action\n");
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->chip_info->name;
+ indio_dev->info = &adf41513_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &adf41513_chan;
+ indio_dev->num_channels = 1;
+
+ ret = adf41513_setup(dev, st);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to setup device\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id adf41513_id[] = {
+ { .name = "adf41510", .driver_data = (kernel_ulong_t)&adf41510_chip_info },
+ { .name = "adf41513", .driver_data = (kernel_ulong_t)&adf41513_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adf41513_id);
+
+static const struct of_device_id adf41513_of_match[] = {
+ { .compatible = "adi,adf41510", .data = &adf41510_chip_info },
+ { .compatible = "adi,adf41513", .data = &adf41513_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adf41513_of_match);
+
+static DEFINE_SIMPLE_DEV_PM_OPS(adf41513_pm_ops, adf41513_pm_suspend, adf41513_pm_resume);
+
+static struct spi_driver adf41513_driver = {
+ .driver = {
+ .name = "adf41513",
+ .pm = pm_ptr(&adf41513_pm_ops),
+ .of_match_table = adf41513_of_match,
+ },
+ .probe = adf41513_probe,
+ .id_table = adf41513_id,
+};
+module_spi_driver(adf41513_driver);
+
+MODULE_AUTHOR("Rodrigo Alencar <rodrigo.alencar@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADF41513 PLL Frequency Synthesizer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 639cac522b44..315317d6eec4 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -691,8 +691,8 @@ static const struct of_device_id adf4350_of_match[] = {
MODULE_DEVICE_TABLE(of, adf4350_of_match);
static const struct spi_device_id adf4350_id[] = {
- {"adf4350", 4350},
- {"adf4351", 4351},
+ { .name = "adf4350", .driver_data = 4350 },
+ { .name = "adf4351", .driver_data = 4351 },
{ }
};
MODULE_DEVICE_TABLE(spi, adf4350_id);
diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c
index d6dc7827fb41..e20ed718d01d 100644
--- a/drivers/iio/frequency/adf4371.c
+++ b/drivers/iio/frequency/adf4371.c
@@ -624,8 +624,8 @@ static int adf4371_probe(struct spi_device *spi)
}
static const struct spi_device_id adf4371_id_table[] = {
- { "adf4371", (kernel_ulong_t)&adf4371_chip_info },
- { "adf4372", (kernel_ulong_t)&adf4372_chip_info },
+ { .name = "adf4371", .driver_data = (kernel_ulong_t)&adf4371_chip_info },
+ { .name = "adf4372", .driver_data = (kernel_ulong_t)&adf4372_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adf4371_id_table);
diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c
index f024680492b8..7385be62fa6a 100644
--- a/drivers/iio/frequency/adf4377.c
+++ b/drivers/iio/frequency/adf4377.c
@@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
@@ -518,14 +519,15 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq)
u64 clkin_freq;
int ret;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
+
ret = regmap_read(st->regmap, 0x12, &ref_div_factor);
if (ret)
- goto exit;
+ return ret;
ret = regmap_bulk_read(st->regmap, 0x10, st->buf, sizeof(st->buf));
if (ret)
- goto exit;
+ return ret;
clkin_freq = clk_get_rate(st->clkin);
ref_div_factor = FIELD_GET(ADF4377_0012_R_DIV_MSK, ref_div_factor);
@@ -533,10 +535,8 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq)
get_unaligned_le16(&st->buf));
*freq = div_u64(clkin_freq, ref_div_factor) * n_int;
-exit:
- mutex_unlock(&st->lock);
- return ret;
+ return 0;
}
static int adf4377_set_freq(struct adf4377_state *st, u64 freq)
@@ -545,26 +545,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq)
u64 f_vco;
int ret;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
- if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ) {
- ret = -EINVAL;
- goto exit;
- }
+ if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ)
+ return -EINVAL;
ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK |
ADF4377_001C_EN_DRCLK_MSK,
FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 1) |
FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 1));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x11, ADF4377_0011_EN_AUTOCAL_MSK |
ADF4377_0011_DCLK_DIV2_MSK,
FIELD_PREP(ADF4377_0011_EN_AUTOCAL_MSK, 1) |
FIELD_PREP(ADF4377_0011_DCLK_DIV2_MSK, st->dclk_div2));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x2E, ADF4377_002E_EN_ADC_CNV_MSK |
ADF4377_002E_EN_ADC_MSK |
@@ -574,56 +572,56 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq)
FIELD_PREP(ADF4377_002E_ADC_A_CONV_MSK,
ADF4377_002E_ADC_A_CONV_VCO_CALIB));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK,
FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 1));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x2F, ADF4377_002F_DCLK_DIV1_MSK,
FIELD_PREP(ADF4377_002F_DCLK_DIV1_MSK, st->dclk_div1));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x24, ADF4377_0024_DCLK_MODE_MSK,
FIELD_PREP(ADF4377_0024_DCLK_MODE_MSK, st->dclk_mode));
if (ret)
- goto exit;
+ return ret;
ret = regmap_write(st->regmap, 0x27,
FIELD_PREP(ADF4377_0027_SYNTH_LOCK_TO_LSB_MSK,
st->synth_lock_timeout));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x28, ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK,
FIELD_PREP(ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK,
st->synth_lock_timeout >> 8));
if (ret)
- goto exit;
+ return ret;
ret = regmap_write(st->regmap, 0x29,
FIELD_PREP(ADF4377_0029_VCO_ALC_TO_LSB_MSK,
st->vco_alc_timeout));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x2A, ADF4377_002A_VCO_ALC_TO_MSB_MSK,
FIELD_PREP(ADF4377_002A_VCO_ALC_TO_MSB_MSK,
st->vco_alc_timeout >> 8));
if (ret)
- goto exit;
+ return ret;
ret = regmap_write(st->regmap, 0x26,
FIELD_PREP(ADF4377_0026_VCO_BAND_DIV_MSK, st->vco_band_div));
if (ret)
- goto exit;
+ return ret;
ret = regmap_write(st->regmap, 0x2D,
FIELD_PREP(ADF4377_002D_ADC_CLK_DIV_MSK, st->adc_clk_div));
if (ret)
- goto exit;
+ return ret;
st->clkout_div_sel = 0;
@@ -641,24 +639,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq)
FIELD_PREP(ADF4377_0011_EN_RDBLR_MSK, 0) |
FIELD_PREP(ADF4377_0011_N_INT_MSB_MSK, st->n_int >> 8));
if (ret)
- goto exit;
+ return ret;
ret = regmap_update_bits(st->regmap, 0x12, ADF4377_0012_R_DIV_MSK |
ADF4377_0012_CLKOUT_DIV_MSK,
FIELD_PREP(ADF4377_0012_CLKOUT_DIV_MSK, st->clkout_div_sel) |
FIELD_PREP(ADF4377_0012_R_DIV_MSK, st->ref_div_factor));
if (ret)
- goto exit;
+ return ret;
ret = regmap_write(st->regmap, 0x10,
FIELD_PREP(ADF4377_0010_N_INT_LSB_MSK, st->n_int));
if (ret)
- goto exit;
+ return ret;
ret = regmap_read_poll_timeout(st->regmap, 0x49, read_val,
!(read_val & (ADF4377_0049_FSM_BUSY_MSK)), 200, 200 * 100);
if (ret)
- goto exit;
+ return ret;
/* Disable EN_DNCLK, EN_DRCLK */
ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK |
@@ -666,26 +664,21 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq)
FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 0) |
FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 0));
if (ret)
- goto exit;
+ return ret;
/* Disable EN_ADC_CLK */
ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK,
FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 0));
if (ret)
- goto exit;
+ return ret;
/* Set output Amplitude */
- ret = regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK |
- ADF4377_0019_CLKOUT1_OP_MSK,
- FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK,
- ADF4377_0019_CLKOUT_420MV) |
- FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK,
- ADF4377_0019_CLKOUT_420MV));
-
-exit:
- mutex_unlock(&st->lock);
-
- return ret;
+ return regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK |
+ ADF4377_0019_CLKOUT1_OP_MSK,
+ FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK,
+ ADF4377_0019_CLKOUT_420MV) |
+ FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK,
+ ADF4377_0019_CLKOUT_420MV));
}
static void adf4377_gpio_init(struct adf4377_state *st)
@@ -919,13 +912,11 @@ static int adf4377_properties_parse(struct adf4377_state *st)
static int adf4377_freq_change(struct notifier_block *nb, unsigned long action, void *data)
{
struct adf4377_state *st = container_of(nb, struct adf4377_state, nb);
- int ret;
if (action == POST_RATE_CHANGE) {
- mutex_lock(&st->lock);
- ret = notifier_from_errno(adf4377_init(st));
- mutex_unlock(&st->lock);
- return ret;
+ guard(mutex)(&st->lock);
+
+ return notifier_from_errno(adf4377_init(st));
}
return NOTIFY_OK;
@@ -1097,8 +1088,8 @@ static int adf4377_probe(struct spi_device *spi)
}
static const struct spi_device_id adf4377_id[] = {
- { "adf4377", (kernel_ulong_t)&adf4377_chip_info },
- { "adf4378", (kernel_ulong_t)&adf4378_chip_info },
+ { .name = "adf4377", .driver_data = (kernel_ulong_t)&adf4377_chip_info },
+ { .name = "adf4378", .driver_data = (kernel_ulong_t)&adf4378_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adf4377_id);
diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c
index b823adfb0f70..c9baaab574e3 100644
--- a/drivers/iio/frequency/admv1013.c
+++ b/drivers/iio/frequency/admv1013.c
@@ -619,7 +619,7 @@ static int admv1013_probe(struct spi_device *spi)
}
static const struct spi_device_id admv1013_id[] = {
- { "admv1013", 0 },
+ { .name = "admv1013" },
{ }
};
MODULE_DEVICE_TABLE(spi, admv1013_id);
diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c
index 5d36ac4bb666..d1d528975229 100644
--- a/drivers/iio/frequency/admv1014.c
+++ b/drivers/iio/frequency/admv1014.c
@@ -781,7 +781,7 @@ static int admv1014_probe(struct spi_device *spi)
}
static const struct spi_device_id admv1014_id[] = {
- { "admv1014", 0 },
+ { .name = "admv1014" },
{ }
};
MODULE_DEVICE_TABLE(spi, admv1014_id);
diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c
index c2dc06f05c21..79a615062172 100644
--- a/drivers/iio/frequency/adrf6780.c
+++ b/drivers/iio/frequency/adrf6780.c
@@ -486,7 +486,7 @@ static int adrf6780_probe(struct spi_device *spi)
}
static const struct spi_device_id adrf6780_id[] = {
- { "adrf6780", 0 },
+ { .name = "adrf6780" },
{ }
};
MODULE_DEVICE_TABLE(spi, adrf6780_id);
diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c
index 178bba95a709..0c8bc1e569fe 100644
--- a/drivers/iio/gyro/adis16080.c
+++ b/drivers/iio/gyro/adis16080.c
@@ -212,8 +212,8 @@ static int adis16080_probe(struct spi_device *spi)
}
static const struct spi_device_id adis16080_ids[] = {
- { "adis16080", ID_ADIS16080 },
- { "adis16100", ID_ADIS16100 },
+ { .name = "adis16080", .driver_data = ID_ADIS16080 },
+ { .name = "adis16100", .driver_data = ID_ADIS16100 },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16080_ids);
diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
index 369c7428e1ef..ff335f81e9ae 100644
--- a/drivers/iio/gyro/adis16136.c
+++ b/drivers/iio/gyro/adis16136.c
@@ -563,10 +563,10 @@ static int adis16136_probe(struct spi_device *spi)
}
static const struct spi_device_id adis16136_ids[] = {
- { "adis16133", ID_ADIS16133 },
- { "adis16135", ID_ADIS16135 },
- { "adis16136", ID_ADIS16136 },
- { "adis16137", ID_ADIS16137 },
+ { .name = "adis16133", .driver_data = ID_ADIS16133 },
+ { .name = "adis16135", .driver_data = ID_ADIS16135 },
+ { .name = "adis16136", .driver_data = ID_ADIS16136 },
+ { .name = "adis16137", .driver_data = ID_ADIS16137 },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16136_ids);
diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c
index 91b9c5f18ec4..a79a8edf4c98 100644
--- a/drivers/iio/gyro/adis16260.c
+++ b/drivers/iio/gyro/adis16260.c
@@ -411,12 +411,12 @@ static int adis16260_probe(struct spi_device *spi)
* support for the on chip filtering.
*/
static const struct spi_device_id adis16260_id[] = {
- {"adis16260", ADIS16260},
- {"adis16265", ADIS16260},
- {"adis16266", ADIS16266},
- {"adis16250", ADIS16260},
- {"adis16255", ADIS16260},
- {"adis16251", ADIS16251},
+ { .name = "adis16260", .driver_data = ADIS16260 },
+ { .name = "adis16265", .driver_data = ADIS16260 },
+ { .name = "adis16266", .driver_data = ADIS16266 },
+ { .name = "adis16250", .driver_data = ADIS16260 },
+ { .name = "adis16255", .driver_data = ADIS16260 },
+ { .name = "adis16251", .driver_data = ADIS16251 },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16260_id);
diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c
index 35928383d4f2..563de2724396 100644
--- a/drivers/iio/gyro/adxrs290.c
+++ b/drivers/iio/gyro/adxrs290.c
@@ -585,9 +585,8 @@ 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_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);
+ if (ret)
+ return ret;
ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig);
if (ret) {
diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c
index a1d8d3cb301b..030d042cab8b 100644
--- a/drivers/iio/gyro/adxrs450.c
+++ b/drivers/iio/gyro/adxrs450.c
@@ -439,8 +439,8 @@ static int adxrs450_probe(struct spi_device *spi)
}
static const struct spi_device_id adxrs450_id[] = {
- {"adxrs450", ID_ADXRS450},
- {"adxrs453", ID_ADXRS453},
+ { .name = "adxrs450", .driver_data = ID_ADXRS450 },
+ { .name = "adxrs453", .driver_data = ID_ADXRS453 },
{ }
};
MODULE_DEVICE_TABLE(spi, adxrs450_id);
diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c
index 6aecc5eb8347..14ae1d03be3d 100644
--- a/drivers/iio/gyro/bmg160_spi.c
+++ b/drivers/iio/gyro/bmg160_spi.c
@@ -33,12 +33,11 @@ static void bmg160_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id bmg160_spi_id[] = {
- {"bmg160", 0},
- {"bmi055_gyro", 0},
- {"bmi088_gyro", 0},
+ { .name = "bmg160" },
+ { .name = "bmi055_gyro" },
+ { .name = "bmi088_gyro" },
{ }
};
-
MODULE_DEVICE_TABLE(spi, bmg160_spi_id);
static const struct of_device_id bmg160_of_match[] = {
diff --git a/drivers/iio/gyro/fxas21002c_spi.c b/drivers/iio/gyro/fxas21002c_spi.c
index bd5b8678da13..40b1e02b2a5c 100644
--- a/drivers/iio/gyro/fxas21002c_spi.c
+++ b/drivers/iio/gyro/fxas21002c_spi.c
@@ -39,7 +39,7 @@ static void fxas21002c_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id fxas21002c_spi_id[] = {
- { "fxas21002c", 0 },
+ { .name = "fxas21002c" },
{ }
};
MODULE_DEVICE_TABLE(spi, fxas21002c_spi_id);
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index adc52a5267f7..58250a972567 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -84,9 +84,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = {
/* Channel read_raw handler */
static int gyro_3d_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
int report_id = -1;
@@ -111,8 +110,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
min < 0);
else {
*val = 0;
- hid_sensor_power_state(&gyro_state->common_attributes,
- false);
+ hid_sensor_power_state(&gyro_state->common_attributes, false);
return -EINVAL;
}
hid_sensor_power_state(&gyro_state->common_attributes, false);
@@ -145,10 +143,8 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int gyro_3d_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
int ret = 0;
@@ -176,8 +172,7 @@ static const struct iio_info gyro_3d_info = {
/* Callback handler to send event after all samples are received and captured */
static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
@@ -198,9 +193,9 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- size_t raw_len, char *raw_data,
- void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
@@ -233,7 +228,7 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
static int gyro_3d_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
struct iio_chan_spec *channels,
- unsigned usage_id,
+ u32 usage_id,
struct gyro_3d_state *st)
{
int ret;
@@ -253,10 +248,10 @@ static int gyro_3d_parse_report(struct platform_device *pdev,
};
}
dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n",
- st->gyro[0].index,
- st->gyro[0].report_id,
- st->gyro[1].index, st->gyro[1].report_id,
- st->gyro[2].index, st->gyro[2].report_id);
+ st->gyro[0].index,
+ st->gyro[0].report_id,
+ st->gyro[1].index, st->gyro[1].report_id,
+ st->gyro[2].index, st->gyro[2].report_id);
st->scale_precision = hid_sensor_format_scale(
HID_USAGE_SENSOR_GYRO_3D,
@@ -317,32 +312,32 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
atomic_set(&gyro_state->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &gyro_state->common_attributes);
+ &gyro_state->common_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
gyro_state->callbacks.send_event = gyro_3d_proc_event;
gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
gyro_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D,
- &gyro_state->callbacks);
+ &gyro_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return ret;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
return ret;
@@ -355,8 +350,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
}
diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c
index adfbbc0d37dc..c56331755b5c 100644
--- a/drivers/iio/gyro/st_gyro_spi.c
+++ b/drivers/iio/gyro/st_gyro_spi.c
@@ -97,15 +97,15 @@ static int st_gyro_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id st_gyro_id_table[] = {
- { L3G4200D_GYRO_DEV_NAME },
- { LSM330D_GYRO_DEV_NAME },
- { LSM330DL_GYRO_DEV_NAME },
- { LSM330DLC_GYRO_DEV_NAME },
- { L3GD20_GYRO_DEV_NAME },
- { L3GD20H_GYRO_DEV_NAME },
- { L3G4IS_GYRO_DEV_NAME },
- { LSM330_GYRO_DEV_NAME },
- { LSM9DS0_GYRO_DEV_NAME },
+ { .name = L3G4200D_GYRO_DEV_NAME },
+ { .name = LSM330D_GYRO_DEV_NAME },
+ { .name = LSM330DL_GYRO_DEV_NAME },
+ { .name = LSM330DLC_GYRO_DEV_NAME },
+ { .name = L3GD20_GYRO_DEV_NAME },
+ { .name = L3GD20H_GYRO_DEV_NAME },
+ { .name = L3G4IS_GYRO_DEV_NAME },
+ { .name = LSM330_GYRO_DEV_NAME },
+ { .name = LSM9DS0_GYRO_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c
index d358f4d5e5da..2a648532242a 100644
--- a/drivers/iio/health/afe4403.c
+++ b/drivers/iio/health/afe4403.c
@@ -544,10 +544,8 @@ static int afe4403_probe(struct spi_device *spi)
iio_trigger_generic_data_rdy_poll,
IRQF_NO_THREAD, AFE4403_DRIVER_NAME,
afe->trig);
- if (ret) {
- dev_err(dev, "Unable to request IRQ\n");
+ if (ret)
return ret;
- }
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
@@ -568,7 +566,7 @@ static int afe4403_probe(struct spi_device *spi)
}
static const struct spi_device_id afe4403_ids[] = {
- { "afe4403", 0 },
+ { .name = "afe4403" },
{ }
};
MODULE_DEVICE_TABLE(spi, afe4403_ids);
diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c
index 2357e2dd7017..22cd46eca217 100644
--- a/drivers/iio/health/afe4404.c
+++ b/drivers/iio/health/afe4404.c
@@ -551,10 +551,8 @@ static int afe4404_probe(struct i2c_client *client)
iio_trigger_generic_data_rdy_poll,
IRQF_NO_THREAD, AFE4404_DRIVER_NAME,
afe->trig);
- if (ret) {
- dev_err(dev, "Unable to request IRQ\n");
+ if (ret)
return ret;
- }
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
index 97352485f5e2..191540e10c20 100644
--- a/drivers/iio/health/max30100.c
+++ b/drivers/iio/health/max30100.c
@@ -489,10 +489,8 @@ static int max30100_probe(struct i2c_client *client)
NULL, max30100_interrupt_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max30100_irq", indio_dev);
- if (ret) {
- dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+ if (ret)
return ret;
- }
return iio_device_register(indio_dev);
}
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index b7402e2bb081..aee96167f01e 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -583,10 +583,8 @@ static int max30102_probe(struct i2c_client *client)
NULL, max30102_interrupt_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max30102_irq", indio_dev);
- if (ret) {
- dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+ if (ret)
return ret;
- }
return iio_device_register(indio_dev);
}
diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c
index 1ae8702ada54..75971c19624a 100644
--- a/drivers/iio/humidity/hdc3020.c
+++ b/drivers/iio/humidity/hdc3020.c
@@ -843,8 +843,7 @@ static int hdc3020_probe(struct i2c_client *client)
IRQF_ONESHOT, "hdc3020",
indio_dev);
if (ret)
- return dev_err_probe(&client->dev, ret,
- "Failed to request IRQ\n");
+ return ret;
}
ret = devm_iio_device_register(&data->client->dev, indio_dev);
diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c
index 5267a14d73ec..7cec81ff5685 100644
--- a/drivers/iio/humidity/hid-sensor-humidity.c
+++ b/drivers/iio/humidity/hid-sensor-humidity.c
@@ -44,7 +44,7 @@ static const struct iio_chan_spec humidity_channels[] = {
/* Adjust channel real bits based on report descriptor */
static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels,
- int channel, int size)
+ int channel, int size)
{
channels[channel].scan_type.sign = 's';
/* Real storage bits will change based on the report desc. */
@@ -54,8 +54,8 @@ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels,
}
static int humidity_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
@@ -100,8 +100,8 @@ static int humidity_read_raw(struct iio_dev *indio_dev,
}
static int humidity_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
@@ -126,7 +126,7 @@ static const struct iio_info humidity_info = {
/* Callback handler to send event after all samples are received and captured */
static int humidity_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned int usage_id, void *pdev)
+ u32 usage_id, void *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
@@ -140,8 +140,9 @@ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned int usage_id, size_t raw_len,
- char *raw_data, void *pdev)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
@@ -158,10 +159,10 @@ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id */
static int humidity_parse_report(struct platform_device *pdev,
- struct hid_sensor_hub_device *hsdev,
- struct iio_chan_spec *channels,
- unsigned int usage_id,
- struct hid_humidity_state *st)
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ u32 usage_id,
+ struct hid_humidity_state *st)
{
int ret;
@@ -214,13 +215,13 @@ static int hid_humidity_probe(struct platform_device *pdev)
if (ret)
return ret;
- humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels,
- sizeof(humidity_channels), GFP_KERNEL);
+ humid_chans = devm_kmemdup(&pdev->dev, humidity_channels,
+ sizeof(humidity_channels), GFP_KERNEL);
if (!humid_chans)
return -ENOMEM;
ret = humidity_parse_report(pdev, hsdev, humid_chans,
- HID_USAGE_SENSOR_HUMIDITY, humid_st);
+ HID_USAGE_SENSOR_HUMIDITY, humid_st);
if (ret)
return ret;
@@ -233,7 +234,7 @@ static int hid_humidity_probe(struct platform_device *pdev)
atomic_set(&humid_st->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &humid_st->common_attributes);
+ &humid_st->common_attributes);
if (ret)
return ret;
@@ -241,7 +242,7 @@ static int hid_humidity_probe(struct platform_device *pdev)
humidity_callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY,
- &humidity_callbacks);
+ &humidity_callbacks);
if (ret)
goto error_remove_trigger;
diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c
index 4d03db19063e..df12c7a25b2e 100644
--- a/drivers/iio/humidity/hts221_buffer.c
+++ b/drivers/iio/humidity/hts221_buffer.c
@@ -122,11 +122,8 @@ int hts221_allocate_trigger(struct iio_dev *iio_dev)
hts221_trigger_handler_thread,
irq_type | IRQF_ONESHOT,
hw->name, hw);
- if (err) {
- dev_err(hw->dev, "failed to request trigger irq %d\n",
- hw->irq);
+ if (err)
return err;
- }
hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
iio_dev->name);
diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c
index e6fef2acd523..f962842cc71d 100644
--- a/drivers/iio/humidity/hts221_spi.c
+++ b/drivers/iio/humidity/hts221_spi.c
@@ -47,7 +47,7 @@ static const struct of_device_id hts221_spi_of_match[] = {
MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
static const struct spi_device_id hts221_spi_id_table[] = {
- { HTS221_DEV_NAME },
+ { .name = HTS221_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h
index e1a56824e07f..4698a857739f 100644
--- a/drivers/iio/iio_core_trigger.h
+++ b/drivers/iio/iio_core_trigger.h
@@ -56,4 +56,4 @@ static inline int iio_trigger_detach_poll_func(struct iio_trigger *trig,
return 0;
}
-#endif /* CONFIG_TRIGGER_CONSUMER */
+#endif /* CONFIG_IIO_TRIGGER */
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 7e0181c27bb6..8bab4616be20 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -109,6 +109,7 @@ config KMX61
be called kmx61.
source "drivers/iio/imu/inv_icm42600/Kconfig"
+source "drivers/iio/imu/inv_icm42607/Kconfig"
source "drivers/iio/imu/inv_icm45600/Kconfig"
source "drivers/iio/imu/inv_mpu6050/Kconfig"
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 13fb7846e9c9..3268dc2371ae 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o
obj-y += inv_icm42600/
+obj-y += inv_icm42607/
obj-y += inv_icm45600/
obj-y += inv_mpu6050/
diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c
index 36323ad149e0..4842346f9f0c 100644
--- a/drivers/iio/imu/adis16400.c
+++ b/drivers/iio/imu/adis16400.c
@@ -1189,21 +1189,21 @@ static int adis16400_probe(struct spi_device *spi)
}
static const struct spi_device_id adis16400_id[] = {
- { "adis16300", (kernel_ulong_t)&adis16300_chip_info },
- { "adis16305", (kernel_ulong_t)&adis16300_chip_info },
- { "adis16334", (kernel_ulong_t)&adis16334_chip_info },
- { "adis16350", (kernel_ulong_t)&adis16350_chip_info },
- { "adis16354", (kernel_ulong_t)&adis16350_chip_info },
- { "adis16355", (kernel_ulong_t)&adis16350_chip_info },
- { "adis16360", (kernel_ulong_t)&adis16360_chip_info },
- { "adis16362", (kernel_ulong_t)&adis16362_chip_info },
- { "adis16364", (kernel_ulong_t)&adis16364_chip_info },
- { "adis16365", (kernel_ulong_t)&adis16360_chip_info },
- { "adis16367", (kernel_ulong_t)&adis16367_chip_info },
- { "adis16400", (kernel_ulong_t)&adis16400_chip_info },
- { "adis16405", (kernel_ulong_t)&adis16400_chip_info },
- { "adis16445", (kernel_ulong_t)&adis16445_chip_info },
- { "adis16448", (kernel_ulong_t)&adis16448_chip_info },
+ { .name = "adis16300", .driver_data = (kernel_ulong_t)&adis16300_chip_info },
+ { .name = "adis16305", .driver_data = (kernel_ulong_t)&adis16300_chip_info },
+ { .name = "adis16334", .driver_data = (kernel_ulong_t)&adis16334_chip_info },
+ { .name = "adis16350", .driver_data = (kernel_ulong_t)&adis16350_chip_info },
+ { .name = "adis16354", .driver_data = (kernel_ulong_t)&adis16350_chip_info },
+ { .name = "adis16355", .driver_data = (kernel_ulong_t)&adis16350_chip_info },
+ { .name = "adis16360", .driver_data = (kernel_ulong_t)&adis16360_chip_info },
+ { .name = "adis16362", .driver_data = (kernel_ulong_t)&adis16362_chip_info },
+ { .name = "adis16364", .driver_data = (kernel_ulong_t)&adis16364_chip_info },
+ { .name = "adis16365", .driver_data = (kernel_ulong_t)&adis16360_chip_info },
+ { .name = "adis16367", .driver_data = (kernel_ulong_t)&adis16367_chip_info },
+ { .name = "adis16400", .driver_data = (kernel_ulong_t)&adis16400_chip_info },
+ { .name = "adis16405", .driver_data = (kernel_ulong_t)&adis16400_chip_info },
+ { .name = "adis16445", .driver_data = (kernel_ulong_t)&adis16445_chip_info },
+ { .name = "adis16448", .driver_data = (kernel_ulong_t)&adis16448_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16400_id);
diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c
index ba1887d36577..cadf21c05bbf 100644
--- a/drivers/iio/imu/adis16460.c
+++ b/drivers/iio/imu/adis16460.c
@@ -394,7 +394,7 @@ static int adis16460_probe(struct spi_device *spi)
}
static const struct spi_device_id adis16460_ids[] = {
- { "adis16460", 0 },
+ { .name = "adis16460" },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16460_ids);
diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c
index 17335386d8e3..5f21dceda8f7 100644
--- a/drivers/iio/imu/adis16475.c
+++ b/drivers/iio/imu/adis16475.c
@@ -2061,33 +2061,33 @@ static const struct of_device_id adis16475_of_match[] = {
MODULE_DEVICE_TABLE(of, adis16475_of_match);
static const struct spi_device_id adis16475_ids[] = {
- { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] },
- { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] },
- { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] },
- { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] },
- { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] },
- { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] },
- { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] },
- { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] },
- { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] },
- { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] },
- { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] },
- { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] },
- { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] },
- { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] },
- { "adis16501", (kernel_ulong_t)&adis16475_chip_info[ADIS16501] },
- { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] },
- { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] },
- { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] },
- { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] },
- { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] },
- { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] },
- { "adis16575-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] },
- { "adis16575-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] },
- { "adis16576-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] },
- { "adis16576-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] },
- { "adis16577-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] },
- { "adis16577-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] },
+ { .name = "adis16470", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16470] },
+ { .name = "adis16475-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] },
+ { .name = "adis16475-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] },
+ { .name = "adis16475-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] },
+ { .name = "adis16477-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] },
+ { .name = "adis16477-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] },
+ { .name = "adis16477-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] },
+ { .name = "adis16465-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] },
+ { .name = "adis16465-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] },
+ { .name = "adis16465-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] },
+ { .name = "adis16467-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] },
+ { .name = "adis16467-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] },
+ { .name = "adis16467-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] },
+ { .name = "adis16500", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16500] },
+ { .name = "adis16501", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16501] },
+ { .name = "adis16505-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] },
+ { .name = "adis16505-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] },
+ { .name = "adis16505-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] },
+ { .name = "adis16507-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] },
+ { .name = "adis16507-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] },
+ { .name = "adis16507-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] },
+ { .name = "adis16575-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] },
+ { .name = "adis16575-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] },
+ { .name = "adis16576-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] },
+ { .name = "adis16576-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] },
+ { .name = "adis16577-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] },
+ { .name = "adis16577-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16475_ids);
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index e009f3824768..5187566c1876 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -110,7 +110,7 @@
#define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20)
-/* Each filter coefficent bank spans two pages */
+/* Each filter coefficient bank spans two pages */
#define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \
ADIS16480_REG((page) + 1, (x) - 60 + 8))
#define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x))
@@ -1467,7 +1467,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p)
* We need to perform the padding to have the buffer
* elements naturally aligned in case there are any
* 32-bit storage size channels enabled which are added
- * in the buffer after the temprature data. In case
+ * in the buffer after the temperature data. In case
* there is no data being added after the temperature
* data, the padding is harmless.
*/
@@ -1806,26 +1806,26 @@ static int adis16480_probe(struct spi_device *spi)
}
static const struct spi_device_id adis16480_ids[] = {
- { "adis16375", ADIS16375 },
- { "adis16480", ADIS16480 },
- { "adis16485", ADIS16485 },
- { "adis16486", ADIS16486 },
- { "adis16487", ADIS16487 },
- { "adis16488", ADIS16488 },
- { "adis16489", ADIS16489 },
- { "adis16490", ADIS16490 },
- { "adis16495-1", ADIS16495_1 },
- { "adis16495-2", ADIS16495_2 },
- { "adis16495-3", ADIS16495_3 },
- { "adis16497-1", ADIS16497_1 },
- { "adis16497-2", ADIS16497_2 },
- { "adis16497-3", ADIS16497_3 },
- { "adis16545-1", ADIS16545_1 },
- { "adis16545-2", ADIS16545_2 },
- { "adis16545-3", ADIS16545_3 },
- { "adis16547-1", ADIS16547_1 },
- { "adis16547-2", ADIS16547_2 },
- { "adis16547-3", ADIS16547_3 },
+ { .name = "adis16375", .driver_data = ADIS16375 },
+ { .name = "adis16480", .driver_data = ADIS16480 },
+ { .name = "adis16485", .driver_data = ADIS16485 },
+ { .name = "adis16486", .driver_data = ADIS16486 },
+ { .name = "adis16487", .driver_data = ADIS16487 },
+ { .name = "adis16488", .driver_data = ADIS16488 },
+ { .name = "adis16489", .driver_data = ADIS16489 },
+ { .name = "adis16490", .driver_data = ADIS16490 },
+ { .name = "adis16495-1", .driver_data = ADIS16495_1 },
+ { .name = "adis16495-2", .driver_data = ADIS16495_2 },
+ { .name = "adis16495-3", .driver_data = ADIS16495_3 },
+ { .name = "adis16497-1", .driver_data = ADIS16497_1 },
+ { .name = "adis16497-2", .driver_data = ADIS16497_2 },
+ { .name = "adis16497-3", .driver_data = ADIS16497_3 },
+ { .name = "adis16545-1", .driver_data = ADIS16545_1 },
+ { .name = "adis16545-2", .driver_data = ADIS16545_2 },
+ { .name = "adis16545-3", .driver_data = ADIS16545_3 },
+ { .name = "adis16547-1", .driver_data = ADIS16547_1 },
+ { .name = "adis16547-2", .driver_data = ADIS16547_2 },
+ { .name = "adis16547-3", .driver_data = ADIS16547_3 },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16480_ids);
diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c
index 1e435d60cd6d..096a612865c4 100644
--- a/drivers/iio/imu/adis16550.c
+++ b/drivers/iio/imu/adis16550.c
@@ -88,25 +88,7 @@ struct adis16550_sync {
u16 max_rate;
};
-struct adis16550_chip_info {
- const struct iio_chan_spec *channels;
- const struct adis16550_sync *sync_mode;
- char *name;
- u32 num_channels;
- u32 gyro_max_val;
- u32 gyro_max_scale;
- u32 accel_max_val;
- u32 accel_max_scale;
- u32 temp_scale;
- u32 deltang_max_val;
- u32 deltvel_max_val;
- u32 int_clk;
- u16 max_dec;
- u16 num_sync;
-};
-
struct adis16550 {
- const struct adis16550_chip_info *info;
struct adis adis;
unsigned long clk_freq_hz;
u32 sync_mode;
@@ -449,8 +431,8 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz)
* The optimal sample rate for the supported IMUs is between
* int_clk - 1000 and int_clk + 500.
*/
- u32 max_sample_rate = st->info->int_clk * 1000 + 500000;
- u32 min_sample_rate = st->info->int_clk * 1000 - 1000000;
+ u32 max_sample_rate = 4000 * 1000 + 500000;
+ u32 min_sample_rate = 4000 * 1000 - 1000000;
if (!freq_hz)
return -EINVAL;
@@ -483,7 +465,7 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz)
if (dec)
dec--;
- dec = min(dec, st->info->max_dec);
+ dec = min(dec, 4095);
return __adis_write_reg_16(&st->adis, ADIS16550_REG_DEC_RATE, dec);
}
@@ -591,30 +573,30 @@ static int adis16550_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ANGL_VEL:
- *val = st->info->gyro_max_val;
- *val2 = st->info->gyro_max_scale;
+ *val = 1;
+ *val2 = IIO_RAD_TO_DEGREE(80 << 16);
return IIO_VAL_FRACTIONAL;
case IIO_ACCEL:
- *val = st->info->accel_max_val;
- *val2 = st->info->accel_max_scale;
+ *val = 1;
+ *val2 = IIO_M_S_2_TO_G(102400000);
return IIO_VAL_FRACTIONAL;
case IIO_TEMP:
- *val = st->info->temp_scale;
+ *val = 4;
return IIO_VAL_INT;
case IIO_DELTA_ANGL:
- *val = st->info->deltang_max_val;
+ *val = IIO_DEGREE_TO_RAD(720);
*val2 = 31;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_DELTA_VELOCITY:
- *val = st->info->deltvel_max_val;
+ *val = 125;
*val2 = 31;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
- /* temperature centered at 25°C */
- *val = DIV_ROUND_CLOSEST(25000, st->info->temp_scale);
+ /* temperature centered at 25°C divided by temp scale */
+ *val = 25000 / 4;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBBIAS:
ret = adis_read_reg_32(&st->adis,
@@ -792,23 +774,6 @@ static const struct adis16550_sync adis16550_sync_modes[] = {
{ ADIS16550_SYNC_MODE_SCALED, 1, 128 },
};
-static const struct adis16550_chip_info adis16550_chip_info = {
- .num_channels = ARRAY_SIZE(adis16550_channels),
- .channels = adis16550_channels,
- .name = "adis16550",
- .gyro_max_val = 1,
- .gyro_max_scale = IIO_RAD_TO_DEGREE(80 << 16),
- .accel_max_val = 1,
- .accel_max_scale = IIO_M_S_2_TO_G(102400000),
- .temp_scale = 4,
- .deltang_max_val = IIO_DEGREE_TO_RAD(720),
- .deltvel_max_val = 125,
- .int_clk = 4000,
- .max_dec = 4095,
- .sync_mode = adis16550_sync_modes,
- .num_sync = ARRAY_SIZE(adis16550_sync_modes),
-};
-
static u32 adis16550_validate_crc(__be32 *buffer, const u8 n_elem)
{
int i;
@@ -917,21 +882,21 @@ static int adis16550_config_sync(struct adis16550 *st)
if (IS_ERR(clk))
return PTR_ERR(clk);
if (!clk) {
- st->clk_freq_hz = st->info->int_clk * 1000;
+ st->clk_freq_hz = 4000000;
return 0;
}
st->clk_freq_hz = clk_get_rate(clk);
- for (i = 0; i < st->info->num_sync; i++) {
- if (st->clk_freq_hz >= st->info->sync_mode[i].min_rate &&
- st->clk_freq_hz <= st->info->sync_mode[i].max_rate) {
- sync_mode_data = &st->info->sync_mode[i];
+ for (i = 0; i < ARRAY_SIZE(adis16550_sync_modes); i++) {
+ if (st->clk_freq_hz >= adis16550_sync_modes[i].min_rate &&
+ st->clk_freq_hz <= adis16550_sync_modes[i].max_rate) {
+ sync_mode_data = &adis16550_sync_modes[i];
break;
}
}
- if (i == st->info->num_sync)
+ if (i == ARRAY_SIZE(adis16550_sync_modes))
return dev_err_probe(dev, -EINVAL, "Clk rate: %lu not in a valid range",
st->clk_freq_hz);
@@ -942,7 +907,7 @@ static int adis16550_config_sync(struct adis16550 *st)
* of [3000 4500].
*/
- sync_scale = DIV_ROUND_CLOSEST(st->info->int_clk, st->clk_freq_hz);
+ sync_scale = DIV_ROUND_CLOSEST(4000, st->clk_freq_hz);
if (3000 > sync_scale || 4500 < sync_scale)
return dev_err_probe(dev, -EINVAL,
@@ -954,7 +919,7 @@ static int adis16550_config_sync(struct adis16550 *st)
if (ret)
return ret;
- st->clk_freq_hz = st->info->int_clk;
+ st->clk_freq_hz = 4000;
}
st->clk_freq_hz *= 1000;
@@ -1063,13 +1028,11 @@ static int adis16550_probe(struct spi_device *spi)
return -ENOMEM;
st = iio_priv(indio_dev);
- st->info = spi_get_device_match_data(spi);
- if (!st->info)
- return -EINVAL;
+
adis = &st->adis;
- indio_dev->name = st->info->name;
- indio_dev->channels = st->info->channels;
- indio_dev->num_channels = st->info->num_channels;
+ indio_dev->name = "adis16550";
+ indio_dev->channels = adis16550_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16550_channels);
indio_dev->available_scan_masks = adis16550_channel_masks;
indio_dev->info = &adis16550_info;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -1116,13 +1079,13 @@ static int adis16550_probe(struct spi_device *spi)
}
static const struct spi_device_id adis16550_id[] = {
- { "adis16550", (kernel_ulong_t)&adis16550_chip_info},
+ { .name = "adis16550" },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16550_id);
static const struct of_device_id adis16550_of_match[] = {
- { .compatible = "adi,adis16550", .data = &adis16550_chip_info },
+ { .compatible = "adi,adis16550" },
{ }
};
MODULE_DEVICE_TABLE(of, adis16550_of_match);
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index 86f6ecfd64aa..1e73a7f1f017 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -5,7 +5,7 @@
* Copyright (c) 2016, Intel Corporation.
* Copyright (c) 2019, Martin Kelly.
*
- * IIO core driver for BMI160, with support for I2C/SPI busses
+ * IIO core driver for BMI160, with support for I2C/SPI buses
*
* TODO: magnetometer, hardware FIFO
*/
diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c
index 2f0a578ee40f..107eb3374922 100644
--- a/drivers/iio/imu/bmi160/bmi160_spi.c
+++ b/drivers/iio/imu/bmi160/bmi160_spi.c
@@ -34,8 +34,8 @@ static int bmi160_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id bmi160_spi_id[] = {
- {"bmi120", 0},
- {"bmi160", 0},
+ { .name = "bmi120" },
+ { .name = "bmi160" },
{ }
};
MODULE_DEVICE_TABLE(spi, bmi160_spi_id);
diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c
index 2ad230788532..fbcf6bb87acc 100644
--- a/drivers/iio/imu/bmi270/bmi270_core.c
+++ b/drivers/iio/imu/bmi270/bmi270_core.c
@@ -1434,7 +1434,7 @@ static int bmi270_trigger_probe(struct bmi270_data *data,
bmi270_irq_thread_handler,
IRQF_ONESHOT, "bmi270-int", indio_dev);
if (ret)
- return dev_err_probe(data->dev, ret, "Failed to request IRQ\n");
+ return ret;
ret = devm_iio_trigger_register(data->dev, data->trig);
if (ret)
diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c
index dc7fa01421bc..759b56b59e54 100644
--- a/drivers/iio/imu/bmi270/bmi270_spi.c
+++ b/drivers/iio/imu/bmi270/bmi270_spi.c
@@ -65,8 +65,8 @@ static int bmi270_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id bmi270_spi_id[] = {
- { "bmi260", (kernel_ulong_t)&bmi260_chip_info },
- { "bmi270", (kernel_ulong_t)&bmi270_chip_info },
+ { .name = "bmi260", .driver_data = (kernel_ulong_t)&bmi260_chip_info },
+ { .name = "bmi270", .driver_data = (kernel_ulong_t)&bmi270_chip_info },
{ }
};
@@ -75,6 +75,7 @@ static const struct of_device_id bmi270_of_match[] = {
{ .compatible = "bosch,bmi270", .data = &bmi270_chip_info },
{ }
};
+MODULE_DEVICE_TABLE(of, bmi270_of_match);
static struct spi_driver bmi270_spi_driver = {
.driver = {
diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c
index f3d499423399..ebeb1b10c38c 100644
--- a/drivers/iio/imu/bmi323/bmi323_core.c
+++ b/drivers/iio/imu/bmi323/bmi323_core.c
@@ -1128,7 +1128,7 @@ static int bmi323_set_watermark(struct iio_dev *indio_dev, unsigned int val)
{
struct bmi323_data *data = iio_priv(indio_dev);
- val = min(val, (u32)BMI323_FIFO_FULL_IN_FRAMES);
+ val = min(val, BMI323_FIFO_FULL_IN_FRAMES);
guard(mutex)(&data->mutex);
data->watermark = val;
@@ -1946,7 +1946,7 @@ static int bmi323_trigger_probe(struct bmi323_data *data,
bmi323_irq_thread_handler,
IRQF_ONESHOT, "bmi323-int", indio_dev);
if (ret)
- return dev_err_probe(data->dev, ret, "Failed to request IRQ\n");
+ return ret;
ret = devm_iio_trigger_register(data->dev, data->trig);
if (ret)
diff --git a/drivers/iio/imu/bmi323/bmi323_spi.c b/drivers/iio/imu/bmi323/bmi323_spi.c
index 92f1c4bcc192..ed0235cc40da 100644
--- a/drivers/iio/imu/bmi323/bmi323_spi.c
+++ b/drivers/iio/imu/bmi323/bmi323_spi.c
@@ -64,7 +64,7 @@ static int bmi323_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id bmi323_spi_ids[] = {
- { "bmi323" },
+ { .name = "bmi323" },
{ }
};
MODULE_DEVICE_TABLE(spi, bmi323_spi_ids);
diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c
index 281ebfd9c15a..9d2eb6fbab4a 100644
--- a/drivers/iio/imu/fxos8700_core.c
+++ b/drivers/iio/imu/fxos8700_core.c
@@ -2,7 +2,7 @@
/*
* FXOS8700 - NXP IMU (accelerometer plus magnetometer)
*
- * IIO core driver for FXOS8700, with support for I2C/SPI busses
+ * IIO core driver for FXOS8700, with support for I2C/SPI buses
*
* TODO: Buffer, trigger, and IRQ support
*/
diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c
index 3edf90220bfa..56ad0c5914d9 100644
--- a/drivers/iio/imu/fxos8700_spi.c
+++ b/drivers/iio/imu/fxos8700_spi.c
@@ -23,7 +23,7 @@ static int fxos8700_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id fxos8700_spi_id[] = {
- {"fxos8700", 0},
+ { .name = "fxos8700" },
{ }
};
MODULE_DEVICE_TABLE(spi, fxos8700_spi_id);
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
index c8b48a5c5ed0..b55d993f0264 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
@@ -6,13 +6,15 @@
#ifndef INV_ICM42600_H_
#define INV_ICM42600_H_
-#include <linux/bits.h>
#include <linux/bitfield.h>
-#include <linux/regmap.h>
+#include <linux/bits.h>
#include <linux/mutex.h>
-#include <linux/regulator/consumer.h>
#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
#include <linux/iio/iio.h>
+
#include <linux/iio/common/inv_sensors_timestamp.h>
#include "inv_icm42600_buffer.h"
@@ -354,6 +356,8 @@ struct inv_icm42600_sensor_state {
cpu_to_le16((_wm) & GENMASK(11, 0))
/* FIFO is 2048 bytes, let 12 samples for reading latency */
#define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16)
+/* INV_ICM42600_FIFO_WATERMARK_MAX / 8 = 232 */
+#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 232
#define INV_ICM42600_REG_INT_CONFIG1 0x0064
#define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6)
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
index 7df920ef3cf0..4b0e3cd8a506 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
@@ -3,25 +3,26 @@
* Copyright (C) 2020 Invensense, Inc.
*/
-#include <linux/kernel.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
-#include <linux/delay.h>
-#include <linux/math64.h>
-#include <linux/minmax.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
-#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
#include "inv_icm42600.h"
-#include "inv_icm42600_temp.h"
#include "inv_icm42600_buffer.h"
+#include "inv_icm42600_temp.h"
#define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \
{ \
@@ -1186,8 +1187,9 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels);
indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks;
- ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
- &inv_icm42600_buffer_ops);
+ ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev,
+ &inv_icm42600_buffer_ops,
+ inv_icm42600_buffer_attrs);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
index 5c3840acf085..998d312f7bde 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
@@ -3,17 +3,20 @@
* Copyright (C) 2020 Invensense, Inc.
*/
-#include <linux/kernel.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
-#include <linux/delay.h>
+#include <linux/stringify.h>
#include <linux/iio/buffer.h>
-#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
#include "inv_icm42600.h"
#include "inv_icm42600_buffer.h"
@@ -186,7 +189,7 @@ static unsigned int inv_icm42600_wm_truncate(unsigned int watermark,
* smallest latency but this is not as simple as choosing the smallest watermark
* value. Latency depends on watermark and ODR. It requires several steps:
* 1) compute gyro and accel latencies and choose the smallest value.
- * 2) adapt the choosen latency so that it is a multiple of both gyro and accel
+ * 2) adapt the chosen latency so that it is a multiple of both gyro and accel
* ones. Otherwise it is possible that you don't meet a requirement. (for
* example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the
* value of 4 will not meet accel latency requirement because 6 is not a
@@ -438,6 +441,40 @@ const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = {
.postdisable = inv_icm42600_buffer_postdisable,
};
+static ssize_t hwfifo_watermark_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int wm;
+
+ guard(mutex)(&st->lock);
+
+ if (indio_dev == st->indio_accel)
+ wm = st->fifo.watermark.eff_accel;
+ else if (indio_dev == st->indio_gyro)
+ wm = st->fifo.watermark.eff_gyro;
+ else
+ return -EINVAL;
+
+ return sysfs_emit(buf, "%u\n", wm);
+}
+
+IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "1");
+IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max,
+ __stringify(INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES));
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0);
+IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_enabled, "1");
+
+const struct iio_dev_attr *inv_icm42600_buffer_attrs[] = {
+ &iio_dev_attr_hwfifo_watermark_min,
+ &iio_dev_attr_hwfifo_watermark_max,
+ &iio_dev_attr_hwfifo_watermark,
+ &iio_dev_attr_hwfifo_enabled,
+ NULL
+};
+
int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
unsigned int max)
{
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
index 88b8b9f780af..e37ee3416337 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
@@ -6,8 +6,8 @@
#ifndef INV_ICM42600_BUFFER_H_
#define INV_ICM42600_BUFFER_H_
-#include <linux/kernel.h>
#include <linux/bits.h>
+#include <linux/kernel.h>
struct inv_icm42600_state;
@@ -80,6 +80,7 @@ ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel,
const void **timestamp, unsigned int *odr);
extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops;
+extern const struct iio_dev_attr *inv_icm42600_buffer_attrs[];
int inv_icm42600_buffer_init(struct inv_icm42600_state *st);
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
index 76eb22488e5f..dc97d8a274e3 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
@@ -3,18 +3,18 @@
* Copyright (C) 2020 Invensense, Inc.
*/
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/mutex.h>
+#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/regulator/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <linux/iio/iio.h>
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
index a18dcac93929..253bf571439d 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
@@ -3,22 +3,23 @@
* Copyright (C) 2020 Invensense, Inc.
*/
-#include <linux/kernel.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
-#include <linux/delay.h>
-#include <linux/math64.h>
#include <linux/iio/buffer.h>
-#include <linux/iio/common/inv_sensors_timestamp.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
#include "inv_icm42600.h"
-#include "inv_icm42600_temp.h"
#include "inv_icm42600_buffer.h"
+#include "inv_icm42600_temp.h"
#define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \
{ \
@@ -772,8 +773,9 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
indio_dev->setup_ops = &inv_icm42600_buffer_ops;
- ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
- &inv_icm42600_buffer_ops);
+ ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev,
+ &inv_icm42600_buffer_ops,
+ inv_icm42600_buffer_attrs);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
index 1013aff4f0ab..28552d2db91d 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
@@ -3,12 +3,12 @@
* Copyright (C) 2020 InvenSense, Inc.
*/
-#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/regmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/property.h>
+#include <linux/regmap.h>
#include "inv_icm42600.h"
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
index 57e3c448dca1..faf743bc6444 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
@@ -3,12 +3,12 @@
* Copyright (C) 2020 InvenSense, Inc.
*/
-#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/spi/spi.h>
-#include <linux/regmap.h>
#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
#include "inv_icm42600.h"
@@ -71,13 +71,13 @@ static int inv_icm42600_probe(struct spi_device *spi)
* supported by this driver
*/
static const struct spi_device_id inv_icm42600_id[] = {
- { "icm42600", INV_CHIP_ICM42600 },
- { "icm42602", INV_CHIP_ICM42602 },
- { "icm42605", INV_CHIP_ICM42605 },
- { "icm42686", INV_CHIP_ICM42686 },
- { "icm42622", INV_CHIP_ICM42622 },
- { "icm42688", INV_CHIP_ICM42688 },
- { "icm42631", INV_CHIP_ICM42631 },
+ { .name = "icm42600", .driver_data = INV_CHIP_ICM42600 },
+ { .name = "icm42602", .driver_data = INV_CHIP_ICM42602 },
+ { .name = "icm42605", .driver_data = INV_CHIP_ICM42605 },
+ { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 },
+ { .name = "icm42622", .driver_data = INV_CHIP_ICM42622 },
+ { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 },
+ { .name = "icm42631", .driver_data = INV_CHIP_ICM42631 },
{ }
};
MODULE_DEVICE_TABLE(spi, inv_icm42600_id);
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
index 727b03d541a5..7f80bda471c3 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
@@ -3,11 +3,12 @@
* Copyright (C) 2020 Invensense, Inc.
*/
-#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+
#include <linux/iio/iio.h>
#include "inv_icm42600.h"
diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig
new file mode 100644
index 000000000000..23f461f57afc
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Kconfig
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config INV_ICM42607
+ tristate
+ select IIO_BUFFER
+ select IIO_INV_SENSORS_TIMESTAMP
+
+config INV_ICM42607_I2C
+ tristate "InvenSense ICM-42607 I2C driver"
+ depends on I2C
+ select INV_ICM42607
+ select REGMAP_I2C
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over I2C.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-i2c.
+
+config INV_ICM42607_SPI
+ tristate "InvenSense ICM-42607 SPI driver"
+ depends on SPI_MASTER
+ select INV_ICM42607
+ select REGMAP_SPI
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over SPI.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-spi.
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
new file mode 100644
index 000000000000..7b907e019601
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
+inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_gyro.o
+inv-icm42607-y += inv_icm42607_accel.o
+inv-icm42607-y += inv_icm42607_temp.o
+
+obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
+inv-icm42607-i2c-y += inv_icm42607_i2c.o
+
+obj-$(CONFIG_INV_ICM42607_SPI) += inv-icm42607-spi.o
+inv-icm42607-spi-y += inv_icm42607_spi.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
new file mode 100644
index 000000000000..4d51b0da1aa1
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -0,0 +1,421 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_H_
+#define INV_ICM42607_H_
+
+#include <linux/bits.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <asm/byteorder.h>
+
+/*
+ * Serial bus slew rates. Rates are expressed as range between the two
+ * values with the midpoint as the typical rate. For the final value of
+ * 2ns, 2ns is considered the max value with no expressed minimum or
+ * typical value.
+ */
+enum inv_icm42607_slew_rate {
+ INV_ICM42607_SLEW_RATE_20_60NS = 0,
+ INV_ICM42607_SLEW_RATE_12_36NS = 1,
+ INV_ICM42607_SLEW_RATE_6_19NS = 2,
+ INV_ICM42607_SLEW_RATE_4_14NS = 3,
+ INV_ICM42607_SLEW_RATE_2_6NS = 4,
+ INV_ICM42607_SLEW_RATE_2NS = 5,
+ INV_ICM42607_SLEW_RATE_NB
+};
+
+enum inv_icm42607_sensor_mode {
+ INV_ICM42607_SENSOR_MODE_OFF = 0,
+ INV_ICM42607_SENSOR_MODE_STANDBY = 1,
+ INV_ICM42607_SENSOR_MODE_LOW_POWER = 2,
+ INV_ICM42607_SENSOR_MODE_LOW_NOISE = 3,
+ INV_ICM42607_SENSOR_MODE_NB
+};
+
+/* gyroscope fullscale values */
+enum inv_icm42607_gyro_fs {
+ INV_ICM42607_GYRO_FS_2000DPS = 0,
+ INV_ICM42607_GYRO_FS_1000DPS = 1,
+ INV_ICM42607_GYRO_FS_500DPS = 2,
+ INV_ICM42607_GYRO_FS_250DPS = 3,
+ INV_ICM42607_GYRO_FS_NB
+};
+
+/* accelerometer fullscale values */
+enum inv_icm42607_accel_fs {
+ INV_ICM42607_ACCEL_FS_16G = 0,
+ INV_ICM42607_ACCEL_FS_8G = 1,
+ INV_ICM42607_ACCEL_FS_4G = 2,
+ INV_ICM42607_ACCEL_FS_2G = 3,
+ INV_ICM42607_ACCEL_FS_NB
+};
+
+/* ODR values - Note Gyro does not support ODR less than 12.5Hz */
+enum inv_icm42607_odr {
+ INV_ICM42607_ODR_1600HZ = 5,
+ INV_ICM42607_ODR_800HZ = 6,
+ INV_ICM42607_ODR_400HZ = 7,
+ INV_ICM42607_ODR_200HZ = 8,
+ INV_ICM42607_ODR_100HZ = 9,
+ INV_ICM42607_ODR_50HZ = 10,
+ INV_ICM42607_ODR_25HZ = 11,
+ INV_ICM42607_ODR_12_5HZ = 12,
+ INV_ICM42607_ODR_6_25HZ_LP = 13,
+ INV_ICM42607_ODR_3_125HZ_LP = 14,
+ INV_ICM42607_ODR_1_5625HZ_LP = 15,
+ INV_ICM42607_ODR_NB
+};
+
+/* Low-Noise mode sensor data filter (bandwidth) */
+enum inv_icm42607_filter_bw {
+ INV_ICM42607_FILTER_BYPASS = 0,
+ INV_ICM42607_FILTER_BW_180HZ = 1,
+ INV_ICM42607_FILTER_BW_121HZ = 2,
+ INV_ICM42607_FILTER_BW_73HZ = 3,
+ INV_ICM42607_FILTER_BW_53HZ = 4,
+ INV_ICM42607_FILTER_BW_34HZ = 5,
+ INV_ICM42607_FILTER_BW_25HZ = 6,
+ INV_ICM42607_FILTER_BW_16HZ = 7,
+ INV_ICM42607_FILTER_BW_NB
+};
+
+/* Low-Power mode sensor data filter (averaging) */
+enum inv_icm42607_filter_avg {
+ INV_ICM42607_FILTER_AVG_2X = 0,
+ INV_ICM42607_FILTER_AVG_4X = 1,
+ INV_ICM42607_FILTER_AVG_8X = 2,
+ INV_ICM42607_FILTER_AVG_16X = 3,
+ INV_ICM42607_FILTER_AVG_32X = 4,
+ INV_ICM42607_FILTER_AVG_64X = 5,
+ /* values 6 and 7 also correspond to 64x. */
+};
+
+/* Temperature sensor data filter (bandwidth) */
+enum inv_icm42607_temp_filter_bw {
+ INV_ICM42607_TEMP_FILTER_BYPASS = 0,
+ INV_ICM42607_TEMP_FILTER_BW_180HZ = 1,
+ INV_ICM42607_TEMP_FILTER_BW_72HZ = 2,
+ INV_ICM42607_TEMP_FILTER_BW_34HZ = 3,
+ INV_ICM42607_TEMP_FILTER_BW_16HZ = 4,
+ INV_ICM42607_TEMP_FILTER_BW_8HZ = 5,
+ INV_ICM42607_TEMP_FILTER_BW_4HZ = 6,
+ /* value 7 also corresponds to 4Hz */
+};
+
+/* Signed so that negative values can signify an invalid condition. */
+struct inv_icm42607_sensor_conf {
+ int mode;
+ int fs;
+ int odr;
+ int filter;
+};
+#define INV_ICM42607_SENSOR_CONF_INIT { -1, -1, -1, -1 }
+
+struct inv_icm42607_conf {
+ struct inv_icm42607_sensor_conf gyro;
+ struct inv_icm42607_sensor_conf accel;
+ ktime_t gyro_stop; /* earliest time to stop the gyro */
+};
+
+struct inv_icm42607_hw {
+ const char *name;
+ const struct inv_icm42607_conf *conf;
+ u8 whoami;
+};
+
+/**
+ * struct inv_icm42607_state - driver state variables
+ * @hw: Hardware specific data.
+ * @lock: lock for serializing multiple registers access.
+ * @map: regmap pointer.
+ * @indio_accel: accelerometer IIO device.
+ * @indio_gyro: gyroscope IIO device.
+ * @vddio_supply: I/O voltage regulator for the chip.
+ * @vddio_en: I/O voltage status for runtime PM.
+ * @conf: chip sensors configurations.
+ * @orientation: sensor chip orientation relative to main hardware.
+ */
+struct inv_icm42607_state {
+ const struct inv_icm42607_hw *hw;
+ struct mutex lock;
+ struct regmap *map;
+ struct iio_dev *indio_accel;
+ struct iio_dev *indio_gyro;
+ struct regulator *vddio_supply;
+ bool vddio_en;
+ struct inv_icm42607_conf conf;
+ struct iio_mount_matrix orientation;
+};
+
+/**
+ * struct inv_icm42607_sensor_state - sensor state variables
+ * @power_mode: sensor requested power mode (for common frequencies)
+ * @filter: sensor filter.
+ */
+struct inv_icm42607_sensor_state {
+ enum inv_icm42607_sensor_mode power_mode;
+ int filter;
+};
+
+/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
+
+/* Register Map for User Bank 0 */
+#define INV_ICM42607_REG_MCLK_RDY 0x00
+
+#define INV_ICM42607_REG_DEVICE_CONFIG 0x01
+#define INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE BIT(2)
+#define INV_ICM42607_DEVICE_CONFIG_SPI_MODE BIT(0)
+
+#define INV_ICM42607_REG_SIGNAL_PATH_RESET 0x02
+#define INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET BIT(4)
+#define INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(2)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG1 0x03
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG2 0x04
+#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG3 0x05
+#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_CONFIG 0x06
+#define INV_ICM42607_INT_CONFIG_INT2_LATCHED BIT(5)
+#define INV_ICM42607_INT_CONFIG_INT2_PUSH_PULL BIT(4)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_LOW 0x00
+#define INV_ICM42607_INT_CONFIG_INT1_LATCHED BIT(2)
+#define INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL BIT(1)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW 0x00
+
+/* all sensor data are 16 bits (2 registers wide) in big-endian */
+#define INV_ICM42607_REG_TEMP_DATA1 0x09
+#define INV_ICM42607_REG_TEMP_DATA0 0x0A
+#define INV_ICM42607_REG_ACCEL_DATA_X1 0x0B
+#define INV_ICM42607_REG_ACCEL_DATA_X0 0x0C
+#define INV_ICM42607_REG_ACCEL_DATA_Y1 0x0D
+#define INV_ICM42607_REG_ACCEL_DATA_Y0 0x0E
+#define INV_ICM42607_REG_ACCEL_DATA_Z1 0x0F
+#define INV_ICM42607_REG_ACCEL_DATA_Z0 0x10
+#define INV_ICM42607_REG_GYRO_DATA_X1 0x11
+#define INV_ICM42607_REG_GYRO_DATA_X0 0x12
+#define INV_ICM42607_REG_GYRO_DATA_Y1 0x13
+#define INV_ICM42607_REG_GYRO_DATA_Y0 0x14
+#define INV_ICM42607_REG_GYRO_DATA_Z1 0x15
+#define INV_ICM42607_REG_GYRO_DATA_Z0 0x16
+#define INV_ICM42607_DATA_INVALID -32768
+
+#define INV_ICM42607_REG_TMST_FSYNCH 0x17
+#define INV_ICM42607_REG_TMST_FSYNCL 0x18
+
+/* APEX Data Registers */
+#define INV_ICM42607_REG_APEX_DATA0 0x31
+#define INV_ICM42607_REG_APEX_DATA1 0x32
+#define INV_ICM42607_REG_APEX_DATA2 0x33
+#define INV_ICM42607_REG_APEX_DATA3 0x34
+#define INV_ICM42607_REG_APEX_DATA4 0x1D
+#define INV_ICM42607_REG_APEX_DATA5 0x1E
+
+#define INV_ICM42607_REG_PWR_MGMT0 0x1F
+#define INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL BIT(7)
+#define INV_ICM42607_PWR_MGMT0_IDLE BIT(4)
+#define INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2)
+#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_GYRO_CONFIG0 0x20
+#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG0 0x21
+#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_TEMP_CONFIG0 0x22
+#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK GENMASK(6, 4)
+
+#define INV_ICM42607_REG_GYRO_CONFIG1 0x23
+#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG1 0x24
+#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK GENMASK(6, 4)
+#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_APEX_CONFIG0 0x25
+#define INV_ICM42607_APEX_CONFIG0_DMP_POWER_SAVE_EN BIT(3)
+#define INV_ICM42607_APEX_CONFIG0_DMP_INIT_EN BIT(2)
+#define INV_ICM42607_APEX_CONFIG0_DMP_MEM_RESET_EN BIT(0)
+
+#define INV_ICM42607_REG_APEX_CONFIG1 0x26
+#define INV_ICM42607_APEX_CONFIG1_SMD_ENABLE BIT(6)
+#define INV_ICM42607_APEX_CONFIG1_FF_ENABLE BIT(5)
+#define INV_ICM42607_APEX_CONFIG1_TILT_ENABLE BIT(4)
+#define INV_ICM42607_APEX_CONFIG1_PED_ENABLE BIT(3)
+#define INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_WOM_CONFIG 0x27
+#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK GENMASK(4, 3)
+#define INV_ICM42607_WOM_CONFIG_INT_MODE BIT(2)
+#define INV_ICM42607_WOM_CONFIG_MODE BIT(1)
+#define INV_ICM42607_WOM_CONFIG_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG1 0x28
+#define INV_ICM42607_FIFO_CONFIG1_MODE BIT(1)
+#define INV_ICM42607_FIFO_CONFIG1_BYPASS BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG2 0x29
+#define INV_ICM42607_REG_FIFO_CONFIG3 0x2A
+#define INV_ICM42607_FIFO_WATERMARK_VAL(_wm) \
+ cpu_to_le16((_wm) & GENMASK(11, 0))
+/* FIFO is 2048 bytes, let 12 samples for reading latency */
+#define INV_ICM42607_FIFO_WATERMARK_MAX (2048 - 12 * 16)
+#define INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE 8
+#define INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE 16
+
+#define INV_ICM42607_REG_INT_SOURCE0 0x2B
+#define INV_ICM42607_INT_SOURCE0_ST_INT1_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE0_FSYNC_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE0_DRDY_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE0_AGC_RDY_INT1_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE1 0x2C
+#define INV_ICM42607_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE1_SMD_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_SOURCE3 0x2D
+#define INV_ICM42607_INT_SOURCE3_ST_INT2_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE3_FSYNC_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE3_PLL_RDY_INT2_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE3_RESET_DONE_INT2_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE3_DRDY_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE3_FIFO_THS_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE3_FIFO_FULL_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE3_AGC_RDY_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE4 0x2E
+#define INV_ICM42607_INT_SOURCE4_I3C_ERROR_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE4_SMD_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE4_WOM_Z_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE4_WOM_Y_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE4_WOM_X_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_LOST_PKT0 0x2F
+#define INV_ICM42607_REG_FIFO_LOST_PKT1 0x30
+
+#define INV_ICM42607_REG_INTF_CONFIG0 0x35
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_FORMAT BIT(6)
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5)
+#define INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS 2
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS 3
+
+#define INV_ICM42607_REG_INTF_CONFIG1 0x36
+#define INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN BIT(3)
+#define INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN BIT(2)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_INT 0
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL 1
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_OFF 2
+
+#define INV_ICM42607_REG_INT_STATUS_DRDY 0x39
+#define INV_ICM42607_INT_STATUS_DRDY_DATA_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS 0x3A
+#define INV_ICM42607_INT_STATUS_ST BIT(7)
+#define INV_ICM42607_INT_STATUS_FSYNC BIT(6)
+#define INV_ICM42607_INT_STATUS_PLL_RDY BIT(5)
+#define INV_ICM42607_INT_STATUS_RESET_DONE BIT(4)
+#define INV_ICM42607_INT_STATUS_FIFO_THS BIT(2)
+#define INV_ICM42607_INT_STATUS_FIFO_FULL BIT(1)
+#define INV_ICM42607_INT_STATUS_AGC_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS2 0x3B
+#define INV_ICM42607_INT_STATUS2_SMD BIT(3)
+#define INV_ICM42607_INT_STATUS2_WOM_INT GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_STATUS3 0x3C
+#define INV_ICM42607_INT_STATUS3_STEP_DET BIT(5)
+#define INV_ICM42607_INT_STATUS3_STEP_CNT_OVF BIT(4)
+#define INV_ICM42607_INT_STATUS3_TILT_DET BIT(3)
+#define INV_ICM42607_INT_STATUS3_FF_DET BIT(2)
+
+/*
+ * FIFO access registers
+ * FIFO count is 16 bits (2 registers) big-endian
+ * FIFO data is a continuous read register to read FIFO content
+ */
+#define INV_ICM42607_REG_FIFO_COUNTH 0x3D
+#define INV_ICM42607_REG_FIFO_COUNTL 0x3E
+#define INV_ICM42607_REG_FIFO_DATA 0x3F
+
+#define INV_ICM42607_REG_WHOAMI 0x75
+#define INV_ICM42607P_WHOAMI 0x60
+#define INV_ICM42607_WHOAMI 0x67
+
+/*
+ * Timings as listed in section 3 of datasheet, all values listed in datasheet
+ * in ms except temp startup time... setting all values in us and using
+ * USEC_PER_MSEC to convert from values displayed in datasheet.
+ */
+#define INV_ICM42607_POWER_UP_TIME_US (100 * USEC_PER_MSEC)
+#define INV_ICM42607_RESET_TIME_US (1 * USEC_PER_MSEC)
+#define INV_ICM42607_ACCEL_STARTUP_TIME_US (10 * USEC_PER_MSEC)
+#define INV_ICM42607_GYRO_STARTUP_TIME_US (30 * USEC_PER_MSEC)
+#define INV_ICM42607_GYRO_STOP_TIME_US (45 * USEC_PER_MSEC)
+#define INV_ICM42607_TEMP_STARTUP_TIME_US 77
+
+/*
+ * Suspend delay assumed from other icm42600 series device, not
+ * documented in datasheet.
+ */
+#define INV_ICM42607_SUSPEND_DELAY_MS (2 * MSEC_PER_SEC)
+
+typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
+
+extern const struct regmap_config inv_icm42607_regmap_config;
+extern const struct inv_icm42607_hw inv_icm42607_hw_data;
+extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
+extern const struct dev_pm_ops inv_icm42607_pm_ops;
+
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
+
+int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode *gyro,
+ enum inv_icm42607_sensor_mode *accel);
+
+int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ enum iio_chan_type chan_type);
+
+int inv_icm42607_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val);
+
+int inv_icm42607_core_probe(struct regmap *regmap,
+ const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup bus_setup);
+
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st);
+
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
new file mode 100644
index 000000000000..0b3f035c2da0
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+
+#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+enum inv_icm42607_accel_scan {
+ INV_ICM42607_ACCEL_SCAN_X,
+ INV_ICM42607_ACCEL_SCAN_Y,
+ INV_ICM42607_ACCEL_SCAN_Z,
+ INV_ICM42607_ACCEL_SCAN_TEMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { }
+};
+
+static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP),
+};
+
+static const int inv_icm42607_accel_scale_nano[][2] = {
+ [INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 },
+ [INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 },
+ [INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 },
+ [INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 },
+};
+
+static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ guard(mutex)(&st->lock);
+
+ idx = st->conf.accel.fs;
+
+ *val = inv_icm42607_accel_scale_nano[idx][0];
+ *val2 = inv_icm42607_accel_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ int ret;
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_accel_scale_nano[idx][0] &&
+ val2 == inv_icm42607_accel_scale_nano[idx][1])
+ break;
+ }
+ if (idx == scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
+}
+
+/* IIO format int + micro , values 0-4 reserved. */
+static const int inv_icm42607_accel_odr[][2] = {
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+ [INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 },
+ [INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 },
+ [INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 },
+};
+
+static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ guard(mutex)(&st->lock);
+
+ odr = st->conf.accel.odr;
+
+ for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_accel_odr); i++) {
+ if (i == odr)
+ break;
+ }
+ if (i == ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_accel_odr[i][0];
+ *val2 = inv_icm42607_accel_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ int ret;
+
+ for (idx = INV_ICM42607_ODR_1600HZ;
+ idx < ARRAY_SIZE(inv_icm42607_accel_odr); idx++) {
+ if (val == inv_icm42607_accel_odr[idx][0] &&
+ val2 == inv_icm42607_accel_odr[idx][1])
+ break;
+ }
+ if (idx == ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
+}
+
+static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ break;
+ case IIO_TEMP:
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return inv_icm42607_temp_read_raw(indio_dev, chan,
+ val, val2, mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = inv_icm42607_read_sensor(indio_dev, chan, &data);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_accel_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+ *vals = (const int *)inv_icm42607_accel_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_accel_odr[INV_ICM42607_ODR_1600HZ];
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = (ARRAY_SIZE(inv_icm42607_accel_odr) -
+ INV_ICM42607_ODR_1600HZ) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+ ret = inv_icm42607_accel_write_scale(indio_dev, val, val2);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info inv_icm42607_accel_info = {
+ .read_raw = inv_icm42607_accel_read_raw,
+ .read_avail = inv_icm42607_accel_read_avail,
+ .write_raw = inv_icm42607_accel_write_raw,
+ .write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt,
+};
+
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ struct inv_icm42607_sensor_state *accel_st;
+ struct iio_dev *indio_dev;
+ const char *name;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->hw->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+
+ accel_st = iio_priv(indio_dev);
+ accel_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ accel_st->filter = INV_ICM42607_FILTER_BW_73HZ;
+
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm42607_accel_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_accel_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels);
+ iio_device_set_drvdata(indio_dev, st);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
new file mode 100644
index 000000000000..190e998f7b8e
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -0,0 +1,702 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+#include <linux/timekeeping.h>
+#include <linux/types.h>
+
+#include <asm/byteorder.h>
+
+#include "inv_icm42607.h"
+
+static bool inv_icm42607_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_MCLK_RDY ... INV_ICM42607_REG_INT_CONFIG:
+ case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_TMST_FSYNCL:
+ case INV_ICM42607_REG_APEX_DATA4 ... INV_ICM42607_REG_INTF_CONFIG1:
+ case INV_ICM42607_REG_INT_STATUS_DRDY ... INV_ICM42607_REG_FIFO_DATA:
+ case INV_ICM42607_REG_WHOAMI:
+ return true;
+ }
+
+ return false;
+}
+
+static bool inv_icm42607_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_DEVICE_CONFIG ... INV_ICM42607_REG_INT_CONFIG:
+ case INV_ICM42607_REG_PWR_MGMT0 ... INV_ICM42607_REG_INT_SOURCE4:
+ case INV_ICM42607_REG_INTF_CONFIG0 ... INV_ICM42607_REG_INTF_CONFIG1:
+ return true;
+ }
+
+ return false;
+}
+
+static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_MCLK_RDY:
+ case INV_ICM42607_REG_SIGNAL_PATH_RESET:
+ case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_APEX_DATA5:
+ case INV_ICM42607_REG_APEX_CONFIG0:
+ case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3:
+ case INV_ICM42607_REG_INT_STATUS_DRDY:
+ case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA:
+ return true;
+ }
+
+ return false;
+}
+
+const struct regmap_config inv_icm42607_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = inv_icm42607_is_writeable_reg,
+ .readable_reg = inv_icm42607_is_readable_reg,
+ .volatile_reg = inv_icm42607_is_volatile_reg,
+ .max_register = INV_ICM42607_REG_WHOAMI,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607");
+
+/* chip initial default configuration */
+static const struct inv_icm42607_conf inv_icm42607_default_conf = {
+ .gyro = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_GYRO_FS_1000DPS,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+ .accel = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_ACCEL_FS_4G,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+};
+
+const struct inv_icm42607_hw inv_icm42607_hw_data = {
+ .whoami = INV_ICM42607_WHOAMI,
+ .name = "icm42607",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_hw_data, "IIO_ICM42607");
+
+const struct inv_icm42607_hw inv_icm42607p_hw_data = {
+ .whoami = INV_ICM42607P_WHOAMI,
+ .name = "icm42607p",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+ return &st->orientation;
+}
+
+static u32 inv_icm42607_odr_to_period_us(enum inv_icm42607_odr odr)
+{
+ static const u32 odr_periods[INV_ICM42607_ODR_NB] = {
+ /* Reserved values */
+ 0, 0, 0, 0, 0,
+ /* 1600Hz */
+ 625,
+ /* 800Hz */
+ 1250,
+ /* 400Hz */
+ 2500,
+ /* 200Hz */
+ 5000,
+ /* 100 Hz */
+ 10000,
+ /* 50Hz */
+ 20000,
+ /* 25Hz */
+ 40000,
+ /* 12.5Hz */
+ 80000,
+ /* 6.25Hz */
+ 160000,
+ /* 3.125Hz */
+ 320000,
+ /* 1.5625Hz */
+ 640000,
+ };
+
+ odr = clamp(odr, INV_ICM42607_ODR_1600HZ, INV_ICM42607_ODR_1_5625HZ_LP);
+
+ return odr_periods[odr];
+}
+
+int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode *gyro,
+ enum inv_icm42607_sensor_mode *accel)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val);
+ if (ret)
+ return ret;
+
+ *gyro = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val);
+ *accel = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val);
+
+ return 0;
+}
+
+static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode gyro,
+ enum inv_icm42607_sensor_mode accel)
+{
+ enum inv_icm42607_sensor_mode oldaccel, oldgyro;
+ unsigned int sleepval_us, odr_us;
+ unsigned int val;
+ s64 disable_wait;
+ int ret;
+
+ ret = inv_icm42607_get_pwr_mgmt0(st, &oldgyro, &oldaccel);
+ if (ret)
+ return ret;
+
+ if (gyro == oldgyro && accel == oldaccel)
+ return 0;
+
+ /*
+ * Datasheet on page 14.26 says we need to ensure the gyro sensor is on
+ * for a minimum of 45ms. So if we transition from an on state to an
+ * off state make sure at least 45ms have passed before power off and
+ * wait if it hasn't. In case some platforms don't respond well to a
+ * sleep of 0, make sure the fsleep duration is > 0.
+ */
+ if (!gyro && oldgyro) {
+ disable_wait = clamp(ktime_us_delta(st->conf.gyro_stop, ktime_get()),
+ 0, INV_ICM42607_GYRO_STOP_TIME_US);
+
+ if (disable_wait > 0)
+ fsleep(disable_wait);
+ }
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro) |
+ FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel);
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ /*
+ * If a state change occurs from off to on, sleep for the startup time
+ * of the sensor. Since more than one sensor can be transitioned from
+ * off to on, select the maximum time from each of the sensors changing
+ * from off to on. Also account for the time it takes for the first
+ * sample to be ready by looking up the odr value and adding it to the
+ * sleep time. The startup time for the temp sensor is considerably
+ * smaller than the startup time for the other sensors and one or more
+ * are required to be on for the temp sensor to function, so any start
+ * delay should be enough.
+ */
+ sleepval_us = 0;
+ odr_us = 0;
+ if (accel && !oldaccel) {
+ sleepval_us = max(sleepval_us, INV_ICM42607_ACCEL_STARTUP_TIME_US);
+ odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.accel.odr));
+ }
+
+ if (gyro && !oldgyro) {
+ sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US);
+ odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.gyro.odr));
+ /* Track the earliest we can turn off the gyroscope. */
+ st->conf.gyro_stop = ktime_add_us(ktime_get(),
+ INV_ICM42607_GYRO_STOP_TIME_US);
+ }
+
+ sleepval_us += odr_us;
+
+ /*
+ * Only sleep if sleepval_us is greater than 0 in case some platforms
+ * have issues with a 0 delay. The 0 delay can happen if one or both
+ * sensors is shut down.
+ */
+ if (sleepval_us > 0)
+ fsleep(sleepval_us);
+
+ return 0;
+}
+
+/*
+ * Sanity test between old and new config values, and note if we need
+ * to update register config0 or config1.
+ */
+static void inv_icm42607_update_config(struct inv_icm42607_sensor_conf *conf,
+ struct inv_icm42607_sensor_conf *oldconf,
+ bool *config0, bool *config1)
+{
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ *config0 = (conf->fs != oldconf->fs) || (conf->odr != oldconf->odr);
+
+ *config1 = (conf->filter != oldconf->filter);
+}
+
+int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ enum iio_chan_type chan_type)
+{
+ enum inv_icm42607_sensor_mode accel_mode, gyro_mode;
+ struct inv_icm42607_sensor_conf *oldconf;
+ bool config0, config1;
+ unsigned int val;
+ int ret;
+
+ switch (chan_type) {
+ case IIO_ACCEL:
+ oldconf = &st->conf.accel;
+ break;
+ case IIO_ANGL_VEL:
+ oldconf = &st->conf.gyro;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Since we are only making one-shot calls today, we can avoid needless
+ * on/off cycles if we rely on the runtime power management to handle
+ * shutting off the sensors when not in use. The downside is if we
+ * enable both sensors then only read from one, we have to wait for
+ * runtime PM to turn it off. So just keep the mode of the sensor we
+ * are not actively using to whatever it's already set as.
+ */
+ ret = inv_icm42607_get_pwr_mgmt0(st, &gyro_mode, &accel_mode);
+ if (ret)
+ return ret;
+
+ inv_icm42607_update_config(conf, oldconf, &config0, &config1);
+
+ if (config0) {
+ if (chan_type == IIO_ANGL_VEL) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ } else {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ }
+ if (ret)
+ return ret;
+
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ if (config1) {
+ if (chan_type == IIO_ANGL_VEL) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1,
+ INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val);
+ } else {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val);
+ }
+ if (ret)
+ return ret;
+
+ oldconf->filter = conf->filter;
+ }
+
+ oldconf->mode = conf->mode;
+
+ switch (chan_type) {
+ case IIO_ACCEL:
+ return inv_icm42607_set_pwr_mgmt0(st, gyro_mode, conf->mode);
+ case IIO_ANGL_VEL:
+ return inv_icm42607_set_pwr_mgmt0(st, conf->mode, accel_mode);
+ default:
+ return -EINVAL;
+ }
+}
+
+int inv_icm42607_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *sensor_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int reg;
+ __be16 data;
+ int ret;
+
+ if ((chan->type != IIO_ANGL_VEL) && (chan->type != IIO_ACCEL))
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ if (chan->type == IIO_ANGL_VEL)
+ reg = INV_ICM42607_REG_GYRO_DATA_X1;
+ else
+ reg = INV_ICM42607_REG_ACCEL_DATA_X1;
+ break;
+ case IIO_MOD_Y:
+ if (chan->type == IIO_ANGL_VEL)
+ reg = INV_ICM42607_REG_GYRO_DATA_Y1;
+ else
+ reg = INV_ICM42607_REG_ACCEL_DATA_Y1;
+ break;
+ case IIO_MOD_Z:
+ if (chan->type == IIO_ANGL_VEL)
+ reg = INV_ICM42607_REG_GYRO_DATA_Z1;
+ else
+ reg = INV_ICM42607_REG_ACCEL_DATA_Z1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ /* enable sensor */
+ conf.mode = sensor_st->power_mode;
+ conf.filter = sensor_st->filter;
+ ret = inv_icm42607_set_sensor_conf(st, &conf, chan->type);
+ if (ret)
+ return ret;
+
+ /* read sensor register data */
+ ret = regmap_bulk_read(st->map, reg, &data, sizeof(data));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpu(data);
+ if (*val == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st,
+ const struct inv_icm42607_conf *conf)
+{
+ unsigned int val;
+ int ret;
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode);
+ val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode);
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->gyro.fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, conf->gyro.filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1,
+ INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, conf->accel.filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK,
+ INV_ICM42607_TEMP_FILTER_BW_34HZ);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0,
+ INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ st->conf = *conf;
+
+ return 0;
+}
+
+static int inv_icm42607_setup(struct inv_icm42607_state *st,
+ inv_icm42607_bus_setup inv_icm42607_bus_setup)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
+ if (ret)
+ return ret;
+
+ /* Warn, but don't fail. */
+ if (val != st->hw->whoami)
+ dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n",
+ val, st->hw->whoami, st->hw->name);
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
+ if (ret)
+ return ret;
+
+ fsleep(1 * USEC_PER_MSEC);
+
+ /*
+ * No polling interval specified in datasheet, so use reset time as
+ * polling interval and 10x reset time as timeout period.
+ */
+ ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
+ val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
+ 1 * USEC_PER_MSEC, 10 * USEC_PER_MSEC);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reset error, reset done bit not set\n");
+
+ /* Sync the regcache again after a reset. */
+ regcache_mark_dirty(st->map);
+ ret = regcache_sync(st->map);
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_bus_setup(st);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
+ val);
+ if (ret)
+ return ret;
+
+ return inv_icm42607_set_init_conf(st, st->hw->conf);
+}
+
+static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ if (st->vddio_en)
+ return 0;
+
+ ret = regulator_enable(st->vddio_supply);
+ if (ret)
+ return ret;
+
+ fsleep(INV_ICM42607_POWER_UP_TIME_US);
+
+ st->vddio_en = true;
+
+ return 0;
+}
+
+static void inv_icm42607_sensors_off(void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+ const struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ st->conf.gyro.mode = INV_ICM42607_SENSOR_MODE_OFF;
+ st->conf.accel.mode = INV_ICM42607_SENSOR_MODE_OFF;
+
+ ret = inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode,
+ st->conf.accel.mode);
+ if (ret)
+ dev_err(dev, "Unable to turn off sensors\n");
+}
+
+static void inv_icm42607_disable_vddio_reg(void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+
+ if (!st->vddio_en)
+ return;
+
+ regulator_disable(st->vddio_supply);
+
+ st->vddio_en = false;
+}
+
+int inv_icm42607_core_probe(struct regmap *regmap,
+ const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup inv_icm42607_bus_setup)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct inv_icm42607_state *st;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, st);
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->hw = hw;
+ st->map = regmap;
+
+ ret = iio_read_mount_matrix(dev, &st->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to retrieve mounting matrix\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get vdd regulator\n");
+
+ st->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(st->vddio_supply))
+ return dev_err_probe(dev, PTR_ERR(st->vddio_supply),
+ "Failed to get vddio regulator\n");
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st);
+ if (ret)
+ return ret;
+
+ /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */
+ ret = inv_icm42607_setup(st, inv_icm42607_bus_setup);
+ if (ret)
+ return ret;
+
+ /*
+ * Ensure if sensors get turned on at some point, they're turned off
+ * as part of teardown.
+ */
+ ret = devm_add_action_or_reset(dev, inv_icm42607_sensors_off, st);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
+ /* Initialize IIO device for Accel */
+ st->indio_accel = inv_icm42607_accel_init(st);
+ if (IS_ERR(st->indio_accel))
+ return PTR_ERR(st->indio_accel);
+
+ /* Initialize IIO device for Gyro */
+ st->indio_gyro = inv_icm42607_gyro_init(st);
+ if (IS_ERR(st->indio_gyro))
+ return PTR_ERR(st->indio_gyro);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
+
+static int inv_icm42607_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ inv_icm42607_disable_vddio_reg(st);
+
+ return 0;
+}
+
+static int inv_icm42607_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ /* Sync the regcache again after regulator shutdown. */
+ regcache_mark_dirty(st->map);
+ ret = regcache_sync(st->map);
+ if (ret)
+ return ret;
+
+ return pm_runtime_force_resume(dev);
+}
+
+static int inv_icm42607_runtime_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+
+ /*
+ * Set sensors state to off. Since we only support one-shot
+ * today we can use runtime PM to turn sensors off when not
+ * in use, and then when needed the reads/writes will
+ * re-enable the sensors as needed. This reduces complexity,
+ * however the tradeoff is that an unused sensor won't be
+ * turned off until the entire chip is no longer in use.
+ */
+ inv_icm42607_sensors_off(st);
+ return 0;
+}
+
+EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = {
+ SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume)
+ RUNTIME_PM_OPS(inv_icm42607_runtime_suspend, NULL, NULL)
+};
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607 device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
new file mode 100644
index 000000000000..5b4683c2dd1e
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+
+#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+enum inv_icm42607_gyro_scan {
+ INV_ICM42607_GYRO_SCAN_X,
+ INV_ICM42607_GYRO_SCAN_Y,
+ INV_ICM42607_GYRO_SCAN_Z,
+ INV_ICM42607_GYRO_SCAN_TEMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { }
+};
+
+static const struct iio_chan_spec inv_icm42607_gyro_channels[] = {
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42607_GYRO_SCAN_X,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42607_GYRO_SCAN_Y,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_GYRO_SCAN_TEMP),
+};
+
+static const int inv_icm42607_gyro_scale_nano[][2] = {
+ [INV_ICM42607_GYRO_FS_2000DPS] = { 0, 1065264 },
+ [INV_ICM42607_GYRO_FS_1000DPS] = { 0, 532632 },
+ [INV_ICM42607_GYRO_FS_500DPS] = { 0, 266316 },
+ [INV_ICM42607_GYRO_FS_250DPS] = { 0, 133158 },
+};
+
+static int inv_icm42607_gyro_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ guard(mutex)(&st->lock);
+
+ idx = st->conf.gyro.fs;
+
+ *val = inv_icm42607_gyro_scale_nano[idx][0];
+ *val2 = inv_icm42607_gyro_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_gyro_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale_nano);
+ int ret;
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_gyro_scale_nano[idx][0] &&
+ val2 == inv_icm42607_gyro_scale_nano[idx][1])
+ break;
+ }
+ if (idx == scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL);
+}
+
+/* IIO format int + micro , values 0-4 reserved. */
+static const int inv_icm42607_gyro_odr[][2] = {
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+};
+
+static int inv_icm42607_gyro_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ guard(mutex)(&st->lock);
+
+ odr = st->conf.gyro.odr;
+
+ for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_gyro_odr); i++) {
+ if (i == odr)
+ break;
+ }
+ if (i == ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_gyro_odr[i][0];
+ *val2 = inv_icm42607_gyro_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = INV_ICM42607_ODR_1600HZ;
+ idx < ARRAY_SIZE(inv_icm42607_gyro_odr); idx++) {
+ if (val == inv_icm42607_gyro_odr[idx][0] &&
+ val2 == inv_icm42607_gyro_odr[idx][1])
+ break;
+ }
+ if (idx == ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL);
+}
+
+static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ break;
+ case IIO_TEMP:
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return inv_icm42607_temp_read_raw(indio_dev, chan,
+ val, val2, mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = inv_icm42607_read_sensor(indio_dev, chan, &data);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_gyro_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ *vals = (const int *)inv_icm42607_gyro_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_gyro_odr[INV_ICM42607_ODR_1600HZ];
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = (ARRAY_SIZE(inv_icm42607_gyro_odr) -
+ INV_ICM42607_ODR_1600HZ) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ ret = inv_icm42607_gyro_write_scale(indio_dev, val, val2);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info inv_icm42607_gyro_info = {
+ .read_raw = inv_icm42607_gyro_read_raw,
+ .read_avail = inv_icm42607_gyro_read_avail,
+ .write_raw = inv_icm42607_gyro_write_raw,
+ .write_raw_get_fmt = inv_icm42607_gyro_write_raw_get_fmt,
+};
+
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm42607_sensor_state *gyro_st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->hw->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+
+ gyro_st = iio_priv(indio_dev);
+ gyro_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ gyro_st->filter = INV_ICM42607_FILTER_BW_73HZ;
+
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm42607_gyro_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_gyro_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels);
+ iio_device_set_drvdata(indio_dev, st);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
new file mode 100644
index 000000000000..e903106af84a
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK,
+ INV_ICM42607_SLEW_RATE_12_36NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2,
+ INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS);
+
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ val);
+}
+
+static int inv_icm42607_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ const struct inv_icm42607_hw *hw;
+ struct regmap *regmap;
+
+ hw = i2c_get_match_data(client);
+ if (!hw)
+ return dev_err_probe(dev, -ENODEV, "Failed to get i2c data\n");
+
+ regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to register i2c regmap\n");
+
+ return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup);
+}
+
+static const struct i2c_device_id inv_icm42607_id[] = {
+ {
+ .name = "icm42607",
+ .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data,
+ }, {
+ .name = "icm42607p",
+ .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, inv_icm42607_id);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ }, {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static struct i2c_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-i2c",
+ .of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
+ },
+ .id_table = inv_icm42607_id,
+ .probe = inv_icm42607_probe,
+};
+module_i2c_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607 I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
new file mode 100644
index 000000000000..cd739375f6b2
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ /* Only support 4-wire mode for now. */
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
+ INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK,
+ INV_ICM42607_SLEW_RATE_2NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3,
+ INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS);
+
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ val);
+}
+
+static int inv_icm42607_probe(struct spi_device *spi)
+{
+ const struct inv_icm42607_hw *hw;
+ struct device *dev = &spi->dev;
+ struct regmap *regmap;
+
+ hw = spi_get_device_match_data(spi);
+ if (!hw)
+ return dev_err_probe(dev, -ENODATA, "Failed to get SPI data\n");
+
+ if (spi->mode & SPI_3WIRE)
+ return dev_err_probe(dev, -ENODEV, "SPI 3-wire mode not supported\n");
+
+ regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to register spi regmap\n");
+
+ return inv_icm42607_core_probe(regmap, hw, inv_icm42607_spi_bus_setup);
+}
+
+static const struct spi_device_id inv_icm42607_spi_id_table[] = {
+ {
+ .name = "icm42607",
+ .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data,
+ }, {
+ .name = "icm42607p",
+ .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, inv_icm42607_spi_id_table);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ },
+ {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static struct spi_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-spi",
+ .of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
+ },
+ .id_table = inv_icm42607_spi_id_table,
+ .probe = inv_icm42607_probe,
+};
+module_spi_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
new file mode 100644
index 000000000000..53f0484c0845
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+
+static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct device *dev = regmap_get_device(st->map);
+ int ret, gyro_mode, accel_mode;
+ unsigned int val;
+ u8 raw[2];
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ /*
+ * Check if both the gyro and accel are off and if so, enable one
+ * of them. The temp sensor cannot be read if both the gyro and
+ * accel sensor are off. Prefer to enable the accel over the gyro
+ * as the datasheet says the gyro uses 5x more power and it has
+ * a minimum run time of 45ms.
+ */
+ ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val);
+ if (ret)
+ return ret;
+
+ accel_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val);
+ gyro_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val);
+ if (!gyro_mode && !accel_mode) {
+ /* enable accel sensor */
+ conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ ret = inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1,
+ raw, sizeof(raw));
+ if (ret)
+ return ret;
+
+ *temp = get_unaligned_be16(raw);
+ if (*temp == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 temp;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = inv_icm42607_temp_read(st, &temp);
+ if (ret)
+ return ret;
+ *val = temp;
+ return IIO_VAL_INT;
+ /*
+ * T°C = (temp / 128) + 25
+ * Tm°C = 1000 * ((temp * 100 / 12800) + 25)
+ * scale: 100000 / 12800 ~= 7.8125
+ * offset: 3200
+ */
+ case IIO_CHAN_INFO_SCALE:
+ *val = 7;
+ *val2 = 812500000;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 3200;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
new file mode 100644
index 000000000000..18499b4d0b94
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_TEMP_H_
+#define INV_ICM42607_TEMP_H_
+
+#include <linux/bitops.h>
+
+struct iio_dev;
+struct iio_chan_spec;
+
+#define INV_ICM42607_TEMP_CHAN(_index) \
+{ \
+ .type = IIO_TEMP, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
index 1c796d4b2a40..95fa934a42b4 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
@@ -190,7 +190,7 @@ struct inv_icm45600_sensor_state {
#define INV_ICM45600_REG_IREG_ADDR 0x7C
#define INV_ICM45600_REG_IREG_DATA 0x7E
-/* Direct acces registers */
+/* Direct access registers */
#define INV_ICM45600_REG_MISC2 0x007F
#define INV_ICM45600_MISC2_SOFT_RESET BIT(1)
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
index 2b9ea317385c..42111c543d3c 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
@@ -422,8 +422,11 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
if (max > 0 && fifo_nb > max)
fifo_nb = max;
- /* Try to read all FIFO data in internal buffer. */
- st->fifo.count = fifo_nb * packet_size;
+ /*
+ * Read all FIFO data into the internal buffer, clamping the
+ * device-reported count to the buffer capacity.
+ */
+ st->fifo.count = min(fifo_nb * packet_size, INV_ICM45600_FIFO_SIZE_MAX);
ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
st->fifo.data, st->fifo.count);
if (ret == -ENOTSUPP || ret == -EFBIG) {
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
index d49053161a65..c1d7aa7e950d 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
@@ -716,7 +716,7 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi
dev_set_drvdata(dev, st);
- st->fifo.data = devm_kzalloc(dev, 8192, GFP_KERNEL);
+ st->fifo.data = devm_kzalloc(dev, INV_ICM45600_FIFO_SIZE_MAX, GFP_KERNEL);
if (!st->fifo.data)
return -ENOMEM;
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c
index 81ba1b60f04b..69acf9bac2db 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c
@@ -22,7 +22,7 @@ static int inv_icm45600_probe(struct i2c_client *client)
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
return -ENODEV;
- chip_info = device_get_match_data(&client->dev);
+ chip_info = i2c_get_match_data(client);
if (!chip_info)
return -ENODEV;
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c
index 450a0f2abaaa..087e590e04c2 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c
@@ -48,14 +48,14 @@ static int inv_icm45600_probe(struct spi_device *spi)
* supported by this driver.
*/
static const struct spi_device_id inv_icm45600_id[] = {
- { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info },
- { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info },
- { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info },
- { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info },
- { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info },
- { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info },
- { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info },
- { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info },
+ { .name = "icm45605", .driver_data = (kernel_ulong_t)&inv_icm45605_chip_info },
+ { .name = "icm45606", .driver_data = (kernel_ulong_t)&inv_icm45606_chip_info },
+ { .name = "icm45608", .driver_data = (kernel_ulong_t)&inv_icm45608_chip_info },
+ { .name = "icm45634", .driver_data = (kernel_ulong_t)&inv_icm45634_chip_info },
+ { .name = "icm45686", .driver_data = (kernel_ulong_t)&inv_icm45686_chip_info },
+ { .name = "icm45687", .driver_data = (kernel_ulong_t)&inv_icm45687_chip_info },
+ { .name = "icm45688p", .driver_data = (kernel_ulong_t)&inv_icm45688p_chip_info },
+ { .name = "icm45689", .driver_data = (kernel_ulong_t)&inv_icm45689_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, inv_icm45600_id);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c
index 47394594d17a..6b858fdfd1c6 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c
@@ -106,9 +106,9 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
return ret;
/*
- * Sensor sentivity
+ * Sensor sensitivity
* 1 uT = 0.01 G and value is in micron (1e6)
- * sensitvity = x uT * 0.01 * 1e6
+ * sensitivity = x uT * 0.01 * 1e6
*/
switch (st->chip_type) {
case INV_MPU9150:
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
index b8204eb0b4c6..71e90ddc19dd 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
@@ -65,23 +65,23 @@ static int inv_mpu_probe(struct spi_device *spi)
* supported by this driver
*/
static const struct spi_device_id inv_mpu_id[] = {
- {"mpu6000", INV_MPU6000},
- {"mpu6500", INV_MPU6500},
- {"mpu6515", INV_MPU6515},
- {"mpu6880", INV_MPU6880},
- {"mpu9250", INV_MPU9250},
- {"mpu9255", INV_MPU9255},
- {"icm20608", INV_ICM20608},
- {"icm20608d", INV_ICM20608D},
- {"icm20609", INV_ICM20609},
- {"icm20689", INV_ICM20689},
- {"icm20600", INV_ICM20600},
- {"icm20602", INV_ICM20602},
- {"icm20690", INV_ICM20690},
- {"iam20380", INV_IAM20380},
- {"iam20680", INV_IAM20680},
- {"iam20680hp", INV_IAM20680HP},
- {"iam20680ht", INV_IAM20680HT},
+ { .name = "mpu6000", .driver_data = INV_MPU6000 },
+ { .name = "mpu6500", .driver_data = INV_MPU6500 },
+ { .name = "mpu6515", .driver_data = INV_MPU6515 },
+ { .name = "mpu6880", .driver_data = INV_MPU6880 },
+ { .name = "mpu9250", .driver_data = INV_MPU9250 },
+ { .name = "mpu9255", .driver_data = INV_MPU9255 },
+ { .name = "icm20608", .driver_data = INV_ICM20608 },
+ { .name = "icm20608d", .driver_data = INV_ICM20608D },
+ { .name = "icm20609", .driver_data = INV_ICM20609 },
+ { .name = "icm20689", .driver_data = INV_ICM20689 },
+ { .name = "icm20600", .driver_data = INV_ICM20600 },
+ { .name = "icm20602", .driver_data = INV_ICM20602 },
+ { .name = "icm20690", .driver_data = INV_ICM20690 },
+ { .name = "iam20380", .driver_data = INV_IAM20380 },
+ { .name = "iam20680", .driver_data = INV_IAM20680 },
+ { .name = "iam20680hp", .driver_data = INV_IAM20680HP },
+ { .name = "iam20680ht", .driver_data = INV_IAM20680HT },
{ }
};
diff --git a/drivers/iio/imu/smi240.c b/drivers/iio/imu/smi240.c
index d159ee59acdd..f419296bba88 100644
--- a/drivers/iio/imu/smi240.c
+++ b/drivers/iio/imu/smi240.c
@@ -593,7 +593,7 @@ static int smi240_probe(struct spi_device *spi)
}
static const struct spi_device_id smi240_spi_id[] = {
- { "smi240" },
+ { .name = "smi240" },
{ }
};
MODULE_DEVICE_TABLE(spi, smi240_spi_id);
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index f4edcb73ec8c..360f1365f892 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -2852,11 +2852,8 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw)
st_lsm6dsx_handler_thread,
irq_type | IRQF_ONESHOT,
"lsm6dsx", hw);
- if (err) {
- dev_err(hw->dev, "failed to request trigger irq %d\n",
- hw->irq);
+ if (err)
return err;
- }
return 0;
}
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c
index 2e72c7ba94dd..5321033daebc 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c
@@ -112,6 +112,11 @@ int st_lsm6dsx_fusion_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
return err;
}
+ if (enable)
+ hw->enable_mask |= BIT(ST_LSM6DSX_ID_FUSION);
+ else
+ hw->enable_mask &= ~BIT(ST_LSM6DSX_ID_FUSION);
+
return st_lsm6dsx_fusion_page_disable(hw);
}
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
index 3389b15df0bc..ed47d5a6f11c 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
@@ -138,30 +138,30 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
- { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
- { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID },
- { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
- { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
- { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
- { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID },
- { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID },
- { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
- { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
- { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID },
- { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID },
- { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID },
- { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID },
- { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID },
- { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID },
- { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID },
- { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID },
- { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID },
- { ST_LSM6DSV_DEV_NAME, ST_LSM6DSV_ID },
- { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID },
- { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID },
- { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID },
- { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID },
- { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID },
+ { .name = ST_LSM6DS3_DEV_NAME, .driver_data = ST_LSM6DS3_ID },
+ { .name = ST_LSM6DS3H_DEV_NAME, .driver_data = ST_LSM6DS3H_ID },
+ { .name = ST_LSM6DSL_DEV_NAME, .driver_data = ST_LSM6DSL_ID },
+ { .name = ST_LSM6DSM_DEV_NAME, .driver_data = ST_LSM6DSM_ID },
+ { .name = ST_ISM330DLC_DEV_NAME, .driver_data = ST_ISM330DLC_ID },
+ { .name = ST_LSM6DSO_DEV_NAME, .driver_data = ST_LSM6DSO_ID },
+ { .name = ST_ASM330LHH_DEV_NAME, .driver_data = ST_ASM330LHH_ID },
+ { .name = ST_LSM6DSOX_DEV_NAME, .driver_data = ST_LSM6DSOX_ID },
+ { .name = ST_LSM6DSR_DEV_NAME, .driver_data = ST_LSM6DSR_ID },
+ { .name = ST_LSM6DS3TRC_DEV_NAME, .driver_data = ST_LSM6DS3TRC_ID },
+ { .name = ST_ISM330DHCX_DEV_NAME, .driver_data = ST_ISM330DHCX_ID },
+ { .name = ST_LSM9DS1_DEV_NAME, .driver_data = ST_LSM9DS1_ID },
+ { .name = ST_LSM6DS0_DEV_NAME, .driver_data = ST_LSM6DS0_ID },
+ { .name = ST_LSM6DSRX_DEV_NAME, .driver_data = ST_LSM6DSRX_ID },
+ { .name = ST_LSM6DST_DEV_NAME, .driver_data = ST_LSM6DST_ID },
+ { .name = ST_LSM6DSOP_DEV_NAME, .driver_data = ST_LSM6DSOP_ID },
+ { .name = ST_ASM330LHHX_DEV_NAME, .driver_data = ST_ASM330LHHX_ID },
+ { .name = ST_LSM6DSTX_DEV_NAME, .driver_data = ST_LSM6DSTX_ID },
+ { .name = ST_LSM6DSV_DEV_NAME, .driver_data = ST_LSM6DSV_ID },
+ { .name = ST_LSM6DSV16X_DEV_NAME, .driver_data = ST_LSM6DSV16X_ID },
+ { .name = ST_LSM6DSO16IS_DEV_NAME, .driver_data = ST_LSM6DSO16IS_ID },
+ { .name = ST_ISM330IS_DEV_NAME, .driver_data = ST_ISM330IS_ID },
+ { .name = ST_ASM330LHB_DEV_NAME, .driver_data = ST_ASM330LHB_ID },
+ { .name = ST_ASM330LHHXG1_DEV_NAME, .driver_data = ST_ASM330LHHXG1_ID },
{ }
};
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c
index 8c5d8535e54c..e67f09e1f093 100644
--- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c
+++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c
@@ -32,8 +32,8 @@ static const struct of_device_id st_lsm9ds0_of_match[] = {
MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match);
static const struct spi_device_id st_lsm9ds0_id_table[] = {
- { LSM303D_IMU_DEV_NAME },
- { LSM9DS0_IMU_DEV_NAME },
+ { .name = LSM303D_IMU_DEV_NAME },
+ { .name = LSM9DS0_IMU_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, st_lsm9ds0_id_table);
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 4763e224ebc6..f7a4be8ec320 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -649,7 +649,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, "IIO_BACKEND");
* @len: Buffer length
*
* This helper is intended to be used by backends that extend an IIO channel
- * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * (through iio_backend_extend_chan_spec()) with extended info. In that case,
* backends are not supposed to give their own callbacks (as they would not have
* a way to get the backend from indio_dev). This is the setter.
*
@@ -851,7 +851,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_filter_type_set, "IIO_BACKEND");
* @back: Backend device
* @timeout_us: Timeout value in us.
*
- * When activated, it initates a proccess that aligns the sample's most
+ * When activated, it initiates a process that aligns the sample's most
* significant bit (MSB) based solely on the captured data, without
* considering any other external signals.
*
@@ -1017,7 +1017,7 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con
* @dev: Consumer device for the backend
* @name: Backend name
*
- * Get's the backend associated with @dev.
+ * Gets the backend associated with @dev.
*
* RETURNS:
* A backend pointer, negative error pointer otherwise.
@@ -1050,7 +1050,7 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get_by_index, "IIO_BACKEND");
* @name: Backend name
* @fwnode: Firmware node of the backend consumer
*
- * Get's the backend associated with a firmware node.
+ * Gets the backend associated with a firmware node.
*
* RETURNS:
* A backend pointer, negative error pointer otherwise.
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 7e3d3872e2e6..767a7794624a 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -19,6 +19,7 @@
#include <linux/idr.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/poll.h>
@@ -26,7 +27,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>
-#include <linux/wordpart.h>
#include <linux/iio/buffer.h>
#include <linux/iio/buffer_impl.h>
@@ -99,6 +99,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_ATTENTION] = "attention",
[IIO_ALTCURRENT] = "altcurrent",
[IIO_COVERAGE] = "coverage",
+ [IIO_VOLUMEFLOW] = "volumeflow",
};
static const char * const iio_modifier_names[] = {
@@ -656,6 +657,7 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
int size, const int *vals)
{
int tmp0, tmp1;
+ int l = 0;
s64 tmp2;
bool scale_db = false;
@@ -699,7 +701,6 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
case IIO_VAL_INT_MULTIPLE:
{
int i;
- int l = 0;
for (i = 0; i < size; ++i)
l += sysfs_emit_at(buf, offset + l, "%d ", vals[i]);
@@ -708,8 +709,26 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
case IIO_VAL_CHAR:
return sysfs_emit_at(buf, offset, "%c", (char)vals[0]);
case IIO_VAL_INT_64:
- tmp2 = (s64)((((u64)vals[1]) << 32) | (u32)vals[0]);
- return sysfs_emit_at(buf, offset, "%lld", tmp2);
+ return sysfs_emit_at(buf, offset, "%lld",
+ iio_val_s64_compose(vals[0], vals[1]));
+ case IIO_VAL_DECIMAL64_MILLI:
+ case IIO_VAL_DECIMAL64_MICRO:
+ case IIO_VAL_DECIMAL64_NANO:
+ case IIO_VAL_DECIMAL64_PICO:
+ case IIO_VAL_DECIMAL64_FEMTO:
+ {
+ int scale = type - IIO_VAL_DECIMAL64_BASE;
+ s64 frac;
+
+ tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]),
+ int_pow(10, scale), &frac);
+ if (tmp2 == 0 && frac < 0)
+ l += sysfs_emit_at(buf, offset, "-");
+
+ l += sysfs_emit_at(buf, offset + l, "%lld.%0*lld", tmp2, scale,
+ abs(frac));
+ return l;
+ }
default:
return 0;
}
@@ -979,6 +998,7 @@ static ssize_t iio_write_channel_info(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
int ret, fract_mult = 100000;
+ int type, dec_scale = 0;
int integer, fract = 0;
long long integer64;
bool is_char = false;
@@ -989,9 +1009,11 @@ static ssize_t iio_write_channel_info(struct device *dev,
if (!indio_dev->info->write_raw)
return -EINVAL;
- if (indio_dev->info->write_raw_get_fmt)
- switch (indio_dev->info->write_raw_get_fmt(indio_dev,
- this_attr->c, this_attr->address)) {
+ if (indio_dev->info->write_raw_get_fmt) {
+ type = indio_dev->info->write_raw_get_fmt(indio_dev,
+ this_attr->c,
+ this_attr->address);
+ switch (type) {
case IIO_VAL_INT:
fract_mult = 0;
break;
@@ -1007,12 +1029,20 @@ static ssize_t iio_write_channel_info(struct device *dev,
case IIO_VAL_CHAR:
is_char = true;
break;
+ case IIO_VAL_DECIMAL64_MILLI:
+ case IIO_VAL_DECIMAL64_MICRO:
+ case IIO_VAL_DECIMAL64_NANO:
+ case IIO_VAL_DECIMAL64_PICO:
+ case IIO_VAL_DECIMAL64_FEMTO:
+ dec_scale = type - IIO_VAL_DECIMAL64_BASE;
+ fallthrough;
case IIO_VAL_INT_64:
is_64bit = true;
break;
default:
return -EINVAL;
}
+ }
if (is_char) {
char ch;
@@ -1021,12 +1051,14 @@ static ssize_t iio_write_channel_info(struct device *dev,
return -EINVAL;
integer = ch;
} else if (is_64bit) {
- ret = kstrtoll(buf, 0, &integer64);
+ if (dec_scale)
+ ret = kstrtodec64(buf, dec_scale, &integer64);
+ else
+ ret = kstrtoll(buf, 0, &integer64);
if (ret)
return ret;
- fract = upper_32_bits(integer64);
- integer = lower_32_bits(integer64);
+ iio_val_s64_decompose(integer64, &integer, &fract);
} else {
ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract,
scale_db);
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 9ce20cb05a9b..fb08e4f02520 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -281,7 +281,7 @@ struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode,
return ERR_PTR(-ENODEV);
}
-EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name);
+EXPORT_SYMBOL_NS_GPL(fwnode_iio_channel_get_by_name, "IIO_CONSUMER");
static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
{
@@ -386,7 +386,7 @@ struct iio_channel *iio_channel_get(struct device *dev,
return iio_channel_get_sys(name, channel_name);
}
-EXPORT_SYMBOL_GPL(iio_channel_get);
+EXPORT_SYMBOL_NS_GPL(iio_channel_get, "IIO_CONSUMER");
void iio_channel_release(struct iio_channel *channel)
{
@@ -395,7 +395,7 @@ void iio_channel_release(struct iio_channel *channel)
iio_device_put(channel->indio_dev);
kfree(channel);
}
-EXPORT_SYMBOL_GPL(iio_channel_release);
+EXPORT_SYMBOL_NS_GPL(iio_channel_release, "IIO_CONSUMER");
static void devm_iio_channel_free(void *iio_channel)
{
@@ -418,7 +418,7 @@ struct iio_channel *devm_iio_channel_get(struct device *dev,
return channel;
}
-EXPORT_SYMBOL_GPL(devm_iio_channel_get);
+EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get, "IIO_CONSUMER");
struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev,
struct fwnode_handle *fwnode,
@@ -437,7 +437,7 @@ struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev,
return channel;
}
-EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name);
+EXPORT_SYMBOL_NS_GPL(devm_fwnode_iio_channel_get_by_name, "IIO_CONSUMER");
struct iio_channel *iio_channel_get_all(struct device *dev)
{
@@ -506,7 +506,7 @@ error_free_chans:
iio_device_put(chans[i].indio_dev);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(iio_channel_get_all);
+EXPORT_SYMBOL_NS_GPL(iio_channel_get_all, "IIO_CONSUMER");
void iio_channel_release_all(struct iio_channel *channels)
{
@@ -518,7 +518,7 @@ void iio_channel_release_all(struct iio_channel *channels)
}
kfree(channels);
}
-EXPORT_SYMBOL_GPL(iio_channel_release_all);
+EXPORT_SYMBOL_NS_GPL(iio_channel_release_all, "IIO_CONSUMER");
static void devm_iio_channel_free_all(void *iio_channels)
{
@@ -541,7 +541,7 @@ struct iio_channel *devm_iio_channel_get_all(struct device *dev)
return channels;
}
-EXPORT_SYMBOL_GPL(devm_iio_channel_get_all);
+EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get_all, "IIO_CONSUMER");
static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
enum iio_chan_info_enum info)
@@ -585,7 +585,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val)
return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_raw);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_raw, "IIO_CONSUMER");
int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
{
@@ -597,7 +597,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_average_raw, "IIO_CONSUMER");
int iio_multiply_value(int *result, s64 multiplier,
unsigned int type, int val, int val2)
@@ -701,7 +701,7 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
return iio_convert_raw_to_processed_unlocked(chan, raw, processed,
scale);
}
-EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
+EXPORT_SYMBOL_NS_GPL(iio_convert_raw_to_processed, "IIO_CONSUMER");
int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
enum iio_chan_info_enum attribute)
@@ -714,13 +714,13 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
return iio_channel_read(chan, val, val2, attribute);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_attribute);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_attribute, "IIO_CONSUMER");
int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
{
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_offset);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_offset, "IIO_CONSUMER");
int iio_read_channel_processed_scale(struct iio_channel *chan, int *val,
unsigned int scale)
@@ -752,20 +752,20 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val,
scale);
}
}
-EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed_scale, "IIO_CONSUMER");
int iio_read_channel_processed(struct iio_channel *chan, int *val)
{
/* This is just a special case with scale factor 1 */
return iio_read_channel_processed_scale(chan, val, 1);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_processed);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed, "IIO_CONSUMER");
int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
{
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_scale);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_scale, "IIO_CONSUMER");
static int iio_channel_read_avail(struct iio_channel *chan,
const int **vals, int *type, int *length,
@@ -794,7 +794,7 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan,
return iio_channel_read_avail(chan, vals, type, length, attribute);
}
-EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute);
+EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_attribute, "IIO_CONSUMER");
int iio_read_avail_channel_raw(struct iio_channel *chan,
const int **vals, int *length)
@@ -811,7 +811,7 @@ int iio_read_avail_channel_raw(struct iio_channel *chan,
return ret;
}
-EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw);
+EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_raw, "IIO_CONSUMER");
static int iio_channel_read_max(struct iio_channel *chan,
int *val, int *val2, int *type,
@@ -867,7 +867,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
}
-EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);
+EXPORT_SYMBOL_NS_GPL(iio_read_max_channel_raw, "IIO_CONSUMER");
static int iio_channel_read_min(struct iio_channel *chan,
int *val, int *val2, int *type,
@@ -923,7 +923,7 @@ int iio_read_min_channel_raw(struct iio_channel *chan, int *val)
return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
}
-EXPORT_SYMBOL_GPL(iio_read_min_channel_raw);
+EXPORT_SYMBOL_NS_GPL(iio_read_min_channel_raw, "IIO_CONSUMER");
int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
{
@@ -937,7 +937,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
return 0;
}
-EXPORT_SYMBOL_GPL(iio_get_channel_type);
+EXPORT_SYMBOL_NS_GPL(iio_get_channel_type, "IIO_CONSUMER");
static int iio_channel_write(struct iio_channel *chan, int val, int val2,
enum iio_chan_info_enum info)
@@ -961,13 +961,13 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,
return iio_channel_write(chan, val, val2, attribute);
}
-EXPORT_SYMBOL_GPL(iio_write_channel_attribute);
+EXPORT_SYMBOL_NS_GPL(iio_write_channel_attribute, "IIO_CONSUMER");
int iio_write_channel_raw(struct iio_channel *chan, int val)
{
return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW);
}
-EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+EXPORT_SYMBOL_NS_GPL(iio_write_channel_raw, "IIO_CONSUMER");
unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
{
@@ -982,7 +982,7 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
return i;
}
-EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
+EXPORT_SYMBOL_NS_GPL(iio_get_channel_ext_info_count, "IIO_CONSUMER");
static const struct iio_chan_spec_ext_info *
iio_lookup_ext_info(const struct iio_channel *chan, const char *attr)
@@ -1017,7 +1017,7 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
return ext_info->read(chan->indio_dev, ext_info->private,
chan->channel, buf);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_ext_info, "IIO_CONSUMER");
ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
const char *buf, size_t len)
@@ -1031,7 +1031,7 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
return ext_info->write(chan->indio_dev, ext_info->private,
chan->channel, buf, len);
}
-EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
+EXPORT_SYMBOL_NS_GPL(iio_write_channel_ext_info, "IIO_CONSUMER");
ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf)
{
@@ -1042,4 +1042,4 @@ ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf)
return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf);
}
-EXPORT_SYMBOL_GPL(iio_read_channel_label);
+EXPORT_SYMBOL_NS_GPL(iio_read_channel_label, "IIO_CONSUMER");
diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c
index 633a105fd7f0..0ea282ecc99a 100644
--- a/drivers/iio/light/adux1020.c
+++ b/drivers/iio/light/adux1020.c
@@ -808,10 +808,8 @@ static int adux1020_probe(struct i2c_client *client)
NULL, adux1020_interrupt_handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
ADUX1020_DRV_NAME, indio_dev);
- if (ret) {
- dev_err(&client->dev, "irq request error %d\n", -ret);
+ if (ret)
return ret;
- }
}
return devm_iio_device_register(&client->dev, indio_dev);
diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c
index 8dacb1730429..d7e0924b8106 100644
--- a/drivers/iio/light/apds9160.c
+++ b/drivers/iio/light/apds9160.c
@@ -1545,11 +1545,8 @@ static int apds9160_probe(struct i2c_client *client)
apds9160_irq_handler,
IRQF_ONESHOT, "apds9160_event",
indio_dev);
- if (ret) {
- return dev_err_probe(dev, ret,
- "request irq (%d) failed\n",
- client->irq);
- }
+ if (ret)
+ return ret;
} else {
indio_dev->info = &apds9160_info_no_events;
indio_dev->channels = apds9160_channels_without_events;
diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c
index d60ade1209f3..f0abbc4a862a 100644
--- a/drivers/iio/light/apds9300.c
+++ b/drivers/iio/light/apds9300.c
@@ -432,10 +432,8 @@ static int apds9300_probe(struct i2c_client *client)
NULL, apds9300_interrupt_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"apds9300_event", indio_dev);
- if (ret) {
- dev_err(&client->dev, "irq request error %d\n", -ret);
+ if (ret)
goto err;
- }
}
ret = iio_device_register(indio_dev);
diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c
index d582bda4d847..f681b02e4922 100644
--- a/drivers/iio/light/apds9306.c
+++ b/drivers/iio/light/apds9306.c
@@ -1284,8 +1284,7 @@ static int apds9306_probe(struct i2c_client *client)
apds9306_irq_handler, IRQF_ONESHOT,
"apds9306_event", indio_dev);
if (ret)
- return dev_err_probe(dev, ret,
- "failed to assign interrupt.\n");
+ return ret;
} else {
indio_dev->info = &apds9306_info_no_events;
indio_dev->channels = apds9306_channels_without_events;
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index 2686c3b0c03b..afe520de60e2 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -1098,10 +1098,8 @@ static int apds9960_probe(struct i2c_client *client)
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"apds9960_event",
indio_dev);
- if (ret) {
- dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+ if (ret)
goto error_power_down;
- }
ret = iio_device_register(indio_dev);
if (ret)
diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c
index 0aa8e5cc6c56..2b8ff36071b8 100644
--- a/drivers/iio/light/bh1745.c
+++ b/drivers/iio/light/bh1745.c
@@ -784,8 +784,7 @@ static int bh1745_setup_triggered_buffer(struct iio_dev *indio_dev,
IRQF_ONESHOT,
"bh1745_interrupt", indio_dev);
if (ret)
- return dev_err_probe(dev, ret,
- "Request for IRQ failed\n");
+ return ret;
}
return 0;
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index 2590fc8fd154..b32a94028f09 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -368,7 +368,7 @@ static int cm32181_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
cm32181->calibscale = val;
- return val;
+ return 0;
case IIO_CHAN_INFO_INT_TIME:
ret = cm32181_write_als_it(cm32181, val2);
return ret;
diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c
index fbb9e0a13733..506c91f44f43 100644
--- a/drivers/iio/light/cm3323.c
+++ b/drivers/iio/light/cm3323.c
@@ -80,22 +80,19 @@ static int cm3323_init(struct iio_dev *indio_dev)
{
int ret;
struct cm3323_data *data = iio_priv(indio_dev);
+ struct device *dev = &data->client->dev;
ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_conf\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error reading reg_conf\n");
/* enable sensor and set auto force mode */
ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
data->reg_conf = ret;
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_conf\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error writing reg_conf\n");
return 0;
}
@@ -236,10 +233,8 @@ static int cm3323_probe(struct i2c_client *client)
indio_dev->modes = INDIO_DIRECT_MODE;
ret = cm3323_init(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "cm3323 chip init failed\n");
+ if (ret < 0)
return ret;
- }
ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev);
if (ret < 0)
diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c
index 98c84f33db60..34c127593947 100644
--- a/drivers/iio/light/cm3605.c
+++ b/drivers/iio/light/cm3605.c
@@ -232,10 +232,8 @@ static int cm3605_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, irq, cm3605_prox_irq,
NULL, 0, "cm3605", indio_dev);
- if (ret) {
- dev_err(dev, "unable to request IRQ\n");
+ if (ret)
goto out_disable_aset;
- }
/* Just name the trigger the same as the driver */
led_trigger_register_simple("cm3605", &cm3605->led);
@@ -324,3 +322,4 @@ module_platform_driver(cm3605_driver);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c
index de29081fd3b2..1fd40d60182e 100644
--- a/drivers/iio/light/gp2ap002.c
+++ b/drivers/iio/light/gp2ap002.c
@@ -580,10 +580,8 @@ static int gp2ap002_probe(struct i2c_client *client)
ret = devm_request_threaded_irq(dev, client->irq, NULL,
gp2ap002_prox_irq, IRQF_ONESHOT,
"gp2ap002", indio_dev);
- if (ret) {
- dev_err(dev, "unable to request IRQ\n");
+ if (ret)
goto out_put_pm;
- }
gp2ap002->irq = client->irq;
/*
@@ -731,3 +729,4 @@ module_i2c_driver(gp2ap002_driver);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 6bf1bc9a38fe..929343e42d83 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -33,9 +33,9 @@ struct als_state {
u32 illum[CHANNEL_SCAN_INDEX_MAX];
aligned_s64 timestamp;
} scan;
- int scale_pre_decml;
- int scale_post_decml;
- int scale_precision;
+ int scale_pre_decml[CHANNEL_SCAN_INDEX_MAX];
+ int scale_post_decml[CHANNEL_SCAN_INDEX_MAX];
+ int scale_precision[CHANNEL_SCAN_INDEX_MAX];
int value_offset;
int num_channels;
s64 timestamp;
@@ -119,9 +119,8 @@ static const struct iio_chan_spec als_channels[] = {
/* Channel read_raw handler */
static int als_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct als_state *als_state = iio_priv(indio_dev);
struct hid_sensor_hub_device *hsdev = als_state->common_attributes.hsdev;
@@ -162,12 +161,12 @@ static int als_read_raw(struct iio_dev *indio_dev,
}
if (report_id >= 0) {
hid_sensor_power_state(&als_state->common_attributes,
- true);
+ true);
*val = sensor_hub_input_attr_get_raw_value(
hsdev, hsdev->usage, address, report_id,
SENSOR_HUB_SYNC, min < 0);
hid_sensor_power_state(&als_state->common_attributes,
- false);
+ false);
} else {
*val = 0;
return -EINVAL;
@@ -175,9 +174,11 @@ static int als_read_raw(struct iio_dev *indio_dev,
ret_type = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
- *val = als_state->scale_pre_decml;
- *val2 = als_state->scale_post_decml;
- ret_type = als_state->scale_precision;
+ if (chan->scan_index >= CHANNEL_SCAN_INDEX_MAX)
+ return -EINVAL;
+ *val = als_state->scale_pre_decml[chan->scan_index];
+ *val2 = als_state->scale_post_decml[chan->scan_index];
+ ret_type = als_state->scale_precision[chan->scan_index];
break;
case IIO_CHAN_INFO_OFFSET:
*val = als_state->value_offset;
@@ -205,10 +206,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int als_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct als_state *als_state = iio_priv(indio_dev);
int ret = 0;
@@ -240,8 +239,7 @@ static const struct iio_info als_info = {
/* Callback handler to send event after all samples are received and captured */
static int als_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct als_state *als_state = iio_priv(indio_dev);
@@ -262,9 +260,9 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- size_t raw_len, char *raw_data,
- void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct als_state *als_state = iio_priv(indio_dev);
@@ -303,9 +301,9 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id*/
static int als_parse_report(struct platform_device *pdev,
- struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- struct als_state *st)
+ struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct als_state *st)
{
struct iio_chan_spec *channels;
int ret, index = 0;
@@ -331,6 +329,10 @@ static int als_parse_report(struct platform_device *pdev,
};
++index;
+ st->scale_precision[i] = hid_sensor_format_scale(usage_id,
+ &st->als[i], &st->scale_pre_decml[i],
+ &st->scale_post_decml[i]);
+
dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index,
st->als[i].report_id);
}
@@ -340,10 +342,6 @@ static int als_parse_report(struct platform_device *pdev,
if (index)
ret = 0;
- st->scale_precision = hid_sensor_format_scale(usage_id,
- &st->als[CHANNEL_SCAN_INDEX_INTENSITY],
- &st->scale_pre_decml, &st->scale_post_decml);
-
return ret;
}
@@ -399,31 +397,31 @@ static int hid_als_probe(struct platform_device *pdev)
atomic_set(&als_state->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &als_state->common_attributes);
+ &als_state->common_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
als_state->callbacks.send_event = als_proc_event;
als_state->callbacks.capture_sample = als_capture_sample;
als_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return ret;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
return ret;
@@ -436,8 +434,8 @@ static void hid_als_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct als_state *als_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
}
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index 95bf2bfd86ee..63fd3eff171c 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -69,9 +69,8 @@ static const struct iio_chan_spec prox_channels[] = {
/* Channel read_raw handler */
static int prox_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct prox_state *prox_state = iio_priv(indio_dev);
struct hid_sensor_hub_device *hsdev;
@@ -134,10 +133,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int prox_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct prox_state *prox_state = iio_priv(indio_dev);
int ret = 0;
@@ -165,8 +162,7 @@ static const struct iio_info prox_info = {
/* Callback handler to send event after all samples are received and captured */
static int prox_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct prox_state *prox_state = iio_priv(indio_dev);
@@ -182,9 +178,9 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int prox_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- size_t raw_len, char *raw_data,
- void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct prox_state *prox_state = iio_priv(indio_dev);
@@ -217,8 +213,8 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id*/
static int prox_parse_report(struct platform_device *pdev,
- struct hid_sensor_hub_device *hsdev,
- struct prox_state *st)
+ struct hid_sensor_hub_device *hsdev,
+ struct prox_state *st)
{
struct iio_chan_spec *channels = st->channels;
int index = 0;
@@ -270,8 +266,7 @@ static int hid_prox_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct prox_state *prox_state;
- indio_dev = devm_iio_device_alloc(&pdev->dev,
- sizeof(struct prox_state));
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct prox_state));
if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
@@ -305,18 +300,12 @@ static int hid_prox_probe(struct platform_device *pdev)
atomic_set(&prox_state->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &prox_state->common_attributes);
+ &prox_state->common_attributes);
if (ret) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
prox_state->callbacks.send_event = prox_proc_event;
prox_state->callbacks.capture_sample = prox_capture_sample;
prox_state->callbacks.pdev = pdev;
@@ -324,13 +313,19 @@ static int hid_prox_probe(struct platform_device *pdev)
&prox_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return ret;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
return ret;
@@ -343,8 +338,8 @@ static void hid_prox_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct prox_state *prox_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
}
diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c
index b88e7c4eae3e..33deb1726689 100644
--- a/drivers/iio/light/isl29028.c
+++ b/drivers/iio/light/isl29028.c
@@ -333,16 +333,6 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
return isl29028_read_als_ir(chip, ir_data);
}
-static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on)
-{
- struct device *dev = regmap_get_device(chip->regmap);
-
- if (on)
- return pm_runtime_resume_and_get(dev);
-
- return pm_runtime_put_autosuspend(dev);
-}
-
/* Channel IO */
static int isl29028_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
@@ -352,7 +342,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
struct device *dev = regmap_get_device(chip->regmap);
int ret;
- ret = isl29028_set_pm_runtime_busy(chip, true);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
@@ -405,11 +395,11 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- ret = isl29028_set_pm_runtime_busy(chip, false);
+ ret = pm_runtime_put_autosuspend(dev);
if (ret < 0)
return ret;
- return ret;
+ return 0;
}
static int isl29028_read_raw(struct iio_dev *indio_dev,
@@ -420,7 +410,7 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
struct device *dev = regmap_get_device(chip->regmap);
int ret, pm_ret;
- ret = isl29028_set_pm_runtime_busy(chip, true);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
@@ -476,10 +466,10 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
/**
* Preserve the ret variable if the call to
- * isl29028_set_pm_runtime_busy() is successful so the reading
+ * pm_runtime_put_autosuspend() is successful so the reading
* (if applicable) is returned to user space.
*/
- pm_ret = isl29028_set_pm_runtime_busy(chip, false);
+ pm_ret = pm_runtime_put_autosuspend(dev);
if (pm_ret < 0)
return pm_ret;
diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c
index bdc74b8226c8..bc031f2c3141 100644
--- a/drivers/iio/light/ltr390.c
+++ b/drivers/iio/light/ltr390.c
@@ -838,8 +838,7 @@ static int ltr390_probe(struct i2c_client *client)
"ltr390_thresh_event",
indio_dev);
if (ret)
- return dev_err_probe(dev, ret,
- "request irq (%d) failed\n", client->irq);
+ return ret;
}
ret = ltr390_pm_init(data);
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 7d045be78c6d..fa0d16890c8b 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -1537,11 +1537,8 @@ static int ltr501_probe(struct i2c_client *client)
IRQF_ONESHOT,
"ltr501_thresh_event",
indio_dev);
- if (ret) {
- dev_err(&client->dev, "request irq (%d) failed\n",
- client->irq);
+ if (ret)
return ret;
- }
} else {
indio_dev->info = data->chip_info->info_no_irq;
}
diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c
index c5bd7fa23fb7..a948353014ac 100644
--- a/drivers/iio/light/opt3001.c
+++ b/drivers/iio/light/opt3001.c
@@ -10,6 +10,7 @@
#include <linux/array_size.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/errno.h>
@@ -100,9 +101,14 @@ struct opt3001_chip_info {
struct opt3001 {
struct i2c_client *client;
- struct device *dev;
+ /*
+ * Ensure data capture and read-modify-write sequences are
+ * not interrupted.
+ */
struct mutex lock;
+
+ /* Allows for IRQs to bypass locking mechanism */
bool ok_to_ignore_lock;
bool result_ready;
wait_queue_head_t result_ready_queue;
@@ -223,11 +229,10 @@ static const struct opt3001_scale opt3002_scales[] = {
},
};
-static int opt3001_find_scale(const struct opt3001 *opt, int val,
- int val2, u8 *exponent)
+static int opt3001_find_scale(const struct opt3001 *opt, int val, int val2,
+ u8 *exponent)
{
- int i;
- for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) {
+ for (unsigned int i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) {
const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i];
/*
* Compare the integer and micro parts to determine value scale.
@@ -242,8 +247,8 @@ static int opt3001_find_scale(const struct opt3001 *opt, int val,
return -EINVAL;
}
-static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent,
- u16 mantissa, int *val, int *val2)
+static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, u16 mantissa,
+ int *val, int *val2)
{
int ret;
int whole = opt->chip_info->factor_whole;
@@ -310,123 +315,143 @@ static const struct iio_chan_spec opt3002_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(1),
};
-static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2)
+static int opt3001_start_conversion(struct opt3001 *opt)
{
- int ret;
- u16 mantissa;
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
u16 reg;
- u8 exponent;
- u16 value;
- long timeout;
-
- if (opt->use_irq) {
- /*
- * Enable the end-of-conversion interrupt mechanism. Note that
- * doing so will overwrite the low-level limit value however we
- * will restore this value later on.
- */
- ret = i2c_smbus_write_word_swapped(opt->client,
- OPT3001_LOW_LIMIT,
- OPT3001_LOW_LIMIT_EOC_ENABLE);
- if (ret < 0) {
- dev_err(opt->dev, "failed to write register %02x\n",
- OPT3001_LOW_LIMIT);
- return ret;
- }
-
- /* Allow IRQ to access the device despite lock being set */
- opt->ok_to_ignore_lock = true;
- }
+ int ret;
/* Reset data-ready indicator flag */
opt->result_ready = false;
/* Configure for single-conversion mode and start a new conversion */
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION);
if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_CONFIGURATION);
- goto err;
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
}
reg = ret;
opt3001_set_mode(opt, &reg, OPT3001_CONFIGURATION_M_SINGLE);
- ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
- reg);
+ ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg);
+ if (ret)
+ dev_err(dev, "failed to write register %02x\n",
+ OPT3001_CONFIGURATION);
+
+ return ret;
+}
+
+static int opt3001_get_processed_irq(struct opt3001 *opt)
+{
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
+ u16 value;
+ int ret;
+
+ /*
+ * Enable the end-of-conversion interrupt mechanism. Note that doing so
+ * will overwrite the low-level limit value however we will restore this
+ * value later on.
+ */
+ ret = i2c_smbus_write_word_swapped(client,
+ OPT3001_LOW_LIMIT,
+ OPT3001_LOW_LIMIT_EOC_ENABLE);
if (ret < 0) {
- dev_err(opt->dev, "failed to write register %02x\n",
- OPT3001_CONFIGURATION);
- goto err;
+ dev_err(dev, "failed to write register %02x\n",
+ OPT3001_LOW_LIMIT);
+ return ret;
}
- if (opt->use_irq) {
- /* Wait for the IRQ to indicate the conversion is complete */
- ret = wait_event_timeout(opt->result_ready_queue,
- opt->result_ready,
- msecs_to_jiffies(OPT3001_RESULT_READY_LONG));
- if (ret == 0) {
- ret = -ETIMEDOUT;
- goto err;
- }
- } else {
- /* Sleep for result ready time */
- timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ?
- OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG;
- msleep(timeout);
-
- /* Check result ready flag */
- ret = i2c_smbus_read_word_swapped(opt->client,
- OPT3001_CONFIGURATION);
- if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_CONFIGURATION);
- goto err;
- }
+ /* Allow IRQ to access the device despite lock being set */
+ opt->ok_to_ignore_lock = true;
- if (!(ret & OPT3001_CONFIGURATION_CRF)) {
- ret = -ETIMEDOUT;
- goto err;
- }
+ ret = opt3001_start_conversion(opt);
+ if (ret)
+ goto err;
- /* Obtain value */
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
- if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_RESULT);
- goto err;
- }
- opt->result = ret;
- opt->result_ready = true;
- }
+ if (wait_event_timeout(opt->result_ready_queue, opt->result_ready,
+ msecs_to_jiffies(OPT3001_RESULT_READY_LONG)))
+ ret = 0;
+ else
+ ret = -ETIMEDOUT;
err:
- if (opt->use_irq)
- /* Disallow IRQ to access the device while lock is active */
- opt->ok_to_ignore_lock = false;
+ opt->ok_to_ignore_lock = false;
if (ret < 0)
return ret;
- if (opt->use_irq) {
- /*
- * Disable the end-of-conversion interrupt mechanism by
- * restoring the low-level limit value (clearing
- * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing
- * those enable bits would affect the actual limit value due to
- * bit-overlap and therefore can't be done.
- */
- value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
- ret = i2c_smbus_write_word_swapped(opt->client,
- OPT3001_LOW_LIMIT,
- value);
- if (ret < 0) {
- dev_err(opt->dev, "failed to write register %02x\n",
- OPT3001_LOW_LIMIT);
- return ret;
- }
+ /*
+ * Disable the end-of-conversion interrupt mechanism by restoring the
+ * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note
+ * that selectively clearing those enable bits would affect the actual
+ * limit value due to bit-overlap and therefore can't be done.
+ */
+ value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
+ ret = i2c_smbus_write_word_swapped(client, OPT3001_LOW_LIMIT, value);
+ if (ret)
+ dev_err(dev, "failed to write register %02x\n",
+ OPT3001_LOW_LIMIT);
+
+ return ret;
+}
+
+static int opt3001_get_processed_noirq(struct opt3001 *opt)
+{
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
+ int ret;
+
+ ret = opt3001_start_conversion(opt);
+ if (ret)
+ return ret;
+
+ if (opt->int_time == OPT3001_INT_TIME_SHORT)
+ msleep(OPT3001_RESULT_READY_SHORT);
+ else
+ msleep(OPT3001_RESULT_READY_LONG);
+
+ /* Check result ready flag */
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
}
+ if (!(ret & OPT3001_CONFIGURATION_CRF))
+ return -ETIMEDOUT;
+
+ /* Obtain value */
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT);
+ if (ret < 0) {
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_RESULT);
+ return ret;
+ }
+
+ opt->result = ret;
+ opt->result_ready = true;
+
+ return 0;
+}
+
+static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2)
+{
+ u16 mantissa;
+ u8 exponent;
+ int ret;
+
+ if (opt->use_irq)
+ ret = opt3001_get_processed_irq(opt);
+ else
+ ret = opt3001_get_processed_noirq(opt);
+ if (ret)
+ return ret;
+
exponent = OPT3001_REG_EXPONENT(opt->result);
mantissa = OPT3001_REG_MANTISSA(opt->result);
@@ -445,13 +470,15 @@ static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2)
static int opt3001_set_int_time(struct opt3001 *opt, int time)
{
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
int ret;
u16 reg;
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION);
if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_CONFIGURATION);
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
return ret;
}
@@ -470,16 +497,14 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time)
return -EINVAL;
}
- return i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
- reg);
+ return i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg);
}
static int opt3001_read_raw(struct iio_dev *iio,
- struct iio_chan_spec const *chan, int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct opt3001 *opt = iio_priv(iio);
- int ret;
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
return -EBUSY;
@@ -487,31 +512,24 @@ static int opt3001_read_raw(struct iio_dev *iio,
if (chan->type != opt->chip_info->chan_type)
return -EINVAL;
- mutex_lock(&opt->lock);
+ guard(mutex)(&opt->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
- ret = opt3001_get_processed(opt, val, val2);
- break;
+ return opt3001_get_processed(opt, val, val2);
case IIO_CHAN_INFO_INT_TIME:
- ret = opt3001_get_int_time(opt, val, val2);
- break;
+ return opt3001_get_int_time(opt, val, val2);
default:
- ret = -EINVAL;
+ return -EINVAL;
}
-
- mutex_unlock(&opt->lock);
-
- return ret;
}
static int opt3001_write_raw(struct iio_dev *iio,
- struct iio_chan_spec const *chan, int val, int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct opt3001 *opt = iio_priv(iio);
- int ret;
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
return -EBUSY;
@@ -525,47 +543,46 @@ static int opt3001_write_raw(struct iio_dev *iio,
if (val != 0)
return -EINVAL;
- mutex_lock(&opt->lock);
- ret = opt3001_set_int_time(opt, val2);
- mutex_unlock(&opt->lock);
+ guard(mutex)(&opt->lock);
- return ret;
+ return opt3001_set_int_time(opt, val2);
}
static int opt3001_read_event_value(struct iio_dev *iio,
- 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)
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
{
struct opt3001 *opt = iio_priv(iio);
- int ret = IIO_VAL_INT_PLUS_MICRO;
- mutex_lock(&opt->lock);
+ guard(mutex)(&opt->lock);
switch (dir) {
case IIO_EV_DIR_RISING:
opt3001_to_iio_ret(opt, opt->high_thresh_exp,
- opt->high_thresh_mantissa, val, val2);
- break;
+ opt->high_thresh_mantissa, val, val2);
+ return IIO_VAL_INT_PLUS_MICRO;
case IIO_EV_DIR_FALLING:
opt3001_to_iio_ret(opt, opt->low_thresh_exp,
- opt->low_thresh_mantissa, val, val2);
- break;
+ opt->low_thresh_mantissa, val, val2);
+ return IIO_VAL_INT_PLUS_MICRO;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
-
- mutex_unlock(&opt->lock);
-
- return ret;
}
static int opt3001_write_event_value(struct iio_dev *iio,
- 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)
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
{
struct opt3001 *opt = iio_priv(iio);
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
int ret;
int whole;
int integer;
@@ -580,12 +597,12 @@ static int opt3001_write_event_value(struct iio_dev *iio,
if (val < 0)
return -EINVAL;
- mutex_lock(&opt->lock);
+ guard(mutex)(&opt->lock);
ret = opt3001_find_scale(opt, val, val2, &exponent);
if (ret < 0) {
- dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2);
- goto err;
+ dev_err(dev, "can't find scale for %d.%06u\n", val, val2);
+ return ret;
}
whole = opt->chip_info->factor_whole;
@@ -608,25 +625,22 @@ static int opt3001_write_event_value(struct iio_dev *iio,
opt->low_thresh_exp = exponent;
break;
default:
- ret = -EINVAL;
- goto err;
+ return -EINVAL;
}
- ret = i2c_smbus_write_word_swapped(opt->client, reg, value);
+ ret = i2c_smbus_write_word_swapped(client, reg, value);
if (ret < 0) {
- dev_err(opt->dev, "failed to write register %02x\n", reg);
- goto err;
+ dev_err(dev, "failed to write register %02x\n", reg);
+ return ret;
}
-err:
- mutex_unlock(&opt->lock);
-
- return ret;
+ return 0;
}
static int opt3001_read_event_config(struct iio_dev *iio,
- const struct iio_chan_spec *chan, enum iio_event_type type,
- enum iio_event_direction dir)
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
{
struct opt3001 *opt = iio_priv(iio);
@@ -634,10 +648,14 @@ static int opt3001_read_event_config(struct iio_dev *iio,
}
static int opt3001_write_event_config(struct iio_dev *iio,
- const struct iio_chan_spec *chan, enum iio_event_type type,
- enum iio_event_direction dir, bool state)
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
{
struct opt3001 *opt = iio_priv(iio);
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
int ret;
u16 mode;
u16 reg;
@@ -648,33 +666,29 @@ static int opt3001_write_event_config(struct iio_dev *iio,
if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN)
return 0;
- mutex_lock(&opt->lock);
+ guard(mutex)(&opt->lock);
mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS
: OPT3001_CONFIGURATION_M_SHUTDOWN;
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION);
if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_CONFIGURATION);
- goto err;
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
}
reg = ret;
opt3001_set_mode(opt, &reg, mode);
- ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
- reg);
+ ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg);
if (ret < 0) {
- dev_err(opt->dev, "failed to write register %02x\n",
- OPT3001_CONFIGURATION);
- goto err;
+ dev_err(dev, "failed to write register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
}
-err:
- mutex_unlock(&opt->lock);
-
- return ret;
+ return 0;
}
static const struct iio_info opt3001_info = {
@@ -689,47 +703,69 @@ static const struct iio_info opt3001_info = {
static int opt3001_read_id(struct opt3001 *opt)
{
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
char manufacturer[2];
u16 device_id;
int ret;
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_MANUFACTURER_ID);
- if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_MANUFACTURER_ID);
- return ret;
- }
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read register %02x\n",
+ OPT3001_MANUFACTURER_ID);
manufacturer[0] = ret >> 8;
manufacturer[1] = ret & 0xff;
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID);
- if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_DEVICE_ID);
- return ret;
- }
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read register %02x\n",
+ OPT3001_DEVICE_ID);
device_id = ret;
- dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0],
- manufacturer[1], device_id);
+ dev_info(dev, "Found %c%c OPT%04x\n", manufacturer[0], manufacturer[1],
+ device_id);
return 0;
}
-static int opt3001_configure(struct opt3001 *opt)
+static void opt3001_power_off(void *data)
{
+ struct opt3001 *opt = data;
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
+ u16 reg_val;
int ret;
- u16 reg;
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION);
if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_CONFIGURATION);
- return ret;
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ return;
}
+ reg_val = ret;
+ opt3001_set_mode(opt, &reg_val, OPT3001_CONFIGURATION_M_SHUTDOWN);
+
+ ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg_val);
+ if (ret < 0)
+ dev_err(dev, "failed to write to register %02x\n",
+ OPT3001_CONFIGURATION);
+}
+
+static int opt3001_configure(struct opt3001 *opt)
+{
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
+ int ret;
+ u16 reg;
+
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+
reg = ret;
/* Enable automatic full-scale setting mode */
@@ -751,30 +787,28 @@ static int opt3001_configure(struct opt3001 *opt)
reg &= ~OPT3001_CONFIGURATION_ME;
reg &= ~OPT3001_CONFIGURATION_FC_MASK;
- ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
- reg);
- if (ret < 0) {
- dev_err(opt->dev, "failed to write register %02x\n",
- OPT3001_CONFIGURATION);
- return ret;
- }
+ ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to write register %02x\n",
+ OPT3001_CONFIGURATION);
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_LOW_LIMIT);
- if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_LOW_LIMIT);
- return ret;
- }
+ ret = devm_add_action_or_reset(dev, opt3001_power_off, opt);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register power off function\n");
+
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_LOW_LIMIT);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read register %02x\n",
+ OPT3001_LOW_LIMIT);
opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret);
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_HIGH_LIMIT);
- if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_HIGH_LIMIT);
- return ret;
- }
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_HIGH_LIMIT);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read register %02x\n",
+ OPT3001_HIGH_LIMIT);
opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret);
@@ -786,6 +820,8 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
{
struct iio_dev *iio = _iio;
struct opt3001 *opt = iio_priv(iio);
+ struct i2c_client *client = opt->client;
+ struct device *dev = &client->dev;
int ret;
bool wake_result_ready_queue = false;
enum iio_chan_type chan_type = opt->chip_info->chan_type;
@@ -794,10 +830,10 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
if (!ok_to_ignore_lock)
mutex_lock(&opt->lock);
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION);
if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_CONFIGURATION);
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
goto out;
}
@@ -805,21 +841,21 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
OPT3001_CONFIGURATION_M_CONTINUOUS) {
if (ret & OPT3001_CONFIGURATION_FH)
iio_push_event(iio,
- IIO_UNMOD_EVENT_CODE(chan_type, 0,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_RISING),
- iio_get_time_ns(iio));
+ IIO_UNMOD_EVENT_CODE(chan_type, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(iio));
if (ret & OPT3001_CONFIGURATION_FL)
iio_push_event(iio,
- IIO_UNMOD_EVENT_CODE(chan_type, 0,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_FALLING),
- iio_get_time_ns(iio));
+ IIO_UNMOD_EVENT_CODE(chan_type, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(iio));
} else if (ret & OPT3001_CONFIGURATION_CRF) {
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
+ ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT);
if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_RESULT);
+ dev_err(dev, "failed to read register %02x\n",
+ OPT3001_RESULT);
goto out;
}
opt->result = ret;
@@ -852,12 +888,13 @@ static int opt3001_probe(struct i2c_client *client)
opt = iio_priv(iio);
opt->client = client;
- opt->dev = dev;
opt->chip_info = i2c_get_match_data(client);
- mutex_init(&opt->lock);
+ ret = devm_mutex_init(dev, &opt->lock);
+ if (ret)
+ return ret;
+
init_waitqueue_head(&opt->result_ready_queue);
- i2c_set_clientdata(client, iio);
if (opt->chip_info->has_id) {
ret = opt3001_read_id(opt);
@@ -875,55 +912,20 @@ static int opt3001_probe(struct i2c_client *client)
iio->modes = INDIO_DIRECT_MODE;
iio->info = &opt3001_info;
- ret = devm_iio_device_register(dev, iio);
- if (ret) {
- dev_err(dev, "failed to register IIO device\n");
- return ret;
- }
-
/* Make use of INT pin only if valid IRQ no. is given */
if (irq > 0) {
- ret = request_threaded_irq(irq, NULL, opt3001_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "opt3001", iio);
- if (ret) {
- dev_err(dev, "failed to request IRQ #%d\n", irq);
+ ret = devm_request_threaded_irq(dev, irq, NULL, opt3001_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "opt3001", iio);
+ if (ret)
return ret;
- }
+
opt->use_irq = true;
} else {
- dev_dbg(opt->dev, "enabling interrupt-less operation\n");
- }
-
- return 0;
-}
-
-static void opt3001_remove(struct i2c_client *client)
-{
- struct iio_dev *iio = i2c_get_clientdata(client);
- struct opt3001 *opt = iio_priv(iio);
- int ret;
- u16 reg;
-
- if (opt->use_irq)
- free_irq(client->irq, iio);
-
- ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
- if (ret < 0) {
- dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_CONFIGURATION);
- return;
+ dev_dbg(dev, "enabling interrupt-less operation\n");
}
- reg = ret;
- opt3001_set_mode(opt, &reg, OPT3001_CONFIGURATION_M_SHUTDOWN);
-
- ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
- reg);
- if (ret < 0) {
- dev_err(opt->dev, "failed to write register %02x\n",
- OPT3001_CONFIGURATION);
- }
+ return devm_iio_device_register(dev, iio);
}
static const struct opt3001_chip_info opt3001_chip_information = {
@@ -964,7 +966,6 @@ MODULE_DEVICE_TABLE(of, opt3001_of_match);
static struct i2c_driver opt3001_driver = {
.probe = opt3001_probe,
- .remove = opt3001_remove,
.id_table = opt3001_id,
.driver = {
diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c
index f79dd342937d..8253a6a8bee4 100644
--- a/drivers/iio/light/opt4060.c
+++ b/drivers/iio/light/opt4060.c
@@ -1205,7 +1205,7 @@ static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
name, idev);
if (ret)
- return dev_err_probe(chip->dev, ret, "Could not request IRQ\n");
+ return ret;
init_completion(&chip->completion);
diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index f961973892f2..7d82efc31f3a 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -987,11 +987,8 @@ static int rpr0521_probe(struct i2c_client *client)
rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"rpr0521_event", indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "request irq %d for trigger0 failed\n",
- client->irq);
+ if (ret)
goto err_pm_disable;
- }
ret = devm_iio_trigger_register(indio_dev->dev.parent,
data->drdy_trigger0);
@@ -1071,7 +1068,10 @@ static int rpr0521_runtime_resume(struct device *dev)
struct rpr0521_data *data = iio_priv(indio_dev);
int ret;
- regcache_sync(data->regmap);
+ ret = regcache_sync(data->regmap);
+ if (ret < 0)
+ return ret;
+
if (data->als_ps_need_en) {
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
if (ret < 0)
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
index 4601ae5d2009..bb7c99041819 100644
--- a/drivers/iio/light/si1145.c
+++ b/drivers/iio/light/si1145.c
@@ -1251,10 +1251,8 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev)
IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
"si1145_irq",
trig);
- if (ret < 0) {
- dev_err(&client->dev, "irq request failed\n");
+ if (ret)
return ret;
- }
ret = devm_iio_trigger_register(&client->dev, trig);
if (ret)
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index bcd729a9924e..94d5261b2663 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -196,11 +196,8 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev)
st_uvis25_trigger_handler_thread,
irq_type | IRQF_ONESHOT,
iio_dev->name, hw);
- if (err) {
- dev_err(dev, "failed to request trigger irq %d\n",
- hw->irq);
+ if (err)
return err;
- }
hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger",
iio_dev->name);
diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c
index c4c15093e9e5..0f78a0abfafb 100644
--- a/drivers/iio/light/st_uvis25_spi.c
+++ b/drivers/iio/light/st_uvis25_spi.c
@@ -46,7 +46,7 @@ static const struct of_device_id st_uvis25_spi_of_match[] = {
MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match);
static const struct spi_device_id st_uvis25_spi_id_table[] = {
- { ST_UVIS25_DEV_NAME },
+ { .name = ST_UVIS25_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table);
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index e7ce6f32592b..7c8a1d2b2ed0 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -673,11 +673,8 @@ static int stk3310_probe(struct i2c_client *client)
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"stk3310_event", indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "request irq %d failed\n",
- client->irq);
+ if (ret)
goto err_standby;
- }
}
ret = iio_device_register(indio_dev);
diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c
index b61442c5c1be..b392943e51c8 100644
--- a/drivers/iio/light/tcs3472.c
+++ b/drivers/iio/light/tcs3472.c
@@ -9,8 +9,6 @@
* TCS34727)
*
* Datasheet: http://ams.com/eng/content/download/319364/1117183/file/TCS3472_Datasheet_EN_v2.pdf
- *
- * TODO: wait time
*/
#include <linux/cleanup.h>
@@ -19,6 +17,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm.h>
+#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
@@ -53,19 +52,30 @@
#define TCS3472_STATUS_AINT BIT(4)
#define TCS3472_STATUS_AVALID BIT(0)
#define TCS3472_ENABLE_AIEN BIT(4)
+#define TCS3472_ENABLE_WEN BIT(3)
#define TCS3472_ENABLE_AEN BIT(1)
#define TCS3472_ENABLE_PON BIT(0)
+#define TCS3472_ENABLE_RUN \
+ (TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON | TCS3472_ENABLE_WEN)
#define TCS3472_CONTROL_AGAIN_MASK (BIT(0) | BIT(1))
+#define TCS3472_CONFIG_WLONG BIT(1)
+
+#define TCS3472_ATIME_TO_US(atime) (((256) - (atime)) * 2400)
struct tcs3472_data {
struct i2c_client *client;
struct mutex lock;
+ int target_freq_hz;
+ int target_freq_uhz;
u16 low_thresh;
u16 high_thresh;
u8 enable;
+ u8 enable_pre_suspend;
u8 control;
u8 atime;
u8 apers;
+ u8 wtime;
+ bool wlong;
};
static const struct iio_event_spec tcs3472_events[] = {
@@ -91,6 +101,7 @@ static const struct iio_event_spec tcs3472_events[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
BIT(IIO_CHAN_INFO_INT_TIME), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.channel2 = IIO_MOD_LIGHT_##_color, \
.address = _addr, \
.scan_index = _si, \
@@ -114,9 +125,65 @@ static const struct iio_chan_spec tcs3472_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(4),
};
+/*
+ * The chip's cycle time is the sum of three components:
+ * - ATIME: the programmable RGBC integration time.
+ * - The fixed RGBC initialization time (2400 us).
+ * - WTIME: the wait time, used only if WEN is set. If WLONG is active,
+ * the wait step is multiplied by 12 (2400 us -> 28800 us).
+ */
+static unsigned int tcs3472_cycle_time_us(struct tcs3472_data *data)
+{
+ unsigned int atime_us = TCS3472_ATIME_TO_US(data->atime);
+ unsigned int init_us = 2400;
+ unsigned int wtime_us;
+
+ if (!(data->enable & TCS3472_ENABLE_WEN))
+ wtime_us = 0;
+ else if (data->wlong)
+ wtime_us = (256 - data->wtime) * 28800;
+ else
+ wtime_us = (256 - data->wtime) * 2400;
+
+ return atime_us + init_us + wtime_us;
+}
+
+/*
+ * Convert a cycle time in microseconds to a frequency in Hz and microhertz.
+ *
+ * Given cycle_us = T (the cycle period in microseconds), the corresponding
+ * frequency is:
+ * f = 1e6 / T [Hz]
+ *
+ * The result is split into the IIO_VAL_INT_PLUS_MICRO format:
+ * val = floor(1e6 / T) [Hz]
+ * val2 = (1e6 mod T) * 1e6 / T [microhertz]
+ */
+static void tcs3472_cycle_to_freq(unsigned int cycle_us, int *val, int *val2)
+{
+ *val = USEC_PER_SEC / cycle_us;
+ *val2 = div_u64((u64)(USEC_PER_SEC % cycle_us) * USEC_PER_SEC,
+ cycle_us);
+}
+
static int tcs3472_req_data(struct tcs3472_data *data)
{
- int tries = 50;
+ /*
+ * The worst-case cycle time is reached with ATIME=0x00, WTIME=0x00
+ * and WLONG=1. So:
+ * 614 ms (Max Integration Time)
+ * + 2.4 ms (RGBC Init)
+ * + 7.37 s (Max Wait Time)
+ * = ~ 8 s (Total Max cycle time).
+ * Use that as a polling upper bound; in normal operation the loop
+ * exits as soon as AVALID is set. So the total number of tries in 8
+ * seconds considering a polling period of 20 ms is 400.
+ * Considering a 20% margin due to oscillator tolerance, the total
+ * duration becomes approximately 9.8 seconds, which corresponds to
+ * about 480 steps. Therefore, setting it to 500 appears to be a
+ * reasonable and safe trade-off.
+ */
+ int tries = 500;
int ret;
while (tries--) {
@@ -164,18 +231,165 @@ static int tcs3472_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
*val = 0;
- *val2 = (256 - data->atime) * 2400;
+ *val2 = TCS3472_ATIME_TO_US(data->atime);
return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ unsigned int cycle_us;
+
+ guard(mutex)(&data->lock);
+ cycle_us = tcs3472_cycle_time_us(data);
+ tcs3472_cycle_to_freq(cycle_us, val, val2);
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
default:
return -EINVAL;
}
}
+/*
+ * __tcs3472_set_sampling_freq() - implementation of sampling frequency
+ * configuration. The caller must hold data->lock.
+ */
+static int __tcs3472_set_sampling_freq(struct tcs3472_data *data,
+ int val, int val2)
+{
+ unsigned int atime_us;
+ unsigned int init_us = 2400;
+ u64 cycle_us;
+ s64 wait_us;
+ int wtime;
+ bool wlong = false;
+ u8 config;
+ int ret;
+
+ if (val < 0 || val2 < 0 || (val == 0 && val2 == 0))
+ return -EINVAL;
+
+ atime_us = TCS3472_ATIME_TO_US(data->atime);
+
+ /*
+ * cycle_us = 1 / freq, expressed in microseconds.
+ * Numerator: 1 [s] = PSEC_PER_SEC [ps]
+ * Denominator: freq [Hz] * MICROHZ_PER_HZ + val2 [uHz] = freq in [uHz]
+ * Result: ps / uHz = us
+ */
+ cycle_us = div64_u64(PSEC_PER_SEC, (u64)val * MICROHZ_PER_HZ + val2);
+
+ /*
+ * wait_us can be negative when the requested frequency is too high
+ * to be reached, or very large when the requested frequency is
+ * close to zero. Use s64 to cover the full range:
+ *
+ * cycle_us = PSEC_PER_SEC / (val * MICROHZ_PER_HZ + val2)
+ *
+ * The divisor of the formula above reaches its maximum when
+ * val = val2 = INT_MAX:
+ * INT_MAX * MICROHZ_PER_HZ + INT_MAX = ~2.15e18
+ * so cycle_us_min = floor(1e12 / 2.15e18) = 0.
+ *
+ * The divisor reaches its minimum (1) when val = 0 and val2 = 1,
+ * so cycle_us_max = 1e12 / 1 = 1e12.
+ *
+ * Therefore:
+ * wait_us_min = 0 - 2400 - 612000 = -616800
+ * wait_us_max = 1e12 - 2400 - 2400 = 999999995200
+ *
+ * Both fit comfortably in s64.
+ */
+ wait_us = (s64)cycle_us - init_us - atime_us;
+ if (wait_us < 2400) {
+ if (data->enable & TCS3472_ENABLE_WEN) {
+ u8 enable = data->enable & ~TCS3472_ENABLE_WEN;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ TCS3472_ENABLE, enable);
+ if (ret)
+ return ret;
+
+ data->enable = enable;
+ }
+
+ data->target_freq_hz = val;
+ data->target_freq_uhz = val2;
+ return 0;
+ }
+
+ /*
+ * Wait state is needed: make sure WEN is active before programming
+ * WTIME (and possibly WLONG).
+ */
+ if (!(data->enable & TCS3472_ENABLE_WEN)) {
+ u8 enable = data->enable | TCS3472_ENABLE_WEN;
+
+ ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
+ enable);
+ if (ret)
+ return ret;
+
+ data->enable = enable;
+ }
+
+ wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 2400);
+ if (wtime < 0) {
+ /*
+ * If wait_us is too high (so the requested frequency is too
+ * low), the resulting wait exceeds what WTIME can represent
+ * (max 614 ms without WLONG). Enable WLONG, whose step is 12x
+ * longer (28.8 ms instead of 2.4 ms), and recompute.
+ */
+ wlong = true;
+ wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 28800);
+ }
+
+ if (wlong != data->wlong) {
+ ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ config = ret;
+ if (wlong)
+ config |= TCS3472_CONFIG_WLONG;
+ else
+ config &= ~TCS3472_CONFIG_WLONG;
+
+ ret = i2c_smbus_write_byte_data(data->client, TCS3472_CONFIG,
+ config);
+ if (ret)
+ return ret;
+
+ data->wlong = wlong;
+ }
+
+ /*
+ * If the requested wait is so long that even WLONG cannot
+ * cover it, wtime may still be negative. Saturate to 0,
+ * which is the largest possible wait (256 * 28.8 ms = 7.37 s).
+ */
+ wtime = clamp(wtime, 0, 255);
+ ret = i2c_smbus_write_byte_data(data->client, TCS3472_WTIME, wtime);
+ if (ret)
+ return ret;
+
+ data->wtime = wtime;
+ data->target_freq_hz = val;
+ data->target_freq_uhz = val2;
+
+ return 0;
+}
+
+static int tcs3472_set_sampling_freq(struct tcs3472_data *data,
+ int val, int val2)
+{
+ guard(mutex)(&data->lock);
+ return __tcs3472_set_sampling_freq(data, val, val2);
+}
+
static int tcs3472_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct tcs3472_data *data = iio_priv(indio_dev);
+ int ret;
int i;
switch (mask) {
@@ -196,15 +410,31 @@ static int tcs3472_write_raw(struct iio_dev *indio_dev,
if (val != 0)
return -EINVAL;
for (i = 0; i < 256; i++) {
- if (val2 == (256 - i) * 2400) {
- data->atime = i;
- return i2c_smbus_write_byte_data(
- data->client, TCS3472_ATIME,
- data->atime);
- }
-
+ if (val2 != (256 - i) * 2400)
+ continue;
+
+ guard(mutex)(&data->lock);
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ TCS3472_ATIME, i);
+ if (ret)
+ return ret;
+
+ data->atime = i;
+
+ /*
+ * ATIME just changed, so the cycle time changed too.
+ * Re-run the sampling frequency logic to recompute
+ * WTIME and preserve the user's last requested
+ * frequency. Lock is already held.
+ */
+ return __tcs3472_set_sampling_freq(data,
+ data->target_freq_hz,
+ data->target_freq_uhz);
}
return -EINVAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return tcs3472_set_sampling_freq(data, val, val2);
default:
return -EINVAL;
}
@@ -234,7 +464,7 @@ static int tcs3472_read_event(struct iio_dev *indio_dev,
data->high_thresh : data->low_thresh;
return IIO_VAL_INT;
case IIO_EV_INFO_PERIOD:
- period = (256 - data->atime) * 2400 *
+ period = tcs3472_cycle_time_us(data) *
tcs3472_intr_pers[data->apers];
*val = period / USEC_PER_SEC;
*val2 = period % USEC_PER_SEC;
@@ -279,11 +509,13 @@ static int tcs3472_write_event(struct iio_dev *indio_dev,
data->low_thresh = val;
return 0;
- case IIO_EV_INFO_PERIOD:
+ case IIO_EV_INFO_PERIOD:{
+ unsigned int cycle_us;
+
period = val * USEC_PER_SEC + val2;
+ cycle_us = tcs3472_cycle_time_us(data);
for (i = 1; i < ARRAY_SIZE(tcs3472_intr_pers) - 1; i++) {
- if (period <= (256 - data->atime) * 2400 *
- tcs3472_intr_pers[i])
+ if (period <= cycle_us * tcs3472_intr_pers[i])
break;
}
ret = i2c_smbus_write_byte_data(data->client, TCS3472_PERS, i);
@@ -293,6 +525,7 @@ static int tcs3472_write_event(struct iio_dev *indio_dev,
data->apers = i;
return 0;
+ }
default:
return -EINVAL;
}
@@ -434,17 +667,16 @@ static const struct iio_info tcs3472_info = {
static int tcs3472_powerdown(struct tcs3472_data *data)
{
int ret;
- u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON;
guard(mutex)(&data->lock);
+ data->enable_pre_suspend = data->enable;
+
ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
- data->enable & ~enable_mask);
+ data->enable & ~TCS3472_ENABLE_RUN);
if (ret)
return ret;
- data->enable &= ~enable_mask;
-
return 0;
}
@@ -458,7 +690,9 @@ static int tcs3472_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct tcs3472_data *data;
struct iio_dev *indio_dev;
+ unsigned int cycle_us;
int ret;
+ u8 enable;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
@@ -498,6 +732,16 @@ static int tcs3472_probe(struct i2c_client *client)
return ret;
data->atime = ret;
+ ret = i2c_smbus_read_byte_data(data->client, TCS3472_WTIME);
+ if (ret < 0)
+ return ret;
+ data->wtime = ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG);
+ if (ret < 0)
+ return ret;
+ data->wlong = (ret & TCS3472_CONFIG_WLONG) ? 1 : 0;
+
ret = i2c_smbus_read_word_data(data->client, TCS3472_AILT);
if (ret < 0)
return ret;
@@ -518,14 +762,30 @@ static int tcs3472_probe(struct i2c_client *client)
if (ret < 0)
return ret;
- /* enable device */
- data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN;
- data->enable &= ~TCS3472_ENABLE_AIEN;
- ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
- data->enable);
+ /*
+ * Enable the chip in its full running state, including WEN. The
+ * actual wait time is controlled by the WTIME and WLONG registers,
+ * which retain their power-on defaults until userspace writes to
+ * sampling_frequency.
+ */
+ enable = (ret | TCS3472_ENABLE_RUN) & ~TCS3472_ENABLE_AIEN;
+
+ ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, enable);
if (ret < 0)
return ret;
+ data->enable = enable;
+
+ /*
+ * Initialize target frequency from the chip's current state so that
+ * subsequent integration_time changes via IIO_CHAN_INFO_INT_TIME can
+ * preserve a meaningful sampling rate, even before userspace writes
+ * sampling_frequency for the first time.
+ */
+ cycle_us = tcs3472_cycle_time_us(data);
+ tcs3472_cycle_to_freq(cycle_us, &data->target_freq_hz,
+ &data->target_freq_uhz);
+
ret = devm_add_action_or_reset(dev, tcs3472_powerdown_action, data);
if (ret)
return ret;
@@ -561,16 +821,21 @@ static int tcs3472_resume(struct device *dev)
struct tcs3472_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
int ret;
- u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON;
guard(mutex)(&data->lock);
+ /*
+ * Restore the full ENABLE register from the snapshot taken in
+ * tcs3472_powerdown(). This preserves the user's last
+ * sampling_frequency configuration (in particular the WEN bit)
+ * across suspend/resume.
+ */
ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
- data->enable | enable_mask);
+ data->enable_pre_suspend);
if (ret)
return ret;
- data->enable |= enable_mask;
+ data->enable = data->enable_pre_suspend;
return 0;
}
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index 45f3513d931e..7bef0c3118c2 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -754,7 +754,7 @@ static int tsl2563_probe(struct i2c_client *client)
"tsl2563_event",
indio_dev);
if (err)
- return dev_err_probe(dev, err, "irq request error\n");
+ return err;
}
err = tsl2563_configure(chip);
diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c
index a0dd122af2cf..53fd423aa7ae 100644
--- a/drivers/iio/light/tsl2583.c
+++ b/drivers/iio/light/tsl2583.c
@@ -456,12 +456,8 @@ static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev)
usleep_range(3000, 3500);
- ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON |
- TSL2583_CNTL_ADC_ENBL);
- if (ret < 0)
- return ret;
-
- return ret;
+ return tsl2583_set_power_state(chip,
+ TSL2583_CNTL_PWR_ON | TSL2583_CNTL_ADC_ENBL);
}
/* Sysfs Interface Functions */
@@ -475,7 +471,7 @@ static ssize_t in_illuminance_input_target_show(struct device *dev,
int ret;
mutex_lock(&chip->als_mutex);
- ret = sprintf(buf, "%d\n", chip->als_settings.als_cal_target);
+ ret = sysfs_emit(buf, "%d\n", chip->als_settings.als_cal_target);
mutex_unlock(&chip->als_mutex);
return ret;
@@ -533,10 +529,10 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev,
int offset = 0;
for (i = 0; i < ARRAY_SIZE(chip->als_settings.als_device_lux); i++) {
- offset += sprintf(buf + offset, "%u,%u,%u,",
- chip->als_settings.als_device_lux[i].ratio,
- chip->als_settings.als_device_lux[i].ch0,
- chip->als_settings.als_device_lux[i].ch1);
+ offset += sysfs_emit_at(buf, offset, "%u,%u,%u,",
+ chip->als_settings.als_device_lux[i].ratio,
+ chip->als_settings.als_device_lux[i].ch0,
+ chip->als_settings.als_device_lux[i].ch1);
if (chip->als_settings.als_device_lux[i].ratio == 0) {
/*
* We just printed the first "0" entry.
@@ -547,7 +543,7 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev,
}
}
- offset += sprintf(buf + offset, "\n");
+ offset += sysfs_emit_at(buf, offset, "\n");
return offset;
}
@@ -639,14 +635,6 @@ static const struct iio_chan_spec tsl2583_channels[] = {
},
};
-static int tsl2583_set_pm_runtime_busy(struct tsl2583_chip *chip, bool on)
-{
- if (on)
- return pm_runtime_resume_and_get(&chip->client->dev);
-
- return pm_runtime_put_autosuspend(&chip->client->dev);
-}
-
static int tsl2583_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -654,7 +642,7 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev,
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret, pm_ret;
- ret = tsl2583_set_pm_runtime_busy(chip, true);
+ ret = pm_runtime_resume_and_get(&chip->client->dev);
if (ret < 0)
return ret;
@@ -722,16 +710,16 @@ read_done:
mutex_unlock(&chip->als_mutex);
if (ret < 0) {
- tsl2583_set_pm_runtime_busy(chip, false);
+ pm_runtime_put_autosuspend(&chip->client->dev);
return ret;
}
/*
* Preserve the ret variable if the call to
- * tsl2583_set_pm_runtime_busy() is successful so the reading
+ * pm_runtime_put_autosuspend() is successful so the reading
* (if applicable) is returned to user space.
*/
- pm_ret = tsl2583_set_pm_runtime_busy(chip, false);
+ pm_ret = pm_runtime_put_autosuspend(&chip->client->dev);
if (pm_ret < 0)
return pm_ret;
@@ -745,7 +733,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev,
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret;
- ret = tsl2583_set_pm_runtime_busy(chip, true);
+ ret = pm_runtime_resume_and_get(&chip->client->dev);
if (ret < 0)
return ret;
@@ -786,15 +774,15 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev,
mutex_unlock(&chip->als_mutex);
if (ret < 0) {
- tsl2583_set_pm_runtime_busy(chip, false);
+ pm_runtime_put_autosuspend(&chip->client->dev);
return ret;
}
- ret = tsl2583_set_pm_runtime_busy(chip, false);
+ ret = pm_runtime_put_autosuspend(&chip->client->dev);
if (ret < 0)
return ret;
- return ret;
+ return 0;
}
static const struct iio_info tsl2583_info = {
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index 244f44379c36..4486a1d9d84d 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -1274,7 +1274,7 @@ static int tsl2772_read_raw(struct iio_dev *indio_dev,
}
break;
case IIO_CHAN_INFO_CALIBSCALE:
- if (chan->type == IIO_LIGHT)
+ if (chan->type == IIO_INTENSITY)
*val = tsl2772_als_gain[chip->settings.als_gain];
else
*val = tsl2772_prox_gain[chip->settings.prox_gain];
@@ -1849,11 +1849,8 @@ static int tsl2772_probe(struct i2c_client *clientp)
IRQF_ONESHOT,
"TSL2772_event",
indio_dev);
- if (ret) {
- dev_err(&clientp->dev,
- "%s: irq request failed\n", __func__);
+ if (ret)
return ret;
- }
} else {
indio_dev->channels = chip->chip_info->channel_without_events;
}
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 128ae3f94074..336468d59ea7 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -537,16 +537,6 @@ static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data)
return !!(ret & VCNL4000_SELF_TIMED_EN);
}
-static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
-{
- struct device *dev = &data->client->dev;
-
- if (on)
- return pm_runtime_resume_and_get(dev);
-
- return pm_runtime_put_autosuspend(dev);
-}
-
static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2)
{
int ret;
@@ -850,7 +840,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = vcnl4000_set_pm_runtime_state(data, true);
+ ret = pm_runtime_resume_and_get(&data->client->dev);
if (ret < 0)
return ret;
@@ -869,7 +859,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
default:
ret = -EINVAL;
}
- vcnl4000_set_pm_runtime_state(data, false);
+ pm_runtime_put_autosuspend(&data->client->dev);
return ret;
case IIO_CHAN_INFO_SCALE:
if (chan->type != IIO_LIGHT)
@@ -1922,7 +1912,7 @@ static void vcnl4000_cleanup(void *data)
static int vcnl4000_probe(struct i2c_client *client)
{
- const char * const regulator_names[] = { "vdd", "vio", "vled" };
+ static const char * const regulator_names[] = { "vdd", "vio", "vled" };
struct device *dev = &client->dev;
struct vcnl4000_data *data;
struct iio_dev *indio_dev;
diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c
index bf3a49b4351d..ce1bd42b0c7c 100644
--- a/drivers/iio/light/vcnl4035.c
+++ b/drivers/iio/light/vcnl4035.c
@@ -145,16 +145,6 @@ static const struct iio_trigger_ops vcnl4035_trigger_ops = {
.set_trigger_state = vcnl4035_als_drdy_set_state,
};
-static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on)
-{
- struct device *dev = &data->client->dev;
-
- if (on)
- return pm_runtime_resume_and_get(dev);
-
- return pm_runtime_put_autosuspend(dev);
-}
-
static int vcnl4035_read_info_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
@@ -202,11 +192,11 @@ static int vcnl4035_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = vcnl4035_set_pm_runtime_state(data, true);
+ ret = pm_runtime_resume_and_get(&data->client->dev);
if (ret < 0)
return ret;
ret = vcnl4035_read_info_raw(indio_dev, chan, val);
- vcnl4035_set_pm_runtime_state(data, false);
+ pm_runtime_put_autosuspend(&data->client->dev);
return ret;
case IIO_CHAN_INFO_INT_TIME:
*val = 50;
@@ -237,7 +227,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev,
if (val <= 0 || val > 800)
return -EINVAL;
- ret = vcnl4035_set_pm_runtime_state(data, true);
+ ret = pm_runtime_resume_and_get(&data->client->dev);
if (ret < 0)
return ret;
@@ -247,7 +237,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev,
if (!ret)
data->als_it_val = val / 100;
- vcnl4035_set_pm_runtime_state(data, false);
+ pm_runtime_put_autosuspend(&data->client->dev);
return ret;
default:
return -EINVAL;
@@ -647,7 +637,10 @@ static int vcnl4035_runtime_resume(struct device *dev)
struct vcnl4035_data *data = iio_priv(indio_dev);
int ret;
- regcache_sync(data->regmap);
+ ret = regcache_sync(data->regmap);
+ if (ret < 0)
+ return ret;
+
ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);
if (ret < 0)
return ret;
diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c
index 7c837d60dad8..ec9c127e7d8a 100644
--- a/drivers/iio/light/veml6030.c
+++ b/drivers/iio/light/veml6030.c
@@ -921,9 +921,7 @@ static int veml6030_set_info(struct iio_dev *indio_dev)
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret < 0)
- return dev_err_probe(&client->dev, ret,
- "irq %d request failed\n",
- client->irq);
+ return ret;
indio_dev->info = &veml6030_info;
} else {
diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c
index 4f270f405b21..aef8a5333442 100644
--- a/drivers/iio/light/vl6180.c
+++ b/drivers/iio/light/vl6180.c
@@ -721,7 +721,7 @@ static int vl6180_probe(struct i2c_client *client)
IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
- return dev_err_probe(&client->dev, ret, "devm_request_irq error\n");
+ return ret;
init_completion(&data->completion);
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index ea2697fb5ab6..d359003b248e 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -209,6 +209,28 @@ config INFINEON_TLV493D
To compile this driver as a module, choose M here: the module
will be called tlv493d.
+config QMC5883L
+ tristate "QST QMC5883L 3-Axis Magnetic Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here to add support driver for QST QMC5883L 3-Axis
+ Magnetic Sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qmc5883l.
+
+config QMC6308
+ tristate "QST QMC6308 3-Axis Magnetic Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here to add support for the QST QMC6308 3-Axis
+ Magnetic Sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qmc6308.
+
config SENSORS_HMC5843
tristate
select IIO_BUFFER
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 7fd9b3fd914e..8f2205975c1b 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -27,6 +27,9 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
obj-$(CONFIG_INFINEON_TLV493D) += tlv493d.o
+obj-$(CONFIG_QMC5883L) += qmc5883l.o
+obj-$(CONFIG_QMC6308) += qmc6308.o
+
obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o
obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o
obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c
index c7fdb7c2f543..375023db2758 100644
--- a/drivers/iio/magnetometer/ak8974.c
+++ b/drivers/iio/magnetometer/ak8974.c
@@ -379,11 +379,7 @@ static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
return -ERANGE;
}
- ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6);
- if (ret)
- return ret;
-
- return ret;
+ return regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6);
}
static irqreturn_t ak8974_drdy_irq(int irq, void *d)
@@ -926,11 +922,8 @@ static int ak8974_probe(struct i2c_client *i2c)
irq_trig,
ak8974->name,
ak8974);
- if (ret) {
- dev_err(&i2c->dev, "unable to request DRDY IRQ "
- "- proceeding without IRQ\n");
+ if (ret)
goto no_irq;
- }
ak8974->drdy_irq = true;
}
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
index bf2551988008..32dff9cdd0f7 100644
--- a/drivers/iio/magnetometer/bmc150_magn.c
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -9,23 +9,25 @@
* (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved
*/
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
+#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
-#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.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/regmap.h>
-#include <linux/regulator/consumer.h>
#include "bmc150_magn.h"
@@ -245,14 +247,14 @@ static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data,
return regmap_update_bits(data->regmap,
BMC150_MAGN_REG_OPMODE_ODR,
BMC150_MAGN_MASK_OPMODE,
- BMC150_MAGN_MODE_SLEEP <<
- BMC150_MAGN_SHIFT_OPMODE);
+ FIELD_PREP(BMC150_MAGN_MASK_OPMODE,
+ BMC150_MAGN_MODE_SLEEP));
case BMC150_MAGN_POWER_MODE_NORMAL:
return regmap_update_bits(data->regmap,
BMC150_MAGN_REG_OPMODE_ODR,
BMC150_MAGN_MASK_OPMODE,
- BMC150_MAGN_MODE_NORMAL <<
- BMC150_MAGN_SHIFT_OPMODE);
+ FIELD_PREP(BMC150_MAGN_MASK_OPMODE,
+ BMC150_MAGN_MODE_NORMAL));
}
return -EINVAL;
@@ -290,7 +292,7 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val)
ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, &reg_val);
if (ret < 0)
return ret;
- odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR;
+ odr_val = FIELD_GET(BMC150_MAGN_MASK_ODR, reg_val);
for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++)
if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) {
@@ -303,21 +305,17 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val)
static int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val)
{
- int ret;
u8 i;
for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) {
- if (bmc150_magn_samp_freq_table[i].freq == val) {
- ret = regmap_update_bits(data->regmap,
- BMC150_MAGN_REG_OPMODE_ODR,
- BMC150_MAGN_MASK_ODR,
- bmc150_magn_samp_freq_table[i].
- reg_val <<
- BMC150_MAGN_SHIFT_ODR);
- if (ret < 0)
- return ret;
- return 0;
- }
+ if (bmc150_magn_samp_freq_table[i].freq != val)
+ continue;
+
+ return regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_OPMODE_ODR,
+ BMC150_MAGN_MASK_ODR,
+ FIELD_PREP(BMC150_MAGN_MASK_ODR,
+ bmc150_magn_samp_freq_table[i].reg_val));
}
return -EINVAL;
@@ -800,7 +798,7 @@ static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig,
ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY,
BMC150_MAGN_MASK_DRDY_EN,
- state << BMC150_MAGN_SHIFT_DRDY_EN);
+ FIELD_PREP(BMC150_MAGN_MASK_DRDY_EN, state));
if (ret < 0)
return ret;
diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c
index 8af8d3528c43..27ab845fb257 100644
--- a/drivers/iio/magnetometer/bmc150_magn_spi.c
+++ b/drivers/iio/magnetometer/bmc150_magn_spi.c
@@ -33,9 +33,9 @@ static void bmc150_magn_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id bmc150_magn_spi_id[] = {
- {"bmc150_magn", 0},
- {"bmc156_magn", 0},
- {"bmm150_magn", 0},
+ { .name = "bmc150_magn" },
+ { .name = "bmc156_magn" },
+ { .name = "bmm150_magn" },
{ }
};
MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id);
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index d8b8bcc865c3..ad18f233ee16 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -134,9 +134,8 @@ static const struct iio_chan_spec magn_3d_channels[] = {
/* Channel read_raw handler */
static int magn_3d_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct magn_3d_state *magn_state = iio_priv(indio_dev);
int report_id = -1;
@@ -166,8 +165,7 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
false);
return -EINVAL;
}
- hid_sensor_power_state(&magn_state->magn_flux_attributes,
- false);
+ hid_sensor_power_state(&magn_state->magn_flux_attributes, false);
ret_type = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
@@ -228,10 +226,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int magn_3d_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct magn_3d_state *magn_state = iio_priv(indio_dev);
int ret = 0;
@@ -269,8 +265,7 @@ static const struct iio_info magn_3d_info = {
/* Callback handler to send event after all samples are received and captured */
static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
- u32 usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct magn_3d_state *magn_state = iio_priv(indio_dev);
@@ -291,9 +286,9 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
- u32 usage_id,
- size_t raw_len, char *raw_data,
- void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct magn_3d_state *magn_state = iio_priv(indio_dev);
@@ -326,7 +321,7 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
iio_val = magn_state->magn_val_addr[offset];
- if (iio_val != NULL)
+ if (iio_val)
*iio_val = *((u32 *)raw_data);
else
ret = -EINVAL;
@@ -367,18 +362,17 @@ static int magn_3d_parse_report(struct platform_device *pdev,
return -EINVAL;
}
- dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n",
- attr_count);
+ dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", attr_count);
dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n",
- st->magn[0].index,
- st->magn[0].report_id,
- st->magn[1].index, st->magn[1].report_id,
- st->magn[2].index, st->magn[2].report_id);
+ st->magn[0].index,
+ st->magn[0].report_id,
+ st->magn[1].index, st->magn[1].report_id,
+ st->magn[2].index, st->magn[2].report_id);
/* Setup IIO channel array */
_channels = devm_kcalloc(&pdev->dev, attr_count,
- sizeof(struct iio_chan_spec),
- GFP_KERNEL);
+ sizeof(struct iio_chan_spec),
+ GFP_KERNEL);
if (!_channels) {
dev_err(&pdev->dev,
"failed to allocate space for iio channels\n");
@@ -425,8 +419,7 @@ static int magn_3d_parse_report(struct platform_device *pdev,
*channels = _channels;
- dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",
- *chan_count);
+ dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", *chan_count);
st->magn_flux_attr.scale_precision = hid_sensor_format_scale(
HID_USAGE_SENSOR_COMPASS_3D,
@@ -467,7 +460,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct magn_3d_state));
- if (indio_dev == NULL)
+ if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
@@ -490,8 +483,8 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
magn_state->rot_attributes.sensitivity.index = -1;
ret = magn_3d_parse_report(pdev, hsdev,
- &channels, &chan_count,
- HID_USAGE_SENSOR_COMPASS_3D, magn_state);
+ &channels, &chan_count,
+ HID_USAGE_SENSOR_COMPASS_3D, magn_state);
if (ret) {
dev_err(&pdev->dev, "failed to parse report\n");
return ret;
@@ -506,32 +499,32 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
atomic_set(&magn_state->magn_flux_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &magn_state->magn_flux_attributes);
+ &magn_state->magn_flux_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
magn_state->callbacks.send_event = magn_3d_proc_event;
magn_state->callbacks.capture_sample = magn_3d_capture_sample;
magn_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
- &magn_state->callbacks);
+ &magn_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return ret;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
return ret;
@@ -544,8 +537,8 @@ static void hid_magn_3d_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct magn_3d_state *magn_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
}
diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c
index 6a55c1559b0d..29dee5dce2ae 100644
--- a/drivers/iio/magnetometer/hmc5843_spi.c
+++ b/drivers/iio/magnetometer/hmc5843_spi.c
@@ -56,7 +56,6 @@ static int hmc5843_spi_probe(struct spi_device *spi)
{
int ret;
struct regmap *regmap;
- const struct spi_device_id *id = spi_get_device_id(spi);
spi->mode = SPI_MODE_3;
spi->max_speed_hz = 8000000;
@@ -69,8 +68,7 @@ static int hmc5843_spi_probe(struct spi_device *spi)
return PTR_ERR(regmap);
return hmc5843_common_probe(&spi->dev,
- regmap,
- id->driver_data, id->name);
+ regmap, HMC5983_ID, "hmc5983");
}
static void hmc5843_spi_remove(struct spi_device *spi)
@@ -79,7 +77,7 @@ static void hmc5843_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id hmc5843_id[] = {
- { "hmc5983", HMC5983_ID },
+ { .name = "hmc5983" },
{ }
};
MODULE_DEVICE_TABLE(spi, hmc5843_id);
diff --git a/drivers/iio/magnetometer/mmc5983.c b/drivers/iio/magnetometer/mmc5983.c
index 03f46e8c1611..d10a6288e147 100644
--- a/drivers/iio/magnetometer/mmc5983.c
+++ b/drivers/iio/magnetometer/mmc5983.c
@@ -327,7 +327,7 @@ static const struct of_device_id mmc5983_of_match[] = {
MODULE_DEVICE_TABLE(of, mmc5983_of_match);
static const struct i2c_device_id mmc5983_id[] = {
- { "mmc5983" },
+ { .name = "mmc5983" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mmc5983_id);
diff --git a/drivers/iio/magnetometer/qmc5883l.c b/drivers/iio/magnetometer/qmc5883l.c
new file mode 100644
index 000000000000..c3469715dc63
--- /dev/null
+++ b/drivers/iio/magnetometer/qmc5883l.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Support for QST QMC5883L 3-Axis Magnetic Sensor on I2C bus.
+ *
+ * Copyright (C) 2026 Siratul Islam <siratul.islam@linux.dev>
+ *
+ * Datasheet available at
+ * <https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/iio/iio.h>
+
+#define QMC5883L_REG_X_LSB 0x00
+#define QMC5883L_REG_STATUS1 0x06
+#define QMC5883L_REG_CTRL1 0x09
+#define QMC5883L_REG_CTRL2 0x0A
+#define QMC5883L_REG_SET_RESET 0x0B
+#define QMC5883L_REG_ID 0x0D
+
+#define QMC5883L_CHIP_ID 0xFF
+
+#define QMC5883L_MODE_MASK GENMASK(1, 0)
+#define QMC5883L_ODR_MASK GENMASK(3, 2)
+#define QMC5883L_RNG_MASK GENMASK(5, 4)
+#define QMC5883L_OSR_MASK GENMASK(7, 6)
+
+#define QMC5883L_MODE_STANDBY 0x00
+#define QMC5883L_MODE_CONT 0x01
+
+#define QMC5883L_ODR_10HZ 0x00
+#define QMC5883L_ODR_50HZ 0x01
+#define QMC5883L_ODR_100HZ 0x02
+#define QMC5883L_ODR_200HZ 0x03
+
+#define QMC5883L_RNG_2G 0x00
+#define QMC5883L_RNG_8G 0x01
+
+#define QMC5883L_OSR_512 0x00
+#define QMC5883L_OSR_256 0x01
+#define QMC5883L_OSR_128 0x02
+#define QMC5883L_OSR_64 0x03
+
+#define QMC5883L_STATUS_DRDY BIT(0)
+#define QMC5883L_STATUS_OVL BIT(1)
+
+#define QMC5883L_SET_RESET_VAL BIT(0)
+#define QMC5883L_INT_DISABLE BIT(0)
+#define QMC5883L_SOFT_RESET BIT(7)
+
+#define QMC5883L_PORT_US 350
+
+struct qmc5883l_data {
+ struct regmap *regmap;
+ /*
+ * Protect data->range/odr/osr.
+ * Protect poll and read during measurement.
+ */
+ struct mutex mutex;
+ u8 range;
+ u8 odr;
+ u8 osr;
+};
+
+enum qmc5883l_chan {
+ QMC5883L_AXIS_X,
+ QMC5883L_AXIS_Y,
+ QMC5883L_AXIS_Z,
+};
+
+static const int qmc5883l_odr_avail[] = { 10, 50, 100, 200 };
+
+static const int qmc5883l_osr_avail[] = { 512, 256, 128, 64 };
+
+static const int qmc5883l_scales[][2] = {
+ [QMC5883L_RNG_2G] = { 0, 83333 },
+ [QMC5883L_RNG_8G] = { 0, 333333 },
+};
+
+static int qmc5883l_take_measurement(struct iio_dev *indio_dev, int index,
+ int *val)
+{
+ struct qmc5883l_data *data = iio_priv(indio_dev);
+ struct regmap *map = data->regmap;
+ unsigned int status;
+ __le16 buf[3];
+ int ret;
+
+ guard(mutex) (&data->mutex);
+
+ /* 50ms headroom over the slowest ODR (10Hz) */
+ ret = regmap_read_poll_timeout(map, QMC5883L_REG_STATUS1,
+ status, (status & QMC5883L_STATUS_DRDY),
+ 2 * USEC_PER_MSEC, 150 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(map, QMC5883L_REG_X_LSB, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (status & QMC5883L_STATUS_OVL)
+ return -ERANGE;
+
+ *val = (s16)le16_to_cpu(buf[index]);
+
+ return 0;
+}
+
+static int qmc5883l_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct qmc5883l_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = qmc5883l_take_measurement(indio_dev, chan->address, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&data->mutex);
+
+ *val = qmc5883l_scales[data->range][0];
+ *val2 = qmc5883l_scales[data->range][1];
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ guard(mutex)(&data->mutex);
+
+ switch (data->odr) {
+ case QMC5883L_ODR_200HZ:
+ *val = 200;
+ break;
+ case QMC5883L_ODR_100HZ:
+ *val = 100;
+ break;
+ case QMC5883L_ODR_50HZ:
+ *val = 50;
+ break;
+ case QMC5883L_ODR_10HZ:
+ *val = 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ guard(mutex)(&data->mutex);
+
+ switch (data->osr) {
+ case QMC5883L_OSR_64:
+ *val = 64;
+ break;
+ case QMC5883L_OSR_128:
+ *val = 128;
+ break;
+ case QMC5883L_OSR_256:
+ *val = 256;
+ break;
+ case QMC5883L_OSR_512:
+ *val = 512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int qmc5883l_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct qmc5883l_data *data = iio_priv(indio_dev);
+ u8 rng, osr, odr;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE: {
+ if (val != 0)
+ return -EINVAL;
+
+ if (val2 == qmc5883l_scales[QMC5883L_RNG_2G][1])
+ rng = QMC5883L_RNG_2G;
+ else if (val2 == qmc5883l_scales[QMC5883L_RNG_8G][1])
+ rng = QMC5883L_RNG_8G;
+ else
+ return -EINVAL;
+
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+ QMC5883L_RNG_MASK,
+ FIELD_PREP(QMC5883L_RNG_MASK, rng));
+ if (ret)
+ return ret;
+
+ data->range = rng;
+
+ return 0;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ switch (val) {
+ case 200:
+ odr = QMC5883L_ODR_200HZ;
+ break;
+ case 100:
+ odr = QMC5883L_ODR_100HZ;
+ break;
+ case 50:
+ odr = QMC5883L_ODR_50HZ;
+ break;
+ case 10:
+ odr = QMC5883L_ODR_10HZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+ QMC5883L_ODR_MASK,
+ FIELD_PREP(QMC5883L_ODR_MASK, odr));
+ if (ret)
+ return ret;
+
+ data->odr = odr;
+
+ return 0;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ switch (val) {
+ case 64:
+ osr = QMC5883L_OSR_64;
+ break;
+ case 128:
+ osr = QMC5883L_OSR_128;
+ break;
+ case 256:
+ osr = QMC5883L_OSR_256;
+ break;
+ case 512:
+ osr = QMC5883L_OSR_512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+ QMC5883L_OSR_MASK,
+ FIELD_PREP(QMC5883L_OSR_MASK, osr));
+ if (ret)
+ return ret;
+
+ data->osr = osr;
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int qmc5883l_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 = qmc5883l_odr_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(qmc5883l_odr_avail);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = qmc5883l_osr_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(qmc5883l_osr_avail);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)qmc5883l_scales;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(qmc5883l_scales) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int qmc5883l_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;
+ }
+}
+
+static const struct iio_info qmc5883l_info = {
+ .read_raw = qmc5883l_read_raw,
+ .write_raw = qmc5883l_write_raw,
+ .read_avail = qmc5883l_read_avail,
+ .write_raw_get_fmt = qmc5883l_write_raw_get_fmt,
+};
+
+static int qmc5883l_init(struct qmc5883l_data *data)
+{
+ struct regmap *map = data->regmap;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(map, QMC5883L_REG_ID, &reg);
+ if (ret)
+ return ret;
+
+ /* Not failing because rev 1.0 had this register reserved */
+ if (reg != QMC5883L_CHIP_ID)
+ dev_warn(regmap_get_device(map),
+ "Unknown chip id: 0x%02x, continuing\n", reg);
+
+ ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_SOFT_RESET);
+ if (ret)
+ return ret;
+
+ /* Use POR completion time as a conservative bound */
+ fsleep(QMC5883L_PORT_US);
+
+ /* DRDY pin not used in this version of the driver */
+ ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_INT_DISABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(map, QMC5883L_REG_SET_RESET, QMC5883L_SET_RESET_VAL);
+ if (ret)
+ return ret;
+
+ data->odr = QMC5883L_ODR_50HZ;
+ data->range = QMC5883L_RNG_2G;
+ data->osr = QMC5883L_OSR_64;
+
+ return regmap_write(map, QMC5883L_REG_CTRL1,
+ FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_CONT) |
+ FIELD_PREP(QMC5883L_ODR_MASK, data->odr) |
+ FIELD_PREP(QMC5883L_RNG_MASK, data->range) |
+ FIELD_PREP(QMC5883L_OSR_MASK, data->osr));
+}
+
+static void qmc5883l_power_down_action(void *priv)
+{
+ struct qmc5883l_data *data = priv;
+
+ regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+ QMC5883L_MODE_MASK,
+ FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_STANDBY));
+}
+
+static bool qmc5883l_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg <= QMC5883L_REG_STATUS1;
+}
+
+static bool qmc5883l_writable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case QMC5883L_REG_CTRL1:
+ case QMC5883L_REG_CTRL2:
+ case QMC5883L_REG_SET_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config qmc5883l_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = QMC5883L_REG_ID,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = qmc5883l_volatile_reg,
+ .writeable_reg = qmc5883l_writable_reg,
+};
+
+#define QMC5883L_CHANNEL(_axis) \
+ { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .address = QMC5883L_AXIS_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ }
+
+static const struct iio_chan_spec qmc5883l_channels[] = {
+ QMC5883L_CHANNEL(X),
+ QMC5883L_CHANNEL(Y),
+ QMC5883L_CHANNEL(Z),
+};
+
+static int qmc5883l_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct qmc5883l_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *map;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ map = devm_regmap_init_i2c(client, &qmc5883l_regmap_config);
+ if (IS_ERR(map))
+ return dev_err_probe(dev, PTR_ERR(map),
+ "regmap initialization failed\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable VDD regulator\n");
+
+ ret = devm_regulator_get_enable(dev, "vddio");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable VDDIO regulator\n");
+
+ /* POR completion time max per datasheet Table 7 */
+ fsleep(QMC5883L_PORT_US);
+
+ data = iio_priv(indio_dev);
+ data->regmap = map;
+
+ ret = devm_mutex_init(dev, &data->mutex);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "qmc5883l";
+ indio_dev->info = &qmc5883l_info;
+ indio_dev->channels = qmc5883l_channels;
+ indio_dev->num_channels = ARRAY_SIZE(qmc5883l_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = qmc5883l_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "qmc5883l init failed\n");
+
+ ret = devm_add_action_or_reset(dev, qmc5883l_power_down_action, data);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id qmc5883l_match[] = {
+ { .compatible = "qstcorp,qmc5883l" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qmc5883l_match);
+
+static const struct i2c_device_id qmc5883l_id[] = {
+ { .name = "qmc5883l" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, qmc5883l_id);
+
+static struct i2c_driver qmc5883l_driver = {
+ .driver = {
+ .name = "qmc5883l",
+ .of_match_table = qmc5883l_match,
+ },
+ .id_table = qmc5883l_id,
+ .probe = qmc5883l_probe,
+};
+module_i2c_driver(qmc5883l_driver);
+
+MODULE_DESCRIPTION("QST QMC5883L 3-Axis Magnetic Sensor driver");
+MODULE_AUTHOR("Siratul Islam <siratul.islam@linux.dev>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/iio/magnetometer/qmc6308.c b/drivers/iio/magnetometer/qmc6308.c
new file mode 100644
index 000000000000..72348cd9ef01
--- /dev/null
+++ b/drivers/iio/magnetometer/qmc6308.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Support for QST QMC6308 3-Axis Magnetic Sensor on I2C bus.
+ *
+ * Copyright (C) 2026 Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
+ *
+ * Datasheet available at
+ * <https://qstcorp.com/upload/pdf/202202/13-52-15%20QMC6308%20Datasheet%20Rev.%20F(1).pdf>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/iio/iio.h>
+
+#define QMC6308_REG_ID 0x00
+#define QMC6308_REG_X_LSB 0x01
+#define QMC6308_REG_STATUS 0x09
+#define QMC6308_REG_CTRL1 0x0A
+#define QMC6308_REG_CTRL2 0x0B
+#define QMC6308_REG_CTRL3 0x0D
+#define QMC6308_REG_CTRL4 0x29
+
+#define QMC6308_CHIP_ID 0x80
+
+/* Control register 1 */
+#define QMC6308_MODE_MASK GENMASK(1, 0)
+#define QMC6308_ODR_MASK GENMASK(3, 2)
+#define QMC6308_OSR1_MASK GENMASK(5, 4)
+#define QMC6308_OSR2_MASK GENMASK(7, 6)
+
+#define QMC6308_MODE_SUSPEND 0x00
+#define QMC6308_MODE_NORMAL 0x01
+
+#define QMC6308_ODR_10HZ 0x00
+#define QMC6308_ODR_50HZ 0x01
+#define QMC6308_ODR_100HZ 0x02
+#define QMC6308_ODR_200HZ 0x03
+
+#define QMC6308_OSR1_8 0x00
+#define QMC6308_OSR1_4 0x01
+#define QMC6308_OSR1_2 0x02
+#define QMC6308_OSR1_1 0x03
+
+/* Control register 2 */
+#define QMC6308_SET_RESET_MASK GENMASK(1, 0)
+#define QMC6308_RNG_MASK GENMASK(3, 2)
+#define QMC6308_SELF_TEST BIT(6)
+#define QMC6308_SOFT_RST BIT(7)
+
+#define QMC6308_SET_RESET_ON 0x00
+
+#define QMC6308_RNG_30G 0x00
+#define QMC6308_RNG_12G 0x01
+#define QMC6308_RNG_8G 0x02
+#define QMC6308_RNG_2G 0x03
+
+/* Status register */
+#define QMC6308_STATUS_DRDY BIT(0)
+#define QMC6308_STATUS_OVFL BIT(1)
+
+/* Power-on completion time (datasheet Table 7) */
+#define QMC6308_POR_US 250
+
+#define QMC6308_AUTOSUSPEND_DELAY_MS 500
+
+struct qmc6308_data {
+ struct regmap *regmap;
+ /* Protect data->range/odr/osr and serialize measurements */
+ struct mutex mutex;
+ struct iio_mount_matrix orientation;
+ u8 range;
+ u8 odr;
+ u8 osr;
+};
+
+enum qmc6308_axis {
+ QMC6308_AXIS_X,
+ QMC6308_AXIS_Y,
+ QMC6308_AXIS_Z,
+};
+
+static const int qmc6308_odr_avail[] = {
+ [QMC6308_ODR_10HZ] = 10,
+ [QMC6308_ODR_50HZ] = 50,
+ [QMC6308_ODR_100HZ] = 100,
+ [QMC6308_ODR_200HZ] = 200,
+};
+
+static const int qmc6308_osr1_avail[] = {
+ [QMC6308_OSR1_8] = 8,
+ [QMC6308_OSR1_4] = 4,
+ [QMC6308_OSR1_2] = 2,
+ [QMC6308_OSR1_1] = 1,
+};
+
+/*
+ * Sensitivity is 1000/2500/3750/15000 LSB/Gauss for the
+ * +-30/12/8/2 Gauss ranges respectively.
+ */
+static const int qmc6308_scales[][2] = {
+ [QMC6308_RNG_30G] = { 0, 1000000 },
+ [QMC6308_RNG_12G] = { 0, 400000 },
+ [QMC6308_RNG_8G] = { 0, 266667 },
+ [QMC6308_RNG_2G] = { 0, 66667 },
+};
+
+static int qmc6308_set_mode(struct qmc6308_data *data, unsigned int mode)
+{
+ return regmap_update_bits(data->regmap, QMC6308_REG_CTRL1,
+ QMC6308_MODE_MASK,
+ FIELD_PREP(QMC6308_MODE_MASK, mode));
+}
+
+static int qmc6308_take_measurement(struct iio_dev *indio_dev, int index,
+ int *val)
+{
+ struct qmc6308_data *data = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(data->regmap);
+ unsigned int status;
+ __le16 buf[3];
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret) {
+ dev_err(dev, "Failed to power on (%d)\n", ret);
+ return ret;
+ }
+
+ guard(mutex)(&data->mutex);
+
+ /*
+ * Reading the status register clears DRDY, which is why the poll
+ * and the data read stay under one mutex hold. A runtime resume
+ * clears DRDY too, so a sample converted before the last suspend
+ * is never returned here.
+ *
+ * The timeout is 50ms of headroom over the slowest ODR (10Hz).
+ */
+ ret = regmap_read_poll_timeout(data->regmap, QMC6308_REG_STATUS,
+ status, (status & QMC6308_STATUS_DRDY),
+ 2 * USEC_PER_MSEC,
+ 150 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, QMC6308_REG_X_LSB, buf,
+ sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (status & QMC6308_STATUS_OVFL)
+ return -ERANGE;
+
+ *val = (s16)le16_to_cpu(buf[index]);
+
+ return 0;
+}
+
+static int qmc6308_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct qmc6308_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = qmc6308_take_measurement(indio_dev, chan->address, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&data->mutex);
+
+ *val = qmc6308_scales[data->range][0];
+ *val2 = qmc6308_scales[data->range][1];
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ guard(mutex)(&data->mutex);
+
+ *val = qmc6308_odr_avail[data->odr];
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ guard(mutex)(&data->mutex);
+
+ *val = qmc6308_osr1_avail[data->osr];
+
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int qmc6308_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct qmc6308_data *data = iio_priv(indio_dev);
+ unsigned int status;
+ unsigned int i;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE: {
+ if (val != 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(qmc6308_scales); i++) {
+ if (val2 == qmc6308_scales[i][1])
+ break;
+ }
+ if (i == ARRAY_SIZE(qmc6308_scales))
+ return -EINVAL;
+
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL2,
+ QMC6308_RNG_MASK,
+ FIELD_PREP(QMC6308_RNG_MASK, i));
+ if (ret)
+ return ret;
+
+ data->range = i;
+
+ /*
+ * The data registers still hold (and DRDY still
+ * advertises) a sample converted at the previous range;
+ * discard it so that a read does not pair old-range data
+ * with the new scale. A conversion already in flight may
+ * still complete at the old range, so this narrows the
+ * window rather than closing it. The range change itself
+ * took effect, so only log a failure here: an error
+ * would mislead userspace about an effective write.
+ */
+ ret = regmap_read(data->regmap, QMC6308_REG_STATUS,
+ &status);
+ if (ret)
+ dev_warn(regmap_get_device(data->regmap),
+ "Failed to discard stale sample (%d)\n", ret);
+
+ return 0;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ for (i = 0; i < ARRAY_SIZE(qmc6308_odr_avail); i++) {
+ if (val == qmc6308_odr_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(qmc6308_odr_avail))
+ return -EINVAL;
+
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1,
+ QMC6308_ODR_MASK,
+ FIELD_PREP(QMC6308_ODR_MASK, i));
+ if (ret)
+ return ret;
+
+ data->odr = i;
+
+ return 0;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ for (i = 0; i < ARRAY_SIZE(qmc6308_osr1_avail); i++) {
+ if (val == qmc6308_osr1_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(qmc6308_osr1_avail))
+ return -EINVAL;
+
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1,
+ QMC6308_OSR1_MASK,
+ FIELD_PREP(QMC6308_OSR1_MASK, i));
+ if (ret)
+ return ret;
+
+ data->osr = i;
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int qmc6308_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 = qmc6308_odr_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(qmc6308_odr_avail);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = qmc6308_osr1_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(qmc6308_osr1_avail);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)qmc6308_scales;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(qmc6308_scales) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int qmc6308_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;
+ }
+}
+
+static const struct iio_mount_matrix *
+qmc6308_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct qmc6308_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
+static const struct iio_chan_spec_ext_info qmc6308_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, qmc6308_get_mount_matrix),
+ { }
+};
+
+static const struct iio_info qmc6308_info = {
+ .read_raw = qmc6308_read_raw,
+ .write_raw = qmc6308_write_raw,
+ .read_avail = qmc6308_read_avail,
+ .write_raw_get_fmt = qmc6308_write_raw_get_fmt,
+};
+
+static int qmc6308_init(struct qmc6308_data *data)
+{
+ struct regmap *map = data->regmap;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(map, QMC6308_REG_ID, &reg);
+ if (ret)
+ return ret;
+
+ /* Allow unknown IDs so that fallback compatibles work */
+ if (reg != QMC6308_CHIP_ID)
+ dev_warn(regmap_get_device(map),
+ "Unknown chip id: 0x%02x, continuing\n", reg);
+
+ /* The SOFT_RST bit is not auto-cleared and must be written back 0 */
+ ret = regmap_write(map, QMC6308_REG_CTRL2, QMC6308_SOFT_RST);
+ if (ret)
+ return ret;
+
+ /*
+ * The datasheet gives no soft-reset completion figure; reuse the
+ * power-on time as a conservative bound.
+ */
+ fsleep(QMC6308_POR_US);
+
+ data->range = QMC6308_RNG_30G;
+ data->odr = QMC6308_ODR_50HZ;
+ data->osr = QMC6308_OSR1_8;
+
+ ret = regmap_write(map, QMC6308_REG_CTRL2,
+ FIELD_PREP(QMC6308_SET_RESET_MASK,
+ QMC6308_SET_RESET_ON) |
+ FIELD_PREP(QMC6308_RNG_MASK, data->range));
+ if (ret)
+ return ret;
+
+ /* OSR2 (second-stage filter) set to its power-on default of 0 */
+ return regmap_write(map, QMC6308_REG_CTRL1,
+ FIELD_PREP(QMC6308_MODE_MASK,
+ QMC6308_MODE_NORMAL) |
+ FIELD_PREP(QMC6308_ODR_MASK, data->odr) |
+ FIELD_PREP(QMC6308_OSR1_MASK, data->osr) |
+ FIELD_PREP(QMC6308_OSR2_MASK, 0));
+}
+
+static void qmc6308_power_down_action(void *priv)
+{
+ struct qmc6308_data *data = priv;
+
+ if (!pm_runtime_status_suspended(regmap_get_device(data->regmap)))
+ qmc6308_set_mode(data, QMC6308_MODE_SUSPEND);
+}
+
+static bool qmc6308_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= QMC6308_REG_X_LSB && reg <= QMC6308_REG_STATUS;
+}
+
+static bool qmc6308_writable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case QMC6308_REG_CTRL1:
+ case QMC6308_REG_CTRL2:
+ case QMC6308_REG_CTRL3:
+ case QMC6308_REG_CTRL4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config qmc6308_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = QMC6308_REG_CTRL4,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = qmc6308_volatile_reg,
+ .writeable_reg = qmc6308_writable_reg,
+};
+
+#define QMC6308_CHANNEL(_axis) \
+ { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .address = QMC6308_AXIS_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .ext_info = qmc6308_ext_info, \
+ }
+
+static const struct iio_chan_spec qmc6308_channels[] = {
+ QMC6308_CHANNEL(X),
+ QMC6308_CHANNEL(Y),
+ QMC6308_CHANNEL(Z),
+};
+
+static int qmc6308_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct qmc6308_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *map;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ map = devm_regmap_init_i2c(client, &qmc6308_regmap_config);
+ if (IS_ERR(map))
+ return dev_err_probe(dev, PTR_ERR(map),
+ "regmap initialization failed\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable VDD regulator\n");
+
+ fsleep(QMC6308_POR_US);
+
+ data = iio_priv(indio_dev);
+ data->regmap = map;
+
+ ret = devm_mutex_init(dev, &data->mutex);
+ if (ret)
+ return ret;
+
+ ret = iio_read_mount_matrix(dev, &data->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read mount matrix\n");
+
+ indio_dev->name = "qmc6308";
+ indio_dev->info = &qmc6308_info;
+ indio_dev->channels = qmc6308_channels;
+ indio_dev->num_channels = ARRAY_SIZE(qmc6308_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = qmc6308_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "qmc6308 init failed\n");
+
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, qmc6308_power_down_action, data);
+ if (ret)
+ return ret;
+
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, QMC6308_AUTOSUSPEND_DELAY_MS);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int qmc6308_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct qmc6308_data *data = iio_priv(indio_dev);
+
+ return qmc6308_set_mode(data, QMC6308_MODE_SUSPEND);
+}
+
+static int qmc6308_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct qmc6308_data *data = iio_priv(indio_dev);
+ unsigned int status;
+ int ret;
+
+ ret = qmc6308_set_mode(data, QMC6308_MODE_NORMAL);
+ if (ret)
+ return ret;
+
+ /*
+ * DRDY may still be set for a sample converted before the last
+ * suspend; reading the status register clears it so the next
+ * measurement waits for fresh data.
+ */
+ ret = regmap_read(data->regmap, QMC6308_REG_STATUS, &status);
+ if (ret) {
+ /* Best effort to leave the chip in a consistent state */
+ qmc6308_set_mode(data, QMC6308_MODE_SUSPEND);
+ }
+
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(qmc6308_pm_ops, qmc6308_runtime_suspend,
+ qmc6308_runtime_resume, NULL);
+
+static const struct of_device_id qmc6308_match[] = {
+ { .compatible = "qstcorp,qmc6308" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qmc6308_match);
+
+static const struct i2c_device_id qmc6308_id[] = {
+ { .name = "qmc6308" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, qmc6308_id);
+
+static struct i2c_driver qmc6308_driver = {
+ .driver = {
+ .name = "qmc6308",
+ .of_match_table = qmc6308_match,
+ .pm = pm_ptr(&qmc6308_pm_ops),
+ },
+ .id_table = qmc6308_id,
+ .probe = qmc6308_probe,
+};
+module_i2c_driver(qmc6308_driver);
+
+MODULE_DESCRIPTION("QST QMC6308 3-Axis Magnetic Sensor driver");
+MODULE_AUTHOR("Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
index ac3f9f7fc808..46fce2978d76 100644
--- a/drivers/iio/magnetometer/rm3100-core.c
+++ b/drivers/iio/magnetometer/rm3100-core.c
@@ -568,10 +568,8 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
IRQF_ONESHOT,
indio_dev->name,
indio_dev);
- if (ret < 0) {
- dev_err(dev, "request irq line failed.\n");
+ if (ret)
return ret;
- }
data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d",
indio_dev->name,
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
index 69693e836ad7..af4642b6c3c0 100644
--- a/drivers/iio/magnetometer/st_magn_spi.c
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -87,12 +87,12 @@ static int st_magn_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id st_magn_id_table[] = {
- { LIS3MDL_MAGN_DEV_NAME },
- { LSM303AGR_MAGN_DEV_NAME },
- { LIS2MDL_MAGN_DEV_NAME },
- { LSM9DS1_MAGN_DEV_NAME },
- { IIS2MDC_MAGN_DEV_NAME },
- { LSM303C_MAGN_DEV_NAME },
+ { .name = LIS3MDL_MAGN_DEV_NAME },
+ { .name = LSM303AGR_MAGN_DEV_NAME },
+ { .name = LIS2MDL_MAGN_DEV_NAME },
+ { .name = LSM9DS1_MAGN_DEV_NAME },
+ { .name = IIS2MDC_MAGN_DEV_NAME },
+ { .name = LSM303C_MAGN_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c
index 4421dafcf94e..562457a67382 100644
--- a/drivers/iio/multiplexer/iio-mux.c
+++ b/drivers/iio/multiplexer/iio-mux.c
@@ -463,3 +463,4 @@ module_platform_driver(mux_driver);
MODULE_DESCRIPTION("IIO multiplexer driver");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
index ea60611192f3..aea28321db95 100644
--- a/drivers/iio/orientation/hid-sensor-incl-3d.c
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -85,8 +85,7 @@ static const struct iio_chan_spec incl_3d_channels[] = {
};
/* Adjust channel real bits based on report descriptor */
-static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan,
- int size)
+static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, int size)
{
chan->scan_type.sign = 's';
/* Real storage bits will change based on the report desc. */
@@ -97,9 +96,8 @@ static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan,
/* Channel read_raw handler */
static int incl_3d_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct incl_3d_state *incl_state = iio_priv(indio_dev);
int report_id = -1;
@@ -124,7 +122,7 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
min < 0);
else {
hid_sensor_power_state(&incl_state->common_attributes,
- false);
+ false);
return -EINVAL;
}
hid_sensor_power_state(&incl_state->common_attributes, false);
@@ -157,10 +155,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int incl_3d_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct incl_3d_state *incl_state = iio_priv(indio_dev);
int ret;
@@ -188,8 +184,7 @@ static const struct iio_info incl_3d_info = {
/* Callback handler to send event after all samples are received and captured */
static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct incl_3d_state *incl_state = iio_priv(indio_dev);
@@ -211,9 +206,9 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- size_t raw_len, char *raw_data,
- void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct incl_3d_state *incl_state = iio_priv(indio_dev);
@@ -246,7 +241,7 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
static int incl_3d_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
struct iio_chan_spec *channels,
- unsigned usage_id,
+ u32 usage_id,
struct incl_3d_state *st)
{
int ret;
@@ -306,7 +301,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct incl_3d_state));
- if (indio_dev == NULL)
+ if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
@@ -349,33 +344,33 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
atomic_set(&incl_state->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &incl_state->common_attributes);
+ &incl_state->common_attributes);
if (ret) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
incl_state->callbacks.send_event = incl_3d_proc_event;
incl_state->callbacks.capture_sample = incl_3d_capture_sample;
incl_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev,
- HID_USAGE_SENSOR_INCLINOMETER_3D,
- &incl_state->callbacks);
+ HID_USAGE_SENSOR_INCLINOMETER_3D,
+ &incl_state->callbacks);
if (ret) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return 0;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes);
return ret;
@@ -388,8 +383,8 @@ static void hid_incl_3d_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct incl_3d_state *incl_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes);
}
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index cc3e66dbb90f..f8c98f866cb0 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -80,9 +80,8 @@ static const struct iio_chan_spec dev_rot_channels[] = {
/* Channel read_raw handler */
static int dev_rot_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int size, int *vals, int *val_len,
- long mask)
+ struct iio_chan_spec const *chan,
+ int size, int *vals, int *val_len, long mask)
{
struct dev_rot_state *rot_state = iio_priv(indio_dev);
struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev;
@@ -164,10 +163,8 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int dev_rot_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct dev_rot_state *rot_state = iio_priv(indio_dev);
int ret;
@@ -211,8 +208,7 @@ static const struct iio_info dev_rot_info = {
/* Callback handler to send event after all samples are received and captured */
static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct dev_rot_state *rot_state = iio_priv(indio_dev);
@@ -244,9 +240,9 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- size_t raw_len, char *raw_data,
- void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct dev_rot_state *rot_state = iio_priv(indio_dev);
@@ -275,7 +271,7 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id*/
static int dev_rot_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
+ u32 usage_id,
struct dev_rot_state *st)
{
int ret;
@@ -313,7 +309,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct dev_rot_state));
- if (indio_dev == NULL)
+ if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
@@ -361,32 +357,32 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
atomic_set(&rot_state->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &rot_state->common_attributes);
+ &rot_state->common_attributes);
if (ret) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
rot_state->callbacks.send_event = dev_rot_proc_event;
rot_state->callbacks.capture_sample = dev_rot_capture_sample;
rot_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
- &rot_state->callbacks);
+ &rot_state->callbacks);
if (ret) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return 0;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
return ret;
@@ -399,8 +395,8 @@ static void hid_dev_rot_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct dev_rot_state *rot_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
}
diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c
index bffa8fe4fe44..d275bc1413fe 100644
--- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c
+++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c
@@ -106,8 +106,8 @@ static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels,
/* Channel read_raw handler */
static int hinge_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct hinge_state *st = iio_priv(indio_dev);
struct hid_sensor_hub_device *hsdev;
@@ -153,8 +153,8 @@ static int hinge_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int hinge_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int val, int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct hinge_state *st = iio_priv(indio_dev);
@@ -189,7 +189,7 @@ static const struct iio_info hinge_info = {
* and captured.
*/
static int hinge_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned int usage_id, void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct hinge_state *st = iio_priv(indio_dev);
@@ -208,8 +208,9 @@ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned int usage_id, size_t raw_len,
- char *raw_data, void *priv)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct hinge_state *st = iio_priv(indio_dev);
@@ -235,7 +236,7 @@ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev,
static int hinge_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
struct iio_chan_spec *channels,
- unsigned int usage_id, struct hinge_state *st)
+ u32 usage_id, struct hinge_state *st)
{
int ret;
int i;
@@ -291,7 +292,7 @@ static int hid_hinge_probe(struct platform_device *pdev)
}
indio_dev->num_channels = ARRAY_SIZE(hinge_channels);
- indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels,
+ indio_dev->channels = devm_kmemdup(&pdev->dev, hinge_channels,
sizeof(hinge_channels), GFP_KERNEL);
if (!indio_dev->channels)
return -ENOMEM;
diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c
index ddf54a1df976..ec1b7bdc706b 100644
--- a/drivers/iio/potentiometer/max5481.c
+++ b/drivers/iio/potentiometer/max5481.c
@@ -169,10 +169,10 @@ static int max5481_probe(struct spi_device *spi)
}
static const struct spi_device_id max5481_id_table[] = {
- { "max5481", max5481 },
- { "max5482", max5482 },
- { "max5483", max5483 },
- { "max5484", max5484 },
+ { .name = "max5481", .driver_data = max5481 },
+ { .name = "max5482", .driver_data = max5482 },
+ { .name = "max5483", .driver_data = max5483 },
+ { .name = "max5484", .driver_data = max5484 },
{ }
};
MODULE_DEVICE_TABLE(spi, max5481_id_table);
diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c
index 9541695af474..1f0d0056f412 100644
--- a/drivers/iio/potentiometer/max5487.c
+++ b/drivers/iio/potentiometer/max5487.c
@@ -125,9 +125,9 @@ static void max5487_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id max5487_id[] = {
- { "MAX5487", 10 },
- { "MAX5488", 50 },
- { "MAX5489", 100 },
+ { .name = "MAX5487", .driver_data = 10 },
+ { .name = "MAX5488", .driver_data = 50 },
+ { .name = "MAX5489", .driver_data = 100 },
{ }
};
MODULE_DEVICE_TABLE(spi, max5487_id);
diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c
index ed0764231224..cfd5d0f6d368 100644
--- a/drivers/iio/potentiometer/mcp41010.c
+++ b/drivers/iio/potentiometer/mcp41010.c
@@ -175,12 +175,12 @@ static const struct of_device_id mcp41010_match[] = {
MODULE_DEVICE_TABLE(of, mcp41010_match);
static const struct spi_device_id mcp41010_id[] = {
- { "mcp41010", MCP41010 },
- { "mcp41050", MCP41050 },
- { "mcp41100", MCP41100 },
- { "mcp42010", MCP42010 },
- { "mcp42050", MCP42050 },
- { "mcp42100", MCP42100 },
+ { .name = "mcp41010", .driver_data = MCP41010 },
+ { .name = "mcp41050", .driver_data = MCP41050 },
+ { .name = "mcp41100", .driver_data = MCP41100 },
+ { .name = "mcp42010", .driver_data = MCP42010 },
+ { .name = "mcp42050", .driver_data = MCP42050 },
+ { .name = "mcp42100", .driver_data = MCP42100 },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp41010_id);
diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c
index dad09ce6a75d..fadf9fcc1817 100644
--- a/drivers/iio/potentiometer/mcp4131.c
+++ b/drivers/iio/potentiometer/mcp4131.c
@@ -406,70 +406,70 @@ static const struct of_device_id mcp4131_dt_ids[] = {
MODULE_DEVICE_TABLE(of, mcp4131_dt_ids);
static const struct spi_device_id mcp4131_id[] = {
- { "mcp4131-502", MCP413x_502 },
- { "mcp4131-103", MCP413x_103 },
- { "mcp4131-503", MCP413x_503 },
- { "mcp4131-104", MCP413x_104 },
- { "mcp4132-502", MCP413x_502 },
- { "mcp4132-103", MCP413x_103 },
- { "mcp4132-503", MCP413x_503 },
- { "mcp4132-104", MCP413x_104 },
- { "mcp4141-502", MCP414x_502 },
- { "mcp4141-103", MCP414x_103 },
- { "mcp4141-503", MCP414x_503 },
- { "mcp4141-104", MCP414x_104 },
- { "mcp4142-502", MCP414x_502 },
- { "mcp4142-103", MCP414x_103 },
- { "mcp4142-503", MCP414x_503 },
- { "mcp4142-104", MCP414x_104 },
- { "mcp4151-502", MCP415x_502 },
- { "mcp4151-103", MCP415x_103 },
- { "mcp4151-503", MCP415x_503 },
- { "mcp4151-104", MCP415x_104 },
- { "mcp4152-502", MCP415x_502 },
- { "mcp4152-103", MCP415x_103 },
- { "mcp4152-503", MCP415x_503 },
- { "mcp4152-104", MCP415x_104 },
- { "mcp4161-502", MCP416x_502 },
- { "mcp4161-103", MCP416x_103 },
- { "mcp4161-503", MCP416x_503 },
- { "mcp4161-104", MCP416x_104 },
- { "mcp4162-502", MCP416x_502 },
- { "mcp4162-103", MCP416x_103 },
- { "mcp4162-503", MCP416x_503 },
- { "mcp4162-104", MCP416x_104 },
- { "mcp4231-502", MCP423x_502 },
- { "mcp4231-103", MCP423x_103 },
- { "mcp4231-503", MCP423x_503 },
- { "mcp4231-104", MCP423x_104 },
- { "mcp4232-502", MCP423x_502 },
- { "mcp4232-103", MCP423x_103 },
- { "mcp4232-503", MCP423x_503 },
- { "mcp4232-104", MCP423x_104 },
- { "mcp4241-502", MCP424x_502 },
- { "mcp4241-103", MCP424x_103 },
- { "mcp4241-503", MCP424x_503 },
- { "mcp4241-104", MCP424x_104 },
- { "mcp4242-502", MCP424x_502 },
- { "mcp4242-103", MCP424x_103 },
- { "mcp4242-503", MCP424x_503 },
- { "mcp4242-104", MCP424x_104 },
- { "mcp4251-502", MCP425x_502 },
- { "mcp4251-103", MCP425x_103 },
- { "mcp4251-503", MCP425x_503 },
- { "mcp4251-104", MCP425x_104 },
- { "mcp4252-502", MCP425x_502 },
- { "mcp4252-103", MCP425x_103 },
- { "mcp4252-503", MCP425x_503 },
- { "mcp4252-104", MCP425x_104 },
- { "mcp4261-502", MCP426x_502 },
- { "mcp4261-103", MCP426x_103 },
- { "mcp4261-503", MCP426x_503 },
- { "mcp4261-104", MCP426x_104 },
- { "mcp4262-502", MCP426x_502 },
- { "mcp4262-103", MCP426x_103 },
- { "mcp4262-503", MCP426x_503 },
- { "mcp4262-104", MCP426x_104 },
+ { .name = "mcp4131-502", .driver_data = MCP413x_502 },
+ { .name = "mcp4131-103", .driver_data = MCP413x_103 },
+ { .name = "mcp4131-503", .driver_data = MCP413x_503 },
+ { .name = "mcp4131-104", .driver_data = MCP413x_104 },
+ { .name = "mcp4132-502", .driver_data = MCP413x_502 },
+ { .name = "mcp4132-103", .driver_data = MCP413x_103 },
+ { .name = "mcp4132-503", .driver_data = MCP413x_503 },
+ { .name = "mcp4132-104", .driver_data = MCP413x_104 },
+ { .name = "mcp4141-502", .driver_data = MCP414x_502 },
+ { .name = "mcp4141-103", .driver_data = MCP414x_103 },
+ { .name = "mcp4141-503", .driver_data = MCP414x_503 },
+ { .name = "mcp4141-104", .driver_data = MCP414x_104 },
+ { .name = "mcp4142-502", .driver_data = MCP414x_502 },
+ { .name = "mcp4142-103", .driver_data = MCP414x_103 },
+ { .name = "mcp4142-503", .driver_data = MCP414x_503 },
+ { .name = "mcp4142-104", .driver_data = MCP414x_104 },
+ { .name = "mcp4151-502", .driver_data = MCP415x_502 },
+ { .name = "mcp4151-103", .driver_data = MCP415x_103 },
+ { .name = "mcp4151-503", .driver_data = MCP415x_503 },
+ { .name = "mcp4151-104", .driver_data = MCP415x_104 },
+ { .name = "mcp4152-502", .driver_data = MCP415x_502 },
+ { .name = "mcp4152-103", .driver_data = MCP415x_103 },
+ { .name = "mcp4152-503", .driver_data = MCP415x_503 },
+ { .name = "mcp4152-104", .driver_data = MCP415x_104 },
+ { .name = "mcp4161-502", .driver_data = MCP416x_502 },
+ { .name = "mcp4161-103", .driver_data = MCP416x_103 },
+ { .name = "mcp4161-503", .driver_data = MCP416x_503 },
+ { .name = "mcp4161-104", .driver_data = MCP416x_104 },
+ { .name = "mcp4162-502", .driver_data = MCP416x_502 },
+ { .name = "mcp4162-103", .driver_data = MCP416x_103 },
+ { .name = "mcp4162-503", .driver_data = MCP416x_503 },
+ { .name = "mcp4162-104", .driver_data = MCP416x_104 },
+ { .name = "mcp4231-502", .driver_data = MCP423x_502 },
+ { .name = "mcp4231-103", .driver_data = MCP423x_103 },
+ { .name = "mcp4231-503", .driver_data = MCP423x_503 },
+ { .name = "mcp4231-104", .driver_data = MCP423x_104 },
+ { .name = "mcp4232-502", .driver_data = MCP423x_502 },
+ { .name = "mcp4232-103", .driver_data = MCP423x_103 },
+ { .name = "mcp4232-503", .driver_data = MCP423x_503 },
+ { .name = "mcp4232-104", .driver_data = MCP423x_104 },
+ { .name = "mcp4241-502", .driver_data = MCP424x_502 },
+ { .name = "mcp4241-103", .driver_data = MCP424x_103 },
+ { .name = "mcp4241-503", .driver_data = MCP424x_503 },
+ { .name = "mcp4241-104", .driver_data = MCP424x_104 },
+ { .name = "mcp4242-502", .driver_data = MCP424x_502 },
+ { .name = "mcp4242-103", .driver_data = MCP424x_103 },
+ { .name = "mcp4242-503", .driver_data = MCP424x_503 },
+ { .name = "mcp4242-104", .driver_data = MCP424x_104 },
+ { .name = "mcp4251-502", .driver_data = MCP425x_502 },
+ { .name = "mcp4251-103", .driver_data = MCP425x_103 },
+ { .name = "mcp4251-503", .driver_data = MCP425x_503 },
+ { .name = "mcp4251-104", .driver_data = MCP425x_104 },
+ { .name = "mcp4252-502", .driver_data = MCP425x_502 },
+ { .name = "mcp4252-103", .driver_data = MCP425x_103 },
+ { .name = "mcp4252-503", .driver_data = MCP425x_503 },
+ { .name = "mcp4252-104", .driver_data = MCP425x_104 },
+ { .name = "mcp4261-502", .driver_data = MCP426x_502 },
+ { .name = "mcp4261-103", .driver_data = MCP426x_103 },
+ { .name = "mcp4261-503", .driver_data = MCP426x_503 },
+ { .name = "mcp4261-104", .driver_data = MCP426x_104 },
+ { .name = "mcp4262-502", .driver_data = MCP426x_502 },
+ { .name = "mcp4262-103", .driver_data = MCP426x_103 },
+ { .name = "mcp4262-503", .driver_data = MCP426x_503 },
+ { .name = "mcp4262-104", .driver_data = MCP426x_104 },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp4131_id);
diff --git a/drivers/iio/potentiometer/x9250.c b/drivers/iio/potentiometer/x9250.c
index 735348492699..974e4447fc2c 100644
--- a/drivers/iio/potentiometer/x9250.c
+++ b/drivers/iio/potentiometer/x9250.c
@@ -198,8 +198,8 @@ static const struct of_device_id x9250_of_match[] = {
MODULE_DEVICE_TABLE(of, x9250_of_match);
static const struct spi_device_id x9250_id_table[] = {
- { "x9250t", (kernel_ulong_t)&x9250_cfg[X9250T] },
- { "x9250u", (kernel_ulong_t)&x9250_cfg[X9250U] },
+ { .name = "x9250t", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250T] },
+ { .name = "x9250u", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250U] },
{ }
};
MODULE_DEVICE_TABLE(spi, x9250_id_table);
diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c
index 1984d990438c..00cb3fea97ca 100644
--- a/drivers/iio/potentiostat/lmp91000.c
+++ b/drivers/iio/potentiostat/lmp91000.c
@@ -422,3 +422,4 @@ module_i2c_driver(lmp91000_driver);
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("LMP91000 digital potentiostat");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c
index b44f1bf4c633..41395036e9ad 100644
--- a/drivers/iio/pressure/abp2030pa.c
+++ b/drivers/iio/pressure/abp2030pa.c
@@ -379,7 +379,6 @@ static int abp2_read_raw(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
- return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (channel->type) {
diff --git a/drivers/iio/pressure/abp2030pa_spi.c b/drivers/iio/pressure/abp2030pa_spi.c
index 8bc59eb499aa..60d078574827 100644
--- a/drivers/iio/pressure/abp2030pa_spi.c
+++ b/drivers/iio/pressure/abp2030pa_spi.c
@@ -45,7 +45,7 @@ static const struct of_device_id abp2_spi_match[] = {
MODULE_DEVICE_TABLE(of, abp2_spi_match);
static const struct spi_device_id abp2_spi_id[] = {
- { "abp2030pa" },
+ { .name = "abp2030pa" },
{ }
};
MODULE_DEVICE_TABLE(spi, abp2_spi_id);
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index dbe42233c81d..3940b8e61a06 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -750,7 +750,10 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
struct bmp280_data *data = iio_priv(indio_dev);
int ret;
- pm_runtime_get_sync(data->dev);
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
ret = bmp280_read_raw_impl(indio_dev, chan, val, val2, mask);
pm_runtime_put_autosuspend(data->dev);
@@ -924,7 +927,10 @@ static int bmp280_write_raw(struct iio_dev *indio_dev,
struct bmp280_data *data = iio_priv(indio_dev);
int ret;
- pm_runtime_get_sync(data->dev);
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
ret = bmp280_write_raw_impl(indio_dev, chan, val, val2, mask);
pm_runtime_put_autosuspend(data->dev);
@@ -1333,7 +1339,7 @@ static int __bmp280_trigger_probe(struct iio_dev *indio_dev,
irq_thread_handler, IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
- return dev_err_probe(dev, ret, "request IRQ failed.\n");
+ return ret;
ret = devm_iio_trigger_register(data->dev, data->trig);
if (ret)
@@ -2254,7 +2260,10 @@ static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val,
struct bmp280_data *data = priv;
int ret;
- pm_runtime_get_sync(data->dev);
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
ret = bmp580_nvmem_read_impl(priv, offset, val, bytes);
pm_runtime_put_autosuspend(data->dev);
@@ -2328,7 +2337,10 @@ static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val,
struct bmp280_data *data = priv;
int ret;
- pm_runtime_get_sync(data->dev);
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
ret = bmp580_nvmem_write_impl(priv, offset, val, bytes);
pm_runtime_put_autosuspend(data->dev);
@@ -3108,11 +3120,17 @@ EXPORT_SYMBOL_NS(bmp085_chip_info, "IIO_BMP280");
static int bmp280_buffer_preenable(struct iio_dev *indio_dev)
{
struct bmp280_data *data = iio_priv(indio_dev);
+ int ret;
- pm_runtime_get_sync(data->dev);
- data->chip_info->set_mode(data, BMP280_NORMAL);
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
- return 0;
+ ret = data->chip_info->set_mode(data, BMP280_NORMAL);
+ if (ret)
+ pm_runtime_put_autosuspend(data->dev);
+
+ return ret;
}
static int bmp280_buffer_postdisable(struct iio_dev *indio_dev)
diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c
index 04bf2f5be5b1..a02a621b8015 100644
--- a/drivers/iio/pressure/bmp280-spi.c
+++ b/drivers/iio/pressure/bmp280-spi.c
@@ -118,13 +118,13 @@ static const struct of_device_id bmp280_of_spi_match[] = {
MODULE_DEVICE_TABLE(of, bmp280_of_spi_match);
static const struct spi_device_id bmp280_spi_id[] = {
- { "bmp085", (kernel_ulong_t)&bmp085_chip_info },
- { "bmp180", (kernel_ulong_t)&bmp180_chip_info },
- { "bmp181", (kernel_ulong_t)&bmp180_chip_info },
- { "bmp280", (kernel_ulong_t)&bmp280_chip_info },
- { "bme280", (kernel_ulong_t)&bme280_chip_info },
- { "bmp380", (kernel_ulong_t)&bmp380_chip_info },
- { "bmp580", (kernel_ulong_t)&bmp580_chip_info },
+ { .name = "bmp085", .driver_data = (kernel_ulong_t)&bmp085_chip_info },
+ { .name = "bmp180", .driver_data = (kernel_ulong_t)&bmp180_chip_info },
+ { .name = "bmp181", .driver_data = (kernel_ulong_t)&bmp180_chip_info },
+ { .name = "bmp280", .driver_data = (kernel_ulong_t)&bmp280_chip_info },
+ { .name = "bme280", .driver_data = (kernel_ulong_t)&bme280_chip_info },
+ { .name = "bmp380", .driver_data = (kernel_ulong_t)&bmp380_chip_info },
+ { .name = "bmp580", .driver_data = (kernel_ulong_t)&bmp580_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, bmp280_spi_id);
diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c
index 01a873165923..961888fd03a7 100644
--- a/drivers/iio/pressure/dlhl60d.c
+++ b/drivers/iio/pressure/dlhl60d.c
@@ -309,10 +309,8 @@ static int dlh_probe(struct i2c_client *client)
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");
+ if (ret)
return ret;
- }
st->use_interrupt = true;
init_completion(&st->completion);
diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index ae499d197555..e688b0776547 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -55,9 +55,8 @@ static const struct iio_chan_spec press_channels[] = {
/* Channel read_raw handler */
static int press_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct press_state *press_state = iio_priv(indio_dev);
int report_id = -1;
@@ -81,7 +80,7 @@ static int press_read_raw(struct iio_dev *indio_dev,
}
if (report_id >= 0) {
hid_sensor_power_state(&press_state->common_attributes,
- true);
+ true);
*val = sensor_hub_input_attr_get_raw_value(
press_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PRESSURE, address,
@@ -89,7 +88,7 @@ static int press_read_raw(struct iio_dev *indio_dev,
SENSOR_HUB_SYNC,
min < 0);
hid_sensor_power_state(&press_state->common_attributes,
- false);
+ false);
} else {
*val = 0;
return -EINVAL;
@@ -123,10 +122,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
/* Channel write_raw handler */
static int press_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct press_state *press_state = iio_priv(indio_dev);
int ret = 0;
@@ -154,8 +151,7 @@ static const struct iio_info press_info = {
/* Callback handler to send event after all samples are received and captured */
static int press_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
- void *priv)
+ u32 usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct press_state *press_state = iio_priv(indio_dev);
@@ -175,7 +171,7 @@ static int press_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int press_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned usage_id,
+ u32 usage_id,
size_t raw_len, char *raw_data,
void *priv)
{
@@ -201,10 +197,10 @@ static int press_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id*/
static int press_parse_report(struct platform_device *pdev,
- struct hid_sensor_hub_device *hsdev,
- struct iio_chan_spec *channels,
- unsigned usage_id,
- struct press_state *st)
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ u32 usage_id,
+ struct press_state *st)
{
int ret;
@@ -221,7 +217,7 @@ static int press_parse_report(struct platform_device *pdev,
};
dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index,
- st->press_attr.report_id);
+ st->press_attr.report_id);
st->scale_precision = hid_sensor_format_scale(
HID_USAGE_SENSOR_PRESSURE,
@@ -240,8 +236,7 @@ static int hid_press_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct press_state *press_state;
- indio_dev = devm_iio_device_alloc(&pdev->dev,
- sizeof(struct press_state));
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct press_state));
if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
@@ -284,32 +279,32 @@ static int hid_press_probe(struct platform_device *pdev)
atomic_set(&press_state->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &press_state->common_attributes);
+ &press_state->common_attributes);
if (ret) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "device register failed\n");
- goto error_remove_trigger;
- }
-
press_state->callbacks.send_event = press_proc_event;
press_state->callbacks.capture_sample = press_capture_sample;
press_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE,
- &press_state->callbacks);
+ &press_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
- goto error_iio_unreg;
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
}
return ret;
-error_iio_unreg:
- iio_device_unregister(indio_dev);
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
return ret;
@@ -322,8 +317,8 @@ static void hid_press_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct press_state *press_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
}
diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c
index be58e550cc12..02021b6f799a 100644
--- a/drivers/iio/pressure/hsc030pa_spi.c
+++ b/drivers/iio/pressure/hsc030pa_spi.c
@@ -39,7 +39,7 @@ static const struct of_device_id hsc_spi_match[] = {
MODULE_DEVICE_TABLE(of, hsc_spi_match);
static const struct spi_device_id hsc_spi_id[] = {
- { "hsc030pa" },
+ { .name = "hsc030pa" },
{ }
};
MODULE_DEVICE_TABLE(spi, hsc_spi_id);
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
index 16e112b796ba..2929ea8e4e01 100644
--- a/drivers/iio/pressure/mpl115.c
+++ b/drivers/iio/pressure/mpl115.c
@@ -203,9 +203,9 @@ int mpl115_probe(struct device *dev, const char *name,
if (data->shutdown) {
/* Enable runtime PM */
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
/*
* As the device takes 3 ms to come up with a fresh
@@ -215,7 +215,10 @@ int mpl115_probe(struct device *dev, const char *name,
*/
pm_runtime_set_autosuspend_delay(dev, 2000);
pm_runtime_use_autosuspend(dev);
- pm_runtime_put(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
dev_dbg(dev, "low-power mode enabled");
} else
diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c
index 4e1d24beff94..3cff616e8d2f 100644
--- a/drivers/iio/pressure/mpl115_spi.c
+++ b/drivers/iio/pressure/mpl115_spi.c
@@ -84,7 +84,7 @@ static int mpl115_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id mpl115_spi_ids[] = {
- { "mpl115", 0 },
+ { .name = "mpl115" },
{ }
};
MODULE_DEVICE_TABLE(spi, mpl115_spi_ids);
diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c
index 23f47c04ed45..6270370383c3 100644
--- a/drivers/iio/pressure/mprls0025pa_spi.c
+++ b/drivers/iio/pressure/mprls0025pa_spi.c
@@ -59,7 +59,7 @@ static const struct of_device_id mpr_spi_match[] = {
MODULE_DEVICE_TABLE(of, mpr_spi_match);
static const struct spi_device_id mpr_spi_id[] = {
- { "mprls0025pa" },
+ { .name = "mprls0025pa" },
{ }
};
MODULE_DEVICE_TABLE(spi, mpr_spi_id);
diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c
index c41aaa6244cd..7133e6744e3f 100644
--- a/drivers/iio/pressure/ms5611_spi.c
+++ b/drivers/iio/pressure/ms5611_spi.c
@@ -113,8 +113,8 @@ static const struct of_device_id ms5611_spi_matches[] = {
MODULE_DEVICE_TABLE(of, ms5611_spi_matches);
static const struct spi_device_id ms5611_id[] = {
- { "ms5611", MS5611 },
- { "ms5607", MS5607 },
+ { .name = "ms5611", .driver_data = MS5611 },
+ { .name = "ms5607", .driver_data = MS5607 },
{ }
};
MODULE_DEVICE_TABLE(spi, ms5611_id);
diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c
index d00d7ed54cb1..f9deb48e6a41 100644
--- a/drivers/iio/pressure/rohm-bm1390.c
+++ b/drivers/iio/pressure/rohm-bm1390.c
@@ -621,17 +621,15 @@ static const struct iio_buffer_setup_ops bm1390_buffer_ops = {
.predisable = bm1390_buffer_predisable,
};
-static irqreturn_t bm1390_trigger_handler(int irq, void *p)
+static bool bm1390_handle_trigger(struct iio_dev *idev)
{
- struct iio_poll_func *pf = p;
- struct iio_dev *idev = pf->indio_dev;
struct bm1390_data *data = iio_priv(idev);
int ret, status;
/* DRDY is acked by reading status reg */
ret = regmap_read(data->regmap, BM1390_REG_STATUS, &status);
if (ret || !status)
- return IRQ_NONE;
+ return false;
dev_dbg(data->dev, "DRDY trig status 0x%x\n", status);
@@ -639,7 +637,7 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p)
ret = bm1390_pressure_read(data, &data->buf.pressure);
if (ret) {
dev_warn(data->dev, "sample read failed %d\n", ret);
- return IRQ_NONE;
+ return false;
}
}
@@ -648,15 +646,26 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p)
&data->buf.temp, sizeof(data->buf.temp));
if (ret) {
dev_warn(data->dev, "temp read failed %d\n", ret);
- return IRQ_HANDLED;
+ return true;
}
}
iio_push_to_buffers_with_ts(idev, &data->buf, sizeof(data->buf),
data->timestamp);
+
+ return true;
+}
+
+static irqreturn_t bm1390_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *idev = pf->indio_dev;
+ bool result;
+
+ result = bm1390_handle_trigger(idev);
iio_trigger_notify_done(idev->trig);
- return IRQ_HANDLED;
+ return IRQ_RETVAL(result);
}
/* Get timestamps and wake the thread if we need to read data */
@@ -796,7 +805,7 @@ static int bm1390_setup_trigger(struct bm1390_data *data, struct iio_dev *idev,
&bm1390_irq_thread_handler,
IRQF_ONESHOT, name, idev);
if (ret)
- return dev_err_probe(data->dev, ret, "Could not request IRQ\n");
+ return ret;
ret = devm_iio_trigger_register(data->dev, itrig);
diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c
index d843186b44af..8add0c392c6a 100644
--- a/drivers/iio/pressure/st_pressure_spi.c
+++ b/drivers/iio/pressure/st_pressure_spi.c
@@ -93,18 +93,18 @@ static int st_press_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id st_press_id_table[] = {
- { LPS001WP_PRESS_DEV_NAME },
- { LPS25H_PRESS_DEV_NAME },
- { LPS331AP_PRESS_DEV_NAME },
- { LPS22HB_PRESS_DEV_NAME },
- { LPS33HW_PRESS_DEV_NAME },
- { LPS35HW_PRESS_DEV_NAME },
- { LPS22HH_PRESS_DEV_NAME },
- { LPS22DF_PRESS_DEV_NAME },
- { "lps001wp-press" },
- { "lps25h-press", },
- { "lps331ap-press" },
- { "lps22hb-press" },
+ { .name = LPS001WP_PRESS_DEV_NAME },
+ { .name = LPS25H_PRESS_DEV_NAME },
+ { .name = LPS331AP_PRESS_DEV_NAME },
+ { .name = LPS22HB_PRESS_DEV_NAME },
+ { .name = LPS33HW_PRESS_DEV_NAME },
+ { .name = LPS35HW_PRESS_DEV_NAME },
+ { .name = LPS22HH_PRESS_DEV_NAME },
+ { .name = LPS22DF_PRESS_DEV_NAME },
+ { .name = "lps001wp-press" },
+ { .name = "lps25h-press" },
+ { .name = "lps331ap-press" },
+ { .name = "lps22hb-press" },
{ }
};
MODULE_DEVICE_TABLE(spi, st_press_id_table);
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
index 2c68fdf2744e..b38493ff3b8b 100644
--- a/drivers/iio/pressure/zpa2326.c
+++ b/drivers/iio/pressure/zpa2326.c
@@ -911,11 +911,8 @@ static int zpa2326_init_managed_irq(struct device *parent,
zpa2326_handle_threaded_irq,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
dev_name(parent), indio_dev);
- if (err) {
- dev_err(parent, "failed to request interrupt %d (%d)", irq,
- err);
+ if (err)
return err;
- }
dev_info(parent, "using interrupt %d", irq);
diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c
index 73e37a77c933..dfbb74a58dc0 100644
--- a/drivers/iio/pressure/zpa2326_spi.c
+++ b/drivers/iio/pressure/zpa2326_spi.c
@@ -60,7 +60,7 @@ static void zpa2326_remove_spi(struct spi_device *spi)
}
static const struct spi_device_id zpa2326_spi_ids[] = {
- { "zpa2326", 0 },
+ { .name = "zpa2326" },
{ }
};
MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids);
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index 3406232822cb..d7f43c3af165 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -428,10 +428,8 @@ static int as3935_probe(struct spi_device *spi)
dev_name(dev),
indio_dev);
- if (ret) {
- dev_err(dev, "unable to request irq\n");
+ if (ret)
return ret;
- }
ret = devm_iio_device_register(dev, indio_dev);
if (ret < 0) {
@@ -448,7 +446,7 @@ static const struct of_device_id as3935_of_match[] = {
MODULE_DEVICE_TABLE(of, as3935_of_match);
static const struct spi_device_id as3935_id[] = {
- {"as3935", 0},
+ { .name = "as3935" },
{ }
};
MODULE_DEVICE_TABLE(spi, as3935_id);
diff --git a/drivers/iio/proximity/d3323aa.c b/drivers/iio/proximity/d3323aa.c
index d40e3dff9eb1..1f43c1fed342 100644
--- a/drivers/iio/proximity/d3323aa.c
+++ b/drivers/iio/proximity/d3323aa.c
@@ -771,7 +771,7 @@ static int d3323aa_probe(struct platform_device *pdev)
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(dev), indio_dev);
if (ret)
- return dev_err_probe(dev, ret, "Could not request IRQ\n");
+ return ret;
ret = d3323aa_setup(indio_dev, D3323AA_LP_FILTER_FREQ_DEFAULT_IDX,
D3323AA_FILTER_GAIN_DEFAULT_IDX,
diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c
index a6ff7cbe9e65..0199a6351a46 100644
--- a/drivers/iio/proximity/hx9023s.c
+++ b/drivers/iio/proximity/hx9023s.c
@@ -1139,7 +1139,7 @@ static int hx9023s_probe(struct i2c_client *client)
IRQF_ONESHOT,
"hx9023s_event", indio_dev);
if (ret)
- return dev_err_probe(dev, ret, "irq request failed\n");
+ return ret;
data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name,
diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c
index 65af31d43453..b3db7f3a49f6 100644
--- a/drivers/iio/proximity/irsd200.c
+++ b/drivers/iio/proximity/irsd200.c
@@ -910,7 +910,7 @@ static int irsd200_probe(struct i2c_client *client)
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
NULL, indio_dev);
if (ret)
- return dev_err_probe(data->dev, ret, "Could not request irq\n");
+ return ret;
trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name,
iio_device_id(indio_dev));
diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c
index eab881b0cdc7..accfc296e1f0 100644
--- a/drivers/iio/proximity/mb1232.c
+++ b/drivers/iio/proximity/mb1232.c
@@ -213,10 +213,8 @@ static int mb1232_probe(struct i2c_client *client)
if (data->irqnr > 0) {
ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq,
IRQF_TRIGGER_FALLING, id->name, indio_dev);
- if (ret < 0) {
- dev_err(dev, "request_irq: %d\n", ret);
+ if (ret)
return ret;
- }
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c
index e03f3f0dee04..4e042214df29 100644
--- a/drivers/iio/proximity/srf04.c
+++ b/drivers/iio/proximity/srf04.c
@@ -303,10 +303,8 @@ static int srf04_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
pdev->name, indio_dev);
- if (ret < 0) {
- dev_err(data->dev, "request_irq: %d\n", ret);
+ if (ret)
return ret;
- }
platform_set_drvdata(pdev, indio_dev);
diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c
index 59b35e40739b..4d4937395d9a 100644
--- a/drivers/iio/proximity/sx_common.c
+++ b/drivers/iio/proximity/sx_common.c
@@ -517,7 +517,7 @@ int sx_common_probe(struct i2c_client *client,
IRQF_ONESHOT,
"sx_event", indio_dev);
if (ret)
- return dev_err_probe(dev, ret, "No IRQ\n");
+ return ret;
data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name,
diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c
index 7f417372566a..6b0d7a9038d9 100644
--- a/drivers/iio/proximity/vcnl3020.c
+++ b/drivers/iio/proximity/vcnl3020.c
@@ -638,12 +638,8 @@ static int vcnl3020_probe(struct i2c_client *client)
NULL, vcnl3020_handle_irq_thread,
IRQF_ONESHOT, indio_dev->name,
indio_dev);
- if (rc) {
- dev_err(&client->dev,
- "Error (%d) irq request failed (%u)\n", rc,
- client->irq);
+ if (rc)
return rc;
- }
}
return devm_iio_device_register(&client->dev, indio_dev);
diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c
index b7127c3c52d2..9fe14ceb8be7 100644
--- a/drivers/iio/proximity/vl53l0x-i2c.c
+++ b/drivers/iio/proximity/vl53l0x-i2c.c
@@ -127,10 +127,8 @@ static int vl53l0x_configure_irq(struct i2c_client *client,
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, vl53l0x_threaded_irq,
irq_flags | IRQF_ONESHOT, indio_dev->name, indio_dev);
- if (ret) {
- dev_err(&client->dev, "devm_request_irq error: %d\n", ret);
+ if (ret)
return ret;
- }
ret = i2c_smbus_write_byte_data(data->client,
VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO,
diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c
index dc4ffbc95d1f..bce238d1b6de 100644
--- a/drivers/iio/proximity/vl53l1x-i2c.c
+++ b/drivers/iio/proximity/vl53l1x-i2c.c
@@ -2,7 +2,7 @@
/*
* Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus.
*
- * Copyright (C) 2026 Siratul Islam <email@sirat.me>
+ * Copyright (C) 2026 Siratul Islam <siratul.islam@linux.dev>
*
* Datasheet available at
* <https://www.st.com/resource/en/datasheet/vl53l1x.pdf>
@@ -42,6 +42,7 @@
#define VL53L1X_REG_SOFT_RESET 0x0000
#define VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008
#define VL53L1X_REG_VHV_CONFIG__INIT 0x000B
+#define VL53L1X_REG_DEFAULT_CONFIG 0x002D
#define VL53L1X_REG_GPIO_HV_MUX__CTRL 0x0030
#define VL53L1X_REG_GPIO__TIO_HV_STATUS 0x0031
#define VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046
@@ -63,7 +64,6 @@
#define VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL 0x00DE
#define VL53L1X_REG_FIRMWARE__SYSTEM_STATUS 0x00E5
#define VL53L1X_REG_IDENTIFICATION__MODEL_ID 0x010F
-#define VL53L1X_REG_DEFAULT_CONFIG 0x002D
#define VL53L1X_MODEL_ID_VAL 0xEACC
@@ -81,6 +81,9 @@
#define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0)
+#define VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED BIT(0)
+#define VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY BIT(0)
+
/* Inter-measurement period uses PLL divider with 1.075 oscillator correction */
static const struct u32_fract vl53l1x_osc_correction = {
.numerator = 1075,
@@ -190,6 +193,17 @@ static int vl53l1x_stop_ranging(struct vl53l1x_data *data)
VL53L1X_MODE_START_STOP);
}
+static int vl53l1x_wait_data_ready(struct vl53l1x_data *data)
+{
+ unsigned int val;
+
+ /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */
+ return regmap_read_poll_timeout(data->regmap,
+ VL53L1X_REG_GPIO__TIO_HV_STATUS, val,
+ (val & VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY) != data->gpio_polarity,
+ 1 * USEC_PER_MSEC, 1 * USEC_PER_SEC);
+}
+
/*
* Default configuration blob from ST's VL53L1X Ultra Lite Driver
* (STSW-IMG009).
@@ -229,10 +243,9 @@ static int vl53l1x_chip_init(struct vl53l1x_data *data)
}
ret = regmap_read_poll_timeout(data->regmap,
- VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, val,
- val & BIT(0),
- 1 * USEC_PER_MSEC,
- 100 * USEC_PER_MSEC);
+ VL53L1X_REG_FIRMWARE__SYSTEM_STATUS,
+ val, val & VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED,
+ 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
if (ret)
return dev_err_probe(dev, ret, "firmware boot timeout\n");
@@ -260,12 +273,7 @@ static int vl53l1x_chip_init(struct vl53l1x_data *data)
if (ret)
return ret;
- /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */
- ret = regmap_read_poll_timeout(data->regmap,
- VL53L1X_REG_GPIO__TIO_HV_STATUS, val,
- (val & 1) != data->gpio_polarity,
- 1 * USEC_PER_MSEC,
- 1000 * USEC_PER_MSEC);
+ ret = vl53l1x_wait_data_ready(data);
if (ret)
return ret;
@@ -460,14 +468,7 @@ static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val)
if (!wait_for_completion_timeout(&data->completion, HZ))
return -ETIMEDOUT;
} else {
- unsigned int rdy;
-
- /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */
- ret = regmap_read_poll_timeout(data->regmap,
- VL53L1X_REG_GPIO__TIO_HV_STATUS, rdy,
- (rdy & 1) != data->gpio_polarity,
- 1 * USEC_PER_MSEC,
- 1000 * USEC_PER_MSEC);
+ ret = vl53l1x_wait_data_ready(data);
if (ret)
return ret;
}
@@ -750,6 +751,6 @@ static struct i2c_driver vl53l1x_driver = {
};
module_i2c_driver(vl53l1x_driver);
-MODULE_AUTHOR("Siratul Islam <email@sirat.me>");
+MODULE_AUTHOR("Siratul Islam <siratul.islam@linux.dev>");
MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c
index 55bcbbd4021a..27ecc434eee9 100644
--- a/drivers/iio/resolver/ad2s1200.c
+++ b/drivers/iio/resolver/ad2s1200.c
@@ -143,18 +143,14 @@ static int ad2s1200_probe(struct spi_device *spi)
st->sdev = spi;
st->sample = devm_gpiod_get(&spi->dev, "adi,sample", GPIOD_OUT_LOW);
- if (IS_ERR(st->sample)) {
- dev_err(&spi->dev, "Failed to claim SAMPLE gpio: err=%ld\n",
- PTR_ERR(st->sample));
- return PTR_ERR(st->sample);
- }
+ if (IS_ERR(st->sample))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->sample),
+ "Failed to claim SAMPLE gpio\n");
st->rdvel = devm_gpiod_get(&spi->dev, "adi,rdvel", GPIOD_OUT_LOW);
- if (IS_ERR(st->rdvel)) {
- dev_err(&spi->dev, "Failed to claim RDVEL gpio: err=%ld\n",
- PTR_ERR(st->rdvel));
- return PTR_ERR(st->rdvel);
- }
+ if (IS_ERR(st->rdvel))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->rdvel),
+ "Failed to claim RDVEL gpio\n");
indio_dev->info = &ad2s1200_info;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -182,8 +178,8 @@ static const struct of_device_id ad2s1200_of_match[] = {
MODULE_DEVICE_TABLE(of, ad2s1200_of_match);
static const struct spi_device_id ad2s1200_id[] = {
- { "ad2s1200" },
- { "ad2s1205" },
+ { .name = "ad2s1200" },
+ { .name = "ad2s1205" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad2s1200_id);
diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c
index 1be19fe8aa3f..3b5ec21e3446 100644
--- a/drivers/iio/resolver/ad2s1210.c
+++ b/drivers/iio/resolver/ad2s1210.c
@@ -1276,10 +1276,8 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev,
return regmap_write(st->regmap, reg, writeval);
}
-static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
+static void ad2s1210_scan_to_buffers(struct iio_dev *indio_dev, s64 timestamp)
{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
struct ad2s1210_state *st = iio_priv(indio_dev);
size_t chan = 0;
int ret;
@@ -1295,15 +1293,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
AD2S1210_REG_POSITION_MSB,
&st->sample.raw, 2);
if (ret < 0)
- goto error_ret;
+ return;
} else {
ret = ad2s1210_set_mode(st, MOD_POS);
if (ret < 0)
- goto error_ret;
+ return;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
- goto error_ret;
+ return;
}
memcpy(&st->scan.chan[chan++], &st->sample.raw, 2);
@@ -1315,15 +1313,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
AD2S1210_REG_VELOCITY_MSB,
&st->sample.raw, 2);
if (ret < 0)
- goto error_ret;
+ return;
} else {
ret = ad2s1210_set_mode(st, MOD_VEL);
if (ret < 0)
- goto error_ret;
+ return;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
- goto error_ret;
+ return;
}
memcpy(&st->scan.chan[chan++], &st->sample.raw, 2);
@@ -1334,16 +1332,22 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &reg_val);
if (ret < 0)
- goto error_ret;
+ return;
st->sample.fault = reg_val;
}
- ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp);
+ ad2s1210_push_events(indio_dev, st->sample.fault, timestamp);
iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
- pf->timestamp);
+ timestamp);
+}
+
+static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
-error_ret:
+ ad2s1210_scan_to_buffers(indio_dev, pf->timestamp);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
@@ -1597,7 +1601,7 @@ static const struct of_device_id ad2s1210_of_match[] = {
MODULE_DEVICE_TABLE(of, ad2s1210_of_match);
static const struct spi_device_id ad2s1210_id[] = {
- { "ad2s1210" },
+ { .name = "ad2s1210" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad2s1210_id);
diff --git a/drivers/iio/resolver/ad2s90.c b/drivers/iio/resolver/ad2s90.c
index 18f1c905eeac..b7a2dcc42871 100644
--- a/drivers/iio/resolver/ad2s90.c
+++ b/drivers/iio/resolver/ad2s90.c
@@ -110,7 +110,7 @@ static const struct of_device_id ad2s90_of_match[] = {
MODULE_DEVICE_TABLE(of, ad2s90_of_match);
static const struct spi_device_id ad2s90_id[] = {
- { "ad2s90" },
+ { .name = "ad2s90" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad2s90_id);
diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c
index 58d4c0273932..5e3262e461f4 100644
--- a/drivers/iio/temperature/hid-sensor-temperature.c
+++ b/drivers/iio/temperature/hid-sensor-temperature.c
@@ -44,7 +44,7 @@ static const struct iio_chan_spec temperature_channels[] = {
/* Adjust channel real bits based on report descriptor */
static void temperature_adjust_channel_bit_mask(struct iio_chan_spec *channels,
- int channel, int size)
+ int channel, int size)
{
channels[channel].scan_type.sign = 's';
/* Real storage bits will change based on the report desc. */
@@ -100,8 +100,8 @@ static int temperature_read_raw(struct iio_dev *indio_dev,
}
static int temperature_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct temperature_state *temp_st = iio_priv(indio_dev);
@@ -124,7 +124,7 @@ static const struct iio_info temperature_info = {
/* Callback handler to send event after all samples are received and captured */
static int temperature_proc_event(struct hid_sensor_hub_device *hsdev,
- unsigned int usage_id, void *pdev)
+ u32 usage_id, void *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct temperature_state *temp_st = iio_priv(indio_dev);
@@ -139,8 +139,9 @@ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev,
/* Capture samples in local storage */
static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev,
- unsigned int usage_id, size_t raw_len,
- char *raw_data, void *pdev)
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct temperature_state *temp_st = iio_priv(indio_dev);
@@ -156,10 +157,10 @@ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id*/
static int temperature_parse_report(struct platform_device *pdev,
- struct hid_sensor_hub_device *hsdev,
- struct iio_chan_spec *channels,
- unsigned int usage_id,
- struct temperature_state *st)
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ u32 usage_id,
+ struct temperature_state *st)
{
int ret;
@@ -170,8 +171,7 @@ static int temperature_parse_report(struct platform_device *pdev,
if (ret < 0)
return ret;
- temperature_adjust_channel_bit_mask(channels, 0,
- st->temperature_attr.size);
+ temperature_adjust_channel_bit_mask(channels, 0, st->temperature_attr.size);
st->scale_precision = hid_sensor_format_scale(
HID_USAGE_SENSOR_TEMPERATURE,
@@ -212,13 +212,13 @@ static int hid_temperature_probe(struct platform_device *pdev)
if (ret)
return ret;
- temp_chans = devm_kmemdup(&indio_dev->dev, temperature_channels,
- sizeof(temperature_channels), GFP_KERNEL);
+ temp_chans = devm_kmemdup(&pdev->dev, temperature_channels,
+ sizeof(temperature_channels), GFP_KERNEL);
if (!temp_chans)
return -ENOMEM;
ret = temperature_parse_report(pdev, hsdev, temp_chans,
- HID_USAGE_SENSOR_TEMPERATURE, temp_st);
+ HID_USAGE_SENSOR_TEMPERATURE, temp_st);
if (ret)
return ret;
@@ -231,7 +231,7 @@ static int hid_temperature_probe(struct platform_device *pdev)
atomic_set(&temp_st->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
- &temp_st->common_attributes);
+ &temp_st->common_attributes);
if (ret)
return ret;
@@ -239,7 +239,7 @@ static int hid_temperature_probe(struct platform_device *pdev)
temperature_callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE,
- &temperature_callbacks);
+ &temperature_callbacks);
if (ret)
goto error_remove_trigger;
diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c
index f8c4917bb118..6efb5252a773 100644
--- a/drivers/iio/temperature/ltc2983.c
+++ b/drivers/iio/temperature/ltc2983.c
@@ -1994,7 +1994,7 @@ static int ltc2983_probe(struct spi_device *spi)
ret = devm_request_irq(dev, spi->irq, ltc2983_irq_handler,
IRQF_TRIGGER_RISING, st->info->name, st);
if (ret)
- return dev_err_probe(dev, ret, "failed to request an irq\n");
+ return ret;
if (st->info->has_eeprom) {
ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_WRITE_CMD,
@@ -2070,11 +2070,11 @@ static const struct ltc2983_chip_info ltm2985_chip_info_data = {
};
static const struct spi_device_id ltc2983_id_table[] = {
- { "adt7604", (kernel_ulong_t)&adt7604_chip_info_data },
- { "ltc2983", (kernel_ulong_t)&ltc2983_chip_info_data },
- { "ltc2984", (kernel_ulong_t)&ltc2984_chip_info_data },
- { "ltc2986", (kernel_ulong_t)&ltc2986_chip_info_data },
- { "ltm2985", (kernel_ulong_t)&ltm2985_chip_info_data },
+ { .name = "adt7604", .driver_data = (kernel_ulong_t)&adt7604_chip_info_data },
+ { .name = "ltc2983", .driver_data = (kernel_ulong_t)&ltc2983_chip_info_data },
+ { .name = "ltc2984", .driver_data = (kernel_ulong_t)&ltc2984_chip_info_data },
+ { .name = "ltc2986", .driver_data = (kernel_ulong_t)&ltc2986_chip_info_data },
+ { .name = "ltm2985", .driver_data = (kernel_ulong_t)&ltm2985_chip_info_data },
{ }
};
MODULE_DEVICE_TABLE(spi, ltc2983_id_table);
diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c
index 13405f3ba829..061f6fe6de5e 100644
--- a/drivers/iio/temperature/max31856.c
+++ b/drivers/iio/temperature/max31856.c
@@ -461,7 +461,7 @@ static int max31856_probe(struct spi_device *spi)
}
static const struct spi_device_id max31856_id[] = {
- { "max31856", 0 },
+ { .name = "max31856" },
{ }
};
MODULE_DEVICE_TABLE(spi, max31856_id);
diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c
index 82cf06c4d272..f9c93f95d80b 100644
--- a/drivers/iio/temperature/max31865.c
+++ b/drivers/iio/temperature/max31865.c
@@ -324,7 +324,7 @@ static int max31865_probe(struct spi_device *spi)
}
static const struct spi_device_id max31865_id[] = {
- { "max31865", 0 },
+ { .name = "max31865" },
{ }
};
MODULE_DEVICE_TABLE(spi, max31865_id);
diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c
index 015afeab1914..5f216e238d55 100644
--- a/drivers/iio/temperature/maxim_thermocouple.c
+++ b/drivers/iio/temperature/maxim_thermocouple.c
@@ -262,15 +262,15 @@ static int maxim_thermocouple_probe(struct spi_device *spi)
}
static const struct spi_device_id maxim_thermocouple_id[] = {
- {"max6675", MAX6675},
- {"max31855", MAX31855},
- {"max31855k", MAX31855K},
- {"max31855j", MAX31855J},
- {"max31855n", MAX31855N},
- {"max31855s", MAX31855S},
- {"max31855t", MAX31855T},
- {"max31855e", MAX31855E},
- {"max31855r", MAX31855R},
+ { .name = "max6675", .driver_data = MAX6675 },
+ { .name = "max31855", .driver_data = MAX31855 },
+ { .name = "max31855k", .driver_data = MAX31855K },
+ { .name = "max31855j", .driver_data = MAX31855J },
+ { .name = "max31855n", .driver_data = MAX31855N },
+ { .name = "max31855s", .driver_data = MAX31855S },
+ { .name = "max31855t", .driver_data = MAX31855T },
+ { .name = "max31855e", .driver_data = MAX31855E },
+ { .name = "max31855r", .driver_data = MAX31855R },
{ }
};
MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c
index 2f6ff87d2a37..4e743424f88e 100644
--- a/drivers/iio/temperature/tmp007.c
+++ b/drivers/iio/temperature/tmp007.c
@@ -527,10 +527,8 @@ static int tmp007_probe(struct i2c_client *client)
NULL, tmp007_interrupt_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
tmp007_id->name, indio_dev);
- if (ret) {
- dev_err(&client->dev, "irq request error %d\n", -ret);
+ if (ret)
return ret;
- }
}
return devm_iio_device_register(&client->dev, indio_dev);
diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c
index 6bc18616ad15..74cb8d62bef3 100644
--- a/drivers/iio/temperature/tmp117.c
+++ b/drivers/iio/temperature/tmp117.c
@@ -9,6 +9,7 @@
* Note: This driver assumes that the sensor has been calibrated beforehand.
*/
+#include <linux/array_size.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
@@ -38,6 +39,7 @@
#define TMP116_DEVICE_ID 0x1116
#define TMP117_DEVICE_ID 0x0117
+#define TMP119_DEVICE_ID 0x2117
struct tmp117_data {
struct i2c_client *client;
@@ -139,6 +141,12 @@ static const struct tmp11x_info tmp117_channels_info = {
.num_channels = ARRAY_SIZE(tmp117_channels)
};
+static const struct tmp11x_info tmp119_channels_info = {
+ .name = "tmp119",
+ .channels = tmp117_channels,
+ .num_channels = ARRAY_SIZE(tmp117_channels)
+};
+
static const struct iio_info tmp117_info = {
.read_raw = tmp117_read_raw,
.write_raw = tmp117_write_raw,
@@ -172,6 +180,9 @@ static int tmp117_probe(struct i2c_client *client)
case TMP117_DEVICE_ID:
match_data = &tmp117_channels_info;
break;
+ case TMP119_DEVICE_ID:
+ match_data = &tmp119_channels_info;
+ break;
default:
dev_info(&client->dev,
"Unknown device id (0x%x), use fallback compatible\n",
@@ -204,6 +215,7 @@ static int tmp117_probe(struct i2c_client *client)
static const struct of_device_id tmp117_of_match[] = {
{ .compatible = "ti,tmp116", .data = &tmp116_channels_info },
{ .compatible = "ti,tmp117", .data = &tmp117_channels_info },
+ { .compatible = "ti,tmp119", .data = &tmp119_channels_info },
{ }
};
MODULE_DEVICE_TABLE(of, tmp117_of_match);
@@ -211,6 +223,7 @@ MODULE_DEVICE_TABLE(of, tmp117_of_match);
static const struct i2c_device_id tmp117_id[] = {
{ .name = "tmp116", .driver_data = (kernel_ulong_t)&tmp116_channels_info },
{ .name = "tmp117", .driver_data = (kernel_ulong_t)&tmp117_channels_info },
+ { .name = "tmp119", .driver_data = (kernel_ulong_t)&tmp119_channels_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp117_id);
diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c
index 872dd8582003..e5d0a2ac4670 100644
--- a/drivers/iio/test/iio-test-format.c
+++ b/drivers/iio/test/iio-test-format.c
@@ -200,56 +200,108 @@ static void iio_test_iio_format_value_multiple(struct kunit *test)
static void iio_test_iio_format_value_integer_64(struct kunit *test)
{
int values[2];
- s64 value;
char *buf;
int ret;
buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
- value = 24;
- values[0] = lower_32_bits(value);
- values[1] = upper_32_bits(value);
+ iio_val_s64_decompose(24, &values[0], &values[1]);
ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "24\n");
- value = -24;
- values[0] = lower_32_bits(value);
- values[1] = upper_32_bits(value);
+ iio_val_s64_decompose(-24, &values[0], &values[1]);
ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-24\n");
- value = 0;
- values[0] = lower_32_bits(value);
- values[1] = upper_32_bits(value);
+ iio_val_s64_decompose(0, &values[0], &values[1]);
ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n");
- value = UINT_MAX;
- values[0] = lower_32_bits(value);
- values[1] = upper_32_bits(value);
+ iio_val_s64_decompose(UINT_MAX, &values[0], &values[1]);
ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "4294967295\n");
- value = -((s64)UINT_MAX);
- values[0] = lower_32_bits(value);
- values[1] = upper_32_bits(value);
+ iio_val_s64_decompose(-((s64)UINT_MAX), &values[0], &values[1]);
ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-4294967295\n");
- value = LLONG_MAX;
- values[0] = lower_32_bits(value);
- values[1] = upper_32_bits(value);
+ iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]);
ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854775807\n");
- value = LLONG_MIN;
- values[0] = lower_32_bits(value);
- values[1] = upper_32_bits(value);
+ iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]);
ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values);
IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854775808\n");
}
+static void iio_test_iio_format_value_decimal_64(struct kunit *test)
+{
+ int values[2];
+ char *buf;
+ int ret;
+
+ buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
+
+ /* DECIMAL64_MILLI: positive >= 1, value 1.234 */
+ iio_val_s64_decompose(1234, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.234\n");
+
+ /* DECIMAL64_MICRO: positive >= 1, value 3.141592 */
+ iio_val_s64_decompose(3141592, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "3.141592\n");
+
+ /* DECIMAL64_MILLI: positive < 1, value 0.042 */
+ iio_val_s64_decompose(42, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.042\n");
+
+ /* DECIMAL64_MILLI: negative <= -1, value -1.234 */
+ iio_val_s64_decompose(-1234, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.234\n");
+
+ /* DECIMAL64_MILLI: negative > -1, value -0.123 */
+ iio_val_s64_decompose(-123, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.123\n");
+
+ /* DECIMAL64_MILLI: zero */
+ iio_val_s64_decompose(0, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000\n");
+
+ /* DECIMAL64_NANO: value 1.000000001 */
+ iio_val_s64_decompose(1000000001, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000001\n");
+
+ /* DECIMAL64_MICRO: large value using upper 32 bits */
+ iio_val_s64_decompose(5000000000000042LL, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "5000000000.000042\n");
+
+ /* limits */
+ iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372.036854775807\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036.854775807\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854.775807\n");
+
+ iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]);
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372.036854775808\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036.854775808\n");
+ ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values);
+ IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854.775808\n");
+}
+
static struct kunit_case iio_format_test_cases[] = {
KUNIT_CASE(iio_test_iio_format_value_integer),
KUNIT_CASE(iio_test_iio_format_value_fixedpoint),
@@ -257,6 +309,7 @@ static struct kunit_case iio_format_test_cases[] = {
KUNIT_CASE(iio_test_iio_format_value_fractional_log2),
KUNIT_CASE(iio_test_iio_format_value_multiple),
KUNIT_CASE(iio_test_iio_format_value_integer_64),
+ KUNIT_CASE(iio_test_iio_format_value_decimal_64),
{ }
};
diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c
index ac6942cf1e44..bcd4a607be19 100644
--- a/drivers/iio/test/iio-test-rescale.c
+++ b/drivers/iio/test/iio-test-rescale.c
@@ -575,7 +575,7 @@ static const struct rescale_tc_data offset_cases[] = {
static void case_to_desc(const struct rescale_tc_data *t, char *desc)
{
- strcpy(desc, t->name);
+ strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(iio_rescale_scale, scale_cases, case_to_desc);
diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c
index ff44f9978b71..4fa42f88bcfa 100644
--- a/drivers/input/joystick/adc-joystick.c
+++ b/drivers/input/joystick/adc-joystick.c
@@ -329,3 +329,4 @@ module_platform_driver(adc_joystick_driver);
MODULE_DESCRIPTION("Input driver for joysticks connected over ADC");
MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c
index f1753207429d..d687459a0c80 100644
--- a/drivers/input/keyboard/adc-keys.c
+++ b/drivers/input/keyboard/adc-keys.c
@@ -202,3 +202,4 @@ module_platform_driver(adc_keys_driver);
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c
index 98d5b2ba63fb..89c4d7b2b89e 100644
--- a/drivers/input/touchscreen/colibri-vf50-ts.c
+++ b/drivers/input/touchscreen/colibri-vf50-ts.c
@@ -372,3 +372,4 @@ module_platform_driver(vf50_touch_driver);
MODULE_AUTHOR("Sanchayan Maity");
MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/input/touchscreen/resistive-adc-touch.c b/drivers/input/touchscreen/resistive-adc-touch.c
index 68f7e2e28f37..c89edc1280fe 100644
--- a/drivers/input/touchscreen/resistive-adc-touch.c
+++ b/drivers/input/touchscreen/resistive-adc-touch.c
@@ -300,3 +300,4 @@ module_platform_driver(grts_driver);
MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index 73a9db2c7537..4aa991a54101 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -548,7 +548,7 @@ struct icc_path *of_icc_get_by_index(struct device *dev, int idx)
path->name = kasprintf(GFP_KERNEL, "%s-%s",
src_data->node->name, dst_data->node->name);
if (!path->name) {
- kfree(path);
+ icc_put(path);
path = ERR_PTR(-ENOMEM);
}
@@ -646,8 +646,9 @@ struct icc_path *icc_get(struct device *dev, const char *src, const char *dst)
path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name);
if (!path->name) {
- kfree(path);
- path = ERR_PTR(-ENOMEM);
+ mutex_unlock(&icc_lock);
+ icc_put(path);
+ return ERR_PTR(-ENOMEM);
}
out:
mutex_unlock(&icc_lock);
diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c
index 08df9188ef94..91f86d9237a6 100644
--- a/drivers/interconnect/debugfs-client.c
+++ b/drivers/interconnect/debugfs-client.c
@@ -142,6 +142,8 @@ int icc_debugfs_client_init(struct dentry *icc_dir)
int ret;
pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE);
+ if (!pdev)
+ return -ENOMEM;
ret = platform_device_add(pdev);
if (ret) {
diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
index 5b8a094ca4ed..b3187acf90b8 100644
--- a/drivers/interconnect/qcom/Kconfig
+++ b/drivers/interconnect/qcom/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config INTERCONNECT_QCOM
tristate "Qualcomm Network-on-Chip interconnect drivers"
- depends on ARCH_QCOM
+ depends on ARCH_QCOM || COMPILE_TEST
help
Support for Qualcomm's Network-on-Chip interconnect hardware.
@@ -47,6 +47,16 @@ config INTERCONNECT_QCOM_KAANAPALI
This is a driver for the Qualcomm Network-on-Chip on kaanapali-based
platforms.
+config INTERCONNECT_QCOM_MAILI
+ tristate "Qualcomm MAILI interconnect driver"
+ depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
+ select INTERCONNECT_QCOM_RPMH
+ select INTERCONNECT_QCOM_BCM_VOTER
+ default ARCH_QCOM
+ help
+ This is a driver for the Qualcomm Network-on-Chip on maili-based
+ platforms.
+
config INTERCONNECT_QCOM_MSM8909
tristate "Qualcomm MSM8909 interconnect driver"
depends on INTERCONNECT_QCOM
diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
index 1c7d410b40cc..5f139a370bbe 100644
--- a/drivers/interconnect/qcom/Makefile
+++ b/drivers/interconnect/qcom/Makefile
@@ -8,6 +8,7 @@ qnoc-eliza-objs := eliza.o
qnoc-glymur-objs := glymur.o
qnoc-hawi-objs := hawi.o
qnoc-kaanapali-objs := kaanapali.o
+qnoc-maili-objs := maili.o
qnoc-milos-objs := milos.o
qnoc-msm8909-objs := msm8909.o
qnoc-msm8916-objs := msm8916.o
@@ -56,6 +57,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o
obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o
obj-$(CONFIG_INTERCONNECT_QCOM_HAWI) += qnoc-hawi.o
obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o
+obj-$(CONFIG_INTERCONNECT_QCOM_MAILI) += qnoc-maili.o
obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o
diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c
index 23a1d116e79a..ca4c8a944755 100644
--- a/drivers/interconnect/qcom/icc-rpm.c
+++ b/drivers/interconnect/qcom/icc-rpm.c
@@ -479,13 +479,11 @@ int qnoc_probe(struct platform_device *pdev)
cd_num = 0;
}
- qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
+ qp = devm_kzalloc(dev, struct_size(qp, intf_clks, cd_num), GFP_KERNEL);
if (!qp)
return -ENOMEM;
- qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL);
- if (!qp->intf_clks)
- return -ENOMEM;
+ qp->num_intf_clks = cd_num;
if (desc->bus_clk_desc) {
qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc),
@@ -507,7 +505,6 @@ int qnoc_probe(struct platform_device *pdev)
return -ENOMEM;
data->num_nodes = num_nodes;
- qp->num_intf_clks = cd_num;
for (i = 0; i < cd_num; i++)
qp->intf_clks[i].id = cds[i];
diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h
index 7d1cb2efa9ee..cdd5f444b152 100644
--- a/drivers/interconnect/qcom/icc-rpm.h
+++ b/drivers/interconnect/qcom/icc-rpm.h
@@ -40,7 +40,6 @@ struct rpm_clk_resource {
/**
* struct qcom_icc_provider - Qualcomm specific interconnect provider
* @provider: generic interconnect provider
- * @num_intf_clks: the total number of intf_clks clk_bulk_data entries
* @type: the ICC provider type
* @regmap: regmap for QoS registers read/write access
* @qos_offset: offset to QoS registers
@@ -49,13 +48,13 @@ struct rpm_clk_resource {
* @bus_clk_rate: bus clock rate in Hz
* @bus_clk_desc: a pointer to a rpm_clk_resource description of bus clocks
* @bus_clk: a pointer to a HLOS-owned bus clock
- * @intf_clks: a clk_bulk_data array of interface clocks
* @keep_alive: whether to always keep a minimum vote on the bus clocks
* @ignore_enxio: whether to ignore ENXIO errors (for MSM8974)
+ * @num_intf_clks: the total number of intf_clks clk_bulk_data entries
+ * @intf_clks: a clk_bulk_data array of interface clocks
*/
struct qcom_icc_provider {
struct icc_provider provider;
- int num_intf_clks;
enum qcom_icc_type type;
struct regmap *regmap;
unsigned int qos_offset;
@@ -64,9 +63,10 @@ struct qcom_icc_provider {
u32 bus_clk_rate[QCOM_SMD_RPM_STATE_NUM];
const struct rpm_clk_resource *bus_clk_desc;
struct clk *bus_clk;
- struct clk_bulk_data *intf_clks;
bool keep_alive;
bool ignore_enxio;
+ int num_intf_clks;
+ struct clk_bulk_data intf_clks[] __counted_by(num_intf_clks);
};
/**
diff --git a/drivers/interconnect/qcom/maili.c b/drivers/interconnect/qcom/maili.c
new file mode 100644
index 000000000000..e5eed7f998f9
--- /dev/null
+++ b/drivers/interconnect/qcom/maili.c
@@ -0,0 +1,2091 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/interconnect.h>
+#include <linux/interconnect-provider.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <dt-bindings/interconnect/qcom,maili-rpmh.h>
+
+#include "bcm-voter.h"
+#include "icc-rpmh.h"
+
+static struct qcom_icc_node ddr_eff_veto_slave = {
+ .name = "ddr_eff_veto_slave",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qup0_core_slave = {
+ .name = "qup0_core_slave",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qup1_core_slave = {
+ .name = "qup1_core_slave",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qup2_core_slave = {
+ .name = "qup2_core_slave",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qup3_core_slave = {
+ .name = "qup3_core_slave",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qup4_core_slave = {
+ .name = "qup4_core_slave",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qup5_core_slave = {
+ .name = "qup5_core_slave",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_aoss = {
+ .name = "qhs_aoss",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_ipa = {
+ .name = "qhs_ipa",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_ipc_router_fence = {
+ .name = "qhs_ipc_router_fence",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_soccp = {
+ .name = "qhs_soccp",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_tme_cfg = {
+ .name = "qhs_tme_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qss_ddrss_cfg = {
+ .name = "qss_ddrss_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qxs_imem = {
+ .name = "qxs_imem",
+ .channels = 1,
+ .buswidth = 8,
+};
+
+static struct qcom_icc_node xs_pcie = {
+ .name = "xs_pcie",
+ .channels = 1,
+ .buswidth = 8,
+};
+
+static struct qcom_icc_node xs_pcie_g4x1 = {
+ .name = "xs_pcie_g4x1",
+ .channels = 1,
+ .buswidth = 8,
+};
+
+static struct qcom_icc_node qhs_lpi_cc = {
+ .name = "qhs_lpi_cc",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qns_lb = {
+ .name = "qns_lb",
+ .channels = 4,
+ .buswidth = 16,
+};
+
+static struct qcom_icc_node srvc_llclpi_noc = {
+ .name = "srvc_llclpi_noc",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node srvc_llclpi_noc_chipcx = {
+ .name = "srvc_llclpi_noc_chipcx",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node ebi = {
+ .name = "ebi",
+ .channels = 4,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node ddr_rt_slave = {
+ .name = "ddr_rt_slave",
+ .channels = 4,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node srvc_pcie_aggre_noc = {
+ .name = "srvc_pcie_aggre_noc",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_ahb2phy0 = {
+ .name = "qhs_ahb2phy0",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_boot_rom = {
+ .name = "qhs_boot_rom",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_camera_cfg = {
+ .name = "qhs_camera_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_clk_ctl = {
+ .name = "qhs_clk_ctl",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_crypto_cfg = {
+ .name = "qhs_crypto_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_display_cfg = {
+ .name = "qhs_display_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_eva_cfg = {
+ .name = "qhs_eva_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_gpuss_cfg = {
+ .name = "qhs_gpuss_cfg",
+ .channels = 1,
+ .buswidth = 8,
+};
+
+static struct qcom_icc_node qhs_i2c = {
+ .name = "qhs_i2c",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_imem_cfg = {
+ .name = "qhs_imem_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_ipc_router = {
+ .name = "qhs_ipc_router",
+ .channels = 4,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_iris_cfg = {
+ .name = "qhs_iris_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_mss_cfg = {
+ .name = "qhs_mss_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_pcie_cfg = {
+ .name = "qhs_pcie_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_pcie_g4x1_cfg = {
+ .name = "qhs_pcie_g4x1_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_prng = {
+ .name = "qhs_prng",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_qspi = {
+ .name = "qhs_qspi",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_qup1 = {
+ .name = "qhs_qup1",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_qup2 = {
+ .name = "qhs_qup2",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_qup3 = {
+ .name = "qhs_qup3",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_qup4 = {
+ .name = "qhs_qup4",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_qup5 = {
+ .name = "qhs_qup5",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_sdc2 = {
+ .name = "qhs_sdc2",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_sdc4 = {
+ .name = "qhs_sdc4",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_tlmm = {
+ .name = "qhs_tlmm",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_ufs_mem_cfg = {
+ .name = "qhs_ufs_mem_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_usb3 = {
+ .name = "qhs_usb3",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qhs_vsense_ctrl_cfg = {
+ .name = "qhs_vsense_ctrl_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qss_qdss_cfg = {
+ .name = "qss_qdss_cfg",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qss_qdss_stm = {
+ .name = "qss_qdss_stm",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node qss_tcsr = {
+ .name = "qss_tcsr",
+ .channels = 1,
+ .buswidth = 4,
+};
+
+static struct qcom_icc_node xs_sys_tcu_cfg = {
+ .name = "xs_sys_tcu_cfg",
+ .channels = 1,
+ .buswidth = 8,
+};
+
+static struct qcom_icc_node ddr_eff_veto_master = {
+ .name = "ddr_eff_veto_master",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &ddr_eff_veto_slave },
+};
+
+static struct qcom_icc_node qup0_core_master = {
+ .name = "qup0_core_master",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qup0_core_slave },
+};
+
+static struct qcom_icc_node qup1_core_master = {
+ .name = "qup1_core_master",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qup1_core_slave },
+};
+
+static struct qcom_icc_node qup2_core_master = {
+ .name = "qup2_core_master",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qup2_core_slave },
+};
+
+static struct qcom_icc_node qup3_core_master = {
+ .name = "qup3_core_master",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qup3_core_slave },
+};
+
+static struct qcom_icc_node qup4_core_master = {
+ .name = "qup4_core_master",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qup4_core_slave },
+};
+
+static struct qcom_icc_node qup5_core_master = {
+ .name = "qup5_core_master",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qup5_core_slave },
+};
+
+static struct qcom_icc_node qnm_gemnoc_pcie = {
+ .name = "qnm_gemnoc_pcie",
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 2,
+ .link_nodes = { &xs_pcie, &xs_pcie_g4x1 },
+};
+
+static struct qcom_icc_node qnm_lpiaon_noc_llclpi_noc = {
+ .name = "qnm_lpiaon_noc_llclpi_noc",
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 4,
+ .link_nodes = { &qhs_lpi_cc, &qns_lb,
+ &srvc_llclpi_noc, &srvc_llclpi_noc_chipcx },
+};
+
+static struct qcom_icc_node llcc_mc = {
+ .name = "llcc_mc",
+ .channels = 4,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &ebi },
+};
+
+static struct qcom_icc_node ddr_rt_mc = {
+ .name = "ddr_rt_mc",
+ .channels = 4,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &ddr_rt_slave },
+};
+
+static struct qcom_icc_node qsm_pcie_anoc_cfg = {
+ .name = "qsm_pcie_anoc_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &srvc_pcie_aggre_noc },
+};
+
+static struct qcom_icc_node qsm_cfg_center = {
+ .name = "qsm_cfg_center",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 14,
+ .link_nodes = { &qhs_boot_rom, &qhs_clk_ctl,
+ &qhs_crypto_cfg, &qhs_gpuss_cfg,
+ &qhs_imem_cfg, &qhs_ipc_router,
+ &qhs_mss_cfg, &qhs_prng,
+ &qhs_tlmm, &qhs_vsense_ctrl_cfg,
+ &qss_qdss_cfg, &qss_qdss_stm,
+ &qss_tcsr, &xs_sys_tcu_cfg },
+};
+
+static struct qcom_icc_node qsm_cfg_east = {
+ .name = "qsm_cfg_east",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 2,
+ .link_nodes = { &qhs_qup2, &qhs_qup4 },
+};
+
+static struct qcom_icc_node qsm_cfg_mm_hf = {
+ .name = "qsm_cfg_mm_hf",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qhs_display_cfg },
+};
+
+static struct qcom_icc_node qsm_cfg_mm_sf = {
+ .name = "qsm_cfg_mm_sf",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 3,
+ .link_nodes = { &qhs_camera_cfg, &qhs_eva_cfg,
+ &qhs_iris_cfg },
+};
+
+static struct qcom_icc_node qsm_cfg_north = {
+ .name = "qsm_cfg_north",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 3,
+ .link_nodes = { &qhs_ahb2phy0, &qhs_ufs_mem_cfg,
+ &qhs_usb3 },
+};
+
+static struct qcom_icc_node qsm_cfg_west = {
+ .name = "qsm_cfg_west",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 2,
+ .link_nodes = { &qhs_qup1, &qhs_sdc2 },
+};
+
+static struct qcom_icc_node qns_llcc = {
+ .name = "qns_llcc",
+ .channels = 4,
+ .buswidth = 16,
+ .num_links = 1,
+ .link_nodes = { &llcc_mc },
+};
+
+static struct qcom_icc_node qns_pcie = {
+ .name = "qns_pcie",
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .link_nodes = { &qnm_gemnoc_pcie },
+};
+
+static struct qcom_icc_node qns_llc_lpinoc = {
+ .name = "qns_llc_lpinoc",
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .link_nodes = { &qnm_lpiaon_noc_llclpi_noc },
+};
+
+static struct qcom_icc_node qss_pcie_anoc_cfg = {
+ .name = "qss_pcie_anoc_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_pcie_anoc_cfg },
+};
+
+static struct qcom_icc_node qss_stdst_center_cfg = {
+ .name = "qss_stdst_center_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cfg_center },
+};
+
+static struct qcom_icc_node qss_stdst_east_cfg = {
+ .name = "qss_stdst_east_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cfg_east },
+};
+
+static struct qcom_icc_node qss_stdst_mm_hf_cfg = {
+ .name = "qss_stdst_mm_hf_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cfg_mm_hf },
+};
+
+static struct qcom_icc_node qss_stdst_mm_sf_cfg = {
+ .name = "qss_stdst_mm_sf_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cfg_mm_sf },
+};
+
+static struct qcom_icc_node qss_stdst_north_cfg = {
+ .name = "qss_stdst_north_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cfg_north },
+};
+
+static struct qcom_icc_node qss_stdst_west_cfg = {
+ .name = "qss_stdst_west_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cfg_west },
+};
+
+static struct qcom_icc_node alm_gic = {
+ .name = "alm_gic",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x14d000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_llcc },
+};
+
+static struct qcom_icc_node qnm_qpace = {
+ .name = "qnm_qpace",
+ .channels = 1,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x153000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_llcc },
+};
+
+static struct qcom_icc_node qsm_cfg_south = {
+ .name = "qsm_cfg_south",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 8,
+ .link_nodes = { &qhs_i2c, &qhs_pcie_cfg,
+ &qhs_pcie_g4x1_cfg, &qhs_qspi,
+ &qhs_qup3, &qhs_qup5,
+ &qhs_sdc4, &qss_pcie_anoc_cfg },
+};
+
+static struct qcom_icc_node qss_stdst_south_cfg = {
+ .name = "qss_stdst_south_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cfg_south },
+};
+
+static struct qcom_icc_node qsm_cnoc_main = {
+ .name = "qsm_cnoc_main",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 7,
+ .link_nodes = { &qss_stdst_center_cfg, &qss_stdst_east_cfg,
+ &qss_stdst_mm_hf_cfg, &qss_stdst_mm_sf_cfg,
+ &qss_stdst_north_cfg, &qss_stdst_south_cfg,
+ &qss_stdst_west_cfg },
+};
+
+static struct qcom_icc_node qss_cfg = {
+ .name = "qss_cfg",
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .link_nodes = { &qsm_cnoc_main },
+};
+
+static struct qcom_icc_node qnm_gemnoc_cnoc = {
+ .name = "qnm_gemnoc_cnoc",
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 8,
+ .link_nodes = { &qhs_aoss, &qhs_ipa,
+ &qhs_ipc_router_fence, &qhs_soccp,
+ &qhs_tme_cfg, &qss_cfg,
+ &qss_ddrss_cfg, &qxs_imem },
+};
+
+static struct qcom_icc_node qns_gem_noc_cnoc = {
+ .name = "qns_gem_noc_cnoc",
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .link_nodes = { &qnm_gemnoc_cnoc },
+};
+
+static struct qcom_icc_node alm_gpu_tcu = {
+ .name = "alm_gpu_tcu",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x145000 },
+ .prio = 1,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 2,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
+};
+
+static struct qcom_icc_node alm_sys_tcu = {
+ .name = "alm_sys_tcu",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x147000 },
+ .prio = 6,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 2,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
+};
+
+static struct qcom_icc_node chm_apps = {
+ .name = "chm_apps",
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_gpu = {
+ .name = "qnm_gpu",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x51000, 0xd1000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_lpass_gemnoc = {
+ .name = "qnm_lpass_gemnoc",
+ .channels = 1,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x149000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_mdsp = {
+ .name = "qnm_mdsp",
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_mnoc_hf = {
+ .name = "qnm_mnoc_hf",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x55000, 0xd5000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_mnoc_sf = {
+ .name = "qnm_mnoc_sf",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x57000, 0xd7000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_nsp_gemnoc = {
+ .name = "qnm_nsp_gemnoc",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x59000, 0xd9000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_pcie = {
+ .name = "qnm_pcie",
+ .channels = 1,
+ .buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x14b000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 2,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
+};
+
+static struct qcom_icc_node qnm_snoc_sf = {
+ .name = "qnm_snoc_sf",
+ .channels = 1,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x14f000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qnm_wlan_q6 = {
+ .name = "qnm_wlan_q6",
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 3,
+ .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = {
+ .name = "qns_lpass_ag_noc_gemnoc",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_lpass_gemnoc },
+};
+
+static struct qcom_icc_node qns_mem_noc_hf = {
+ .name = "qns_mem_noc_hf",
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_mnoc_hf },
+};
+
+static struct qcom_icc_node qns_mem_noc_sf = {
+ .name = "qns_mem_noc_sf",
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_mnoc_sf },
+};
+
+static struct qcom_icc_node qns_nsp_gemnoc = {
+ .name = "qns_nsp_gemnoc",
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_nsp_gemnoc },
+};
+
+static struct qcom_icc_node qns_pcie_gemnoc = {
+ .name = "qns_pcie_gemnoc",
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .link_nodes = { &qnm_pcie },
+};
+
+static struct qcom_icc_node qns_gemnoc_sf = {
+ .name = "qns_gemnoc_sf",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_snoc_sf },
+};
+
+static struct qcom_icc_node qnm_lpiaon_noc = {
+ .name = "qnm_lpiaon_noc",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qns_lpass_ag_noc_gemnoc },
+};
+
+static struct qcom_icc_node qnm_camnoc_hf = {
+ .name = "qnm_camnoc_hf",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x50000, 0x51000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_hf },
+};
+
+static struct qcom_icc_node qnm_mdp = {
+ .name = "qnm_mdp",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x52000, 0x53000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_hf },
+};
+
+static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = {
+ .name = "qnm_camnoc_nrt_icp_sf",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x20000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = {
+ .name = "qnm_camnoc_rt_cdm_sf",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x28000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_camnoc_sf = {
+ .name = "qnm_camnoc_sf",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x21000, 0x22000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_mdss_dcp = {
+ .name = "qnm_mdss_dcp",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x29000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_vapss_hcp = {
+ .name = "qnm_vapss_hcp",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_video_cv_cpu = {
+ .name = "qnm_video_cv_cpu",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x24000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_video_eva = {
+ .name = "qnm_video_eva",
+ .channels = 2,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x25000, 0x26000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_video_mvp = {
+ .name = "qnm_video_mvp",
+ .channels = 1,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x23000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_video_v_cpu = {
+ .name = "qnm_video_v_cpu",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x27000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_node qnm_nsp = {
+ .name = "qnm_nsp",
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qns_nsp_gemnoc },
+};
+
+static struct qcom_icc_node xm_pcie = {
+ .name = "xm_pcie",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xc000 },
+ .prio = 3,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_pcie_gemnoc },
+};
+
+static struct qcom_icc_node xm_pcie_g4x1 = {
+ .name = "xm_pcie_g4x1",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xd000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_pcie_gemnoc },
+};
+
+static struct qcom_icc_node qnm_aggre_noc = {
+ .name = "qnm_aggre_noc",
+ .channels = 1,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x20000 },
+ .prio = 2,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_gemnoc_sf },
+};
+
+static struct qcom_icc_node qnm_apss_noc = {
+ .name = "qnm_apss_noc",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x1e000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_gemnoc_sf },
+};
+
+static struct qcom_icc_node qnm_cnoc_data = {
+ .name = "qnm_cnoc_data",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x1f000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_gemnoc_sf },
+};
+
+static struct qcom_icc_node qns_a1noc_snoc = {
+ .name = "qns_a1noc_snoc",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_aggre_noc },
+};
+
+static struct qcom_icc_node qns_lpass_aggnoc = {
+ .name = "qns_lpass_aggnoc",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_lpiaon_noc },
+};
+
+static struct qcom_icc_node qhm_qspi = {
+ .name = "qhm_qspi",
+ .channels = 1,
+ .buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x49000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qhm_qup2 = {
+ .name = "qhm_qup2",
+ .channels = 1,
+ .buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x48000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qhm_qup3 = {
+ .name = "qhm_qup3",
+ .channels = 1,
+ .buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x46000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qhm_qup4 = {
+ .name = "qhm_qup4",
+ .channels = 1,
+ .buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x47000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qhm_qup5 = {
+ .name = "qhm_qup5",
+ .channels = 1,
+ .buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x4e000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qxm_crypto = {
+ .name = "qxm_crypto",
+ .channels = 1,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x40000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qxm_ipa = {
+ .name = "qxm_ipa",
+ .channels = 1,
+ .buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x41000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qxm_qup1 = {
+ .name = "qxm_qup1",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x4d000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qxm_soccp = {
+ .name = "qxm_soccp",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x45000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node xm_qdss_etr_0 = {
+ .name = "xm_qdss_etr_0",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x42000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node xm_qdss_etr_1 = {
+ .name = "xm_qdss_etr_1",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x43000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node xm_sdc2 = {
+ .name = "xm_sdc2",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x44000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node xm_sdc4 = {
+ .name = "xm_sdc4",
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node xm_ufs_mem = {
+ .name = "xm_ufs_mem",
+ .channels = 1,
+ .buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x4b000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node xm_usb3 = {
+ .name = "xm_usb3",
+ .channels = 1,
+ .buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x4c000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
+ .num_links = 1,
+ .link_nodes = { &qns_a1noc_snoc },
+};
+
+static struct qcom_icc_node qnm_lpass_lpinoc = {
+ .name = "qnm_lpass_lpinoc",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 2,
+ .link_nodes = { &qns_llc_lpinoc, &qns_lpass_aggnoc },
+};
+
+static struct qcom_icc_node qns_lpi_aon_noc = {
+ .name = "qns_lpi_aon_noc",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qnm_lpass_lpinoc },
+};
+
+static struct qcom_icc_node qnm_lpinoc_dsp_qns4m = {
+ .name = "qnm_lpinoc_dsp_qns4m",
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .link_nodes = { &qns_lpi_aon_noc },
+};
+
+static struct qcom_icc_bcm bcm_acv = {
+ .name = "ACV",
+ .enable_mask = BIT(3),
+ .num_nodes = 1,
+ .nodes = { &ebi },
+};
+
+static struct qcom_icc_bcm bcm_ce0 = {
+ .name = "CE0",
+ .num_nodes = 1,
+ .nodes = { &qxm_crypto },
+};
+
+static struct qcom_icc_bcm bcm_cn0 = {
+ .name = "CN0",
+ .enable_mask = BIT(0),
+ .keepalive = true,
+ .num_nodes = 25,
+ .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie,
+ &qhs_aoss, &qhs_ipa,
+ &qhs_ipc_router_fence, &qhs_soccp,
+ &qhs_tme_cfg, &qss_cfg,
+ &qss_ddrss_cfg, &qxs_imem,
+ &xs_pcie, &xs_pcie_g4x1,
+ &qsm_cfg_center, &qsm_cfg_east,
+ &qsm_cfg_mm_hf, &qsm_cfg_mm_sf,
+ &qsm_cfg_north, &qsm_cfg_south,
+ &qsm_cfg_west, &qhs_ahb2phy0,
+ &qhs_boot_rom, &qhs_camera_cfg,
+ &qhs_clk_ctl, &qhs_crypto_cfg,
+ &qhs_eva_cfg },
+};
+
+static struct qcom_icc_bcm bcm_cn1 = {
+ .name = "CN1",
+ .num_nodes = 6,
+ .nodes = { &qhs_display_cfg, &qhs_qup1,
+ &qhs_qup2, &qhs_qup3,
+ &qhs_qup4, &qhs_qup5 },
+};
+
+static struct qcom_icc_bcm bcm_co0 = {
+ .name = "CO0",
+ .enable_mask = BIT(0),
+ .num_nodes = 2,
+ .nodes = { &qnm_nsp, &qns_nsp_gemnoc },
+};
+
+static struct qcom_icc_bcm bcm_de0 = {
+ .name = "DE0",
+ .enable_mask = BIT(0),
+ .num_nodes = 1,
+ .nodes = { &ddr_eff_veto_slave },
+};
+
+static struct qcom_icc_bcm bcm_lp0 = {
+ .name = "LP0",
+ .num_nodes = 5,
+ .nodes = { &qnm_lpiaon_noc_llclpi_noc, &qns_lb,
+ &qnm_lpass_lpinoc, &qns_llc_lpinoc,
+ &qns_lpass_aggnoc },
+};
+
+static struct qcom_icc_bcm bcm_mc0 = {
+ .name = "MC0",
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &ebi },
+};
+
+static struct qcom_icc_bcm bcm_mc5 = {
+ .name = "MC5",
+ .num_nodes = 1,
+ .nodes = { &ddr_rt_slave },
+};
+
+static struct qcom_icc_bcm bcm_mm0 = {
+ .name = "MM0",
+ .num_nodes = 1,
+ .nodes = { &qns_mem_noc_hf },
+};
+
+static struct qcom_icc_bcm bcm_mm1 = {
+ .name = "MM1",
+ .enable_mask = BIT(0),
+ .num_nodes = 9,
+ .nodes = { &qnm_camnoc_hf, &qnm_camnoc_nrt_icp_sf,
+ &qnm_camnoc_rt_cdm_sf, &qnm_camnoc_sf,
+ &qnm_vapss_hcp, &qnm_video_cv_cpu,
+ &qnm_video_mvp, &qnm_video_v_cpu,
+ &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_bcm bcm_qpc0 = {
+ .name = "QPC0",
+ .num_nodes = 1,
+ .nodes = { &qnm_qpace },
+};
+
+static struct qcom_icc_bcm bcm_qup0 = {
+ .name = "QUP0",
+ .vote_scale = 1,
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qup0_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_qup1 = {
+ .name = "QUP1",
+ .vote_scale = 1,
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qup1_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_qup2 = {
+ .name = "QUP2",
+ .vote_scale = 1,
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qup2_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_qup3 = {
+ .name = "QUP3",
+ .vote_scale = 1,
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qup3_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_qup4 = {
+ .name = "QUP4",
+ .vote_scale = 1,
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qup4_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_qup5 = {
+ .name = "QUP5",
+ .vote_scale = 1,
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qup5_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_sh0 = {
+ .name = "SH0",
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qns_llcc },
+};
+
+static struct qcom_icc_bcm bcm_sh1 = {
+ .name = "SH1",
+ .enable_mask = BIT(0),
+ .num_nodes = 15,
+ .nodes = { &alm_gic, &alm_gpu_tcu,
+ &alm_sys_tcu, &chm_apps,
+ &qnm_gpu, &qnm_lpass_gemnoc,
+ &qnm_mdsp, &qnm_mnoc_hf,
+ &qnm_mnoc_sf, &qnm_nsp_gemnoc,
+ &qnm_pcie, &qnm_snoc_sf,
+ &qnm_wlan_q6, &qns_gem_noc_cnoc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_bcm bcm_sn0 = {
+ .name = "SN0",
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qns_gemnoc_sf },
+};
+
+static struct qcom_icc_bcm bcm_sn2 = {
+ .name = "SN2",
+ .num_nodes = 1,
+ .nodes = { &qnm_aggre_noc },
+};
+
+static struct qcom_icc_bcm bcm_sn3 = {
+ .name = "SN3",
+ .num_nodes = 1,
+ .nodes = { &qns_pcie_gemnoc },
+};
+
+static struct qcom_icc_bcm * const aggre1_noc_bcms[] = {
+ &bcm_ce0,
+};
+
+static struct qcom_icc_node * const aggre1_noc_nodes[] = {
+ [MASTER_QSPI_0] = &qhm_qspi,
+ [MASTER_QUP_2] = &qhm_qup2,
+ [MASTER_QUP_3] = &qhm_qup3,
+ [MASTER_QUP_4] = &qhm_qup4,
+ [MASTER_QUP_5] = &qhm_qup5,
+ [MASTER_CRYPTO] = &qxm_crypto,
+ [MASTER_IPA] = &qxm_ipa,
+ [MASTER_QUP_1] = &qxm_qup1,
+ [MASTER_SOCCP_PROC] = &qxm_soccp,
+ [MASTER_QDSS_ETR] = &xm_qdss_etr_0,
+ [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1,
+ [MASTER_SDCC_2] = &xm_sdc2,
+ [MASTER_SDCC_4] = &xm_sdc4,
+ [MASTER_UFS_MEM] = &xm_ufs_mem,
+ [MASTER_USB3] = &xm_usb3,
+ [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc,
+};
+
+static const struct regmap_config maili_aggre1_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x56200,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_aggre1_noc = {
+ .config = &maili_aggre1_noc_regmap_config,
+ .nodes = aggre1_noc_nodes,
+ .num_nodes = ARRAY_SIZE(aggre1_noc_nodes),
+ .bcms = aggre1_noc_bcms,
+ .num_bcms = ARRAY_SIZE(aggre1_noc_bcms),
+ .qos_requires_clocks = true,
+};
+
+static struct qcom_icc_bcm * const clk_virt_bcms[] = {
+ &bcm_de0,
+ &bcm_qup0,
+ &bcm_qup1,
+ &bcm_qup2,
+ &bcm_qup3,
+ &bcm_qup4,
+ &bcm_qup5,
+};
+
+static struct qcom_icc_node * const clk_virt_nodes[] = {
+ [MASTER_DDR_EFF_VETO] = &ddr_eff_veto_master,
+ [MASTER_QUP_CORE_0] = &qup0_core_master,
+ [MASTER_QUP_CORE_1] = &qup1_core_master,
+ [MASTER_QUP_CORE_2] = &qup2_core_master,
+ [MASTER_QUP_CORE_3] = &qup3_core_master,
+ [MASTER_QUP_CORE_4] = &qup4_core_master,
+ [MASTER_QUP_CORE_5] = &qup5_core_master,
+ [SLAVE_DDR_EFF_VETO] = &ddr_eff_veto_slave,
+ [SLAVE_QUP_CORE_0] = &qup0_core_slave,
+ [SLAVE_QUP_CORE_1] = &qup1_core_slave,
+ [SLAVE_QUP_CORE_2] = &qup2_core_slave,
+ [SLAVE_QUP_CORE_3] = &qup3_core_slave,
+ [SLAVE_QUP_CORE_4] = &qup4_core_slave,
+ [SLAVE_QUP_CORE_5] = &qup5_core_slave,
+};
+
+static const struct qcom_icc_desc maili_clk_virt = {
+ .nodes = clk_virt_nodes,
+ .num_nodes = ARRAY_SIZE(clk_virt_nodes),
+ .bcms = clk_virt_bcms,
+ .num_bcms = ARRAY_SIZE(clk_virt_bcms),
+};
+
+static struct qcom_icc_bcm * const cnoc_main_bcms[] = {
+ &bcm_cn0,
+};
+
+static struct qcom_icc_node * const cnoc_main_nodes[] = {
+ [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc,
+ [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie,
+ [SLAVE_AOSS] = &qhs_aoss,
+ [SLAVE_IPA_CFG] = &qhs_ipa,
+ [SLAVE_IPC_ROUTER_FENCE] = &qhs_ipc_router_fence,
+ [SLAVE_SOCCP] = &qhs_soccp,
+ [SLAVE_TME_CFG] = &qhs_tme_cfg,
+ [SLAVE_CNOC_CFG] = &qss_cfg,
+ [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg,
+ [SLAVE_IMEM] = &qxs_imem,
+ [SLAVE_PCIE_0] = &xs_pcie,
+ [SLAVE_PCIE_1] = &xs_pcie_g4x1,
+};
+
+static const struct regmap_config maili_cnoc_main_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x20000,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_cnoc_main = {
+ .config = &maili_cnoc_main_regmap_config,
+ .nodes = cnoc_main_nodes,
+ .num_nodes = ARRAY_SIZE(cnoc_main_nodes),
+ .bcms = cnoc_main_bcms,
+ .num_bcms = ARRAY_SIZE(cnoc_main_bcms),
+};
+
+static struct qcom_icc_bcm * const gem_noc_bcms[] = {
+ &bcm_qpc0,
+ &bcm_sh0,
+ &bcm_sh1,
+};
+
+static struct qcom_icc_node * const gem_noc_nodes[] = {
+ [MASTER_GIC] = &alm_gic,
+ [MASTER_GPU_TCU] = &alm_gpu_tcu,
+ [MASTER_SYS_TCU] = &alm_sys_tcu,
+ [MASTER_APPSS_PROC] = &chm_apps,
+ [MASTER_GFX3D] = &qnm_gpu,
+ [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc,
+ [MASTER_MSS_PROC] = &qnm_mdsp,
+ [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf,
+ [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf,
+ [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc,
+ [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie,
+ [MASTER_QPACE] = &qnm_qpace,
+ [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf,
+ [MASTER_WLAN_Q6] = &qnm_wlan_q6,
+ [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc,
+ [SLAVE_LLCC] = &qns_llcc,
+ [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie,
+};
+
+static const struct regmap_config maili_gem_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x160200,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_gem_noc = {
+ .config = &maili_gem_noc_regmap_config,
+ .nodes = gem_noc_nodes,
+ .num_nodes = ARRAY_SIZE(gem_noc_nodes),
+ .bcms = gem_noc_bcms,
+ .num_bcms = ARRAY_SIZE(gem_noc_bcms),
+};
+
+static struct qcom_icc_bcm * const llclpi_noc_bcms[] = {
+ &bcm_lp0,
+};
+
+static struct qcom_icc_node * const llclpi_noc_nodes[] = {
+ [MASTER_LPIAON_NOC_LLCLPI_NOC] = &qnm_lpiaon_noc_llclpi_noc,
+ [SLAVE_LPASS_LPI_CC] = &qhs_lpi_cc,
+ [SLAVE_LLCC_ISLAND] = &qns_lb,
+ [SLAVE_SERVICE_LLCLPI_NOC] = &srvc_llclpi_noc,
+ [SLAVE_SERVICE_LLCLPI_NOC_CHIPCX] = &srvc_llclpi_noc_chipcx,
+};
+
+static const struct regmap_config maili_llclpi_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x17200,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_llclpi_noc = {
+ .config = &maili_llclpi_noc_regmap_config,
+ .nodes = llclpi_noc_nodes,
+ .num_nodes = ARRAY_SIZE(llclpi_noc_nodes),
+ .bcms = llclpi_noc_bcms,
+ .num_bcms = ARRAY_SIZE(llclpi_noc_bcms),
+};
+
+static struct qcom_icc_node * const lpass_ag_noc_nodes[] = {
+ [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc,
+ [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc,
+};
+
+static const struct regmap_config maili_lpass_ag_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xc080,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_lpass_ag_noc = {
+ .config = &maili_lpass_ag_noc_regmap_config,
+ .nodes = lpass_ag_noc_nodes,
+ .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes),
+};
+
+static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = {
+ &bcm_lp0,
+};
+
+static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = {
+ [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc,
+ [SLAVE_LPIAON_NOC_LLCLPI_NOC] = &qns_llc_lpinoc,
+ [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc,
+};
+
+static const struct regmap_config maili_lpass_lpiaon_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x19080,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_lpass_lpiaon_noc = {
+ .config = &maili_lpass_lpiaon_noc_regmap_config,
+ .nodes = lpass_lpiaon_noc_nodes,
+ .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes),
+ .bcms = lpass_lpiaon_noc_bcms,
+ .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms),
+};
+
+static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = {
+ [MASTER_LPASS_PROC] = &qnm_lpinoc_dsp_qns4m,
+ [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc,
+};
+
+static const struct regmap_config maili_lpass_lpicx_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x46080,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_lpass_lpicx_noc = {
+ .config = &maili_lpass_lpicx_noc_regmap_config,
+ .nodes = lpass_lpicx_noc_nodes,
+ .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes),
+};
+
+static struct qcom_icc_bcm * const mc_virt_bcms[] = {
+ &bcm_acv,
+ &bcm_mc0,
+ &bcm_mc5,
+};
+
+static struct qcom_icc_node * const mc_virt_nodes[] = {
+ [MASTER_LLCC] = &llcc_mc,
+ [MASTER_DDR_RT] = &ddr_rt_mc,
+ [SLAVE_EBI1] = &ebi,
+ [SLAVE_DDR_RT] = &ddr_rt_slave,
+};
+
+static const struct qcom_icc_desc maili_mc_virt = {
+ .nodes = mc_virt_nodes,
+ .num_nodes = ARRAY_SIZE(mc_virt_nodes),
+ .bcms = mc_virt_bcms,
+ .num_bcms = ARRAY_SIZE(mc_virt_bcms),
+};
+
+static struct qcom_icc_bcm * const mmss_noc_bcms[] = {
+ &bcm_mm0,
+ &bcm_mm1,
+};
+
+static struct qcom_icc_node * const mmss_noc_nodes[] = {
+ [MASTER_CAMNOC_HF] = &qnm_camnoc_hf,
+ [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf,
+ [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf,
+ [MASTER_CAMNOC_SF] = &qnm_camnoc_sf,
+ [MASTER_MDP] = &qnm_mdp,
+ [MASTER_MDSS_DCP] = &qnm_mdss_dcp,
+ [MASTER_CDSP_HCP] = &qnm_vapss_hcp,
+ [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu,
+ [MASTER_VIDEO_EVA] = &qnm_video_eva,
+ [MASTER_VIDEO_MVP] = &qnm_video_mvp,
+ [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu,
+ [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf,
+ [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf,
+};
+
+static const struct regmap_config maili_mmss_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x5f800,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_mmss_noc = {
+ .config = &maili_mmss_noc_regmap_config,
+ .nodes = mmss_noc_nodes,
+ .num_nodes = ARRAY_SIZE(mmss_noc_nodes),
+ .bcms = mmss_noc_bcms,
+ .num_bcms = ARRAY_SIZE(mmss_noc_bcms),
+};
+
+static struct qcom_icc_bcm * const nsp_noc_bcms[] = {
+ &bcm_co0,
+};
+
+static struct qcom_icc_node * const nsp_noc_nodes[] = {
+ [MASTER_CDSP_PROC] = &qnm_nsp,
+ [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc,
+};
+
+static const struct regmap_config maili_nsp_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x21280,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_nsp_noc = {
+ .config = &maili_nsp_noc_regmap_config,
+ .nodes = nsp_noc_nodes,
+ .num_nodes = ARRAY_SIZE(nsp_noc_nodes),
+ .bcms = nsp_noc_bcms,
+ .num_bcms = ARRAY_SIZE(nsp_noc_bcms),
+};
+
+static struct qcom_icc_bcm * const pcie_anoc_bcms[] = {
+ &bcm_sn3,
+};
+
+static struct qcom_icc_node * const pcie_anoc_nodes[] = {
+ [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg,
+ [MASTER_PCIE_0] = &xm_pcie,
+ [MASTER_PCIE_1] = &xm_pcie_g4x1,
+ [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_gemnoc,
+ [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc,
+};
+
+static const struct regmap_config maili_pcie_anoc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x12400,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_pcie_anoc = {
+ .config = &maili_pcie_anoc_regmap_config,
+ .nodes = pcie_anoc_nodes,
+ .num_nodes = ARRAY_SIZE(pcie_anoc_nodes),
+ .bcms = pcie_anoc_bcms,
+ .num_bcms = ARRAY_SIZE(pcie_anoc_bcms),
+ .qos_requires_clocks = true,
+};
+
+static struct qcom_icc_bcm * const stdst_cfg_bcms[] = {
+ &bcm_cn0,
+ &bcm_cn1,
+};
+
+static struct qcom_icc_node * const stdst_cfg_nodes[] = {
+ [MASTER_CFG_CENTER] = &qsm_cfg_center,
+ [MASTER_CFG_EAST] = &qsm_cfg_east,
+ [MASTER_CFG_MM_HF] = &qsm_cfg_mm_hf,
+ [MASTER_CFG_MM_SF] = &qsm_cfg_mm_sf,
+ [MASTER_CFG_NORTH] = &qsm_cfg_north,
+ [MASTER_CFG_SOUTH] = &qsm_cfg_south,
+ [MASTER_CFG_WEST] = &qsm_cfg_west,
+ [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0,
+ [SLAVE_BOOT_ROM] = &qhs_boot_rom,
+ [SLAVE_CAMERA_CFG] = &qhs_camera_cfg,
+ [SLAVE_CLK_CTL] = &qhs_clk_ctl,
+ [SLAVE_CRYPTO_CFG] = &qhs_crypto_cfg,
+ [SLAVE_DISPLAY_CFG] = &qhs_display_cfg,
+ [SLAVE_EVA_CFG] = &qhs_eva_cfg,
+ [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg,
+ [SLAVE_I2C] = &qhs_i2c,
+ [SLAVE_IMEM_CFG] = &qhs_imem_cfg,
+ [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router,
+ [SLAVE_IRIS_CFG] = &qhs_iris_cfg,
+ [SLAVE_CNOC_MSS] = &qhs_mss_cfg,
+ [SLAVE_PCIE_0_CFG] = &qhs_pcie_cfg,
+ [SLAVE_PCIE_1_CFG] = &qhs_pcie_g4x1_cfg,
+ [SLAVE_PRNG] = &qhs_prng,
+ [SLAVE_QSPI_0] = &qhs_qspi,
+ [SLAVE_QUP_1] = &qhs_qup1,
+ [SLAVE_QUP_2] = &qhs_qup2,
+ [SLAVE_QUP_3] = &qhs_qup3,
+ [SLAVE_QUP_4] = &qhs_qup4,
+ [SLAVE_QUP_5] = &qhs_qup5,
+ [SLAVE_SDCC_2] = &qhs_sdc2,
+ [SLAVE_SDCC_4] = &qhs_sdc4,
+ [SLAVE_TLMM] = &qhs_tlmm,
+ [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg,
+ [SLAVE_USB3] = &qhs_usb3,
+ [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg,
+ [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg,
+ [SLAVE_QDSS_CFG] = &qss_qdss_cfg,
+ [SLAVE_QDSS_STM] = &qss_qdss_stm,
+ [SLAVE_TCSR] = &qss_tcsr,
+ [SLAVE_TCU] = &xs_sys_tcu_cfg,
+};
+
+static const struct regmap_config maili_stdst_cfg_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x9000,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_stdst_cfg = {
+ .config = &maili_stdst_cfg_regmap_config,
+ .nodes = stdst_cfg_nodes,
+ .num_nodes = ARRAY_SIZE(stdst_cfg_nodes),
+ .bcms = stdst_cfg_bcms,
+ .num_bcms = ARRAY_SIZE(stdst_cfg_bcms),
+};
+
+static struct qcom_icc_node * const stdst_main_nodes[] = {
+ [MASTER_CNOC_STARDUST] = &qsm_cnoc_main,
+ [SLAVE_STARDUST_CENTER_CFG] = &qss_stdst_center_cfg,
+ [SLAVE_STARDUST_EAST_CFG] = &qss_stdst_east_cfg,
+ [SLAVE_STARDUST_MM_HF_CFG] = &qss_stdst_mm_hf_cfg,
+ [SLAVE_STARDUST_MM_SF_CFG] = &qss_stdst_mm_sf_cfg,
+ [SLAVE_STARDUST_NORTH_CFG] = &qss_stdst_north_cfg,
+ [SLAVE_STARDUST_SOUTH_CFG] = &qss_stdst_south_cfg,
+ [SLAVE_STARDUST_WEST_CFG] = &qss_stdst_west_cfg,
+};
+
+static const struct regmap_config maili_stdst_main_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xf9000,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_stdst_main = {
+ .config = &maili_stdst_main_regmap_config,
+ .nodes = stdst_main_nodes,
+ .num_nodes = ARRAY_SIZE(stdst_main_nodes),
+};
+
+static struct qcom_icc_bcm * const system_noc_bcms[] = {
+ &bcm_sn0,
+ &bcm_sn2,
+};
+
+static struct qcom_icc_node * const system_noc_nodes[] = {
+ [MASTER_A1NOC_SNOC] = &qnm_aggre_noc,
+ [MASTER_APSS_NOC] = &qnm_apss_noc,
+ [MASTER_CNOC_SNOC] = &qnm_cnoc_data,
+ [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf,
+};
+
+static const struct regmap_config maili_system_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x20080,
+ .fast_io = true,
+};
+
+static const struct qcom_icc_desc maili_system_noc = {
+ .config = &maili_system_noc_regmap_config,
+ .nodes = system_noc_nodes,
+ .num_nodes = ARRAY_SIZE(system_noc_nodes),
+ .bcms = system_noc_bcms,
+ .num_bcms = ARRAY_SIZE(system_noc_bcms),
+};
+
+static const struct of_device_id qnoc_of_match[] = {
+ { .compatible = "qcom,maili-aggre-noc", .data = &maili_aggre1_noc },
+ { .compatible = "qcom,maili-clk-virt", .data = &maili_clk_virt },
+ { .compatible = "qcom,maili-cnoc-main", .data = &maili_cnoc_main },
+ { .compatible = "qcom,maili-gem-noc", .data = &maili_gem_noc },
+ { .compatible = "qcom,maili-llclpi-noc", .data = &maili_llclpi_noc },
+ { .compatible = "qcom,maili-lpass-ag-noc", .data = &maili_lpass_ag_noc },
+ { .compatible = "qcom,maili-lpass-lpiaon-noc", .data = &maili_lpass_lpiaon_noc },
+ { .compatible = "qcom,maili-lpass-lpicx-noc", .data = &maili_lpass_lpicx_noc },
+ { .compatible = "qcom,maili-mc-virt", .data = &maili_mc_virt },
+ { .compatible = "qcom,maili-mmss-noc", .data = &maili_mmss_noc },
+ { .compatible = "qcom,maili-nsp-noc", .data = &maili_nsp_noc },
+ { .compatible = "qcom,maili-pcie-anoc", .data = &maili_pcie_anoc },
+ { .compatible = "qcom,maili-stdst-cfg", .data = &maili_stdst_cfg },
+ { .compatible = "qcom,maili-stdst-main", .data = &maili_stdst_main },
+ { .compatible = "qcom,maili-system-noc", .data = &maili_system_noc },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qnoc_of_match);
+
+static struct platform_driver qnoc_driver = {
+ .probe = qcom_icc_rpmh_probe,
+ .remove = qcom_icc_rpmh_remove,
+ .driver = {
+ .name = "qnoc-maili",
+ .of_match_table = qnoc_of_match,
+ .sync_state = icc_sync_state,
+ },
+};
+
+static int __init qnoc_driver_init(void)
+{
+ return platform_driver_register(&qnoc_driver);
+}
+core_initcall(qnoc_driver_init);
+
+static void __exit qnoc_driver_exit(void)
+{
+ platform_driver_unregister(&qnoc_driver);
+}
+module_exit(qnoc_driver_exit);
+
+MODULE_DESCRIPTION("Qualcomm Maili NoC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c
index b33f00da1880..ecad636b53e0 100644
--- a/drivers/interconnect/qcom/osm-l3.c
+++ b/drivers/interconnect/qcom/osm-l3.c
@@ -60,6 +60,7 @@ struct qcom_osm_l3_desc {
unsigned int lut_row_size;
unsigned int reg_freq_lut;
unsigned int reg_perf_state;
+ unsigned int lut_max_entries;
};
#define DEFINE_QNODE(_name, _buswidth) \
@@ -90,6 +91,7 @@ static const struct qcom_osm_l3_desc osm_l3 = {
.lut_row_size = OSM_LUT_ROW_SIZE,
.reg_freq_lut = OSM_REG_FREQ_LUT,
.reg_perf_state = OSM_REG_PERF_STATE,
+ .lut_max_entries = LUT_MAX_ENTRIES,
};
static const struct qcom_osm_l3_desc epss_l3_perf_state = {
@@ -98,6 +100,16 @@ static const struct qcom_osm_l3_desc epss_l3_perf_state = {
.lut_row_size = EPSS_LUT_ROW_SIZE,
.reg_freq_lut = EPSS_REG_FREQ_LUT,
.reg_perf_state = EPSS_REG_PERF_STATE,
+ .lut_max_entries = LUT_MAX_ENTRIES,
+};
+
+static const struct qcom_osm_l3_desc shikra_epss_l3_perf_state = {
+ .nodes = epss_l3_nodes,
+ .num_nodes = ARRAY_SIZE(epss_l3_nodes),
+ .lut_row_size = EPSS_LUT_ROW_SIZE,
+ .reg_freq_lut = EPSS_REG_FREQ_LUT,
+ .reg_perf_state = EPSS_REG_PERF_STATE,
+ .lut_max_entries = 12,
};
static const struct qcom_osm_l3_desc epss_l3_l3_vote = {
@@ -106,6 +118,7 @@ static const struct qcom_osm_l3_desc epss_l3_l3_vote = {
.lut_row_size = EPSS_LUT_ROW_SIZE,
.reg_freq_lut = EPSS_REG_FREQ_LUT,
.reg_perf_state = EPSS_REG_L3_VOTE,
+ .lut_max_entries = LUT_MAX_ENTRIES,
};
static int qcom_osm_l3_set(struct icc_node *src, struct icc_node *dst)
@@ -189,7 +202,7 @@ static int qcom_osm_l3_probe(struct platform_device *pdev)
qp->reg_perf_state = desc->reg_perf_state;
- for (i = 0; i < LUT_MAX_ENTRIES; i++) {
+ for (i = 0; i < desc->lut_max_entries; i++) {
info = readl_relaxed(qp->base + desc->reg_freq_lut +
i * desc->lut_row_size);
src = FIELD_GET(LUT_SRC, info);
@@ -272,6 +285,7 @@ static const struct of_device_id osm_l3_of_match[] = {
{ .compatible = "qcom,sc7180-osm-l3", .data = &osm_l3 },
{ .compatible = "qcom,sc7280-epss-l3", .data = &epss_l3_perf_state },
{ .compatible = "qcom,sdm845-osm-l3", .data = &osm_l3 },
+ { .compatible = "qcom,shikra-epss-l3", .data = &shikra_epss_l3_perf_state },
{ .compatible = "qcom,sm8150-osm-l3", .data = &osm_l3 },
{ .compatible = "qcom,sc8180x-osm-l3", .data = &osm_l3 },
{ .compatible = "qcom,sm8250-epss-l3", .data = &epss_l3_perf_state },
diff --git a/drivers/interconnect/qcom/sc8280xp.c b/drivers/interconnect/qcom/sc8280xp.c
index 4110536664d0..15e284fcd89a 100644
--- a/drivers/interconnect/qcom/sc8280xp.c
+++ b/drivers/interconnect/qcom/sc8280xp.c
@@ -215,6 +215,11 @@ static struct qcom_icc_node qhm_qspi = {
.name = "qhm_qspi",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x5000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -223,6 +228,11 @@ static struct qcom_icc_node qhm_qup1 = {
.name = "qhm_qup1",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x6000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -231,6 +241,11 @@ static struct qcom_icc_node qhm_qup2 = {
.name = "qhm_qup2",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xc000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -247,6 +262,12 @@ static struct qcom_icc_node qxm_ipa = {
.name = "qxm_ipa",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x8000 },
+ .prio = 2,
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -255,6 +276,11 @@ static struct qcom_icc_node xm_emac_1 = {
.name = "xm_emac_1",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xd000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -263,6 +289,11 @@ static struct qcom_icc_node xm_sdc4 = {
.name = "xm_sdc4",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x7000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -271,6 +302,11 @@ static struct qcom_icc_node xm_ufs_mem = {
.name = "xm_ufs_mem",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x9000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -279,6 +315,11 @@ static struct qcom_icc_node xm_usb3_0 = {
.name = "xm_usb3_0",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xb000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_snoc },
};
@@ -287,6 +328,11 @@ static struct qcom_icc_node xm_usb3_1 = {
.name = "xm_usb3_1",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_snoc },
};
@@ -295,6 +341,11 @@ static struct qcom_icc_node xm_usb3_mp = {
.name = "xm_usb3_mp",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xe000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_snoc },
};
@@ -303,6 +354,11 @@ static struct qcom_icc_node xm_usb4_host0 = {
.name = "xm_usb4_host0",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xf000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_snoc },
};
@@ -311,6 +367,11 @@ static struct qcom_icc_node xm_usb4_host1 = {
.name = "xm_usb4_host1",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x10000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_snoc },
};
@@ -319,6 +380,11 @@ static struct qcom_icc_node qhm_qdss_bam = {
.name = "qhm_qdss_bam",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -327,6 +393,11 @@ static struct qcom_icc_node qhm_qup0 = {
.name = "qhm_qup0",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xb000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -343,6 +414,12 @@ static struct qcom_icc_node qxm_crypto = {
.name = "qxm_crypto",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xe000 },
+ .prio = 2,
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -367,6 +444,11 @@ static struct qcom_icc_node xm_emac_0 = {
.name = "xm_emac_0",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x11000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -375,6 +457,11 @@ static struct qcom_icc_node xm_pcie3_0 = {
.name = "xm_pcie3_0",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x12000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_gem_noc },
};
@@ -383,6 +470,11 @@ static struct qcom_icc_node xm_pcie3_1 = {
.name = "xm_pcie3_1",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xf000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_gem_noc },
};
@@ -391,6 +483,11 @@ static struct qcom_icc_node xm_pcie3_2a = {
.name = "xm_pcie3_2a",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x13000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_gem_noc },
};
@@ -399,6 +496,11 @@ static struct qcom_icc_node xm_pcie3_2b = {
.name = "xm_pcie3_2b",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x14000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_gem_noc },
};
@@ -407,6 +509,11 @@ static struct qcom_icc_node xm_pcie3_3a = {
.name = "xm_pcie3_3a",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x15000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_gem_noc },
};
@@ -415,6 +522,11 @@ static struct qcom_icc_node xm_pcie3_3b = {
.name = "xm_pcie3_3b",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x16000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_gem_noc },
};
@@ -423,6 +535,11 @@ static struct qcom_icc_node xm_pcie3_4 = {
.name = "xm_pcie3_4",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x10000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_gem_noc },
};
@@ -431,6 +548,11 @@ static struct qcom_icc_node xm_qdss_etr = {
.name = "xm_qdss_etr",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x7000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -439,6 +561,11 @@ static struct qcom_icc_node xm_sdc2 = {
.name = "xm_sdc2",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x8000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -447,6 +574,11 @@ static struct qcom_icc_node xm_ufs_card = {
.name = "xm_ufs_card",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x9000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -586,6 +718,11 @@ static struct qcom_icc_node alm_gpu_tcu = {
.name = "alm_gpu_tcu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa0000 },
+ .prio = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -595,6 +732,11 @@ static struct qcom_icc_node alm_pcie_tcu = {
.name = "alm_pcie_tcu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa1000 },
+ .prio = 3,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -604,6 +746,11 @@ static struct qcom_icc_node alm_sys_tcu = {
.name = "alm_sys_tcu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa2000 },
+ .prio = 6,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -623,6 +770,11 @@ static struct qcom_icc_node qnm_cmpnoc0 = {
.name = "qnm_cmpnoc0",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x1b000, 0x66000 },
+ .urg_fwd = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -632,6 +784,11 @@ static struct qcom_icc_node qnm_cmpnoc1 = {
.name = "qnm_cmpnoc1",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x24000, 0x6f000 },
+ .urg_fwd = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -651,6 +808,11 @@ static struct qcom_icc_node qnm_gpu = {
.name = "qnm_gpu",
.channels = 4,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 4,
+ .port_offsets = { 0x1c000, 0x1d000, 0x67000, 0x68000 },
+ .urg_fwd = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -660,6 +822,11 @@ static struct qcom_icc_node qnm_mnoc_hf = {
.name = "qnm_mnoc_hf",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x1e000, 0x69000 },
+ .urg_fwd = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_llcc,
&qns_pcie },
@@ -669,6 +836,11 @@ static struct qcom_icc_node qnm_mnoc_sf = {
.name = "qnm_mnoc_sf",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x1f000, 0x6a000 },
+ .urg_fwd = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -678,6 +850,12 @@ static struct qcom_icc_node qnm_pcie = {
.name = "qnm_pcie",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa3000 },
+ .prio = 2,
+ .urg_fwd = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc },
@@ -687,6 +865,11 @@ static struct qcom_icc_node qnm_snoc_gc = {
.name = "qnm_snoc_gc",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa4000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_llcc },
};
@@ -695,6 +878,11 @@ static struct qcom_icc_node qnm_snoc_sf = {
.name = "qnm_snoc_sf",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa5000 },
+ .urg_fwd = 1,
+ },
.num_links = 3,
.link_nodes = { &qns_gem_noc_cnoc,
&qns_llcc,
@@ -737,6 +925,11 @@ static struct qcom_icc_node qnm_camnoc_hf = {
.name = "qnm_camnoc_hf",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x10000, 0x10180 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -745,6 +938,11 @@ static struct qcom_icc_node qnm_mdp0_0 = {
.name = "qnm_mdp0_0",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x16000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -753,6 +951,11 @@ static struct qcom_icc_node qnm_mdp0_1 = {
.name = "qnm_mdp0_1",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x16080 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -761,6 +964,11 @@ static struct qcom_icc_node qnm_mdp1_0 = {
.name = "qnm_mdp1_0",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xf000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -769,6 +977,11 @@ static struct qcom_icc_node qnm_mdp1_1 = {
.name = "qnm_mdp1_1",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xf800 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -785,6 +998,11 @@ static struct qcom_icc_node qnm_rot_0 = {
.name = "qnm_rot_0",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x17000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -793,6 +1011,11 @@ static struct qcom_icc_node qnm_rot_1 = {
.name = "qnm_rot_1",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xe000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -801,6 +1024,11 @@ static struct qcom_icc_node qnm_video0 = {
.name = "qnm_video0",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x14000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -809,6 +1037,11 @@ static struct qcom_icc_node qnm_video1 = {
.name = "qnm_video1",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x14080 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -817,6 +1050,11 @@ static struct qcom_icc_node qnm_video_cvp = {
.name = "qnm_video_cvp",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x15000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -825,6 +1063,11 @@ static struct qcom_icc_node qxm_camnoc_icp = {
.name = "qxm_camnoc_icp",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x11000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -833,6 +1076,11 @@ static struct qcom_icc_node qxm_camnoc_sf = {
.name = "qxm_camnoc_sf",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x12000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -899,6 +1147,11 @@ static struct qcom_icc_node qnm_lpass_noc = {
.name = "qnm_lpass_noc",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xe000 },
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_gemnoc_sf },
};
@@ -915,6 +1168,12 @@ static struct qcom_icc_node qxm_pimem = {
.name = "qxm_pimem",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x10000 },
+ .prio = 2,
+ .urg_fwd = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_gemnoc_gc },
};
@@ -923,6 +1182,11 @@ static struct qcom_icc_node xm_gic = {
.name = "xm_gic",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x12000 },
+ .prio = 2,
+ },
.num_links = 1,
.link_nodes = { &qns_gemnoc_gc },
};
@@ -1996,11 +2260,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = {
[SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc,
};
+static const struct regmap_config sc8280xp_aggre1_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x3af80,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_aggre1_noc = {
+ .config = &sc8280xp_aggre1_noc_regmap_config,
.nodes = aggre1_noc_nodes,
.num_nodes = ARRAY_SIZE(aggre1_noc_nodes),
.bcms = aggre1_noc_bcms,
.num_bcms = ARRAY_SIZE(aggre1_noc_bcms),
+ .qos_requires_clocks = true,
};
static struct qcom_icc_bcm * const aggre2_noc_bcms[] = {
@@ -2032,11 +2306,21 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = {
[SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc,
};
+static const struct regmap_config sc8280xp_aggre2_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x3af80,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_aggre2_noc = {
+ .config = &sc8280xp_aggre2_noc_regmap_config,
.nodes = aggre2_noc_nodes,
.num_nodes = ARRAY_SIZE(aggre2_noc_nodes),
.bcms = aggre2_noc_bcms,
.num_bcms = ARRAY_SIZE(aggre2_noc_bcms),
+ .qos_requires_clocks = true,
};
static struct qcom_icc_bcm * const clk_virt_bcms[] = {
@@ -2158,7 +2442,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = {
[SLAVE_TCU] = &xs_sys_tcu_cfg,
};
+static const struct regmap_config sc8280xp_config_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x2c000,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_config_noc = {
+ .config = &sc8280xp_config_noc_regmap_config,
.nodes = config_noc_nodes,
.num_nodes = ARRAY_SIZE(config_noc_nodes),
.bcms = config_noc_bcms,
@@ -2174,7 +2467,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = {
[SLAVE_GEM_NOC_CFG] = &qns_gemnoc,
};
+static const struct regmap_config sc8280xp_dc_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x5080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_dc_noc = {
+ .config = &sc8280xp_dc_noc_regmap_config,
.nodes = dc_noc_nodes,
.num_nodes = ARRAY_SIZE(dc_noc_nodes),
.bcms = dc_noc_bcms,
@@ -2208,7 +2510,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = {
[SLAVE_SERVICE_GEM_NOC] = &srvc_sys_gemnoc,
};
+static const struct regmap_config sc8280xp_gem_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xb8400,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_gem_noc = {
+ .config = &sc8280xp_gem_noc_regmap_config,
.nodes = gem_noc_nodes,
.num_nodes = ARRAY_SIZE(gem_noc_nodes),
.bcms = gem_noc_bcms,
@@ -2231,7 +2542,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = {
[SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc,
};
+static const struct regmap_config sc8280xp_lpass_ag_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xf080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_lpass_ag_noc = {
+ .config = &sc8280xp_lpass_ag_noc_regmap_config,
.nodes = lpass_ag_noc_nodes,
.num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes),
.bcms = lpass_ag_noc_bcms,
@@ -2279,7 +2599,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = {
[SLAVE_SERVICE_MNOC] = &srvc_mnoc,
};
+static const struct regmap_config sc8280xp_mmss_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x1fa80,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_mmss_noc = {
+ .config = &sc8280xp_mmss_noc_regmap_config,
.nodes = mmss_noc_nodes,
.num_nodes = ARRAY_SIZE(mmss_noc_nodes),
.bcms = mmss_noc_bcms,
@@ -2299,7 +2628,16 @@ static struct qcom_icc_node * const nspa_noc_nodes[] = {
[SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc,
};
+static const struct regmap_config sc8280xp_nspa_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x10000,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_nspa_noc = {
+ .config = &sc8280xp_nspa_noc_regmap_config,
.nodes = nspa_noc_nodes,
.num_nodes = ARRAY_SIZE(nspa_noc_nodes),
.bcms = nspa_noc_bcms,
@@ -2319,7 +2657,16 @@ static struct qcom_icc_node * const nspb_noc_nodes[] = {
[SLAVE_SERVICE_NSPB_NOC] = &service_nspb_noc,
};
+static const struct regmap_config sc8280xp_nspb_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x10000,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_nspb_noc = {
+ .config = &sc8280xp_nspb_noc_regmap_config,
.nodes = nspb_noc_nodes,
.num_nodes = ARRAY_SIZE(nspb_noc_nodes),
.bcms = nspb_noc_bcms,
@@ -2348,7 +2695,16 @@ static struct qcom_icc_node * const system_noc_main_nodes[] = {
[SLAVE_SERVICE_SNOC] = &srvc_snoc,
};
+static const struct regmap_config sc8280xp_system_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x1a400,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc sc8280xp_system_noc_main = {
+ .config = &sc8280xp_system_noc_regmap_config,
.nodes = system_noc_main_nodes,
.num_nodes = ARRAY_SIZE(system_noc_main_nodes),
.bcms = system_noc_main_bcms,
diff --git a/drivers/interconnect/qcom/x1e80100.c b/drivers/interconnect/qcom/x1e80100.c
index 2ba2823c7860..8075e0ff2059 100644
--- a/drivers/interconnect/qcom/x1e80100.c
+++ b/drivers/interconnect/qcom/x1e80100.c
@@ -173,6 +173,13 @@ static struct qcom_icc_node qhm_qspi = {
.name = "qhm_qspi",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xb000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -181,6 +188,13 @@ static struct qcom_icc_node qhm_qup1 = {
.name = "qhm_qup1",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xc000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -189,6 +203,13 @@ static struct qcom_icc_node xm_sdc4 = {
.name = "xm_sdc4",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xd000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -197,6 +218,13 @@ static struct qcom_icc_node xm_ufs_mem = {
.name = "xm_ufs_mem",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xe000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -205,6 +233,13 @@ static struct qcom_icc_node qhm_qup0 = {
.name = "qhm_qup0",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x16000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -213,6 +248,13 @@ static struct qcom_icc_node qhm_qup2 = {
.name = "qhm_qup2",
.channels = 1,
.buswidth = 4,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x11000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -221,6 +263,13 @@ static struct qcom_icc_node qxm_crypto = {
.name = "qxm_crypto",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x12000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -237,6 +286,13 @@ static struct qcom_icc_node xm_qdss_etr_0 = {
.name = "xm_qdss_etr_0",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x13000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -245,6 +301,13 @@ static struct qcom_icc_node xm_qdss_etr_1 = {
.name = "xm_qdss_etr_1",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x14000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -253,6 +316,13 @@ static struct qcom_icc_node xm_sdc2 = {
.name = "xm_sdc2",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x15000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -337,6 +407,13 @@ static struct qcom_icc_node alm_gpu_tcu = {
.name = "alm_gpu_tcu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x271000 },
+ .prio = 1,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -345,6 +422,13 @@ static struct qcom_icc_node alm_pcie_tcu = {
.name = "alm_pcie_tcu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x27d000 },
+ .prio = 3,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -353,6 +437,13 @@ static struct qcom_icc_node alm_sys_tcu = {
.name = "alm_sys_tcu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x273000 },
+ .prio = 6,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -370,6 +461,13 @@ static struct qcom_icc_node qnm_gpu = {
.name = "qnm_gpu",
.channels = 4,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 4,
+ .port_offsets = { 0x51000, 0x58000, 0xd1000, 0xd8000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -378,6 +476,13 @@ static struct qcom_icc_node qnm_lpass = {
.name = "qnm_lpass",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x275000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 3,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
&qns_pcie },
@@ -387,6 +492,13 @@ static struct qcom_icc_node qnm_mnoc_hf = {
.name = "qnm_mnoc_hf",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x53000, 0xd3000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -395,6 +507,13 @@ static struct qcom_icc_node qnm_mnoc_sf = {
.name = "qnm_mnoc_sf",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x55000, 0xd5000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -403,6 +522,13 @@ static struct qcom_icc_node qnm_nsp_noc = {
.name = "qnm_nsp_noc",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x57000, 0xd7000 },
+ .prio = 0,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 3,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
&qns_pcie },
@@ -412,6 +538,13 @@ static struct qcom_icc_node qnm_pcie = {
.name = "qnm_pcie",
.channels = 1,
.buswidth = 64,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x277000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -420,6 +553,13 @@ static struct qcom_icc_node qnm_snoc_sf = {
.name = "qnm_snoc_sf",
.channels = 1,
.buswidth = 64,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x27b000 },
+ .prio = 2,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 3,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
&qns_pcie },
@@ -429,6 +569,13 @@ static struct qcom_icc_node xm_gic = {
.name = "xm_gic",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x27f000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_llcc },
};
@@ -469,6 +616,13 @@ static struct qcom_icc_node qnm_av1_enc = {
.name = "qnm_av1_enc",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x2f000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -477,6 +631,13 @@ static struct qcom_icc_node qnm_camnoc_hf = {
.name = "qnm_camnoc_hf",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x28000, 0x29000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -485,6 +646,13 @@ static struct qcom_icc_node qnm_camnoc_icp = {
.name = "qnm_camnoc_icp",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x2a000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -493,6 +661,13 @@ static struct qcom_icc_node qnm_camnoc_sf = {
.name = "qnm_camnoc_sf",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x2b000, 0x2c000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -501,6 +676,13 @@ static struct qcom_icc_node qnm_eva = {
.name = "qnm_eva",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x33000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -509,6 +691,13 @@ static struct qcom_icc_node qnm_mdp = {
.name = "qnm_mdp",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x2d000, 0x2e000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -517,6 +706,13 @@ static struct qcom_icc_node qnm_video = {
.name = "qnm_video",
.channels = 2,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 2,
+ .port_offsets = { 0x30000, 0x31000 },
+ .prio = 0,
+ .urg_fwd = 1,
+ .prio_fwd_disable = 0,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -525,6 +721,13 @@ static struct qcom_icc_node qnm_video_cv_cpu = {
.name = "qnm_video_cv_cpu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x32000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -533,6 +736,13 @@ static struct qcom_icc_node qnm_video_v_cpu = {
.name = "qnm_video_v_cpu",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x34000 },
+ .prio = 4,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -573,6 +783,13 @@ static struct qcom_icc_node xm_pcie_3 = {
.name = "xm_pcie_3",
.channels = 1,
.buswidth = 64,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x7000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_north_gem_noc },
};
@@ -581,6 +798,13 @@ static struct qcom_icc_node xm_pcie_4 = {
.name = "xm_pcie_4",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x8000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_north_gem_noc },
};
@@ -589,6 +813,13 @@ static struct qcom_icc_node xm_pcie_5 = {
.name = "xm_pcie_5",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x9000 },
+ .prio = 3,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_north_gem_noc },
};
@@ -597,6 +828,13 @@ static struct qcom_icc_node xm_pcie_0 = {
.name = "xm_pcie_0",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x9000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -605,6 +843,13 @@ static struct qcom_icc_node xm_pcie_1 = {
.name = "xm_pcie_1",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -613,6 +858,13 @@ static struct qcom_icc_node xm_pcie_2 = {
.name = "xm_pcie_2",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xb000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -621,6 +873,13 @@ static struct qcom_icc_node xm_pcie_6a = {
.name = "xm_pcie_6a",
.channels = 1,
.buswidth = 32,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xc000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -629,6 +888,13 @@ static struct qcom_icc_node xm_pcie_6b = {
.name = "xm_pcie_6b",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xd000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -653,6 +919,13 @@ static struct qcom_icc_node qnm_gic = {
.name = "qnm_gic",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x1c000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_gemnoc_sf },
};
@@ -685,6 +958,13 @@ static struct qcom_icc_node xm_usb2_0 = {
.name = "xm_usb2_0",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x6000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_north_snoc },
};
@@ -693,6 +973,13 @@ static struct qcom_icc_node xm_usb3_mp = {
.name = "xm_usb3_mp",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0x7000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_north_snoc },
};
@@ -701,6 +988,13 @@ static struct qcom_icc_node xm_usb3_0 = {
.name = "xm_usb3_0",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xa000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -709,6 +1003,13 @@ static struct qcom_icc_node xm_usb3_1 = {
.name = "xm_usb3_1",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xb000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -717,6 +1018,13 @@ static struct qcom_icc_node xm_usb3_2 = {
.name = "xm_usb3_2",
.channels = 1,
.buswidth = 8,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xc000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -725,6 +1033,13 @@ static struct qcom_icc_node xm_usb4_0 = {
.name = "xm_usb4_0",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xd000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -733,6 +1048,13 @@ static struct qcom_icc_node xm_usb4_1 = {
.name = "xm_usb4_1",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xe000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -741,6 +1063,13 @@ static struct qcom_icc_node xm_usb4_2 = {
.name = "xm_usb4_2",
.channels = 1,
.buswidth = 16,
+ .qosbox = &(const struct qcom_icc_qosbox) {
+ .num_ports = 1,
+ .port_offsets = { 0xf000 },
+ .prio = 2,
+ .urg_fwd = 0,
+ .prio_fwd_disable = 1,
+ },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -1466,11 +1795,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = {
[SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc,
};
+static const struct regmap_config x1e80100_aggre1_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x14400,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_aggre1_noc = {
+ .config = &x1e80100_aggre1_noc_regmap_config,
.nodes = aggre1_noc_nodes,
.num_nodes = ARRAY_SIZE(aggre1_noc_nodes),
.bcms = aggre1_noc_bcms,
.num_bcms = ARRAY_SIZE(aggre1_noc_bcms),
+ .qos_requires_clocks = true,
};
static struct qcom_icc_bcm * const aggre2_noc_bcms[] = {
@@ -1488,7 +1827,16 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = {
[SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc,
};
+static const struct regmap_config x1e80100_aggre2_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x1c400,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_aggre2_noc = {
+ .config = &x1e80100_aggre2_noc_regmap_config,
.nodes = aggre2_noc_nodes,
.num_nodes = ARRAY_SIZE(aggre2_noc_nodes),
.bcms = aggre2_noc_bcms,
@@ -1573,7 +1921,16 @@ static struct qcom_icc_node * const cnoc_cfg_nodes[] = {
[SLAVE_TCU] = &xs_sys_tcu_cfg,
};
+static const struct regmap_config x1e80100_cnoc_cfg_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x6600,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_cnoc_cfg = {
+ .config = &x1e80100_cnoc_cfg_regmap_config,
.nodes = cnoc_cfg_nodes,
.num_nodes = ARRAY_SIZE(cnoc_cfg_nodes),
.bcms = cnoc_cfg_bcms,
@@ -1603,7 +1960,16 @@ static struct qcom_icc_node * const cnoc_main_nodes[] = {
[SLAVE_PCIE_6B] = &xs_pcie_6b,
};
+static const struct regmap_config x1e80100_cnoc_main_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x14400,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_cnoc_main = {
+ .config = &x1e80100_cnoc_main_regmap_config,
.nodes = cnoc_main_nodes,
.num_nodes = ARRAY_SIZE(cnoc_main_nodes),
.bcms = cnoc_main_bcms,
@@ -1633,7 +1999,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = {
[SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie,
};
+static const struct regmap_config x1e80100_gem_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x311200,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_gem_noc = {
+ .config = &x1e80100_gem_noc_regmap_config,
.nodes = gem_noc_nodes,
.num_nodes = ARRAY_SIZE(gem_noc_nodes),
.bcms = gem_noc_bcms,
@@ -1648,7 +2023,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = {
[SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc,
};
+static const struct regmap_config x1e80100_lpass_ag_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xe080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_lpass_ag_noc = {
+ .config = &x1e80100_lpass_ag_noc_regmap_config,
.nodes = lpass_ag_noc_nodes,
.num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes),
.bcms = lpass_ag_noc_bcms,
@@ -1664,7 +2048,16 @@ static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = {
[SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc,
};
+static const struct regmap_config x1e80100_lpass_lpiaon_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x19080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_lpass_lpiaon_noc = {
+ .config = &x1e80100_lpass_lpiaon_noc_regmap_config,
.nodes = lpass_lpiaon_noc_nodes,
.num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes),
.bcms = lpass_lpiaon_noc_bcms,
@@ -1679,7 +2072,16 @@ static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = {
[SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc,
};
+static const struct regmap_config x1e80100_lpass_lpicx_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x3a200,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_lpass_lpicx_noc = {
+ .config = &x1e80100_lpass_lpicx_noc_regmap_config,
.nodes = lpass_lpicx_noc_nodes,
.num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes),
.bcms = lpass_lpicx_noc_bcms,
@@ -1724,7 +2126,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = {
[SLAVE_SERVICE_MNOC] = &srvc_mnoc,
};
+static const struct regmap_config x1e80100_mmss_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x5b800,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_mmss_noc = {
+ .config = &x1e80100_mmss_noc_regmap_config,
.nodes = mmss_noc_nodes,
.num_nodes = ARRAY_SIZE(mmss_noc_nodes),
.bcms = mmss_noc_bcms,
@@ -1740,7 +2151,16 @@ static struct qcom_icc_node * const nsp_noc_nodes[] = {
[SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc,
};
+static const struct regmap_config x1e80100_nsp_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xe080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_nsp_noc = {
+ .config = &x1e80100_nsp_noc_regmap_config,
.nodes = nsp_noc_nodes,
.num_nodes = ARRAY_SIZE(nsp_noc_nodes),
.bcms = nsp_noc_bcms,
@@ -1757,7 +2177,16 @@ static struct qcom_icc_node * const pcie_center_anoc_nodes[] = {
[SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc,
};
+static const struct regmap_config x1e80100_pcie_center_anoc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x7000,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_pcie_center_anoc = {
+ .config = &x1e80100_pcie_center_anoc_regmap_config,
.nodes = pcie_center_anoc_nodes,
.num_nodes = ARRAY_SIZE(pcie_center_anoc_nodes),
.bcms = pcie_center_anoc_bcms,
@@ -1774,7 +2203,16 @@ static struct qcom_icc_node * const pcie_north_anoc_nodes[] = {
[SLAVE_PCIE_NORTH] = &qns_pcie_north_gem_noc,
};
+static const struct regmap_config x1e80100_pcie_north_anoc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x9080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_pcie_north_anoc = {
+ .config = &x1e80100_pcie_north_anoc_regmap_config,
.nodes = pcie_north_anoc_nodes,
.num_nodes = ARRAY_SIZE(pcie_north_anoc_nodes),
.bcms = pcie_north_anoc_bcms,
@@ -1793,7 +2231,16 @@ static struct qcom_icc_node * const pcie_south_anoc_nodes[] = {
[SLAVE_PCIE_SOUTH] = &qns_pcie_south_gem_noc,
};
+static const struct regmap_config x1e80100_pcie_south_anoc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xd080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_pcie_south_anoc = {
+ .config = &x1e80100_pcie_south_anoc_regmap_config,
.nodes = pcie_south_anoc_nodes,
.num_nodes = ARRAY_SIZE(pcie_south_anoc_nodes),
.bcms = pcie_south_anoc_bcms,
@@ -1815,7 +2262,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = {
[SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf,
};
+static const struct regmap_config x1e80100_system_noc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x1c080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_system_noc = {
+ .config = &x1e80100_system_noc_regmap_config,
.nodes = system_noc_nodes,
.num_nodes = ARRAY_SIZE(system_noc_nodes),
.bcms = system_noc_bcms,
@@ -1831,7 +2287,16 @@ static struct qcom_icc_node * const usb_center_anoc_nodes[] = {
[SLAVE_USB_NOC_SNOC] = &qns_aggre_usb_snoc,
};
+static const struct regmap_config x1e80100_usb_center_anoc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x8800,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_usb_center_anoc = {
+ .config = &x1e80100_usb_center_anoc_regmap_config,
.nodes = usb_center_anoc_nodes,
.num_nodes = ARRAY_SIZE(usb_center_anoc_nodes),
.bcms = usb_center_anoc_bcms,
@@ -1847,11 +2312,21 @@ static struct qcom_icc_node * const usb_north_anoc_nodes[] = {
[SLAVE_AGGRE_USB_NORTH] = &qns_aggre_usb_north_snoc,
};
+static const struct regmap_config x1e80100_usb_north_anoc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x7080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_usb_north_anoc = {
+ .config = &x1e80100_usb_north_anoc_regmap_config,
.nodes = usb_north_anoc_nodes,
.num_nodes = ARRAY_SIZE(usb_north_anoc_nodes),
.bcms = usb_north_anoc_bcms,
.num_bcms = ARRAY_SIZE(usb_north_anoc_bcms),
+ .qos_requires_clocks = true,
};
static struct qcom_icc_bcm * const usb_south_anoc_bcms[] = {
@@ -1867,11 +2342,21 @@ static struct qcom_icc_node * const usb_south_anoc_nodes[] = {
[SLAVE_AGGRE_USB_SOUTH] = &qns_aggre_usb_south_snoc,
};
+static const struct regmap_config x1e80100_usb_south_anoc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xf080,
+ .fast_io = true,
+};
+
static const struct qcom_icc_desc x1e80100_usb_south_anoc = {
+ .config = &x1e80100_usb_south_anoc_regmap_config,
.nodes = usb_south_anoc_nodes,
.num_nodes = ARRAY_SIZE(usb_south_anoc_nodes),
.bcms = usb_south_anoc_bcms,
.num_bcms = ARRAY_SIZE(usb_south_anoc_bcms),
+ .qos_requires_clocks = true,
};
static const struct of_device_id qnoc_of_match[] = {
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c
index 1bbefc6de708..2169e4b75f98 100644
--- a/drivers/ipack/devices/ipoctal.c
+++ b/drivers/ipack/devices/ipoctal.c
@@ -10,6 +10,8 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/kref.h>
+#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/serial.h>
@@ -25,6 +27,8 @@
static const struct tty_operations ipoctal_fops;
+static void ipoctal_release(struct kref *kref);
+
struct ipoctal_channel {
struct ipoctal_stats stats;
unsigned int nb_bytes;
@@ -49,6 +53,10 @@ struct ipoctal {
struct tty_driver *tty_drv;
u8 __iomem *mem8_space;
u8 __iomem *int_space;
+ struct kref kref;
+ struct module *carrier_owner;
+ struct rw_semaphore remove_sem;
+ bool removed;
};
static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan,
@@ -70,8 +78,14 @@ static void ipoctal_reset_channel(struct ipoctal_channel *channel)
static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
{
struct ipoctal_channel *channel;
+ struct ipoctal *ipoctal;
channel = dev_get_drvdata(tty->dev);
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return -ENODEV;
/*
* Enable RX. TX will be enabled when
@@ -95,6 +109,7 @@ static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty)
if (res)
goto err_put_carrier;
+ kref_get(&ipoctal->kref);
tty->driver_data = channel;
return 0;
@@ -460,8 +475,13 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf,
size_t count)
{
struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
size_t char_copied;
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed || !channel->tty_port.xmit_buf)
+ return 0;
+
char_copied = ipoctal_copy_write_buffer(channel, buf, count);
/* As the IP-OCTAL 485 only supports half duplex, do it manually */
@@ -501,8 +521,13 @@ static void ipoctal_set_termios(struct tty_struct *tty,
unsigned char mr2 = 0;
unsigned char csr = 0;
struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
speed_t baud;
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return;
+
cflag = tty->termios.c_cflag;
/* Disable and reset everything before change the setup */
@@ -631,10 +656,16 @@ static void ipoctal_hangup(struct tty_struct *tty)
{
unsigned long flags;
struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal;
if (channel == NULL)
return;
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return;
+
spin_lock_irqsave(&channel->lock, flags);
channel->nb_bytes = 0;
channel->pointer_read = 0;
@@ -651,10 +682,16 @@ static void ipoctal_hangup(struct tty_struct *tty)
static void ipoctal_shutdown(struct tty_struct *tty)
{
struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal;
if (channel == NULL)
return;
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return;
+
ipoctal_reset_channel(channel);
tty_port_set_initialized(&channel->tty_port, false);
}
@@ -664,8 +701,9 @@ static void ipoctal_cleanup(struct tty_struct *tty)
struct ipoctal_channel *channel = tty->driver_data;
struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
- /* release the carrier driver */
- ipack_put_carrier(ipoctal->dev);
+ /* release the carrier driver via cached owner */
+ module_put(ipoctal->carrier_owner);
+ kref_put(&ipoctal->kref, ipoctal_release);
}
static const struct tty_operations ipoctal_fops = {
@@ -683,6 +721,13 @@ static const struct tty_operations ipoctal_fops = {
.cleanup = ipoctal_cleanup,
};
+static void ipoctal_release(struct kref *kref)
+{
+ struct ipoctal *ipoctal = container_of(kref, struct ipoctal, kref);
+
+ kfree(ipoctal);
+}
+
static int ipoctal_probe(struct ipack_device *dev)
{
int res;
@@ -692,7 +737,11 @@ static int ipoctal_probe(struct ipack_device *dev)
if (ipoctal == NULL)
return -ENOMEM;
+ kref_init(&ipoctal->kref);
+ init_rwsem(&ipoctal->remove_sem);
+
ipoctal->dev = dev;
+ ipoctal->carrier_owner = dev->bus->owner;
res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot);
if (res)
goto out_uninst;
@@ -701,7 +750,7 @@ static int ipoctal_probe(struct ipack_device *dev)
return 0;
out_uninst:
- kfree(ipoctal);
+ kref_put(&ipoctal->kref, ipoctal_release);
return res;
}
@@ -709,6 +758,9 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
{
int i;
+ scoped_guard(rwsem_write, &ipoctal->remove_sem)
+ ipoctal->removed = true;
+
ipoctal->dev->bus->ops->free_irq(ipoctal->dev);
for (i = 0; i < NR_CHANNELS; i++) {
@@ -725,7 +777,7 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
tty_unregister_driver(ipoctal->tty_drv);
kfree(ipoctal->tty_drv->name);
tty_driver_kref_put(ipoctal->tty_drv);
- kfree(ipoctal);
+ kref_put(&ipoctal->kref, ipoctal_release);
}
static void ipoctal_remove(struct ipack_device *idev)
diff --git a/drivers/mailbox/mailbox-mchp-ipc-sbi.c b/drivers/mailbox/mailbox-mchp-ipc-sbi.c
index b87bf2fb4b9b..f081f8a9bcf8 100644
--- a/drivers/mailbox/mailbox-mchp-ipc-sbi.c
+++ b/drivers/mailbox/mailbox-mchp-ipc-sbi.c
@@ -378,6 +378,8 @@ static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc)
for_each_online_cpu(cpuid) {
hartid = cpuid_to_hartid_map(cpuid);
irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid);
+ if (!irq_name)
+ return -ENOMEM;
ret = platform_get_irq_byname_optional(pdev, irq_name);
if (ret <= 0)
continue;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 390256ed91f4..7364931dad3a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -57,28 +57,6 @@ config DUMMY_IRQ
The sole purpose of this module is to help with debugging of systems on
which spurious IRQs would happen on disabled IRQ vector.
-config IBM_ASM
- tristate "Device driver for IBM RSA service processor"
- depends on X86 && PCI && INPUT
- depends on SERIAL_8250 || SERIAL_8250=n
- help
- This option enables device driver support for in-band access to the
- IBM RSA (Condor) service processor in eServer xSeries systems.
- The ibmasm device driver allows user space application to access
- ASM (Advanced Systems Management) functions on the service
- processor. The driver is meant to be used in conjunction with
- a user space API.
- The ibmasm driver also enables the OS to use the UART on the
- service processor board as a regular serial port. To make use of
- this feature serial driver support (CONFIG_SERIAL_8250) must be
- enabled.
-
- WARNING: This software may not be supported or function
- correctly on your IBM server. Please consult the IBM ServerProven
- website <https://www-03.ibm.com/systems/info/x86servers/serverproven/compat/us/>
- for information on the specific driver level and support statement
- for your IBM server.
-
config IBMVMC
tristate "IBM Virtual Management Channel support"
depends on PPC_PSERIES
@@ -185,19 +163,6 @@ config ENCLOSURE_SERVICES
driver (SCSI/ATA) which supports enclosures
or a SCSI enclosure device (SES) to use these services.
-config SGI_XP
- tristate "Support communication between SGI SSIs"
- depends on NET
- depends on X86_UV && SMP
- depends on X86_64 || BROKEN
- select SGI_GRU if X86_64 && SMP
- help
- An SGI machine can be divided into multiple Single System
- Images which act independently of each other and have
- hardware based memory protection from the others. Enabling
- this feature will allow for direct communication between SSIs
- based on a network adapter and DMA messaging.
-
config SMPRO_ERRMON
tristate "Ampere Computing SMPro error monitor driver"
depends on MFD_SMPRO || COMPILE_TEST
@@ -310,27 +275,6 @@ config QCOM_FASTRPC
applications DSP processor. Say M if you want to enable this
module.
-config SGI_GRU
- tristate "SGI GRU driver"
- depends on X86_UV && SMP
- select MMU_NOTIFIER
- help
- The GRU is a hardware resource located in the system chipset. The GRU
- contains memory that can be mmapped into the user address space.
- This memory is used to communicate with the GRU to perform functions
- such as load/store, scatter/gather, bcopy, AMOs, etc. The GRU is
- directly accessed by user instructions using user virtual addresses.
- GRU instructions (ex., bcopy) use user virtual addresses for operands.
-
- If you are not running on a SGI UV system, say N.
-
-config SGI_GRU_DEBUG
- bool "SGI GRU driver debug"
- depends on SGI_GRU
- help
- This option enables additional debugging code for the SGI GRU driver.
- If you are unsure, say N.
-
config APDS9802ALS
tristate "Medfield Avago APDS9802 ALS Sensor module"
depends on I2C
@@ -494,16 +438,6 @@ config MISC_RTSX
tristate
default MISC_RTSX_PCI || MISC_RTSX_USB
-config HISI_HIKEY_USB
- tristate "USB GPIO Hub on HiSilicon Hikey 960/970 Platform"
- depends on (OF && GPIOLIB) || COMPILE_TEST
- depends on USB_ROLE_SWITCH
- help
- If you say yes here this adds support for the on-board USB GPIO hub
- found on HiKey 960/970 boards, which is necessary to support
- switching between the dual-role USB-C port and the USB-A host ports
- using only one USB controller.
-
config OPEN_DICE
tristate "Open Profile for DICE driver"
depends on OF_RESERVED_MEM
@@ -651,4 +585,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig"
source "drivers/misc/keba/Kconfig"
source "drivers/misc/amd-sbi/Kconfig"
source "drivers/misc/rp1/Kconfig"
+source "drivers/misc/issei/Kconfig"
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fed47c7672b9..e8d8d5d88c0d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -3,7 +3,6 @@
# Makefile for misc devices that really don't fit anywhere else.
#
-obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_IBMVMC) += ibmvmc.o
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
@@ -22,8 +21,6 @@ obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
-obj-$(CONFIG_SGI_XP) += sgi-xp/
-obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o
obj-$(CONFIG_SMPRO_MISC) += smpro-misc.o
obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o
@@ -56,7 +53,6 @@ obj-y += cardreader/
obj-$(CONFIG_PVPANIC) += pvpanic/
obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
-obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
obj-$(CONFIG_NTSYNC) += ntsync.o
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
obj-$(CONFIG_OPEN_DICE) += open-dice.o
@@ -74,3 +70,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o
obj-y += keba/
obj-y += amd-sbi/
obj-$(CONFIG_MISC_RP1) += rp1/
+obj-$(CONFIG_INTEL_SSEI) += issei/
diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c
index 469478f7a1d3..a74b0266ea16 100644
--- a/drivers/misc/ad525x_dpot-i2c.c
+++ b/drivers/misc/ad525x_dpot-i2c.c
@@ -73,38 +73,39 @@ static void ad_dpot_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id ad_dpot_id[] = {
- {"ad5258", AD5258_ID},
- {"ad5259", AD5259_ID},
- {"ad5251", AD5251_ID},
- {"ad5252", AD5252_ID},
- {"ad5253", AD5253_ID},
- {"ad5254", AD5254_ID},
- {"ad5255", AD5255_ID},
- {"ad5241", AD5241_ID},
- {"ad5242", AD5242_ID},
- {"ad5243", AD5243_ID},
- {"ad5245", AD5245_ID},
- {"ad5246", AD5246_ID},
- {"ad5247", AD5247_ID},
- {"ad5248", AD5248_ID},
- {"ad5280", AD5280_ID},
- {"ad5282", AD5282_ID},
- {"adn2860", ADN2860_ID},
- {"ad5273", AD5273_ID},
- {"ad5161", AD5161_ID},
- {"ad5171", AD5171_ID},
- {"ad5170", AD5170_ID},
- {"ad5172", AD5172_ID},
- {"ad5173", AD5173_ID},
- {"ad5272", AD5272_ID},
- {"ad5274", AD5274_ID},
- {}
+ { .name = "ad5258", .driver_data = AD5258_ID },
+ { .name = "ad5259", .driver_data = AD5259_ID },
+ { .name = "ad5251", .driver_data = AD5251_ID },
+ { .name = "ad5252", .driver_data = AD5252_ID },
+ { .name = "ad5253", .driver_data = AD5253_ID },
+ { .name = "ad5254", .driver_data = AD5254_ID },
+ { .name = "ad5255", .driver_data = AD5255_ID },
+ { .name = "ad5241", .driver_data = AD5241_ID },
+ { .name = "ad5242", .driver_data = AD5242_ID },
+ { .name = "ad5243", .driver_data = AD5243_ID },
+ { .name = "ad5245", .driver_data = AD5245_ID },
+ { .name = "ad5246", .driver_data = AD5246_ID },
+ { .name = "ad5247", .driver_data = AD5247_ID },
+ { .name = "ad5248", .driver_data = AD5248_ID },
+ { .name = "ad5280", .driver_data = AD5280_ID },
+ { .name = "ad5282", .driver_data = AD5282_ID },
+ { .name = "adn2860", .driver_data = ADN2860_ID },
+ { .name = "ad5273", .driver_data = AD5273_ID },
+ { .name = "ad5161", .driver_data = AD5161_ID },
+ { .name = "ad5171", .driver_data = AD5171_ID },
+ { .name = "ad5170", .driver_data = AD5170_ID },
+ { .name = "ad5172", .driver_data = AD5172_ID },
+ { .name = "ad5173", .driver_data = AD5173_ID },
+ { .name = "ad5272", .driver_data = AD5272_ID },
+ { .name = "ad5274", .driver_data = AD5274_ID },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ad_dpot_id);
static struct i2c_driver ad_dpot_i2c_driver = {
.driver = {
.name = "ad_dpot",
+ .dev_groups = ad_dpot_groups,
},
.probe = ad_dpot_i2c_probe,
.remove = ad_dpot_i2c_remove,
diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c
index 263055bda48b..c4c2ec52a83d 100644
--- a/drivers/misc/ad525x_dpot-spi.c
+++ b/drivers/misc/ad525x_dpot-spi.c
@@ -96,41 +96,42 @@ static void ad_dpot_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id ad_dpot_spi_id[] = {
- {"ad5160", AD5160_ID},
- {"ad5161", AD5161_ID},
- {"ad5162", AD5162_ID},
- {"ad5165", AD5165_ID},
- {"ad5200", AD5200_ID},
- {"ad5201", AD5201_ID},
- {"ad5203", AD5203_ID},
- {"ad5204", AD5204_ID},
- {"ad5206", AD5206_ID},
- {"ad5207", AD5207_ID},
- {"ad5231", AD5231_ID},
- {"ad5232", AD5232_ID},
- {"ad5233", AD5233_ID},
- {"ad5235", AD5235_ID},
- {"ad5260", AD5260_ID},
- {"ad5262", AD5262_ID},
- {"ad5263", AD5263_ID},
- {"ad5290", AD5290_ID},
- {"ad5291", AD5291_ID},
- {"ad5292", AD5292_ID},
- {"ad5293", AD5293_ID},
- {"ad7376", AD7376_ID},
- {"ad8400", AD8400_ID},
- {"ad8402", AD8402_ID},
- {"ad8403", AD8403_ID},
- {"adn2850", ADN2850_ID},
- {"ad5270", AD5270_ID},
- {"ad5271", AD5271_ID},
- {}
+ { .name = "ad5160", .driver_data = AD5160_ID },
+ { .name = "ad5161", .driver_data = AD5161_ID },
+ { .name = "ad5162", .driver_data = AD5162_ID },
+ { .name = "ad5165", .driver_data = AD5165_ID },
+ { .name = "ad5200", .driver_data = AD5200_ID },
+ { .name = "ad5201", .driver_data = AD5201_ID },
+ { .name = "ad5203", .driver_data = AD5203_ID },
+ { .name = "ad5204", .driver_data = AD5204_ID },
+ { .name = "ad5206", .driver_data = AD5206_ID },
+ { .name = "ad5207", .driver_data = AD5207_ID },
+ { .name = "ad5231", .driver_data = AD5231_ID },
+ { .name = "ad5232", .driver_data = AD5232_ID },
+ { .name = "ad5233", .driver_data = AD5233_ID },
+ { .name = "ad5235", .driver_data = AD5235_ID },
+ { .name = "ad5260", .driver_data = AD5260_ID },
+ { .name = "ad5262", .driver_data = AD5262_ID },
+ { .name = "ad5263", .driver_data = AD5263_ID },
+ { .name = "ad5290", .driver_data = AD5290_ID },
+ { .name = "ad5291", .driver_data = AD5291_ID },
+ { .name = "ad5292", .driver_data = AD5292_ID },
+ { .name = "ad5293", .driver_data = AD5293_ID },
+ { .name = "ad7376", .driver_data = AD7376_ID },
+ { .name = "ad8400", .driver_data = AD8400_ID },
+ { .name = "ad8402", .driver_data = AD8402_ID },
+ { .name = "ad8403", .driver_data = AD8403_ID },
+ { .name = "adn2850", .driver_data = ADN2850_ID },
+ { .name = "ad5270", .driver_data = AD5270_ID },
+ { .name = "ad5271", .driver_data = AD5271_ID },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad_dpot_spi_id);
static struct spi_driver ad_dpot_spi_driver = {
.driver = {
.name = "ad_dpot",
+ .dev_groups = ad_dpot_groups,
},
.probe = ad_dpot_spi_probe,
.remove = ad_dpot_spi_remove,
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index 57bead9fba1b..a4e22fd4a107 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -630,66 +630,132 @@ static struct attribute *ad525x_attributes_commands[] = {
NULL
};
-static const struct attribute_group ad525x_group_commands = {
- .attrs = ad525x_attributes_commands,
+static struct attribute *ad525x_attributes[] = {
+ &dev_attr_rdac0.attr,
+ &dev_attr_rdac1.attr,
+ &dev_attr_rdac2.attr,
+ &dev_attr_rdac3.attr,
+ &dev_attr_rdac4.attr,
+ &dev_attr_rdac5.attr,
+ &dev_attr_eeprom0.attr,
+ &dev_attr_eeprom1.attr,
+ &dev_attr_eeprom2.attr,
+ &dev_attr_eeprom3.attr,
+ &dev_attr_eeprom4.attr,
+ &dev_attr_eeprom5.attr,
+ &dev_attr_tolerance0.attr,
+ &dev_attr_tolerance1.attr,
+ &dev_attr_tolerance2.attr,
+ &dev_attr_tolerance3.attr,
+ &dev_attr_tolerance4.attr,
+ &dev_attr_tolerance5.attr,
+ &dev_attr_otp0.attr,
+ &dev_attr_otp1.attr,
+ &dev_attr_otp2.attr,
+ &dev_attr_otp3.attr,
+ &dev_attr_otp4.attr,
+ &dev_attr_otp5.attr,
+ &dev_attr_otp0en.attr,
+ &dev_attr_otp1en.attr,
+ &dev_attr_otp2en.attr,
+ &dev_attr_otp3en.attr,
+ &dev_attr_otp4en.attr,
+ &dev_attr_otp5en.attr,
+ &dev_attr_inc_all.attr,
+ &dev_attr_dec_all.attr,
+ &dev_attr_inc_all_6db.attr,
+ &dev_attr_dec_all_6db.attr,
+ NULL
};
-static int ad_dpot_add_files(struct device *dev,
- unsigned int features, unsigned int rdac)
+static int ad525x_attr_index(struct attribute *attr,
+ const struct attribute * const *attrs)
{
- int err = sysfs_create_file(&dev->kobj,
- dpot_attrib_wipers[rdac]);
- if (features & F_CMD_EEP)
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_eeprom[rdac]);
- if (features & F_CMD_TOL)
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_tolerance[rdac]);
- if (features & F_CMD_OTP) {
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_otp_en[rdac]);
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_otp[rdac]);
- }
+ int i;
- if (err)
- dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
- rdac);
+ for (i = 0; attrs[i]; i++)
+ if (attr == attrs[i])
+ return i;
- return err;
+ return -ENOENT;
}
-static inline void ad_dpot_remove_files(struct device *dev,
- unsigned int features, unsigned int rdac)
+static bool ad525x_is_command_attr(struct attribute *attr)
{
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_wipers[rdac]);
- if (features & F_CMD_EEP)
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_eeprom[rdac]);
- if (features & F_CMD_TOL)
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_tolerance[rdac]);
- if (features & F_CMD_OTP) {
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_otp_en[rdac]);
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_otp[rdac]);
+ int i;
+
+ for (i = 0; ad525x_attributes_commands[i]; i++) {
+ if (attr == ad525x_attributes_commands[i])
+ return true;
}
+
+ return false;
+}
+
+static umode_t ad525x_is_visible(struct kobject *kobj, struct attribute *attr,
+ int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct dpot_data *data = dev_get_drvdata(dev);
+ int rdac;
+
+ if (!data)
+ return 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_wipers);
+ if (rdac >= 0)
+ return data->wipers & BIT(rdac) ? attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_eeprom);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_EEP) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_tolerance);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_TOL) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_otp);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_otp_en);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ?
+ attr->mode : 0;
+
+ if (ad525x_is_command_attr(attr))
+ return data->feat & F_CMD_INC ? attr->mode : 0;
+
+ return attr->mode;
}
+static const struct attribute_group ad525x_group = {
+ .attrs = ad525x_attributes,
+ .is_visible = ad525x_is_visible,
+};
+
+const struct attribute_group *ad_dpot_groups[] = {
+ &ad525x_group,
+ NULL
+};
+EXPORT_SYMBOL(ad_dpot_groups);
+
int ad_dpot_probe(struct device *dev,
struct ad_dpot_bus_data *bdata, unsigned long devid,
const char *name)
{
struct dpot_data *data;
- int i, err = 0;
+ int i;
data = kzalloc_obj(struct dpot_data);
if (!data) {
- err = -ENOMEM;
- goto exit;
+ dev_err(dev, "failed to create client for %s ID 0x%lX\n",
+ name, devid);
+ return -ENOMEM;
}
dev_set_drvdata(dev, data);
@@ -705,51 +771,22 @@ int ad_dpot_probe(struct device *dev,
data->wipers = DPOT_WIPERS(devid);
for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i)) {
- err = ad_dpot_add_files(dev, data->feat, i);
- if (err)
- goto exit_remove_files;
+ if (data->wipers & BIT(i)) {
/* power-up midscale */
if (data->feat & F_RDACS_WONLY)
data->rdac_cache[i] = data->max_pos / 2;
}
- if (data->feat & F_CMD_INC)
- err = sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-
- if (err) {
- dev_err(dev, "failed to register sysfs hooks\n");
- goto exit_free;
- }
-
dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
name, data->max_pos);
return 0;
-
-exit_remove_files:
- for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i))
- ad_dpot_remove_files(dev, data->feat, i);
-
-exit_free:
- kfree(data);
- dev_set_drvdata(dev, NULL);
-exit:
- dev_err(dev, "failed to create client for %s ID 0x%lX\n",
- name, devid);
- return err;
}
EXPORT_SYMBOL(ad_dpot_probe);
void ad_dpot_remove(struct device *dev)
{
struct dpot_data *data = dev_get_drvdata(dev);
- int i;
-
- for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i))
- ad_dpot_remove_files(dev, data->feat, i);
kfree(data);
}
diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h
index 72a9d6801937..2e877c89523b 100644
--- a/drivers/misc/ad525x_dpot.h
+++ b/drivers/misc/ad525x_dpot.h
@@ -10,6 +10,8 @@
#include <linux/types.h>
+struct attribute_group;
+
#define DPOT_CONF(features, wipers, max_pos, uid) \
(((features) << 18) | (((wipers) & 0xFF) << 10) | \
((max_pos & 0xF) << 6) | (uid & 0x3F))
@@ -210,5 +212,6 @@ struct ad_dpot_bus_data {
int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata,
unsigned long devid, const char *name);
void ad_dpot_remove(struct device *dev);
+extern const struct attribute_group *ad_dpot_groups[];
#endif
diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig
index 30e7fad7356c..1a96b71f8506 100644
--- a/drivers/misc/amd-sbi/Kconfig
+++ b/drivers/misc/amd-sbi/Kconfig
@@ -20,3 +20,16 @@ config AMD_SBRMI_HWMON
This provides support for RMI device hardware monitoring. If enabled,
a hardware monitoring device will be created for each socket in
the system.
+
+config AMD_SBTSI
+ tristate "AMD side band TSI support"
+ depends on I3C_OR_I2C
+ depends on ARM || ARM64 || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ Enables support for the AMD SB-TSI (Side Band Temperature Sensor
+ Interface) driver, which provides access to emulated CPU temperature
+ sensors on AMD SoCs via an I2C/I3C connected BMC device.
+
+ This driver can also be built as a module. If so, the module will
+ be called sbtsi.
diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile
index 38eaaa651fd9..ce9321f5c601 100644
--- a/drivers/misc/amd-sbi/Makefile
+++ b/drivers/misc/amd-sbi/Makefile
@@ -2,3 +2,6 @@
sbrmi-i2c-objs += rmi-i2c.o rmi-core.o
sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON) += rmi-hwmon.o
obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o
+# SBTSI Configuration
+sbtsi-objs += tsi.o tsi-core.o
+obj-$(CONFIG_AMD_SBTSI) += sbtsi.o
diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c
index d4238ebad3c6..95c9109101b5 100644
--- a/drivers/misc/amd-sbi/rmi-core.c
+++ b/drivers/misc/amd-sbi/rmi-core.c
@@ -581,6 +581,8 @@ int create_misc_rmi_device(struct sbrmi_data *data,
GFP_KERNEL,
"sbrmi-%x",
data->dev_static_addr);
+ if (!data->sbrmi_misc_dev.name)
+ return -ENOMEM;
data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR;
data->sbrmi_misc_dev.fops = &sbrmi_fops;
data->sbrmi_misc_dev.parent = dev;
@@ -588,6 +590,8 @@ int create_misc_rmi_device(struct sbrmi_data *data,
GFP_KERNEL,
"sbrmi-%x",
data->dev_static_addr);
+ if (!data->sbrmi_misc_dev.nodename)
+ return -ENOMEM;
data->sbrmi_misc_dev.mode = 0600;
return misc_register(&data->sbrmi_misc_dev);
diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c
index 37e5ea83bf97..463a9a757464 100644
--- a/drivers/misc/amd-sbi/rmi-i2c.c
+++ b/drivers/misc/amd-sbi/rmi-i2c.c
@@ -141,8 +141,8 @@ static void sbrmi_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id sbrmi_id[] = {
- {"sbrmi-i2c"},
- {}
+ { .name = "sbrmi-i2c" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sbrmi_id);
@@ -226,7 +226,7 @@ static const struct i3c_device_id sbrmi_i3c_id[] = {
I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */
I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */
I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */
- {}
+ { }
};
MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id);
diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c
new file mode 100644
index 000000000000..5c178702c67a
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi-core.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tsi-core.c - file defining SB-TSI protocols compliant
+ * AMD SoC device.
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <uapi/misc/amd-apml.h>
+#include "tsi-core.h"
+
+static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data)
+{
+ return container_of(data, struct sbtsi_i3c_priv, data);
+}
+
+void sbtsi_data_release(struct kref *kref)
+{
+ struct sbtsi_data *data = container_of(kref, struct sbtsi_data, kref);
+
+ mutex_destroy(&data->lock);
+ if (data->is_i3c)
+ kfree(to_sbtsi_i3c_priv(data));
+ else
+ kfree(data);
+}
+
+/* I2C transfer function */
+static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+ if (is_read) {
+ int ret = i2c_smbus_read_byte_data(data->client, reg);
+
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return 0;
+ }
+ return i2c_smbus_write_byte_data(data->client, reg, *val);
+}
+
+/* I3C read transfer function */
+static int sbtsi_i3c_read(struct sbtsi_data *data, u8 reg, u8 *val)
+{
+ struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data);
+ struct i3c_xfer xfers[2] = { };
+ int ret;
+
+ priv->tx[0] = reg;
+
+ /* Write the register address (DMA_TO_DEVICE). */
+ xfers[0].rnw = false;
+ xfers[0].len = 1;
+ xfers[0].data.out = priv->tx;
+
+ /* Read the data byte into a separate buffer (DMA_FROM_DEVICE). */
+ xfers[1].rnw = true;
+ xfers[1].len = 1;
+ xfers[1].data.in = &priv->rx;
+
+ ret = i3c_device_do_xfers(data->i3cdev, xfers, 2, I3C_SDR);
+ if (ret)
+ return ret;
+
+ *val = priv->rx;
+ return ret;
+}
+
+/* I3C write transfer function */
+static int sbtsi_i3c_write(struct sbtsi_data *data, u8 reg, u8 val)
+{
+ struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data);
+ struct i3c_xfer xfers = {
+ .rnw = false,
+ .len = 2,
+ .data.out = priv->tx,
+ };
+
+ priv->tx[0] = reg;
+ priv->tx[1] = val;
+
+ return i3c_device_do_xfers(data->i3cdev, &xfers, 1, I3C_SDR);
+}
+
+/* Unified transfer function for I2C and I3C access */
+int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+ if (data->is_i3c)
+ return is_read ? sbtsi_i3c_read(data, reg, val)
+ : sbtsi_i3c_write(data, reg, *val);
+ return sbtsi_i2c_xfer(data, reg, val, is_read);
+}
+EXPORT_SYMBOL_GPL(sbtsi_xfer);
+
+/*
+ * The mutex protects against concurrent register transfers to the device
+ * over the shared bus.
+ */
+static int sbtsi_xfer_ioctl(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+ guard(sbtsi)(data);
+
+ if (data->detached)
+ return -ENODEV;
+
+ return sbtsi_xfer(data, reg, val, is_read);
+}
+
+static int apml_tsi_reg_xfer(struct sbtsi_data *data,
+ struct apml_tsi_xfer_msg __user *arg)
+{
+ struct apml_tsi_xfer_msg msg = { 0 };
+ int ret;
+
+ if (copy_from_user(&msg, arg, sizeof(struct apml_tsi_xfer_msg)))
+ return -EFAULT;
+
+ /*
+ * rflag is a boolean direction flag (0 = write, 1 = read). Reject
+ * any other value so the upper values stay reserved for future
+ * extensions instead of being silently treated as a read.
+ */
+ if (msg.pad || msg.rflag > 1)
+ return -EINVAL;
+
+ ret = sbtsi_xfer_ioctl(data, msg.reg_addr, &msg.data_in_out, msg.rflag);
+
+ if (msg.rflag && !ret) {
+ if (copy_to_user(arg, &msg, sizeof(struct apml_tsi_xfer_msg)))
+ return -EFAULT;
+ }
+ return ret;
+}
+
+static int sbtsi_open(struct inode *inode, struct file *fp)
+{
+ struct sbtsi_data *data;
+
+ data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev);
+ scoped_guard(sbtsi, data) {
+ if (data->detached)
+ return -ENODEV;
+ }
+
+ kref_get(&data->kref);
+
+ return 0;
+}
+
+static int sbtsi_release(struct inode *inode, struct file *fp)
+{
+ struct sbtsi_data *data;
+
+ data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev);
+ kref_put(&data->kref, sbtsi_data_release);
+ return 0;
+}
+
+static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct sbtsi_data *data;
+
+ data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev);
+ switch (cmd) {
+ case SBTSI_IOCTL_REG_XFER_CMD:
+ return apml_tsi_reg_xfer(data, argp);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations sbtsi_fops = {
+ .owner = THIS_MODULE,
+ .open = sbtsi_open,
+ .release = sbtsi_release,
+ .unlocked_ioctl = sbtsi_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+};
+
+int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev)
+{
+ int ret;
+
+ data->sbtsi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL,
+ "sbtsi-%x", data->dev_addr);
+ if (!data->sbtsi_misc_dev.name)
+ return -ENOMEM;
+ data->sbtsi_misc_dev.minor = MISC_DYNAMIC_MINOR;
+ data->sbtsi_misc_dev.fops = &sbtsi_fops;
+ data->sbtsi_misc_dev.parent = dev;
+ data->sbtsi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL,
+ "sbtsi-%x", data->dev_addr);
+ if (!data->sbtsi_misc_dev.nodename)
+ return -ENOMEM;
+ data->sbtsi_misc_dev.mode = 0600;
+
+ ret = misc_register(&data->sbtsi_misc_dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h
new file mode 100644
index 000000000000..4cf55c46230e
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi-core.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * AMD SBTSI core driver private definitions.
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _LINUX_TSI_CORE_H_
+#define _LINUX_TSI_CORE_H_
+
+#include <linux/cache.h>
+#include <linux/misc/tsi.h>
+
+/**
+ * struct sbtsi_i3c_priv - per-device state for I3C SBTSI (includes DMA-safe buffers)
+ * @data: public device state exposed via dev_set_drvdata()
+ * @tx: outgoing I3C bytes (DMA_TO_DEVICE); [0] register address, [1] value
+ * @rx: incoming I3C data byte (DMA_FROM_DEVICE)
+ */
+struct sbtsi_i3c_priv {
+ struct sbtsi_data data;
+ u8 tx[2];
+ u8 rx __aligned(ARCH_DMA_MINALIGN);
+};
+
+int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev);
+
+void sbtsi_data_release(struct kref *kref);
+#endif /* _LINUX_TSI_CORE_H_ */
diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c
new file mode 100644
index 000000000000..f06f417f451c
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tsi.c - AMD SBTSI I2C/I3C core driver. Probes the SBTSI device over I2C/I3C
+ * and publishes an auxiliary device on the auxiliary bus.
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include "tsi-core.h"
+
+#define SBTSI_REG_CONFIG 0x03 /* RO */
+
+/*
+ * Bit for reporting value with temperature measurement range.
+ * bit == 0: Use default temperature range (0C to 255.875C).
+ * bit == 1: Use extended temperature range (-49C to +206.875C).
+ */
+#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2
+
+/*
+ * ReadOrder bit specifies the reading order of integer and decimal part of
+ * CPU temperature for atomic reads. If bit == 0, reading integer part triggers
+ * latching of the decimal part, so integer part should be read first.
+ */
+#define SBTSI_CONFIG_READ_ORDER_SHIFT 5
+
+static void sbtsi_adev_release(struct device *dev)
+{
+ kfree(to_auxiliary_dev(dev));
+}
+
+static void sbtsi_unregister_hwmon_adev(void *_adev)
+{
+ struct auxiliary_device *adev = _adev;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static void sbtsi_misc_unregister(void *arg)
+{
+ struct sbtsi_data *data = arg;
+
+ misc_deregister(&data->sbtsi_misc_dev);
+
+ guard(sbtsi)(data);
+ data->detached = true;
+}
+
+static void sbtsi_driver_unref(void *arg)
+{
+ struct sbtsi_data *data = arg;
+
+ kref_put(&data->kref, sbtsi_data_release);
+}
+
+/*
+ * Create and publish an auxiliary device. The hwmon driver in
+ * drivers/hwmon/sbtsi_temp.c binds to this device.
+ *
+ * @dev: I2C device (parent of the auxiliary device)
+ * @dev_addr: I2C address — used as the auxiliary device instance ID so that
+ * each socket gets a unique name.
+ */
+static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = kzalloc_obj(*adev);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->name = AMD_SBTSI_AUX_HWMON;
+ adev->id = dev_addr;
+ adev->dev.parent = dev;
+ adev->dev.release = sbtsi_adev_release;
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ kfree(adev);
+ return ret;
+ }
+
+ ret = __auxiliary_device_add(adev, AMD_SBTSI_ADEV);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev);
+}
+
+static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data)
+{
+ u8 val;
+ int err;
+
+ mutex_init(&data->lock);
+ kref_init(&data->kref);
+
+ err = devm_add_action_or_reset(dev, sbtsi_driver_unref, data);
+ if (err)
+ return err;
+
+ err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true);
+ if (err)
+ return err;
+
+ data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val);
+ data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val);
+
+ dev_set_drvdata(dev, data);
+ err = sbtsi_create_hwmon_adev(dev, data->dev_addr);
+ if (err < 0)
+ return err;
+
+ err = create_misc_tsi_device(data, dev);
+ if (err)
+ return err;
+
+ return devm_add_action_or_reset(dev, sbtsi_misc_unregister, data);
+}
+
+static int sbtsi_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct sbtsi_data *data;
+
+ data = kzalloc_obj(*data);
+ if (!data)
+ return -ENOMEM;
+
+ data->is_i3c = false;
+ data->client = client;
+
+ /* In a multi-socket system, devices that are otherwise identical do not
+ * share the same static address; each instance resides at a unique I2C
+ * client address on the same or different bus. Use the I2C client
+ * address as the auxiliary device instance ID to ensure each socket
+ * receives a distinct auxiliary device name.
+ */
+ data->dev_addr = client->addr;
+ return sbtsi_probe_common(dev, data);
+}
+
+static const struct i2c_device_id sbtsi_id[] = {
+ { .name = "sbtsi" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sbtsi_id);
+
+static const struct of_device_id __maybe_unused sbtsi_of_match[] = {
+ {
+ .compatible = "amd,sbtsi",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sbtsi_of_match);
+
+static struct i2c_driver sbtsi_driver = {
+ .driver = {
+ .name = "sbtsi-i2c",
+ .of_match_table = of_match_ptr(sbtsi_of_match),
+ },
+ .probe = sbtsi_i2c_probe,
+ .id_table = sbtsi_id,
+};
+
+static int sbtsi_i3c_probe(struct i3c_device *i3cdev)
+{
+ struct device *dev = i3cdev_to_dev(i3cdev);
+ struct i3c_device_info devinfo;
+ struct sbtsi_i3c_priv *i3c_priv;
+ struct sbtsi_data *data;
+
+ /*
+ * AMD OOB devices differ on basis of Instance ID,
+ * for SBTSI, instance ID is 0.
+ * As the device Id match is not on basis of Instance ID,
+ * add the below check to probe the SBTSI device only and
+ * not other OOB devices.
+ */
+ i3c_device_get_info(i3cdev, &devinfo);
+ if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0)
+ return -ENXIO;
+
+ i3c_priv = kzalloc_obj(*i3c_priv);
+ if (!i3c_priv)
+ return -ENOMEM;
+
+ data = &i3c_priv->data;
+ data->i3cdev = i3cdev;
+ data->is_i3c = true;
+ /*
+ * In a multi-socket system, otherwise identical devices do not share
+ * the same address; each instance is enumerated with a distinct dynamic
+ * (assigned) address on the I3C bus. Use that address (passed in as
+ * dev_addr) as the auxiliary device instance ID so that every socket
+ * gets a unique auxiliary device name.
+ */
+ data->dev_addr = devinfo.dyn_addr;
+
+ return sbtsi_probe_common(dev, data);
+}
+
+static const struct i3c_device_id sbtsi_i3c_id[] = {
+ /* PID for AMD SBTSI device */
+ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL), /* Socket:0, Turin and Genoa */
+ I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */
+ I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */
+ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */
+ I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */
+ {}
+};
+MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id);
+
+static struct i3c_driver sbtsi_i3c_driver = {
+ .driver = {
+ .name = "sbtsi-i3c",
+ },
+ .probe = sbtsi_i3c_probe,
+ .id_table = sbtsi_i3c_id,
+};
+
+module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver);
+
+MODULE_DESCRIPTION("AMD SB-TSI I2C/I3C core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c
index fc504bd8d916..1488e28819b7 100644
--- a/drivers/misc/apds9802als.c
+++ b/drivers/misc/apds9802als.c
@@ -285,7 +285,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend,
#endif /* CONFIG_PM */
static const struct i2c_device_id apds9802als_id[] = {
- { DRIVER_NAME },
+ { .name = DRIVER_NAME },
{ }
};
diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c
index 5773ffb46f0f..88ee3a417616 100644
--- a/drivers/misc/bcm-vk/bcm_vk_dev.c
+++ b/drivers/misc/bcm-vk/bcm_vk_dev.c
@@ -1370,21 +1370,15 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_msgq_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
- if (err) {
- dev_err(dev, "failed to request msgq IRQ %d for MSIX %d\n",
- pdev->irq + vk->num_irqs, vk->num_irqs + 1);
+ if (err)
goto err_irq;
- }
}
/* one irq for notification from VK */
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_notf_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
- if (err) {
- dev_err(dev, "failed to request notf IRQ %d for MSIX %d\n",
- pdev->irq + vk->num_irqs, vk->num_irqs + 1);
+ if (err)
goto err_irq;
- }
vk->num_irqs++;
for (i = 0;
@@ -1393,11 +1387,8 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_tty_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
- if (err) {
- dev_err(dev, "failed request tty IRQ %d for MSIX %d\n",
- pdev->irq + vk->num_irqs, vk->num_irqs + 1);
+ if (err)
goto err_irq;
- }
bcm_vk_tty_set_irq_enabled(vk, i);
}
@@ -1632,7 +1623,7 @@ static void bcm_vk_shutdown(struct pci_dev *pdev)
}
static const struct pci_device_id bcm_vk_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE), },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE) },
{ }
};
MODULE_DEVICE_TABLE(pci, bcm_vk_ids);
diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c
index 3916ec07ecad..17114092a284 100644
--- a/drivers/misc/bcm-vk/bcm_vk_msg.c
+++ b/drivers/misc/bcm-vk/bcm_vk_msg.c
@@ -9,6 +9,7 @@
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/poll.h>
#include <linux/sizes.h>
#include <linux/spinlock.h>
@@ -108,7 +109,8 @@ u32 msgq_avail_space(const struct bcm_vk_msgq __iomem *msgq,
bool bcm_vk_drv_access_ok(struct bcm_vk *vk)
{
- return (!!atomic_read(&vk->msgq_inited));
+ /* Pair with the release store after message queue initialization. */
+ return !!atomic_read_acquire(&vk->msgq_inited);
}
void bcm_vk_set_host_alert(struct bcm_vk *vk, u32 bit_mask)
@@ -501,7 +503,8 @@ int bcm_vk_sync_msgq(struct bcm_vk *vk, bool force_sync)
msgq++;
}
}
- atomic_set(&vk->msgq_inited, 1);
+ /* Publish message queue info before allowing driver access. */
+ atomic_set_release(&vk->msgq_inited, 1);
return ret;
}
@@ -1088,6 +1091,7 @@ ssize_t bcm_vk_write(struct file *p_file,
u32 q_num;
u32 msg_size;
u32 msgq_size;
+ size_t entry_size;
if (!bcm_vk_drv_access_ok(vk))
return -EPERM;
@@ -1095,20 +1099,26 @@ ssize_t bcm_vk_write(struct file *p_file,
dev_dbg(dev, "Msg count %zu\n", count);
/* first, do sanity check where count should be multiple of basic blk */
- if (count & (VK_MSGQ_BLK_SIZE - 1)) {
- dev_err(dev, "Failure with size %zu not multiple of %zu\n",
+ if (!count || count & (VK_MSGQ_BLK_SIZE - 1)) {
+ dev_err(dev, "Failure with size %zu not a positive multiple of %zu\n",
count, VK_MSGQ_BLK_SIZE);
rc = -EINVAL;
goto write_err;
}
+ if (check_add_overflow(sizeof(*entry), count, &entry_size) ||
+ check_add_overflow(entry_size, vk->ib_sgl_size, &entry_size)) {
+ rc = -EOVERFLOW;
+ goto write_err;
+ }
+
/* allocate the work entry + buffer for size count and inband sgl */
- entry = kzalloc(sizeof(*entry) + count + vk->ib_sgl_size,
- GFP_KERNEL);
+ entry = kzalloc(entry_size, GFP_KERNEL);
if (!entry) {
rc = -ENOMEM;
goto write_err;
}
+ entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT;
/* now copy msg from user space, and then formulate the work entry */
if (copy_from_user(&entry->to_v_msg[0], buf, count)) {
@@ -1116,7 +1126,6 @@ ssize_t bcm_vk_write(struct file *p_file,
goto write_free_ent;
}
- entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT;
entry->ctx = ctx;
/* do a check on the blk size which could not exceed queue space */
@@ -1353,4 +1362,3 @@ void bcm_vk_msg_remove(struct bcm_vk *vk)
bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_v_msg_chan, NULL);
bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_h_msg_chan, NULL);
}
-
diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c
index 45f8fc69a711..98fc5970b2bb 100644
--- a/drivers/misc/bh1770glc.c
+++ b/drivers/misc/bh1770glc.c
@@ -1363,9 +1363,9 @@ static int bh1770_runtime_resume(struct device *dev)
#endif
static const struct i2c_device_id bh1770_id[] = {
- { "bh1770glc" },
- { "sfh7770" },
- {}
+ { .name = "bh1770glc" },
+ { .name = "sfh7770" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, bh1770_id);
diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c
index 8e7ea2c9142d..9e0f4f91763e 100644
--- a/drivers/misc/cardreader/alcor_pci.c
+++ b/drivers/misc/cardreader/alcor_pci.c
@@ -47,7 +47,7 @@ static const struct pci_device_id pci_ids[] = {
.driver_data = (kernel_ulong_t)&au6621_cfg },
{ PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6625),
.driver_data = (kernel_ulong_t)&au6625_cfg },
- {},
+ { }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c
index c4d54ca2fa80..c6e602523538 100644
--- a/drivers/misc/cardreader/rtsx_pcr.c
+++ b/drivers/misc/cardreader/rtsx_pcr.c
@@ -1196,6 +1196,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
/* Gating real mcu clock */
err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1,
RTS5261_MCU_CLOCK_GATING, 0);
+ if (err < 0)
+ return err;
err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL,
SSC_POWER_DOWN, 0);
} else {
diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c
index 2dd212f04fed..8e0d11e59c4e 100644
--- a/drivers/misc/cb710/core.c
+++ b/drivers/misc/cb710/core.c
@@ -292,9 +292,8 @@ static void cb710_remove_one(struct pci_dev *pdev)
}
static const struct pci_device_id cb710_pci_tbl[] = {
- { PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH,
- PCI_ANY_ID, PCI_ANY_ID, },
- { 0, }
+ { PCI_DEVICE(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH) },
+ { }
};
static SIMPLE_DEV_PM_OPS(cb710_pm_ops, cb710_suspend, cb710_resume);
diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c
index cb09e056531a..4500d7d0ea77 100644
--- a/drivers/misc/ds1682.c
+++ b/drivers/misc/ds1682.c
@@ -271,14 +271,14 @@ static void ds1682_remove(struct i2c_client *client)
}
static const struct i2c_device_id ds1682_id[] = {
- { "ds1682" },
+ { .name = "ds1682" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1682_id);
static const struct of_device_id ds1682_of_match[] = {
- { .compatible = "dallas,ds1682", },
- {}
+ { .compatible = "dallas,ds1682" },
+ { }
};
MODULE_DEVICE_TABLE(of, ds1682_of_match);
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 4d0ce47aa282..d6af02588c66 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -1,52 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "EEPROM support"
-config EEPROM_AT24
- tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
- depends on I2C && SYSFS
- select NVMEM
- select NVMEM_SYSFS
- select REGMAP
- select REGMAP_I2C
- help
- Enable this driver to get read/write support to most I2C EEPROMs
- and compatible devices like FRAMs, SRAMs, ROMs etc. After you
- configure the driver to know about each chip on your target
- board. Use these generic chip names, instead of vendor-specific
- ones like at24c64, 24lc02 or fm24c04:
-
- 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
- 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048
-
- Unless you like data loss puzzles, always be sure that any chip
- you configure as a 24c32 (32 kbit) or larger is NOT really a
- 24c16 (16 kbit) or smaller, and vice versa. Marking the chip
- as read-only won't help recover from this. Also, if your chip
- has any software write-protect mechanism you may want to review the
- code to make sure this driver won't turn it on by accident.
-
- If you use this with an SMBus adapter instead of an I2C adapter,
- full functionality is not available. Only smaller devices are
- supported (24c16 and below, max 4 kByte).
-
- This driver can also be built as a module. If so, the module
- will be called at24.
-
-config EEPROM_AT25
- tristate "SPI EEPROMs (FRAMs) from most vendors"
- depends on SPI && SYSFS
- select NVMEM
- select NVMEM_SYSFS
- select SPI_MEM
- help
- Enable this driver to get read/write support to most SPI EEPROMs
- and Cypress FRAMs,
- after you configure the board init code to know about each eeprom
- on your target board.
-
- This driver can also be built as a module. If so, the module
- will be called at25.
-
config EEPROM_MAX6875
tristate "Maxim MAX6874/5 power supply supervisor"
depends on I2C
@@ -69,22 +23,6 @@ config EEPROM_93CX6
If unsure, say N.
-config EEPROM_93XX46
- tristate "Microwire EEPROM 93XX46 support"
- depends on SPI && SYSFS
- select REGMAP
- select NVMEM
- select NVMEM_SYSFS
- help
- Driver for the microwire EEPROM chipsets 93xx46x. The driver
- supports both read and write commands and also the command to
- erase the whole EEPROM.
-
- This driver can also be built as a module. If so, the module
- will be called eeprom_93xx46.
-
- If unsure, say N.
-
config EEPROM_DIGSY_MTC_CFG
bool "DigsyMTC display configuration EEPROMs device"
depends on GPIO_MPC5200 && SPI_GPIO
@@ -107,35 +45,4 @@ config EEPROM_IDT_89HPESX
This driver can also be built as a module. If so, the module
will be called idt_89hpesx.
-config EEPROM_EE1004
- tristate "SPD EEPROMs on DDR4 memory modules"
- depends on I2C && SYSFS
- select NVMEM
- select NVMEM_SYSFS
- help
- Enable this driver to get read support to SPD EEPROMs following
- the JEDEC EE1004 standard. These are typically found on DDR4
- SDRAM memory modules.
-
- This driver can also be built as a module. If so, the module
- will be called ee1004.
-
-config EEPROM_M24LR
- tristate "STMicroelectronics M24LR RFID/NFC EEPROM support"
- depends on I2C && SYSFS
- select REGMAP_I2C
- select NVMEM
- select NVMEM_SYSFS
- help
- This enables support for STMicroelectronics M24LR RFID/NFC EEPROM
- chips. These dual-interface devices expose two I2C addresses:
- one for EEPROM memory access and another for control and system
- configuration (e.g. UID, password handling).
-
- This driver provides a sysfs interface for control functions and
- integrates with the nvmem subsystem for EEPROM access.
-
- To compile this driver as a module, choose M here: the
- module will be called m24lr.
-
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 8f311fd6a4ce..44a20cf025a1 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1,10 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_EEPROM_AT24) += at24.o
-obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
-obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o
-obj-$(CONFIG_EEPROM_EE1004) += ee1004.o
-obj-$(CONFIG_EEPROM_M24LR) += m24lr.o
diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c
index e056d2dea8c3..ea7478cfdd27 100644
--- a/drivers/misc/eeprom/idt_89hpesx.c
+++ b/drivers/misc/eeprom/idt_89hpesx.c
@@ -1375,12 +1375,12 @@ static void idt_remove(struct i2c_client *client)
* ee_ids - array of supported EEPROMs
*/
static const struct i2c_device_id ee_ids[] = {
- { "24c32", 4096},
- { "24c64", 8192},
- { "24c128", 16384},
- { "24c256", 32768},
- { "24c512", 65536},
- {}
+ { .name = "24c32", .driver_data = 4096 },
+ { .name = "24c64", .driver_data = 8192 },
+ { .name = "24c128", .driver_data = 16384 },
+ { .name = "24c256", .driver_data = 32768 },
+ { .name = "24c512", .driver_data = 65536 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ee_ids);
@@ -1388,115 +1388,115 @@ MODULE_DEVICE_TABLE(i2c, ee_ids);
* idt_ids - supported IDT 89HPESx devices
*/
static const struct i2c_device_id idt_ids[] = {
- { "89hpes8nt2" },
- { "89hpes12nt3" },
-
- { "89hpes24nt6ag2" },
- { "89hpes32nt8ag2" },
- { "89hpes32nt8bg2" },
- { "89hpes12nt12g2" },
- { "89hpes16nt16g2" },
- { "89hpes24nt24g2" },
- { "89hpes32nt24ag2" },
- { "89hpes32nt24bg2" },
-
- { "89hpes12n3" },
- { "89hpes12n3a" },
- { "89hpes24n3" },
- { "89hpes24n3a" },
-
- { "89hpes32h8" },
- { "89hpes32h8g2" },
- { "89hpes48h12" },
- { "89hpes48h12g2" },
- { "89hpes48h12ag2" },
- { "89hpes16h16" },
- { "89hpes22h16" },
- { "89hpes22h16g2" },
- { "89hpes34h16" },
- { "89hpes34h16g2" },
- { "89hpes64h16" },
- { "89hpes64h16g2" },
- { "89hpes64h16ag2" },
-
- /* { "89hpes3t3" }, // No SMBus-slave iface */
- { "89hpes12t3g2" },
- { "89hpes24t3g2" },
- /* { "89hpes4t4" }, // No SMBus-slave iface */
- { "89hpes16t4" },
- { "89hpes4t4g2" },
- { "89hpes10t4g2" },
- { "89hpes16t4g2" },
- { "89hpes16t4ag2" },
- { "89hpes5t5" },
- { "89hpes6t5" },
- { "89hpes8t5" },
- { "89hpes8t5a" },
- { "89hpes24t6" },
- { "89hpes6t6g2" },
- { "89hpes24t6g2" },
- { "89hpes16t7" },
- { "89hpes32t8" },
- { "89hpes32t8g2" },
- { "89hpes48t12" },
- { "89hpes48t12g2" },
+ { .name = "89hpes8nt2" },
+ { .name = "89hpes12nt3" },
+
+ { .name = "89hpes24nt6ag2" },
+ { .name = "89hpes32nt8ag2" },
+ { .name = "89hpes32nt8bg2" },
+ { .name = "89hpes12nt12g2" },
+ { .name = "89hpes16nt16g2" },
+ { .name = "89hpes24nt24g2" },
+ { .name = "89hpes32nt24ag2" },
+ { .name = "89hpes32nt24bg2" },
+
+ { .name = "89hpes12n3" },
+ { .name = "89hpes12n3a" },
+ { .name = "89hpes24n3" },
+ { .name = "89hpes24n3a" },
+
+ { .name = "89hpes32h8" },
+ { .name = "89hpes32h8g2" },
+ { .name = "89hpes48h12" },
+ { .name = "89hpes48h12g2" },
+ { .name = "89hpes48h12ag2" },
+ { .name = "89hpes16h16" },
+ { .name = "89hpes22h16" },
+ { .name = "89hpes22h16g2" },
+ { .name = "89hpes34h16" },
+ { .name = "89hpes34h16g2" },
+ { .name = "89hpes64h16" },
+ { .name = "89hpes64h16g2" },
+ { .name = "89hpes64h16ag2" },
+
+ /* { .name = "89hpes3t3" }, // No SMBus-slave iface */
+ { .name = "89hpes12t3g2" },
+ { .name = "89hpes24t3g2" },
+ /* { .name = "89hpes4t4" }, // No SMBus-slave iface */
+ { .name = "89hpes16t4" },
+ { .name = "89hpes4t4g2" },
+ { .name = "89hpes10t4g2" },
+ { .name = "89hpes16t4g2" },
+ { .name = "89hpes16t4ag2" },
+ { .name = "89hpes5t5" },
+ { .name = "89hpes6t5" },
+ { .name = "89hpes8t5" },
+ { .name = "89hpes8t5a" },
+ { .name = "89hpes24t6" },
+ { .name = "89hpes6t6g2" },
+ { .name = "89hpes24t6g2" },
+ { .name = "89hpes16t7" },
+ { .name = "89hpes32t8" },
+ { .name = "89hpes32t8g2" },
+ { .name = "89hpes48t12" },
+ { .name = "89hpes48t12g2" },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, idt_ids);
static const struct of_device_id idt_of_match[] = {
- { .compatible = "idt,89hpes8nt2", },
- { .compatible = "idt,89hpes12nt3", },
-
- { .compatible = "idt,89hpes24nt6ag2", },
- { .compatible = "idt,89hpes32nt8ag2", },
- { .compatible = "idt,89hpes32nt8bg2", },
- { .compatible = "idt,89hpes12nt12g2", },
- { .compatible = "idt,89hpes16nt16g2", },
- { .compatible = "idt,89hpes24nt24g2", },
- { .compatible = "idt,89hpes32nt24ag2", },
- { .compatible = "idt,89hpes32nt24bg2", },
-
- { .compatible = "idt,89hpes12n3", },
- { .compatible = "idt,89hpes12n3a", },
- { .compatible = "idt,89hpes24n3", },
- { .compatible = "idt,89hpes24n3a", },
-
- { .compatible = "idt,89hpes32h8", },
- { .compatible = "idt,89hpes32h8g2", },
- { .compatible = "idt,89hpes48h12", },
- { .compatible = "idt,89hpes48h12g2", },
- { .compatible = "idt,89hpes48h12ag2", },
- { .compatible = "idt,89hpes16h16", },
- { .compatible = "idt,89hpes22h16", },
- { .compatible = "idt,89hpes22h16g2", },
- { .compatible = "idt,89hpes34h16", },
- { .compatible = "idt,89hpes34h16g2", },
- { .compatible = "idt,89hpes64h16", },
- { .compatible = "idt,89hpes64h16g2", },
- { .compatible = "idt,89hpes64h16ag2", },
-
- { .compatible = "idt,89hpes12t3g2", },
- { .compatible = "idt,89hpes24t3g2", },
-
- { .compatible = "idt,89hpes16t4", },
- { .compatible = "idt,89hpes4t4g2", },
- { .compatible = "idt,89hpes10t4g2", },
- { .compatible = "idt,89hpes16t4g2", },
- { .compatible = "idt,89hpes16t4ag2", },
- { .compatible = "idt,89hpes5t5", },
- { .compatible = "idt,89hpes6t5", },
- { .compatible = "idt,89hpes8t5", },
- { .compatible = "idt,89hpes8t5a", },
- { .compatible = "idt,89hpes24t6", },
- { .compatible = "idt,89hpes6t6g2", },
- { .compatible = "idt,89hpes24t6g2", },
- { .compatible = "idt,89hpes16t7", },
- { .compatible = "idt,89hpes32t8", },
- { .compatible = "idt,89hpes32t8g2", },
- { .compatible = "idt,89hpes48t12", },
- { .compatible = "idt,89hpes48t12g2", },
- { },
+ { .compatible = "idt,89hpes8nt2" },
+ { .compatible = "idt,89hpes12nt3" },
+
+ { .compatible = "idt,89hpes24nt6ag2" },
+ { .compatible = "idt,89hpes32nt8ag2" },
+ { .compatible = "idt,89hpes32nt8bg2" },
+ { .compatible = "idt,89hpes12nt12g2" },
+ { .compatible = "idt,89hpes16nt16g2" },
+ { .compatible = "idt,89hpes24nt24g2" },
+ { .compatible = "idt,89hpes32nt24ag2" },
+ { .compatible = "idt,89hpes32nt24bg2" },
+
+ { .compatible = "idt,89hpes12n3" },
+ { .compatible = "idt,89hpes12n3a" },
+ { .compatible = "idt,89hpes24n3" },
+ { .compatible = "idt,89hpes24n3a" },
+
+ { .compatible = "idt,89hpes32h8" },
+ { .compatible = "idt,89hpes32h8g2" },
+ { .compatible = "idt,89hpes48h12" },
+ { .compatible = "idt,89hpes48h12g2" },
+ { .compatible = "idt,89hpes48h12ag2" },
+ { .compatible = "idt,89hpes16h16" },
+ { .compatible = "idt,89hpes22h16" },
+ { .compatible = "idt,89hpes22h16g2" },
+ { .compatible = "idt,89hpes34h16" },
+ { .compatible = "idt,89hpes34h16g2" },
+ { .compatible = "idt,89hpes64h16" },
+ { .compatible = "idt,89hpes64h16g2" },
+ { .compatible = "idt,89hpes64h16ag2" },
+
+ { .compatible = "idt,89hpes12t3g2" },
+ { .compatible = "idt,89hpes24t3g2" },
+
+ { .compatible = "idt,89hpes16t4" },
+ { .compatible = "idt,89hpes4t4g2" },
+ { .compatible = "idt,89hpes10t4g2" },
+ { .compatible = "idt,89hpes16t4g2" },
+ { .compatible = "idt,89hpes16t4ag2" },
+ { .compatible = "idt,89hpes5t5" },
+ { .compatible = "idt,89hpes6t5" },
+ { .compatible = "idt,89hpes8t5" },
+ { .compatible = "idt,89hpes8t5a" },
+ { .compatible = "idt,89hpes24t6" },
+ { .compatible = "idt,89hpes6t6g2" },
+ { .compatible = "idt,89hpes24t6g2" },
+ { .compatible = "idt,89hpes16t7" },
+ { .compatible = "idt,89hpes32t8" },
+ { .compatible = "idt,89hpes32t8g2" },
+ { .compatible = "idt,89hpes48t12" },
+ { .compatible = "idt,89hpes48t12g2" },
+ { }
};
MODULE_DEVICE_TABLE(of, idt_of_match);
diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c
index 5731d2dc8a57..be5e6d19be10 100644
--- a/drivers/misc/eeprom/max6875.c
+++ b/drivers/misc/eeprom/max6875.c
@@ -183,7 +183,7 @@ static void max6875_remove(struct i2c_client *client)
}
static const struct i2c_device_id max6875_id[] = {
- { "max6875" },
+ { .name = "max6875" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6875_id);
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
index cf6382981777..de457378c501 100644
--- a/drivers/misc/enclosure.c
+++ b/drivers/misc/enclosure.c
@@ -184,8 +184,8 @@ EXPORT_SYMBOL_GPL(enclosure_unregister);
static void enclosure_link_name(struct enclosure_component *cdev, char *name)
{
- strcpy(name, "enclosure_device:");
- strcat(name, dev_name(&cdev->cdev));
+ snprintf(name, ENCLOSURE_NAME_SIZE, "enclosure_device:%s",
+ dev_name(&cdev->cdev));
}
static void enclosure_remove_links(struct enclosure_component *cdev)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index eb6c2a78d3c7..90fd669636ec 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -22,7 +22,11 @@
#include <linux/firmware/qcom/qcom_scm.h>
#include <uapi/misc/fastrpc.h>
#include <linux/of_reserved_mem.h>
+#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/iopoll.h>
#define ADSP_DOMAIN_ID (0)
#define MDSP_DOMAIN_ID (1)
@@ -37,7 +41,27 @@
#define FASTRPC_CTX_MAX (256)
#define FASTRPC_INIT_HANDLE 1
#define FASTRPC_DSP_UTILITIES_HANDLE 2
-#define FASTRPC_CTXID_MASK (0xFF0)
+/*
+ * Maximum handle value for static handles.
+ * Static handles are pre-defined, fixed numeric values statically assigned
+ * in the IDL file or FastRPC framework.
+ */
+#define FASTRPC_MAX_STATIC_HANDLE (20)
+#define FASTRPC_CTXID_MASK GENMASK(15, 8)
+/* Sequence number occupies bits 63:16 of the ctxid / message context */
+#define FASTRPC_CTXID_SEQ_SHIFT 16
+#define FASTRPC_CTXID_SEQ_MASK GENMASK_ULL(63, 16)
+
+/*
+ * Newer DSP firmware implements a PD (Protection Domain) notification
+ * framework that sends PD state notifications upon request. The PD exit
+ * notification is unconditionally sent by the DSP with this fixed sentinel
+ * in the context field rather than the context of an outstanding invocation.
+ * Since the fastrpc driver does not support the DSP PD notification framework,
+ * this message must be dropped rather than matched against the context idr.
+ */
+#define FASTRPC_DSP_PD_NOTIFY_CTX 0xABCDABCD
+
#define INIT_FILELEN_MAX (2 * 1024 * 1024)
#define INIT_FILE_NAMELEN_MAX (128)
#define FASTRPC_DEVICE_NAME "fastrpc"
@@ -105,6 +129,12 @@
#define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev)
+/* Poll response number from remote processor for call completion */
+#define FASTRPC_POLL_RESPONSE (0xdecaf)
+
+/* Polling mode timeout limit */
+#define FASTRPC_POLL_MAX_TIMEOUT_US (10000)
+
struct fastrpc_phy_page {
dma_addr_t addr; /* dma address */
u64 size; /* size of contiguous region */
@@ -233,9 +263,16 @@ struct fastrpc_invoke_ctx {
int pid;
int client_id;
u32 sc;
+ u64 *fdlist;
u32 *crc;
+ /* Poll memory that DSP updates */
+ u32 *poll_addr;
u64 ctxid;
u64 msg_sz;
+ /* work done status flag */
+ bool is_work_done;
+ /* process updates poll memory instead of glink response */
+ bool is_polled;
struct kref refcount;
struct list_head node; /* list of ctxs */
struct completion work;
@@ -261,6 +298,7 @@ struct fastrpc_soc_data {
u32 sid_pos;
u32 dma_addr_bits_cdsp;
u32 dma_addr_bits_default;
+ bool poll_mode_supported;
};
struct fastrpc_channel_ctx {
@@ -283,6 +321,9 @@ struct fastrpc_channel_ctx {
struct list_head invoke_interrupted_mmaps;
bool secure;
bool unsigned_support;
+ bool poll_mode_supported;
+ /* Per-channel sequence counter; incremented on every context allocation */
+ atomic_t ctx_seq;
u64 dma_mask;
const struct fastrpc_soc_data *soc_data;
};
@@ -306,6 +347,8 @@ struct fastrpc_user {
int client_id;
int pd;
bool is_secure_dev;
+ /* Flags poll mode state */
+ bool poll_mode;
/* Lock for lists */
spinlock_t lock;
/* lock for allocations */
@@ -422,6 +465,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
static void fastrpc_buf_free(struct fastrpc_buf *buf)
{
+ if (!buf)
+ return;
+
dma_free_coherent(buf->dev, buf->size, buf->virt,
fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr));
kfree(buf);
@@ -515,8 +561,7 @@ static void fastrpc_user_free(struct kref *ref)
struct fastrpc_map *map, *m;
struct fastrpc_buf *buf, *b;
- if (fl->init_mem)
- fastrpc_buf_free(fl->init_mem);
+ fastrpc_buf_free(fl->init_mem);
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
list_del(&ctx->node);
@@ -561,11 +606,10 @@ static void fastrpc_context_free(struct kref *ref)
for (i = 0; i < ctx->nbufs; i++)
fastrpc_map_put(ctx->maps[i]);
- if (ctx->buf)
- fastrpc_buf_free(ctx->buf);
+ fastrpc_buf_free(ctx->buf);
spin_lock_irqsave(&cctx->lock, flags);
- idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4);
+ idr_remove(&cctx->ctx_idr, FIELD_GET(FASTRPC_CTXID_MASK, ctx->ctxid));
spin_unlock_irqrestore(&cctx->lock, flags);
kfree(ctx->maps);
@@ -703,7 +747,9 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
spin_unlock_irqrestore(&cctx->lock, flags);
goto err_idr;
}
- ctx->ctxid = ret << 4;
+ ctx->ctxid = FIELD_PREP(FASTRPC_CTXID_MASK, ret) |
+ FIELD_PREP(FASTRPC_CTXID_SEQ_MASK,
+ (u64)atomic_inc_return(&cctx->ctx_seq));
spin_unlock_irqrestore(&cctx->lock, flags);
kref_init(&ctx->refcount);
@@ -968,7 +1014,8 @@ static int fastrpc_get_meta_size(struct fastrpc_invoke_ctx *ctx)
sizeof(struct fastrpc_invoke_buf) +
sizeof(struct fastrpc_phy_page)) * ctx->nscalars +
sizeof(u64) * FASTRPC_MAX_FDLIST +
- sizeof(u32) * FASTRPC_MAX_CRCLIST;
+ sizeof(u32) * FASTRPC_MAX_CRCLIST +
+ sizeof(u32);
return size;
}
@@ -1063,6 +1110,10 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
rpra = ctx->buf->virt;
list = fastrpc_invoke_buf_start(rpra, ctx->nscalars);
pages = fastrpc_phy_page_start(list, ctx->nscalars);
+ ctx->fdlist = (u64 *)(pages + ctx->nscalars);
+ ctx->poll_addr = (u32 *)((uintptr_t)ctx->fdlist + sizeof(u64) * FASTRPC_MAX_FDLIST +
+ sizeof(u32) * FASTRPC_MAX_CRCLIST);
+
args = (uintptr_t)ctx->buf->virt + metalen;
rlen = pkt_size - metalen;
ctx->rpra = rpra;
@@ -1165,18 +1216,11 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx,
union fastrpc_remote_arg *rpra = ctx->rpra;
struct fastrpc_user *fl = ctx->fl;
struct fastrpc_map *mmap = NULL;
- struct fastrpc_invoke_buf *list;
- struct fastrpc_phy_page *pages;
- u64 *fdlist;
- int i, inbufs, outbufs, handles;
+ u64 *fdlist = ctx->fdlist;
+ int i, inbufs;
int ret = 0;
inbufs = REMOTE_SCALARS_INBUFS(ctx->sc);
- outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc);
- handles = REMOTE_SCALARS_INHANDLES(ctx->sc) + REMOTE_SCALARS_OUTHANDLES(ctx->sc);
- list = fastrpc_invoke_buf_start(rpra, ctx->nscalars);
- pages = fastrpc_phy_page_start(list, ctx->nscalars);
- fdlist = (uint64_t *)(pages + inbufs + outbufs + handles);
for (i = inbufs; i < ctx->nbufs; ++i) {
if (!ctx->maps[i]) {
@@ -1239,6 +1283,71 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx,
}
+static u32 fastrpc_read_poll_addr(struct fastrpc_invoke_ctx *ctx)
+{
+ dma_rmb();
+ return READ_ONCE(*ctx->poll_addr);
+}
+
+static int poll_for_remote_response(struct fastrpc_invoke_ctx *ctx)
+{
+ u32 val;
+ int ret;
+
+ /*
+ * Poll until DSP writes FASTRPC_POLL_RESPONSE into *ctx->poll_addr
+ * or until another path marks the work done.
+ */
+ ret = read_poll_timeout_atomic(fastrpc_read_poll_addr, val,
+ (val == FASTRPC_POLL_RESPONSE) || ctx->is_work_done, 1,
+ FASTRPC_POLL_MAX_TIMEOUT_US, false, ctx);
+
+ if (!ret && val == FASTRPC_POLL_RESPONSE) {
+ /*
+ * DSP writes FASTRPC_POLL_RESPONSE to signal successful
+ * completion via the poll path.
+ */
+ ctx->is_work_done = true;
+ ctx->retval = 0;
+ }
+
+ if (ret == -ETIMEDOUT)
+ ret = -EIO;
+
+ return ret;
+}
+
+static inline int fastrpc_wait_for_response(struct fastrpc_invoke_ctx *ctx,
+ u32 kernel)
+{
+ int err = 0;
+
+ if (kernel) {
+ if (!wait_for_completion_timeout(&ctx->work, 10 * HZ))
+ err = -ETIMEDOUT;
+ } else {
+ err = wait_for_completion_interruptible(&ctx->work);
+ }
+
+ return err;
+}
+
+static int fastrpc_wait_for_completion(struct fastrpc_invoke_ctx *ctx,
+ u32 kernel)
+{
+ int err;
+
+ if (ctx->is_polled) {
+ err = poll_for_remote_response(ctx);
+ if (!err)
+ return 0;
+ /* If polling timed out or failed, move to normal response mode */
+ ctx->is_polled = false;
+ }
+
+ return fastrpc_wait_for_response(ctx, kernel);
+}
+
static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
u32 handle, u32 sc,
struct fastrpc_invoke_args *args)
@@ -1274,13 +1383,14 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
if (err)
goto bail;
- if (kernel) {
- if (!wait_for_completion_timeout(&ctx->work, 10 * HZ))
- err = -ETIMEDOUT;
- } else {
- err = wait_for_completion_interruptible(&ctx->work);
- }
+ /*
+ * Set message context as polled if the call is for a user PD
+ * dynamic module and user has enabled poll mode.
+ */
+ if (handle > FASTRPC_MAX_STATIC_HANDLE && fl->pd == USER_PD && fl->poll_mode)
+ ctx->is_polled = true;
+ err = fastrpc_wait_for_completion(ctx, kernel);
if (err)
goto bail;
@@ -1846,6 +1956,35 @@ done:
return 0;
}
+static int fastrpc_set_option(struct fastrpc_user *fl, char __user *argp)
+{
+ struct fastrpc_ioctl_set_option opt = {0};
+ int i;
+
+ if (copy_from_user(&opt, argp, sizeof(opt)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(opt.reserved); i++) {
+ if (opt.reserved[i] != 0)
+ return -EINVAL;
+ }
+
+ if (opt.request_id != FASTRPC_POLL_MODE)
+ return -EINVAL;
+
+ if (!fl->cctx->poll_mode_supported)
+ return -EOPNOTSUPP;
+
+ if (opt.value == FASTRPC_POLL_MODE_ENABLE)
+ fl->poll_mode = true;
+ else if (opt.value == FASTRPC_POLL_MODE_DISABLE)
+ fl->poll_mode = false;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp)
{
struct fastrpc_ioctl_capability cap = {0};
@@ -2207,6 +2346,9 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
case FASTRPC_IOCTL_MEM_UNMAP:
err = fastrpc_req_mem_unmap(fl, argp);
break;
+ case FASTRPC_IOCTL_SET_OPTION:
+ err = fastrpc_set_option(fl, argp);
+ break;
case FASTRPC_IOCTL_GET_DSP_INFO:
err = fastrpc_get_dsp_info(fl, argp);
break;
@@ -2232,19 +2374,22 @@ static int fastrpc_cb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
int i, sessions = 0;
unsigned long flags;
- int rc;
u32 dma_bits;
+ u32 sid = 0;
+ int rc;
cctx = dev_get_drvdata(dev->parent);
if (!cctx)
return -EINVAL;
of_property_read_u32(dev->of_node, "qcom,nsessions", &sessions);
+ if (of_property_read_u32(dev->of_node, "reg", &sid))
+ dev_info(dev, "FastRPC Session ID not specified in DT\n");
spin_lock_irqsave(&cctx->lock, flags);
if (cctx->sesscount >= FASTRPC_MAX_SESSIONS) {
- dev_err(&pdev->dev, "too many sessions\n");
spin_unlock_irqrestore(&cctx->lock, flags);
+ dev_err(&pdev->dev, "too many sessions\n");
return -ENOSPC;
}
dma_bits = cctx->soc_data->dma_addr_bits_default;
@@ -2253,13 +2398,11 @@ static int fastrpc_cb_probe(struct platform_device *pdev)
sess->valid = true;
sess->dev = dev;
dev_set_drvdata(dev, sess);
+ sess->sid = sid;
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");
-
if (sessions > 0) {
struct fastrpc_session_ctx *dup_sess;
@@ -2298,8 +2441,8 @@ static void fastrpc_cb_remove(struct platform_device *pdev)
}
static const struct of_device_id fastrpc_match_table[] = {
- { .compatible = "qcom,fastrpc-compute-cb", },
- {}
+ { .compatible = "qcom,fastrpc-compute-cb" },
+ { }
};
static struct platform_driver fastrpc_cb_driver = {
@@ -2362,6 +2505,7 @@ static const struct fastrpc_soc_data kaanapali_soc_data = {
.sid_pos = 56,
.dma_addr_bits_cdsp = 34,
.dma_addr_bits_default = 32,
+ .poll_mode_supported = true,
};
static const struct fastrpc_soc_data default_soc_data = {
@@ -2370,6 +2514,29 @@ static const struct fastrpc_soc_data default_soc_data = {
.dma_addr_bits_default = 32,
};
+/*
+ * Exception list for older platforms that use default_soc_data but whose
+ * DSP firmware supports FastRPC polling mode.
+ *
+ * NOTE: This list is intentionally closed.
+ * Do NOT add new platforms here. New SoCs must advertise polling mode
+ * support via their soc_data.
+ */
+
+static const struct of_device_id fastrpc_poll_supported_machines[] __maybe_unused = {
+ { .compatible = "qcom,milos" },
+ { .compatible = "qcom,qcs8300" },
+ { .compatible = "qcom,sa8775p" },
+ { .compatible = "qcom,sar2130p" },
+ { .compatible = "qcom,sm8450" },
+ { .compatible = "qcom,sm8550" },
+ { .compatible = "qcom,sm8650" },
+ { .compatible = "qcom,sm8750" },
+ { .compatible = "qcom,x1e80100" },
+ { .compatible = "qcom,x1p42100" },
+ {},
+};
+
static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct device *rdev = &rpdev->dev;
@@ -2436,6 +2603,8 @@ 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;
+ data->poll_mode_supported = soc_data->poll_mode_supported ||
+ of_machine_get_match(fastrpc_poll_supported_machines);
switch (domain_id) {
case ADSP_DOMAIN_ID:
@@ -2465,6 +2634,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
}
kref_init(&data->refcount);
+ atomic_set(&data->ctx_seq, 0);
rdev->dma_mask = &data->dma_mask;
dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
@@ -2551,7 +2721,15 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
if (!cctx)
return -ENODEV;
- ctxid = ((rsp->ctx & FASTRPC_CTXID_MASK) >> 4);
+ /*
+ * A PD exit notification from the DSP PD notification framework carries
+ * this sentinel rather than a real context. Drop it: a real context is
+ * (idr_index << 4) | pd and can never collide with this value.
+ */
+ if (rsp->ctx == FASTRPC_DSP_PD_NOTIFY_CTX)
+ return 0;
+
+ ctxid = FIELD_GET(FASTRPC_CTXID_MASK, rsp->ctx);
spin_lock_irqsave(&cctx->lock, flags);
ctx = idr_find(&cctx->ctx_idr, ctxid);
@@ -2562,7 +2740,25 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
return -ENOENT;
}
+ /*
+ * Validate the sequence number embedded in the upper bits of the
+ * context ID. Under high concurrency the IDR slot can be recycled
+ * for a new request before a late (or duplicate) glink COMPLETE
+ * response for the previous request arrives. Without this check the
+ * driver would call complete() on the wrong context, waking a thread
+ * whose buffers are still being accessed by the DSP.
+ */
+ if (FIELD_GET(FASTRPC_CTXID_SEQ_MASK, rsp->ctx) !=
+ FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)) {
+ dev_dbg(&rpdev->dev,
+ "Stale glink response ctx 0x%llx (expected seq 0x%llx), dropping\n",
+ rsp->ctx,
+ FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid));
+ return 0;
+ }
+
ctx->retval = rsp->retval;
+ ctx->is_work_done = true;
complete(&ctx->work);
/*
@@ -2578,7 +2774,7 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
static const struct of_device_id fastrpc_rpmsg_of_match[] = {
{ .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/gehc-achc.c b/drivers/misc/gehc-achc.c
index b8fca4d393c6..9792189c0923 100644
--- a/drivers/misc/gehc-achc.c
+++ b/drivers/misc/gehc-achc.c
@@ -538,8 +538,8 @@ static int gehc_achc_probe(struct spi_device *spi)
}
static const struct spi_device_id gehc_achc_id[] = {
- { "ge,achc", 0 },
- { "achc", 0 },
+ { .name = "ge,achc" },
+ { .name = "achc" },
{ }
};
MODULE_DEVICE_TABLE(spi, gehc_achc_id);
diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c
index 86bfa82723ff..e602eb3c51fa 100644
--- a/drivers/misc/genwqe/card_base.c
+++ b/drivers/misc/genwqe/card_base.c
@@ -48,60 +48,42 @@ static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX];
/* PCI structure for identifying device by PCI vendor and device ID */
static const struct pci_device_id genwqe_device_table[] = {
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5,
- .class = (PCI_CLASSCODE_GENWQE5 << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- /* Initial SR-IOV bring-up image */
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */
- .device = 0x0000, /* VF Device ID */
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- /* Fixed up image */
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */
- .device = 0x0000, /* VF Device ID */
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- /* Even one more ... */
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_NEW,
- .class = (PCI_CLASSCODE_GENWQE5 << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- { 0, } /* 0 terminated list. */
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5),
+ .class = PCI_CLASSCODE_GENWQE5 << 8,
+ .class_mask = ~0,
+ }, {
+ /* Initial SR-IOV bring-up image */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM_SRIOV),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000,
+ PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5_SRIOV),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ /* Fixed up image */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000,
+ PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ /* Even one more ... */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5_NEW),
+ .class = (PCI_CLASSCODE_GENWQE5 << 8),
+ .class_mask = ~0,
+ },
+ { } /* 0 terminated list. */
};
-
MODULE_DEVICE_TABLE(pci, genwqe_device_table);
/**
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index a2c4a9b4f871..e3b5fb337182 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -413,7 +413,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
size -= size_to_map;
map_offs = 0;
- if (prev_daddr == daddr) {
+ if (last_s && prev_daddr == daddr) {
u32 prev_len = be32_to_cpu(last_s->len);
/* pr_info("daddr combining: "
diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c
index 5ba40222eb12..033f30014aa6 100644
--- a/drivers/misc/hi6421v600-irq.c
+++ b/drivers/misc/hi6421v600-irq.c
@@ -274,18 +274,15 @@ static int hi6421v600_irq_probe(struct platform_device *pdev)
NULL,
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
"pmic", priv);
- if (ret < 0) {
- dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
- ret);
+ if (ret < 0)
return ret;
- }
return 0;
}
static const struct platform_device_id hi6421v600_irq_table[] = {
{ .name = "hi6421v600-irq" },
- {},
+ { }
};
MODULE_DEVICE_TABLE(platform, hi6421v600_irq_table);
diff --git a/drivers/misc/hisi_hikey_usb.c b/drivers/misc/hisi_hikey_usb.c
deleted file mode 100644
index 79f06001259b..000000000000
--- a/drivers/misc/hisi_hikey_usb.c
+++ /dev/null
@@ -1,274 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Support for usb functionality of Hikey series boards
- * based on Hisilicon Kirin Soc.
- *
- * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd.
- * http://www.huawei.com
- *
- * Authors: Yu Chen <chenyu56@huawei.com>
- */
-
-#include <linux/gpio/consumer.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/notifier.h>
-#include <linux/platform_device.h>
-#include <linux/property.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/string_choices.h>
-#include <linux/usb/role.h>
-
-#define DEVICE_DRIVER_NAME "hisi_hikey_usb"
-
-#define HUB_VBUS_POWER_ON 1
-#define HUB_VBUS_POWER_OFF 0
-#define USB_SWITCH_TO_HUB 1
-#define USB_SWITCH_TO_TYPEC 0
-#define TYPEC_VBUS_POWER_ON 1
-#define TYPEC_VBUS_POWER_OFF 0
-
-struct hisi_hikey_usb {
- struct device *dev;
- struct gpio_desc *otg_switch;
- struct gpio_desc *typec_vbus;
- struct gpio_desc *reset;
-
- struct regulator *regulator;
-
- struct usb_role_switch *hub_role_sw;
-
- struct usb_role_switch *dev_role_sw;
- enum usb_role role;
-
- struct mutex lock;
- struct work_struct work;
-
- struct notifier_block nb;
-};
-
-static void hub_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, int value)
-{
- int ret, status;
-
- if (!hisi_hikey_usb->regulator)
- return;
-
- status = regulator_is_enabled(hisi_hikey_usb->regulator);
- if (status == !!value)
- return;
-
- if (value)
- ret = regulator_enable(hisi_hikey_usb->regulator);
- else
- ret = regulator_disable(hisi_hikey_usb->regulator);
-
- if (ret)
- dev_err(hisi_hikey_usb->dev,
- "Can't switch regulator state to %s\n",
- str_enabled_disabled(value));
-}
-
-static void usb_switch_ctrl(struct hisi_hikey_usb *hisi_hikey_usb,
- int switch_to)
-{
- if (!hisi_hikey_usb->otg_switch)
- return;
-
- gpiod_set_value_cansleep(hisi_hikey_usb->otg_switch, switch_to);
-}
-
-static void usb_typec_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb,
- int value)
-{
- if (!hisi_hikey_usb->typec_vbus)
- return;
-
- gpiod_set_value_cansleep(hisi_hikey_usb->typec_vbus, value);
-}
-
-static void relay_set_role_switch(struct work_struct *work)
-{
- struct hisi_hikey_usb *hisi_hikey_usb = container_of(work,
- struct hisi_hikey_usb,
- work);
- struct usb_role_switch *sw;
- enum usb_role role;
-
- if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw)
- return;
-
- mutex_lock(&hisi_hikey_usb->lock);
- switch (hisi_hikey_usb->role) {
- case USB_ROLE_NONE:
- usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF);
- usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_HUB);
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_ON);
- break;
- case USB_ROLE_HOST:
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF);
- usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC);
- usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_ON);
- break;
- case USB_ROLE_DEVICE:
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF);
- usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF);
- usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC);
- break;
- default:
- break;
- }
- sw = hisi_hikey_usb->dev_role_sw;
- role = hisi_hikey_usb->role;
- mutex_unlock(&hisi_hikey_usb->lock);
-
- usb_role_switch_set_role(sw, role);
-}
-
-static int hub_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
-{
- struct hisi_hikey_usb *hisi_hikey_usb = usb_role_switch_get_drvdata(sw);
-
- if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw)
- return -EINVAL;
-
- mutex_lock(&hisi_hikey_usb->lock);
- hisi_hikey_usb->role = role;
- mutex_unlock(&hisi_hikey_usb->lock);
-
- schedule_work(&hisi_hikey_usb->work);
-
- return 0;
-}
-
-static int hisi_hikey_usb_of_role_switch(struct platform_device *pdev,
- struct hisi_hikey_usb *hisi_hikey_usb)
-{
- struct device *dev = &pdev->dev;
- struct usb_role_switch_desc hub_role_switch = {NULL};
-
- if (!device_property_read_bool(dev, "usb-role-switch"))
- return 0;
-
- hisi_hikey_usb->otg_switch = devm_gpiod_get(dev, "otg-switch",
- GPIOD_OUT_HIGH);
- if (IS_ERR(hisi_hikey_usb->otg_switch)) {
- dev_err(dev, "get otg-switch failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->otg_switch));
- return PTR_ERR(hisi_hikey_usb->otg_switch);
- }
-
- hisi_hikey_usb->typec_vbus = devm_gpiod_get(dev, "typec-vbus",
- GPIOD_OUT_LOW);
- if (IS_ERR(hisi_hikey_usb->typec_vbus)) {
- dev_err(dev, "get typec-vbus failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->typec_vbus));
- return PTR_ERR(hisi_hikey_usb->typec_vbus);
- }
-
- hisi_hikey_usb->reset = devm_gpiod_get_optional(dev,
- "hub-reset-en",
- GPIOD_OUT_HIGH);
- if (IS_ERR(hisi_hikey_usb->reset)) {
- dev_err(dev, "get hub-reset-en failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->reset));
- return PTR_ERR(hisi_hikey_usb->reset);
- }
-
- hisi_hikey_usb->dev_role_sw = usb_role_switch_get(dev);
- if (!hisi_hikey_usb->dev_role_sw)
- return -EPROBE_DEFER;
- if (IS_ERR(hisi_hikey_usb->dev_role_sw)) {
- dev_err(dev, "get device role switch failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->dev_role_sw));
- return PTR_ERR(hisi_hikey_usb->dev_role_sw);
- }
-
- INIT_WORK(&hisi_hikey_usb->work, relay_set_role_switch);
-
- hub_role_switch.fwnode = dev_fwnode(dev);
- hub_role_switch.set = hub_usb_role_switch_set;
- hub_role_switch.driver_data = hisi_hikey_usb;
-
- hisi_hikey_usb->hub_role_sw = usb_role_switch_register(dev,
- &hub_role_switch);
-
- if (IS_ERR(hisi_hikey_usb->hub_role_sw)) {
- dev_err(dev,
- "failed to register hub role with error %ld\n",
- PTR_ERR(hisi_hikey_usb->hub_role_sw));
- usb_role_switch_put(hisi_hikey_usb->dev_role_sw);
- return PTR_ERR(hisi_hikey_usb->hub_role_sw);
- }
-
- return 0;
-}
-
-static int hisi_hikey_usb_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hisi_hikey_usb *hisi_hikey_usb;
- int ret;
-
- hisi_hikey_usb = devm_kzalloc(dev, sizeof(*hisi_hikey_usb), GFP_KERNEL);
- if (!hisi_hikey_usb)
- return -ENOMEM;
-
- hisi_hikey_usb->dev = &pdev->dev;
- mutex_init(&hisi_hikey_usb->lock);
-
- hisi_hikey_usb->regulator = devm_regulator_get(dev, "hub-vdd");
- if (IS_ERR(hisi_hikey_usb->regulator)) {
- if (PTR_ERR(hisi_hikey_usb->regulator) == -EPROBE_DEFER) {
- dev_info(dev, "waiting for hub-vdd-supply\n");
- return PTR_ERR(hisi_hikey_usb->regulator);
- }
- dev_err(dev, "get hub-vdd-supply failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->regulator));
- return PTR_ERR(hisi_hikey_usb->regulator);
- }
-
- ret = hisi_hikey_usb_of_role_switch(pdev, hisi_hikey_usb);
- if (ret)
- return ret;
-
- platform_set_drvdata(pdev, hisi_hikey_usb);
-
- return 0;
-}
-
-static void hisi_hikey_usb_remove(struct platform_device *pdev)
-{
- struct hisi_hikey_usb *hisi_hikey_usb = platform_get_drvdata(pdev);
-
- if (hisi_hikey_usb->hub_role_sw) {
- usb_role_switch_unregister(hisi_hikey_usb->hub_role_sw);
-
- if (hisi_hikey_usb->dev_role_sw)
- usb_role_switch_put(hisi_hikey_usb->dev_role_sw);
- } else {
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF);
- }
-}
-
-static const struct of_device_id id_table_hisi_hikey_usb[] = {
- { .compatible = "hisilicon,usbhub" },
- {}
-};
-MODULE_DEVICE_TABLE(of, id_table_hisi_hikey_usb);
-
-static struct platform_driver hisi_hikey_usb_driver = {
- .probe = hisi_hikey_usb_probe,
- .remove = hisi_hikey_usb_remove,
- .driver = {
- .name = DEVICE_DRIVER_NAME,
- .of_match_table = id_table_hisi_hikey_usb,
- },
-};
-
-module_platform_driver(hisi_hikey_usb_driver);
-
-MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>");
-MODULE_DESCRIPTION("Driver Support for USB functionality of Hikey");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c
index ff92c6edff6b..275dc054ca04 100644
--- a/drivers/misc/hmc6352.c
+++ b/drivers/misc/hmc6352.c
@@ -121,7 +121,7 @@ static void hmc6352_remove(struct i2c_client *client)
}
static const struct i2c_device_id hmc6352_id[] = {
- { "hmc6352" },
+ { .name = "hmc6352" },
{ }
};
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c
index ff3f03ea577e..8988858c2f31 100644
--- a/drivers/misc/hpilo.c
+++ b/drivers/misc/hpilo.c
@@ -33,10 +33,10 @@ static unsigned int max_ccb = 16;
static char ilo_hwdev[MAX_ILO_DEV];
static const struct pci_device_id ilo_blacklist[] = {
/* auxiliary iLO */
- {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979)},
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979) },
/* CL */
- {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289)},
- {}
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289) },
+ { }
};
static inline int get_entry_id(int entry)
@@ -160,11 +160,16 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb,
ret = fifo_dequeue(hw, fifobar, &entry);
if (ret) {
+ int pkt_len;
+
pkt_id = get_entry_id(entry);
+ pkt_len = get_entry_len(entry);
+ if (pkt_id >= NR_QENTRY || pkt_len > desc_mem_sz(1))
+ return 0;
if (id)
*id = pkt_id;
if (len)
- *len = get_entry_len(entry);
+ *len = pkt_len;
if (pkt)
*pkt = (void *)(desc + desc_mem_sz(pkt_id));
}
diff --git a/drivers/misc/ibmasm/Makefile b/drivers/misc/ibmasm/Makefile
deleted file mode 100644
index 1b9dd0f44411..000000000000
--- a/drivers/misc/ibmasm/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-
-obj-$(CONFIG_IBM_ASM) := ibmasm.o
-
-ibmasm-y := module.o \
- ibmasmfs.o \
- event.o \
- command.o \
- remote.o \
- heartbeat.o \
- r_heartbeat.o \
- dot_command.o \
- lowlevel.o
-
-ibmasm-$(CONFIG_SERIAL_8250) += uart.o
-
diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c
deleted file mode 100644
index 6b037675ff45..000000000000
--- a/drivers/misc/ibmasm/command.c
+++ /dev/null
@@ -1,173 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-
-static void exec_next_command(struct service_processor *sp);
-
-static atomic_t command_count = ATOMIC_INIT(0);
-
-struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size)
-{
- struct command *cmd;
-
- if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE)
- return NULL;
-
- cmd = kzalloc_obj(struct command);
- if (cmd == NULL)
- return NULL;
-
-
- cmd->buffer = kzalloc(buffer_size, GFP_KERNEL);
- if (cmd->buffer == NULL) {
- kfree(cmd);
- return NULL;
- }
- cmd->buffer_size = buffer_size;
-
- kref_init(&cmd->kref);
- cmd->lock = &sp->lock;
-
- cmd->status = IBMASM_CMD_PENDING;
- init_waitqueue_head(&cmd->wait);
- INIT_LIST_HEAD(&cmd->queue_node);
-
- atomic_inc(&command_count);
- dbg("command count: %d\n", atomic_read(&command_count));
-
- return cmd;
-}
-
-void ibmasm_free_command(struct kref *kref)
-{
- struct command *cmd = to_command(kref);
-
- list_del(&cmd->queue_node);
- atomic_dec(&command_count);
- dbg("command count: %d\n", atomic_read(&command_count));
- kfree(cmd->buffer);
- kfree(cmd);
-}
-
-static void enqueue_command(struct service_processor *sp, struct command *cmd)
-{
- list_add_tail(&cmd->queue_node, &sp->command_queue);
-}
-
-static struct command *dequeue_command(struct service_processor *sp)
-{
- struct command *cmd;
- struct list_head *next;
-
- if (list_empty(&sp->command_queue))
- return NULL;
-
- next = sp->command_queue.next;
- list_del_init(next);
- cmd = list_entry(next, struct command, queue_node);
-
- return cmd;
-}
-
-static inline void do_exec_command(struct service_processor *sp)
-{
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
-
- if (ibmasm_send_i2o_message(sp)) {
- sp->current_command->status = IBMASM_CMD_FAILED;
- wake_up(&sp->current_command->wait);
- command_put(sp->current_command);
- exec_next_command(sp);
- }
-}
-
-/*
- * exec_command
- * send a command to a service processor
- * Commands are executed sequentially. One command (sp->current_command)
- * is sent to the service processor. Once the interrupt handler gets a
- * message of type command_response, the message is copied into
- * the current commands buffer,
- */
-void ibmasm_exec_command(struct service_processor *sp, struct command *cmd)
-{
- unsigned long flags;
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
-
- spin_lock_irqsave(&sp->lock, flags);
-
- if (!sp->current_command) {
- sp->current_command = cmd;
- command_get(sp->current_command);
- spin_unlock_irqrestore(&sp->lock, flags);
- do_exec_command(sp);
- } else {
- enqueue_command(sp, cmd);
- spin_unlock_irqrestore(&sp->lock, flags);
- }
-}
-
-static void exec_next_command(struct service_processor *sp)
-{
- unsigned long flags;
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
-
- spin_lock_irqsave(&sp->lock, flags);
- sp->current_command = dequeue_command(sp);
- if (sp->current_command) {
- command_get(sp->current_command);
- spin_unlock_irqrestore(&sp->lock, flags);
- do_exec_command(sp);
- } else {
- spin_unlock_irqrestore(&sp->lock, flags);
- }
-}
-
-/*
- * Sleep until a command has failed or a response has been received
- * and the command status been updated by the interrupt handler.
- * (see receive_response).
- */
-void ibmasm_wait_for_response(struct command *cmd, int timeout)
-{
- wait_event_interruptible_timeout(cmd->wait,
- cmd->status == IBMASM_CMD_COMPLETE ||
- cmd->status == IBMASM_CMD_FAILED,
- timeout * HZ);
-}
-
-/*
- * receive_command_response
- * called by the interrupt handler when a dot command of type command_response
- * was received.
- */
-void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size)
-{
- struct command *cmd = sp->current_command;
-
- if (!sp->current_command)
- return;
-
- memcpy_fromio(cmd->buffer, response, min(size, cmd->buffer_size));
- cmd->status = IBMASM_CMD_COMPLETE;
- wake_up(&sp->current_command->wait);
- command_put(sp->current_command);
- exec_next_command(sp);
-}
diff --git a/drivers/misc/ibmasm/dot_command.c b/drivers/misc/ibmasm/dot_command.c
deleted file mode 100644
index df389bd4c9df..000000000000
--- a/drivers/misc/ibmasm/dot_command.c
+++ /dev/null
@@ -1,138 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include "ibmasm.h"
-#include "dot_command.h"
-
-/*
- * Dispatch an incoming message to the specific handler for the message.
- * Called from interrupt context.
- */
-void ibmasm_receive_message(struct service_processor *sp, void *message, int message_size)
-{
- u32 size;
- struct dot_command_header *header = (struct dot_command_header *)message;
-
- if (message_size == 0)
- return;
-
- size = get_dot_command_size(message);
- if (size == 0)
- return;
-
- if (size > message_size)
- size = message_size;
-
- switch (header->type) {
- case sp_event:
- ibmasm_receive_event(sp, message, size);
- break;
- case sp_command_response:
- ibmasm_receive_command_response(sp, message, size);
- break;
- case sp_heartbeat:
- ibmasm_receive_heartbeat(sp, message, size);
- break;
- default:
- dev_err(sp->dev, "Received unknown message from service processor\n");
- }
-}
-
-
-#define INIT_BUFFER_SIZE 32
-
-
-/*
- * send the 4.3.5.10 dot command (driver VPD) to the service processor
- */
-int ibmasm_send_driver_vpd(struct service_processor *sp)
-{
- struct command *command;
- struct dot_command_header *header;
- u8 *vpd_command;
- u8 *vpd_data;
- int result = 0;
-
- command = ibmasm_new_command(sp, INIT_BUFFER_SIZE);
- if (command == NULL)
- return -ENOMEM;
-
- header = (struct dot_command_header *)command->buffer;
- header->type = sp_write;
- header->command_size = 4;
- header->data_size = 16;
- header->status = 0;
- header->reserved = 0;
-
- vpd_command = command->buffer + sizeof(struct dot_command_header);
- vpd_command[0] = 0x4;
- vpd_command[1] = 0x3;
- vpd_command[2] = 0x5;
- vpd_command[3] = 0xa;
-
- vpd_data = vpd_command + header->command_size;
- vpd_data[0] = 0;
- strcat(vpd_data, IBMASM_DRIVER_VPD);
- vpd_data[10] = 0;
- vpd_data[15] = 0;
-
- ibmasm_exec_command(sp, command);
- ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL);
-
- if (command->status != IBMASM_CMD_COMPLETE)
- result = -ENODEV;
-
- command_put(command);
-
- return result;
-}
-
-struct os_state_command {
- struct dot_command_header header;
- unsigned char command[3];
- unsigned char data;
-};
-
-/*
- * send the 4.3.6 dot command (os state) to the service processor
- * During driver init this function is called with os state "up".
- * This causes the service processor to start sending heartbeats the
- * driver.
- * During driver exit the function is called with os state "down",
- * causing the service processor to stop the heartbeats.
- */
-int ibmasm_send_os_state(struct service_processor *sp, int os_state)
-{
- struct command *cmd;
- struct os_state_command *os_state_cmd;
- int result = 0;
-
- cmd = ibmasm_new_command(sp, sizeof(struct os_state_command));
- if (cmd == NULL)
- return -ENOMEM;
-
- os_state_cmd = (struct os_state_command *)cmd->buffer;
- os_state_cmd->header.type = sp_write;
- os_state_cmd->header.command_size = 3;
- os_state_cmd->header.data_size = 1;
- os_state_cmd->header.status = 0;
- os_state_cmd->command[0] = 4;
- os_state_cmd->command[1] = 3;
- os_state_cmd->command[2] = 6;
- os_state_cmd->data = os_state;
-
- ibmasm_exec_command(sp, cmd);
- ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
-
- if (cmd->status != IBMASM_CMD_COMPLETE)
- result = -ENODEV;
-
- command_put(cmd);
- return result;
-}
diff --git a/drivers/misc/ibmasm/dot_command.h b/drivers/misc/ibmasm/dot_command.h
deleted file mode 100644
index e03399ebe239..000000000000
--- a/drivers/misc/ibmasm/dot_command.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#ifndef __DOT_COMMAND_H__
-#define __DOT_COMMAND_H__
-
-/*
- * dot commands are the protocol used to communicate with the service
- * processor.
- * They consist of header, a command of variable length and data of
- * variable length.
- */
-
-/* dot command types */
-#define sp_write 0
-#define sp_write_next 1
-#define sp_read 2
-#define sp_read_next 3
-#define sp_command_response 4
-#define sp_event 5
-#define sp_heartbeat 6
-
-#pragma pack(1)
-struct dot_command_header {
- u8 type;
- u8 command_size;
- u16 data_size;
- u8 status;
- u8 reserved;
-};
-#pragma pack()
-
-static inline size_t get_dot_command_size(void *buffer)
-{
- struct dot_command_header *cmd = (struct dot_command_header *)buffer;
- return sizeof(struct dot_command_header) + cmd->command_size + cmd->data_size;
-}
-
-static inline unsigned int get_dot_command_timeout(void *buffer)
-{
- struct dot_command_header *header = (struct dot_command_header *)buffer;
- unsigned char *cmd = buffer + sizeof(struct dot_command_header);
-
- /* dot commands 6.3.1, 7.1 and 8.x need a longer timeout */
-
- if (header->command_size == 3) {
- if ((cmd[0] == 6) && (cmd[1] == 3) && (cmd[2] == 1))
- return IBMASM_CMD_TIMEOUT_EXTRA;
- } else if (header->command_size == 2) {
- if ((cmd[0] == 7) && (cmd[1] == 1))
- return IBMASM_CMD_TIMEOUT_EXTRA;
- if (cmd[0] == 8)
- return IBMASM_CMD_TIMEOUT_EXTRA;
- }
- return IBMASM_CMD_TIMEOUT_NORMAL;
-}
-
-#endif /* __DOT_COMMAND_H__ */
diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c
deleted file mode 100644
index 3990bf2b1728..000000000000
--- a/drivers/misc/ibmasm/event.c
+++ /dev/null
@@ -1,163 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-
-/*
- * ASM service processor event handling routines.
- *
- * Events are signalled to the device drivers through interrupts.
- * They have the format of dot commands, with the type field set to
- * sp_event.
- * The driver does not interpret the events, it simply stores them in a
- * circular buffer.
- */
-
-static void wake_up_event_readers(struct service_processor *sp)
-{
- struct event_reader *reader;
-
- list_for_each_entry(reader, &sp->event_buffer->readers, node)
- wake_up_interruptible(&reader->wait);
-}
-
-/*
- * receive_event
- * Called by the interrupt handler when a dot command of type sp_event is
- * received.
- * Store the event in the circular event buffer, wake up any sleeping
- * event readers.
- * There is no reader marker in the buffer, therefore readers are
- * responsible for keeping up with the writer, or they will lose events.
- */
-void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size)
-{
- struct event_buffer *buffer = sp->event_buffer;
- struct ibmasm_event *event;
- unsigned long flags;
-
- data_size = min(data_size, IBMASM_EVENT_MAX_SIZE);
-
- spin_lock_irqsave(&sp->lock, flags);
- /* copy the event into the next slot in the circular buffer */
- event = &buffer->events[buffer->next_index];
- memcpy_fromio(event->data, data, data_size);
- event->data_size = data_size;
- event->serial_number = buffer->next_serial_number;
-
- /* advance indices in the buffer */
- buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS;
- buffer->next_serial_number++;
- spin_unlock_irqrestore(&sp->lock, flags);
-
- wake_up_event_readers(sp);
-}
-
-static inline int event_available(struct event_buffer *b, struct event_reader *r)
-{
- return (r->next_serial_number < b->next_serial_number);
-}
-
-/*
- * get_next_event
- * Called by event readers (initiated from user space through the file
- * system).
- * Sleeps until a new event is available.
- */
-int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader)
-{
- struct event_buffer *buffer = sp->event_buffer;
- struct ibmasm_event *event;
- unsigned int index;
- unsigned long flags;
-
- reader->cancelled = 0;
-
- if (wait_event_interruptible(reader->wait,
- event_available(buffer, reader) || reader->cancelled))
- return -ERESTARTSYS;
-
- if (!event_available(buffer, reader))
- return 0;
-
- spin_lock_irqsave(&sp->lock, flags);
-
- index = buffer->next_index;
- event = &buffer->events[index];
- while (event->serial_number < reader->next_serial_number) {
- index = (index + 1) % IBMASM_NUM_EVENTS;
- event = &buffer->events[index];
- }
- memcpy(reader->data, event->data, event->data_size);
- reader->data_size = event->data_size;
- reader->next_serial_number = event->serial_number + 1;
-
- spin_unlock_irqrestore(&sp->lock, flags);
-
- return event->data_size;
-}
-
-void ibmasm_cancel_next_event(struct event_reader *reader)
-{
- reader->cancelled = 1;
- wake_up_interruptible(&reader->wait);
-}
-
-void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader)
-{
- unsigned long flags;
-
- reader->next_serial_number = sp->event_buffer->next_serial_number;
- init_waitqueue_head(&reader->wait);
- spin_lock_irqsave(&sp->lock, flags);
- list_add(&reader->node, &sp->event_buffer->readers);
- spin_unlock_irqrestore(&sp->lock, flags);
-}
-
-void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sp->lock, flags);
- list_del(&reader->node);
- spin_unlock_irqrestore(&sp->lock, flags);
-}
-
-int ibmasm_event_buffer_init(struct service_processor *sp)
-{
- struct event_buffer *buffer;
- struct ibmasm_event *event;
- int i;
-
- buffer = kmalloc_obj(struct event_buffer);
- if (!buffer)
- return -ENOMEM;
-
- buffer->next_index = 0;
- buffer->next_serial_number = 1;
-
- event = buffer->events;
- for (i=0; i<IBMASM_NUM_EVENTS; i++, event++)
- event->serial_number = 0;
-
- INIT_LIST_HEAD(&buffer->readers);
-
- sp->event_buffer = buffer;
-
- return 0;
-}
-
-void ibmasm_event_buffer_exit(struct service_processor *sp)
-{
- kfree(sp->event_buffer);
-}
diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c
deleted file mode 100644
index 59c9a0d95659..000000000000
--- a/drivers/misc/ibmasm/heartbeat.c
+++ /dev/null
@@ -1,88 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/notifier.h>
-#include <linux/panic_notifier.h>
-#include "ibmasm.h"
-#include "dot_command.h"
-#include "lowlevel.h"
-
-static int suspend_heartbeats = 0;
-
-/*
- * Once the driver indicates to the service processor that it is running
- * - see send_os_state() - the service processor sends periodic heartbeats
- * to the driver. The driver must respond to the heartbeats or else the OS
- * will be rebooted.
- * In the case of a panic the interrupt handler continues to work and thus
- * continues to respond to heartbeats, making the service processor believe
- * the OS is still running and thus preventing a reboot.
- * To prevent this from happening a callback is added the panic_notifier_list.
- * Before responding to a heartbeat the driver checks if a panic has happened,
- * if yes it suspends heartbeat, causing the service processor to reboot as
- * expected.
- */
-static int panic_happened(struct notifier_block *n, unsigned long val, void *v)
-{
- suspend_heartbeats = 1;
- return 0;
-}
-
-static struct notifier_block panic_notifier = { panic_happened, NULL, 1 };
-
-void ibmasm_register_panic_notifier(void)
-{
- atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier);
-}
-
-void ibmasm_unregister_panic_notifier(void)
-{
- atomic_notifier_chain_unregister(&panic_notifier_list,
- &panic_notifier);
-}
-
-
-int ibmasm_heartbeat_init(struct service_processor *sp)
-{
- sp->heartbeat = ibmasm_new_command(sp, HEARTBEAT_BUFFER_SIZE);
- if (sp->heartbeat == NULL)
- return -ENOMEM;
-
- return 0;
-}
-
-void ibmasm_heartbeat_exit(struct service_processor *sp)
-{
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
- ibmasm_wait_for_response(sp->heartbeat, IBMASM_CMD_TIMEOUT_NORMAL);
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
- suspend_heartbeats = 1;
- command_put(sp->heartbeat);
-}
-
-void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size)
-{
- struct command *cmd = sp->heartbeat;
- struct dot_command_header *header = (struct dot_command_header *)cmd->buffer;
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
- if (suspend_heartbeats)
- return;
-
- /* return the received dot command to sender */
- cmd->status = IBMASM_CMD_PENDING;
- size = min(size, cmd->buffer_size);
- memcpy_fromio(cmd->buffer, message, size);
- header->type = sp_write;
- ibmasm_exec_command(sp, cmd);
-}
diff --git a/drivers/misc/ibmasm/i2o.h b/drivers/misc/ibmasm/i2o.h
deleted file mode 100644
index 468fa84a3257..000000000000
--- a/drivers/misc/ibmasm/i2o.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#pragma pack(1)
-struct i2o_header {
- u8 version;
- u8 message_flags;
- u16 message_size;
- u8 target;
- u8 initiator_and_target;
- u8 initiator;
- u8 function;
- u32 initiator_context;
-};
-#pragma pack()
-
-#define I2O_HEADER_TEMPLATE \
- { .version = 0x01, \
- .message_flags = 0x00, \
- .function = 0xFF, \
- .initiator = 0x00, \
- .initiator_and_target = 0x40, \
- .target = 0x00, \
- .initiator_context = 0x0 }
-
-#define I2O_MESSAGE_SIZE 0x1000
-#define I2O_COMMAND_SIZE (I2O_MESSAGE_SIZE - sizeof(struct i2o_header))
-
-#pragma pack(1)
-struct i2o_message {
- struct i2o_header header;
- void *data;
-};
-#pragma pack()
-
-static inline unsigned short outgoing_message_size(unsigned int data_size)
-{
- unsigned int size;
- unsigned short i2o_size;
-
- if (data_size > I2O_COMMAND_SIZE)
- data_size = I2O_COMMAND_SIZE;
-
- size = sizeof(struct i2o_header) + data_size;
-
- i2o_size = size / sizeof(u32);
-
- if (size % sizeof(u32))
- i2o_size++;
-
- return i2o_size;
-}
-
-static inline u32 incoming_data_size(struct i2o_message *i2o_message)
-{
- return (sizeof(u32) * i2o_message->header.message_size);
-}
diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h
deleted file mode 100644
index a5ced88ca923..000000000000
--- a/drivers/misc/ibmasm/ibmasm.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/wait.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/kref.h>
-#include <linux/device.h>
-#include <linux/input.h>
-#include <linux/time64.h>
-
-/* Driver identification */
-#define DRIVER_NAME "ibmasm"
-#define DRIVER_VERSION "1.0"
-#define DRIVER_AUTHOR "Max Asbock <masbock@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
-#define DRIVER_DESC "IBM ASM Service Processor Driver"
-
-#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
-#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME)
-
-extern int ibmasm_debug;
-#define dbg(STR, ARGS...) \
- do { \
- if (ibmasm_debug) \
- printk(KERN_DEBUG STR , ##ARGS); \
- } while (0)
-
-static inline char *get_timestamp(char *buf)
-{
- struct timespec64 now;
-
- ktime_get_real_ts64(&now);
- sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec,
- now.tv_nsec / NSEC_PER_USEC);
- return buf;
-}
-
-#define IBMASM_CMD_PENDING 0
-#define IBMASM_CMD_COMPLETE 1
-#define IBMASM_CMD_FAILED 2
-
-#define IBMASM_CMD_TIMEOUT_NORMAL 45
-#define IBMASM_CMD_TIMEOUT_EXTRA 240
-
-#define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000
-
-#define REVERSE_HEARTBEAT_TIMEOUT 120
-
-#define HEARTBEAT_BUFFER_SIZE 0x400
-
-#ifdef IA64
-#define IBMASM_DRIVER_VPD "Lin64 6.08 "
-#else
-#define IBMASM_DRIVER_VPD "Lin32 6.08 "
-#endif
-
-#define SYSTEM_STATE_OS_UP 5
-#define SYSTEM_STATE_OS_DOWN 4
-
-#define IBMASM_NAME_SIZE 16
-
-#define IBMASM_NUM_EVENTS 10
-#define IBMASM_EVENT_MAX_SIZE 2048u
-
-
-struct command {
- struct list_head queue_node;
- wait_queue_head_t wait;
- unsigned char *buffer;
- size_t buffer_size;
- int status;
- struct kref kref;
- spinlock_t *lock;
-};
-#define to_command(c) container_of(c, struct command, kref)
-
-void ibmasm_free_command(struct kref *kref);
-static inline void command_put(struct command *cmd)
-{
- unsigned long flags;
- spinlock_t *lock = cmd->lock;
-
- spin_lock_irqsave(lock, flags);
- kref_put(&cmd->kref, ibmasm_free_command);
- spin_unlock_irqrestore(lock, flags);
-}
-
-static inline void command_get(struct command *cmd)
-{
- kref_get(&cmd->kref);
-}
-
-
-struct ibmasm_event {
- unsigned int serial_number;
- unsigned int data_size;
- unsigned char data[IBMASM_EVENT_MAX_SIZE];
-};
-
-struct event_buffer {
- struct ibmasm_event events[IBMASM_NUM_EVENTS];
- unsigned int next_serial_number;
- unsigned int next_index;
- struct list_head readers;
-};
-
-struct event_reader {
- int cancelled;
- unsigned int next_serial_number;
- wait_queue_head_t wait;
- struct list_head node;
- unsigned int data_size;
- unsigned char data[IBMASM_EVENT_MAX_SIZE];
-};
-
-struct reverse_heartbeat {
- wait_queue_head_t wait;
- unsigned int stopped;
-};
-
-struct ibmasm_remote {
- struct input_dev *keybd_dev;
- struct input_dev *mouse_dev;
-};
-
-struct service_processor {
- struct list_head node;
- spinlock_t lock;
- void __iomem *base_address;
- unsigned int irq;
- struct command *current_command;
- struct command *heartbeat;
- struct list_head command_queue;
- struct event_buffer *event_buffer;
- char dirname[IBMASM_NAME_SIZE];
- char devname[IBMASM_NAME_SIZE];
- unsigned int number;
- struct ibmasm_remote remote;
- int serial_line;
- struct device *dev;
-};
-
-/* command processing */
-struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size);
-void ibmasm_exec_command(struct service_processor *sp, struct command *cmd);
-void ibmasm_wait_for_response(struct command *cmd, int timeout);
-void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size);
-
-/* event processing */
-int ibmasm_event_buffer_init(struct service_processor *sp);
-void ibmasm_event_buffer_exit(struct service_processor *sp);
-void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size);
-void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader);
-void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader);
-int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader);
-void ibmasm_cancel_next_event(struct event_reader *reader);
-
-/* heartbeat - from SP to OS */
-void ibmasm_register_panic_notifier(void);
-void ibmasm_unregister_panic_notifier(void);
-int ibmasm_heartbeat_init(struct service_processor *sp);
-void ibmasm_heartbeat_exit(struct service_processor *sp);
-void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size);
-
-/* reverse heartbeat - from OS to SP */
-void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
-int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
-void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb);
-
-/* dot commands */
-void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size);
-int ibmasm_send_driver_vpd(struct service_processor *sp);
-int ibmasm_send_os_state(struct service_processor *sp, int os_state);
-
-/* low level message processing */
-int ibmasm_send_i2o_message(struct service_processor *sp);
-irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id);
-
-/* remote console */
-void ibmasm_handle_mouse_interrupt(struct service_processor *sp);
-int ibmasm_init_remote_input_dev(struct service_processor *sp);
-void ibmasm_free_remote_input_dev(struct service_processor *sp);
-
-/* file system */
-int ibmasmfs_register(void);
-void ibmasmfs_unregister(void);
-void ibmasmfs_add_sp(struct service_processor *sp);
-
-/* uart */
-#if IS_ENABLED(CONFIG_SERIAL_8250)
-void ibmasm_register_uart(struct service_processor *sp);
-void ibmasm_unregister_uart(struct service_processor *sp);
-#else
-#define ibmasm_register_uart(sp) do { } while(0)
-#define ibmasm_unregister_uart(sp) do { } while(0)
-#endif
diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c
deleted file mode 100644
index dfdfa9ba4747..000000000000
--- a/drivers/misc/ibmasm/ibmasmfs.c
+++ /dev/null
@@ -1,604 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-/*
- * Parts of this code are based on an article by Jonathan Corbet
- * that appeared in Linux Weekly News.
- */
-
-
-/*
- * The IBMASM file virtual filesystem. It creates the following hierarchy
- * dynamically when mounted from user space:
- *
- * /ibmasm
- * |-- 0
- * | |-- command
- * | |-- event
- * | |-- reverse_heartbeat
- * | `-- remote_video
- * | |-- depth
- * | |-- height
- * | `-- width
- * .
- * .
- * .
- * `-- n
- * |-- command
- * |-- event
- * |-- reverse_heartbeat
- * `-- remote_video
- * |-- depth
- * |-- height
- * `-- width
- *
- * For each service processor the following files are created:
- *
- * command: execute dot commands
- * write: execute a dot command on the service processor
- * read: return the result of a previously executed dot command
- *
- * events: listen for service processor events
- * read: sleep (interruptible) until an event occurs
- * write: wakeup sleeping event listener
- *
- * reverse_heartbeat: send a heartbeat to the service processor
- * read: sleep (interruptible) until the reverse heartbeat fails
- * write: wakeup sleeping heartbeat listener
- *
- * remote_video/width
- * remote_video/height
- * remote_video/width: control remote display settings
- * write: set value
- * read: read value
- */
-
-#include <linux/fs.h>
-#include <linux/fs_context.h>
-#include <linux/pagemap.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <asm/io.h>
-#include "ibmasm.h"
-#include "remote.h"
-#include "dot_command.h"
-
-#define IBMASMFS_MAGIC 0x66726f67
-
-static LIST_HEAD(service_processors);
-
-static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
-static void ibmasmfs_create_files (struct super_block *sb);
-static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc);
-
-static int ibmasmfs_get_tree(struct fs_context *fc)
-{
- return get_tree_single(fc, ibmasmfs_fill_super);
-}
-
-static const struct fs_context_operations ibmasmfs_context_ops = {
- .get_tree = ibmasmfs_get_tree,
-};
-
-static int ibmasmfs_init_fs_context(struct fs_context *fc)
-{
- fc->ops = &ibmasmfs_context_ops;
- return 0;
-}
-
-static const struct super_operations ibmasmfs_s_ops = {
- .statfs = simple_statfs,
- .drop_inode = inode_just_drop,
-};
-
-static struct file_system_type ibmasmfs_type = {
- .owner = THIS_MODULE,
- .name = "ibmasmfs",
- .init_fs_context = ibmasmfs_init_fs_context,
- .kill_sb = kill_anon_super,
-};
-MODULE_ALIAS_FS("ibmasmfs");
-
-static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc)
-{
- struct inode *root;
-
- sb->s_blocksize = PAGE_SIZE;
- sb->s_blocksize_bits = PAGE_SHIFT;
- sb->s_magic = IBMASMFS_MAGIC;
- sb->s_op = &ibmasmfs_s_ops;
- sb->s_time_gran = 1;
-
- root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
- if (!root)
- return -ENOMEM;
-
- root->i_op = &simple_dir_inode_operations;
- root->i_fop = &simple_dir_operations;
-
- sb->s_root = d_make_root(root);
- if (!sb->s_root)
- return -ENOMEM;
-
- ibmasmfs_create_files(sb);
- return 0;
-}
-
-static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
-{
- struct inode *ret = new_inode(sb);
-
- if (ret) {
- ret->i_ino = get_next_ino();
- ret->i_mode = mode;
- simple_inode_init_ts(ret);
- }
- return ret;
-}
-
-static int ibmasmfs_create_file(struct dentry *parent,
- const char *name,
- const struct file_operations *fops,
- void *data,
- int mode)
-{
- struct dentry *dentry;
- struct inode *inode;
-
- dentry = d_alloc_name(parent, name);
- if (!dentry)
- return -ENOMEM;
-
- inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode);
- if (!inode) {
- dput(dentry);
- return -ENOMEM;
- }
-
- inode->i_fop = fops;
- inode->i_private = data;
-
- d_make_persistent(dentry, inode);
- dput(dentry);
- return 0;
-}
-
-static struct dentry *ibmasmfs_create_dir(struct dentry *parent,
- const char *name)
-{
- struct dentry *dentry;
- struct inode *inode;
-
- dentry = d_alloc_name(parent, name);
- if (!dentry)
- return NULL;
-
- inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500);
- if (!inode) {
- dput(dentry);
- return NULL;
- }
-
- inode->i_op = &simple_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
-
- d_make_persistent(dentry, inode);
- dput(dentry);
- return dentry; // borrowed
-}
-
-int ibmasmfs_register(void)
-{
- return register_filesystem(&ibmasmfs_type);
-}
-
-void ibmasmfs_unregister(void)
-{
- unregister_filesystem(&ibmasmfs_type);
-}
-
-void ibmasmfs_add_sp(struct service_processor *sp)
-{
- list_add(&sp->node, &service_processors);
-}
-
-/* struct to save state between command file operations */
-struct ibmasmfs_command_data {
- struct service_processor *sp;
- struct command *command;
-};
-
-/* struct to save state between event file operations */
-struct ibmasmfs_event_data {
- struct service_processor *sp;
- struct event_reader reader;
- int active;
-};
-
-/* struct to save state between reverse heartbeat file operations */
-struct ibmasmfs_heartbeat_data {
- struct service_processor *sp;
- struct reverse_heartbeat heartbeat;
- int active;
-};
-
-static int command_file_open(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_command_data *command_data;
-
- if (!inode->i_private)
- return -ENODEV;
-
- command_data = kmalloc_obj(struct ibmasmfs_command_data);
- if (!command_data)
- return -ENOMEM;
-
- command_data->command = NULL;
- command_data->sp = inode->i_private;
- file->private_data = command_data;
- return 0;
-}
-
-static int command_file_close(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_command_data *command_data = file->private_data;
-
- if (command_data->command)
- command_put(command_data->command);
-
- kfree(command_data);
- return 0;
-}
-
-static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_command_data *command_data = file->private_data;
- struct command *cmd;
- int len;
- unsigned long flags;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
- return 0;
- if (*offset != 0)
- return 0;
-
- spin_lock_irqsave(&command_data->sp->lock, flags);
- cmd = command_data->command;
- if (cmd == NULL) {
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
- return 0;
- }
- command_data->command = NULL;
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
-
- if (cmd->status != IBMASM_CMD_COMPLETE) {
- command_put(cmd);
- return -EIO;
- }
- len = min(count, cmd->buffer_size);
- if (copy_to_user(buf, cmd->buffer, len)) {
- command_put(cmd);
- return -EFAULT;
- }
- command_put(cmd);
-
- return len;
-}
-
-static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
-{
- struct ibmasmfs_command_data *command_data = file->private_data;
- struct command *cmd;
- unsigned long flags;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
- return 0;
- if (count < sizeof(struct dot_command_header))
- return -EINVAL;
- if (*offset != 0)
- return 0;
-
- /* commands are executed sequentially, only one command at a time */
- if (command_data->command)
- return -EAGAIN;
-
- cmd = ibmasm_new_command(command_data->sp, count);
- if (!cmd)
- return -ENOMEM;
-
- if (copy_from_user(cmd->buffer, ubuff, count)) {
- command_put(cmd);
- return -EFAULT;
- }
-
- if (count < get_dot_command_size(cmd->buffer)) {
- command_put(cmd);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&command_data->sp->lock, flags);
- if (command_data->command) {
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
- command_put(cmd);
- return -EAGAIN;
- }
- command_data->command = cmd;
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
-
- ibmasm_exec_command(command_data->sp, cmd);
- ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
-
- return count;
-}
-
-static int event_file_open(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_event_data *event_data;
- struct service_processor *sp;
-
- if (!inode->i_private)
- return -ENODEV;
-
- sp = inode->i_private;
-
- event_data = kmalloc_obj(struct ibmasmfs_event_data);
- if (!event_data)
- return -ENOMEM;
-
- ibmasm_event_reader_register(sp, &event_data->reader);
-
- event_data->sp = sp;
- event_data->active = 0;
- file->private_data = event_data;
- return 0;
-}
-
-static int event_file_close(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_event_data *event_data = file->private_data;
-
- ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
- kfree(event_data);
- return 0;
-}
-
-static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_event_data *event_data = file->private_data;
- struct event_reader *reader = &event_data->reader;
- struct service_processor *sp = event_data->sp;
- int ret;
- unsigned long flags;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
- return 0;
- if (*offset != 0)
- return 0;
-
- spin_lock_irqsave(&sp->lock, flags);
- if (event_data->active) {
- spin_unlock_irqrestore(&sp->lock, flags);
- return -EBUSY;
- }
- event_data->active = 1;
- spin_unlock_irqrestore(&sp->lock, flags);
-
- ret = ibmasm_get_next_event(sp, reader);
- if (ret <= 0)
- goto out;
-
- if (count < reader->data_size) {
- ret = -EINVAL;
- goto out;
- }
-
- if (copy_to_user(buf, reader->data, reader->data_size)) {
- ret = -EFAULT;
- goto out;
- }
- ret = reader->data_size;
-
-out:
- event_data->active = 0;
- return ret;
-}
-
-static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_event_data *event_data = file->private_data;
-
- if (*offset < 0)
- return -EINVAL;
- if (count != 1)
- return 0;
- if (*offset != 0)
- return 0;
-
- ibmasm_cancel_next_event(&event_data->reader);
- return 0;
-}
-
-static int r_heartbeat_file_open(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_heartbeat_data *rhbeat;
-
- if (!inode->i_private)
- return -ENODEV;
-
- rhbeat = kmalloc_obj(struct ibmasmfs_heartbeat_data);
- if (!rhbeat)
- return -ENOMEM;
-
- rhbeat->sp = inode->i_private;
- rhbeat->active = 0;
- ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
- file->private_data = rhbeat;
- return 0;
-}
-
-static int r_heartbeat_file_close(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
-
- kfree(rhbeat);
- return 0;
-}
-
-static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
- unsigned long flags;
- int result;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > 1024)
- return 0;
- if (*offset != 0)
- return 0;
-
- /* allow only one reverse heartbeat per process */
- spin_lock_irqsave(&rhbeat->sp->lock, flags);
- if (rhbeat->active) {
- spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
- return -EBUSY;
- }
- rhbeat->active = 1;
- spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
-
- result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
- rhbeat->active = 0;
-
- return result;
-}
-
-static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
-
- if (*offset < 0)
- return -EINVAL;
- if (count != 1)
- return 0;
- if (*offset != 0)
- return 0;
-
- if (rhbeat->active)
- ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
-
- return 1;
-}
-
-static int remote_settings_file_close(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- void __iomem *address = (void __iomem *)file->private_data;
- int len = 0;
- unsigned int value;
- char lbuf[20];
-
- value = readl(address);
- len = snprintf(lbuf, sizeof(lbuf), "%d\n", value);
-
- return simple_read_from_buffer(buf, count, offset, lbuf, len);
-}
-
-static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
-{
- void __iomem *address = (void __iomem *)file->private_data;
- char *buff;
- unsigned int value;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > 1024)
- return 0;
- if (*offset != 0)
- return 0;
-
- buff = memdup_user_nul(ubuff, count);
- if (IS_ERR(buff))
- return PTR_ERR(buff);
-
- value = simple_strtoul(buff, NULL, 10);
- writel(value, address);
- kfree(buff);
-
- return count;
-}
-
-static const struct file_operations command_fops = {
- .open = command_file_open,
- .release = command_file_close,
- .read = command_file_read,
- .write = command_file_write,
- .llseek = generic_file_llseek,
-};
-
-static const struct file_operations event_fops = {
- .open = event_file_open,
- .release = event_file_close,
- .read = event_file_read,
- .write = event_file_write,
- .llseek = generic_file_llseek,
-};
-
-static const struct file_operations r_heartbeat_fops = {
- .open = r_heartbeat_file_open,
- .release = r_heartbeat_file_close,
- .read = r_heartbeat_file_read,
- .write = r_heartbeat_file_write,
- .llseek = generic_file_llseek,
-};
-
-static const struct file_operations remote_settings_fops = {
- .open = simple_open,
- .release = remote_settings_file_close,
- .read = remote_settings_file_read,
- .write = remote_settings_file_write,
- .llseek = generic_file_llseek,
-};
-
-
-static void ibmasmfs_create_files (struct super_block *sb)
-{
- struct list_head *entry;
- struct service_processor *sp;
-
- list_for_each(entry, &service_processors) {
- struct dentry *dir;
- struct dentry *remote_dir;
- sp = list_entry(entry, struct service_processor, node);
- dir = ibmasmfs_create_dir(sb->s_root, sp->dirname);
- if (!dir)
- continue;
-
- ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR);
-
- remote_dir = ibmasmfs_create_dir(dir, "remote_video");
- if (!remote_dir)
- continue;
-
- ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR);
- }
-}
diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c
deleted file mode 100644
index 5313230f36ad..000000000000
--- a/drivers/misc/ibmasm/lowlevel.c
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include "ibmasm.h"
-#include "lowlevel.h"
-#include "i2o.h"
-#include "dot_command.h"
-#include "remote.h"
-
-static struct i2o_header header = I2O_HEADER_TEMPLATE;
-
-
-int ibmasm_send_i2o_message(struct service_processor *sp)
-{
- u32 mfa;
- size_t command_size;
- struct i2o_message *message;
- struct command *command = sp->current_command;
-
- command_size = get_dot_command_size(command->buffer);
- if (command_size > command->buffer_size)
- return 1;
- if (command_size > I2O_COMMAND_SIZE)
- command_size = I2O_COMMAND_SIZE;
-
- mfa = get_mfa_inbound(sp->base_address);
- if (!mfa)
- return 1;
-
- header.message_size = outgoing_message_size((unsigned int)command_size);
- message = get_i2o_message(sp->base_address, mfa);
-
- memcpy_toio(&message->header, &header, sizeof(struct i2o_header));
- memcpy_toio(&message->data, command->buffer, command_size);
-
- set_mfa_inbound(sp->base_address, mfa);
-
- return 0;
-}
-
-irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id)
-{
- u32 mfa;
- struct service_processor *sp = (struct service_processor *)dev_id;
- void __iomem *base_address = sp->base_address;
- char tsbuf[32];
-
- if (!sp_interrupt_pending(base_address))
- return IRQ_NONE;
-
- dbg("respond to interrupt at %s\n", get_timestamp(tsbuf));
-
- if (mouse_interrupt_pending(sp)) {
- ibmasm_handle_mouse_interrupt(sp);
- clear_mouse_interrupt(sp);
- }
-
- mfa = get_mfa_outbound(base_address);
- if (valid_mfa(mfa)) {
- struct i2o_message *msg = get_i2o_message(base_address, mfa);
- ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg));
- } else
- dbg("didn't get a valid MFA\n");
-
- set_mfa_outbound(base_address, mfa);
- dbg("finished interrupt at %s\n", get_timestamp(tsbuf));
-
- return IRQ_HANDLED;
-}
diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h
deleted file mode 100644
index 25f1ed07c3c5..000000000000
--- a/drivers/misc/ibmasm/lowlevel.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-/* Condor service processor specific hardware definitions */
-
-#ifndef __IBMASM_CONDOR_H__
-#define __IBMASM_CONDOR_H__
-
-#include <asm/io.h>
-
-#define VENDORID_IBM 0x1014
-#define DEVICEID_RSA 0x010F
-
-#define GET_MFA_ADDR(x) (x & 0xFFFFFF00)
-
-#define MAILBOX_FULL(x) (x & 0x00000001)
-
-#define NO_MFAS_AVAILABLE 0xFFFFFFFF
-
-
-#define INBOUND_QUEUE_PORT 0x40 /* contains address of next free MFA */
-#define OUTBOUND_QUEUE_PORT 0x44 /* contains address of posted MFA */
-
-#define SP_INTR_MASK 0x00000008
-#define UART_INTR_MASK 0x00000010
-
-#define INTR_STATUS_REGISTER 0x13A0
-#define INTR_CONTROL_REGISTER 0x13A4
-
-#define SCOUT_COM_A_BASE 0x0000
-#define SCOUT_COM_B_BASE 0x0100
-#define SCOUT_COM_C_BASE 0x0200
-#define SCOUT_COM_D_BASE 0x0300
-
-static inline int sp_interrupt_pending(void __iomem *base_address)
-{
- return SP_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER);
-}
-
-static inline int uart_interrupt_pending(void __iomem *base_address)
-{
- return UART_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER);
-}
-
-static inline void ibmasm_enable_interrupts(void __iomem *base_address, int mask)
-{
- void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER;
- writel( readl(ctrl_reg) & ~mask, ctrl_reg);
-}
-
-static inline void ibmasm_disable_interrupts(void __iomem *base_address, int mask)
-{
- void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER;
- writel( readl(ctrl_reg) | mask, ctrl_reg);
-}
-
-static inline void enable_sp_interrupts(void __iomem *base_address)
-{
- ibmasm_enable_interrupts(base_address, SP_INTR_MASK);
-}
-
-static inline void disable_sp_interrupts(void __iomem *base_address)
-{
- ibmasm_disable_interrupts(base_address, SP_INTR_MASK);
-}
-
-static inline void enable_uart_interrupts(void __iomem *base_address)
-{
- ibmasm_enable_interrupts(base_address, UART_INTR_MASK);
-}
-
-static inline void disable_uart_interrupts(void __iomem *base_address)
-{
- ibmasm_disable_interrupts(base_address, UART_INTR_MASK);
-}
-
-#define valid_mfa(mfa) ( (mfa) != NO_MFAS_AVAILABLE )
-
-static inline u32 get_mfa_outbound(void __iomem *base_address)
-{
- int retry;
- u32 mfa;
-
- for (retry=0; retry<=10; retry++) {
- mfa = readl(base_address + OUTBOUND_QUEUE_PORT);
- if (valid_mfa(mfa))
- break;
- }
- return mfa;
-}
-
-static inline void set_mfa_outbound(void __iomem *base_address, u32 mfa)
-{
- writel(mfa, base_address + OUTBOUND_QUEUE_PORT);
-}
-
-static inline u32 get_mfa_inbound(void __iomem *base_address)
-{
- u32 mfa = readl(base_address + INBOUND_QUEUE_PORT);
-
- if (MAILBOX_FULL(mfa))
- return 0;
-
- return mfa;
-}
-
-static inline void set_mfa_inbound(void __iomem *base_address, u32 mfa)
-{
- writel(mfa, base_address + INBOUND_QUEUE_PORT);
-}
-
-static inline struct i2o_message *get_i2o_message(void __iomem *base_address, u32 mfa)
-{
- return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address);
-}
-
-#endif /* __IBMASM_CONDOR_H__ */
diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c
deleted file mode 100644
index 4509c15a76a8..000000000000
--- a/drivers/misc/ibmasm/module.c
+++ /dev/null
@@ -1,224 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- *
- * This driver is based on code originally written by Pete Reynolds
- * and others.
- */
-
-/*
- * The ASM device driver does the following things:
- *
- * 1) When loaded it sends a message to the service processor,
- * indicating that an OS is * running. This causes the service processor
- * to send periodic heartbeats to the OS.
- *
- * 2) Answers the periodic heartbeats sent by the service processor.
- * Failure to do so would result in system reboot.
- *
- * 3) Acts as a pass through for dot commands sent from user applications.
- * The interface for this is the ibmasmfs file system.
- *
- * 4) Allows user applications to register for event notification. Events
- * are sent to the driver through interrupts. They can be read from user
- * space through the ibmasmfs file system.
- *
- * 5) Allows user space applications to send heartbeats to the service
- * processor (aka reverse heartbeats). Again this happens through ibmasmfs.
- *
- * 6) Handles remote mouse and keyboard event interrupts and makes them
- * available to user applications through ibmasmfs.
- *
- */
-
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-#include "remote.h"
-
-int ibmasm_debug = 0;
-module_param(ibmasm_debug, int , S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(ibmasm_debug, " Set debug mode on or off");
-
-
-static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- int result;
- struct service_processor *sp;
-
- if ((result = pci_enable_device(pdev))) {
- dev_err(&pdev->dev, "Failed to enable PCI device\n");
- return result;
- }
- if ((result = pci_request_regions(pdev, DRIVER_NAME))) {
- dev_err(&pdev->dev, "Failed to allocate PCI resources\n");
- goto error_resources;
- }
- /* vnc client won't work without bus-mastering */
- pci_set_master(pdev);
-
- sp = kzalloc_obj(struct service_processor);
- if (sp == NULL) {
- dev_err(&pdev->dev, "Failed to allocate memory\n");
- result = -ENOMEM;
- goto error_kmalloc;
- }
-
- spin_lock_init(&sp->lock);
- INIT_LIST_HEAD(&sp->command_queue);
-
- pci_set_drvdata(pdev, (void *)sp);
- sp->dev = &pdev->dev;
- sp->number = pdev->bus->number;
- snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number);
- snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number);
-
- result = ibmasm_event_buffer_init(sp);
- if (result) {
- dev_err(sp->dev, "Failed to allocate event buffer\n");
- goto error_eventbuffer;
- }
-
- result = ibmasm_heartbeat_init(sp);
- if (result) {
- dev_err(sp->dev, "Failed to allocate heartbeat command\n");
- goto error_heartbeat;
- }
-
- sp->irq = pdev->irq;
- sp->base_address = pci_ioremap_bar(pdev, 0);
- if (!sp->base_address) {
- dev_err(sp->dev, "Failed to ioremap pci memory\n");
- result = -ENODEV;
- goto error_ioremap;
- }
-
- result = request_irq(sp->irq, ibmasm_interrupt_handler, IRQF_SHARED, sp->devname, (void*)sp);
- if (result) {
- dev_err(sp->dev, "Failed to register interrupt handler\n");
- goto error_request_irq;
- }
-
- enable_sp_interrupts(sp->base_address);
-
- result = ibmasm_init_remote_input_dev(sp);
- if (result) {
- dev_err(sp->dev, "Failed to initialize remote queue\n");
- goto error_init_remote;
- }
-
- result = ibmasm_send_driver_vpd(sp);
- if (result) {
- dev_err(sp->dev, "Failed to send driver VPD to service processor\n");
- goto error_send_message;
- }
- result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP);
- if (result) {
- dev_err(sp->dev, "Failed to send OS state to service processor\n");
- goto error_send_message;
- }
- ibmasmfs_add_sp(sp);
-
- ibmasm_register_uart(sp);
-
- return 0;
-
-error_send_message:
- ibmasm_free_remote_input_dev(sp);
-error_init_remote:
- disable_sp_interrupts(sp->base_address);
- free_irq(sp->irq, (void *)sp);
-error_request_irq:
- iounmap(sp->base_address);
-error_ioremap:
- ibmasm_heartbeat_exit(sp);
-error_heartbeat:
- ibmasm_event_buffer_exit(sp);
-error_eventbuffer:
- kfree(sp);
-error_kmalloc:
- pci_release_regions(pdev);
-error_resources:
- pci_disable_device(pdev);
-
- return result;
-}
-
-static void ibmasm_remove_one(struct pci_dev *pdev)
-{
- struct service_processor *sp = pci_get_drvdata(pdev);
-
- dbg("Unregistering UART\n");
- ibmasm_unregister_uart(sp);
- dbg("Sending OS down message\n");
- if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN))
- err("failed to get response to 'Send OS State' command\n");
- dbg("Disabling heartbeats\n");
- ibmasm_heartbeat_exit(sp);
- dbg("Disabling interrupts\n");
- disable_sp_interrupts(sp->base_address);
- dbg("Freeing SP irq\n");
- free_irq(sp->irq, (void *)sp);
- dbg("Cleaning up\n");
- ibmasm_free_remote_input_dev(sp);
- iounmap(sp->base_address);
- ibmasm_event_buffer_exit(sp);
- kfree(sp);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
-}
-
-static struct pci_device_id ibmasm_pci_table[] =
-{
- { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) },
- {},
-};
-
-static struct pci_driver ibmasm_driver = {
- .name = DRIVER_NAME,
- .id_table = ibmasm_pci_table,
- .probe = ibmasm_init_one,
- .remove = ibmasm_remove_one,
-};
-
-static void __exit ibmasm_exit (void)
-{
- ibmasm_unregister_panic_notifier();
- ibmasmfs_unregister();
- pci_unregister_driver(&ibmasm_driver);
- info(DRIVER_DESC " version " DRIVER_VERSION " unloaded");
-}
-
-static int __init ibmasm_init(void)
-{
- int result = pci_register_driver(&ibmasm_driver);
- if (result)
- return result;
-
- result = ibmasmfs_register();
- if (result) {
- pci_unregister_driver(&ibmasm_driver);
- err("Failed to register ibmasmfs file system");
- return result;
- }
-
- ibmasm_register_panic_notifier();
- info(DRIVER_DESC " version " DRIVER_VERSION " loaded");
- return 0;
-}
-
-module_init(ibmasm_init);
-module_exit(ibmasm_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(pci, ibmasm_pci_table);
-
diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c
deleted file mode 100644
index 21c9b6a6f2c3..000000000000
--- a/drivers/misc/ibmasm/r_heartbeat.c
+++ /dev/null
@@ -1,86 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/sched/signal.h>
-#include "ibmasm.h"
-#include "dot_command.h"
-
-/*
- * Reverse Heartbeat, i.e. heartbeats sent from the driver to the
- * service processor.
- * These heartbeats are initiated by user level programs.
- */
-
-/* the reverse heartbeat dot command */
-#pragma pack(1)
-static struct {
- struct dot_command_header header;
- unsigned char command[3];
-} rhb_dot_cmd = {
- .header = {
- .type = sp_read,
- .command_size = 3,
- .data_size = 0,
- .status = 0
- },
- .command = { 4, 3, 6 }
-};
-#pragma pack()
-
-void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
-{
- init_waitqueue_head(&rhb->wait);
- rhb->stopped = 0;
-}
-
-/*
- * start_reverse_heartbeat
- * Loop forever, sending a reverse heartbeat dot command to the service
- * processor, then sleeping. The loop comes to an end if the service
- * processor fails to respond 3 times or we were interrupted.
- */
-int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
-{
- struct command *cmd;
- int times_failed = 0;
- int result = 1;
-
- cmd = ibmasm_new_command(sp, sizeof rhb_dot_cmd);
- if (!cmd)
- return -ENOMEM;
-
- while (times_failed < 3) {
- memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd);
- cmd->status = IBMASM_CMD_PENDING;
- ibmasm_exec_command(sp, cmd);
- ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
-
- if (cmd->status != IBMASM_CMD_COMPLETE)
- times_failed++;
-
- wait_event_interruptible_timeout(rhb->wait,
- rhb->stopped,
- REVERSE_HEARTBEAT_TIMEOUT * HZ);
-
- if (signal_pending(current) || rhb->stopped) {
- result = -EINTR;
- break;
- }
- }
- command_put(cmd);
- rhb->stopped = 0;
-
- return result;
-}
-
-void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb)
-{
- rhb->stopped = 1;
- wake_up_interruptible(&rhb->wait);
-}
diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c
deleted file mode 100644
index 521531738c9a..000000000000
--- a/drivers/misc/ibmasm/remote.c
+++ /dev/null
@@ -1,273 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Authors: Max Asböck <amax@us.ibm.com>
- * Vernon Mauery <vernux@us.ibm.com>
- */
-
-/* Remote mouse and keyboard event handling functions */
-
-#include <linux/pci.h>
-#include "ibmasm.h"
-#include "remote.h"
-
-#define MOUSE_X_MAX 1600
-#define MOUSE_Y_MAX 1200
-
-static const unsigned short xlate_high[XLATE_SIZE] = {
- [KEY_SYM_ENTER & 0xff] = KEY_ENTER,
- [KEY_SYM_KPSLASH & 0xff] = KEY_KPSLASH,
- [KEY_SYM_KPSTAR & 0xff] = KEY_KPASTERISK,
- [KEY_SYM_KPMINUS & 0xff] = KEY_KPMINUS,
- [KEY_SYM_KPDOT & 0xff] = KEY_KPDOT,
- [KEY_SYM_KPPLUS & 0xff] = KEY_KPPLUS,
- [KEY_SYM_KP0 & 0xff] = KEY_KP0,
- [KEY_SYM_KP1 & 0xff] = KEY_KP1,
- [KEY_SYM_KP2 & 0xff] = KEY_KP2, [KEY_SYM_KPDOWN & 0xff] = KEY_KP2,
- [KEY_SYM_KP3 & 0xff] = KEY_KP3,
- [KEY_SYM_KP4 & 0xff] = KEY_KP4, [KEY_SYM_KPLEFT & 0xff] = KEY_KP4,
- [KEY_SYM_KP5 & 0xff] = KEY_KP5,
- [KEY_SYM_KP6 & 0xff] = KEY_KP6, [KEY_SYM_KPRIGHT & 0xff] = KEY_KP6,
- [KEY_SYM_KP7 & 0xff] = KEY_KP7,
- [KEY_SYM_KP8 & 0xff] = KEY_KP8, [KEY_SYM_KPUP & 0xff] = KEY_KP8,
- [KEY_SYM_KP9 & 0xff] = KEY_KP9,
- [KEY_SYM_BK_SPC & 0xff] = KEY_BACKSPACE,
- [KEY_SYM_TAB & 0xff] = KEY_TAB,
- [KEY_SYM_CTRL & 0xff] = KEY_LEFTCTRL,
- [KEY_SYM_ALT & 0xff] = KEY_LEFTALT,
- [KEY_SYM_INSERT & 0xff] = KEY_INSERT,
- [KEY_SYM_DELETE & 0xff] = KEY_DELETE,
- [KEY_SYM_SHIFT & 0xff] = KEY_LEFTSHIFT,
- [KEY_SYM_UARROW & 0xff] = KEY_UP,
- [KEY_SYM_DARROW & 0xff] = KEY_DOWN,
- [KEY_SYM_LARROW & 0xff] = KEY_LEFT,
- [KEY_SYM_RARROW & 0xff] = KEY_RIGHT,
- [KEY_SYM_ESCAPE & 0xff] = KEY_ESC,
- [KEY_SYM_PAGEUP & 0xff] = KEY_PAGEUP,
- [KEY_SYM_PAGEDOWN & 0xff] = KEY_PAGEDOWN,
- [KEY_SYM_HOME & 0xff] = KEY_HOME,
- [KEY_SYM_END & 0xff] = KEY_END,
- [KEY_SYM_F1 & 0xff] = KEY_F1,
- [KEY_SYM_F2 & 0xff] = KEY_F2,
- [KEY_SYM_F3 & 0xff] = KEY_F3,
- [KEY_SYM_F4 & 0xff] = KEY_F4,
- [KEY_SYM_F5 & 0xff] = KEY_F5,
- [KEY_SYM_F6 & 0xff] = KEY_F6,
- [KEY_SYM_F7 & 0xff] = KEY_F7,
- [KEY_SYM_F8 & 0xff] = KEY_F8,
- [KEY_SYM_F9 & 0xff] = KEY_F9,
- [KEY_SYM_F10 & 0xff] = KEY_F10,
- [KEY_SYM_F11 & 0xff] = KEY_F11,
- [KEY_SYM_F12 & 0xff] = KEY_F12,
- [KEY_SYM_CAP_LOCK & 0xff] = KEY_CAPSLOCK,
- [KEY_SYM_NUM_LOCK & 0xff] = KEY_NUMLOCK,
- [KEY_SYM_SCR_LOCK & 0xff] = KEY_SCROLLLOCK,
-};
-
-static const unsigned short xlate[XLATE_SIZE] = {
- [NO_KEYCODE] = KEY_RESERVED,
- [KEY_SYM_SPACE] = KEY_SPACE,
- [KEY_SYM_TILDE] = KEY_GRAVE, [KEY_SYM_BKTIC] = KEY_GRAVE,
- [KEY_SYM_ONE] = KEY_1, [KEY_SYM_BANG] = KEY_1,
- [KEY_SYM_TWO] = KEY_2, [KEY_SYM_AT] = KEY_2,
- [KEY_SYM_THREE] = KEY_3, [KEY_SYM_POUND] = KEY_3,
- [KEY_SYM_FOUR] = KEY_4, [KEY_SYM_DOLLAR] = KEY_4,
- [KEY_SYM_FIVE] = KEY_5, [KEY_SYM_PERCENT] = KEY_5,
- [KEY_SYM_SIX] = KEY_6, [KEY_SYM_CARAT] = KEY_6,
- [KEY_SYM_SEVEN] = KEY_7, [KEY_SYM_AMPER] = KEY_7,
- [KEY_SYM_EIGHT] = KEY_8, [KEY_SYM_STAR] = KEY_8,
- [KEY_SYM_NINE] = KEY_9, [KEY_SYM_LPAREN] = KEY_9,
- [KEY_SYM_ZERO] = KEY_0, [KEY_SYM_RPAREN] = KEY_0,
- [KEY_SYM_MINUS] = KEY_MINUS, [KEY_SYM_USCORE] = KEY_MINUS,
- [KEY_SYM_EQUAL] = KEY_EQUAL, [KEY_SYM_PLUS] = KEY_EQUAL,
- [KEY_SYM_LBRKT] = KEY_LEFTBRACE, [KEY_SYM_LCURLY] = KEY_LEFTBRACE,
- [KEY_SYM_RBRKT] = KEY_RIGHTBRACE, [KEY_SYM_RCURLY] = KEY_RIGHTBRACE,
- [KEY_SYM_SLASH] = KEY_BACKSLASH, [KEY_SYM_PIPE] = KEY_BACKSLASH,
- [KEY_SYM_TIC] = KEY_APOSTROPHE, [KEY_SYM_QUOTE] = KEY_APOSTROPHE,
- [KEY_SYM_SEMIC] = KEY_SEMICOLON, [KEY_SYM_COLON] = KEY_SEMICOLON,
- [KEY_SYM_COMMA] = KEY_COMMA, [KEY_SYM_LT] = KEY_COMMA,
- [KEY_SYM_PERIOD] = KEY_DOT, [KEY_SYM_GT] = KEY_DOT,
- [KEY_SYM_BSLASH] = KEY_SLASH, [KEY_SYM_QMARK] = KEY_SLASH,
- [KEY_SYM_A] = KEY_A, [KEY_SYM_a] = KEY_A,
- [KEY_SYM_B] = KEY_B, [KEY_SYM_b] = KEY_B,
- [KEY_SYM_C] = KEY_C, [KEY_SYM_c] = KEY_C,
- [KEY_SYM_D] = KEY_D, [KEY_SYM_d] = KEY_D,
- [KEY_SYM_E] = KEY_E, [KEY_SYM_e] = KEY_E,
- [KEY_SYM_F] = KEY_F, [KEY_SYM_f] = KEY_F,
- [KEY_SYM_G] = KEY_G, [KEY_SYM_g] = KEY_G,
- [KEY_SYM_H] = KEY_H, [KEY_SYM_h] = KEY_H,
- [KEY_SYM_I] = KEY_I, [KEY_SYM_i] = KEY_I,
- [KEY_SYM_J] = KEY_J, [KEY_SYM_j] = KEY_J,
- [KEY_SYM_K] = KEY_K, [KEY_SYM_k] = KEY_K,
- [KEY_SYM_L] = KEY_L, [KEY_SYM_l] = KEY_L,
- [KEY_SYM_M] = KEY_M, [KEY_SYM_m] = KEY_M,
- [KEY_SYM_N] = KEY_N, [KEY_SYM_n] = KEY_N,
- [KEY_SYM_O] = KEY_O, [KEY_SYM_o] = KEY_O,
- [KEY_SYM_P] = KEY_P, [KEY_SYM_p] = KEY_P,
- [KEY_SYM_Q] = KEY_Q, [KEY_SYM_q] = KEY_Q,
- [KEY_SYM_R] = KEY_R, [KEY_SYM_r] = KEY_R,
- [KEY_SYM_S] = KEY_S, [KEY_SYM_s] = KEY_S,
- [KEY_SYM_T] = KEY_T, [KEY_SYM_t] = KEY_T,
- [KEY_SYM_U] = KEY_U, [KEY_SYM_u] = KEY_U,
- [KEY_SYM_V] = KEY_V, [KEY_SYM_v] = KEY_V,
- [KEY_SYM_W] = KEY_W, [KEY_SYM_w] = KEY_W,
- [KEY_SYM_X] = KEY_X, [KEY_SYM_x] = KEY_X,
- [KEY_SYM_Y] = KEY_Y, [KEY_SYM_y] = KEY_Y,
- [KEY_SYM_Z] = KEY_Z, [KEY_SYM_z] = KEY_Z,
-};
-
-static void print_input(struct remote_input *input)
-{
- if (input->type == INPUT_TYPE_MOUSE) {
- unsigned char buttons = input->mouse_buttons;
- dbg("remote mouse movement: (x,y)=(%d,%d)%s%s%s%s\n",
- input->data.mouse.x, input->data.mouse.y,
- (buttons) ? " -- buttons:" : "",
- (buttons & REMOTE_BUTTON_LEFT) ? "left " : "",
- (buttons & REMOTE_BUTTON_MIDDLE) ? "middle " : "",
- (buttons & REMOTE_BUTTON_RIGHT) ? "right" : ""
- );
- } else {
- dbg("remote keypress (code, flag, down):"
- "%d (0x%x) [0x%x] [0x%x]\n",
- input->data.keyboard.key_code,
- input->data.keyboard.key_code,
- input->data.keyboard.key_flag,
- input->data.keyboard.key_down
- );
- }
-}
-
-static void send_mouse_event(struct input_dev *dev, struct remote_input *input)
-{
- unsigned char buttons = input->mouse_buttons;
-
- input_report_abs(dev, ABS_X, input->data.mouse.x);
- input_report_abs(dev, ABS_Y, input->data.mouse.y);
- input_report_key(dev, BTN_LEFT, buttons & REMOTE_BUTTON_LEFT);
- input_report_key(dev, BTN_MIDDLE, buttons & REMOTE_BUTTON_MIDDLE);
- input_report_key(dev, BTN_RIGHT, buttons & REMOTE_BUTTON_RIGHT);
- input_sync(dev);
-}
-
-static void send_keyboard_event(struct input_dev *dev,
- struct remote_input *input)
-{
- unsigned int key;
- unsigned short code = input->data.keyboard.key_code;
-
- if (code & 0xff00)
- key = xlate_high[code & 0xff];
- else
- key = xlate[code];
- input_report_key(dev, key, input->data.keyboard.key_down);
- input_sync(dev);
-}
-
-void ibmasm_handle_mouse_interrupt(struct service_processor *sp)
-{
- unsigned long reader;
- unsigned long writer;
- struct remote_input input;
-
- reader = get_queue_reader(sp);
- writer = get_queue_writer(sp);
-
- while (reader != writer) {
- if (reader >= REMOTE_QUEUE_SIZE || writer >= REMOTE_QUEUE_SIZE) {
- set_queue_reader(sp, 0);
- break;
- }
-
- memcpy_fromio(&input, get_queue_entry(sp, reader),
- sizeof(struct remote_input));
-
- print_input(&input);
- if (input.type == INPUT_TYPE_MOUSE) {
- send_mouse_event(sp->remote.mouse_dev, &input);
- } else if (input.type == INPUT_TYPE_KEYBOARD) {
- send_keyboard_event(sp->remote.keybd_dev, &input);
- } else
- break;
-
- reader = advance_queue_reader(sp, reader);
- writer = get_queue_writer(sp);
- }
-}
-
-int ibmasm_init_remote_input_dev(struct service_processor *sp)
-{
- /* set up the mouse input device */
- struct input_dev *mouse_dev, *keybd_dev;
- struct pci_dev *pdev = to_pci_dev(sp->dev);
- int error = -ENOMEM;
- int i;
-
- sp->remote.mouse_dev = mouse_dev = input_allocate_device();
- sp->remote.keybd_dev = keybd_dev = input_allocate_device();
-
- if (!mouse_dev || !keybd_dev)
- goto err_free_devices;
-
- mouse_dev->id.bustype = BUS_PCI;
- mouse_dev->id.vendor = pdev->vendor;
- mouse_dev->id.product = pdev->device;
- mouse_dev->id.version = 1;
- mouse_dev->dev.parent = sp->dev;
- mouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- mouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
- BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
- set_bit(BTN_TOUCH, mouse_dev->keybit);
- mouse_dev->name = "ibmasm RSA I remote mouse";
- input_set_abs_params(mouse_dev, ABS_X, 0, MOUSE_X_MAX, 0, 0);
- input_set_abs_params(mouse_dev, ABS_Y, 0, MOUSE_Y_MAX, 0, 0);
-
- keybd_dev->id.bustype = BUS_PCI;
- keybd_dev->id.vendor = pdev->vendor;
- keybd_dev->id.product = pdev->device;
- keybd_dev->id.version = 2;
- keybd_dev->dev.parent = sp->dev;
- keybd_dev->evbit[0] = BIT_MASK(EV_KEY);
- keybd_dev->name = "ibmasm RSA I remote keyboard";
-
- for (i = 0; i < XLATE_SIZE; i++) {
- if (xlate_high[i])
- set_bit(xlate_high[i], keybd_dev->keybit);
- if (xlate[i])
- set_bit(xlate[i], keybd_dev->keybit);
- }
-
- error = input_register_device(mouse_dev);
- if (error)
- goto err_free_devices;
-
- error = input_register_device(keybd_dev);
- if (error)
- goto err_unregister_mouse_dev;
-
- enable_mouse_interrupts(sp);
-
- printk(KERN_INFO "ibmasm remote responding to events on RSA card %d\n", sp->number);
-
- return 0;
-
- err_unregister_mouse_dev:
- input_unregister_device(mouse_dev);
- mouse_dev = NULL; /* so we don't try to free it again below */
- err_free_devices:
- input_free_device(mouse_dev);
- input_free_device(keybd_dev);
-
- return error;
-}
-
-void ibmasm_free_remote_input_dev(struct service_processor *sp)
-{
- disable_mouse_interrupts(sp);
- input_unregister_device(sp->remote.mouse_dev);
- input_unregister_device(sp->remote.keybd_dev);
-}
-
diff --git a/drivers/misc/ibmasm/remote.h b/drivers/misc/ibmasm/remote.h
deleted file mode 100644
index ec4e78ec5a67..000000000000
--- a/drivers/misc/ibmasm/remote.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- *
- * Originally written by Pete Reynolds
- */
-
-#ifndef _IBMASM_REMOTE_H_
-#define _IBMASM_REMOTE_H_
-
-#include <asm/io.h>
-
-/* pci offsets */
-#define CONDOR_MOUSE_DATA 0x000AC000
-#define CONDOR_MOUSE_ISR_CONTROL 0x00
-#define CONDOR_MOUSE_ISR_STATUS 0x04
-#define CONDOR_MOUSE_Q_READER 0x08
-#define CONDOR_MOUSE_Q_WRITER 0x0C
-#define CONDOR_MOUSE_Q_BEGIN 0x10
-#define CONDOR_MOUSE_MAX_X 0x14
-#define CONDOR_MOUSE_MAX_Y 0x18
-
-#define CONDOR_INPUT_DESKTOP_INFO 0x1F0
-#define CONDOR_INPUT_DISPLAY_RESX 0x1F4
-#define CONDOR_INPUT_DISPLAY_RESY 0x1F8
-#define CONDOR_INPUT_DISPLAY_BITS 0x1FC
-#define CONDOR_OUTPUT_VNC_STATUS 0x200
-
-#define CONDOR_MOUSE_INTR_STATUS_MASK 0x00000001
-
-#define INPUT_TYPE_MOUSE 0x1
-#define INPUT_TYPE_KEYBOARD 0x2
-
-
-/* mouse button states received from SP */
-#define REMOTE_DOUBLE_CLICK 0xF0
-#define REMOTE_BUTTON_LEFT 0x01
-#define REMOTE_BUTTON_MIDDLE 0x02
-#define REMOTE_BUTTON_RIGHT 0x04
-
-/* size of keysym/keycode translation matrices */
-#define XLATE_SIZE 256
-
-struct mouse_input {
- unsigned short y;
- unsigned short x;
-};
-
-
-struct keyboard_input {
- unsigned short key_code;
- unsigned char key_flag;
- unsigned char key_down;
-};
-
-
-
-struct remote_input {
- union {
- struct mouse_input mouse;
- struct keyboard_input keyboard;
- } data;
-
- unsigned char type;
- unsigned char pad1;
- unsigned char mouse_buttons;
- unsigned char pad3;
-};
-
-#define mouse_addr(sp) (sp->base_address + CONDOR_MOUSE_DATA)
-#define display_width(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESX)
-#define display_height(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESY)
-#define display_depth(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_BITS)
-#define desktop_info(sp) (mouse_addr(sp) + CONDOR_INPUT_DESKTOP_INFO)
-#define vnc_status(sp) (mouse_addr(sp) + CONDOR_OUTPUT_VNC_STATUS)
-#define isr_control(sp) (mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
-
-#define mouse_interrupt_pending(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS)
-#define clear_mouse_interrupt(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS)
-#define enable_mouse_interrupts(sp) writel(1, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
-#define disable_mouse_interrupts(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
-
-/* remote input queue operations */
-#define REMOTE_QUEUE_SIZE 60
-
-#define get_queue_writer(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_WRITER)
-#define get_queue_reader(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_READER)
-#define set_queue_reader(sp, reader) writel(reader, mouse_addr(sp) + CONDOR_MOUSE_Q_READER)
-
-#define queue_begin (mouse_addr(sp) + CONDOR_MOUSE_Q_BEGIN)
-
-#define get_queue_entry(sp, read_index) \
- ((void*)(queue_begin + read_index * sizeof(struct remote_input)))
-
-static inline int advance_queue_reader(struct service_processor *sp, unsigned long reader)
-{
- reader++;
- if (reader == REMOTE_QUEUE_SIZE)
- reader = 0;
-
- set_queue_reader(sp, reader);
- return reader;
-}
-
-#define NO_KEYCODE 0
-#define KEY_SYM_BK_SPC 0xFF08
-#define KEY_SYM_TAB 0xFF09
-#define KEY_SYM_ENTER 0xFF0D
-#define KEY_SYM_SCR_LOCK 0xFF14
-#define KEY_SYM_ESCAPE 0xFF1B
-#define KEY_SYM_HOME 0xFF50
-#define KEY_SYM_LARROW 0xFF51
-#define KEY_SYM_UARROW 0xFF52
-#define KEY_SYM_RARROW 0xFF53
-#define KEY_SYM_DARROW 0xFF54
-#define KEY_SYM_PAGEUP 0xFF55
-#define KEY_SYM_PAGEDOWN 0xFF56
-#define KEY_SYM_END 0xFF57
-#define KEY_SYM_INSERT 0xFF63
-#define KEY_SYM_NUM_LOCK 0xFF7F
-#define KEY_SYM_KPSTAR 0xFFAA
-#define KEY_SYM_KPPLUS 0xFFAB
-#define KEY_SYM_KPMINUS 0xFFAD
-#define KEY_SYM_KPDOT 0xFFAE
-#define KEY_SYM_KPSLASH 0xFFAF
-#define KEY_SYM_KPRIGHT 0xFF96
-#define KEY_SYM_KPUP 0xFF97
-#define KEY_SYM_KPLEFT 0xFF98
-#define KEY_SYM_KPDOWN 0xFF99
-#define KEY_SYM_KP0 0xFFB0
-#define KEY_SYM_KP1 0xFFB1
-#define KEY_SYM_KP2 0xFFB2
-#define KEY_SYM_KP3 0xFFB3
-#define KEY_SYM_KP4 0xFFB4
-#define KEY_SYM_KP5 0xFFB5
-#define KEY_SYM_KP6 0xFFB6
-#define KEY_SYM_KP7 0xFFB7
-#define KEY_SYM_KP8 0xFFB8
-#define KEY_SYM_KP9 0xFFB9
-#define KEY_SYM_F1 0xFFBE // 1B 5B 5B 41
-#define KEY_SYM_F2 0xFFBF // 1B 5B 5B 42
-#define KEY_SYM_F3 0xFFC0 // 1B 5B 5B 43
-#define KEY_SYM_F4 0xFFC1 // 1B 5B 5B 44
-#define KEY_SYM_F5 0xFFC2 // 1B 5B 5B 45
-#define KEY_SYM_F6 0xFFC3 // 1B 5B 31 37 7E
-#define KEY_SYM_F7 0xFFC4 // 1B 5B 31 38 7E
-#define KEY_SYM_F8 0xFFC5 // 1B 5B 31 39 7E
-#define KEY_SYM_F9 0xFFC6 // 1B 5B 32 30 7E
-#define KEY_SYM_F10 0xFFC7 // 1B 5B 32 31 7E
-#define KEY_SYM_F11 0xFFC8 // 1B 5B 32 33 7E
-#define KEY_SYM_F12 0xFFC9 // 1B 5B 32 34 7E
-#define KEY_SYM_SHIFT 0xFFE1
-#define KEY_SYM_CTRL 0xFFE3
-#define KEY_SYM_ALT 0xFFE9
-#define KEY_SYM_CAP_LOCK 0xFFE5
-#define KEY_SYM_DELETE 0xFFFF
-#define KEY_SYM_TILDE 0x60
-#define KEY_SYM_BKTIC 0x7E
-#define KEY_SYM_ONE 0x31
-#define KEY_SYM_BANG 0x21
-#define KEY_SYM_TWO 0x32
-#define KEY_SYM_AT 0x40
-#define KEY_SYM_THREE 0x33
-#define KEY_SYM_POUND 0x23
-#define KEY_SYM_FOUR 0x34
-#define KEY_SYM_DOLLAR 0x24
-#define KEY_SYM_FIVE 0x35
-#define KEY_SYM_PERCENT 0x25
-#define KEY_SYM_SIX 0x36
-#define KEY_SYM_CARAT 0x5E
-#define KEY_SYM_SEVEN 0x37
-#define KEY_SYM_AMPER 0x26
-#define KEY_SYM_EIGHT 0x38
-#define KEY_SYM_STAR 0x2A
-#define KEY_SYM_NINE 0x39
-#define KEY_SYM_LPAREN 0x28
-#define KEY_SYM_ZERO 0x30
-#define KEY_SYM_RPAREN 0x29
-#define KEY_SYM_MINUS 0x2D
-#define KEY_SYM_USCORE 0x5F
-#define KEY_SYM_EQUAL 0x2B
-#define KEY_SYM_PLUS 0x3D
-#define KEY_SYM_LBRKT 0x5B
-#define KEY_SYM_LCURLY 0x7B
-#define KEY_SYM_RBRKT 0x5D
-#define KEY_SYM_RCURLY 0x7D
-#define KEY_SYM_SLASH 0x5C
-#define KEY_SYM_PIPE 0x7C
-#define KEY_SYM_TIC 0x27
-#define KEY_SYM_QUOTE 0x22
-#define KEY_SYM_SEMIC 0x3B
-#define KEY_SYM_COLON 0x3A
-#define KEY_SYM_COMMA 0x2C
-#define KEY_SYM_LT 0x3C
-#define KEY_SYM_PERIOD 0x2E
-#define KEY_SYM_GT 0x3E
-#define KEY_SYM_BSLASH 0x2F
-#define KEY_SYM_QMARK 0x3F
-#define KEY_SYM_A 0x41
-#define KEY_SYM_B 0x42
-#define KEY_SYM_C 0x43
-#define KEY_SYM_D 0x44
-#define KEY_SYM_E 0x45
-#define KEY_SYM_F 0x46
-#define KEY_SYM_G 0x47
-#define KEY_SYM_H 0x48
-#define KEY_SYM_I 0x49
-#define KEY_SYM_J 0x4A
-#define KEY_SYM_K 0x4B
-#define KEY_SYM_L 0x4C
-#define KEY_SYM_M 0x4D
-#define KEY_SYM_N 0x4E
-#define KEY_SYM_O 0x4F
-#define KEY_SYM_P 0x50
-#define KEY_SYM_Q 0x51
-#define KEY_SYM_R 0x52
-#define KEY_SYM_S 0x53
-#define KEY_SYM_T 0x54
-#define KEY_SYM_U 0x55
-#define KEY_SYM_V 0x56
-#define KEY_SYM_W 0x57
-#define KEY_SYM_X 0x58
-#define KEY_SYM_Y 0x59
-#define KEY_SYM_Z 0x5A
-#define KEY_SYM_a 0x61
-#define KEY_SYM_b 0x62
-#define KEY_SYM_c 0x63
-#define KEY_SYM_d 0x64
-#define KEY_SYM_e 0x65
-#define KEY_SYM_f 0x66
-#define KEY_SYM_g 0x67
-#define KEY_SYM_h 0x68
-#define KEY_SYM_i 0x69
-#define KEY_SYM_j 0x6A
-#define KEY_SYM_k 0x6B
-#define KEY_SYM_l 0x6C
-#define KEY_SYM_m 0x6D
-#define KEY_SYM_n 0x6E
-#define KEY_SYM_o 0x6F
-#define KEY_SYM_p 0x70
-#define KEY_SYM_q 0x71
-#define KEY_SYM_r 0x72
-#define KEY_SYM_s 0x73
-#define KEY_SYM_t 0x74
-#define KEY_SYM_u 0x75
-#define KEY_SYM_v 0x76
-#define KEY_SYM_w 0x77
-#define KEY_SYM_x 0x78
-#define KEY_SYM_y 0x79
-#define KEY_SYM_z 0x7A
-#define KEY_SYM_SPACE 0x20
-#endif /* _IBMASM_REMOTE_H_ */
diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c
deleted file mode 100644
index a5d4c8e010ee..000000000000
--- a/drivers/misc/ibmasm/uart.c
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/termios.h>
-#include <linux/tty.h>
-#include <linux/serial_core.h>
-#include <linux/serial_reg.h>
-#include <linux/serial_8250.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-
-
-void ibmasm_register_uart(struct service_processor *sp)
-{
- struct uart_8250_port uart;
- void __iomem *iomem_base;
-
- iomem_base = sp->base_address + SCOUT_COM_B_BASE;
-
- /* read the uart scratch register to determine if the UART
- * is dedicated to the service processor or if the OS can use it
- */
- if (0 == readl(iomem_base + UART_SCR)) {
- dev_info(sp->dev, "IBM SP UART not registered, owned by service processor\n");
- sp->serial_line = -1;
- return;
- }
-
- memset(&uart, 0, sizeof(uart));
- uart.port.irq = sp->irq;
- uart.port.uartclk = 3686400;
- uart.port.flags = UPF_SHARE_IRQ;
- uart.port.iotype = UPIO_MEM;
- uart.port.membase = iomem_base;
-
- sp->serial_line = serial8250_register_8250_port(&uart);
- if (sp->serial_line < 0) {
- dev_err(sp->dev, "Failed to register serial port\n");
- return;
- }
- enable_uart_interrupts(sp->base_address);
-}
-
-void ibmasm_unregister_uart(struct service_processor *sp)
-{
- if (sp->serial_line < 0)
- return;
-
- disable_uart_interrupts(sp->base_address);
- serial8250_unregister_port(sp->serial_line);
-}
diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c
index beb18c34f20d..da82b1edb7c9 100644
--- a/drivers/misc/ibmvmc.c
+++ b/drivers/misc/ibmvmc.c
@@ -1040,7 +1040,7 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer,
size_t count, loff_t *ppos)
{
struct inode *inode;
- struct ibmvmc_buffer *vmc_buffer;
+ struct ibmvmc_buffer *vmc_buffer = NULL;
struct ibmvmc_file_session *session;
struct crq_server_adapter *adapter;
struct ibmvmc_hmc *hmc;
@@ -1130,9 +1130,15 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer,
dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n",
(unsigned long)file, (unsigned long)count);
- ibmvmc_send_msg(adapter, vmc_buffer, hmc, count);
+ if (ibmvmc_send_msg(adapter, vmc_buffer, hmc, count)) {
+ ret = -EIO;
+ goto out;
+ }
+ vmc_buffer = NULL;
ret = p - buffer;
out:
+ if (vmc_buffer)
+ vmc_buffer->free = 1;
spin_unlock_irqrestore(&hmc->lock, flags);
return (ssize_t)(ret);
}
@@ -1653,6 +1659,13 @@ static int ibmvmc_recv_msg(struct crq_server_adapter *adapter,
return -1;
}
+ if (msg_len > buffer->size) {
+ dev_err(adapter->dev, "Recv_msg: msg_len 0x%lx exceeds buffer size 0x%x\n",
+ msg_len, buffer->size);
+ spin_unlock_irqrestore(&hmc->lock, flags);
+ return -1;
+ }
+
/* RDMA the data into the partition. */
rc = h_copy_rdma(msg_len,
adapter->riobn,
@@ -2298,8 +2311,8 @@ static void ibmvmc_remove(struct vio_dev *vdev)
}
static struct vio_device_id ibmvmc_device_table[] = {
- { "ibm,vmc", "IBM,vmc" },
- { "", "" }
+ { .type = "ibm,vmc", .compat = "IBM,vmc" },
+ { }
};
MODULE_DEVICE_TABLE(vio, ibmvmc_device_table);
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c
index 015710762a65..e8bcf0df2da8 100644
--- a/drivers/misc/ics932s401.c
+++ b/drivers/misc/ics932s401.c
@@ -95,7 +95,7 @@ static int ics932s401_detect(struct i2c_client *client,
static void ics932s401_remove(struct i2c_client *client);
static const struct i2c_device_id ics932s401_id[] = {
- { "ics932s401" },
+ { .name = "ics932s401" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ics932s401_id);
diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c
index 95480e16ae5f..c9b526a212d0 100644
--- a/drivers/misc/isl29003.c
+++ b/drivers/misc/isl29003.c
@@ -449,8 +449,8 @@ static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id isl29003_id[] = {
- { "isl29003" },
- {}
+ { .name = "isl29003" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, isl29003_id);
diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c
index c288aeec16c0..dd966db28ec4 100644
--- a/drivers/misc/isl29020.c
+++ b/drivers/misc/isl29020.c
@@ -177,7 +177,7 @@ static void isl29020_remove(struct i2c_client *client)
}
static const struct i2c_device_id isl29020_id[] = {
- { "isl29020" },
+ { .name = "isl29020" },
{ }
};
diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig
new file mode 100644
index 000000000000..12a3fd809969
--- /dev/null
+++ b/drivers/misc/issei/Kconfig
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023-2026 Intel Corporation
+config INTEL_SSEI
+ tristate "Intel Silicon Security Engine Interface"
+ help
+ The ISSEI (Intel Silicon Security Engine Interface)
+ subsystem provides a communication channel between the host and the
+ Silicon Security Engine.
+ Enable this driver to get SPDM and other features on Intel client CPUs
+ released in 2024 (Lunar Lake) or later.
+
+ If selected, the /dev/isseiX device will be created.
+ If in doubt, select N.
+
+if INTEL_SSEI
+
+config INTEL_SSEI_HW_HECI
+ tristate "Intel Silicon Security Engine Interface Hardware"
+ depends on X86 && PCI
+ help
+ HECI interface of communication channel between
+ the host and the Silicon Security Engine.
+
+ Implementation of ISSEI communication channel over
+ the HECI hardware PCI device.
+ This device is available on Intel client CPUs released in 2024
+ (Lunar Lake) or later.
+
+endif
diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile
new file mode 100644
index 000000000000..8fac360c3342
--- /dev/null
+++ b/drivers/misc/issei/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023-2026 Intel Corporation
+ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"'
+
+obj-$(CONFIG_INTEL_SSEI) += issei.o
+issei-objs += cdev.o
+issei-objs += dma.o
+issei-objs += fw_client.o
+issei-objs += host_client.o
+issei-objs += ham.o
+issei-objs += main.o
+
+obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o
+issei-heci-objs := pci_heci.o
+issei-heci-objs += hw_heci.o
diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c
new file mode 100644
index 000000000000..b7d65e2d0813
--- /dev/null
+++ b/drivers/misc/issei/cdev.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/atomic.h>
+#include <linux/cdev.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/issei.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/xarray.h>
+
+#include "issei_dev.h"
+#include "host_client.h"
+#include "cdev.h"
+
+struct class *issei_class;
+static dev_t issei_devt;
+
+#define ISSEI_MAX_DEVS MINORMASK
+
+static DEFINE_XARRAY_ALLOC(issei_minor_xa);
+
+static int issei_open(struct inode *inode, struct file *fp)
+{
+ struct issei_host_client *cl;
+ struct issei_device *idev;
+
+ xa_lock(&issei_minor_xa);
+ idev = xa_load(&issei_minor_xa, iminor(inode));
+ if (idev)
+ get_device(&idev->dev);
+ xa_unlock(&issei_minor_xa);
+ if (!idev)
+ return -ENODEV;
+
+ cl = issei_cl_create(idev, fp);
+ if (IS_ERR(cl)) {
+ put_device(&idev->dev);
+ return PTR_ERR(cl);
+ }
+ fp->private_data = cl;
+
+ return nonseekable_open(inode, fp);
+}
+
+static int issei_release(struct inode *inode, struct file *fp)
+{
+ struct issei_host_client *cl = fp->private_data;
+ struct issei_device *idev = cl->idev;
+
+ issei_cl_remove(cl);
+ put_device(&idev->dev);
+
+ return 0;
+}
+
+static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
+{
+ struct issei_host_client *cl = file->private_data;
+ struct issei_connect_client_data conn;
+ struct issei_device *idev = cl->idev;
+ int ret;
+
+ switch (cmd) {
+ case IOCTL_ISSEI_CONNECT_CLIENT:
+ dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n");
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -ENODEV;
+ }
+
+ if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) {
+ dev_dbg(&idev->dev, "failed to copy data from userland\n");
+ return -EFAULT;
+ }
+
+ ret = issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid,
+ &conn.out_client_properties.max_msg_length,
+ &conn.out_client_properties.protocol_version,
+ &conn.out_client_properties.flags);
+ if (ret)
+ return ret;
+
+ if (copy_to_user((char __user *)data, &conn, sizeof(conn))) {
+ dev_dbg(&idev->dev, "failed to copy data to userland\n");
+ issei_cl_disconnect(cl);
+ return -EFAULT;
+ }
+ return 0;
+
+ case IOCTL_ISSEI_DISCONNECT_CLIENT:
+ dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n");
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -ENODEV;
+ }
+
+ return issei_cl_disconnect(cl);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static ssize_t issei_write(struct file *file, const char __user *ubuf,
+ size_t length, loff_t *offset)
+{
+ struct issei_host_client *cl = file->private_data;
+ struct issei_device *idev = cl->idev;
+ ssize_t ret;
+
+ if (!length)
+ return 0;
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -EBUSY;
+ }
+
+ /* sanity check */
+ if (length > idev->dma.length.h2f) {
+ dev_dbg(&idev->dev, "Write is too big %zu > %zu\n",
+ length, idev->dma.length.h2f);
+ return -EFBIG;
+ }
+
+ u8 *buf __free(kfree) = memdup_user(ubuf, length);
+ if (IS_ERR(buf)) {
+ dev_dbg(&idev->dev, "failed to copy data from userland\n");
+ return PTR_ERR(buf);
+ }
+
+ do {
+ ret = issei_cl_write(cl, buf, length);
+ if (ret < 0 && ret != -EAGAIN)
+ return ret;
+ /* buf is consumed by issei_cl_write on success */
+ if (ret >= 0)
+ retain_and_null_ptr(buf);
+ if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != 1)) {
+ issei_cl_clean_all_wbuf(cl);
+ if (signal_pending(current))
+ return -EINTR;
+ return -ERESTARTSYS;
+ }
+ } while (ret == -EAGAIN);
+
+ return ret;
+}
+
+static ssize_t issei_read(struct file *file, char __user *ubuf,
+ size_t length, loff_t *offset)
+{
+ struct issei_host_client *cl = file->private_data;
+ struct issei_device *idev = cl->idev;
+ u8 *data = NULL;
+ ssize_t ret;
+
+ if (!length)
+ return 0;
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -EBUSY;
+ }
+
+ /* sanity check */
+ if (length > idev->dma.length.f2h) {
+ dev_dbg(&idev->dev, "Read is too big %zu > %zu\n",
+ length, idev->dma.length.f2h);
+ return -EFBIG;
+ }
+
+ ret = issei_cl_read(cl, &data, length);
+ if (ret < 0) {
+ if (ret != -ENOENT)
+ return ret;
+
+ if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) != 0)) {
+ if (signal_pending(current))
+ return -EINTR;
+ return -ERESTARTSYS;
+ }
+
+ ret = issei_cl_read(cl, &data, length);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (copy_to_user(ubuf, data, ret)) {
+ dev_dbg(&idev->dev, "failed to copy data to userland\n");
+ ret = -EFAULT;
+ } else {
+ *offset = 0;
+ }
+
+ kfree(data);
+
+ return ret;
+}
+
+static __poll_t issei_poll(struct file *file, poll_table *wait)
+{
+ __poll_t req_events = poll_requested_events(wait);
+ struct issei_host_client *cl = file->private_data;
+ struct issei_device *idev = cl->idev;
+ __poll_t mask = 0;
+ int ret;
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return EPOLLERR;
+ }
+
+ if (req_events & (EPOLLIN | EPOLLRDNORM)) {
+ poll_wait(file, &cl->read_wait, wait);
+ ret = issei_cl_check_read(cl);
+ if (ret == 1)
+ mask |= EPOLLIN | EPOLLRDNORM;
+ else if (ret < 0)
+ mask |= EPOLLERR;
+ }
+
+ if (req_events & (EPOLLOUT | EPOLLWRNORM)) {
+ poll_wait(file, &cl->write_wait, wait);
+ ret = issei_cl_check_write(cl);
+ if (ret == 0)
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ else if (ret < 0)
+ mask |= EPOLLERR;
+ }
+
+ return mask;
+}
+
+static ssize_t fw_ver_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct issei_device *idev = dev_get_drvdata(device);
+
+ return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1],
+ idev->fw_version[2], idev->fw_version[3]);
+}
+static DEVICE_ATTR_RO(fw_ver);
+
+static struct attribute *issei_attrs[] = {
+ &dev_attr_fw_ver.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(issei);
+
+static const struct file_operations issei_fops = {
+ .owner = THIS_MODULE,
+ .open = issei_open,
+ .unlocked_ioctl = issei_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .write = issei_write,
+ .read = issei_read,
+ .release = issei_release,
+ .poll = issei_poll,
+};
+
+static void issei_device_release(struct device *dev)
+{
+ kfree(dev_get_drvdata(dev));
+}
+
+static void issei_device_init(struct issei_device *idev, struct device *parent,
+ const struct issei_dma_length *dma_length,
+ const struct issei_hw_ops *ops)
+{
+ idev->parent = parent;
+ idev->power_down = false;
+ init_waitqueue_head(&idev->wait_has_data);
+ idev->has_data = false;
+ init_waitqueue_head(&idev->wait_rst_state);
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+
+ mutex_init(&idev->client_lock);
+ INIT_LIST_HEAD(&idev->host_client_list);
+ idev->host_client_last_id = 0;
+ idev->host_client_count = 0;
+ INIT_LIST_HEAD(&idev->fw_client_list);
+ INIT_LIST_HEAD(&idev->write_queue);
+ idev->last_write_ts = 0;
+
+ idev->dma.length = *dma_length;
+
+ idev->ops = ops;
+}
+
+/**
+ * issei_register: register issei character device
+ * @hw_size: size of the hardware structure to allocate
+ * @parent: parent device
+ * @dma_length: structure with DMA sizes
+ * @ops: hardware-related operations
+ *
+ * Return: pointer allocated to issei_device structure, error on failure
+ */
+struct issei_device *issei_register(size_t hw_size, struct device *parent,
+ const struct issei_dma_length *dma_length,
+ const struct issei_hw_ops *ops)
+{
+ struct issei_device *idev;
+ u32 minor;
+ int ret, devno;
+
+ idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL);
+ if (!idev)
+ return ERR_PTR(-ENOMEM);
+
+ issei_device_init(idev, parent, dma_length, ops);
+
+ ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL);
+ if (ret < 0) {
+ dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret);
+ kfree(idev);
+ return ERR_PTR(ret);
+ }
+
+ idev->minor = minor;
+ devno = MKDEV(MAJOR(issei_devt), idev->minor);
+
+ device_initialize(&idev->dev);
+ idev->dev.devt = devno;
+ idev->dev.class = issei_class;
+ idev->dev.parent = parent;
+ idev->dev.groups = issei_groups;
+ idev->dev.release = issei_device_release;
+ dev_set_drvdata(&idev->dev, idev);
+
+ idev->cdev = cdev_alloc();
+ if (!idev->cdev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ idev->cdev->ops = &issei_fops;
+ if (parent->driver)
+ idev->cdev->owner = parent->driver->owner;
+ cdev_set_parent(idev->cdev, &idev->dev.kobj);
+
+ ret = cdev_add(idev->cdev, devno, 1);
+ if (ret) {
+ dev_err(parent, "unable to add device %d:%u ret = %d\n",
+ MAJOR(issei_devt), idev->minor, ret);
+ goto err_del_cdev;
+ }
+
+ ret = dev_set_name(&idev->dev, "issei%u", idev->minor);
+ if (ret) {
+ dev_err(parent, "unable to set name to device %d:%u ret = %d\n",
+ MAJOR(issei_devt), idev->minor, ret);
+ goto err_del_cdev;
+ }
+
+ ret = device_add(&idev->dev);
+ if (ret) {
+ dev_err(parent, "unable to add device %d:%u ret = %d\n",
+ MAJOR(issei_devt), idev->minor, ret);
+ goto err_del_cdev;
+ }
+
+ idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj);
+ if (!idev->fw_clients) {
+ ret = -ENOMEM;
+ goto err_del_dev;
+ }
+
+ return idev;
+
+err_del_dev:
+ device_del(&idev->dev);
+err_del_cdev:
+ cdev_del(idev->cdev);
+err:
+ put_device(&idev->dev);
+ xa_erase(&issei_minor_xa, minor);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(issei_register);
+
+/**
+ * issei_deregister: remove issei character device
+ * @idev: the device structure
+ */
+void issei_deregister(struct issei_device *idev)
+{
+ u32 minor = idev->minor;
+
+ cdev_del(idev->cdev);
+
+ kset_unregister(idev->fw_clients);
+
+ device_del(&idev->dev);
+
+ put_device(&idev->dev);
+
+ xa_erase(&issei_minor_xa, minor);
+}
+EXPORT_SYMBOL_GPL(issei_deregister);
+
+static int __init issei_cdev_init(void)
+{
+ int ret;
+
+ issei_class = class_create("issei");
+ if (IS_ERR(issei_class)) {
+ pr_err("couldn't create class\n");
+ return PTR_ERR(issei_class);
+ }
+
+ ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei");
+ if (ret < 0) {
+ pr_err("unable to allocate char dev region\n");
+ class_destroy(issei_class);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit issei_cdev_exit(void)
+{
+ unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS);
+ class_destroy(issei_class);
+}
+
+module_init(issei_cdev_init);
+module_exit(issei_cdev_exit);
+
+MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h
new file mode 100644
index 000000000000..30075a624f2d
--- /dev/null
+++ b/drivers/misc/issei/cdev.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_CDEV_H_
+#define _ISSEI_CDEV_H_
+
+struct device;
+struct issei_device;
+struct issei_dma_length;
+struct issei_hw_ops;
+
+struct issei_device *issei_register(size_t hw_size, struct device *parent,
+ const struct issei_dma_length *dma_length,
+ const struct issei_hw_ops *ops);
+void issei_deregister(struct issei_device *idev);
+
+#endif /* _ISSEI_CDEV_H_ */
diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c
new file mode 100644
index 000000000000..457d28f31c06
--- /dev/null
+++ b/drivers/misc/issei/dma.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/dev_printk.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timekeeping.h>
+
+#include "issei_dev.h"
+#include "hw_msg.h"
+
+static inline size_t __issei_dma_size(const struct issei_dma *dma)
+{
+ return dma->length.h2f + dma->length.f2h + dma->length.ctl;
+}
+
+/**
+ * issei_dmam_setup - setup DMA buffer and clean it
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_dmam_setup(struct issei_device *idev)
+{
+ struct issei_dma *dma = &idev->dma;
+ size_t size;
+
+ size = __issei_dma_size(dma);
+ if (!size)
+ return -EINVAL;
+
+ if (!dma->vaddr)
+ dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr,
+ GFP_KERNEL | __GFP_ZERO);
+ if (dma->vaddr)
+ memset(dma->vaddr, 0, size);
+ return dma->vaddr ? 0 : -ENOMEM;
+}
+
+static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma)
+{
+ return dma->vaddr + dma->length.h2f + dma->length.f2h;
+}
+
+static bool __issei_dma_is_read_busy(struct issei_dma *dma)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(dma);
+
+ return ctl->f2h_counter_wr != ctl->f2h_counter_rd;
+}
+
+static bool __issei_dma_is_write_busy(struct issei_dma *dma)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(dma);
+
+ return ctl->h2f_counter_wr != ctl->h2f_counter_rd;
+}
+
+static void __issei_dma_read_finalize(struct issei_device *idev)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
+
+ dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd);
+ /* No need to check overflow - the firmware counters overflow the same way */
+ ctl->f2h_counter_rd++;
+}
+
+static void __issei_dma_write_finalize(struct issei_device *idev)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
+
+ dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr);
+ /* No need to check overflow - the firmware counters overflow the same way */
+ ctl->h2f_counter_wr++;
+}
+
+/**
+ * issei_dma_write - write data package to DMA
+ * @idev: issei device object
+ * @data: data atructure
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data)
+{
+ u8 *write_buf = idev->dma.vaddr;
+ struct ham_message_header *hdr = (struct ham_message_header *)write_buf;
+
+ if (data->length > idev->dma.length.h2f - sizeof(*hdr)) {
+ dev_err(&idev->dev, "Message is too big\n");
+ return -EMSGSIZE;
+ }
+
+ if (__issei_dma_is_write_busy(&idev->dma)) {
+ if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) {
+ dev_err(&idev->dev, "Write stuck in queue\n");
+ return -EIO;
+ }
+ dev_info(&idev->dev, "Write is busy\n");
+ return -EBUSY;
+ }
+
+ hdr->length = data->length;
+ hdr->fw_id = data->fw_id;
+ hdr->host_id = data->host_id;
+ hdr->flags = data->flags;
+ hdr->status = data->status;
+ hdr->reserved = 0;
+
+ memcpy(write_buf + sizeof(*hdr), data->buf, data->length);
+
+ __issei_dma_write_finalize(idev);
+ idev->last_write_ts = ktime_get();
+ return 0;
+}
+
+/**
+ * issei_dma_read - read data package from DMA
+ * @idev: issei device object
+ * @data: data atructure
+ *
+ * Return: %0 on success, <0 on failures
+ */
+int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data)
+{
+ u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f;
+ struct ham_message_header *hdr = (struct ham_message_header *)read_buf;
+
+ if (!__issei_dma_is_read_busy(&idev->dma)) {
+ dev_dbg(&idev->dev, "Nothing to read\n");
+ return -ENODATA;
+ }
+
+ dev_dbg(&idev->dev, "Reading header\n");
+ data->length = hdr->length;
+ data->fw_id = hdr->fw_id;
+ data->host_id = hdr->host_id;
+ data->flags = hdr->flags;
+ data->status = hdr->status;
+
+ if (data->length > idev->dma.length.f2h - sizeof(*hdr)) {
+ dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n",
+ data->length, idev->dma.length.f2h - sizeof(*hdr));
+ return -EIO;
+ }
+
+ dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length);
+ data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL);
+ if (!data->buf)
+ return -ENOMEM;
+ __issei_dma_read_finalize(idev);
+ return 0;
+}
diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h
new file mode 100644
index 000000000000..e6b7aeb50ae6
--- /dev/null
+++ b/drivers/misc/issei/dma.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_DMA_H_
+#define _ISSEI_DMA_H_
+
+#include <linux/types.h>
+
+struct issei_device;
+
+/**
+ * struct issei_dma_length - sizes of DMA memory portions
+ * @h2f: host to firmware buffer size
+ * @f2h: firmware to host buffer size
+ * @ctl: control buffer size
+ */
+struct issei_dma_length {
+ size_t h2f;
+ size_t f2h;
+ size_t ctl;
+};
+
+/**
+ * struct issei_dma - DMA memory structure
+ * @vaddr: virtual address
+ * @daddr: physical address
+ * @length: memory sizes structure
+ */
+struct issei_dma {
+ void *vaddr;
+ dma_addr_t daddr;
+ struct issei_dma_length length;
+};
+
+/* Operation statuses */
+#define HAMS_SUCCESS 0x00
+#define HAMS_PROTOCOL_NOT_SUPPORTED 0x01
+#define HAMS_DEPRECATED_BUS_MSG 0x02
+#define HAMS_CLIENT_NOT_EXISTS 0x03
+#define HAMS_MSG_TOO_BIG 0x04
+#define HAMS_MSG_NOT_CONSUMED 0x05
+#define HAMS_CORRUPTED_BUS_MSG 0x06
+#define HAMS_CORRUPTED_HEADER 0x07
+#define HAMS_INVALID_LENGTH 0x08
+#define HAMS_SHARED_MEMORY_SIZE_UNSUPPORTED 0x09
+#define HAMS_GENERAL_FATAL_ERROR 0xff
+
+/**
+ * struct issei_dma_data - data passed through channel
+ * @fw_id: firmware client id
+ * @host_id: host client id
+ * @flags: flags bitmap
+ * @status: operation status
+ * @length: data length
+ * @buf: pointer to data buffer
+ */
+struct issei_dma_data {
+ u16 fw_id;
+ u16 host_id;
+ u32 flags;
+ u32 status;
+ u32 length;
+ void *buf;
+};
+
+int issei_dmam_setup(struct issei_device *idev);
+int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data);
+int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data);
+
+#endif /*_ISSEI_DMA_H_*/
diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c
new file mode 100644
index 000000000000..b8e48dbf1c9c
--- /dev/null
+++ b/drivers/misc/issei/fw_client.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/bug.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/sprintf.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+#include "issei_dev.h"
+#include "fw_client.h"
+
+/*
+ * Specific attribute handlers for fw_clients kset.
+ * Provide only show function as all fw_client attributes are read-only.
+ */
+
+struct issei_fw_cl_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct issei_fw_client *fw_cl, const struct issei_fw_cl_attr *attr,
+ char *buf);
+};
+#define to_issei_fw_cl_attr(x) container_of_const(x, struct issei_fw_cl_attr, attr)
+
+static ssize_t fw_cl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ const struct issei_fw_cl_attr *issei_attr;
+ struct issei_fw_client *fw_cl;
+
+ issei_attr = to_issei_fw_cl_attr(attr);
+ fw_cl = to_issei_fw_client(kobj);
+
+ if (!issei_attr->show)
+ return -EIO;
+
+ return issei_attr->show(fw_cl, issei_attr, buf);
+}
+
+static const struct sysfs_ops fw_cl_sysfs_ops = {
+ .show = fw_cl_attr_show,
+};
+
+#define FW_CL_ATTR_RO(_name) \
+ struct issei_fw_cl_attr fw_cl_attr_##_name = __ATTR_RO(_name)
+
+/* fw_client attributes */
+
+static ssize_t id_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", fw_cl->id);
+}
+static FW_CL_ATTR_RO(id);
+
+static ssize_t ver_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", fw_cl->ver);
+}
+static FW_CL_ATTR_RO(ver);
+
+static ssize_t uuid_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%pUb\n", &fw_cl->uuid);
+}
+static FW_CL_ATTR_RO(uuid);
+
+static ssize_t mtu_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", fw_cl->mtu);
+}
+static FW_CL_ATTR_RO(mtu);
+
+static const struct attribute *const fw_cl_attrs[] = {
+ &fw_cl_attr_id.attr,
+ &fw_cl_attr_ver.attr,
+ &fw_cl_attr_uuid.attr,
+ &fw_cl_attr_mtu.attr,
+ NULL,
+};
+
+static const struct attribute_group fw_cl_group = {
+ .attrs_const = fw_cl_attrs,
+};
+__ATTRIBUTE_GROUPS(fw_cl);
+
+static void issei_fw_cl_init(struct issei_fw_client *fw_cl, u16 id, u8 ver, const uuid_t *uuid,
+ u32 mtu, u32 flags)
+{
+ INIT_LIST_HEAD(&fw_cl->list);
+ fw_cl->id = id;
+ fw_cl->ver = ver;
+ fw_cl->uuid = *uuid;
+ fw_cl->mtu = mtu;
+ fw_cl->flags = flags;
+}
+
+static void fw_cl_release(struct kobject *kobj)
+{
+ struct issei_fw_client *fw_cl = to_issei_fw_client(kobj);
+
+ kfree(fw_cl);
+}
+
+static const struct kobj_type fw_client_ktype = {
+ .sysfs_ops = &fw_cl_sysfs_ops,
+ .release = fw_cl_release,
+ .default_groups = fw_cl_groups,
+};
+
+/**
+ * issei_fw_cl_create - create firmware client object and add to list
+ * @idev: issei device object
+ * @id: firmware client id
+ * @ver: firmware client version
+ * @uuid: firmware client unique id
+ * @mtu: firmware client maximum message size
+ * @flags: firmware client flags
+ *
+ * Should be called under idev->client_lock
+ *
+ * Return: pointer to newly created object on success, ERR_PTR on failure
+ */
+struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver,
+ const uuid_t *uuid, u32 mtu, u32 flags)
+{
+ int ret;
+ struct issei_fw_client *fw_cl = kzalloc_obj(*fw_cl);
+
+ if (!fw_cl)
+ return ERR_PTR(-ENOMEM);
+
+ WARN_ON(!mutex_is_locked(&idev->client_lock));
+
+ issei_fw_cl_init(fw_cl, id, ver, uuid, mtu, flags);
+ fw_cl->kobj.kset = idev->fw_clients;
+
+ ret = kobject_init_and_add(&fw_cl->kobj, &fw_client_ktype, NULL, "%u", id);
+ if (ret) {
+ kobject_put(&fw_cl->kobj);
+ return ERR_PTR(ret);
+ }
+
+ list_add_tail(&fw_cl->list, &idev->fw_client_list);
+
+ dev_dbg(&idev->dev, "FW client %pUb created\n", uuid);
+
+ kobject_uevent(&fw_cl->kobj, KOBJ_ADD);
+
+ return fw_cl;
+}
+
+static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl)
+{
+ WARN(fw_cl->cl, "Removing connected client!\n");
+
+ dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid);
+
+ list_del(&fw_cl->list);
+ kobject_put(&fw_cl->kobj);
+}
+
+/**
+ * issei_fw_cl_remove_all - remove all firmware client objects
+ * @idev: issei device object
+ */
+void issei_fw_cl_remove_all(struct issei_device *idev)
+{
+ struct issei_fw_client *fw_cl, *next;
+
+ guard(mutex)(&idev->client_lock);
+
+ list_for_each_entry_safe(fw_cl, next, &idev->fw_client_list, list)
+ __issei_fw_cl_remove(idev, fw_cl);
+}
+
+/**
+ * issei_fw_cl_find_by_uuid - find firmware client by uuid
+ * @idev: issei device object
+ * @uuid: uuid to search by it
+ *
+ * Should be called under idev->client_lock
+ *
+ * Return: pointer to firmware client object if found, NULL on failure
+ */
+struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid)
+{
+ struct issei_fw_client *fw_cl;
+
+ WARN_ON(!mutex_is_locked(&idev->client_lock));
+
+ list_for_each_entry(fw_cl, &idev->fw_client_list, list) {
+ if (uuid_equal(&fw_cl->uuid, uuid)) {
+ kobject_get(&fw_cl->kobj);
+ return fw_cl;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * issei_fw_cl_connect - connect firmware and host client
+ * @fw_cl: firmware client
+ * @cl: host client
+ *
+ * Should be called under idev->client_lock
+ *
+ * Return: 0 on success, -EBUSY if already connected
+ */
+int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl)
+{
+ if (fw_cl->cl)
+ return -EBUSY;
+
+ kobject_get(&fw_cl->kobj);
+ fw_cl->cl = cl;
+ return 0;
+}
+
+/**
+ * issei_fw_cl_disconnect - disconnect firmware and host client
+ * @fw_cl: firmware client
+ *
+ * Should be called under idev->client_lock
+ */
+void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl)
+{
+ WARN_ON(!fw_cl->cl);
+
+ fw_cl->cl = NULL;
+ kobject_put(&fw_cl->kobj);
+}
diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h
new file mode 100644
index 000000000000..377f733d7e91
--- /dev/null
+++ b/drivers/misc/issei/fw_client.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_FW_CLIENT_H_
+#define _ISSEI_FW_CLIENT_H_
+
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+struct issei_device;
+struct issei_host_client;
+
+/**
+ * struct issei_fw_client - represents firmware queue
+ * @kobj: associated kobject
+ * @list: link in firmware clients list
+ * @id: firmware client id
+ * @ver: firmware client version
+ * @uuid: firmware client protocol id
+ * @mtu: firmware client maximum buffer size
+ * @flags: firmware client flags
+ * @cl: pointer to host client, if connected
+ */
+struct issei_fw_client {
+ struct kobject kobj;
+ struct list_head list;
+ u16 id;
+ u8 ver;
+ uuid_t uuid;
+ u32 mtu;
+ u32 flags;
+ struct issei_host_client *cl;
+};
+#define to_issei_fw_client(x) container_of(x, struct issei_fw_client, kobj)
+
+struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver,
+ const uuid_t *uuid, u32 mtu, u32 flags);
+void issei_fw_cl_remove_all(struct issei_device *idev);
+struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid);
+
+int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl);
+void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl);
+
+#endif /* _ISSEI_FW_CLIENT_H_ */
diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c
new file mode 100644
index 000000000000..674d9733d16c
--- /dev/null
+++ b/drivers/misc/issei/ham.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dma.h"
+#include "fw_client.h"
+#include "ham.h"
+#include "hw_msg.h"
+#include "issei_dev.h"
+
+static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf)
+{
+ struct issei_dma_data data = { };
+ int ret;
+
+ data.length = length;
+ data.buf = buf;
+ ret = issei_dma_write(idev, &data);
+ if (ret)
+ return ret;
+ return idev->ops->irq_write_generate(idev);
+}
+
+/**
+ * issei_ham_send_start_req - send start request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_start_req(struct issei_device *idev)
+{
+ struct ham_start_message_req req;
+
+ req.header.cmd = HAM_BUS_CMD_START_REQ;
+ req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER;
+ req.heci_capabilities_length = 0;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+/**
+ * issei_ham_send_clients_req - send clients request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_clients_req(struct issei_device *idev)
+{
+ struct ham_get_clients_req req;
+
+ req.header.cmd = HAM_BUS_CMD_CLIENT_REQ;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_start_message_res *res = (struct ham_start_message_res *)buf;
+ int ret;
+
+ if (idev->rst_state != ISSEI_RST_STATE_START) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_START);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small start response size %zu < %zu\n",
+ length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->heci_capabilities_length) {
+ dev_err(&idev->dev, "Wrong start response size %zu != %u\n",
+ length - sizeof(*res), res->heci_capabilities_length);
+ return -EPROTO;
+ }
+
+ memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version));
+ idev->fw_protocol_ver = res->supported_version;
+ dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver,
+ idev->fw_version[0], idev->fw_version[1],
+ idev->fw_version[2], idev->fw_version[3]);
+
+ ret = issei_ham_send_clients_req(idev);
+ if (ret == -EBUSY)
+ ret = 0;
+
+ return ret;
+}
+
+static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf;
+ struct ham_client_properties *client;
+
+ if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) {
+ dev_err(&idev->dev, "Wrong response size %zu < %zu\n",
+ length - sizeof(*res),
+ res->client_count * sizeof(struct ham_client_properties));
+ return -EPROTO;
+ }
+
+ guard(mutex)(&idev->client_lock);
+
+ for (size_t i = 0; i < res->client_count; i++) {
+ client = &res->clients_props[i];
+ dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u",
+ client->client_number, client->protocol_ver, &client->client_uuid,
+ client->client_mtu, client->flags);
+ issei_fw_cl_create(idev, client->client_number, client->protocol_ver,
+ &client->client_uuid, client->client_mtu, client->flags);
+ }
+ return 0;
+}
+
+static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_bus_message *hdr = (struct ham_bus_message *)buf;
+
+ if (length < sizeof(*hdr)) {
+ dev_err(&idev->dev, "Small bus message size %zu < %zu\n",
+ length, sizeof(*hdr));
+ return -EPROTO;
+ }
+
+ switch (hdr->cmd) {
+ case HAM_BUS_CMD_START_RSP:
+ return issei_ham_start_rsp(idev, buf, length);
+
+ case HAM_BUS_CMD_CLIENT_RSP:
+ return issei_ham_client_rsp(idev, buf, length);
+
+ default:
+ dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd);
+ return -EPROTO;
+ }
+}
+
+/**
+ * issei_ham_process_ham_rsp - process response from firmware and release buffer
+ * @idev: issei device object
+ * @buf: response buffer
+ * @length: response buffer length
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ int ret;
+
+ ret = __issei_ham_process_ham_rsp(idev, buf, length);
+ kfree(buf);
+ return ret;
+}
diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h
new file mode 100644
index 000000000000..37ea64bb2920
--- /dev/null
+++ b/drivers/misc/issei/ham.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HAM_H_
+#define _ISSEI_HAM_H_
+
+#include <linux/types.h>
+
+struct issei_device;
+
+int issei_ham_send_start_req(struct issei_device *idev);
+int issei_ham_send_clients_req(struct issei_device *idev);
+
+static inline bool issei_is_ham_rsp(u16 fw_id, u16 host_id)
+{
+ return fw_id == 0 && host_id == 0;
+}
+
+int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length);
+
+#endif /* _ISSEI_HAM_H_ */
diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c
new file mode 100644
index 000000000000..8f36d1c319ec
--- /dev/null
+++ b/drivers/misc/issei/host_client.c
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/uuid.h>
+
+#include "fw_client.h"
+#include "host_client.h"
+#include "issei_dev.h"
+
+static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl)
+{
+ return cl->fw_cl ? cl->fw_cl->id : 0;
+}
+
+#define ISSEI_CL_FMT "cl:host=%02d fw=%02d "
+
+#define cl_dbg(_dev_, _cl_, format, arg...) do { \
+ struct issei_host_client *_l_cl_ = _cl_; \
+ dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
+ __issei_cl_fw_id(_l_cl_), ##arg); \
+} while (0)
+
+#define cl_warn(_dev_, _cl_, format, arg...) do { \
+ struct issei_host_client *_l_cl_ = _cl_; \
+ dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
+ __issei_cl_fw_id(_l_cl_), ##arg); \
+} while (0)
+
+#define cl_err(_dev_, _cl_, format, arg...) do { \
+ struct issei_host_client *_l_cl_ = _cl_; \
+ dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
+ __issei_cl_fw_id(_l_cl_), ##arg); \
+} while (0)
+
+static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf)
+{
+ list_del(&wbuf->list);
+ kfree(wbuf->data);
+ kfree(wbuf);
+}
+
+static void __issei_cl_release_rbuf(struct issei_host_client *cl)
+{
+ cl->read_data = NULL;
+ cl->read_data_size = 0;
+}
+
+static void __issei_cl_clean_rbuf(struct issei_host_client *cl)
+{
+ kfree(cl->read_data);
+ __issei_cl_release_rbuf(cl);
+}
+
+static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct issei_host_client *cl)
+{
+ struct issei_write_buf *wbuf, *next;
+
+ if (!cl->write_in_progress)
+ return;
+ list_for_each_entry_safe(wbuf, next, &idev->write_queue, list) {
+ if (wbuf->cl == cl) {
+ __issei_cl_clean_wbuf(wbuf);
+ break;
+ }
+ }
+ cl->write_in_progress = false;
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->write_wait))
+ wake_up_interruptible(&cl->write_wait);
+}
+
+static struct issei_host_client *__issei_cl_by_id(struct issei_device *idev, u16 id)
+{
+ struct issei_host_client *cl;
+
+ list_for_each_entry(cl, &idev->host_client_list, list) {
+ if (cl->id == id)
+ return cl;
+ }
+ return NULL;
+}
+
+static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_client *cl)
+{
+ if (cl->state == ISSEI_HOST_CL_STATE_DISCONNECTED)
+ return;
+
+ __issei_cl_clean_all_wbuf(idev, cl);
+
+ if (!WARN_ON(!cl->fw_cl)) {
+ issei_fw_cl_disconnect(cl->fw_cl);
+ cl->fw_cl = NULL;
+ }
+ cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED;
+
+ if (cl->read_data)
+ __issei_cl_clean_rbuf(cl);
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->read_wait))
+ wake_up_interruptible(&cl->read_wait);
+ cl_dbg(idev, cl, "Disconnected\n");
+}
+
+static void __issei_cl_init(struct issei_host_client *cl, struct issei_device *idev,
+ u16 id, struct file *fp)
+{
+ INIT_LIST_HEAD(&cl->list);
+ cl->idev = idev;
+ cl->id = id;
+ cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED;
+ cl->fp = fp;
+ init_waitqueue_head(&cl->write_wait);
+ init_waitqueue_head(&cl->read_wait);
+ __issei_cl_release_rbuf(cl);
+}
+
+/**
+ * issei_cl_create - create the host client
+ * @idev: issei device
+ * @fp: file pointer to associate with host client
+ *
+ * Return: client pointer on success, ERR_PTR on error
+ */
+struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp)
+{
+ struct issei_host_client *cl;
+ u16 id;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) {
+ dev_err(&idev->dev, "Maximum open clients %d is reached.\n",
+ ISSEI_HOST_CLIENTS_MAX);
+ return ERR_PTR(-EMFILE);
+ }
+
+ do {
+ if (check_add_overflow(idev->host_client_last_id, 1, &id)) /* overflow */
+ id = 1;
+ idev->host_client_last_id = id;
+ /* Not an endless loop as we have less clients then id's */
+ } while (__issei_cl_by_id(idev, id));
+
+ cl = kzalloc_obj(*cl);
+ if (!cl)
+ return ERR_PTR(-ENOMEM);
+
+ __issei_cl_init(cl, idev, id, fp);
+ list_add_tail(&cl->list, &idev->host_client_list);
+ idev->host_client_count++;
+
+ cl_dbg(idev, cl, "Created\n");
+ return cl;
+}
+
+/**
+ * issei_cl_remove - disconnect and free the host client
+ * @cl: host client
+ */
+void issei_cl_remove(struct issei_host_client *cl)
+{
+ struct issei_device *idev;
+
+ /* don't shout on error exit path */
+ if (!cl)
+ return;
+
+ idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ idev->host_client_count--;
+ list_del(&cl->list);
+
+ __issei_cl_disconnect(idev, cl);
+
+ cl_dbg(idev, cl, "Removed\n");
+ kfree(cl);
+}
+
+/**
+ * issei_cl_connect - connect between FW and host client
+ * @cl: host client
+ * @uuid: FW client unique ID
+ * @mtu: memory for FW client max message size
+ * @ver: memory for FW client version
+ * @flags: memory for FW client flags
+ *
+ * Search for firmware client by UUID and connect it to provided
+ * host client, if not already connected to some client.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver,
+ u32 *flags)
+{
+ struct issei_device *idev = cl->idev;
+ struct issei_fw_client *fw_cl;
+ int ret;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state == ISSEI_HOST_CL_STATE_CONNECTED) {
+ cl_err(idev, cl, "Already connected\n");
+ return -EISCONN;
+ }
+
+ fw_cl = issei_fw_cl_find_by_uuid(idev, uuid);
+ if (!fw_cl) {
+ cl_dbg(idev, cl, "FW client %pUb not found\n", uuid);
+ return -ENOTTY;
+ }
+
+ ret = issei_fw_cl_connect(fw_cl, cl); /* calls kobject_get for fw_cl on success */
+ kobject_put(&fw_cl->kobj);
+ if (ret) {
+ cl_err(idev, cl, "FW client is already connected ret = %d\n", ret);
+ return ret;
+ }
+
+ cl->fw_cl = fw_cl;
+ cl->state = ISSEI_HOST_CL_STATE_CONNECTED;
+
+ *mtu = fw_cl->mtu;
+ *ver = fw_cl->ver;
+ *flags = fw_cl->flags;
+ cl_dbg(idev, cl, "Connected\n");
+ return 0;
+}
+
+/**
+ * issei_cl_disconnect - disconnect between FW and host client
+ * @cl: host client
+ *
+ * Return: 0 on success, -ENOTCONN if not connected
+ */
+int issei_cl_disconnect(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+ __issei_cl_disconnect(idev, cl);
+ return 0;
+}
+
+/**
+ * issei_cl_all_disconnect - disconnect all FW clients
+ * @idev: issei device
+ */
+void issei_cl_all_disconnect(struct issei_device *idev)
+{
+ struct issei_host_client *cl;
+
+ guard(mutex)(&idev->client_lock);
+
+ list_for_each_entry(cl, &idev->host_client_list, list)
+ __issei_cl_disconnect(idev, cl);
+}
+
+/**
+ * issei_cl_write - enqueue write request
+ * @cl: host client
+ * @buf: buffer to write
+ * @buf_size: buffer size
+ *
+ * Add write request to the write queue and wakes working thread.
+ * This call takes ownership of buf memory, if succeeded.
+ *
+ * Return: size of data on success, <0 on error
+ */
+ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size)
+{
+ struct issei_device *idev = cl->idev;
+ struct issei_write_buf *wbuf;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+
+ if (cl->write_in_progress) {
+ cl_dbg(idev, cl, "Another write is in progress\n");
+ return -EAGAIN;
+ }
+
+ if (buf_size > cl->fw_cl->mtu) {
+ cl_err(idev, cl, "Write is too big %zu > %u\n", buf_size, cl->fw_cl->mtu);
+ return -EFBIG;
+ }
+
+ wbuf = kmalloc_obj(*wbuf);
+ if (!wbuf)
+ return -ENOMEM;
+ wbuf->cl = cl;
+ wbuf->data = buf;
+ wbuf->data_size = buf_size;
+ list_add_tail(&wbuf->list, &idev->write_queue);
+ cl->write_in_progress = true;
+ cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size);
+
+ issei_poke_process_thread(idev);
+
+ return buf_size;
+}
+
+/**
+ * issei_cl_write_from_queue - writes first request from queue to firmware
+ * @idev: issei device
+ *
+ * Tries to write first request from the write queue to firmware.
+ * Releases buf memory, if succeeded.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_cl_write_from_queue(struct issei_device *idev)
+{
+ struct issei_write_buf *wbuf;
+ struct issei_dma_data data;
+ struct issei_host_client *cl;
+ int ret;
+
+ guard(mutex)(&idev->client_lock);
+
+ wbuf = list_first_entry_or_null(&idev->write_queue, struct issei_write_buf, list);
+ if (!wbuf)
+ return 0;
+
+ cl = wbuf->cl;
+
+ data.fw_id = cl->fw_cl->id;
+ data.host_id = cl->id;
+ data.flags = 0;
+ data.status = 0;
+ data.length = wbuf->data_size;
+ data.buf = (void *)wbuf->data;
+ ret = issei_dma_write(idev, &data);
+ if (ret == -EBUSY)
+ return 0;
+ if (ret == -EIO)
+ return ret;
+ if (ret >= 0)
+ idev->ops->irq_write_generate(idev);
+ cl->write_in_progress = false;
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->write_wait))
+ wake_up_interruptible(&cl->write_wait);
+ cl_dbg(idev, cl, "Write %zu bytes\n", wbuf->data_size);
+ __issei_cl_clean_wbuf(wbuf);
+ return 0;
+}
+
+static struct issei_host_client *__issei_cl_read_buf_check(struct issei_device *idev, u16 fw_id,
+ u16 host_id, size_t buf_size)
+{
+ struct issei_host_client *cl;
+
+ cl = __issei_cl_by_id(idev, host_id);
+ if (!cl) {
+ dev_dbg(&idev->dev, "No client %u\n", host_id);
+ return ERR_PTR(-ENOTTY);
+ }
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) {
+ cl_dbg(idev, cl, "Not connected\n");
+ return ERR_PTR(-ENODEV);
+ }
+ if (cl->fw_cl->id != fw_id) {
+ cl_dbg(idev, cl, "Wrong firmware client %u ?= %u\n", cl->fw_cl->id, fw_id);
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (buf_size > cl->fw_cl->mtu) {
+ cl_err(idev, cl, "Read is too big %zu > %u\n", buf_size, cl->fw_cl->mtu);
+ __issei_cl_disconnect(idev, cl);
+ return NULL;
+ }
+
+ if (cl->read_data) {
+ cl_err(idev, cl, "Previous data was not read by user-space, disconnecting\n");
+ __issei_cl_disconnect(idev, cl);
+ return NULL;
+ }
+
+ return cl;
+}
+
+/**
+ * issei_cl_read_buf - process data from firmware
+ * @idev: issei device
+ * @fw_id: firmware client id
+ * @host_id: host client id
+ * @buf: buffer with data
+ * @buf_size: buffer size
+ *
+ * Puts data from firmware into provided host client storage.
+ * Free buffer or consume it.
+ *
+ * Return: 0 on success or recoverable error, <0 on unrecoverable error
+ */
+int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size)
+{
+ struct issei_host_client *cl;
+
+ guard(mutex)(&idev->client_lock);
+
+ cl = __issei_cl_read_buf_check(idev, fw_id, host_id, buf_size);
+ if (IS_ERR_OR_NULL(cl)) {
+ kfree(buf);
+ return PTR_ERR(cl);
+ }
+
+ cl->read_data = buf;
+ cl->read_data_size = buf_size;
+
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->read_wait))
+ wake_up_interruptible(&cl->read_wait);
+ cl_dbg(idev, cl, "Read %zu bytes\n", buf_size);
+
+ return 0;
+}
+
+/**
+ * issei_cl_read - read data from queue to provided buffer
+ * @cl: host client
+ * @buf: buffer to store data
+ * @buf_size: buffer size
+ *
+ * Tries to take data buffer from client and return it to caller.
+ * The caller receives ownership of the data buffer.
+ *
+ * Return: read data size on success, <0 on error
+ */
+ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size)
+{
+ struct issei_device *idev = cl->idev;
+ size_t read_data_size;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+
+ if (!cl->read_data)
+ return -ENOENT;
+
+ if (cl->read_data_size > buf_size) {
+ cl_err(idev, cl, "Buffer is too small %zu > %zu\n",
+ cl->read_data_size, buf_size);
+ return -EFBIG;
+ }
+
+ *buf = cl->read_data;
+ read_data_size = cl->read_data_size;
+ cl_dbg(idev, cl, "Read by client %zu bytes\n", read_data_size);
+ __issei_cl_release_rbuf(cl);
+ return read_data_size;
+}
+
+/**
+ * issei_cl_check_read - check if client has data to read
+ *
+ * @cl: host client
+ *
+ * Return: 1 - data available, 0 - no data, < 0 on error
+ */
+int issei_cl_check_read(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+ if (!cl->read_data)
+ return 0;
+ return 1;
+}
+
+/**
+ * issei_cl_check_write - check if client is ready to write
+ *
+ * @cl: host client
+ *
+ * Return: 1 - can not write, 0 - can write, < 0 on error
+ */
+int issei_cl_check_write(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+ if (cl->write_in_progress)
+ return 1;
+ return 0;
+}
+
+void issei_cl_clean_all_wbuf(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ __issei_cl_clean_all_wbuf(idev, cl);
+}
diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h
new file mode 100644
index 000000000000..05e2f4ade9c3
--- /dev/null
+++ b/drivers/misc/issei/host_client.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HOST_CLIENT_H_
+#define _ISSEI_HOST_CLIENT_H_
+
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/wait.h>
+
+struct file;
+
+struct issei_device;
+struct issei_fw_client;
+
+/**
+ * enum issei_host_client_state - host client states
+ * @ISSEI_HOST_CL_STATE_DISCONNECTED: host client is disconnected
+ * @ISSEI_HOST_CL_STATE_CONNECTED: host client is connected
+ */
+enum issei_host_client_state {
+ ISSEI_HOST_CL_STATE_DISCONNECTED,
+ ISSEI_HOST_CL_STATE_CONNECTED,
+};
+
+/**
+ * struct issei_host_client - represents host client
+ * @list: link in host clients list
+ * @idev: issei parent device
+ * @id: host client id
+ * @fp: file associated with client
+ *
+ * @write_wait: waitqueue for pending write data
+ * @write_in_progress: indicator for write in process
+ *
+ * @state: host client state
+ * @fw_cl: pointer to firmware client, if connected
+ *
+ * @read_wait: waitqueue for read object
+ * @read_data: received data pointer
+ * @read_data_size: received data size
+ */
+struct issei_host_client {
+ struct list_head list;
+ struct issei_device *idev;
+ u16 id;
+ const struct file *fp;
+
+ wait_queue_head_t write_wait;
+ bool write_in_progress;
+
+ enum issei_host_client_state state;
+ struct issei_fw_client *fw_cl;
+
+ wait_queue_head_t read_wait;
+ u8 *read_data;
+ size_t read_data_size;
+};
+
+struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp);
+void issei_cl_remove(struct issei_host_client *cl);
+
+int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver,
+ u32 *flags);
+int issei_cl_disconnect(struct issei_host_client *cl);
+void issei_cl_all_disconnect(struct issei_device *idev);
+
+ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size);
+int issei_cl_write_from_queue(struct issei_device *idev);
+int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size);
+ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size);
+int issei_cl_check_read(struct issei_host_client *cl);
+int issei_cl_check_write(struct issei_host_client *cl);
+void issei_cl_clean_all_wbuf(struct issei_host_client *cl);
+
+#endif /* ISSEI_HOST_CLIENT_H_ */
diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c
new file mode 100644
index 000000000000..501774aa95ff
--- /dev/null
+++ b/drivers/misc/issei/hw_heci.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/align.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "hw_heci.h"
+#include "hw_heci_regs.h"
+#include "hw_msg.h"
+
+/**
+ * heci_reg_read - Reads 32bit data from the issei heci device
+ * @hw: the heci hardware structure
+ * @offset: offset from which to read the data
+ *
+ * Return: register value (u32)
+ */
+static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned long offset)
+{
+ return ioread32(hw->mem_addr + offset);
+}
+
+/**
+ * heci_reg_write - Writes 32bit data to the issei heci device
+ *
+ * @hw: the heci hardware structure
+ * @offset: offset from which to write the data
+ * @value: register value to write (u32)
+ */
+static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned long offset, u32 value)
+{
+ iowrite32(value, hw->mem_addr + offset);
+}
+
+/**
+ * heci_fwcbrw_read - Reads 32bit data from heci circular buffer
+ * @hw: the heci hardware structure
+ *
+ * Return: FW_CB_RW register value (u32)
+ */
+static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw)
+{
+ return heci_reg_read(hw, FW_CB_RW);
+}
+
+/**
+ * heci_hcbww_write - write 32bit data to the host circular buffer
+ * @hw: the heci hardware structure
+ * @data: 32bit data to be written to the host circular buffer
+ */
+static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 data)
+{
+ heci_reg_write(hw, H_CB_WW, data);
+}
+
+/**
+ * heci_irq_src - Filters IRQ source bits from the host CSR
+ * @hcsr: host CSR register value
+ *
+ * Return: interrupt source bits of host CSR
+ */
+static inline u32 heci_irq_src(u32 hcsr)
+{
+ return hcsr & H_CSR_IS;
+}
+
+/**
+ * heci_hcsr_read - Reads 32bit data from the host CSR
+ * @idev: the device structure
+ *
+ * Return: H_CSR register value (u32)
+ */
+static inline u32 heci_hcsr_read(const struct issei_device *idev)
+{
+ return heci_reg_read(to_heci_hw(idev), H_CSR);
+}
+
+/**
+ * heci_hcsr_write - writes H_CSR register to device
+ * @idev: the device structure
+ * @reg: new register value
+ */
+static inline void heci_hcsr_write(struct issei_device *idev, u32 reg)
+{
+ heci_reg_write(to_heci_hw(idev), H_CSR, reg);
+}
+
+/**
+ * heci_hcsr_set - writes H_CSR register to the heci device
+ * @idev: the device structure
+ * @reg: new register value
+ *
+ * Writes H_CSR register to the heci device
+ * and ignores the H_IS bit for it is write-one-to-zero.
+ *
+ */
+static inline void heci_hcsr_set(struct issei_device *idev, u32 reg)
+{
+ reg &= ~H_CSR_IS;
+ heci_hcsr_write(idev, reg);
+}
+
+/**
+ * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG)
+ * @idev: the device structure
+ */
+static inline void heci_hcsr_set_hig(struct issei_device *idev)
+{
+ u32 reg;
+
+ reg = heci_hcsr_read(idev) | H_CSR_IG;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * heci_fwcsr_read - Reads 32bit data from the FW CSR
+ * @idev: the device structure
+ *
+ * Return: FW_CSR_HA register value (u32)
+ */
+static inline u32 heci_fwcsr_read(const struct issei_device *idev)
+{
+ return heci_reg_read(to_heci_hw(idev), FW_CSR_HA);
+}
+
+/**
+ * heci_count_full_read_slots - counts read full slots.
+ * @idev: the device structure
+ *
+ * Return: -EOVERFLOW if overflow, otherwise filled slots count
+ */
+static int heci_count_full_read_slots(struct issei_device *idev)
+{
+ u8 buffer_depth, filled_slots;
+ u8 read_ptr, write_ptr;
+ u32 reg;
+
+ reg = heci_fwcsr_read(idev);
+ buffer_depth = (u8)FIELD_GET(FW_CSR_CBD, reg);
+ read_ptr = (u8)FIELD_GET(FW_CSR_CBRP, reg);
+ write_ptr = (u8)FIELD_GET(FW_CSR_CBWP, reg);
+ filled_slots = write_ptr - read_ptr;
+
+ /* check for overflow */
+ if (filled_slots > buffer_depth)
+ return -EOVERFLOW;
+
+ dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots);
+ return filled_slots;
+}
+
+/**
+ * heci_irq_disable - disables heci device interrupts
+ * @idev: the device structure
+ * @reg: supplied hcsr register value
+ *
+ * disables heci device interrupts using supplied hcsr register value.
+ */
+static inline void heci_irq_disable(struct issei_device *idev, u32 reg)
+{
+ reg &= ~H_CSR_IE;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * heci_irq_clear - clear and stop interrupts
+ * @idev: the device structure
+ * @reg: supplied hcsr register value
+ */
+static inline void heci_irq_clear(struct issei_device *idev, u32 reg)
+{
+ if (heci_irq_src(reg))
+ heci_hcsr_write(idev, reg);
+}
+
+/**
+ * issei_heci_irq_clear - clear and stop interrupts
+ * @idev: the device structure
+ */
+static void issei_heci_irq_clear(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ heci_irq_clear(idev, reg);
+}
+
+/**
+ * issei_heci_irq_enable - enables heci device interrupts
+ * @idev: the device structure
+ */
+static void issei_heci_irq_enable(struct issei_device *idev)
+{
+ u32 reg;
+
+ reg = heci_hcsr_read(idev) | H_CSR_IE;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * issei_heci_irq_disable - disables heci device interrupts
+ * @idev: the device structure
+ */
+static void issei_heci_irq_disable(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ heci_irq_disable(idev, reg);
+}
+
+static void issei_heci_irq_sync(struct issei_device *idev)
+{
+ synchronize_irq(to_heci_hw(idev)->irq);
+}
+
+/**
+ * issei_heci_hw_reset_release - release device from the reset
+ * @idev: the device structure
+ */
+static void issei_heci_hw_reset_release(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ reg |= H_CSR_IG;
+ reg &= ~H_CSR_RST;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * heci_hw_is_ready - check whether the hw has turned ready
+ * @idev: the device structure
+ *
+ * Return: bool
+ */
+static bool heci_hw_is_ready(struct issei_device *idev)
+{
+ u32 reg = heci_fwcsr_read(idev);
+
+ return reg & FW_CSR_RDY;
+}
+
+/**
+ * heci_hw_is_resetting - check whether the hw is in reset
+ * @idev: the device structure
+ *
+ * Return: bool
+ */
+static bool heci_hw_is_resetting(struct issei_device *idev)
+{
+ u32 reg = heci_fwcsr_read(idev);
+
+ return reg & FW_CSR_RST;
+}
+
+/**
+ * issei_heci_host_set_ready - enable device
+ * @idev: the device structure
+ */
+static void issei_heci_host_set_ready(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ reg |= H_CSR_IE | H_CSR_IG | H_CSR_RDY;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * issei_heci_hw_reset - resets fw via heci csr register.
+ * @idev: the device structure
+ * @enable: if interrupt should be enabled after reset.
+ *
+ * Return: 0 on success an error code otherwise
+ */
+static int issei_heci_hw_reset(struct issei_device *idev, bool enable)
+{
+ u32 reg;
+
+ if (enable)
+ issei_heci_irq_enable(idev);
+
+ reg = heci_hcsr_read(idev);
+ /*
+ * H_CSR_RST may be found lit before reset is started,
+ * for example if preceding reset flow hasn't completed.
+ * In that case asserting H_CSR_RST will be ignored, therefore
+ * we need to clean H_CSR_RST bit to start a successful reset sequence.
+ */
+ if (reg & H_CSR_RST) {
+ dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg);
+ reg &= ~H_CSR_RST;
+ heci_hcsr_set(idev, reg);
+ reg = heci_hcsr_read(idev);
+ }
+
+ reg |= H_CSR_RST | H_CSR_IG | H_CSR_IS;
+
+ if (!enable)
+ reg &= ~H_CSR_IE;
+
+ heci_hcsr_write(idev, reg);
+
+ /*
+ * Host reads the H_CSR once to ensure that the
+ * posted write to H_CSR completes.
+ */
+ reg = heci_hcsr_read(idev);
+
+ if (!(reg & H_CSR_RST))
+ dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg);
+
+ if (reg & H_CSR_RDY)
+ dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg);
+
+ if (!enable)
+ issei_heci_hw_reset_release(idev);
+ return 0;
+}
+
+/**
+ * issei_heci_irq_write_generate - generate interrupt to signal write completion.
+ * @idev: the device structure
+ *
+ * Return: 0 on success, -EIO if hardware is not ready and requires reset
+ */
+static int issei_heci_irq_write_generate(struct issei_device *idev)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock)
+ heci_hcsr_set_hig(idev);
+ if (!heci_hw_is_ready(idev))
+ return -EIO;
+ return 0;
+}
+
+/**
+ * issei_heci_hw_config - initial hardware configuration.
+ * @idev: the device structure
+ *
+ * Return: 0 always
+ */
+static int issei_heci_hw_config(struct issei_device *idev)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+ u32 reg;
+
+ /* Doesn't change in runtime */
+ reg = heci_hcsr_read(idev);
+ hw->hbuf_depth = FIELD_GET(H_CSR_CBD, reg);
+
+ return 0;
+}
+
+/**
+ * heci_write_hbuf - write to hardware buffer
+ * @idev: the device structure
+ * @data: data to write
+ * @data_len: data size
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t data_len)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
+ dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
+ data_len, CB_SLOT_SIZE);
+ return -EINVAL;
+ }
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock) {
+ const u32 *reg_buf = data;
+ size_t i;
+
+ for (i = 0; i < data_len / CB_SLOT_SIZE; i++)
+ heci_hcbww_write(hw, reg_buf[i]);
+ }
+
+ return issei_heci_irq_write_generate(idev);
+}
+
+/**
+ * heci_read_hbuf - read data from hardware buffer
+ * @idev: the device structure
+ * @data: buffer to store read data
+ * @data_len: buffer size
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+ u32 *reg_buf = data;
+
+ if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
+ dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
+ data_len, CB_SLOT_SIZE);
+ return -EINVAL;
+ }
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock) {
+ for (; data_len >= CB_SLOT_SIZE; data_len -= CB_SLOT_SIZE)
+ *reg_buf++ = heci_fwcbrw_read(hw);
+ }
+ return 0;
+}
+
+/**
+ * issei_heci_setup_message_send - send setup message to firmware
+ * @idev: the device structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int issei_heci_setup_message_send(struct issei_device *idev)
+{
+ struct ham_setup_shared_memory_req req = {
+ .msg_id = HAM_CB_MESSAGE_ID_REQ,
+ .ver = HAM_CB_MESSAGE_VER,
+ .reserved = 0,
+ .buffer_physical_address = idev->dma.daddr,
+ .host_to_fw_section_length = idev->dma.length.h2f,
+ .fw_to_host_section_length = idev->dma.length.f2h,
+ .control_length = idev->dma.length.ctl
+ };
+ int ret;
+
+ ret = heci_write_hbuf(idev, &req, sizeof(req));
+ if (ret)
+ dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * issei_heci_setup_message_recv - receive setup message response from firmware
+ * @idev: the device structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int issei_heci_setup_message_recv(struct issei_device *idev)
+{
+ struct ham_setup_shared_memory_res res;
+ int ret;
+
+ if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) {
+ dev_dbg(&idev->dev, "Setup response is not fully received\n");
+ return -ENODATA;
+ }
+
+ ret = heci_read_hbuf(idev, &res, sizeof(res));
+ if (ret) {
+ dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret);
+ return ret;
+ }
+
+ if (res.msg_id != HAM_CB_MESSAGE_ID_RES) {
+ dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n",
+ res.msg_id, HAM_CB_MESSAGE_ID_RES);
+ return -EPROTO;
+ }
+ if (res.status != 0) {
+ dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static const struct issei_hw_ops hw_heci_ops = {
+ .irq_clear = issei_heci_irq_clear,
+ .irq_enable = issei_heci_irq_enable,
+ .irq_disable = issei_heci_irq_disable,
+ .irq_sync = issei_heci_irq_sync,
+
+ .hw_reset = issei_heci_hw_reset,
+ .hw_config = issei_heci_hw_config,
+ .hw_reset_release = issei_heci_hw_reset_release,
+ .host_set_ready = issei_heci_host_set_ready,
+
+ .hw_is_ready = heci_hw_is_ready,
+
+ .setup_message_send = issei_heci_setup_message_send,
+ .setup_message_recv = issei_heci_setup_message_recv,
+ .irq_write_generate = issei_heci_irq_write_generate,
+};
+
+const struct issei_hw_ops *issei_heci_get_ops(void)
+{
+ return &hw_heci_ops;
+}
+
+irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id)
+{
+ struct issei_device *idev = dev_id;
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+ u32 reg;
+
+ reg = heci_hcsr_read(idev);
+ if (!heci_irq_src(reg))
+ return IRQ_NONE;
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock) {
+ reg = heci_hcsr_read(idev);
+ heci_irq_clear(idev, reg);
+
+ if (heci_hw_is_resetting(idev))
+ heci_hcsr_set_hig(idev);
+ }
+
+ dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg));
+
+ issei_poke_process_thread(idev);
+
+ return IRQ_HANDLED;
+}
+
+static const struct hw_heci_cfg hw_heci_pch_cfg = {
+ .dma_length.h2f = SZ_64K,
+ .dma_length.f2h = SZ_64K,
+ .dma_length.ctl = SZ_4K,
+};
+
+const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx)
+{
+ return &hw_heci_pch_cfg;
+}
+
+/**
+ * issei_heci_dev_init - initializes the issei device structure with hw_heci
+ * @idev: device structure
+ * @mem_addr: memory address on bar
+ * @cfg: per device generation config
+ */
+void issei_heci_dev_init(struct issei_device *idev,
+ void __iomem *mem_addr, const struct hw_heci_cfg *cfg)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ spin_lock_init(&hw->access_lock);
+ hw->mem_addr = mem_addr;
+ hw->cfg = cfg;
+}
diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h
new file mode 100644
index 000000000000..0ae2faeedf12
--- /dev/null
+++ b/drivers/misc/issei/hw_heci.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HW_HECI_H_
+#define _ISSEI_HW_HECI_H_
+#include <linux/irqreturn.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "issei_dev.h"
+
+/*
+ * hw_heci_cfg - issei heci device configuration
+ *
+ * @dma_length: DMA area length
+ */
+struct hw_heci_cfg {
+ const struct issei_dma_length dma_length;
+};
+
+/**
+ * struct issei_heci_hw - issei heci hw specific data
+ *
+ * @cfg: per device generation config and ops
+ * @mem_addr: io memory address
+ * @irq: device irq number
+ * @access_lock: spinlock to protect hw access
+ * @hbuf_depth: depth of hardware host/write buffer in slots
+ */
+struct issei_heci_hw {
+ const struct hw_heci_cfg *cfg;
+ void __iomem *mem_addr;
+ int irq;
+ spinlock_t access_lock;
+ u8 hbuf_depth;
+};
+
+#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw)
+
+const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx);
+const struct issei_hw_ops *issei_heci_get_ops(void);
+
+void issei_heci_dev_init(struct issei_device *idev,
+ void __iomem *mem_addr, const struct hw_heci_cfg *cfg);
+
+irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id);
+
+#endif /* _ISSEI_HW_HECI_H_ */
diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci_regs.h
new file mode 100644
index 000000000000..a991b670a9d3
--- /dev/null
+++ b/drivers/misc/issei/hw_heci_regs.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HW_HECI_REGS_H_
+#define _ISSEI_HW_HECI_REGS_H_
+
+#include <linux/bits.h>
+
+/* H_CB_WW - Host Circular Buffer (CB) Write Window register */
+#define H_CB_WW 0x0
+/* H_CSR - Host Control Status register */
+#define H_CSR 0x4
+#define H_CSR_CBD GENMASK(31, 24) /* Host Circular Buffer Depth */
+#define H_CSR_CBWP GENMASK(23, 16) /* Host Circular Buffer Write Pointer */
+#define H_CSR_CBRP GENMASK(15, 8) /* Host Circular Buffer Read Pointer */
+#define H_CSR_RST BIT(4) /* Host Reset */
+#define H_CSR_RDY BIT(3) /* Host Ready */
+#define H_CSR_IG BIT(2) /* Host Interrupt Generate */
+#define H_CSR_IS BIT(1) /* Host Interrupt Status */
+#define H_CSR_IE BIT(0) /* Host Interrupt Enable */
+/* FW_CB_RW - FW Circular Buffer Read Window register (read only) */
+#define FW_CB_RW 0x8
+/* FW_CSR_HA - FW Control Status Host Access register (read only) */
+#define FW_CSR_HA 0xC
+#define FW_CSR_CBD GENMASK(31, 24) /* FW CB (Circular Buffer) Depth */
+#define FW_CSR_CBWP GENMASK(23, 16) /* FW CB Write Pointer */
+#define FW_CSR_CBRP GENMASK(15, 8) /* FW CB Read Pointer */
+#define FW_CSR_RST BIT(4) /* FW Reset */
+#define FW_CSR_RDY BIT(3) /* FW Ready */
+#define FW_CSR_IG BIT(2) /* FW Interrupt Generate */
+#define FW_CSR_IS BIT(1) /* FW Interrupt Status */
+#define FW_CSR_IE BIT(0) /* FW Interrupt Enable */
+
+#define CB_SLOT_SIZE sizeof(u32) /* Circular Buffer windows size */
+
+#endif /* _ISSEI_HW_HECI_REGS_H_ */
diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h
new file mode 100644
index 000000000000..28fd3775f64c
--- /dev/null
+++ b/drivers/misc/issei/hw_msg.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HW_MSG_H_
+#define _ISSEI_HW_MSG_H_
+
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+#define HAM_CB_MESSAGE_ID_REQ 0x8086cafe
+#define HAM_CB_MESSAGE_ID_RES 0xcafe8086
+#define HAM_CB_MESSAGE_VER 0x1
+
+/**
+ * struct ham_setup_shared_memory_req - shared memory setup request
+ * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_REQ
+ * @ver: message version (%HAM_CB_MESSAGE_VER)
+ * @reserved: reserved
+ * @buffer_physical_address: physical address of DMA buffer
+ * @host_to_fw_section_length: memory size for host to fw communication
+ * @fw_to_host_section_length: memory size for fw to host communication
+ * @control_length: memory size for control buffer
+ */
+struct ham_setup_shared_memory_req {
+ u32 msg_id;
+ u16 ver;
+ u16 reserved;
+ u64 buffer_physical_address;
+ u32 host_to_fw_section_length;
+ u32 fw_to_host_section_length;
+ u32 control_length;
+} __packed __aligned(4);
+
+/**
+ * struct ham_setup_shared_memory_res - shared memory setup response
+ * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_RES
+ * @status: operation status
+ */
+struct ham_setup_shared_memory_res {
+ u32 msg_id;
+ u32 status;
+};
+
+/**
+ * struct control_buffer - control buffer structure
+ * @h2f_counter_wr: write counter host to fw
+ * @h2f_counter_rd: read counter host to fw
+ * @f2h_counter_wr: write counter fw to host
+ * @f2h_counter_rd: read counter fw to host
+ */
+struct control_buffer {
+ u32 h2f_counter_wr;
+ u32 h2f_counter_rd;
+ u32 f2h_counter_wr;
+ u32 f2h_counter_rd;
+};
+
+/* HAM messages over DMA */
+
+/**
+ * struct ham_message_header - message header over DMA
+ * @length: message length (payload only, not including header)
+ * @fw_id: firmware client id (0 means Bus Message)
+ * @host_id: host client id (0 means Bus Message)
+ * @flags: message flags
+ * @status: operation status
+ * @reserved: reserved
+ */
+struct ham_message_header {
+ u32 length;
+ u16 fw_id;
+ u16 host_id;
+ u32 flags;
+ u32 status;
+ u32 reserved;
+};
+
+/* Bus Commands */
+#define HAM_BUS_CMD_START_REQ 0x00
+#define HAM_BUS_CMD_START_RSP 0x80
+#define HAM_BUS_CMD_CLIENT_REQ 0x01
+#define HAM_BUS_CMD_CLIENT_RSP 0x81
+
+/**
+ * struct ham_bus_message - bus message header
+ * @cmd: command code
+ */
+struct ham_bus_message {
+ u32 cmd;
+};
+
+#define HAM_SUPPORTED_VERSION 0x01
+
+/**
+ * struct ham_start_message_req - start message
+ * @header: bus message header (%HAM_BUS_CMD_START_REQ)
+ * @supported_version: supported protocol version
+ * @heci_capabilities_length: protocol capabilities length in bytes
+ * @heci_capabilities: protocol capabilities data
+ */
+struct ham_start_message_req {
+ struct ham_bus_message header;
+ u16 supported_version;
+ u8 heci_capabilities_length;
+ u8 heci_capabilities[] __counted_by(heci_capabilities_length);
+} __packed;
+
+/**
+ * struct ham_start_message_res - start message response
+ * @header: bus message header (%HAM_BUS_CMD_START_RSP)
+ * @fw_version: firmware version (four u16 blocks)
+ * @supported_version: supported protocol version
+ * @heci_capabilities_length: protocol capabilities length in bytes
+ * @heci_capabilities: protocol capabilities data
+ */
+struct ham_start_message_res {
+ struct ham_bus_message header;
+ u16 fw_version[4];
+ u16 supported_version;
+ u8 heci_capabilities_length;
+ u8 heci_capabilities[] __counted_by(heci_capabilities_length);
+} __packed;
+
+/**
+ * struct ham_get_clients_req - clients list request
+ * @header: bus message header (%HAM_BUS_CMD_CLIENT_REQ)
+ */
+struct ham_get_clients_req {
+ struct ham_bus_message header;
+};
+
+/**
+ * struct ham_client_properties - single client properties
+ * @client_number: client id in firmware
+ * @protocol_ver: client protocol version
+ * @reserved: reserved
+ * @client_uuid: protocol name (UUID)
+ * @client_mtu: max message length supported by client
+ * @flags: client flags
+ */
+struct ham_client_properties {
+ u16 client_number;
+ u8 protocol_ver;
+ u8 reserved;
+ uuid_t client_uuid;
+ u32 client_mtu;
+ u32 flags;
+};
+
+/**
+ * struct ham_get_clients_res - client properties response
+ * @header: bus message header (%HAM_BUS_CMD_CLIENT_RSP)
+ * @client_count: number of clients in firmware
+ * @reserved: reserved
+ * @clients_props: list of client properties
+ */
+struct ham_get_clients_res {
+ struct ham_bus_message header;
+ u16 client_count;
+ u16 reserved;
+ struct ham_client_properties clients_props[] __counted_by(client_count);
+};
+
+#endif /* _ISSEI_HW_MSG_H_ */
diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h
new file mode 100644
index 000000000000..b47c4a7c2da4
--- /dev/null
+++ b/drivers/misc/issei/issei_dev.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_DEV_H_
+#define _ISSEI_DEV_H_
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include "dma.h"
+
+struct cdev;
+struct kset;
+
+struct issei_device;
+struct issei_host_client;
+
+extern struct class *issei_class;
+
+#define ISSEI_HOST_CLIENTS_MAX 255
+
+#define ISSEI_SUPPORTED_PROTOCOL_VER 1
+
+#define ISSEI_MAX_CONSEC_RESET 3
+
+#define ISSEI_RST_HW_READY_TIMEOUT_MSEC (2 * MSEC_PER_SEC)
+#define ISSEI_RST_STEP_TIMEOUT_MSEC (2 * MSEC_PER_SEC)
+#define ISSEI_STOP_TIMEOUT_MSEC 500
+#define ISSEI_WRITE_TIMEOUT_MSEC (MSEC_PER_SEC)
+
+/**
+ * struct issei_write_buf - write buffer object
+ * @list: linked list pointer
+ * @cl: host client that requested this write
+ * @data: data to write
+ * @data_size: data size
+ */
+struct issei_write_buf {
+ struct list_head list;
+ struct issei_host_client *cl;
+ const u8 *data;
+ size_t data_size;
+};
+
+/**
+ * struct issei_hw_ops - callbacks for hardware operations
+ * @irq_clear: clear irq
+ * @irq_enable: enable irq
+ * @irq_disable: disable irq
+ * @irq_sync: sync irq
+ * @hw_reset: initiate hardware reset
+ * @hw_config: initial hardware config
+ * @hw_is_ready: check if hardware is ready
+ * @hw_reset_release: release hardware from reset
+ * @host_set_ready: set host ready indicator
+ * @setup_message_send: send setup message
+ * @setup_message_recv: receive setup message
+ * @irq_write_generate: generate interrupt on write complete
+ */
+struct issei_hw_ops {
+ void (*irq_clear)(struct issei_device *idev);
+ void (*irq_enable)(struct issei_device *idev);
+ void (*irq_disable)(struct issei_device *idev);
+ void (*irq_sync)(struct issei_device *idev);
+ int (*hw_reset)(struct issei_device *idev, bool enable);
+ int (*hw_config)(struct issei_device *idev);
+ bool (*hw_is_ready)(struct issei_device *idev);
+ void (*hw_reset_release)(struct issei_device *idev);
+ void (*host_set_ready)(struct issei_device *idev);
+ int (*setup_message_send)(struct issei_device *idev);
+ int (*setup_message_recv)(struct issei_device *idev);
+ int (*irq_write_generate)(struct issei_device *idev);
+};
+
+/**
+ * enum issei_rst_state: driver reset flow states
+ * @ISSEI_RST_STATE_INIT: initial state
+ * @ISSEI_RST_STATE_HW_READY: waiting for HW to be ready
+ * @ISSEI_RST_STATE_SETUP: waiting for channel setup completion
+ * @ISSEI_RST_STATE_START: waiting for start handshake completion
+ * @ISSEI_RST_STATE_CLIENT_ENUM: waiting for client enumeration
+ * @ISSEI_RST_STATE_DONE: reset flow is done
+ * @ISSEI_RST_STATE_DISABLED: flow is disabled
+ */
+enum issei_rst_state {
+ ISSEI_RST_STATE_INIT,
+ ISSEI_RST_STATE_HW_READY,
+ ISSEI_RST_STATE_SETUP,
+ ISSEI_RST_STATE_START,
+ ISSEI_RST_STATE_CLIENT_ENUM,
+ ISSEI_RST_STATE_DONE,
+ ISSEI_RST_STATE_DISABLED,
+};
+
+/**
+ * struct issei_device - issei device
+ * @parent: parent device object
+ * @dev: associated device object
+ * @cdev: character device
+ * @minor: allocated minor number
+ * @wait_has_data: wait queue for data
+ * @has_data: there are data to process
+ * @power_down: device is powering down
+ * @wait_rst_state: waitqueue for reset state processing
+ * @rst_state: reset state
+ * @fw_protocol_ver: protocol version
+ * @fw_version: firmware version
+ * @process_thread: worker thread
+ * @reset_count: number of consecutive link reset attempts
+ * @all_reset_count: cumilative number of link reset attempts
+ * @client_lock: mutex to protect client lists and write queue
+ * @host_client_list: host clients list
+ * @host_client_last_id: last allocated host client id
+ * @host_client_count: number of active host clients
+ * @fw_client_list: firmware clients list
+ * @write_queue: write queue
+ * @last_write_ts: last write timestamp
+ * @dma: DMA memory configuration
+ * @ops: hardware operations
+ * @hw: hw-specific data
+ */
+struct issei_device {
+ struct device *parent;
+ struct device dev;
+ struct cdev *cdev;
+ u32 minor;
+ wait_queue_head_t wait_has_data;
+ bool has_data;
+ bool power_down;
+ wait_queue_head_t wait_rst_state;
+ enum issei_rst_state rst_state;
+ u16 fw_protocol_ver;
+ u16 fw_version[4];
+ /* reset flow */
+ struct task_struct *process_thread;
+ u8 reset_count;
+ u8 all_reset_count;
+ /* clients */
+ struct mutex client_lock;
+ struct list_head host_client_list;
+ u16 host_client_last_id;
+ u8 host_client_count;
+ struct kset *fw_clients;
+ struct list_head fw_client_list;
+ struct list_head write_queue;
+ ktime_t last_write_ts;
+ struct issei_dma dma;
+ const struct issei_hw_ops *ops;
+ char hw[];
+};
+
+static inline void issei_poke_process_thread(struct issei_device *idev)
+{
+ WRITE_ONCE(idev->has_data, true);
+ wake_up_interruptible(&idev->wait_has_data);
+}
+
+int issei_start(struct issei_device *idev);
+void issei_stop(struct issei_device *idev);
+#endif /* _ISSEI_DEV_H_ */
diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c
new file mode 100644
index 000000000000..5987fb340250
--- /dev/null
+++ b/drivers/misc/issei/main.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "cdev.h"
+#include "fw_client.h"
+#include "host_client.h"
+#include "ham.h"
+#include "issei_dev.h"
+
+static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_state state)
+{
+ idev->rst_state = state;
+ /* wake up the thread */
+ if (waitqueue_active(&idev->wait_rst_state))
+ wake_up(&idev->wait_rst_state);
+}
+
+static int issei_reset(struct issei_device *idev)
+{
+ int ret;
+
+ idev->ops->irq_clear(idev);
+
+ issei_cl_all_disconnect(idev);
+ issei_fw_cl_remove_all(idev);
+ /* No need to check for overflow here, the counter is used only for info */
+ idev->all_reset_count++;
+ ret = idev->ops->hw_reset(idev, !idev->power_down);
+ issei_dmam_setup(idev);
+ if (ret) {
+ dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret);
+ return ret;
+ }
+
+ if (idev->power_down) {
+ dev_dbg(&idev->dev, "powering down: end of reset\n");
+ issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int issei_process_read_msg(struct issei_device *idev)
+{
+ struct issei_dma_data data = {};
+ int ret;
+
+ ret = issei_dma_read(idev, &data);
+ if (ret)
+ return ret;
+
+ dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id,
+ data.status, data.length);
+ if (data.status != HAMS_SUCCESS) {
+ dev_err(&idev->dev, "Command failed with status 0x%02X", data.status);
+ kfree(data.buf);
+ ret = -EIO;
+ } else {
+ if (issei_is_ham_rsp(data.fw_id, data.host_id))
+ ret = issei_ham_process_ham_rsp(idev, data.buf, data.length);
+ else
+ ret = issei_cl_read_buf(idev, data.fw_id, data.host_id,
+ data.buf, data.length);
+ }
+ idev->ops->irq_write_generate(idev);
+ return ret;
+}
+
+static int issei_process_write_msg(struct issei_device *idev)
+{
+ if (idev->rst_state != ISSEI_RST_STATE_DONE)
+ return 0;
+
+ return issei_cl_write_from_queue(idev);
+}
+
+static int issei_process_thread(void *_dev)
+{
+ long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT;
+ struct issei_device *idev = _dev;
+ int ret;
+
+ while (!kthread_should_stop()) {
+ dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state);
+ if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) {
+ if (!idev->power_down)
+ dev_dbg(&idev->dev, "HW not ready, resetting\n");
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ }
+ if (idev->power_down)
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ WRITE_ONCE(idev->has_data, false);
+ dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state);
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ ret = 0;
+ switch (idev->rst_state) {
+ case ISSEI_RST_STATE_DISABLED:
+ if (idev->power_down) {
+ dev_dbg(&idev->dev, "Interrupt in power down?\n");
+ break;
+ }
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ fallthrough;
+
+ case ISSEI_RST_STATE_INIT:
+ idev->ops->irq_clear(idev);
+ idev->ops->irq_sync(idev);
+
+ if (!idev->power_down) {
+ idev->reset_count++;
+ if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) {
+ dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
+ issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED);
+ break;
+ }
+ }
+
+ ret = issei_reset(idev);
+ if (idev->power_down) {
+ dev_dbg(&idev->dev, "Powering down\n");
+ return 0;
+ }
+ if (ret)
+ break;
+
+ idev->rst_state = ISSEI_RST_STATE_HW_READY;
+ timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC);
+ break;
+
+ case ISSEI_RST_STATE_HW_READY:
+ if (!idev->ops->hw_is_ready(idev)) {
+ dev_dbg(&idev->dev, "HW is not ready?\n");
+ timeout = old_timeout;
+ break;
+ }
+
+ dev_dbg(&idev->dev, "HW is ready\n");
+ idev->ops->hw_reset_release(idev);
+ idev->ops->host_set_ready(idev);
+ ret = idev->ops->setup_message_send(idev);
+ if (ret)
+ break;
+
+ idev->rst_state = ISSEI_RST_STATE_SETUP;
+ timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+ break;
+
+ case ISSEI_RST_STATE_SETUP:
+ ret = idev->ops->setup_message_recv(idev);
+ if (ret) {
+ if (ret == -ENODATA) {
+ ret = 0;
+ timeout = old_timeout;
+ }
+ } else {
+ timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+ ret = issei_ham_send_start_req(idev);
+ idev->rst_state = ISSEI_RST_STATE_START;
+ }
+ break;
+
+ case ISSEI_RST_STATE_START:
+ ret = issei_process_read_msg(idev);
+ if (!ret) {
+ timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+ idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM;
+ } else if (ret == -ENODATA) {
+ ret = 0;
+ timeout = old_timeout;
+ }
+ break;
+
+ case ISSEI_RST_STATE_CLIENT_ENUM:
+ ret = issei_process_read_msg(idev);
+ if (ret) {
+ if (ret == -ENODATA) {
+ ret = 0;
+ timeout = old_timeout;
+ }
+ } else {
+ idev->reset_count = 0;
+ idev->rst_state = ISSEI_RST_STATE_DONE;
+ dev_dbg(&idev->dev, "Reset finished successfully\n");
+ }
+ break;
+
+ case ISSEI_RST_STATE_DONE:
+ ret = issei_process_read_msg(idev);
+ if (ret != 0 && ret != -ENODATA)
+ break;
+
+ ret = issei_process_write_msg(idev);
+ break;
+ }
+
+ if (ret) {
+ dev_warn(&idev->dev, "Process failed ret = %d\n", ret);
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ continue;
+ }
+
+ /*
+ * Every thread that has data to process sets the 'has_data' flag and
+ * triggers the wait queue.
+ * The processing thread, in each loop iteration, resets 'has_data'
+ * and processes all available data.
+ *
+ * After processing, the thread waits for 'has_data' to be set again.
+ *
+ * If the wait function times out but 'has_data' becomes 1 before
+ * the subsequent atomic read check, this is acceptable from a flow
+ * perspective - the thread will continue processing the data.
+ *
+ * The 'has_data' flag cannot become 0 between the wait function and
+ * the atomic read check, since only this thread is allowed to reset it to 0.
+ */
+
+ old_timeout = wait_event_interruptible_timeout(idev->wait_has_data,
+ READ_ONCE(idev->has_data),
+ timeout);
+ if (idev->rst_state == ISSEI_RST_STATE_DISABLED)
+ continue;
+
+ if (!READ_ONCE(idev->has_data)) {
+ dev_warn(&idev->dev, "Timed out at state %d, resetting\n",
+ idev->rst_state);
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * issei_start - configure HW device and start processing thread.
+ * @idev: the device structure
+ *
+ * Return: 0 on success, < 0 on failure
+ */
+int issei_start(struct issei_device *idev)
+{
+ int ret;
+
+ idev->power_down = false;
+
+ ret = issei_dmam_setup(idev);
+ if (ret)
+ return ret;
+
+ idev->ops->irq_clear(idev);
+
+ ret = idev->ops->hw_config(idev);
+ if (ret)
+ return ret;
+
+ idev->process_thread = kthread_run(issei_process_thread, idev,
+ "kisseiprocess/%s", dev_name(&idev->dev));
+ if (IS_ERR(idev->process_thread)) {
+ ret = PTR_ERR(idev->process_thread);
+ dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret);
+ return ret;
+ }
+
+ issei_poke_process_thread(idev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(issei_start);
+
+/**
+ * issei_stop - stop interrupts and processing thread.
+ * @idev: the device structure
+ */
+void issei_stop(struct issei_device *idev)
+{
+ idev->power_down = true;
+
+ idev->ops->irq_clear(idev);
+ idev->ops->irq_sync(idev);
+
+ issei_poke_process_thread(idev);
+
+ wait_event_timeout(idev->wait_rst_state,
+ (idev->rst_state == ISSEI_RST_STATE_DISABLED),
+ msecs_to_jiffies(ISSEI_STOP_TIMEOUT_MSEC));
+ kthread_stop(idev->process_thread);
+}
+EXPORT_SYMBOL_GPL(issei_stop);
diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c
new file mode 100644
index 000000000000..86c567a4bf0f
--- /dev/null
+++ b/drivers/misc/issei/pci_heci.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+
+#include "cdev.h"
+#include "hw_heci.h"
+#include "hw_heci_regs.h"
+
+static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct device *dev = &pdev->dev;
+ const struct hw_heci_cfg *cfg;
+ struct issei_device *idev;
+ struct issei_heci_hw *hw;
+ char __iomem *registers;
+ int err;
+
+ cfg = issei_heci_get_cfg(ent->driver_data);
+ if (!cfg)
+ return dev_err_probe(dev, -ENODEV, "no usable configuration.\n");
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return dev_err_probe(dev, err, "failed to enable pci device.\n");
+
+ pci_set_master(pdev);
+
+ registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME);
+ if (IS_ERR(registers))
+ return dev_err_probe(dev, PTR_ERR(registers), "failed to get pci region.\n");
+
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (err)
+ return dev_err_probe(dev, err, "no usable DMA configuration.\n");
+
+ idev = issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_get_ops());
+ if (IS_ERR(idev))
+ return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n");
+
+ issei_heci_dev_init(idev, registers, cfg);
+
+ pci_set_drvdata(pdev, idev);
+
+ err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
+ if (err < 0) {
+ dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n");
+ goto deregister;
+ }
+
+ hw = to_heci_hw(idev);
+ hw->irq = pci_irq_vector(pdev, 0);
+
+ err = request_threaded_irq(hw->irq,
+ issei_heci_irq_quick_handler,
+ NULL,
+ IRQF_SHARED, KBUILD_MODNAME, idev);
+ if (err)
+ goto release_irq;
+
+ err = issei_start(idev);
+ if (err) {
+ dev_err_probe(dev, err, "init hw failure.\n");
+ goto free_irq;
+ }
+
+ return 0;
+
+free_irq:
+ idev->ops->irq_disable(idev);
+ free_irq(hw->irq, idev);
+release_irq:
+ pci_free_irq_vectors(pdev);
+deregister:
+ issei_deregister(idev);
+ return err;
+}
+
+static void issei_heci_shutdown(struct pci_dev *pdev)
+{
+ struct issei_device *idev = pci_get_drvdata(pdev);
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ issei_stop(idev);
+
+ idev->ops->irq_disable(idev);
+ free_irq(hw->irq, idev);
+ pci_free_irq_vectors(pdev);
+}
+
+static void issei_heci_remove(struct pci_dev *pdev)
+{
+ issei_heci_shutdown(pdev);
+
+ issei_deregister(pci_get_drvdata(pdev));
+}
+
+static int issei_heci_pm_suspend(struct device *device)
+{
+ struct issei_device *idev = dev_get_drvdata(device);
+
+ issei_stop(idev);
+ idev->ops->irq_disable(idev);
+
+ return 0;
+}
+
+static int issei_heci_pm_resume(struct device *device)
+{
+ struct issei_device *idev = dev_get_drvdata(device);
+
+ return issei_start(idev);
+}
+
+static const struct dev_pm_ops issei_heci_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(issei_heci_pm_suspend, issei_heci_pm_resume)
+};
+
+static const struct pci_device_id heci_pci_tbl[] = {
+ {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */
+ {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panther Lake H */
+ {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panther Lake P */
+ {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */
+ {PCI_VDEVICE(INTEL, 0xD358)}, /* Nova Lake H */
+ {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */
+ {}
+};
+MODULE_DEVICE_TABLE(pci, heci_pci_tbl);
+
+static struct pci_driver issei_heci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = heci_pci_tbl,
+ .probe = issei_heci_probe,
+ .remove = issei_heci_remove,
+ .shutdown = issei_heci_shutdown,
+ .driver = {
+ .pm = &issei_heci_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+module_pci_driver(issei_heci_driver);
+
+MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface - HECI");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("INTEL_SSEI");
diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c
index 6c65fbf22e75..c2ca63e40814 100644
--- a/drivers/misc/keba/cp500.c
+++ b/drivers/misc/keba/cp500.c
@@ -887,7 +887,7 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
else
return -ENODEV;
- ret = pci_enable_device(pci_dev);
+ ret = pcim_enable_device(pci_dev);
if (ret)
return ret;
pci_set_master(pci_dev);
@@ -896,18 +896,15 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
startup.end = startup.start + cp500->devs->startup.size - 1;
cp500->system_startup_addr = devm_ioremap_resource(&pci_dev->dev,
&startup);
- if (IS_ERR(cp500->system_startup_addr)) {
- ret = PTR_ERR(cp500->system_startup_addr);
- goto out_disable;
- }
+ if (IS_ERR(cp500->system_startup_addr))
+ return PTR_ERR(cp500->system_startup_addr);
cp500->msix_num = pci_alloc_irq_vectors(pci_dev, CP500_NUM_MSIX_NO_MMI,
CP500_NUM_MSIX, PCI_IRQ_MSIX);
if (cp500->msix_num < CP500_NUM_MSIX_NO_MMI) {
dev_err(&pci_dev->dev,
"Hardware does not support enough MSI-X interrupts\n");
- ret = -ENODEV;
- goto out_disable;
+ return -ENODEV;
}
cp500_vers = ioread32(cp500->system_startup_addr + CP500_VERSION_REG);
@@ -937,9 +934,6 @@ out_unregister_nvmem:
nvmem_unregister_notifier(&cp500->nvmem_notifier);
out_free_irq:
pci_free_irq_vectors(pci_dev);
-out_disable:
- pci_clear_master(pci_dev);
- pci_disable_device(pci_dev);
return ret;
}
@@ -962,9 +956,6 @@ static void cp500_remove(struct pci_dev *pci_dev)
pci_set_drvdata(pci_dev, 0);
pci_free_irq_vectors(pci_dev);
-
- pci_clear_master(pci_dev);
- pci_disable_device(pci_dev);
}
static struct pci_device_id cp500_ids[] = {
diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c
index fc54afd1d05b..874dd50fac66 100644
--- a/drivers/misc/keba/lan9252.c
+++ b/drivers/misc/keba/lan9252.c
@@ -339,8 +339,8 @@ out:
}
static const struct spi_device_id lan9252_id[] = {
- {"lan9252"},
- {}
+ { .name = "lan9252" },
+ { }
};
MODULE_DEVICE_TABLE(spi, lan9252_id);
diff --git a/drivers/misc/lan966x_pci.c b/drivers/misc/lan966x_pci.c
index 0bb90c0943bf..b0949c653e5b 100644
--- a/drivers/misc/lan966x_pci.c
+++ b/drivers/misc/lan966x_pci.c
@@ -183,6 +183,7 @@ static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
return 0;
err_unload_overlay:
+ of_platform_depopulate(dev);
lan966x_pci_unload_overlay(data);
return ret;
}
diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c
index 93949df3bcff..740b5cf53842 100644
--- a/drivers/misc/lattice-ecp3-config.c
+++ b/drivers/misc/lattice-ecp3-config.c
@@ -219,8 +219,8 @@ static void lattice_ecp3_remove(struct spi_device *spi)
}
static const struct spi_device_id lattice_ecp3_id[] = {
- { "ecp3-17", 0 },
- { "ecp3-35", 0 },
+ { .name = "ecp3-17" },
+ { .name = "ecp3-35" },
{ }
};
MODULE_DEVICE_TABLE(spi, lattice_ecp3_id);
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
index 15119584473c..7c3ca6d604d1 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
@@ -95,7 +95,7 @@ static union axis_conversion lis3lv02d_axis_map =
#ifdef CONFIG_OF
static const struct of_device_id lis3lv02d_i2c_dt_ids[] = {
{ .compatible = "st,lis3lv02d" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids);
#endif
@@ -247,9 +247,9 @@ static int lis3_i2c_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct i2c_device_id lis3lv02d_id[] = {
- {"lis3lv02d", LIS3LV02D},
- {"lis331dlh", LIS331DLH},
- {}
+ { .name = "lis3lv02d", .driver_data = LIS3LV02D },
+ { .name = "lis331dlh", .driver_data = LIS331DLH },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lis3lv02d_id);
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
index 203a108b8883..5bf95176a00e 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
@@ -60,7 +60,7 @@ static union axis_conversion lis3lv02d_axis_normal =
#ifdef CONFIG_OF
static const struct of_device_id lis302dl_spi_dt_ids[] = {
{ .compatible = "st,lis302dl-spi" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
#endif
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
index 48bea1117771..b0b678e6b28d 100644
--- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
@@ -150,7 +150,7 @@ static const struct pci_device_id pci1xxxx_tbl[] = {
{ PCI_DEVICE(0x1055, 0xA035) },
{ PCI_DEVICE(0x1055, 0xA045) },
{ PCI_DEVICE(0x1055, 0xA055) },
- {0,}
+ { }
};
MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl);
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
index 8eddbaa1fccd..82635db04dc5 100644
--- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
@@ -513,8 +513,8 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev,
static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume);
static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = {
- {.name = "mchp_pci1xxxx_gp.gp_gpio"},
- {}
+ { .name = "mchp_pci1xxxx_gp.gp_gpio" },
+ { }
};
MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_gpio_auxiliary_id_table);
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
index a2ed477e0370..09841c4f1a06 100644
--- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
@@ -419,8 +419,8 @@ static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev)
}
static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = {
- {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"},
- {},
+ { .name = "mchp_pci1xxxx_gp.gp_otp_e2p" },
+ { }
};
MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table);
diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c
index 73d5beeb9c34..376840b2c354 100644
--- a/drivers/misc/mei/gsc-me.c
+++ b/drivers/misc/mei/gsc-me.c
@@ -101,10 +101,8 @@ static int mei_gsc_probe(struct auxiliary_device *aux_dev,
mei_me_irq_quick_handler,
mei_me_irq_thread_handler,
IRQF_ONESHOT, KBUILD_MODNAME, dev);
- if (ret) {
- dev_err(device, "irq register failed %d\n", ret);
+ if (ret)
goto err;
- }
}
ret = mei_register(dev, device);
diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c
index f6a258c2b838..9fa69acf28d5 100644
--- a/drivers/misc/mei/mei_lb.c
+++ b/drivers/misc/mei/mei_lb.c
@@ -281,7 +281,7 @@ static int mei_lb_check_response_v2(const struct device *dev, ssize_t bytes,
bytes, sizeof(rsp->rheader));
return -ENOMSG;
}
- if (rsp->rheader.header.command_id != MEI_LB2_CMD) {
+ if (rsp->rheader.header.command_id != cpu_to_le32(MEI_LB2_CMD)) {
dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n",
rsp->rheader.header.command_id, MEI_LB2_CMD);
return -EPROTO;
@@ -327,7 +327,7 @@ static int mei_lb_push_payload_v2(struct device *dev, struct mei_cl_device *clde
if (sent_data + chunk_size == payload_size)
last_chunk = MEI_LB2_FLAG_LST_CHUNK;
- req->header.command_id = MEI_LB2_CMD;
+ req->header.command_id = cpu_to_le32(MEI_LB2_CMD);
req->type = cpu_to_le32(type);
req->flags = cpu_to_le32(flags | first_chunk | last_chunk);
req->reserved = 0;
diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c
index 70792bf9b3c0..6e1e8008f267 100644
--- a/drivers/misc/mei/pci-csc.c
+++ b/drivers/misc/mei/pci-csc.c
@@ -240,7 +240,7 @@ static const struct dev_pm_ops mei_csc_pm_ops = {
static const struct pci_device_id mei_csc_pci_tbl[] = {
{ PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) },
- {}
+ { }
};
MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl);
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 55e0b8a98827..790a4647811b 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -27,114 +27,114 @@
/* mei_pci_tbl - PCI Device ID Table */
static const struct pci_device_id mei_me_pci_tbl[] = {
- {PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)},
+ { PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG) },
/* required last entry */
- {0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index f5441bc5efe2..815ca72877ce 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -24,10 +24,10 @@
#include "hw-txe.h"
static const struct pci_device_id mei_txe_pci_tbl[] = {
- {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */
- {PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */
+ { PCI_VDEVICE(INTEL, 0x0F18) }, /* Baytrail */
+ { PCI_VDEVICE(INTEL, 0x2298) }, /* Cherrytrail */
- {0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c
index 5100234ba5b6..ef6dcc043862 100644
--- a/drivers/misc/mei/platform-vsc.c
+++ b/drivers/misc/mei/platform-vsc.c
@@ -435,7 +435,7 @@ static int mei_vsc_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume);
static const struct platform_device_id mei_vsc_id_table[] = {
- { MEI_VSC_DRV_NAME },
+ { .name = MEI_VSC_DRV_NAME },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, mei_vsc_id_table);
diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c
index 5ecf99883996..432bc59f11f9 100644
--- a/drivers/misc/mei/vsc-tp.c
+++ b/drivers/misc/mei/vsc-tp.c
@@ -552,11 +552,11 @@ static void vsc_tp_remove(struct spi_device *spi)
}
static const struct acpi_device_id vsc_tp_acpi_ids[] = {
- { "INTC1009" }, /* Raptor Lake */
- { "INTC1058" }, /* Tiger Lake */
- { "INTC1094" }, /* Alder Lake */
- { "INTC10D0" }, /* Meteor Lake */
- {}
+ { .id = "INTC1009" }, /* Raptor Lake */
+ { .id = "INTC1058" }, /* Tiger Lake */
+ { .id = "INTC1094" }, /* Alder Lake */
+ { .id = "INTC10D0" }, /* Meteor Lake */
+ { }
};
MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids);
diff --git a/drivers/misc/mrvl_cn10k_dpi.c b/drivers/misc/mrvl_cn10k_dpi.c
index 7d5433121ff6..73ff0f198ec3 100644
--- a/drivers/misc/mrvl_cn10k_dpi.c
+++ b/drivers/misc/mrvl_cn10k_dpi.c
@@ -470,10 +470,8 @@ static int dpi_irq_init(struct dpipf *dpi)
ret = devm_request_irq(dev, pci_irq_vector(pdev, DPI_MBOX_PF_VF_INT_IDX),
dpi_mbox_intr_handler, 0, "dpi-mbox", dpi);
- if (ret) {
- dev_err(dev, "DPI: request_irq failed for mbox; err=%d\n", ret);
+ if (ret)
return ret;
- }
dpi_reg_write(dpi, DPI_MBOX_VF_PF_INT_ENA_W1S, GENMASK_ULL(31, 0));
@@ -658,7 +656,7 @@ static void dpi_remove(struct pci_dev *pdev)
static const struct pci_device_id dpi_id_table[] = {
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_MRVL_CN10K_DPI_PF,
PCI_VENDOR_ID_CAVIUM, PCI_SUBDEVID_MRVL_CN10K_DPI_PF) },
- { 0, } /* end of table */
+ { } /* end of table */
};
static struct pci_driver dpi_driver = {
diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c
index 3960506eb7ab..a3a9f9bfc6b7 100644
--- a/drivers/misc/nsm.c
+++ b/drivers/misc/nsm.c
@@ -61,7 +61,7 @@ struct nsm {
/* NSM device ID */
static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID },
- { 0 },
+ { }
};
static struct nsm *file_to_nsm(struct file *file)
@@ -243,7 +243,7 @@ static int nsm_sendrecv_msg_locked(struct nsm *nsm)
goto cleanup;
}
- msg->resp.len = len;
+ msg->resp.len = min_t(unsigned int, len, sizeof(msg->resp.data));
rc = 0;
@@ -365,9 +365,8 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
/* Copy user argument struct to kernel argument struct */
- r = -EFAULT;
if (copy_from_user(&raw, argp, _IOC_SIZE(cmd)))
- return r;
+ return -EFAULT;
mutex_lock(&nsm->lock);
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index 02c9d1192812..4a805919bb0c 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -875,6 +875,9 @@ static int setup_wait(struct ntsync_device *dev,
if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
return -EINVAL;
+ if (!args->owner)
+ return -EINVAL;
+
if (size >= sizeof(fds))
return -EINVAL;
diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
index cb920aa88d3a..0cf38bd482a9 100644
--- a/drivers/misc/ocxl/pci.c
+++ b/drivers/misc/ocxl/pci.c
@@ -9,7 +9,7 @@
* vendor/device ID to help differentiate devices.
*/
static const struct pci_device_id ocxl_pci_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), },
+ { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B) },
{ }
};
MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);
diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c
index 45060fb4ea27..b3cf59954111 100644
--- a/drivers/misc/open-dice.c
+++ b/drivers/misc/open-dice.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2021 - Google LLC
* Author: David Brazdil <dbrazdil@google.com>
+ * Author: Song Guo <songguo@google.com>
*
* Driver for Open Profile for DICE.
*
@@ -19,6 +20,7 @@
* close(fd);
*/
+#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
@@ -31,7 +33,8 @@
struct open_dice_drvdata {
struct mutex lock;
char name[16];
- struct reserved_mem *rmem;
+ phys_addr_t mem_base;
+ resource_size_t mem_size;
struct miscdevice misc;
};
@@ -45,14 +48,14 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata)
void *kaddr;
mutex_lock(&drvdata->lock);
- kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base,
- drvdata->rmem->size, MEMREMAP_WC);
+ kaddr = devm_memremap(drvdata->misc.this_device, drvdata->mem_base,
+ drvdata->mem_size, MEMREMAP_WC);
if (IS_ERR(kaddr)) {
mutex_unlock(&drvdata->lock);
return PTR_ERR(kaddr);
}
- memset(kaddr, 0, drvdata->rmem->size);
+ memset(kaddr, 0, drvdata->mem_size);
devm_memunmap(drvdata->misc.this_device, kaddr);
mutex_unlock(&drvdata->lock);
return 0;
@@ -64,7 +67,7 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata)
static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len,
loff_t *off)
{
- unsigned long val = to_open_dice_drvdata(filp)->rmem->size;
+ unsigned long val = to_open_dice_drvdata(filp)->mem_size;
return simple_read_from_buffer(ptr, len, off, &val, sizeof(val));
}
@@ -102,8 +105,8 @@ static int open_dice_mmap_prepare(struct vm_area_desc *desc)
/* Create write-combine mapping so all clients observe a wipe. */
desc->page_prot = pgprot_writecombine(desc->page_prot);
vma_desc_set_flags(desc, VMA_DONTCOPY_BIT, VMA_DONTDUMP_BIT);
- mmap_action_simple_ioremap(desc, drvdata->rmem->base,
- drvdata->rmem->size);
+ mmap_action_simple_ioremap(desc, drvdata->mem_base,
+ drvdata->mem_size);
return 0;
}
@@ -118,22 +121,40 @@ static int __init open_dice_probe(struct platform_device *pdev)
{
static unsigned int dev_idx;
struct device *dev = &pdev->dev;
- struct reserved_mem *rmem;
struct open_dice_drvdata *drvdata;
+ phys_addr_t mem_base;
+ resource_size_t mem_size;
int ret;
- rmem = of_reserved_mem_lookup(dev->of_node);
- if (!rmem) {
- dev_err(dev, "failed to lookup reserved memory\n");
+ if (dev->of_node) {
+ struct reserved_mem *rmem = of_reserved_mem_lookup(dev->of_node);
+
+ if (!rmem) {
+ dev_err(dev, "failed to lookup reserved memory\n");
+ return -EINVAL;
+ }
+ mem_base = rmem->base;
+ mem_size = rmem->size;
+ } else if (is_acpi_node(dev->fwnode)) {
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(dev, "failed to get MMIO resource\n");
+ return -EINVAL;
+ }
+ mem_base = res->start;
+ mem_size = resource_size(res);
+ } else {
+ dev_err(dev, "device not supported (no DT or ACPI node)\n");
return -EINVAL;
}
- if (!rmem->size || (rmem->size > ULONG_MAX)) {
+ if (!mem_size || (mem_size > ULONG_MAX)) {
dev_err(dev, "invalid memory region size\n");
return -EINVAL;
}
- if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) {
+ if (!PAGE_ALIGNED(mem_base) || !PAGE_ALIGNED(mem_size)) {
dev_err(dev, "memory region must be page-aligned\n");
return -EINVAL;
}
@@ -143,7 +164,8 @@ static int __init open_dice_probe(struct platform_device *pdev)
return -ENOMEM;
*drvdata = (struct open_dice_drvdata){
- .rmem = rmem,
+ .mem_base = mem_base,
+ .mem_size = mem_size,
.misc = (struct miscdevice){
.parent = dev,
.name = drvdata->name,
@@ -177,7 +199,7 @@ static void open_dice_remove(struct platform_device *pdev)
static const struct of_device_id open_dice_of_match[] = {
{ .compatible = "google,open-dice" },
- {},
+ { }
};
static struct platform_driver open_dice_driver = {
@@ -207,3 +229,4 @@ module_exit(open_dice_exit);
MODULE_DESCRIPTION("Driver for Open Profile for DICE.");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");
+MODULE_AUTHOR("Song Guo <songguo@google.com>");
diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c
index 19c4fa017f24..15785597da40 100644
--- a/drivers/misc/pch_phub.c
+++ b/drivers/misc/pch_phub.c
@@ -83,22 +83,16 @@
#define PCH_PHUB_OROM_SIZE 15360
+enum pch_phub_type {
+ PCH_EG20T,
+ PCH_ML7213,
+ PCH_ML7223M,
+ PCH_ML7223N,
+ PCH_ML7831,
+};
+
/**
* struct pch_phub_reg - PHUB register structure
- * @phub_id_reg: PHUB_ID register val
- * @q_pri_val_reg: QUEUE_PRI_VAL register val
- * @rc_q_maxsize_reg: RC_QUEUE_MAXSIZE register val
- * @bri_q_maxsize_reg: BRI_QUEUE_MAXSIZE register val
- * @comp_resp_timeout_reg: COMP_RESP_TIMEOUT register val
- * @bus_slave_control_reg: BUS_SLAVE_CONTROL_REG register val
- * @deadlock_avoid_type_reg: DEADLOCK_AVOID_TYPE register val
- * @intpin_reg_wpermit_reg0: INTPIN_REG_WPERMIT register 0 val
- * @intpin_reg_wpermit_reg1: INTPIN_REG_WPERMIT register 1 val
- * @intpin_reg_wpermit_reg2: INTPIN_REG_WPERMIT register 2 val
- * @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val
- * @int_reduce_control_reg: INT_REDUCE_CONTROL registers val
- * @clkcfg_reg: CLK CFG register val
- * @funcsel_reg: Function select register value
* @pch_phub_base_address: Register base address
* @pch_phub_extrom_base_address: external rom base address
* @pch_mac_start_address: MAC address area start address
@@ -107,25 +101,11 @@
* @pdev: pointer to pci device struct
*/
struct pch_phub_reg {
- u32 phub_id_reg;
- u32 q_pri_val_reg;
- u32 rc_q_maxsize_reg;
- u32 bri_q_maxsize_reg;
- u32 comp_resp_timeout_reg;
- u32 bus_slave_control_reg;
- u32 deadlock_avoid_type_reg;
- u32 intpin_reg_wpermit_reg0;
- u32 intpin_reg_wpermit_reg1;
- u32 intpin_reg_wpermit_reg2;
- u32 intpin_reg_wpermit_reg3;
- u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
- u32 clkcfg_reg;
- u32 funcsel_reg;
void __iomem *pch_phub_base_address;
void __iomem *pch_phub_extrom_base_address;
u32 pch_mac_start_address;
u32 pch_opt_rom_start_address;
- int ioh_type;
+ enum pch_phub_type ioh_type;
struct pci_dev *pdev;
};
@@ -344,10 +324,19 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data)
int retval;
int i;
- if ((chip->ioh_type == 1) || (chip->ioh_type == 5)) /* EG20T or ML7831*/
+ switch (chip->ioh_type) {
+ case PCH_EG20T:
+ case PCH_ML7831:
retval = pch_phub_gbe_serial_rom_conf(chip);
- else /* ML7223 */
+ break;
+
+ case PCH_ML7213:
+ case PCH_ML7223M:
+ case PCH_ML7223N:
retval = pch_phub_gbe_serial_rom_conf_mp(chip);
+ break;
+ }
+
if (retval)
return retval;
@@ -537,14 +526,6 @@ static const struct bin_attribute pch_bin_attr = {
.write = pch_phub_bin_write,
};
-enum {
- PCH_EG20T,
- PCH_ML7213,
- PCH_ML7223M,
- PCH_ML7223N,
- PCH_ML7831,
-};
-
static int pch_phub_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 7686d777a27e..6b18ea914689 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -1419,18 +1419,18 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0),
.driver_data = (kernel_ulong_t)&default_data,
},
- { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),},
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8) },
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A),
.driver_data = (kernel_ulong_t)&default_data,
},
{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654),
- .driver_data = (kernel_ulong_t)&am654_data
+ .driver_data = (kernel_ulong_t)&am654_data,
},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1),},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),},
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1) },
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0),
.driver_data = (kernel_ulong_t)&default_data,
},
@@ -1449,8 +1449,8 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ROCKCHIP, PCI_DEVICE_ID_ROCKCHIP_RK3588),
.driver_data = (kernel_ulong_t)&rk3588_data,
},
- { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP),},
- { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP),},
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP) },
{ }
};
MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index 34a5054a6b40..331cae539290 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -481,10 +481,10 @@ static int __maybe_unused phantom_resume(struct device *dev_d)
}
static struct pci_device_id phantom_pci_tbl[] = {
- { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050,
- .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050,
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
.class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
- { 0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c
index bedcda9b6ac5..724dfddf69e4 100644
--- a/drivers/misc/pvpanic/pvpanic-mmio.c
+++ b/drivers/misc/pvpanic/pvpanic-mmio.c
@@ -51,14 +51,14 @@ static int pvpanic_mmio_probe(struct platform_device *pdev)
}
static const struct of_device_id pvpanic_mmio_match[] = {
- { .compatible = "qemu,pvpanic-mmio", },
- {}
+ { .compatible = "qemu,pvpanic-mmio" },
+ { }
};
MODULE_DEVICE_TABLE(of, pvpanic_mmio_match);
static const struct acpi_device_id pvpanic_device_ids[] = {
- { "QEMU0001", 0 },
- { "", 0 }
+ { .id = "QEMU0001" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c
index b21598a18f6d..572c6148a297 100644
--- a/drivers/misc/pvpanic/pvpanic-pci.c
+++ b/drivers/misc/pvpanic/pvpanic-pci.c
@@ -35,8 +35,8 @@ static int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
}
static const struct pci_device_id pvpanic_pci_id_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)},
- {}
+ { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC) },
+ { }
};
MODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl);
diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c
index 3c57f7429147..b58bd34fc471 100644
--- a/drivers/misc/qcom-coincell.c
+++ b/drivers/misc/qcom-coincell.c
@@ -124,8 +124,8 @@ static int qcom_coincell_probe(struct platform_device *pdev)
}
static const struct of_device_id qcom_coincell_match_table[] = {
- { .compatible = "qcom,pm8941-coincell", },
- {}
+ { .compatible = "qcom,pm8941-coincell" },
+ { }
};
MODULE_DEVICE_TABLE(of, qcom_coincell_match_table);
diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c
index 81685e3f3296..207a676913a2 100644
--- a/drivers/misc/rp1/rp1_pci.c
+++ b/drivers/misc/rp1/rp1_pci.c
@@ -166,6 +166,9 @@ static void rp1_unregister_interrupts(struct pci_dev *pdev)
struct rp1_dev *rp1 = pci_get_drvdata(pdev);
int irq, i;
+ for (i = 0; i < RP1_INT_END; i++)
+ irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), NULL, NULL);
+
if (rp1->domain) {
for (i = 0; i < RP1_INT_END; i++) {
irq = irq_find_mapping(rp1->domain, i);
@@ -191,13 +194,13 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (!rp1_node) {
dev_err(dev, "Missing of_node for device\n");
err = -EINVAL;
- goto err_put_node;
+ goto err_out;
}
rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL);
if (!rp1) {
err = -ENOMEM;
- goto err_put_node;
+ goto err_out;
}
rp1->pdev = pdev;
@@ -206,21 +209,21 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_err(&pdev->dev,
"Not initialized - is the firmware running?\n");
err = -EINVAL;
- goto err_put_node;
+ goto err_out;
}
err = pcim_enable_device(pdev);
if (err < 0) {
err = dev_err_probe(&pdev->dev, err,
"Enabling PCI device has failed");
- goto err_put_node;
+ goto err_out;
}
rp1->bar1 = pcim_iomap(pdev, 1, 0);
if (!rp1->bar1) {
dev_err(&pdev->dev, "Cannot map PCI BAR\n");
err = -EIO;
- goto err_put_node;
+ goto err_out;
}
pci_set_master(pdev);
@@ -230,16 +233,16 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err < 0) {
err = dev_err_probe(&pdev->dev, err,
"Failed to allocate MSI-X vectors\n");
- goto err_put_node;
+ goto err_out;
} else if (err != RP1_INT_END) {
dev_err(&pdev->dev, "Cannot allocate enough interrupts\n");
err = -EINVAL;
- goto err_put_node;
+ goto err_out;
}
pci_set_drvdata(pdev, rp1);
- rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END,
- &rp1_domain_ops, rp1);
+ rp1->domain = irq_domain_create_linear(of_fwnode_handle(rp1_node), RP1_INT_END,
+ &rp1_domain_ops, rp1);
if (!rp1->domain) {
dev_err(&pdev->dev, "Error creating IRQ domain\n");
err = -ENOMEM;
@@ -267,15 +270,11 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_unregister_interrupts;
}
- of_node_put(rp1_node);
-
return 0;
err_unregister_interrupts:
rp1_unregister_interrupts(pdev);
-err_put_node:
- of_node_put(rp1_node);
-
+err_out:
return err;
}
@@ -288,7 +287,7 @@ static void rp1_remove(struct pci_dev *pdev)
}
static const struct pci_device_id dev_id_table[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), },
+ { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0) },
{ }
};
MODULE_DEVICE_TABLE(pci, dev_id_table);
diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile
deleted file mode 100644
index 8132116ec0f0..000000000000
--- a/drivers/misc/sgi-gru/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-ccflags-$(CONFIG_SGI_GRU_DEBUG) := -DDEBUG
-
-obj-$(CONFIG_SGI_GRU) := gru.o
-gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o grukdump.o
-
diff --git a/drivers/misc/sgi-gru/gru.h b/drivers/misc/sgi-gru/gru.h
deleted file mode 100644
index 6ae045037219..000000000000
--- a/drivers/misc/sgi-gru/gru.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser 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
- */
-
-#ifndef __GRU_H__
-#define __GRU_H__
-
-/*
- * GRU architectural definitions
- */
-#define GRU_CACHE_LINE_BYTES 64
-#define GRU_HANDLE_STRIDE 256
-#define GRU_CB_BASE 0
-#define GRU_DS_BASE 0x20000
-
-/*
- * Size used to map GRU GSeg
- */
-#if defined(CONFIG_X86_64)
-#define GRU_GSEG_PAGESIZE (256 * 1024UL) /* ZZZ 2MB ??? */
-#else
-#error "Unsupported architecture"
-#endif
-
-/*
- * Structure for obtaining GRU resource information
- */
-struct gru_chiplet_info {
- int node;
- int chiplet;
- int blade;
- int total_dsr_bytes;
- int total_cbr;
- int total_user_dsr_bytes;
- int total_user_cbr;
- int free_user_dsr_bytes;
- int free_user_cbr;
-};
-
-/*
- * Statictics kept for each context.
- */
-struct gru_gseg_statistics {
- unsigned long fmm_tlbmiss;
- unsigned long upm_tlbmiss;
- unsigned long tlbdropin;
- unsigned long context_stolen;
- unsigned long reserved[10];
-};
-
-/* Flags for GRU options on the gru_create_context() call */
-/* Select one of the follow 4 options to specify how TLB misses are handled */
-#define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */
-#define GRU_OPT_MISS_USER_POLL 0x0001 /* User will poll CB for faults */
-#define GRU_OPT_MISS_FMM_INTR 0x0002 /* Send interrupt to cpu to
- handle fault */
-#define GRU_OPT_MISS_FMM_POLL 0x0003 /* Use system polling thread */
-#define GRU_OPT_MISS_MASK 0x0003 /* Mask for TLB MISS option */
-
-
-
-#endif /* __GRU_H__ */
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h
deleted file mode 100644
index da5eb9edf9ec..000000000000
--- a/drivers/misc/sgi-gru/gru_instructions.h
+++ /dev/null
@@ -1,726 +0,0 @@
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser 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
- */
-
-#ifndef __GRU_INSTRUCTIONS_H__
-#define __GRU_INSTRUCTIONS_H__
-
-extern int gru_check_status_proc(void *cb);
-extern int gru_wait_proc(void *cb);
-extern void gru_wait_abort_proc(void *cb);
-
-
-
-/*
- * Architecture dependent functions
- */
-
-#if defined(CONFIG_X86_64)
-#include <asm/cacheflush.h>
-#define __flush_cache(p) clflush(p)
-#define gru_ordered_store_ulong(p, v) \
- do { \
- barrier(); \
- *(unsigned long *)p = v; \
- } while (0)
-#else
-#error "Unsupported architecture"
-#endif
-
-/*
- * Control block status and exception codes
- */
-#define CBS_IDLE 0
-#define CBS_EXCEPTION 1
-#define CBS_ACTIVE 2
-#define CBS_CALL_OS 3
-
-/* CB substatus bitmasks */
-#define CBSS_MSG_QUEUE_MASK 7
-#define CBSS_IMPLICIT_ABORT_ACTIVE_MASK 8
-
-/* CB substatus message queue values (low 3 bits of substatus) */
-#define CBSS_NO_ERROR 0
-#define CBSS_LB_OVERFLOWED 1
-#define CBSS_QLIMIT_REACHED 2
-#define CBSS_PAGE_OVERFLOW 3
-#define CBSS_AMO_NACKED 4
-#define CBSS_PUT_NACKED 5
-
-/*
- * Structure used to fetch exception detail for CBs that terminate with
- * CBS_EXCEPTION
- */
-struct control_block_extended_exc_detail {
- unsigned long cb;
- int opc;
- int ecause;
- int exopc;
- long exceptdet0;
- int exceptdet1;
- int cbrstate;
- int cbrexecstatus;
-};
-
-/*
- * Instruction formats
- */
-
-/*
- * Generic instruction format.
- * This definition has precise bit field definitions.
- */
-struct gru_instruction_bits {
- /* DW 0 - low */
- unsigned int icmd: 1;
- unsigned char ima: 3; /* CB_DelRep, unmapped mode */
- unsigned char reserved0: 4;
- unsigned int xtype: 3;
- unsigned int iaa0: 2;
- unsigned int iaa1: 2;
- unsigned char reserved1: 1;
- unsigned char opc: 8; /* opcode */
- unsigned char exopc: 8; /* extended opcode */
- /* DW 0 - high */
- unsigned int idef2: 22; /* TRi0 */
- unsigned char reserved2: 2;
- unsigned char istatus: 2;
- unsigned char isubstatus:4;
- unsigned char reserved3: 1;
- unsigned char tlb_fault_color: 1;
- /* DW 1 */
- unsigned long idef4; /* 42 bits: TRi1, BufSize */
- /* DW 2-6 */
- unsigned long idef1; /* BAddr0 */
- unsigned long idef5; /* Nelem */
- unsigned long idef6; /* Stride, Operand1 */
- unsigned long idef3; /* BAddr1, Value, Operand2 */
- unsigned long reserved4;
- /* DW 7 */
- unsigned long avalue; /* AValue */
-};
-
-/*
- * Generic instruction with friendlier names. This format is used
- * for inline instructions.
- */
-struct gru_instruction {
- /* DW 0 */
- union {
- unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */
- struct {
- unsigned int op32;
- unsigned int tri0;
- };
- };
- unsigned long tri1_bufsize; /* DW 1 */
- unsigned long baddr0; /* DW 2 */
- unsigned long nelem; /* DW 3 */
- unsigned long op1_stride; /* DW 4 */
- unsigned long op2_value_baddr1; /* DW 5 */
- unsigned long reserved0; /* DW 6 */
- unsigned long avalue; /* DW 7 */
-};
-
-/* Some shifts and masks for the low 64 bits of a GRU command */
-#define GRU_CB_ICMD_SHFT 0
-#define GRU_CB_ICMD_MASK 0x1
-#define GRU_CB_XTYPE_SHFT 8
-#define GRU_CB_XTYPE_MASK 0x7
-#define GRU_CB_IAA0_SHFT 11
-#define GRU_CB_IAA0_MASK 0x3
-#define GRU_CB_IAA1_SHFT 13
-#define GRU_CB_IAA1_MASK 0x3
-#define GRU_CB_IMA_SHFT 1
-#define GRU_CB_IMA_MASK 0x3
-#define GRU_CB_OPC_SHFT 16
-#define GRU_CB_OPC_MASK 0xff
-#define GRU_CB_EXOPC_SHFT 24
-#define GRU_CB_EXOPC_MASK 0xff
-#define GRU_IDEF2_SHFT 32
-#define GRU_IDEF2_MASK 0x3ffff
-#define GRU_ISTATUS_SHFT 56
-#define GRU_ISTATUS_MASK 0x3
-
-/* GRU instruction opcodes (opc field) */
-#define OP_NOP 0x00
-#define OP_BCOPY 0x01
-#define OP_VLOAD 0x02
-#define OP_IVLOAD 0x03
-#define OP_VSTORE 0x04
-#define OP_IVSTORE 0x05
-#define OP_VSET 0x06
-#define OP_IVSET 0x07
-#define OP_MESQ 0x08
-#define OP_GAMXR 0x09
-#define OP_GAMIR 0x0a
-#define OP_GAMIRR 0x0b
-#define OP_GAMER 0x0c
-#define OP_GAMERR 0x0d
-#define OP_BSTORE 0x0e
-#define OP_VFLUSH 0x0f
-
-
-/* Extended opcodes values (exopc field) */
-
-/* GAMIR - AMOs with implicit operands */
-#define EOP_IR_FETCH 0x01 /* Plain fetch of memory */
-#define EOP_IR_CLR 0x02 /* Fetch and clear */
-#define EOP_IR_INC 0x05 /* Fetch and increment */
-#define EOP_IR_DEC 0x07 /* Fetch and decrement */
-#define EOP_IR_QCHK1 0x0d /* Queue check, 64 byte msg */
-#define EOP_IR_QCHK2 0x0e /* Queue check, 128 byte msg */
-
-/* GAMIRR - Registered AMOs with implicit operands */
-#define EOP_IRR_FETCH 0x01 /* Registered fetch of memory */
-#define EOP_IRR_CLR 0x02 /* Registered fetch and clear */
-#define EOP_IRR_INC 0x05 /* Registered fetch and increment */
-#define EOP_IRR_DEC 0x07 /* Registered fetch and decrement */
-#define EOP_IRR_DECZ 0x0f /* Registered fetch and decrement, update on zero*/
-
-/* GAMER - AMOs with explicit operands */
-#define EOP_ER_SWAP 0x00 /* Exchange argument and memory */
-#define EOP_ER_OR 0x01 /* Logical OR with memory */
-#define EOP_ER_AND 0x02 /* Logical AND with memory */
-#define EOP_ER_XOR 0x03 /* Logical XOR with memory */
-#define EOP_ER_ADD 0x04 /* Add value to memory */
-#define EOP_ER_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/
-#define EOP_ER_CADD 0x0c /* Queue check, operand1*64 byte msg */
-
-/* GAMERR - Registered AMOs with explicit operands */
-#define EOP_ERR_SWAP 0x00 /* Exchange argument and memory */
-#define EOP_ERR_OR 0x01 /* Logical OR with memory */
-#define EOP_ERR_AND 0x02 /* Logical AND with memory */
-#define EOP_ERR_XOR 0x03 /* Logical XOR with memory */
-#define EOP_ERR_ADD 0x04 /* Add value to memory */
-#define EOP_ERR_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/
-#define EOP_ERR_EPOLL 0x09 /* Poll for equality */
-#define EOP_ERR_NPOLL 0x0a /* Poll for inequality */
-
-/* GAMXR - SGI Arithmetic unit */
-#define EOP_XR_CSWAP 0x0b /* Masked compare exchange */
-
-
-/* Transfer types (xtype field) */
-#define XTYPE_B 0x0 /* byte */
-#define XTYPE_S 0x1 /* short (2-byte) */
-#define XTYPE_W 0x2 /* word (4-byte) */
-#define XTYPE_DW 0x3 /* doubleword (8-byte) */
-#define XTYPE_CL 0x6 /* cacheline (64-byte) */
-
-
-/* Instruction access attributes (iaa0, iaa1 fields) */
-#define IAA_RAM 0x0 /* normal cached RAM access */
-#define IAA_NCRAM 0x2 /* noncoherent RAM access */
-#define IAA_MMIO 0x1 /* noncoherent memory-mapped I/O space */
-#define IAA_REGISTER 0x3 /* memory-mapped registers, etc. */
-
-
-/* Instruction mode attributes (ima field) */
-#define IMA_MAPPED 0x0 /* Virtual mode */
-#define IMA_CB_DELAY 0x1 /* hold read responses until status changes */
-#define IMA_UNMAPPED 0x2 /* bypass the TLBs (OS only) */
-#define IMA_INTERRUPT 0x4 /* Interrupt when instruction completes */
-
-/* CBE ecause bits */
-#define CBE_CAUSE_RI (1 << 0)
-#define CBE_CAUSE_INVALID_INSTRUCTION (1 << 1)
-#define CBE_CAUSE_UNMAPPED_MODE_FORBIDDEN (1 << 2)
-#define CBE_CAUSE_PE_CHECK_DATA_ERROR (1 << 3)
-#define CBE_CAUSE_IAA_GAA_MISMATCH (1 << 4)
-#define CBE_CAUSE_DATA_SEGMENT_LIMIT_EXCEPTION (1 << 5)
-#define CBE_CAUSE_OS_FATAL_TLB_FAULT (1 << 6)
-#define CBE_CAUSE_EXECUTION_HW_ERROR (1 << 7)
-#define CBE_CAUSE_TLBHW_ERROR (1 << 8)
-#define CBE_CAUSE_RA_REQUEST_TIMEOUT (1 << 9)
-#define CBE_CAUSE_HA_REQUEST_TIMEOUT (1 << 10)
-#define CBE_CAUSE_RA_RESPONSE_FATAL (1 << 11)
-#define CBE_CAUSE_RA_RESPONSE_NON_FATAL (1 << 12)
-#define CBE_CAUSE_HA_RESPONSE_FATAL (1 << 13)
-#define CBE_CAUSE_HA_RESPONSE_NON_FATAL (1 << 14)
-#define CBE_CAUSE_ADDRESS_SPACE_DECODE_ERROR (1 << 15)
-#define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16)
-#define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17)
-#define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18)
-#define CBE_CAUSE_FORCED_ERROR (1 << 19)
-
-/* CBE cbrexecstatus bits */
-#define CBR_EXS_ABORT_OCC_BIT 0
-#define CBR_EXS_INT_OCC_BIT 1
-#define CBR_EXS_PENDING_BIT 2
-#define CBR_EXS_QUEUED_BIT 3
-#define CBR_EXS_TLB_INVAL_BIT 4
-#define CBR_EXS_EXCEPTION_BIT 5
-#define CBR_EXS_CB_INT_PENDING_BIT 6
-
-#define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT)
-#define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT)
-#define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT)
-#define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT)
-#define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT)
-#define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT)
-#define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT)
-
-/*
- * Exceptions are retried for the following cases. If any OTHER bits are set
- * in ecause, the exception is not retryable.
- */
-#define EXCEPTION_RETRY_BITS (CBE_CAUSE_EXECUTION_HW_ERROR | \
- CBE_CAUSE_TLBHW_ERROR | \
- CBE_CAUSE_RA_REQUEST_TIMEOUT | \
- CBE_CAUSE_RA_RESPONSE_NON_FATAL | \
- CBE_CAUSE_HA_RESPONSE_NON_FATAL | \
- CBE_CAUSE_RA_RESPONSE_DATA_ERROR | \
- CBE_CAUSE_HA_RESPONSE_DATA_ERROR \
- )
-
-/* Message queue head structure */
-union gru_mesqhead {
- unsigned long val;
- struct {
- unsigned int head;
- unsigned int limit;
- };
-};
-
-
-/* Generate the low word of a GRU instruction */
-static inline unsigned long
-__opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype,
- unsigned char iaa0, unsigned char iaa1,
- unsigned long idef2, unsigned char ima)
-{
- return (1 << GRU_CB_ICMD_SHFT) |
- ((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) |
- (idef2<< GRU_IDEF2_SHFT) |
- (iaa0 << GRU_CB_IAA0_SHFT) |
- (iaa1 << GRU_CB_IAA1_SHFT) |
- (ima << GRU_CB_IMA_SHFT) |
- (xtype << GRU_CB_XTYPE_SHFT) |
- (opcode << GRU_CB_OPC_SHFT) |
- (exopc << GRU_CB_EXOPC_SHFT);
-}
-
-/*
- * Architecture specific intrinsics
- */
-static inline void gru_flush_cache(void *p)
-{
- __flush_cache(p);
-}
-
-/*
- * Store the lower 64 bits of the command including the "start" bit. Then
- * start the instruction executing.
- */
-static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64)
-{
- gru_ordered_store_ulong(ins, op64);
- mb();
- gru_flush_cache(ins);
-}
-
-
-/* Convert "hints" to IMA */
-#define CB_IMA(h) ((h) | IMA_UNMAPPED)
-
-/* Convert data segment cache line index into TRI0 / TRI1 value */
-#define GRU_DINDEX(i) ((i) * GRU_CACHE_LINE_BYTES)
-
-/* Inline functions for GRU instructions.
- * Note:
- * - nelem and stride are in elements
- * - tri0/tri1 is in bytes for the beginning of the data segment.
- */
-static inline void gru_vload_phys(void *cb, unsigned long gpa,
- unsigned int tri0, int iaa, unsigned long hints)
-{
- struct gru_instruction *ins = (struct gru_instruction *)cb;
-
- ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
- ins->nelem = 1;
- ins->op1_stride = 1;
- gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0,
- (unsigned long)tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vstore_phys(void *cb, unsigned long gpa,
- unsigned int tri0, int iaa, unsigned long hints)
-{
- struct gru_instruction *ins = (struct gru_instruction *)cb;
-
- ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
- ins->nelem = 1;
- ins->op1_stride = 1;
- gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0,
- (unsigned long)tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vload(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned char xtype, unsigned long nelem,
- unsigned long stride, unsigned long hints)
-{
- struct gru_instruction *ins = (struct gru_instruction *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->op1_stride = stride;
- gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0,
- (unsigned long)tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vstore(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned char xtype, unsigned long nelem,
- unsigned long stride, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->op1_stride = stride;
- gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_ivload(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned int tri1, unsigned char xtype,
- unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_ivstore(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned int tri1,
- unsigned char xtype, unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vset(void *cb, unsigned long mem_addr,
- unsigned long value, unsigned char xtype, unsigned long nelem,
- unsigned long stride, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->op2_value_baddr1 = value;
- ins->nelem = nelem;
- ins->op1_stride = stride;
- gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_ivset(void *cb, unsigned long mem_addr,
- unsigned int tri1, unsigned long value, unsigned char xtype,
- unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->op2_value_baddr1 = value;
- ins->nelem = nelem;
- ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_vflush(void *cb, unsigned long mem_addr,
- unsigned long nelem, unsigned char xtype, unsigned long stride,
- unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->op1_stride = stride;
- ins->nelem = nelem;
- gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_nop(void *cb, int hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints)));
-}
-
-
-static inline void gru_bcopy(void *cb, const unsigned long src,
- unsigned long dest,
- unsigned int tri0, unsigned int xtype, unsigned long nelem,
- unsigned int bufsize, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op2_value_baddr1 = (long)dest;
- ins->nelem = nelem;
- ins->tri1_bufsize = bufsize;
- gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM,
- IAA_RAM, tri0, CB_IMA(hints)));
-}
-
-static inline void gru_bstore(void *cb, const unsigned long src,
- unsigned long dest, unsigned int tri0, unsigned int xtype,
- unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op2_value_baddr1 = (long)dest;
- ins->nelem = nelem;
- gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_gamir(void *cb, int exopc, unsigned long src,
- unsigned int xtype, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamirr(void *cb, int exopc, unsigned long src,
- unsigned int xtype, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamer(void *cb, int exopc, unsigned long src,
- unsigned int xtype,
- unsigned long operand1, unsigned long operand2,
- unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op1_stride = operand1;
- ins->op2_value_baddr1 = operand2;
- gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamerr(void *cb, int exopc, unsigned long src,
- unsigned int xtype, unsigned long operand1,
- unsigned long operand2, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op1_stride = operand1;
- ins->op2_value_baddr1 = operand2;
- gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamxr(void *cb, unsigned long src,
- unsigned int tri0, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->nelem = 4;
- gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW,
- IAA_RAM, 0, 0, CB_IMA(hints)));
-}
-
-static inline void gru_mesq(void *cb, unsigned long queue,
- unsigned long tri0, unsigned long nelem,
- unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)queue;
- ins->nelem = nelem;
- gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline unsigned long gru_get_amo_value(void *cb)
-{
- struct gru_instruction *ins = (void *)cb;
-
- return ins->avalue;
-}
-
-static inline int gru_get_amo_value_head(void *cb)
-{
- struct gru_instruction *ins = (void *)cb;
-
- return ins->avalue & 0xffffffff;
-}
-
-static inline int gru_get_amo_value_limit(void *cb)
-{
- struct gru_instruction *ins = (void *)cb;
-
- return ins->avalue >> 32;
-}
-
-static inline union gru_mesqhead gru_mesq_head(int head, int limit)
-{
- union gru_mesqhead mqh;
-
- mqh.head = head;
- mqh.limit = limit;
- return mqh;
-}
-
-/*
- * Get struct control_block_extended_exc_detail for CB.
- */
-extern int gru_get_cb_exception_detail(void *cb,
- struct control_block_extended_exc_detail *excdet);
-
-#define GRU_EXC_STR_SIZE 256
-
-
-/*
- * Control block definition for checking status
- */
-struct gru_control_block_status {
- unsigned int icmd :1;
- unsigned int ima :3;
- unsigned int reserved0 :4;
- unsigned int unused1 :24;
- unsigned int unused2 :24;
- unsigned int istatus :2;
- unsigned int isubstatus :4;
- unsigned int unused3 :2;
-};
-
-/* Get CB status */
-static inline int gru_get_cb_status(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
-
- return cbs->istatus;
-}
-
-/* Get CB message queue substatus */
-static inline int gru_get_cb_message_queue_substatus(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
-
- return cbs->isubstatus & CBSS_MSG_QUEUE_MASK;
-}
-
-/* Get CB substatus */
-static inline int gru_get_cb_substatus(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
-
- return cbs->isubstatus;
-}
-
-/*
- * User interface to check an instruction status. UPM and exceptions
- * are handled automatically. However, this function does NOT wait
- * for an active instruction to complete.
- *
- */
-static inline int gru_check_status(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
- int ret;
-
- ret = cbs->istatus;
- if (ret != CBS_ACTIVE)
- ret = gru_check_status_proc(cb);
- return ret;
-}
-
-/*
- * User interface (via inline function) to wait for an instruction
- * to complete. Completion status (IDLE or EXCEPTION is returned
- * to the user. Exception due to hardware errors are automatically
- * retried before returning an exception.
- *
- */
-static inline int gru_wait(void *cb)
-{
- return gru_wait_proc(cb);
-}
-
-/*
- * Wait for CB to complete. Aborts program if error. (Note: error does NOT
- * mean TLB mis - only fatal errors such as memory parity error or user
- * bugs will cause termination.
- */
-static inline void gru_wait_abort(void *cb)
-{
- gru_wait_abort_proc(cb);
-}
-
-/*
- * Get a pointer to the start of a gseg
- * p - Any valid pointer within the gseg
- */
-static inline void *gru_get_gseg_pointer (void *p)
-{
- return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1));
-}
-
-/*
- * Get a pointer to a control block
- * gseg - GSeg address returned from gru_get_thread_gru_segment()
- * index - index of desired CB
- */
-static inline void *gru_get_cb_pointer(void *gseg,
- int index)
-{
- return gseg + GRU_CB_BASE + index * GRU_HANDLE_STRIDE;
-}
-
-/*
- * Get a pointer to a cacheline in the data segment portion of a GSeg
- * gseg - GSeg address returned from gru_get_thread_gru_segment()
- * index - index of desired cache line
- */
-static inline void *gru_get_data_pointer(void *gseg, int index)
-{
- return gseg + GRU_DS_BASE + index * GRU_CACHE_LINE_BYTES;
-}
-
-/*
- * Convert a vaddr into the tri index within the GSEG
- * vaddr - virtual address of within gseg
- */
-static inline int gru_get_tri(void *vaddr)
-{
- return ((unsigned long)vaddr & (GRU_GSEG_PAGESIZE - 1)) - GRU_DS_BASE;
-}
-#endif /* __GRU_INSTRUCTIONS_H__ */
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
deleted file mode 100644
index 3557d78ee47a..000000000000
--- a/drivers/misc/sgi-gru/grufault.c
+++ /dev/null
@@ -1,903 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * FAULT HANDLER FOR GRU DETECTED TLB MISSES
- *
- * This file contains code that handles TLB misses within the GRU.
- * These misses are reported either via interrupts or user polling of
- * the user CB.
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/security.h>
-#include <linux/sync_core.h>
-#include <linux/prefetch.h>
-#include "gru.h"
-#include "grutables.h"
-#include "grulib.h"
-#include "gru_instructions.h"
-#include <asm/uv/uv_hub.h>
-
-/* Return codes for vtop functions */
-#define VTOP_SUCCESS 0
-#define VTOP_INVALID -1
-#define VTOP_RETRY -2
-
-
-/*
- * Test if a physical address is a valid GRU GSEG address
- */
-static inline int is_gru_paddr(unsigned long paddr)
-{
- return paddr >= gru_start_paddr && paddr < gru_end_paddr;
-}
-
-/*
- * Find the vma of a GRU segment. Caller must hold mmap_lock.
- */
-struct vm_area_struct *gru_find_vma(unsigned long vaddr)
-{
- struct vm_area_struct *vma;
-
- vma = vma_lookup(current->mm, vaddr);
- if (vma && vma->vm_ops == &gru_vm_ops)
- return vma;
- return NULL;
-}
-
-/*
- * Find and lock the gts that contains the specified user vaddr.
- *
- * Returns:
- * - *gts with the mmap_lock locked for read and the GTS locked.
- * - NULL if vaddr invalid OR is not a valid GSEG vaddr.
- */
-
-static struct gru_thread_state *gru_find_lock_gts(unsigned long vaddr)
-{
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- struct gru_thread_state *gts = NULL;
-
- mmap_read_lock(mm);
- vma = gru_find_vma(vaddr);
- if (vma)
- gts = gru_find_thread_state(vma, TSID(vaddr, vma));
- if (gts)
- mutex_lock(&gts->ts_ctxlock);
- else
- mmap_read_unlock(mm);
- return gts;
-}
-
-static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr)
-{
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- struct gru_thread_state *gts = ERR_PTR(-EINVAL);
-
- mmap_write_lock(mm);
- vma = gru_find_vma(vaddr);
- if (!vma)
- goto err;
-
- gts = gru_alloc_thread_state(vma, TSID(vaddr, vma));
- if (IS_ERR(gts))
- goto err;
- mutex_lock(&gts->ts_ctxlock);
- mmap_write_downgrade(mm);
- return gts;
-
-err:
- mmap_write_unlock(mm);
- return gts;
-}
-
-/*
- * Unlock a GTS that was previously locked with gru_find_lock_gts().
- */
-static void gru_unlock_gts(struct gru_thread_state *gts)
-{
- mutex_unlock(&gts->ts_ctxlock);
- mmap_read_unlock(current->mm);
-}
-
-/*
- * Set a CB.istatus to active using a user virtual address. This must be done
- * just prior to a TFH RESTART. The new cb.istatus is an in-cache status ONLY.
- * If the line is evicted, the status may be lost. The in-cache update
- * is necessary to prevent the user from seeing a stale cb.istatus that will
- * change as soon as the TFH restart is complete. Races may cause an
- * occasional failure to clear the cb.istatus, but that is ok.
- */
-static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk)
-{
- if (cbk) {
- cbk->istatus = CBS_ACTIVE;
- }
-}
-
-/*
- * Read & clear a TFM
- *
- * The GRU has an array of fault maps. A map is private to a cpu
- * Only one cpu will be accessing a cpu's fault map.
- *
- * This function scans the cpu-private fault map & clears all bits that
- * are set. The function returns a bitmap that indicates the bits that
- * were cleared. Note that sense the maps may be updated asynchronously by
- * the GRU, atomic operations must be used to clear bits.
- */
-static void get_clear_fault_map(struct gru_state *gru,
- struct gru_tlb_fault_map *imap,
- struct gru_tlb_fault_map *dmap)
-{
- unsigned long i, k;
- struct gru_tlb_fault_map *tfm;
-
- tfm = get_tfm_for_cpu(gru, gru_cpu_fault_map_id());
- prefetchw(tfm); /* Helps on hardware, required for emulator */
- for (i = 0; i < BITS_TO_LONGS(GRU_NUM_CBE); i++) {
- k = tfm->fault_bits[i];
- if (k)
- k = xchg(&tfm->fault_bits[i], 0UL);
- imap->fault_bits[i] = k;
- k = tfm->done_bits[i];
- if (k)
- k = xchg(&tfm->done_bits[i], 0UL);
- dmap->fault_bits[i] = k;
- }
-
- /*
- * Not functionally required but helps performance. (Required
- * on emulator)
- */
- gru_flush_cache(tfm);
-}
-
-/*
- * Atomic (interrupt context) & non-atomic (user context) functions to
- * convert a vaddr into a physical address. The size of the page
- * is returned in pageshift.
- * returns:
- * 0 - successful
- * < 0 - error code
- * 1 - (atomic only) try again in non-atomic context
- */
-static int non_atomic_pte_lookup(struct vm_area_struct *vma,
- unsigned long vaddr, int write,
- unsigned long *paddr, int *pageshift)
-{
- struct page *page;
-
-#ifdef CONFIG_HUGETLB_PAGE
- *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT;
-#else
- *pageshift = PAGE_SHIFT;
-#endif
- if (get_user_pages(vaddr, 1, write ? FOLL_WRITE : 0, &page) <= 0)
- return -EFAULT;
- *paddr = page_to_phys(page);
- put_page(page);
- return 0;
-}
-
-/*
- * atomic_pte_lookup
- *
- * Convert a user virtual address to a physical address
- * Only supports Intel large pages (2MB only) on x86_64.
- * ZZZ - hugepage support is incomplete
- *
- * NOTE: mmap_lock is already held on entry to this function. This
- * guarantees existence of the page tables.
- */
-static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr,
- int write, unsigned long *paddr, int *pageshift)
-{
- pgd_t *pgdp;
- p4d_t *p4dp;
- pud_t *pudp;
- pmd_t *pmdp;
- pte_t pte;
-
- pgdp = pgd_offset(vma->vm_mm, vaddr);
- if (unlikely(pgd_none(*pgdp)))
- goto err;
-
- p4dp = p4d_offset(pgdp, vaddr);
- if (unlikely(p4d_none(*p4dp)))
- goto err;
-
- pudp = pud_offset(p4dp, vaddr);
- if (unlikely(pud_none(*pudp)))
- goto err;
-
- pmdp = pmd_offset(pudp, vaddr);
- if (unlikely(pmd_none(*pmdp)))
- goto err;
-#ifdef CONFIG_X86_64
- if (unlikely(pmd_leaf(*pmdp)))
- pte = ptep_get((pte_t *)pmdp);
- else
-#endif
- pte = *pte_offset_kernel(pmdp, vaddr);
-
- if (unlikely(!pte_present(pte) ||
- (write && (!pte_write(pte) || !pte_dirty(pte)))))
- return 1;
-
- *paddr = pte_pfn(pte) << PAGE_SHIFT;
-#ifdef CONFIG_HUGETLB_PAGE
- *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT;
-#else
- *pageshift = PAGE_SHIFT;
-#endif
- return 0;
-
-err:
- return 1;
-}
-
-static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
- int write, int atomic, unsigned long *gpa, int *pageshift)
-{
- struct mm_struct *mm = gts->ts_mm;
- struct vm_area_struct *vma;
- unsigned long paddr;
- int ret, ps;
-
- vma = find_vma(mm, vaddr);
- if (!vma)
- goto inval;
-
- /*
- * Atomic lookup is faster & usually works even if called in non-atomic
- * context.
- */
- rmb(); /* Must/check ms_range_active before loading PTEs */
- ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &ps);
- if (ret) {
- if (atomic)
- goto upm;
- if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps))
- goto inval;
- }
- if (is_gru_paddr(paddr))
- goto inval;
- paddr = paddr & ~((1UL << ps) - 1);
- *gpa = uv_soc_phys_ram_to_gpa(paddr);
- *pageshift = ps;
- return VTOP_SUCCESS;
-
-inval:
- return VTOP_INVALID;
-upm:
- return VTOP_RETRY;
-}
-
-
-/*
- * Flush a CBE from cache. The CBE is clean in the cache. Dirty the
- * CBE cacheline so that the line will be written back to home agent.
- * Otherwise the line may be silently dropped. This has no impact
- * except on performance.
- */
-static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe)
-{
- if (unlikely(cbe)) {
- cbe->cbrexecstatus = 0; /* make CL dirty */
- gru_flush_cache(cbe);
- }
-}
-
-/*
- * Preload the TLB with entries that may be required. Currently, preloading
- * is implemented only for BCOPY. Preload <tlb_preload_count> pages OR to
- * the end of the bcopy tranfer, whichever is smaller.
- */
-static void gru_preload_tlb(struct gru_state *gru,
- struct gru_thread_state *gts, int atomic,
- unsigned long fault_vaddr, int asid, int write,
- unsigned char tlb_preload_count,
- struct gru_tlb_fault_handle *tfh,
- struct gru_control_block_extended *cbe)
-{
- unsigned long vaddr = 0, gpa;
- int ret, pageshift;
-
- if (cbe->opccpy != OP_BCOPY)
- return;
-
- if (fault_vaddr == cbe->cbe_baddr0)
- vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1;
- else if (fault_vaddr == cbe->cbe_baddr1)
- vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1;
-
- fault_vaddr &= PAGE_MASK;
- vaddr &= PAGE_MASK;
- vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE);
-
- while (vaddr > fault_vaddr) {
- ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
- if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write,
- GRU_PAGESIZE(pageshift)))
- return;
- gru_dbg(grudev,
- "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n",
- atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh,
- vaddr, asid, write, pageshift, gpa);
- vaddr -= PAGE_SIZE;
- STAT(tlb_preload_page);
- }
-}
-
-/*
- * Drop a TLB entry into the GRU. The fault is described by info in an TFH.
- * Input:
- * cb Address of user CBR. Null if not running in user context
- * Return:
- * 0 = dropin, exception, or switch to UPM successful
- * 1 = range invalidate active
- * < 0 = error code
- *
- */
-static int gru_try_dropin(struct gru_state *gru,
- struct gru_thread_state *gts,
- struct gru_tlb_fault_handle *tfh,
- struct gru_instruction_bits *cbk)
-{
- struct gru_control_block_extended *cbe = NULL;
- unsigned char tlb_preload_count = gts->ts_tlb_preload_count;
- int pageshift = 0, asid, write, ret, atomic = !cbk, indexway;
- unsigned long gpa = 0, vaddr = 0;
-
- /*
- * NOTE: The GRU contains magic hardware that eliminates races between
- * TLB invalidates and TLB dropins. If an invalidate occurs
- * in the window between reading the TFH and the subsequent TLB dropin,
- * the dropin is ignored. This eliminates the need for additional locks.
- */
-
- /*
- * Prefetch the CBE if doing TLB preloading
- */
- if (unlikely(tlb_preload_count)) {
- cbe = gru_tfh_to_cbe(tfh);
- prefetchw(cbe);
- }
-
- /*
- * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call.
- * Might be a hardware race OR a stupid user. Ignore FMM because FMM
- * is a transient state.
- */
- if (tfh->status != TFHSTATUS_EXCEPTION) {
- gru_flush_cache(tfh);
- sync_core();
- if (tfh->status != TFHSTATUS_EXCEPTION)
- goto failnoexception;
- STAT(tfh_stale_on_fault);
- }
- if (tfh->state == TFHSTATE_IDLE)
- goto failidle;
- if (tfh->state == TFHSTATE_MISS_FMM && cbk)
- goto failfmm;
-
- write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0;
- vaddr = tfh->missvaddr;
- asid = tfh->missasid;
- indexway = tfh->indexway;
- if (asid == 0)
- goto failnoasid;
-
- rmb(); /* TFH must be cache resident before reading ms_range_active */
-
- /*
- * TFH is cache resident - at least briefly. Fail the dropin
- * if a range invalidate is active.
- */
- if (atomic_read(&gts->ts_gms->ms_range_active))
- goto failactive;
-
- ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
- if (ret == VTOP_INVALID)
- goto failinval;
- if (ret == VTOP_RETRY)
- goto failupm;
-
- if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) {
- gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift);
- if (atomic || !gru_update_cch(gts)) {
- gts->ts_force_cch_reload = 1;
- goto failupm;
- }
- }
-
- if (unlikely(cbe) && pageshift == PAGE_SHIFT) {
- gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe);
- gru_flush_cache_cbe(cbe);
- }
-
- gru_cb_set_istatus_active(cbk);
- gts->ustats.tlbdropin++;
- tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write,
- GRU_PAGESIZE(pageshift));
- gru_dbg(grudev,
- "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x,"
- " rw %d, ps %d, gpa 0x%lx\n",
- atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid,
- indexway, write, pageshift, gpa);
- STAT(tlb_dropin);
- return 0;
-
-failnoasid:
- /* No asid (delayed unload). */
- STAT(tlb_dropin_fail_no_asid);
- gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- if (!cbk)
- tfh_user_polling_mode(tfh);
- else
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- return -EAGAIN;
-
-failupm:
- /* Atomic failure switch CBR to UPM */
- tfh_user_polling_mode(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_upm);
- gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- return 1;
-
-failfmm:
- /* FMM state on UPM call */
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_fmm);
- gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state);
- return 0;
-
-failnoexception:
- /* TFH status did not show exception pending */
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- if (cbk)
- gru_flush_cache(cbk);
- STAT(tlb_dropin_fail_no_exception);
- gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n",
- tfh, tfh->status, tfh->state);
- return 0;
-
-failidle:
- /* TFH state was idle - no miss pending */
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- if (cbk)
- gru_flush_cache(cbk);
- STAT(tlb_dropin_fail_idle);
- gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state);
- return 0;
-
-failinval:
- /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */
- tfh_exception(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_invalid);
- gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- return -EFAULT;
-
-failactive:
- /* Range invalidate active. Switch to UPM iff atomic */
- if (!cbk)
- tfh_user_polling_mode(tfh);
- else
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_range_active);
- gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n",
- tfh, vaddr);
- return 1;
-}
-
-/*
- * Process an external interrupt from the GRU. This interrupt is
- * caused by a TLB miss.
- * Note that this is the interrupt handler that is registered with linux
- * interrupt handlers.
- */
-static irqreturn_t gru_intr(int chiplet, int blade)
-{
- struct gru_state *gru;
- struct gru_tlb_fault_map imap, dmap;
- struct gru_thread_state *gts;
- struct gru_tlb_fault_handle *tfh = NULL;
- struct completion *cmp;
- int cbrnum, ctxnum;
-
- STAT(intr);
-
- gru = &gru_base[blade]->bs_grus[chiplet];
- if (!gru) {
- dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n",
- raw_smp_processor_id(), chiplet);
- return IRQ_NONE;
- }
- get_clear_fault_map(gru, &imap, &dmap);
- gru_dbg(grudev,
- "cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n",
- smp_processor_id(), chiplet, gru->gs_gid,
- imap.fault_bits[0], imap.fault_bits[1],
- dmap.fault_bits[0], dmap.fault_bits[1]);
-
- for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) {
- STAT(intr_cbr);
- cmp = gru->gs_blade->bs_async_wq;
- if (cmp)
- complete(cmp);
- gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n",
- gru->gs_gid, cbrnum, cmp ? cmp->done : -1);
- }
-
- for_each_cbr_in_tfm(cbrnum, imap.fault_bits) {
- STAT(intr_tfh);
- tfh = get_tfh_by_index(gru, cbrnum);
- prefetchw(tfh); /* Helps on hdw, required for emulator */
-
- /*
- * When hardware sets a bit in the faultmap, it implicitly
- * locks the GRU context so that it cannot be unloaded.
- * The gts cannot change until a TFH start/writestart command
- * is issued.
- */
- ctxnum = tfh->ctxnum;
- gts = gru->gs_gts[ctxnum];
-
- /* Spurious interrupts can cause this. Ignore. */
- if (!gts) {
- STAT(intr_spurious);
- continue;
- }
-
- /*
- * This is running in interrupt context. Trylock the mmap_lock.
- * If it fails, retry the fault in user context.
- */
- gts->ustats.fmm_tlbmiss++;
- if (!gts->ts_force_cch_reload &&
- mmap_read_trylock(gts->ts_mm)) {
- gru_try_dropin(gru, gts, tfh, NULL);
- mmap_read_unlock(gts->ts_mm);
- } else {
- tfh_user_polling_mode(tfh);
- STAT(intr_mm_lock_failed);
- }
- }
- return IRQ_HANDLED;
-}
-
-irqreturn_t gru0_intr(int irq, void *dev_id)
-{
- return gru_intr(0, uv_numa_blade_id());
-}
-
-irqreturn_t gru1_intr(int irq, void *dev_id)
-{
- return gru_intr(1, uv_numa_blade_id());
-}
-
-irqreturn_t gru_intr_mblade(int irq, void *dev_id)
-{
- int blade;
-
- for_each_possible_blade(blade) {
- if (uv_blade_nr_possible_cpus(blade))
- continue;
- gru_intr(0, blade);
- gru_intr(1, blade);
- }
- return IRQ_HANDLED;
-}
-
-
-static int gru_user_dropin(struct gru_thread_state *gts,
- struct gru_tlb_fault_handle *tfh,
- void *cb)
-{
- struct gru_mm_struct *gms = gts->ts_gms;
- int ret;
-
- gts->ustats.upm_tlbmiss++;
- while (1) {
- wait_event(gms->ms_wait_queue,
- atomic_read(&gms->ms_range_active) == 0);
- prefetchw(tfh); /* Helps on hdw, required for emulator */
- ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb);
- if (ret <= 0)
- return ret;
- STAT(call_os_wait_queue);
- }
-}
-
-/*
- * This interface is called as a result of a user detecting a "call OS" bit
- * in a user CB. Normally means that a TLB fault has occurred.
- * cb - user virtual address of the CB
- */
-int gru_handle_user_call_os(unsigned long cb)
-{
- struct gru_tlb_fault_handle *tfh;
- struct gru_thread_state *gts;
- void *cbk;
- int ucbnum, cbrnum, ret = -EINVAL;
-
- STAT(call_os);
-
- /* sanity check the cb pointer */
- ucbnum = get_cb_number((void *)cb);
- if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB)
- return -EINVAL;
-
-again:
- gts = gru_find_lock_gts(cb);
- if (!gts)
- return -EINVAL;
- gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
-
- if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE)
- goto exit;
-
- if (gru_check_context_placement(gts)) {
- gru_unlock_gts(gts);
- gru_unload_context(gts, 1);
- goto again;
- }
-
- /*
- * CCH may contain stale data if ts_force_cch_reload is set.
- */
- if (gts->ts_gru && gts->ts_force_cch_reload) {
- gts->ts_force_cch_reload = 0;
- gru_update_cch(gts);
- }
-
- ret = -EAGAIN;
- cbrnum = thread_cbr_number(gts, ucbnum);
- if (gts->ts_gru) {
- tfh = get_tfh_by_index(gts->ts_gru, cbrnum);
- cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr,
- gts->ts_ctxnum, ucbnum);
- ret = gru_user_dropin(gts, tfh, cbk);
- }
-exit:
- gru_unlock_gts(gts);
- return ret;
-}
-
-/*
- * Fetch the exception detail information for a CB that terminated with
- * an exception.
- */
-int gru_get_exception_detail(unsigned long arg)
-{
- struct control_block_extended_exc_detail excdet;
- struct gru_control_block_extended *cbe;
- struct gru_thread_state *gts;
- int ucbnum, cbrnum, ret;
-
- STAT(user_exception);
- if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet)))
- return -EFAULT;
-
- gts = gru_find_lock_gts(excdet.cb);
- if (!gts)
- return -EINVAL;
-
- gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
- ucbnum = get_cb_number((void *)excdet.cb);
- if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) {
- ret = -EINVAL;
- } else if (gts->ts_gru) {
- cbrnum = thread_cbr_number(gts, ucbnum);
- cbe = get_cbe_by_index(gts->ts_gru, cbrnum);
- gru_flush_cache(cbe); /* CBE not coherent */
- sync_core(); /* make sure we are have current data */
- excdet.opc = cbe->opccpy;
- excdet.exopc = cbe->exopccpy;
- excdet.ecause = cbe->ecause;
- excdet.exceptdet0 = cbe->idef1upd;
- excdet.exceptdet1 = cbe->idef3upd;
- excdet.cbrstate = cbe->cbrstate;
- excdet.cbrexecstatus = cbe->cbrexecstatus;
- gru_flush_cache_cbe(cbe);
- ret = 0;
- } else {
- ret = -EAGAIN;
- }
- gru_unlock_gts(gts);
-
- gru_dbg(grudev,
- "cb 0x%lx, op %d, exopc %d, cbrstate %d, cbrexecstatus 0x%x, ecause 0x%x, "
- "exdet0 0x%lx, exdet1 0x%x\n",
- excdet.cb, excdet.opc, excdet.exopc, excdet.cbrstate, excdet.cbrexecstatus,
- excdet.ecause, excdet.exceptdet0, excdet.exceptdet1);
- if (!ret && copy_to_user((void __user *)arg, &excdet, sizeof(excdet)))
- ret = -EFAULT;
- return ret;
-}
-
-/*
- * User request to unload a context. Content is saved for possible reload.
- */
-static int gru_unload_all_contexts(void)
-{
- struct gru_thread_state *gts;
- struct gru_state *gru;
- int gid, ctxnum;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- foreach_gid(gid) {
- gru = GID_TO_GRU(gid);
- spin_lock(&gru->gs_lock);
- for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
- gts = gru->gs_gts[ctxnum];
- if (gts && mutex_trylock(&gts->ts_ctxlock)) {
- spin_unlock(&gru->gs_lock);
- gru_unload_context(gts, 1);
- mutex_unlock(&gts->ts_ctxlock);
- spin_lock(&gru->gs_lock);
- }
- }
- spin_unlock(&gru->gs_lock);
- }
- return 0;
-}
-
-int gru_user_unload_context(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_unload_context_req req;
-
- STAT(user_unload_context);
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- gru_dbg(grudev, "gseg 0x%lx\n", req.gseg);
-
- if (!req.gseg)
- return gru_unload_all_contexts();
-
- gts = gru_find_lock_gts(req.gseg);
- if (!gts)
- return -EINVAL;
-
- if (gts->ts_gru)
- gru_unload_context(gts, 1);
- gru_unlock_gts(gts);
-
- return 0;
-}
-
-/*
- * User request to flush a range of virtual addresses from the GRU TLB
- * (Mainly for testing).
- */
-int gru_user_flush_tlb(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_flush_tlb_req req;
- struct gru_mm_struct *gms;
-
- STAT(user_flush_tlb);
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- gru_dbg(grudev, "gseg 0x%lx, vaddr 0x%lx, len 0x%lx\n", req.gseg,
- req.vaddr, req.len);
-
- gts = gru_find_lock_gts(req.gseg);
- if (!gts)
- return -EINVAL;
-
- gms = gts->ts_gms;
- gru_unlock_gts(gts);
- gru_flush_tlb_range(gms, req.vaddr, req.len);
-
- return 0;
-}
-
-/*
- * Fetch GSEG statisticss
- */
-long gru_get_gseg_statistics(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_get_gseg_statistics_req req;
-
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- /*
- * The library creates arrays of contexts for threaded programs.
- * If no gts exists in the array, the context has never been used & all
- * statistics are implicitly 0.
- */
- gts = gru_find_lock_gts(req.gseg);
- if (gts) {
- memcpy(&req.stats, &gts->ustats, sizeof(gts->ustats));
- gru_unlock_gts(gts);
- } else {
- memset(&req.stats, 0, sizeof(gts->ustats));
- }
-
- if (copy_to_user((void __user *)arg, &req, sizeof(req)))
- return -EFAULT;
-
- return 0;
-}
-
-/*
- * Register the current task as the user of the GSEG slice.
- * Needed for TLB fault interrupt targeting.
- */
-int gru_set_context_option(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_set_context_option_req req;
- int ret = 0;
-
- STAT(set_context_option);
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
- gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1);
-
- gts = gru_find_lock_gts(req.gseg);
- if (!gts) {
- gts = gru_alloc_locked_gts(req.gseg);
- if (IS_ERR(gts))
- return PTR_ERR(gts);
- }
-
- switch (req.op) {
- case sco_blade_chiplet:
- /* Select blade/chiplet for GRU context */
- if (req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB ||
- req.val1 < -1 || req.val1 >= GRU_MAX_BLADES ||
- (req.val1 >= 0 && !gru_base[req.val1])) {
- ret = -EINVAL;
- } else {
- gts->ts_user_blade_id = req.val1;
- gts->ts_user_chiplet_id = req.val0;
- if (gru_check_context_placement(gts)) {
- gru_unlock_gts(gts);
- gru_unload_context(gts, 1);
- return ret;
- }
- }
- break;
- case sco_gseg_owner:
- /* Register the current task as the GSEG owner */
- gts->ts_tgid_owner = current->tgid;
- break;
- case sco_cch_req_slice:
- /* Set the CCH slice option */
- gts->ts_cch_req_slice = req.val1 & 3;
- break;
- default:
- ret = -EINVAL;
- }
- gru_unlock_gts(gts);
-
- return ret;
-}
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c
deleted file mode 100644
index e755690c9805..000000000000
--- a/drivers/misc/sgi-gru/grufile.c
+++ /dev/null
@@ -1,540 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * FILE OPERATIONS & DRIVER INITIALIZATION
- *
- * This file supports the user system call for file open, close, mmap, etc.
- * This also incudes the driver initialization code.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/miscdevice.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/uaccess.h>
-#ifdef CONFIG_X86_64
-#include <asm/uv/uv_irq.h>
-#endif
-#include <asm/uv/uv.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-
-#include <asm/uv/uv_hub.h>
-#include <asm/uv/uv_mmrs.h>
-
-struct gru_blade_state *gru_base[GRU_MAX_BLADES] __read_mostly;
-unsigned long gru_start_paddr __read_mostly;
-void *gru_start_vaddr __read_mostly;
-unsigned long gru_end_paddr __read_mostly;
-unsigned int gru_max_gids __read_mostly;
-struct gru_stats_s gru_stats;
-
-/* Guaranteed user available resources on each node */
-static int max_user_cbrs, max_user_dsr_bytes;
-
-static struct miscdevice gru_miscdev;
-
-static int gru_supported(void)
-{
- return is_uv_system() &&
- (uv_hub_info->hub_revision < UV3_HUB_REVISION_BASE);
-}
-
-/*
- * gru_vma_close
- *
- * Called when unmapping a device mapping. Frees all gru resources
- * and tables belonging to the vma.
- */
-static void gru_vma_close(struct vm_area_struct *vma)
-{
- struct gru_vma_data *vdata;
- struct gru_thread_state *gts;
- struct list_head *entry, *next;
-
- if (!vma->vm_private_data)
- return;
-
- vdata = vma->vm_private_data;
- vma->vm_private_data = NULL;
- gru_dbg(grudev, "vma %p, file %p, vdata %p\n", vma, vma->vm_file,
- vdata);
- list_for_each_safe(entry, next, &vdata->vd_head) {
- gts =
- list_entry(entry, struct gru_thread_state, ts_next);
- list_del(&gts->ts_next);
- mutex_lock(&gts->ts_ctxlock);
- if (gts->ts_gru)
- gru_unload_context(gts, 0);
- mutex_unlock(&gts->ts_ctxlock);
- gts_drop(gts);
- }
- kfree(vdata);
- STAT(vdata_free);
-}
-
-/*
- * gru_file_mmap
- *
- * Called when mmapping the device. Initializes the vma with a fault handler
- * and private data structure necessary to allocate, track, and free the
- * underlying pages.
- */
-static int gru_file_mmap(struct file *file, struct vm_area_struct *vma)
-{
- if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) != (VM_SHARED | VM_WRITE))
- return -EPERM;
-
- if (vma->vm_start & (GRU_GSEG_PAGESIZE - 1) ||
- vma->vm_end & (GRU_GSEG_PAGESIZE - 1))
- return -EINVAL;
-
- vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_LOCKED |
- VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
- vma->vm_page_prot = PAGE_SHARED;
- vma->vm_ops = &gru_vm_ops;
-
- vma->vm_private_data = gru_alloc_vma_data(vma, 0);
- if (!vma->vm_private_data)
- return -ENOMEM;
-
- gru_dbg(grudev, "file %p, vaddr 0x%lx, vma %p, vdata %p\n",
- file, vma->vm_start, vma, vma->vm_private_data);
- return 0;
-}
-
-/*
- * Create a new GRU context
- */
-static int gru_create_new_context(unsigned long arg)
-{
- struct gru_create_context_req req;
- struct vm_area_struct *vma;
- struct gru_vma_data *vdata;
- int ret = -EINVAL;
-
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- if (req.data_segment_bytes > max_user_dsr_bytes)
- return -EINVAL;
- if (req.control_blocks > max_user_cbrs || !req.maximum_thread_count)
- return -EINVAL;
-
- if (!(req.options & GRU_OPT_MISS_MASK))
- req.options |= GRU_OPT_MISS_FMM_INTR;
-
- mmap_write_lock(current->mm);
- vma = gru_find_vma(req.gseg);
- if (vma) {
- vdata = vma->vm_private_data;
- vdata->vd_user_options = req.options;
- vdata->vd_dsr_au_count =
- GRU_DS_BYTES_TO_AU(req.data_segment_bytes);
- vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks);
- vdata->vd_tlb_preload_count = req.tlb_preload_count;
- ret = 0;
- }
- mmap_write_unlock(current->mm);
-
- return ret;
-}
-
-/*
- * Get GRU configuration info (temp - for emulator testing)
- */
-static long gru_get_config_info(unsigned long arg)
-{
- struct gru_config_info info;
- int nodesperblade;
-
- if (num_online_nodes() > 1 &&
- (uv_node_to_blade_id(1) == uv_node_to_blade_id(0)))
- nodesperblade = 2;
- else
- nodesperblade = 1;
- memset(&info, 0, sizeof(info));
- info.cpus = num_online_cpus();
- info.nodes = num_online_nodes();
- info.blades = info.nodes / nodesperblade;
- info.chiplets = GRU_CHIPLETS_PER_BLADE * info.blades;
-
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-/*
- * gru_file_unlocked_ioctl
- *
- * Called to update file attributes via IOCTL calls.
- */
-static long gru_file_unlocked_ioctl(struct file *file, unsigned int req,
- unsigned long arg)
-{
- int err = -EBADRQC;
-
- gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg);
-
- switch (req) {
- case GRU_CREATE_CONTEXT:
- err = gru_create_new_context(arg);
- break;
- case GRU_SET_CONTEXT_OPTION:
- err = gru_set_context_option(arg);
- break;
- case GRU_USER_GET_EXCEPTION_DETAIL:
- err = gru_get_exception_detail(arg);
- break;
- case GRU_USER_UNLOAD_CONTEXT:
- err = gru_user_unload_context(arg);
- break;
- case GRU_USER_FLUSH_TLB:
- err = gru_user_flush_tlb(arg);
- break;
- case GRU_USER_CALL_OS:
- err = gru_handle_user_call_os(arg);
- break;
- case GRU_GET_GSEG_STATISTICS:
- err = gru_get_gseg_statistics(arg);
- break;
- case GRU_KTEST:
- err = gru_ktest(arg);
- break;
- case GRU_GET_CONFIG_INFO:
- err = gru_get_config_info(arg);
- break;
- case GRU_DUMP_CHIPLET_STATE:
- err = gru_dump_chiplet_request(arg);
- break;
- }
- return err;
-}
-
-/*
- * Called at init time to build tables for all GRUs that are present in the
- * system.
- */
-static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr,
- void *vaddr, int blade_id, int chiplet_id)
-{
- spin_lock_init(&gru->gs_lock);
- spin_lock_init(&gru->gs_asid_lock);
- gru->gs_gru_base_paddr = paddr;
- gru->gs_gru_base_vaddr = vaddr;
- gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id;
- gru->gs_blade = gru_base[blade_id];
- gru->gs_blade_id = blade_id;
- gru->gs_chiplet_id = chiplet_id;
- gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1;
- gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1;
- gru->gs_asid_limit = MAX_ASID;
- gru_tgh_flush_init(gru);
- if (gru->gs_gid >= gru_max_gids)
- gru_max_gids = gru->gs_gid + 1;
- gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n",
- blade_id, gru->gs_gid, gru->gs_gru_base_vaddr,
- gru->gs_gru_base_paddr);
-}
-
-static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
-{
- int pnode, nid, bid, chip;
- int cbrs, dsrbytes, n;
- int order = get_order(sizeof(struct gru_blade_state));
- struct page *page;
- struct gru_state *gru;
- unsigned long paddr;
- void *vaddr;
-
- max_user_cbrs = GRU_NUM_CB;
- max_user_dsr_bytes = GRU_NUM_DSR_BYTES;
- for_each_possible_blade(bid) {
- pnode = uv_blade_to_pnode(bid);
- nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */
- page = alloc_pages_node(nid, GFP_KERNEL, order);
- if (!page)
- goto fail;
- gru_base[bid] = page_address(page);
- memset(gru_base[bid], 0, sizeof(struct gru_blade_state));
- gru_base[bid]->bs_lru_gru = &gru_base[bid]->bs_grus[0];
- spin_lock_init(&gru_base[bid]->bs_lock);
- init_rwsem(&gru_base[bid]->bs_kgts_sema);
-
- dsrbytes = 0;
- cbrs = 0;
- for (gru = gru_base[bid]->bs_grus, chip = 0;
- chip < GRU_CHIPLETS_PER_BLADE;
- chip++, gru++) {
- paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip);
- vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip);
- gru_init_chiplet(gru, paddr, vaddr, bid, chip);
- n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE;
- cbrs = max(cbrs, n);
- n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES;
- dsrbytes = max(dsrbytes, n);
- }
- max_user_cbrs = min(max_user_cbrs, cbrs);
- max_user_dsr_bytes = min(max_user_dsr_bytes, dsrbytes);
- }
-
- return 0;
-
-fail:
- for (bid--; bid >= 0; bid--)
- free_pages((unsigned long)gru_base[bid], order);
- return -ENOMEM;
-}
-
-static void gru_free_tables(void)
-{
- int bid;
- int order = get_order(sizeof(struct gru_state) *
- GRU_CHIPLETS_PER_BLADE);
-
- for (bid = 0; bid < GRU_MAX_BLADES; bid++)
- free_pages((unsigned long)gru_base[bid], order);
-}
-
-static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep)
-{
- unsigned long mmr = 0;
- int core;
-
- /*
- * We target the cores of a blade and not the hyperthreads themselves.
- * There is a max of 8 cores per socket and 2 sockets per blade,
- * making for a max total of 16 cores (i.e., 16 CPUs without
- * hyperthreading and 32 CPUs with hyperthreading).
- */
- core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
- if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu))
- return 0;
-
- if (chiplet == 0) {
- mmr = UVH_GR0_TLB_INT0_CONFIG +
- core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG);
- } else if (chiplet == 1) {
- mmr = UVH_GR1_TLB_INT0_CONFIG +
- core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG);
- } else {
- BUG();
- }
-
- *corep = core;
- return mmr;
-}
-
-static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name,
- irq_handler_t irq_handler, int cpu, int blade)
-{
- unsigned long mmr;
- int irq, core;
- int ret;
-
- mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
- if (mmr == 0)
- return 0;
-
- irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU);
- if (irq < 0) {
- printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n",
- GRU_DRIVER_ID_STR, -irq);
- return irq;
- }
-
- ret = request_irq(irq, irq_handler, 0, irq_name, NULL);
- if (ret) {
- uv_teardown_irq(irq);
- printk(KERN_ERR "%s: request_irq failed, errno=%d\n",
- GRU_DRIVER_ID_STR, -ret);
- return ret;
- }
- gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq;
- return 0;
-}
-
-static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade)
-{
- int irq, core;
- unsigned long mmr;
-
- mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
- if (mmr) {
- irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core];
- if (irq) {
- free_irq(irq, NULL);
- uv_teardown_irq(irq);
- }
- }
-}
-
-static void gru_teardown_tlb_irqs(void)
-{
- int blade;
- int cpu;
-
- for_each_online_cpu(cpu) {
- blade = uv_cpu_to_blade_id(cpu);
- gru_chiplet_teardown_tlb_irq(0, cpu, blade);
- gru_chiplet_teardown_tlb_irq(1, cpu, blade);
- }
- for_each_possible_blade(blade) {
- if (uv_blade_nr_possible_cpus(blade))
- continue;
- gru_chiplet_teardown_tlb_irq(0, 0, blade);
- gru_chiplet_teardown_tlb_irq(1, 0, blade);
- }
-}
-
-static int gru_setup_tlb_irqs(void)
-{
- int blade;
- int cpu;
- int ret;
-
- for_each_online_cpu(cpu) {
- blade = uv_cpu_to_blade_id(cpu);
- ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade);
- if (ret != 0)
- goto exit1;
-
- ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade);
- if (ret != 0)
- goto exit1;
- }
- for_each_possible_blade(blade) {
- if (uv_blade_nr_possible_cpus(blade))
- continue;
- ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade);
- if (ret != 0)
- goto exit1;
-
- ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade);
- if (ret != 0)
- goto exit1;
- }
-
- return 0;
-
-exit1:
- gru_teardown_tlb_irqs();
- return ret;
-}
-
-/*
- * gru_init
- *
- * Called at boot or module load time to initialize the GRUs.
- */
-static int __init gru_init(void)
-{
- int ret;
-
- if (!gru_supported())
- return 0;
-
- gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG) &
- 0x7fffffffffffUL;
- gru_start_vaddr = __va(gru_start_paddr);
- gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE;
- printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n",
- gru_start_paddr, gru_end_paddr);
- ret = misc_register(&gru_miscdev);
- if (ret) {
- printk(KERN_ERR "%s: misc_register failed\n",
- GRU_DRIVER_ID_STR);
- goto exit0;
- }
-
- ret = gru_proc_init();
- if (ret) {
- printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR);
- goto exit1;
- }
-
- ret = gru_init_tables(gru_start_paddr, gru_start_vaddr);
- if (ret) {
- printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR);
- goto exit2;
- }
-
- ret = gru_setup_tlb_irqs();
- if (ret != 0)
- goto exit3;
-
- gru_kservices_init();
-
- printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR,
- GRU_DRIVER_VERSION_STR);
- return 0;
-
-exit3:
- gru_free_tables();
-exit2:
- gru_proc_exit();
-exit1:
- misc_deregister(&gru_miscdev);
-exit0:
- return ret;
-
-}
-
-static void __exit gru_exit(void)
-{
- if (!gru_supported())
- return;
-
- gru_teardown_tlb_irqs();
- gru_kservices_exit();
- gru_free_tables();
- misc_deregister(&gru_miscdev);
- gru_proc_exit();
- mmu_notifier_synchronize();
-}
-
-static const struct file_operations gru_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = gru_file_unlocked_ioctl,
- .mmap = gru_file_mmap,
- .llseek = noop_llseek,
-};
-
-static struct miscdevice gru_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "gru",
- .fops = &gru_fops,
-};
-
-const struct vm_operations_struct gru_vm_ops = {
- .close = gru_vma_close,
- .fault = gru_fault,
-};
-
-#ifndef MODULE
-fs_initcall(gru_init);
-#else
-module_init(gru_init);
-#endif
-module_exit(gru_exit);
-
-module_param(gru_options, ulong, 0644);
-MODULE_PARM_DESC(gru_options, "Various debug options");
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(GRU_DRIVER_ID_STR GRU_DRIVER_VERSION_STR);
-MODULE_VERSION(GRU_DRIVER_VERSION_STR);
-
diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c
deleted file mode 100644
index 695316a83b01..000000000000
--- a/drivers/misc/sgi-gru/gruhandles.c
+++ /dev/null
@@ -1,192 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * GRU KERNEL MCS INSTRUCTIONS
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-
-/* 10 sec */
-#include <linux/sync_core.h>
-#include <asm/tsc.h>
-#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
-#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz)
-
-/* Extract the status field from a kernel handle */
-#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3)
-
-struct mcs_op_statistic mcs_op_statistics[mcsop_last];
-
-static void update_mcs_stats(enum mcs_op op, unsigned long clks)
-{
- unsigned long nsec;
-
- nsec = CLKS2NSEC(clks);
- atomic_long_inc(&mcs_op_statistics[op].count);
- atomic_long_add(nsec, &mcs_op_statistics[op].total);
- if (mcs_op_statistics[op].max < nsec)
- mcs_op_statistics[op].max = nsec;
-}
-
-static void start_instruction(void *h)
-{
- unsigned long *w0 = h;
-
- wmb(); /* setting CMD/STATUS bits must be last */
- *w0 = *w0 | 0x20001;
- gru_flush_cache(h);
-}
-
-static void report_instruction_timeout(void *h)
-{
- unsigned long goff = GSEGPOFF((unsigned long)h);
- char *id = "???";
-
- if (TYPE_IS(CCH, goff))
- id = "CCH";
- else if (TYPE_IS(TGH, goff))
- id = "TGH";
- else if (TYPE_IS(TFH, goff))
- id = "TFH";
-
- panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id);
-}
-
-static int wait_instruction_complete(void *h, enum mcs_op opc)
-{
- int status;
- unsigned long start_time = get_cycles();
-
- while (1) {
- cpu_relax();
- status = GET_MSEG_HANDLE_STATUS(h);
- if (status != CCHSTATUS_ACTIVE)
- break;
- if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) {
- report_instruction_timeout(h);
- start_time = get_cycles();
- }
- }
- if (gru_options & OPT_STATS)
- update_mcs_stats(opc, get_cycles() - start_time);
- return status;
-}
-
-int cch_allocate(struct gru_context_configuration_handle *cch)
-{
- int ret;
-
- cch->opc = CCHOP_ALLOCATE;
- start_instruction(cch);
- ret = wait_instruction_complete(cch, cchop_allocate);
-
- /*
- * Stop speculation into the GSEG being mapped by the previous ALLOCATE.
- * The GSEG memory does not exist until the ALLOCATE completes.
- */
- sync_core();
- return ret;
-}
-
-int cch_start(struct gru_context_configuration_handle *cch)
-{
- cch->opc = CCHOP_START;
- start_instruction(cch);
- return wait_instruction_complete(cch, cchop_start);
-}
-
-int cch_interrupt(struct gru_context_configuration_handle *cch)
-{
- cch->opc = CCHOP_INTERRUPT;
- start_instruction(cch);
- return wait_instruction_complete(cch, cchop_interrupt);
-}
-
-int cch_deallocate(struct gru_context_configuration_handle *cch)
-{
- int ret;
-
- cch->opc = CCHOP_DEALLOCATE;
- start_instruction(cch);
- ret = wait_instruction_complete(cch, cchop_deallocate);
-
- /*
- * Stop speculation into the GSEG being unmapped by the previous
- * DEALLOCATE.
- */
- sync_core();
- return ret;
-}
-
-int cch_interrupt_sync(struct gru_context_configuration_handle
- *cch)
-{
- cch->opc = CCHOP_INTERRUPT_SYNC;
- start_instruction(cch);
- return wait_instruction_complete(cch, cchop_interrupt_sync);
-}
-
-int tgh_invalidate(struct gru_tlb_global_handle *tgh,
- unsigned long vaddr, unsigned long vaddrmask,
- int asid, int pagesize, int global, int n,
- unsigned short ctxbitmap)
-{
- tgh->vaddr = vaddr;
- tgh->asid = asid;
- tgh->pagesize = pagesize;
- tgh->n = n;
- tgh->global = global;
- tgh->vaddrmask = vaddrmask;
- tgh->ctxbitmap = ctxbitmap;
- tgh->opc = TGHOP_TLBINV;
- start_instruction(tgh);
- return wait_instruction_complete(tgh, tghop_invalidate);
-}
-
-int tfh_write_only(struct gru_tlb_fault_handle *tfh,
- unsigned long paddr, int gaa,
- unsigned long vaddr, int asid, int dirty,
- int pagesize)
-{
- tfh->fillasid = asid;
- tfh->fillvaddr = vaddr;
- tfh->pfn = paddr >> GRU_PADDR_SHIFT;
- tfh->gaa = gaa;
- tfh->dirty = dirty;
- tfh->pagesize = pagesize;
- tfh->opc = TFHOP_WRITE_ONLY;
- start_instruction(tfh);
- return wait_instruction_complete(tfh, tfhop_write_only);
-}
-
-void tfh_write_restart(struct gru_tlb_fault_handle *tfh,
- unsigned long paddr, int gaa,
- unsigned long vaddr, int asid, int dirty,
- int pagesize)
-{
- tfh->fillasid = asid;
- tfh->fillvaddr = vaddr;
- tfh->pfn = paddr >> GRU_PADDR_SHIFT;
- tfh->gaa = gaa;
- tfh->dirty = dirty;
- tfh->pagesize = pagesize;
- tfh->opc = TFHOP_WRITE_RESTART;
- start_instruction(tfh);
-}
-
-void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh)
-{
- tfh->opc = TFHOP_USER_POLLING_MODE;
- start_instruction(tfh);
-}
-
-void tfh_exception(struct gru_tlb_fault_handle *tfh)
-{
- tfh->opc = TFHOP_EXCEPTION;
- start_instruction(tfh);
-}
-
diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h
deleted file mode 100644
index 5a498bf8d003..000000000000
--- a/drivers/misc/sgi-gru/gruhandles.h
+++ /dev/null
@@ -1,517 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * SN Platform GRU Driver
- *
- * GRU HANDLE DEFINITION
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#ifndef __GRUHANDLES_H__
-#define __GRUHANDLES_H__
-#include "gru_instructions.h"
-
-/*
- * Manifest constants for GRU Memory Map
- */
-#define GRU_GSEG0_BASE 0
-#define GRU_MCS_BASE (64 * 1024 * 1024)
-#define GRU_SIZE (128UL * 1024 * 1024)
-
-/* Handle & resource counts */
-#define GRU_NUM_CB 128
-#define GRU_NUM_DSR_BYTES (32 * 1024)
-#define GRU_NUM_TFM 16
-#define GRU_NUM_TGH 24
-#define GRU_NUM_CBE 128
-#define GRU_NUM_TFH 128
-#define GRU_NUM_CCH 16
-
-/* Maximum resource counts that can be reserved by user programs */
-#define GRU_NUM_USER_CBR GRU_NUM_CBE
-#define GRU_NUM_USER_DSR_BYTES GRU_NUM_DSR_BYTES
-
-/* Bytes per handle & handle stride. Code assumes all cb, tfh, cbe handles
- * are the same */
-#define GRU_HANDLE_BYTES 64
-#define GRU_HANDLE_STRIDE 256
-
-/* Base addresses of handles */
-#define GRU_TFM_BASE (GRU_MCS_BASE + 0x00000)
-#define GRU_TGH_BASE (GRU_MCS_BASE + 0x08000)
-#define GRU_CBE_BASE (GRU_MCS_BASE + 0x10000)
-#define GRU_TFH_BASE (GRU_MCS_BASE + 0x18000)
-#define GRU_CCH_BASE (GRU_MCS_BASE + 0x20000)
-
-/* User gseg constants */
-#define GRU_GSEG_STRIDE (4 * 1024 * 1024)
-#define GSEG_BASE(a) ((a) & ~(GRU_GSEG_PAGESIZE - 1))
-
-/* Data segment constants */
-#define GRU_DSR_AU_BYTES 1024
-#define GRU_DSR_CL (GRU_NUM_DSR_BYTES / GRU_CACHE_LINE_BYTES)
-#define GRU_DSR_AU_CL (GRU_DSR_AU_BYTES / GRU_CACHE_LINE_BYTES)
-#define GRU_DSR_AU (GRU_NUM_DSR_BYTES / GRU_DSR_AU_BYTES)
-
-/* Control block constants */
-#define GRU_CBR_AU_SIZE 2
-#define GRU_CBR_AU (GRU_NUM_CBE / GRU_CBR_AU_SIZE)
-
-/* Convert resource counts to the number of AU */
-#define GRU_DS_BYTES_TO_AU(n) DIV_ROUND_UP(n, GRU_DSR_AU_BYTES)
-#define GRU_CB_COUNT_TO_AU(n) DIV_ROUND_UP(n, GRU_CBR_AU_SIZE)
-
-/* UV limits */
-#define GRU_CHIPLETS_PER_HUB 2
-#define GRU_HUBS_PER_BLADE 1
-#define GRU_CHIPLETS_PER_BLADE (GRU_HUBS_PER_BLADE * GRU_CHIPLETS_PER_HUB)
-
-/* User GRU Gseg offsets */
-#define GRU_CB_BASE 0
-#define GRU_CB_LIMIT (GRU_CB_BASE + GRU_HANDLE_STRIDE * GRU_NUM_CBE)
-#define GRU_DS_BASE 0x20000
-#define GRU_DS_LIMIT (GRU_DS_BASE + GRU_NUM_DSR_BYTES)
-
-/* Convert a GRU physical address to the chiplet offset */
-#define GSEGPOFF(h) ((h) & (GRU_SIZE - 1))
-
-/* Convert an arbitrary handle address to the beginning of the GRU segment */
-#define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1)))
-
-/* Test a valid handle address to determine the type */
-#define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \
- GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \
- (((h) & (GRU_HANDLE_STRIDE - 1)) == 0))
-
-
-/* General addressing macros. */
-static inline void *get_gseg_base_address(void *base, int ctxnum)
-{
- return (void *)(base + GRU_GSEG0_BASE + GRU_GSEG_STRIDE * ctxnum);
-}
-
-static inline void *get_gseg_base_address_cb(void *base, int ctxnum, int line)
-{
- return (void *)(get_gseg_base_address(base, ctxnum) +
- GRU_CB_BASE + GRU_HANDLE_STRIDE * line);
-}
-
-static inline void *get_gseg_base_address_ds(void *base, int ctxnum, int line)
-{
- return (void *)(get_gseg_base_address(base, ctxnum) + GRU_DS_BASE +
- GRU_CACHE_LINE_BYTES * line);
-}
-
-static inline struct gru_tlb_fault_map *get_tfm(void *base, int ctxnum)
-{
- return (struct gru_tlb_fault_map *)(base + GRU_TFM_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_tlb_global_handle *get_tgh(void *base, int ctxnum)
-{
- return (struct gru_tlb_global_handle *)(base + GRU_TGH_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_control_block_extended *get_cbe(void *base, int ctxnum)
-{
- return (struct gru_control_block_extended *)(base + GRU_CBE_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_tlb_fault_handle *get_tfh(void *base, int ctxnum)
-{
- return (struct gru_tlb_fault_handle *)(base + GRU_TFH_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_context_configuration_handle *get_cch(void *base,
- int ctxnum)
-{
- return (struct gru_context_configuration_handle *)(base +
- GRU_CCH_BASE + ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline unsigned long get_cb_number(void *cb)
-{
- return (((unsigned long)cb - GRU_CB_BASE) % GRU_GSEG_PAGESIZE) /
- GRU_HANDLE_STRIDE;
-}
-
-/* byte offset to a specific GRU chiplet. (p=pnode, c=chiplet (0 or 1)*/
-static inline unsigned long gru_chiplet_paddr(unsigned long paddr, int pnode,
- int chiplet)
-{
- return paddr + GRU_SIZE * (2 * pnode + chiplet);
-}
-
-static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet)
-{
- return vaddr + GRU_SIZE * (2 * pnode + chiplet);
-}
-
-static inline struct gru_control_block_extended *gru_tfh_to_cbe(
- struct gru_tlb_fault_handle *tfh)
-{
- unsigned long cbe;
-
- cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE;
- return (struct gru_control_block_extended*)cbe;
-}
-
-
-
-
-/*
- * Global TLB Fault Map
- * Bitmap of outstanding TLB misses needing interrupt/polling service.
- *
- */
-struct gru_tlb_fault_map {
- unsigned long fault_bits[BITS_TO_LONGS(GRU_NUM_CBE)];
- unsigned long fill0[2];
- unsigned long done_bits[BITS_TO_LONGS(GRU_NUM_CBE)];
- unsigned long fill1[2];
-};
-
-/*
- * TGH - TLB Global Handle
- * Used for TLB flushing.
- *
- */
-struct gru_tlb_global_handle {
- unsigned int cmd:1; /* DW 0 */
- unsigned int delresp:1;
- unsigned int opc:1;
- unsigned int fill1:5;
-
- unsigned int fill2:8;
-
- unsigned int status:2;
- unsigned long fill3:2;
- unsigned int state:3;
- unsigned long fill4:1;
-
- unsigned int cause:3;
- unsigned long fill5:37;
-
- unsigned long vaddr:64; /* DW 1 */
-
- unsigned int asid:24; /* DW 2 */
- unsigned int fill6:8;
-
- unsigned int pagesize:5;
- unsigned int fill7:11;
-
- unsigned int global:1;
- unsigned int fill8:15;
-
- unsigned long vaddrmask:39; /* DW 3 */
- unsigned int fill9:9;
- unsigned int n:10;
- unsigned int fill10:6;
-
- unsigned int ctxbitmap:16; /* DW4 */
- unsigned long fill11[3];
-};
-
-enum gru_tgh_cmd {
- TGHCMD_START
-};
-
-enum gru_tgh_opc {
- TGHOP_TLBNOP,
- TGHOP_TLBINV
-};
-
-enum gru_tgh_status {
- TGHSTATUS_IDLE,
- TGHSTATUS_EXCEPTION,
- TGHSTATUS_ACTIVE
-};
-
-enum gru_tgh_state {
- TGHSTATE_IDLE,
- TGHSTATE_PE_INVAL,
- TGHSTATE_INTERRUPT_INVAL,
- TGHSTATE_WAITDONE,
- TGHSTATE_RESTART_CTX,
-};
-
-enum gru_tgh_cause {
- TGHCAUSE_RR_ECC,
- TGHCAUSE_TLB_ECC,
- TGHCAUSE_LRU_ECC,
- TGHCAUSE_PS_ECC,
- TGHCAUSE_MUL_ERR,
- TGHCAUSE_DATA_ERR,
- TGHCAUSE_SW_FORCE
-};
-
-
-/*
- * TFH - TLB Global Handle
- * Used for TLB dropins into the GRU TLB.
- *
- */
-struct gru_tlb_fault_handle {
- unsigned int cmd:1; /* DW 0 - low 32*/
- unsigned int delresp:1;
- unsigned int fill0:2;
- unsigned int opc:3;
- unsigned int fill1:9;
-
- unsigned int status:2;
- unsigned int fill2:2;
- unsigned int state:3;
- unsigned int fill3:1;
-
- unsigned int cause:6;
- unsigned int cb_int:1;
- unsigned int fill4:1;
-
- unsigned int indexway:12; /* DW 0 - high 32 */
- unsigned int fill5:4;
-
- unsigned int ctxnum:4;
- unsigned int fill6:12;
-
- unsigned long missvaddr:64; /* DW 1 */
-
- unsigned int missasid:24; /* DW 2 */
- unsigned int fill7:8;
- unsigned int fillasid:24;
- unsigned int dirty:1;
- unsigned int gaa:2;
- unsigned long fill8:5;
-
- unsigned long pfn:41; /* DW 3 */
- unsigned int fill9:7;
- unsigned int pagesize:5;
- unsigned int fill10:11;
-
- unsigned long fillvaddr:64; /* DW 4 */
-
- unsigned long fill11[3];
-};
-
-enum gru_tfh_opc {
- TFHOP_NOOP,
- TFHOP_RESTART,
- TFHOP_WRITE_ONLY,
- TFHOP_WRITE_RESTART,
- TFHOP_EXCEPTION,
- TFHOP_USER_POLLING_MODE = 7,
-};
-
-enum tfh_status {
- TFHSTATUS_IDLE,
- TFHSTATUS_EXCEPTION,
- TFHSTATUS_ACTIVE,
-};
-
-enum tfh_state {
- TFHSTATE_INACTIVE,
- TFHSTATE_IDLE,
- TFHSTATE_MISS_UPM,
- TFHSTATE_MISS_FMM,
- TFHSTATE_HW_ERR,
- TFHSTATE_WRITE_TLB,
- TFHSTATE_RESTART_CBR,
-};
-
-/* TFH cause bits */
-enum tfh_cause {
- TFHCAUSE_NONE,
- TFHCAUSE_TLB_MISS,
- TFHCAUSE_TLB_MOD,
- TFHCAUSE_HW_ERROR_RR,
- TFHCAUSE_HW_ERROR_MAIN_ARRAY,
- TFHCAUSE_HW_ERROR_VALID,
- TFHCAUSE_HW_ERROR_PAGESIZE,
- TFHCAUSE_INSTRUCTION_EXCEPTION,
- TFHCAUSE_UNCORRECTIBLE_ERROR,
-};
-
-/* GAA values */
-#define GAA_RAM 0x0
-#define GAA_NCRAM 0x2
-#define GAA_MMIO 0x1
-#define GAA_REGISTER 0x3
-
-/* GRU paddr shift for pfn. (NOTE: shift is NOT by actual pagesize) */
-#define GRU_PADDR_SHIFT 12
-
-/*
- * Context Configuration handle
- * Used to allocate resources to a GSEG context.
- *
- */
-struct gru_context_configuration_handle {
- unsigned int cmd:1; /* DW0 */
- unsigned int delresp:1;
- unsigned int opc:3;
- unsigned int unmap_enable:1;
- unsigned int req_slice_set_enable:1;
- unsigned int req_slice:2;
- unsigned int cb_int_enable:1;
- unsigned int tlb_int_enable:1;
- unsigned int tfm_fault_bit_enable:1;
- unsigned int tlb_int_select:4;
-
- unsigned int status:2;
- unsigned int state:2;
- unsigned int reserved2:4;
-
- unsigned int cause:4;
- unsigned int tfm_done_bit_enable:1;
- unsigned int unused:3;
-
- unsigned int dsr_allocation_map;
-
- unsigned long cbr_allocation_map; /* DW1 */
-
- unsigned int asid[8]; /* DW 2 - 5 */
- unsigned short sizeavail[8]; /* DW 6 - 7 */
-} __attribute__ ((packed));
-
-enum gru_cch_opc {
- CCHOP_START = 1,
- CCHOP_ALLOCATE,
- CCHOP_INTERRUPT,
- CCHOP_DEALLOCATE,
- CCHOP_INTERRUPT_SYNC,
-};
-
-enum gru_cch_status {
- CCHSTATUS_IDLE,
- CCHSTATUS_EXCEPTION,
- CCHSTATUS_ACTIVE,
-};
-
-enum gru_cch_state {
- CCHSTATE_INACTIVE,
- CCHSTATE_MAPPED,
- CCHSTATE_ACTIVE,
- CCHSTATE_INTERRUPTED,
-};
-
-/* CCH Exception cause */
-enum gru_cch_cause {
- CCHCAUSE_REGION_REGISTER_WRITE_ERROR = 1,
- CCHCAUSE_ILLEGAL_OPCODE = 2,
- CCHCAUSE_INVALID_START_REQUEST = 3,
- CCHCAUSE_INVALID_ALLOCATION_REQUEST = 4,
- CCHCAUSE_INVALID_DEALLOCATION_REQUEST = 5,
- CCHCAUSE_INVALID_INTERRUPT_REQUEST = 6,
- CCHCAUSE_CCH_BUSY = 7,
- CCHCAUSE_NO_CBRS_TO_ALLOCATE = 8,
- CCHCAUSE_BAD_TFM_CONFIG = 9,
- CCHCAUSE_CBR_RESOURCES_OVERSUBSCRIPED = 10,
- CCHCAUSE_DSR_RESOURCES_OVERSUBSCRIPED = 11,
- CCHCAUSE_CBR_DEALLOCATION_ERROR = 12,
-};
-/*
- * CBE - Control Block Extended
- * Maintains internal GRU state for active CBs.
- *
- */
-struct gru_control_block_extended {
- unsigned int reserved0:1; /* DW 0 - low */
- unsigned int imacpy:3;
- unsigned int reserved1:4;
- unsigned int xtypecpy:3;
- unsigned int iaa0cpy:2;
- unsigned int iaa1cpy:2;
- unsigned int reserved2:1;
- unsigned int opccpy:8;
- unsigned int exopccpy:8;
-
- unsigned int idef2cpy:22; /* DW 0 - high */
- unsigned int reserved3:10;
-
- unsigned int idef4cpy:22; /* DW 1 */
- unsigned int reserved4:10;
- unsigned int idef4upd:22;
- unsigned int reserved5:10;
-
- unsigned long idef1upd:64; /* DW 2 */
-
- unsigned long idef5cpy:64; /* DW 3 */
-
- unsigned long idef6cpy:64; /* DW 4 */
-
- unsigned long idef3upd:64; /* DW 5 */
-
- unsigned long idef5upd:64; /* DW 6 */
-
- unsigned int idef2upd:22; /* DW 7 */
- unsigned int reserved6:10;
-
- unsigned int ecause:20;
- unsigned int cbrstate:4;
- unsigned int cbrexecstatus:8;
-};
-
-/* CBE fields for active BCOPY instructions */
-#define cbe_baddr0 idef1upd
-#define cbe_baddr1 idef3upd
-#define cbe_src_cl idef6cpy
-#define cbe_nelemcur idef5upd
-
-enum gru_cbr_state {
- CBRSTATE_INACTIVE,
- CBRSTATE_IDLE,
- CBRSTATE_PE_CHECK,
- CBRSTATE_QUEUED,
- CBRSTATE_WAIT_RESPONSE,
- CBRSTATE_INTERRUPTED,
- CBRSTATE_INTERRUPTED_MISS_FMM,
- CBRSTATE_BUSY_INTERRUPT_MISS_FMM,
- CBRSTATE_INTERRUPTED_MISS_UPM,
- CBRSTATE_BUSY_INTERRUPTED_MISS_UPM,
- CBRSTATE_REQUEST_ISSUE,
- CBRSTATE_BUSY_INTERRUPT,
-};
-
-/* CBE cbrexecstatus bits - defined in gru_instructions.h*/
-/* CBE ecause bits - defined in gru_instructions.h */
-
-/*
- * Convert a processor pagesize into the strange encoded pagesize used by the
- * GRU. Processor pagesize is encoded as log of bytes per page. (or PAGE_SHIFT)
- * pagesize log pagesize grupagesize
- * 4k 12 0
- * 16k 14 1
- * 64k 16 2
- * 256k 18 3
- * 1m 20 4
- * 2m 21 5
- * 4m 22 6
- * 16m 24 7
- * 64m 26 8
- * ...
- */
-#define GRU_PAGESIZE(sh) ((((sh) > 20 ? (sh) + 2 : (sh)) >> 1) - 6)
-#define GRU_SIZEAVAIL(sh) (1UL << GRU_PAGESIZE(sh))
-
-/* minimum TLB purge count to ensure a full purge */
-#define GRUMAXINVAL 1024UL
-
-int cch_allocate(struct gru_context_configuration_handle *cch);
-int cch_start(struct gru_context_configuration_handle *cch);
-int cch_interrupt(struct gru_context_configuration_handle *cch);
-int cch_deallocate(struct gru_context_configuration_handle *cch);
-int cch_interrupt_sync(struct gru_context_configuration_handle *cch);
-int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr,
- unsigned long vaddrmask, int asid, int pagesize, int global, int n,
- unsigned short ctxbitmap);
-int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
- int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
-void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
- int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
-void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh);
-void tfh_exception(struct gru_tlb_fault_handle *tfh);
-
-#endif /* __GRUHANDLES_H__ */
diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c
deleted file mode 100644
index 9869f4f2f476..000000000000
--- a/drivers/misc/sgi-gru/grukdump.c
+++ /dev/null
@@ -1,223 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * Dump GRU State
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-#include <asm/uv/uv_hub.h>
-
-#include <linux/nospec.h>
-
-#include "gru.h"
-#include "grutables.h"
-#include "gruhandles.h"
-#include "grulib.h"
-
-#define CCH_LOCK_ATTEMPTS 10
-
-static int gru_user_copy_handle(void __user **dp, void *s)
-{
- if (copy_to_user(*dp, s, GRU_HANDLE_BYTES))
- return -1;
- *dp += GRU_HANDLE_BYTES;
- return 0;
-}
-
-static int gru_dump_context_data(void *grubase,
- struct gru_context_configuration_handle *cch,
- void __user *ubuf, int ctxnum, int dsrcnt,
- int flush_cbrs)
-{
- void *cb, *cbe, *tfh, *gseg;
- int i, scr;
-
- gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
- cb = gseg + GRU_CB_BASE;
- cbe = grubase + GRU_CBE_BASE;
- tfh = grubase + GRU_TFH_BASE;
-
- for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) {
- if (flush_cbrs)
- gru_flush_cache(cb);
- if (gru_user_copy_handle(&ubuf, cb))
- goto fail;
- if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE))
- goto fail;
- if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE))
- goto fail;
- cb += GRU_HANDLE_STRIDE;
- }
- if (dsrcnt)
- memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE);
- return 0;
-
-fail:
- return -EFAULT;
-}
-
-static int gru_dump_tfm(struct gru_state *gru,
- void __user *ubuf, void __user *ubufend)
-{
- struct gru_tlb_fault_map *tfm;
- int i;
-
- if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
- return -EFBIG;
-
- for (i = 0; i < GRU_NUM_TFM; i++) {
- tfm = get_tfm(gru->gs_gru_base_vaddr, i);
- if (gru_user_copy_handle(&ubuf, tfm))
- goto fail;
- }
- return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES;
-
-fail:
- return -EFAULT;
-}
-
-static int gru_dump_tgh(struct gru_state *gru,
- void __user *ubuf, void __user *ubufend)
-{
- struct gru_tlb_global_handle *tgh;
- int i;
-
- if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
- return -EFBIG;
-
- for (i = 0; i < GRU_NUM_TGH; i++) {
- tgh = get_tgh(gru->gs_gru_base_vaddr, i);
- if (gru_user_copy_handle(&ubuf, tgh))
- goto fail;
- }
- return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES;
-
-fail:
- return -EFAULT;
-}
-
-static int gru_dump_context(struct gru_state *gru, int ctxnum,
- void __user *ubuf, void __user *ubufend, char data_opt,
- char lock_cch, char flush_cbrs)
-{
- struct gru_dump_context_header hdr;
- struct gru_dump_context_header __user *uhdr = ubuf;
- struct gru_context_configuration_handle *cch, *ubufcch;
- struct gru_thread_state *gts;
- int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0;
- void *grubase;
-
- memset(&hdr, 0, sizeof(hdr));
- grubase = gru->gs_gru_base_vaddr;
- cch = get_cch(grubase, ctxnum);
- for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) {
- cch_locked = trylock_cch_handle(cch);
- if (cch_locked)
- break;
- msleep(1);
- }
-
- ubuf += sizeof(hdr);
- ubufcch = ubuf;
- if (gru_user_copy_handle(&ubuf, cch)) {
- if (cch_locked)
- unlock_cch_handle(cch);
- return -EFAULT;
- }
- if (cch_locked)
- ubufcch->delresp = 0;
- bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES;
-
- if (cch_locked || !lock_cch) {
- gts = gru->gs_gts[ctxnum];
- if (gts && gts->ts_vma) {
- hdr.pid = gts->ts_tgid_owner;
- hdr.vaddr = gts->ts_vma->vm_start;
- }
- if (cch->state != CCHSTATE_INACTIVE) {
- cbrcnt = hweight64(cch->cbr_allocation_map) *
- GRU_CBR_AU_SIZE;
- dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) *
- GRU_DSR_AU_CL : 0;
- }
- bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES;
- if (bytes > ubufend - ubuf)
- ret = -EFBIG;
- else
- ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum,
- dsrcnt, flush_cbrs);
- }
- if (cch_locked)
- unlock_cch_handle(cch);
- if (ret)
- return ret;
-
- hdr.magic = GRU_DUMP_MAGIC;
- hdr.gid = gru->gs_gid;
- hdr.ctxnum = ctxnum;
- hdr.cbrcnt = cbrcnt;
- hdr.dsrcnt = dsrcnt;
- hdr.cch_locked = cch_locked;
- if (copy_to_user(uhdr, &hdr, sizeof(hdr)))
- return -EFAULT;
-
- return bytes;
-}
-
-int gru_dump_chiplet_request(unsigned long arg)
-{
- struct gru_state *gru;
- struct gru_dump_chiplet_state_req req;
- void __user *ubuf;
- void __user *ubufend;
- int ctxnum, ret, cnt = 0;
-
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- /* Currently, only dump by gid is implemented */
- if (req.gid >= gru_max_gids)
- return -EINVAL;
- req.gid = array_index_nospec(req.gid, gru_max_gids);
-
- gru = GID_TO_GRU(req.gid);
- ubuf = req.buf;
- ubufend = req.buf + req.buflen;
-
- ret = gru_dump_tfm(gru, ubuf, ubufend);
- if (ret < 0)
- goto fail;
- ubuf += ret;
-
- ret = gru_dump_tgh(gru, ubuf, ubufend);
- if (ret < 0)
- goto fail;
- ubuf += ret;
-
- for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
- if (req.ctxnum == ctxnum || req.ctxnum < 0) {
- ret = gru_dump_context(gru, ctxnum, ubuf, ubufend,
- req.data_opt, req.lock_cch,
- req.flush_cbrs);
- if (ret < 0)
- goto fail;
- ubuf += ret;
- cnt++;
- }
- }
-
- if (copy_to_user((void __user *)arg, &req, sizeof(req)))
- return -EFAULT;
- return cnt;
-
-fail:
- return ret;
-}
diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c
deleted file mode 100644
index 205945ce9e86..000000000000
--- a/drivers/misc/sgi-gru/grukservices.c
+++ /dev/null
@@ -1,1157 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * KERNEL SERVICES THAT USE THE GRU
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/miscdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
-#include <linux/sync_core.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <asm/io_apic.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-#include "grukservices.h"
-#include "gru_instructions.h"
-#include <asm/uv/uv_hub.h>
-
-/*
- * Kernel GRU Usage
- *
- * The following is an interim algorithm for management of kernel GRU
- * resources. This will likely be replaced when we better understand the
- * kernel/user requirements.
- *
- * Blade percpu resources reserved for kernel use. These resources are
- * reserved whenever the kernel context for the blade is loaded. Note
- * that the kernel context is not guaranteed to be always available. It is
- * loaded on demand & can be stolen by a user if the user demand exceeds the
- * kernel demand. The kernel can always reload the kernel context but
- * a SLEEP may be required!!!.
- *
- * Async Overview:
- *
- * Each blade has one "kernel context" that owns GRU kernel resources
- * located on the blade. Kernel drivers use GRU resources in this context
- * for sending messages, zeroing memory, etc.
- *
- * The kernel context is dynamically loaded on demand. If it is not in
- * use by the kernel, the kernel context can be unloaded & given to a user.
- * The kernel context will be reloaded when needed. This may require that
- * a context be stolen from a user.
- * NOTE: frequent unloading/reloading of the kernel context is
- * expensive. We are depending on batch schedulers, cpusets, sane
- * drivers or some other mechanism to prevent the need for frequent
- * stealing/reloading.
- *
- * The kernel context consists of two parts:
- * - 1 CB & a few DSRs that are reserved for each cpu on the blade.
- * Each cpu has it's own private resources & does not share them
- * with other cpus. These resources are used serially, ie,
- * locked, used & unlocked on each call to a function in
- * grukservices.
- * (Now that we have dynamic loading of kernel contexts, I
- * may rethink this & allow sharing between cpus....)
- *
- * - Additional resources can be reserved long term & used directly
- * by UV drivers located in the kernel. Drivers using these GRU
- * resources can use asynchronous GRU instructions that send
- * interrupts on completion.
- * - these resources must be explicitly locked/unlocked
- * - locked resources prevent (obviously) the kernel
- * context from being unloaded.
- * - drivers using these resource directly issue their own
- * GRU instruction and must wait/check completion.
- *
- * When these resources are reserved, the caller can optionally
- * associate a wait_queue with the resources and use asynchronous
- * GRU instructions. When an async GRU instruction completes, the
- * driver will do a wakeup on the event.
- *
- */
-
-
-#define ASYNC_HAN_TO_BID(h) ((h) - 1)
-#define ASYNC_BID_TO_HAN(b) ((b) + 1)
-#define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)]
-
-#define GRU_NUM_KERNEL_CBR 1
-#define GRU_NUM_KERNEL_DSR_BYTES 256
-#define GRU_NUM_KERNEL_DSR_CL (GRU_NUM_KERNEL_DSR_BYTES / \
- GRU_CACHE_LINE_BYTES)
-
-/* GRU instruction attributes for all instructions */
-#define IMA IMA_CB_DELAY
-
-/* GRU cacheline size is always 64 bytes - even on arches with 128 byte lines */
-#define __gru_cacheline_aligned__ \
- __attribute__((__aligned__(GRU_CACHE_LINE_BYTES)))
-
-#define MAGIC 0x1234567887654321UL
-
-/* Default retry count for GRU errors on kernel instructions */
-#define EXCEPTION_RETRY_LIMIT 3
-
-/* Status of message queue sections */
-#define MQS_EMPTY 0
-#define MQS_FULL 1
-#define MQS_NOOP 2
-
-/*----------------- RESOURCE MANAGEMENT -------------------------------------*/
-/* optimized for x86_64 */
-struct message_queue {
- union gru_mesqhead head __gru_cacheline_aligned__; /* CL 0 */
- int qlines; /* DW 1 */
- long hstatus[2];
- void *next __gru_cacheline_aligned__;/* CL 1 */
- void *limit;
- void *start;
- void *start2;
- char data ____cacheline_aligned; /* CL 2 */
-};
-
-/* First word in every message - used by mesq interface */
-struct message_header {
- char present;
- char present2;
- char lines;
- char fill;
-};
-
-#define HSTATUS(mq, h) ((mq) + offsetof(struct message_queue, hstatus[h]))
-
-/*
- * Reload the blade's kernel context into a GRU chiplet. Called holding
- * the bs_kgts_sema for READ. Will steal user contexts if necessary.
- */
-static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id)
-{
- struct gru_state *gru;
- struct gru_thread_state *kgts;
- void *vaddr;
- int ctxnum, ncpus;
-
- up_read(&bs->bs_kgts_sema);
- down_write(&bs->bs_kgts_sema);
-
- if (!bs->bs_kgts) {
- do {
- bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0);
- if (!IS_ERR(bs->bs_kgts))
- break;
- msleep(1);
- } while (true);
- bs->bs_kgts->ts_user_blade_id = blade_id;
- }
- kgts = bs->bs_kgts;
-
- if (!kgts->ts_gru) {
- STAT(load_kernel_context);
- ncpus = uv_blade_nr_possible_cpus(blade_id);
- kgts->ts_cbr_au_count = GRU_CB_COUNT_TO_AU(
- GRU_NUM_KERNEL_CBR * ncpus + bs->bs_async_cbrs);
- kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU(
- GRU_NUM_KERNEL_DSR_BYTES * ncpus +
- bs->bs_async_dsr_bytes);
- while (!gru_assign_gru_context(kgts)) {
- msleep(1);
- gru_steal_context(kgts);
- }
- gru_load_context(kgts);
- gru = bs->bs_kgts->ts_gru;
- vaddr = gru->gs_gru_base_vaddr;
- ctxnum = kgts->ts_ctxnum;
- bs->kernel_cb = get_gseg_base_address_cb(vaddr, ctxnum, 0);
- bs->kernel_dsr = get_gseg_base_address_ds(vaddr, ctxnum, 0);
- }
- downgrade_write(&bs->bs_kgts_sema);
-}
-
-/*
- * Free all kernel contexts that are not currently in use.
- * Returns 0 if all freed, else number of inuse context.
- */
-static int gru_free_kernel_contexts(void)
-{
- struct gru_blade_state *bs;
- struct gru_thread_state *kgts;
- int bid, ret = 0;
-
- for (bid = 0; bid < GRU_MAX_BLADES; bid++) {
- bs = gru_base[bid];
- if (!bs)
- continue;
-
- /* Ignore busy contexts. Don't want to block here. */
- if (down_write_trylock(&bs->bs_kgts_sema)) {
- kgts = bs->bs_kgts;
- if (kgts && kgts->ts_gru)
- gru_unload_context(kgts, 0);
- bs->bs_kgts = NULL;
- up_write(&bs->bs_kgts_sema);
- kfree(kgts);
- } else {
- ret++;
- }
- }
- return ret;
-}
-
-/*
- * Lock & load the kernel context for the specified blade.
- */
-static struct gru_blade_state *gru_lock_kernel_context(int blade_id)
-{
- struct gru_blade_state *bs;
- int bid;
-
- STAT(lock_kernel_context);
-again:
- bid = blade_id < 0 ? uv_numa_blade_id() : blade_id;
- bs = gru_base[bid];
-
- /* Handle the case where migration occurred while waiting for the sema */
- down_read(&bs->bs_kgts_sema);
- if (blade_id < 0 && bid != uv_numa_blade_id()) {
- up_read(&bs->bs_kgts_sema);
- goto again;
- }
- if (!bs->bs_kgts || !bs->bs_kgts->ts_gru)
- gru_load_kernel_context(bs, bid);
- return bs;
-
-}
-
-/*
- * Unlock the kernel context for the specified blade. Context is not
- * unloaded but may be stolen before next use.
- */
-static void gru_unlock_kernel_context(int blade_id)
-{
- struct gru_blade_state *bs;
-
- bs = gru_base[blade_id];
- up_read(&bs->bs_kgts_sema);
- STAT(unlock_kernel_context);
-}
-
-/*
- * Reserve & get pointers to the DSR/CBRs reserved for the current cpu.
- * - returns with preemption disabled
- */
-static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr)
-{
- struct gru_blade_state *bs;
- int lcpu;
-
- BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES);
- bs = gru_lock_kernel_context(-1);
- lcpu = uv_blade_processor_id();
- *cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE;
- *dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES;
- return 0;
-}
-
-/*
- * Free the current cpus reserved DSR/CBR resources.
- */
-static void gru_free_cpu_resources(void *cb, void *dsr)
-{
- gru_unlock_kernel_context(uv_numa_blade_id());
-}
-
-/*
- * Reserve GRU resources to be used asynchronously.
- * Note: currently supports only 1 reservation per blade.
- *
- * input:
- * blade_id - blade on which resources should be reserved
- * cbrs - number of CBRs
- * dsr_bytes - number of DSR bytes needed
- * output:
- * handle to identify resource
- * (0 = async resources already reserved)
- */
-unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes,
- struct completion *cmp)
-{
- struct gru_blade_state *bs;
- struct gru_thread_state *kgts;
- int ret = 0;
-
- bs = gru_base[blade_id];
-
- down_write(&bs->bs_kgts_sema);
-
- /* Verify no resources already reserved */
- if (bs->bs_async_dsr_bytes + bs->bs_async_cbrs)
- goto done;
- bs->bs_async_dsr_bytes = dsr_bytes;
- bs->bs_async_cbrs = cbrs;
- bs->bs_async_wq = cmp;
- kgts = bs->bs_kgts;
-
- /* Resources changed. Unload context if already loaded */
- if (kgts && kgts->ts_gru)
- gru_unload_context(kgts, 0);
- ret = ASYNC_BID_TO_HAN(blade_id);
-
-done:
- up_write(&bs->bs_kgts_sema);
- return ret;
-}
-
-/*
- * Release async resources previously reserved.
- *
- * input:
- * han - handle to identify resources
- */
-void gru_release_async_resources(unsigned long han)
-{
- struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
-
- down_write(&bs->bs_kgts_sema);
- bs->bs_async_dsr_bytes = 0;
- bs->bs_async_cbrs = 0;
- bs->bs_async_wq = NULL;
- up_write(&bs->bs_kgts_sema);
-}
-
-/*
- * Wait for async GRU instructions to complete.
- *
- * input:
- * han - handle to identify resources
- */
-void gru_wait_async_cbr(unsigned long han)
-{
- struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
-
- wait_for_completion(bs->bs_async_wq);
- mb();
-}
-
-/*
- * Lock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- * output:
- * cb - pointer to first CBR
- * dsr - pointer to first DSR
- */
-void gru_lock_async_resource(unsigned long han, void **cb, void **dsr)
-{
- struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
- int blade_id = ASYNC_HAN_TO_BID(han);
- int ncpus;
-
- gru_lock_kernel_context(blade_id);
- ncpus = uv_blade_nr_possible_cpus(blade_id);
- if (cb)
- *cb = bs->kernel_cb + ncpus * GRU_HANDLE_STRIDE;
- if (dsr)
- *dsr = bs->kernel_dsr + ncpus * GRU_NUM_KERNEL_DSR_BYTES;
-}
-
-/*
- * Unlock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- */
-void gru_unlock_async_resource(unsigned long han)
-{
- int blade_id = ASYNC_HAN_TO_BID(han);
-
- gru_unlock_kernel_context(blade_id);
-}
-
-/*----------------------------------------------------------------------*/
-int gru_get_cb_exception_detail(void *cb,
- struct control_block_extended_exc_detail *excdet)
-{
- struct gru_control_block_extended *cbe;
- struct gru_thread_state *kgts = NULL;
- unsigned long off;
- int cbrnum, bid;
-
- /*
- * Locate kgts for cb. This algorithm is SLOW but
- * this function is rarely called (ie., almost never).
- * Performance does not matter.
- */
- for_each_possible_blade(bid) {
- if (!gru_base[bid])
- break;
- kgts = gru_base[bid]->bs_kgts;
- if (!kgts || !kgts->ts_gru)
- continue;
- off = cb - kgts->ts_gru->gs_gru_base_vaddr;
- if (off < GRU_SIZE)
- break;
- kgts = NULL;
- }
- BUG_ON(!kgts);
- cbrnum = thread_cbr_number(kgts, get_cb_number(cb));
- cbe = get_cbe(GRUBASE(cb), cbrnum);
- gru_flush_cache(cbe); /* CBE not coherent */
- sync_core();
- excdet->opc = cbe->opccpy;
- excdet->exopc = cbe->exopccpy;
- excdet->ecause = cbe->ecause;
- excdet->exceptdet0 = cbe->idef1upd;
- excdet->exceptdet1 = cbe->idef3upd;
- gru_flush_cache(cbe);
- return 0;
-}
-
-static char *gru_get_cb_exception_detail_str(int ret, void *cb,
- char *buf, int size)
-{
- struct gru_control_block_status *gen = cb;
- struct control_block_extended_exc_detail excdet;
-
- if (ret > 0 && gen->istatus == CBS_EXCEPTION) {
- gru_get_cb_exception_detail(cb, &excdet);
- snprintf(buf, size,
- "GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x,"
- "excdet0 0x%lx, excdet1 0x%x", smp_processor_id(),
- gen, excdet.opc, excdet.exopc, excdet.ecause,
- excdet.exceptdet0, excdet.exceptdet1);
- } else {
- snprintf(buf, size, "No exception");
- }
- return buf;
-}
-
-static int gru_wait_idle_or_exception(struct gru_control_block_status *gen)
-{
- while (gen->istatus >= CBS_ACTIVE) {
- cpu_relax();
- barrier();
- }
- return gen->istatus;
-}
-
-static int gru_retry_exception(void *cb)
-{
- struct gru_control_block_status *gen = cb;
- struct control_block_extended_exc_detail excdet;
- int retry = EXCEPTION_RETRY_LIMIT;
-
- while (1) {
- if (gru_wait_idle_or_exception(gen) == CBS_IDLE)
- return CBS_IDLE;
- if (gru_get_cb_message_queue_substatus(cb))
- return CBS_EXCEPTION;
- gru_get_cb_exception_detail(cb, &excdet);
- if ((excdet.ecause & ~EXCEPTION_RETRY_BITS) ||
- (excdet.cbrexecstatus & CBR_EXS_ABORT_OCC))
- break;
- if (retry-- == 0)
- break;
- gen->icmd = 1;
- gru_flush_cache(gen);
- }
- return CBS_EXCEPTION;
-}
-
-int gru_check_status_proc(void *cb)
-{
- struct gru_control_block_status *gen = cb;
- int ret;
-
- ret = gen->istatus;
- if (ret == CBS_EXCEPTION)
- ret = gru_retry_exception(cb);
- rmb();
- return ret;
-
-}
-
-int gru_wait_proc(void *cb)
-{
- struct gru_control_block_status *gen = cb;
- int ret;
-
- ret = gru_wait_idle_or_exception(gen);
- if (ret == CBS_EXCEPTION)
- ret = gru_retry_exception(cb);
- rmb();
- return ret;
-}
-
-static void gru_abort(int ret, void *cb, char *str)
-{
- char buf[GRU_EXC_STR_SIZE];
-
- panic("GRU FATAL ERROR: %s - %s\n", str,
- gru_get_cb_exception_detail_str(ret, cb, buf, sizeof(buf)));
-}
-
-void gru_wait_abort_proc(void *cb)
-{
- int ret;
-
- ret = gru_wait_proc(cb);
- if (ret)
- gru_abort(ret, cb, "gru_wait_abort");
-}
-
-
-/*------------------------------ MESSAGE QUEUES -----------------------------*/
-
-/* Internal status . These are NOT returned to the user. */
-#define MQIE_AGAIN -1 /* try again */
-
-
-/*
- * Save/restore the "present" flag that is in the second line of 2-line
- * messages
- */
-static inline int get_present2(void *p)
-{
- struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES;
- return mhdr->present;
-}
-
-static inline void restore_present2(void *p, int val)
-{
- struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES;
- mhdr->present = val;
-}
-
-/*
- * Create a message queue.
- * qlines - message queue size in cache lines. Includes 2-line header.
- */
-int gru_create_message_queue(struct gru_message_queue_desc *mqd,
- void *p, unsigned int bytes, int nasid, int vector, int apicid)
-{
- struct message_queue *mq = p;
- unsigned int qlines;
-
- qlines = bytes / GRU_CACHE_LINE_BYTES - 2;
- memset(mq, 0, bytes);
- mq->start = &mq->data;
- mq->start2 = &mq->data + (qlines / 2 - 1) * GRU_CACHE_LINE_BYTES;
- mq->next = &mq->data;
- mq->limit = &mq->data + (qlines - 2) * GRU_CACHE_LINE_BYTES;
- mq->qlines = qlines;
- mq->hstatus[0] = 0;
- mq->hstatus[1] = 1;
- mq->head = gru_mesq_head(2, qlines / 2 + 1);
- mqd->mq = mq;
- mqd->mq_gpa = uv_gpa(mq);
- mqd->qlines = qlines;
- mqd->interrupt_pnode = nasid >> 1;
- mqd->interrupt_vector = vector;
- mqd->interrupt_apicid = apicid;
- return 0;
-}
-EXPORT_SYMBOL_GPL(gru_create_message_queue);
-
-/*
- * Send a NOOP message to a message queue
- * Returns:
- * 0 - if queue is full after the send. This is the normal case
- * but various races can change this.
- * -1 - if mesq sent successfully but queue not full
- * >0 - unexpected error. MQE_xxx returned
- */
-static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg)
-{
- const struct message_header noop_header = {
- .present = MQS_NOOP, .lines = 1};
- unsigned long m;
- int substatus, ret;
- struct message_header save_mhdr, *mhdr = mesg;
-
- STAT(mesq_noop);
- save_mhdr = *mhdr;
- *mhdr = noop_header;
- gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), 1, IMA);
- ret = gru_wait(cb);
-
- if (ret) {
- substatus = gru_get_cb_message_queue_substatus(cb);
- switch (substatus) {
- case CBSS_NO_ERROR:
- STAT(mesq_noop_unexpected_error);
- ret = MQE_UNEXPECTED_CB_ERR;
- break;
- case CBSS_LB_OVERFLOWED:
- STAT(mesq_noop_lb_overflow);
- ret = MQE_CONGESTION;
- break;
- case CBSS_QLIMIT_REACHED:
- STAT(mesq_noop_qlimit_reached);
- ret = 0;
- break;
- case CBSS_AMO_NACKED:
- STAT(mesq_noop_amo_nacked);
- ret = MQE_CONGESTION;
- break;
- case CBSS_PUT_NACKED:
- STAT(mesq_noop_put_nacked);
- m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
- gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, 1, 1,
- IMA);
- if (gru_wait(cb) == CBS_IDLE)
- ret = MQIE_AGAIN;
- else
- ret = MQE_UNEXPECTED_CB_ERR;
- break;
- case CBSS_PAGE_OVERFLOW:
- STAT(mesq_noop_page_overflow);
- fallthrough;
- default:
- BUG();
- }
- }
- *mhdr = save_mhdr;
- return ret;
-}
-
-/*
- * Handle a gru_mesq full.
- */
-static int send_message_queue_full(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg, int lines)
-{
- union gru_mesqhead mqh;
- unsigned int limit, head;
- unsigned long avalue;
- int half, qlines;
-
- /* Determine if switching to first/second half of q */
- avalue = gru_get_amo_value(cb);
- head = gru_get_amo_value_head(cb);
- limit = gru_get_amo_value_limit(cb);
-
- qlines = mqd->qlines;
- half = (limit != qlines);
-
- if (half)
- mqh = gru_mesq_head(qlines / 2 + 1, qlines);
- else
- mqh = gru_mesq_head(2, qlines / 2 + 1);
-
- /* Try to get lock for switching head pointer */
- gru_gamir(cb, EOP_IR_CLR, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
- if (!gru_get_amo_value(cb)) {
- STAT(mesq_qf_locked);
- return MQE_QUEUE_FULL;
- }
-
- /* Got the lock. Send optional NOP if queue not full, */
- if (head != limit) {
- if (send_noop_message(cb, mqd, mesg)) {
- gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half),
- XTYPE_DW, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
- STAT(mesq_qf_noop_not_full);
- return MQIE_AGAIN;
- }
- avalue++;
- }
-
- /* Then flip queuehead to other half of queue. */
- gru_gamer(cb, EOP_ERR_CSWAP, mqd->mq_gpa, XTYPE_DW, mqh.val, avalue,
- IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
-
- /* If not successfully in swapping queue head, clear the hstatus lock */
- if (gru_get_amo_value(cb) != avalue) {
- STAT(mesq_qf_switch_head_failed);
- gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), XTYPE_DW,
- IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
- }
- return MQIE_AGAIN;
-cberr:
- STAT(mesq_qf_unexpected_error);
- return MQE_UNEXPECTED_CB_ERR;
-}
-
-/*
- * Handle a PUT failure. Note: if message was a 2-line message, one of the
- * lines might have successfully have been written. Before sending the
- * message, "present" must be cleared in BOTH lines to prevent the receiver
- * from prematurely seeing the full message.
- */
-static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg, int lines)
-{
- unsigned long m;
- int ret, loops = 200; /* experimentally determined */
-
- m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
- if (lines == 2) {
- gru_vset(cb, m, 0, XTYPE_CL, lines, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- return MQE_UNEXPECTED_CB_ERR;
- }
- gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- return MQE_UNEXPECTED_CB_ERR;
-
- if (!mqd->interrupt_vector)
- return MQE_OK;
-
- /*
- * Send a noop message in order to deliver a cross-partition interrupt
- * to the SSI that contains the target message queue. Normally, the
- * interrupt is automatically delivered by hardware following mesq
- * operations, but some error conditions require explicit delivery.
- * The noop message will trigger delivery. Otherwise partition failures
- * could cause unrecovered errors.
- */
- do {
- ret = send_noop_message(cb, mqd, mesg);
- } while ((ret == MQIE_AGAIN || ret == MQE_CONGESTION) && (loops-- > 0));
-
- if (ret == MQIE_AGAIN || ret == MQE_CONGESTION) {
- /*
- * Don't indicate to the app to resend the message, as it's
- * already been successfully sent. We simply send an OK
- * (rather than fail the send with MQE_UNEXPECTED_CB_ERR),
- * assuming that the other side is receiving enough
- * interrupts to get this message processed anyway.
- */
- ret = MQE_OK;
- }
- return ret;
-}
-
-/*
- * Handle a gru_mesq failure. Some of these failures are software recoverable
- * or retryable.
- */
-static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg, int lines)
-{
- int substatus, ret = 0;
-
- substatus = gru_get_cb_message_queue_substatus(cb);
- switch (substatus) {
- case CBSS_NO_ERROR:
- STAT(mesq_send_unexpected_error);
- ret = MQE_UNEXPECTED_CB_ERR;
- break;
- case CBSS_LB_OVERFLOWED:
- STAT(mesq_send_lb_overflow);
- ret = MQE_CONGESTION;
- break;
- case CBSS_QLIMIT_REACHED:
- STAT(mesq_send_qlimit_reached);
- ret = send_message_queue_full(cb, mqd, mesg, lines);
- break;
- case CBSS_AMO_NACKED:
- STAT(mesq_send_amo_nacked);
- ret = MQE_CONGESTION;
- break;
- case CBSS_PUT_NACKED:
- STAT(mesq_send_put_nacked);
- ret = send_message_put_nacked(cb, mqd, mesg, lines);
- break;
- case CBSS_PAGE_OVERFLOW:
- STAT(mesq_page_overflow);
- fallthrough;
- default:
- BUG();
- }
- return ret;
-}
-
-/*
- * Send a message to a message queue
- * mqd message queue descriptor
- * mesg message. ust be vaddr within a GSEG
- * bytes message size (<= 2 CL)
- */
-int gru_send_message_gpa(struct gru_message_queue_desc *mqd, void *mesg,
- unsigned int bytes)
-{
- struct message_header *mhdr;
- void *cb;
- void *dsr;
- int istatus, clines, ret;
-
- STAT(mesq_send);
- BUG_ON(bytes < sizeof(int) || bytes > 2 * GRU_CACHE_LINE_BYTES);
-
- clines = DIV_ROUND_UP(bytes, GRU_CACHE_LINE_BYTES);
- if (gru_get_cpu_resources(bytes, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- memcpy(dsr, mesg, bytes);
- mhdr = dsr;
- mhdr->present = MQS_FULL;
- mhdr->lines = clines;
- if (clines == 2) {
- mhdr->present2 = get_present2(mhdr);
- restore_present2(mhdr, MQS_FULL);
- }
-
- do {
- ret = MQE_OK;
- gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), clines, IMA);
- istatus = gru_wait(cb);
- if (istatus != CBS_IDLE)
- ret = send_message_failure(cb, mqd, dsr, clines);
- } while (ret == MQIE_AGAIN);
- gru_free_cpu_resources(cb, dsr);
-
- if (ret)
- STAT(mesq_send_failed);
- return ret;
-}
-EXPORT_SYMBOL_GPL(gru_send_message_gpa);
-
-/*
- * Advance the receive pointer for the queue to the next message.
- */
-void gru_free_message(struct gru_message_queue_desc *mqd, void *mesg)
-{
- struct message_queue *mq = mqd->mq;
- struct message_header *mhdr = mq->next;
- void *next, *pnext;
- int half = -1;
- int lines = mhdr->lines;
-
- if (lines == 2)
- restore_present2(mhdr, MQS_EMPTY);
- mhdr->present = MQS_EMPTY;
-
- pnext = mq->next;
- next = pnext + GRU_CACHE_LINE_BYTES * lines;
- if (next == mq->limit) {
- next = mq->start;
- half = 1;
- } else if (pnext < mq->start2 && next >= mq->start2) {
- half = 0;
- }
-
- if (half >= 0)
- mq->hstatus[half] = 1;
- mq->next = next;
-}
-EXPORT_SYMBOL_GPL(gru_free_message);
-
-/*
- * Get next message from message queue. Return NULL if no message
- * present. User must call next_message() to move to next message.
- * rmq message queue
- */
-void *gru_get_next_message(struct gru_message_queue_desc *mqd)
-{
- struct message_queue *mq = mqd->mq;
- struct message_header *mhdr = mq->next;
- int present = mhdr->present;
-
- /* skip NOOP messages */
- while (present == MQS_NOOP) {
- gru_free_message(mqd, mhdr);
- mhdr = mq->next;
- present = mhdr->present;
- }
-
- /* Wait for both halves of 2 line messages */
- if (present == MQS_FULL && mhdr->lines == 2 &&
- get_present2(mhdr) == MQS_EMPTY)
- present = MQS_EMPTY;
-
- if (!present) {
- STAT(mesq_receive_none);
- return NULL;
- }
-
- if (mhdr->lines == 2)
- restore_present2(mhdr, mhdr->present2);
-
- STAT(mesq_receive);
- return mhdr;
-}
-EXPORT_SYMBOL_GPL(gru_get_next_message);
-
-/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/
-
-/*
- * Load a DW from a global GPA. The GPA can be a memory or MMR address.
- */
-int gru_read_gpa(unsigned long *value, unsigned long gpa)
-{
- void *cb;
- void *dsr;
- int ret, iaa;
-
- STAT(read_gpa);
- if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- iaa = gpa >> 62;
- gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA);
- ret = gru_wait(cb);
- if (ret == CBS_IDLE)
- *value = *(unsigned long *)dsr;
- gru_free_cpu_resources(cb, dsr);
- return ret;
-}
-EXPORT_SYMBOL_GPL(gru_read_gpa);
-
-
-/*
- * Copy a block of data using the GRU resources
- */
-int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa,
- unsigned int bytes)
-{
- void *cb;
- void *dsr;
- int ret;
-
- STAT(copy_gpa);
- if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- gru_bcopy(cb, src_gpa, dest_gpa, gru_get_tri(dsr),
- XTYPE_B, bytes, GRU_NUM_KERNEL_DSR_CL, IMA);
- ret = gru_wait(cb);
- gru_free_cpu_resources(cb, dsr);
- return ret;
-}
-EXPORT_SYMBOL_GPL(gru_copy_gpa);
-
-/* ------------------- KERNEL QUICKTESTS RUN AT STARTUP ----------------*/
-/* Temp - will delete after we gain confidence in the GRU */
-
-static int quicktest0(unsigned long arg)
-{
- unsigned long word0;
- unsigned long word1;
- void *cb;
- void *dsr;
- unsigned long *p;
- int ret = -EIO;
-
- if (gru_get_cpu_resources(GRU_CACHE_LINE_BYTES, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- p = dsr;
- word0 = MAGIC;
- word1 = 0;
-
- gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE) {
- printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id());
- goto done;
- }
-
- if (*p != MAGIC) {
- printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p);
- goto done;
- }
- gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE) {
- printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id());
- goto done;
- }
-
- if (word0 != word1 || word1 != MAGIC) {
- printk(KERN_DEBUG
- "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n",
- smp_processor_id(), word1, MAGIC);
- goto done;
- }
- ret = 0;
-
-done:
- gru_free_cpu_resources(cb, dsr);
- return ret;
-}
-
-#define ALIGNUP(p, q) ((void *)(((unsigned long)(p) + (q) - 1) & ~(q - 1)))
-
-static int quicktest1(unsigned long arg)
-{
- struct gru_message_queue_desc mqd;
- void *p, *mq;
- int i, ret = -EIO;
- char mes[GRU_CACHE_LINE_BYTES], *m;
-
- /* Need 1K cacheline aligned that does not cross page boundary */
- p = kmalloc(4096, 0);
- if (p == NULL)
- return -ENOMEM;
- mq = ALIGNUP(p, 1024);
- memset(mes, 0xee, sizeof(mes));
-
- gru_create_message_queue(&mqd, mq, 8 * GRU_CACHE_LINE_BYTES, 0, 0, 0);
- for (i = 0; i < 6; i++) {
- mes[8] = i;
- do {
- ret = gru_send_message_gpa(&mqd, mes, sizeof(mes));
- } while (ret == MQE_CONGESTION);
- if (ret)
- break;
- }
- if (ret != MQE_QUEUE_FULL || i != 4) {
- printk(KERN_DEBUG "GRU:%d quicktest1: unexpected status %d, i %d\n",
- smp_processor_id(), ret, i);
- goto done;
- }
-
- for (i = 0; i < 6; i++) {
- m = gru_get_next_message(&mqd);
- if (!m || m[8] != i)
- break;
- gru_free_message(&mqd, m);
- }
- if (i != 4) {
- printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n",
- smp_processor_id(), i, m, m ? m[8] : -1);
- goto done;
- }
- ret = 0;
-
-done:
- kfree(p);
- return ret;
-}
-
-static int quicktest2(unsigned long arg)
-{
- static DECLARE_COMPLETION(cmp);
- unsigned long han;
- int blade_id = 0;
- int numcb = 4;
- int ret = 0;
- unsigned long *buf;
- void *cb0, *cb;
- struct gru_control_block_status *gen;
- int i, k, istatus, bytes;
-
- bytes = numcb * 4 * 8;
- buf = kmalloc(bytes, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- ret = -EBUSY;
- han = gru_reserve_async_resources(blade_id, numcb, 0, &cmp);
- if (!han)
- goto done;
-
- gru_lock_async_resource(han, &cb0, NULL);
- memset(buf, 0xee, bytes);
- for (i = 0; i < numcb; i++)
- gru_vset(cb0 + i * GRU_HANDLE_STRIDE, uv_gpa(&buf[i * 4]), 0,
- XTYPE_DW, 4, 1, IMA_INTERRUPT);
-
- ret = 0;
- k = numcb;
- do {
- gru_wait_async_cbr(han);
- for (i = 0; i < numcb; i++) {
- cb = cb0 + i * GRU_HANDLE_STRIDE;
- istatus = gru_check_status(cb);
- if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS)
- break;
- }
- if (i == numcb)
- continue;
- if (istatus != CBS_IDLE) {
- printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i);
- ret = -EFAULT;
- } else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] ||
- buf[4 * i + 3]) {
- printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
- smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]);
- ret = -EIO;
- }
- k--;
- gen = cb;
- gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */
- } while (k);
- BUG_ON(cmp.done);
-
- gru_unlock_async_resource(han);
- gru_release_async_resources(han);
-done:
- kfree(buf);
- return ret;
-}
-
-#define BUFSIZE 200
-static int quicktest3(unsigned long arg)
-{
- char buf1[BUFSIZE], buf2[BUFSIZE];
- int ret = 0;
-
- memset(buf2, 0, sizeof(buf2));
- memset(buf1, get_cycles() & 255, sizeof(buf1));
- gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE);
- if (memcmp(buf1, buf2, BUFSIZE)) {
- printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id());
- ret = -EIO;
- }
- return ret;
-}
-
-/*
- * Debugging only. User hook for various kernel tests
- * of driver & gru.
- */
-int gru_ktest(unsigned long arg)
-{
- int ret = -EINVAL;
-
- switch (arg & 0xff) {
- case 0:
- ret = quicktest0(arg);
- break;
- case 1:
- ret = quicktest1(arg);
- break;
- case 2:
- ret = quicktest2(arg);
- break;
- case 3:
- ret = quicktest3(arg);
- break;
- case 99:
- ret = gru_free_kernel_contexts();
- break;
- }
- return ret;
-
-}
-
-int gru_kservices_init(void)
-{
- return 0;
-}
-
-void gru_kservices_exit(void)
-{
- if (gru_free_kernel_contexts())
- BUG();
-}
-
diff --git a/drivers/misc/sgi-gru/grukservices.h b/drivers/misc/sgi-gru/grukservices.h
deleted file mode 100644
index 510e45e9737e..000000000000
--- a/drivers/misc/sgi-gru/grukservices.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-#ifndef __GRU_KSERVICES_H_
-#define __GRU_KSERVICES_H_
-
-
-/*
- * Message queues using the GRU to send/receive messages.
- *
- * These function allow the user to create a message queue for
- * sending/receiving 1 or 2 cacheline messages using the GRU.
- *
- * Processes SENDING messages will use a kernel CBR/DSR to send
- * the message. This is transparent to the caller.
- *
- * The receiver does not use any GRU resources.
- *
- * The functions support:
- * - single receiver
- * - multiple senders
- * - cross partition message
- *
- * Missing features ZZZ:
- * - user options for dealing with timeouts, queue full, etc.
- * - gru_create_message_queue() needs interrupt vector info
- */
-
-struct gru_message_queue_desc {
- void *mq; /* message queue vaddress */
- unsigned long mq_gpa; /* global address of mq */
- int qlines; /* queue size in CL */
- int interrupt_vector; /* interrupt vector */
- int interrupt_pnode; /* pnode for interrupt */
- int interrupt_apicid; /* lapicid for interrupt */
-};
-
-/*
- * Initialize a user allocated chunk of memory to be used as
- * a message queue. The caller must ensure that the queue is
- * in contiguous physical memory and is cacheline aligned.
- *
- * Message queue size is the total number of bytes allocated
- * to the queue including a 2 cacheline header that is used
- * to manage the queue.
- *
- * Input:
- * mqd pointer to message queue descriptor
- * p pointer to user allocated mesq memory.
- * bytes size of message queue in bytes
- * vector interrupt vector (zero if no interrupts)
- * nasid nasid of blade where interrupt is delivered
- * apicid apicid of cpu for interrupt
- *
- * Errors:
- * 0 OK
- * >0 error
- */
-extern int gru_create_message_queue(struct gru_message_queue_desc *mqd,
- void *p, unsigned int bytes, int nasid, int vector, int apicid);
-
-/*
- * Send a message to a message queue.
- *
- * Note: The message queue transport mechanism uses the first 32
- * bits of the message. Users should avoid using these bits.
- *
- *
- * Input:
- * mqd pointer to message queue descriptor
- * mesg pointer to message. Must be 64-bit aligned
- * bytes size of message in bytes
- *
- * Output:
- * 0 message sent
- * >0 Send failure - see error codes below
- *
- */
-extern int gru_send_message_gpa(struct gru_message_queue_desc *mqd,
- void *mesg, unsigned int bytes);
-
-/* Status values for gru_send_message() */
-#define MQE_OK 0 /* message sent successfully */
-#define MQE_CONGESTION 1 /* temporary congestion, try again */
-#define MQE_QUEUE_FULL 2 /* queue is full */
-#define MQE_UNEXPECTED_CB_ERR 3 /* unexpected CB error */
-#define MQE_PAGE_OVERFLOW 10 /* BUG - queue overflowed a page */
-#define MQE_BUG_NO_RESOURCES 11 /* BUG - could not alloc GRU cb/dsr */
-
-/*
- * Advance the receive pointer for the message queue to the next message.
- * Note: current API requires messages to be gotten & freed in order. Future
- * API extensions may allow for out-of-order freeing.
- *
- * Input
- * mqd pointer to message queue descriptor
- * mesq message being freed
- */
-extern void gru_free_message(struct gru_message_queue_desc *mqd,
- void *mesq);
-
-/*
- * Get next message from message queue. Returns pointer to
- * message OR NULL if no message present.
- * User must call gru_free_message() after message is processed
- * in order to move the queue pointers to next message.
- *
- * Input
- * mqd pointer to message queue descriptor
- *
- * Output:
- * p pointer to message
- * NULL no message available
- */
-extern void *gru_get_next_message(struct gru_message_queue_desc *mqd);
-
-
-/*
- * Read a GRU global GPA. Source can be located in a remote partition.
- *
- * Input:
- * value memory address where MMR value is returned
- * gpa source numalink physical address of GPA
- *
- * Output:
- * 0 OK
- * >0 error
- */
-int gru_read_gpa(unsigned long *value, unsigned long gpa);
-
-
-/*
- * Copy data using the GRU. Source or destination can be located in a remote
- * partition.
- *
- * Input:
- * dest_gpa destination global physical address
- * src_gpa source global physical address
- * bytes number of bytes to copy
- *
- * Output:
- * 0 OK
- * >0 error
- */
-extern int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa,
- unsigned int bytes);
-
-/*
- * Reserve GRU resources to be used asynchronously.
- *
- * input:
- * blade_id - blade on which resources should be reserved
- * cbrs - number of CBRs
- * dsr_bytes - number of DSR bytes needed
- * cmp - completion structure for waiting for
- * async completions
- * output:
- * handle to identify resource
- * (0 = no resources)
- */
-extern unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes,
- struct completion *cmp);
-
-/*
- * Release async resources previously reserved.
- *
- * input:
- * han - handle to identify resources
- */
-extern void gru_release_async_resources(unsigned long han);
-
-/*
- * Wait for async GRU instructions to complete.
- *
- * input:
- * han - handle to identify resources
- */
-extern void gru_wait_async_cbr(unsigned long han);
-
-/*
- * Lock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- * output:
- * cb - pointer to first CBR
- * dsr - pointer to first DSR
- */
-extern void gru_lock_async_resource(unsigned long han, void **cb, void **dsr);
-
-/*
- * Unlock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- */
-extern void gru_unlock_async_resource(unsigned long han);
-
-#endif /* __GRU_KSERVICES_H_ */
diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h
deleted file mode 100644
index 85c103923632..000000000000
--- a/drivers/misc/sgi-gru/grulib.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser 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
- */
-
-#ifndef __GRULIB_H__
-#define __GRULIB_H__
-
-#define GRU_BASENAME "gru"
-#define GRU_FULLNAME "/dev/gru"
-#define GRU_IOCTL_NUM 'G'
-
-/*
- * Maximum number of GRU segments that a user can have open
- * ZZZ temp - set high for testing. Revisit.
- */
-#define GRU_MAX_OPEN_CONTEXTS 32
-
-/* Set Number of Request Blocks */
-#define GRU_CREATE_CONTEXT _IOWR(GRU_IOCTL_NUM, 1, void *)
-
-/* Set Context Options */
-#define GRU_SET_CONTEXT_OPTION _IOWR(GRU_IOCTL_NUM, 4, void *)
-
-/* Fetch exception detail */
-#define GRU_USER_GET_EXCEPTION_DETAIL _IOWR(GRU_IOCTL_NUM, 6, void *)
-
-/* For user call_os handling - normally a TLB fault */
-#define GRU_USER_CALL_OS _IOWR(GRU_IOCTL_NUM, 8, void *)
-
-/* For user unload context */
-#define GRU_USER_UNLOAD_CONTEXT _IOWR(GRU_IOCTL_NUM, 9, void *)
-
-/* For dumpping GRU chiplet state */
-#define GRU_DUMP_CHIPLET_STATE _IOWR(GRU_IOCTL_NUM, 11, void *)
-
-/* For getting gseg statistics */
-#define GRU_GET_GSEG_STATISTICS _IOWR(GRU_IOCTL_NUM, 12, void *)
-
-/* For user TLB flushing (primarily for tests) */
-#define GRU_USER_FLUSH_TLB _IOWR(GRU_IOCTL_NUM, 50, void *)
-
-/* Get some config options (primarily for tests & emulator) */
-#define GRU_GET_CONFIG_INFO _IOWR(GRU_IOCTL_NUM, 51, void *)
-
-/* Various kernel self-tests */
-#define GRU_KTEST _IOWR(GRU_IOCTL_NUM, 52, void *)
-
-#define CONTEXT_WINDOW_BYTES(th) (GRU_GSEG_PAGESIZE * (th))
-#define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th))
-#define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1)))
-
-struct gru_get_gseg_statistics_req {
- unsigned long gseg;
- struct gru_gseg_statistics stats;
-};
-
-/*
- * Structure used to pass TLB flush parameters to the driver
- */
-struct gru_create_context_req {
- unsigned long gseg;
- unsigned int data_segment_bytes;
- unsigned int control_blocks;
- unsigned int maximum_thread_count;
- unsigned int options;
- unsigned char tlb_preload_count;
-};
-
-/*
- * Structure used to pass unload context parameters to the driver
- */
-struct gru_unload_context_req {
- unsigned long gseg;
-};
-
-/*
- * Structure used to set context options
- */
-enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet};
-struct gru_set_context_option_req {
- unsigned long gseg;
- int op;
- int val0;
- long val1;
-};
-
-/*
- * Structure used to pass TLB flush parameters to the driver
- */
-struct gru_flush_tlb_req {
- unsigned long gseg;
- unsigned long vaddr;
- size_t len;
-};
-
-/*
- * Structure used to pass TLB flush parameters to the driver
- */
-enum {dcs_pid, dcs_gid};
-struct gru_dump_chiplet_state_req {
- unsigned int op;
- unsigned int gid;
- int ctxnum;
- char data_opt;
- char lock_cch;
- char flush_cbrs;
- char fill[10];
- pid_t pid;
- void *buf;
- size_t buflen;
- /* ---- output --- */
- unsigned int num_contexts;
-};
-
-#define GRU_DUMP_MAGIC 0x3474ab6c
-struct gru_dump_context_header {
- unsigned int magic;
- unsigned int gid;
- unsigned char ctxnum;
- unsigned char cbrcnt;
- unsigned char dsrcnt;
- pid_t pid;
- unsigned long vaddr;
- int cch_locked;
- unsigned long data[];
-};
-
-/*
- * GRU configuration info (temp - for testing)
- */
-struct gru_config_info {
- int cpus;
- int blades;
- int nodes;
- int chiplets;
- int fill[16];
-};
-
-#endif /* __GRULIB_H__ */
diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c
deleted file mode 100644
index 278b76cbd281..000000000000
--- a/drivers/misc/sgi-gru/grumain.c
+++ /dev/null
@@ -1,969 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * DRIVER TABLE MANAGER + GRU CONTEXT LOAD/UNLOAD
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/sched.h>
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/err.h>
-#include <linux/prefetch.h>
-#include <asm/uv/uv_hub.h>
-#include "gru.h"
-#include "grutables.h"
-#include "gruhandles.h"
-
-unsigned long gru_options __read_mostly;
-
-static struct device_driver gru_driver = {
- .name = "gru"
-};
-
-static struct device gru_device = {
- .init_name = "",
- .driver = &gru_driver,
-};
-
-struct device *grudev = &gru_device;
-
-/*
- * Select a gru fault map to be used by the current cpu. Note that
- * multiple cpus may be using the same map.
- * ZZZ should be inline but did not work on emulator
- */
-int gru_cpu_fault_map_id(void)
-{
- int cpu = smp_processor_id();
- int id, core;
-
- core = uv_cpu_core_number(cpu);
- id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
- return id;
-}
-
-/*--------- ASID Management -------------------------------------------
- *
- * Initially, assign asids sequentially from MIN_ASID .. MAX_ASID.
- * Once MAX is reached, flush the TLB & start over. However,
- * some asids may still be in use. There won't be many (percentage wise) still
- * in use. Search active contexts & determine the value of the first
- * asid in use ("x"s below). Set "limit" to this value.
- * This defines a block of assignable asids.
- *
- * When "limit" is reached, search forward from limit+1 and determine the
- * next block of assignable asids.
- *
- * Repeat until MAX_ASID is reached, then start over again.
- *
- * Each time MAX_ASID is reached, increment the asid generation. Since
- * the search for in-use asids only checks contexts with GRUs currently
- * assigned, asids in some contexts will be missed. Prior to loading
- * a context, the asid generation of the GTS asid is rechecked. If it
- * doesn't match the current generation, a new asid will be assigned.
- *
- * 0---------------x------------x---------------------x----|
- * ^-next ^-limit ^-MAX_ASID
- *
- * All asid manipulation & context loading/unloading is protected by the
- * gs_lock.
- */
-
-/* Hit the asid limit. Start over */
-static int gru_wrap_asid(struct gru_state *gru)
-{
- gru_dbg(grudev, "gid %d\n", gru->gs_gid);
- STAT(asid_wrap);
- gru->gs_asid_gen++;
- return MIN_ASID;
-}
-
-/* Find the next chunk of unused asids */
-static int gru_reset_asid_limit(struct gru_state *gru, int asid)
-{
- int i, gid, inuse_asid, limit;
-
- gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid);
- STAT(asid_next);
- limit = MAX_ASID;
- if (asid >= limit)
- asid = gru_wrap_asid(gru);
- gru_flush_all_tlb(gru);
- gid = gru->gs_gid;
-again:
- for (i = 0; i < GRU_NUM_CCH; i++) {
- if (!gru->gs_gts[i] || is_kernel_context(gru->gs_gts[i]))
- continue;
- inuse_asid = gru->gs_gts[i]->ts_gms->ms_asids[gid].mt_asid;
- gru_dbg(grudev, "gid %d, gts %p, gms %p, inuse 0x%x, cxt %d\n",
- gru->gs_gid, gru->gs_gts[i], gru->gs_gts[i]->ts_gms,
- inuse_asid, i);
- if (inuse_asid == asid) {
- asid += ASID_INC;
- if (asid >= limit) {
- /*
- * empty range: reset the range limit and
- * start over
- */
- limit = MAX_ASID;
- if (asid >= MAX_ASID)
- asid = gru_wrap_asid(gru);
- goto again;
- }
- }
-
- if ((inuse_asid > asid) && (inuse_asid < limit))
- limit = inuse_asid;
- }
- gru->gs_asid_limit = limit;
- gru->gs_asid = asid;
- gru_dbg(grudev, "gid %d, new asid 0x%x, new_limit 0x%x\n", gru->gs_gid,
- asid, limit);
- return asid;
-}
-
-/* Assign a new ASID to a thread context. */
-static int gru_assign_asid(struct gru_state *gru)
-{
- int asid;
-
- gru->gs_asid += ASID_INC;
- asid = gru->gs_asid;
- if (asid >= gru->gs_asid_limit)
- asid = gru_reset_asid_limit(gru, asid);
-
- gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid);
- return asid;
-}
-
-/*
- * Clear n bits in a word. Return a word indicating the bits that were cleared.
- * Optionally, build an array of chars that contain the bit numbers allocated.
- */
-static unsigned long reserve_resources(unsigned long *p, int n, int mmax,
- signed char *idx)
-{
- unsigned long bits = 0;
- int i;
-
- while (n--) {
- i = find_first_bit(p, mmax);
- if (i == mmax)
- BUG();
- __clear_bit(i, p);
- __set_bit(i, &bits);
- if (idx)
- *idx++ = i;
- }
- return bits;
-}
-
-unsigned long gru_reserve_cb_resources(struct gru_state *gru, int cbr_au_count,
- signed char *cbmap)
-{
- return reserve_resources(&gru->gs_cbr_map, cbr_au_count, GRU_CBR_AU,
- cbmap);
-}
-
-unsigned long gru_reserve_ds_resources(struct gru_state *gru, int dsr_au_count,
- signed char *dsmap)
-{
- return reserve_resources(&gru->gs_dsr_map, dsr_au_count, GRU_DSR_AU,
- dsmap);
-}
-
-static void reserve_gru_resources(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- gru->gs_active_contexts++;
- gts->ts_cbr_map =
- gru_reserve_cb_resources(gru, gts->ts_cbr_au_count,
- gts->ts_cbr_idx);
- gts->ts_dsr_map =
- gru_reserve_ds_resources(gru, gts->ts_dsr_au_count, NULL);
-}
-
-static void free_gru_resources(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- gru->gs_active_contexts--;
- gru->gs_cbr_map |= gts->ts_cbr_map;
- gru->gs_dsr_map |= gts->ts_dsr_map;
-}
-
-/*
- * Check if a GRU has sufficient free resources to satisfy an allocation
- * request. Note: GRU locks may or may not be held when this is called. If
- * not held, recheck after acquiring the appropriate locks.
- *
- * Returns 1 if sufficient resources, 0 if not
- */
-static int check_gru_resources(struct gru_state *gru, int cbr_au_count,
- int dsr_au_count, int max_active_contexts)
-{
- return hweight64(gru->gs_cbr_map) >= cbr_au_count
- && hweight64(gru->gs_dsr_map) >= dsr_au_count
- && gru->gs_active_contexts < max_active_contexts;
-}
-
-/*
- * TLB manangment requires tracking all GRU chiplets that have loaded a GSEG
- * context.
- */
-static int gru_load_mm_tracker(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- struct gru_mm_struct *gms = gts->ts_gms;
- struct gru_mm_tracker *asids = &gms->ms_asids[gru->gs_gid];
- unsigned short ctxbitmap = (1 << gts->ts_ctxnum);
- int asid;
-
- spin_lock(&gms->ms_asid_lock);
- asid = asids->mt_asid;
-
- spin_lock(&gru->gs_asid_lock);
- if (asid == 0 || (asids->mt_ctxbitmap == 0 && asids->mt_asid_gen !=
- gru->gs_asid_gen)) {
- asid = gru_assign_asid(gru);
- asids->mt_asid = asid;
- asids->mt_asid_gen = gru->gs_asid_gen;
- STAT(asid_new);
- } else {
- STAT(asid_reuse);
- }
- spin_unlock(&gru->gs_asid_lock);
-
- BUG_ON(asids->mt_ctxbitmap & ctxbitmap);
- asids->mt_ctxbitmap |= ctxbitmap;
- if (!test_bit(gru->gs_gid, gms->ms_asidmap))
- __set_bit(gru->gs_gid, gms->ms_asidmap);
- spin_unlock(&gms->ms_asid_lock);
-
- gru_dbg(grudev,
- "gid %d, gts %p, gms %p, ctxnum %d, asid 0x%x, asidmap 0x%lx\n",
- gru->gs_gid, gts, gms, gts->ts_ctxnum, asid,
- gms->ms_asidmap[0]);
- return asid;
-}
-
-static void gru_unload_mm_tracker(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- struct gru_mm_struct *gms = gts->ts_gms;
- struct gru_mm_tracker *asids;
- unsigned short ctxbitmap;
-
- asids = &gms->ms_asids[gru->gs_gid];
- ctxbitmap = (1 << gts->ts_ctxnum);
- spin_lock(&gms->ms_asid_lock);
- spin_lock(&gru->gs_asid_lock);
- BUG_ON((asids->mt_ctxbitmap & ctxbitmap) != ctxbitmap);
- asids->mt_ctxbitmap ^= ctxbitmap;
- gru_dbg(grudev, "gid %d, gts %p, gms %p, ctxnum %d, asidmap 0x%lx\n",
- gru->gs_gid, gts, gms, gts->ts_ctxnum, gms->ms_asidmap[0]);
- spin_unlock(&gru->gs_asid_lock);
- spin_unlock(&gms->ms_asid_lock);
-}
-
-/*
- * Decrement the reference count on a GTS structure. Free the structure
- * if the reference count goes to zero.
- */
-void gts_drop(struct gru_thread_state *gts)
-{
- if (gts && refcount_dec_and_test(&gts->ts_refcnt)) {
- if (gts->ts_gms)
- gru_drop_mmu_notifier(gts->ts_gms);
- kfree(gts);
- STAT(gts_free);
- }
-}
-
-/*
- * Locate the GTS structure for the current thread.
- */
-static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data
- *vdata, int tsid)
-{
- struct gru_thread_state *gts;
-
- list_for_each_entry(gts, &vdata->vd_head, ts_next)
- if (gts->ts_tsid == tsid)
- return gts;
- return NULL;
-}
-
-/*
- * Allocate a thread state structure.
- */
-struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
- int cbr_au_count, int dsr_au_count,
- unsigned char tlb_preload_count, int options, int tsid)
-{
- struct gru_thread_state *gts;
- struct gru_mm_struct *gms;
- int bytes;
-
- bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count);
- bytes += sizeof(struct gru_thread_state);
- gts = kmalloc(bytes, GFP_KERNEL);
- if (!gts)
- return ERR_PTR(-ENOMEM);
-
- STAT(gts_alloc);
- memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */
- refcount_set(&gts->ts_refcnt, 1);
- mutex_init(&gts->ts_ctxlock);
- gts->ts_cbr_au_count = cbr_au_count;
- gts->ts_dsr_au_count = dsr_au_count;
- gts->ts_tlb_preload_count = tlb_preload_count;
- gts->ts_user_options = options;
- gts->ts_user_blade_id = -1;
- gts->ts_user_chiplet_id = -1;
- gts->ts_tsid = tsid;
- gts->ts_ctxnum = NULLCTX;
- gts->ts_tlb_int_select = -1;
- gts->ts_cch_req_slice = -1;
- gts->ts_sizeavail = GRU_SIZEAVAIL(PAGE_SHIFT);
- if (vma) {
- gts->ts_mm = current->mm;
- gts->ts_vma = vma;
- gms = gru_register_mmu_notifier();
- if (IS_ERR(gms))
- goto err;
- gts->ts_gms = gms;
- }
-
- gru_dbg(grudev, "alloc gts %p\n", gts);
- return gts;
-
-err:
- gts_drop(gts);
- return ERR_CAST(gms);
-}
-
-/*
- * Allocate a vma private data structure.
- */
-struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid)
-{
- struct gru_vma_data *vdata = NULL;
-
- vdata = kmalloc_obj(*vdata);
- if (!vdata)
- return NULL;
-
- STAT(vdata_alloc);
- INIT_LIST_HEAD(&vdata->vd_head);
- spin_lock_init(&vdata->vd_lock);
- gru_dbg(grudev, "alloc vdata %p\n", vdata);
- return vdata;
-}
-
-/*
- * Find the thread state structure for the current thread.
- */
-struct gru_thread_state *gru_find_thread_state(struct vm_area_struct *vma,
- int tsid)
-{
- struct gru_vma_data *vdata = vma->vm_private_data;
- struct gru_thread_state *gts;
-
- spin_lock(&vdata->vd_lock);
- gts = gru_find_current_gts_nolock(vdata, tsid);
- spin_unlock(&vdata->vd_lock);
- gru_dbg(grudev, "vma %p, gts %p\n", vma, gts);
- return gts;
-}
-
-/*
- * Allocate a new thread state for a GSEG. Note that races may allow
- * another thread to race to create a gts.
- */
-struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma,
- int tsid)
-{
- struct gru_vma_data *vdata = vma->vm_private_data;
- struct gru_thread_state *gts, *ngts;
-
- gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count,
- vdata->vd_dsr_au_count,
- vdata->vd_tlb_preload_count,
- vdata->vd_user_options, tsid);
- if (IS_ERR(gts))
- return gts;
-
- spin_lock(&vdata->vd_lock);
- ngts = gru_find_current_gts_nolock(vdata, tsid);
- if (ngts) {
- gts_drop(gts);
- gts = ngts;
- STAT(gts_double_allocate);
- } else {
- list_add(&gts->ts_next, &vdata->vd_head);
- }
- spin_unlock(&vdata->vd_lock);
- gru_dbg(grudev, "vma %p, gts %p\n", vma, gts);
- return gts;
-}
-
-/*
- * Free the GRU context assigned to the thread state.
- */
-static void gru_free_gru_context(struct gru_thread_state *gts)
-{
- struct gru_state *gru;
-
- gru = gts->ts_gru;
- gru_dbg(grudev, "gts %p, gid %d\n", gts, gru->gs_gid);
-
- spin_lock(&gru->gs_lock);
- gru->gs_gts[gts->ts_ctxnum] = NULL;
- free_gru_resources(gru, gts);
- BUG_ON(test_bit(gts->ts_ctxnum, &gru->gs_context_map) == 0);
- __clear_bit(gts->ts_ctxnum, &gru->gs_context_map);
- gts->ts_ctxnum = NULLCTX;
- gts->ts_gru = NULL;
- gts->ts_blade = -1;
- spin_unlock(&gru->gs_lock);
-
- gts_drop(gts);
- STAT(free_context);
-}
-
-/*
- * Prefetching cachelines help hardware performance.
- * (Strictly a performance enhancement. Not functionally required).
- */
-static void prefetch_data(void *p, int num, int stride)
-{
- while (num-- > 0) {
- prefetchw(p);
- p += stride;
- }
-}
-
-static inline long gru_copy_handle(void *d, void *s)
-{
- memcpy(d, s, GRU_HANDLE_BYTES);
- return GRU_HANDLE_BYTES;
-}
-
-static void gru_prefetch_context(void *gseg, void *cb, void *cbe,
- unsigned long cbrmap, unsigned long length)
-{
- int i, scr;
-
- prefetch_data(gseg + GRU_DS_BASE, length / GRU_CACHE_LINE_BYTES,
- GRU_CACHE_LINE_BYTES);
-
- for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
- prefetch_data(cb, 1, GRU_CACHE_LINE_BYTES);
- prefetch_data(cbe + i * GRU_HANDLE_STRIDE, 1,
- GRU_CACHE_LINE_BYTES);
- cb += GRU_HANDLE_STRIDE;
- }
-}
-
-static void gru_load_context_data(void *save, void *grubase, int ctxnum,
- unsigned long cbrmap, unsigned long dsrmap,
- int data_valid)
-{
- void *gseg, *cb, *cbe;
- unsigned long length;
- int i, scr;
-
- gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
- cb = gseg + GRU_CB_BASE;
- cbe = grubase + GRU_CBE_BASE;
- length = hweight64(dsrmap) * GRU_DSR_AU_BYTES;
- gru_prefetch_context(gseg, cb, cbe, cbrmap, length);
-
- for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
- if (data_valid) {
- save += gru_copy_handle(cb, save);
- save += gru_copy_handle(cbe + i * GRU_HANDLE_STRIDE,
- save);
- } else {
- memset(cb, 0, GRU_CACHE_LINE_BYTES);
- memset(cbe + i * GRU_HANDLE_STRIDE, 0,
- GRU_CACHE_LINE_BYTES);
- }
- /* Flush CBE to hide race in context restart */
- mb();
- gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
- cb += GRU_HANDLE_STRIDE;
- }
-
- if (data_valid)
- memcpy(gseg + GRU_DS_BASE, save, length);
- else
- memset(gseg + GRU_DS_BASE, 0, length);
-}
-
-static void gru_unload_context_data(void *save, void *grubase, int ctxnum,
- unsigned long cbrmap, unsigned long dsrmap)
-{
- void *gseg, *cb, *cbe;
- unsigned long length;
- int i, scr;
-
- gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
- cb = gseg + GRU_CB_BASE;
- cbe = grubase + GRU_CBE_BASE;
- length = hweight64(dsrmap) * GRU_DSR_AU_BYTES;
-
- /* CBEs may not be coherent. Flush them from cache */
- for_each_cbr_in_allocation_map(i, &cbrmap, scr)
- gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
- mb(); /* Let the CL flush complete */
-
- gru_prefetch_context(gseg, cb, cbe, cbrmap, length);
-
- for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
- save += gru_copy_handle(save, cb);
- save += gru_copy_handle(save, cbe + i * GRU_HANDLE_STRIDE);
- cb += GRU_HANDLE_STRIDE;
- }
- memcpy(save, gseg + GRU_DS_BASE, length);
-}
-
-void gru_unload_context(struct gru_thread_state *gts, int savestate)
-{
- struct gru_state *gru = gts->ts_gru;
- struct gru_context_configuration_handle *cch;
- int ctxnum = gts->ts_ctxnum;
-
- if (!is_kernel_context(gts))
- zap_special_vma_range(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE);
- cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
-
- gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n",
- gts, gts->ts_cbr_map, gts->ts_dsr_map);
- lock_cch_handle(cch);
- if (cch_interrupt_sync(cch))
- BUG();
-
- if (!is_kernel_context(gts))
- gru_unload_mm_tracker(gru, gts);
- if (savestate) {
- gru_unload_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr,
- ctxnum, gts->ts_cbr_map,
- gts->ts_dsr_map);
- gts->ts_data_valid = 1;
- }
-
- if (cch_deallocate(cch))
- BUG();
- unlock_cch_handle(cch);
-
- gru_free_gru_context(gts);
-}
-
-/*
- * Load a GRU context by copying it from the thread data structure in memory
- * to the GRU.
- */
-void gru_load_context(struct gru_thread_state *gts)
-{
- struct gru_state *gru = gts->ts_gru;
- struct gru_context_configuration_handle *cch;
- int i, err, asid, ctxnum = gts->ts_ctxnum;
-
- cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
- lock_cch_handle(cch);
- cch->tfm_fault_bit_enable =
- (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
- || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
- cch->tlb_int_enable = (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
- if (cch->tlb_int_enable) {
- gts->ts_tlb_int_select = gru_cpu_fault_map_id();
- cch->tlb_int_select = gts->ts_tlb_int_select;
- }
- if (gts->ts_cch_req_slice >= 0) {
- cch->req_slice_set_enable = 1;
- cch->req_slice = gts->ts_cch_req_slice;
- } else {
- cch->req_slice_set_enable =0;
- }
- cch->tfm_done_bit_enable = 0;
- cch->dsr_allocation_map = gts->ts_dsr_map;
- cch->cbr_allocation_map = gts->ts_cbr_map;
-
- if (is_kernel_context(gts)) {
- cch->unmap_enable = 1;
- cch->tfm_done_bit_enable = 1;
- cch->cb_int_enable = 1;
- cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */
- } else {
- cch->unmap_enable = 0;
- cch->tfm_done_bit_enable = 0;
- cch->cb_int_enable = 0;
- asid = gru_load_mm_tracker(gru, gts);
- for (i = 0; i < 8; i++) {
- cch->asid[i] = asid + i;
- cch->sizeavail[i] = gts->ts_sizeavail;
- }
- }
-
- err = cch_allocate(cch);
- if (err) {
- gru_dbg(grudev,
- "err %d: cch %p, gts %p, cbr 0x%lx, dsr 0x%lx\n",
- err, cch, gts, gts->ts_cbr_map, gts->ts_dsr_map);
- BUG();
- }
-
- gru_load_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, ctxnum,
- gts->ts_cbr_map, gts->ts_dsr_map, gts->ts_data_valid);
-
- if (cch_start(cch))
- BUG();
- unlock_cch_handle(cch);
-
- gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n",
- gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map,
- (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select);
-}
-
-/*
- * Update fields in an active CCH:
- * - retarget interrupts on local blade
- * - update sizeavail mask
- */
-int gru_update_cch(struct gru_thread_state *gts)
-{
- struct gru_context_configuration_handle *cch;
- struct gru_state *gru = gts->ts_gru;
- int i, ctxnum = gts->ts_ctxnum, ret = 0;
-
- cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
-
- lock_cch_handle(cch);
- if (cch->state == CCHSTATE_ACTIVE) {
- if (gru->gs_gts[gts->ts_ctxnum] != gts)
- goto exit;
- if (cch_interrupt(cch))
- BUG();
- for (i = 0; i < 8; i++)
- cch->sizeavail[i] = gts->ts_sizeavail;
- gts->ts_tlb_int_select = gru_cpu_fault_map_id();
- cch->tlb_int_select = gru_cpu_fault_map_id();
- cch->tfm_fault_bit_enable =
- (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
- || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
- if (cch_start(cch))
- BUG();
- ret = 1;
- }
-exit:
- unlock_cch_handle(cch);
- return ret;
-}
-
-/*
- * Update CCH tlb interrupt select. Required when all the following is true:
- * - task's GRU context is loaded into a GRU
- * - task is using interrupt notification for TLB faults
- * - task has migrated to a different cpu on the same blade where
- * it was previously running.
- */
-static int gru_retarget_intr(struct gru_thread_state *gts)
-{
- if (gts->ts_tlb_int_select < 0
- || gts->ts_tlb_int_select == gru_cpu_fault_map_id())
- return 0;
-
- gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select,
- gru_cpu_fault_map_id());
- return gru_update_cch(gts);
-}
-
-/*
- * Check if a GRU context is allowed to use a specific chiplet. By default
- * a context is assigned to any blade-local chiplet. However, users can
- * override this.
- * Returns 1 if assignment allowed, 0 otherwise
- */
-static int gru_check_chiplet_assignment(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- int blade_id;
- int chiplet_id;
-
- blade_id = gts->ts_user_blade_id;
- if (blade_id < 0)
- blade_id = uv_numa_blade_id();
-
- chiplet_id = gts->ts_user_chiplet_id;
- return gru->gs_blade_id == blade_id &&
- (chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id);
-}
-
-/*
- * Unload the gru context if it is not assigned to the correct blade or
- * chiplet. Misassignment can occur if the process migrates to a different
- * blade or if the user changes the selected blade/chiplet.
- */
-int gru_check_context_placement(struct gru_thread_state *gts)
-{
- struct gru_state *gru;
- int ret = 0;
-
- /*
- * If the current task is the context owner, verify that the
- * context is correctly placed. This test is skipped for non-owner
- * references. Pthread apps use non-owner references to the CBRs.
- */
- gru = gts->ts_gru;
- /*
- * If gru or gts->ts_tgid_owner isn't initialized properly, return
- * success to indicate that the caller does not need to unload the
- * gru context.The caller is responsible for their inspection and
- * reinitialization if needed.
- */
- if (!gru || gts->ts_tgid_owner != current->tgid)
- return ret;
-
- if (!gru_check_chiplet_assignment(gru, gts)) {
- STAT(check_context_unload);
- ret = -EINVAL;
- } else if (gru_retarget_intr(gts)) {
- STAT(check_context_retarget_intr);
- }
-
- return ret;
-}
-
-
-/*
- * Insufficient GRU resources available on the local blade. Steal a context from
- * a process. This is a hack until a _real_ resource scheduler is written....
- */
-#define next_ctxnum(n) ((n) < GRU_NUM_CCH - 2 ? (n) + 1 : 0)
-#define next_gru(b, g) (((g) < &(b)->bs_grus[GRU_CHIPLETS_PER_BLADE - 1]) ? \
- ((g)+1) : &(b)->bs_grus[0])
-
-static int is_gts_stealable(struct gru_thread_state *gts,
- struct gru_blade_state *bs)
-{
- if (is_kernel_context(gts))
- return down_write_trylock(&bs->bs_kgts_sema);
- else
- return mutex_trylock(&gts->ts_ctxlock);
-}
-
-static void gts_stolen(struct gru_thread_state *gts,
- struct gru_blade_state *bs)
-{
- if (is_kernel_context(gts)) {
- up_write(&bs->bs_kgts_sema);
- STAT(steal_kernel_context);
- } else {
- mutex_unlock(&gts->ts_ctxlock);
- STAT(steal_user_context);
- }
-}
-
-void gru_steal_context(struct gru_thread_state *gts)
-{
- struct gru_blade_state *blade;
- struct gru_state *gru, *gru0;
- struct gru_thread_state *ngts = NULL;
- int ctxnum, ctxnum0, flag = 0, cbr, dsr;
- int blade_id;
-
- blade_id = gts->ts_user_blade_id;
- if (blade_id < 0)
- blade_id = uv_numa_blade_id();
- cbr = gts->ts_cbr_au_count;
- dsr = gts->ts_dsr_au_count;
-
- blade = gru_base[blade_id];
- spin_lock(&blade->bs_lock);
-
- ctxnum = next_ctxnum(blade->bs_lru_ctxnum);
- gru = blade->bs_lru_gru;
- if (ctxnum == 0)
- gru = next_gru(blade, gru);
- blade->bs_lru_gru = gru;
- blade->bs_lru_ctxnum = ctxnum;
- ctxnum0 = ctxnum;
- gru0 = gru;
- while (1) {
- if (gru_check_chiplet_assignment(gru, gts)) {
- if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH))
- break;
- spin_lock(&gru->gs_lock);
- for (; ctxnum < GRU_NUM_CCH; ctxnum++) {
- if (flag && gru == gru0 && ctxnum == ctxnum0)
- break;
- ngts = gru->gs_gts[ctxnum];
- /*
- * We are grabbing locks out of order, so trylock is
- * needed. GTSs are usually not locked, so the odds of
- * success are high. If trylock fails, try to steal a
- * different GSEG.
- */
- if (ngts && is_gts_stealable(ngts, blade))
- break;
- ngts = NULL;
- }
- spin_unlock(&gru->gs_lock);
- if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0))
- break;
- }
- if (flag && gru == gru0)
- break;
- flag = 1;
- ctxnum = 0;
- gru = next_gru(blade, gru);
- }
- spin_unlock(&blade->bs_lock);
-
- if (ngts) {
- gts->ustats.context_stolen++;
- ngts->ts_steal_jiffies = jiffies;
- gru_unload_context(ngts, is_kernel_context(ngts) ? 0 : 1);
- gts_stolen(ngts, blade);
- } else {
- STAT(steal_context_failed);
- }
- gru_dbg(grudev,
- "stole gid %d, ctxnum %d from gts %p. Need cb %d, ds %d;"
- " avail cb %ld, ds %ld\n",
- gru->gs_gid, ctxnum, ngts, cbr, dsr, hweight64(gru->gs_cbr_map),
- hweight64(gru->gs_dsr_map));
-}
-
-/*
- * Assign a gru context.
- */
-static int gru_assign_context_number(struct gru_state *gru)
-{
- int ctxnum;
-
- ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH);
- __set_bit(ctxnum, &gru->gs_context_map);
- return ctxnum;
-}
-
-/*
- * Scan the GRUs on the local blade & assign a GRU context.
- */
-struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts)
-{
- struct gru_state *gru, *grux;
- int i, max_active_contexts;
- int blade_id = gts->ts_user_blade_id;
-
- if (blade_id < 0)
- blade_id = uv_numa_blade_id();
-again:
- gru = NULL;
- max_active_contexts = GRU_NUM_CCH;
- for_each_gru_on_blade(grux, blade_id, i) {
- if (!gru_check_chiplet_assignment(grux, gts))
- continue;
- if (check_gru_resources(grux, gts->ts_cbr_au_count,
- gts->ts_dsr_au_count,
- max_active_contexts)) {
- gru = grux;
- max_active_contexts = grux->gs_active_contexts;
- if (max_active_contexts == 0)
- break;
- }
- }
-
- if (gru) {
- spin_lock(&gru->gs_lock);
- if (!check_gru_resources(gru, gts->ts_cbr_au_count,
- gts->ts_dsr_au_count, GRU_NUM_CCH)) {
- spin_unlock(&gru->gs_lock);
- goto again;
- }
- reserve_gru_resources(gru, gts);
- gts->ts_gru = gru;
- gts->ts_blade = gru->gs_blade_id;
- gts->ts_ctxnum = gru_assign_context_number(gru);
- refcount_inc(&gts->ts_refcnt);
- gru->gs_gts[gts->ts_ctxnum] = gts;
- spin_unlock(&gru->gs_lock);
-
- STAT(assign_context);
- gru_dbg(grudev,
- "gseg %p, gts %p, gid %d, ctx %d, cbr %d, dsr %d\n",
- gseg_virtual_address(gts->ts_gru, gts->ts_ctxnum), gts,
- gts->ts_gru->gs_gid, gts->ts_ctxnum,
- gts->ts_cbr_au_count, gts->ts_dsr_au_count);
- } else {
- gru_dbg(grudev, "failed to allocate a GTS %s\n", "");
- STAT(assign_context_failed);
- }
-
- return gru;
-}
-
-/*
- * gru_nopage
- *
- * Map the user's GRU segment
- *
- * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries.
- */
-vm_fault_t gru_fault(struct vm_fault *vmf)
-{
- struct vm_area_struct *vma = vmf->vma;
- struct gru_thread_state *gts;
- unsigned long paddr, vaddr;
- unsigned long expires;
-
- vaddr = vmf->address;
- gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n",
- vma, vaddr, GSEG_BASE(vaddr));
- STAT(nopfn);
-
- /* The following check ensures vaddr is a valid address in the VMA */
- gts = gru_find_thread_state(vma, TSID(vaddr, vma));
- if (!gts)
- return VM_FAULT_SIGBUS;
-
-again:
- mutex_lock(&gts->ts_ctxlock);
-
- if (gru_check_context_placement(gts)) {
- mutex_unlock(&gts->ts_ctxlock);
- gru_unload_context(gts, 1);
- return VM_FAULT_NOPAGE;
- }
-
- if (!gts->ts_gru) {
- STAT(load_user_context);
- if (!gru_assign_gru_context(gts)) {
- mutex_unlock(&gts->ts_ctxlock);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */
- expires = gts->ts_steal_jiffies + GRU_STEAL_DELAY;
- if (time_before(expires, jiffies))
- gru_steal_context(gts);
- goto again;
- }
- gru_load_context(gts);
- paddr = gseg_physical_address(gts->ts_gru, gts->ts_ctxnum);
- remap_pfn_range(vma, vaddr & ~(GRU_GSEG_PAGESIZE - 1),
- paddr >> PAGE_SHIFT, GRU_GSEG_PAGESIZE,
- vma->vm_page_prot);
- }
-
- mutex_unlock(&gts->ts_ctxlock);
-
- return VM_FAULT_NOPAGE;
-}
-
diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c
deleted file mode 100644
index 97b8b38ab47d..000000000000
--- a/drivers/misc/sgi-gru/gruprocfs.c
+++ /dev/null
@@ -1,308 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * PROC INTERFACES
- *
- * This file supports the /proc interfaces for the GRU driver
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/proc_fs.h>
-#include <linux/device.h>
-#include <linux/seq_file.h>
-#include <linux/uaccess.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-
-#define printstat(s, f) printstat_val(s, &gru_stats.f, #f)
-
-static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id)
-{
- unsigned long val = atomic_long_read(v);
-
- seq_printf(s, "%16lu %s\n", val, id);
-}
-
-static int statistics_show(struct seq_file *s, void *p)
-{
- printstat(s, vdata_alloc);
- printstat(s, vdata_free);
- printstat(s, gts_alloc);
- printstat(s, gts_free);
- printstat(s, gms_alloc);
- printstat(s, gms_free);
- printstat(s, gts_double_allocate);
- printstat(s, assign_context);
- printstat(s, assign_context_failed);
- printstat(s, free_context);
- printstat(s, load_user_context);
- printstat(s, load_kernel_context);
- printstat(s, lock_kernel_context);
- printstat(s, unlock_kernel_context);
- printstat(s, steal_user_context);
- printstat(s, steal_kernel_context);
- printstat(s, steal_context_failed);
- printstat(s, nopfn);
- printstat(s, asid_new);
- printstat(s, asid_next);
- printstat(s, asid_wrap);
- printstat(s, asid_reuse);
- printstat(s, intr);
- printstat(s, intr_cbr);
- printstat(s, intr_tfh);
- printstat(s, intr_spurious);
- printstat(s, intr_mm_lock_failed);
- printstat(s, call_os);
- printstat(s, call_os_wait_queue);
- printstat(s, user_flush_tlb);
- printstat(s, user_unload_context);
- printstat(s, user_exception);
- printstat(s, set_context_option);
- printstat(s, check_context_retarget_intr);
- printstat(s, check_context_unload);
- printstat(s, tlb_dropin);
- printstat(s, tlb_preload_page);
- printstat(s, tlb_dropin_fail_no_asid);
- printstat(s, tlb_dropin_fail_upm);
- printstat(s, tlb_dropin_fail_invalid);
- printstat(s, tlb_dropin_fail_range_active);
- printstat(s, tlb_dropin_fail_idle);
- printstat(s, tlb_dropin_fail_fmm);
- printstat(s, tlb_dropin_fail_no_exception);
- printstat(s, tfh_stale_on_fault);
- printstat(s, mmu_invalidate_range);
- printstat(s, mmu_invalidate_page);
- printstat(s, flush_tlb);
- printstat(s, flush_tlb_gru);
- printstat(s, flush_tlb_gru_tgh);
- printstat(s, flush_tlb_gru_zero_asid);
- printstat(s, copy_gpa);
- printstat(s, read_gpa);
- printstat(s, mesq_receive);
- printstat(s, mesq_receive_none);
- printstat(s, mesq_send);
- printstat(s, mesq_send_failed);
- printstat(s, mesq_noop);
- printstat(s, mesq_send_unexpected_error);
- printstat(s, mesq_send_lb_overflow);
- printstat(s, mesq_send_qlimit_reached);
- printstat(s, mesq_send_amo_nacked);
- printstat(s, mesq_send_put_nacked);
- printstat(s, mesq_qf_locked);
- printstat(s, mesq_qf_noop_not_full);
- printstat(s, mesq_qf_switch_head_failed);
- printstat(s, mesq_qf_unexpected_error);
- printstat(s, mesq_noop_unexpected_error);
- printstat(s, mesq_noop_lb_overflow);
- printstat(s, mesq_noop_qlimit_reached);
- printstat(s, mesq_noop_amo_nacked);
- printstat(s, mesq_noop_put_nacked);
- printstat(s, mesq_noop_page_overflow);
- return 0;
-}
-
-static ssize_t statistics_write(struct file *file, const char __user *userbuf,
- size_t count, loff_t *data)
-{
- memset(&gru_stats, 0, sizeof(gru_stats));
- return count;
-}
-
-static int mcs_statistics_show(struct seq_file *s, void *p)
-{
- int op;
- unsigned long total, count, max;
- static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt",
- "cch_interrupt_sync", "cch_deallocate", "tfh_write_only",
- "tfh_write_restart", "tgh_invalidate"};
-
- seq_puts(s, "#id count aver-clks max-clks\n");
- for (op = 0; op < mcsop_last; op++) {
- count = atomic_long_read(&mcs_op_statistics[op].count);
- total = atomic_long_read(&mcs_op_statistics[op].total);
- max = mcs_op_statistics[op].max;
- seq_printf(s, "%-20s%12ld%12ld%12ld\n", id[op], count,
- count ? total / count : 0, max);
- }
- return 0;
-}
-
-static ssize_t mcs_statistics_write(struct file *file,
- const char __user *userbuf, size_t count, loff_t *data)
-{
- memset(mcs_op_statistics, 0, sizeof(mcs_op_statistics));
- return count;
-}
-
-static int options_show(struct seq_file *s, void *p)
-{
- seq_printf(s, "#bitmask: 1=trace, 2=statistics\n");
- seq_printf(s, "0x%lx\n", gru_options);
- return 0;
-}
-
-static ssize_t options_write(struct file *file, const char __user *userbuf,
- size_t count, loff_t *data)
-{
- int ret;
-
- ret = kstrtoul_from_user(userbuf, count, 0, &gru_options);
- if (ret)
- return ret;
-
- return count;
-}
-
-static int cch_seq_show(struct seq_file *file, void *data)
-{
- long gid = *(long *)data;
- int i;
- struct gru_state *gru = GID_TO_GRU(gid);
- struct gru_thread_state *ts;
- const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" };
-
- if (gid == 0)
- seq_puts(file, "# gid bid ctx# asid pid cbrs dsbytes mode\n");
- if (gru)
- for (i = 0; i < GRU_NUM_CCH; i++) {
- ts = gru->gs_gts[i];
- if (!ts)
- continue;
- seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n",
- gru->gs_gid, gru->gs_blade_id, i,
- is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid,
- is_kernel_context(ts) ? 0 : ts->ts_tgid_owner,
- ts->ts_cbr_au_count * GRU_CBR_AU_SIZE,
- ts->ts_cbr_au_count * GRU_DSR_AU_BYTES,
- mode[ts->ts_user_options &
- GRU_OPT_MISS_MASK]);
- }
-
- return 0;
-}
-
-static int gru_seq_show(struct seq_file *file, void *data)
-{
- long gid = *(long *)data, ctxfree, cbrfree, dsrfree;
- struct gru_state *gru = GID_TO_GRU(gid);
-
- if (gid == 0) {
- seq_puts(file, "# gid nid ctx cbr dsr ctx cbr dsr\n");
- seq_puts(file, "# busy busy busy free free free\n");
- }
- if (gru) {
- ctxfree = GRU_NUM_CCH - gru->gs_active_contexts;
- cbrfree = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE;
- dsrfree = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES;
- seq_printf(file, " %5d%5d%7ld%6ld%6ld%8ld%6ld%6ld\n",
- gru->gs_gid, gru->gs_blade_id, GRU_NUM_CCH - ctxfree,
- GRU_NUM_CBE - cbrfree, GRU_NUM_DSR_BYTES - dsrfree,
- ctxfree, cbrfree, dsrfree);
- }
-
- return 0;
-}
-
-static void seq_stop(struct seq_file *file, void *data)
-{
-}
-
-static void *seq_start(struct seq_file *file, loff_t *gid)
-{
- if (*gid < gru_max_gids)
- return gid;
- return NULL;
-}
-
-static void *seq_next(struct seq_file *file, void *data, loff_t *gid)
-{
- (*gid)++;
- if (*gid < gru_max_gids)
- return gid;
- return NULL;
-}
-
-static const struct seq_operations cch_seq_ops = {
- .start = seq_start,
- .next = seq_next,
- .stop = seq_stop,
- .show = cch_seq_show
-};
-
-static const struct seq_operations gru_seq_ops = {
- .start = seq_start,
- .next = seq_next,
- .stop = seq_stop,
- .show = gru_seq_show
-};
-
-static int statistics_open(struct inode *inode, struct file *file)
-{
- return single_open(file, statistics_show, NULL);
-}
-
-static int mcs_statistics_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mcs_statistics_show, NULL);
-}
-
-static int options_open(struct inode *inode, struct file *file)
-{
- return single_open(file, options_show, NULL);
-}
-
-/* *INDENT-OFF* */
-static const struct proc_ops statistics_proc_ops = {
- .proc_open = statistics_open,
- .proc_read = seq_read,
- .proc_write = statistics_write,
- .proc_lseek = seq_lseek,
- .proc_release = single_release,
-};
-
-static const struct proc_ops mcs_statistics_proc_ops = {
- .proc_open = mcs_statistics_open,
- .proc_read = seq_read,
- .proc_write = mcs_statistics_write,
- .proc_lseek = seq_lseek,
- .proc_release = single_release,
-};
-
-static const struct proc_ops options_proc_ops = {
- .proc_open = options_open,
- .proc_read = seq_read,
- .proc_write = options_write,
- .proc_lseek = seq_lseek,
- .proc_release = single_release,
-};
-
-static struct proc_dir_entry *proc_gru __read_mostly;
-
-int gru_proc_init(void)
-{
- proc_gru = proc_mkdir("sgi_uv/gru", NULL);
- if (!proc_gru)
- return -1;
- if (!proc_create("statistics", 0644, proc_gru, &statistics_proc_ops))
- goto err;
- if (!proc_create("mcs_statistics", 0644, proc_gru, &mcs_statistics_proc_ops))
- goto err;
- if (!proc_create("debug_options", 0644, proc_gru, &options_proc_ops))
- goto err;
- if (!proc_create_seq("cch_status", 0444, proc_gru, &cch_seq_ops))
- goto err;
- if (!proc_create_seq("gru_status", 0444, proc_gru, &gru_seq_ops))
- goto err;
- return 0;
-err:
- remove_proc_subtree("sgi_uv/gru", NULL);
- return -1;
-}
-
-void gru_proc_exit(void)
-{
- remove_proc_subtree("sgi_uv/gru", NULL);
-}
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h
deleted file mode 100644
index 640daf1994df..000000000000
--- a/drivers/misc/sgi-gru/grutables.h
+++ /dev/null
@@ -1,659 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * SN Platform GRU Driver
- *
- * GRU DRIVER TABLES, MACROS, externs, etc
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#ifndef __GRUTABLES_H__
-#define __GRUTABLES_H__
-
-/*
- * GRU Chiplet:
- * The GRU is a user addressible memory accelerator. It provides
- * several forms of load, store, memset, bcopy instructions. In addition, it
- * contains special instructions for AMOs, sending messages to message
- * queues, etc.
- *
- * The GRU is an integral part of the node controller. It connects
- * directly to the cpu socket. In its current implementation, there are 2
- * GRU chiplets in the node controller on each blade (~node).
- *
- * The entire GRU memory space is fully coherent and cacheable by the cpus.
- *
- * Each GRU chiplet has a physical memory map that looks like the following:
- *
- * +-----------------+
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * +-----------------+
- * | system control |
- * +-----------------+ _______ +-------------+
- * |/////////////////| / | |
- * |/////////////////| / | |
- * |/////////////////| / | instructions|
- * |/////////////////| / | |
- * |/////////////////| / | |
- * |/////////////////| / |-------------|
- * |/////////////////| / | |
- * +-----------------+ | |
- * | context 15 | | data |
- * +-----------------+ | |
- * | ...... | \ | |
- * +-----------------+ \____________ +-------------+
- * | context 1 |
- * +-----------------+
- * | context 0 |
- * +-----------------+
- *
- * Each of the "contexts" is a chunk of memory that can be mmaped into user
- * space. The context consists of 2 parts:
- *
- * - an instruction space that can be directly accessed by the user
- * to issue GRU instructions and to check instruction status.
- *
- * - a data area that acts as normal RAM.
- *
- * User instructions contain virtual addresses of data to be accessed by the
- * GRU. The GRU contains a TLB that is used to convert these user virtual
- * addresses to physical addresses.
- *
- * The "system control" area of the GRU chiplet is used by the kernel driver
- * to manage user contexts and to perform functions such as TLB dropin and
- * purging.
- *
- * One context may be reserved for the kernel and used for cross-partition
- * communication. The GRU will also be used to asynchronously zero out
- * large blocks of memory (not currently implemented).
- *
- *
- * Tables:
- *
- * VDATA-VMA Data - Holds a few parameters. Head of linked list of
- * GTS tables for threads using the GSEG
- * GTS - Gru Thread State - contains info for managing a GSEG context. A
- * GTS is allocated for each thread accessing a
- * GSEG.
- * GTD - GRU Thread Data - contains shadow copy of GRU data when GSEG is
- * not loaded into a GRU
- * GMS - GRU Memory Struct - Used to manage TLB shootdowns. Tracks GRUs
- * where a GSEG has been loaded. Similar to
- * an mm_struct but for GRU.
- *
- * GS - GRU State - Used to manage the state of a GRU chiplet
- * BS - Blade State - Used to manage state of all GRU chiplets
- * on a blade
- *
- *
- * Normal task tables for task using GRU.
- * - 2 threads in process
- * - 2 GSEGs open in process
- * - GSEG1 is being used by both threads
- * - GSEG2 is used only by thread 2
- *
- * task -->|
- * task ---+---> mm ->------ (notifier) -------+-> gms
- * | |
- * |--> vma -> vdata ---> gts--->| GSEG1 (thread1)
- * | | |
- * | +-> gts--->| GSEG1 (thread2)
- * | |
- * |--> vma -> vdata ---> gts--->| GSEG2 (thread2)
- * .
- * .
- *
- * GSEGs are marked DONTCOPY on fork
- *
- * At open
- * file.private_data -> NULL
- *
- * At mmap,
- * vma -> vdata
- *
- * After gseg reference
- * vma -> vdata ->gts
- *
- * After fork
- * parent
- * vma -> vdata -> gts
- * child
- * (vma is not copied)
- *
- */
-
-#include <linux/refcount.h>
-#include <linux/rmap.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/mmu_notifier.h>
-#include <linux/mm_types.h>
-#include "gru.h"
-#include "grulib.h"
-#include "gruhandles.h"
-
-extern struct gru_stats_s gru_stats;
-extern struct gru_blade_state *gru_base[];
-extern unsigned long gru_start_paddr, gru_end_paddr;
-extern void *gru_start_vaddr;
-extern unsigned int gru_max_gids;
-
-#define GRU_MAX_BLADES MAX_NUMNODES
-#define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE)
-
-#define GRU_DRIVER_ID_STR "SGI GRU Device Driver"
-#define GRU_DRIVER_VERSION_STR "0.85"
-
-/*
- * GRU statistics.
- */
-struct gru_stats_s {
- atomic_long_t vdata_alloc;
- atomic_long_t vdata_free;
- atomic_long_t gts_alloc;
- atomic_long_t gts_free;
- atomic_long_t gms_alloc;
- atomic_long_t gms_free;
- atomic_long_t gts_double_allocate;
- atomic_long_t assign_context;
- atomic_long_t assign_context_failed;
- atomic_long_t free_context;
- atomic_long_t load_user_context;
- atomic_long_t load_kernel_context;
- atomic_long_t lock_kernel_context;
- atomic_long_t unlock_kernel_context;
- atomic_long_t steal_user_context;
- atomic_long_t steal_kernel_context;
- atomic_long_t steal_context_failed;
- atomic_long_t nopfn;
- atomic_long_t asid_new;
- atomic_long_t asid_next;
- atomic_long_t asid_wrap;
- atomic_long_t asid_reuse;
- atomic_long_t intr;
- atomic_long_t intr_cbr;
- atomic_long_t intr_tfh;
- atomic_long_t intr_spurious;
- atomic_long_t intr_mm_lock_failed;
- atomic_long_t call_os;
- atomic_long_t call_os_wait_queue;
- atomic_long_t user_flush_tlb;
- atomic_long_t user_unload_context;
- atomic_long_t user_exception;
- atomic_long_t set_context_option;
- atomic_long_t check_context_retarget_intr;
- atomic_long_t check_context_unload;
- atomic_long_t tlb_dropin;
- atomic_long_t tlb_preload_page;
- atomic_long_t tlb_dropin_fail_no_asid;
- atomic_long_t tlb_dropin_fail_upm;
- atomic_long_t tlb_dropin_fail_invalid;
- atomic_long_t tlb_dropin_fail_range_active;
- atomic_long_t tlb_dropin_fail_idle;
- atomic_long_t tlb_dropin_fail_fmm;
- atomic_long_t tlb_dropin_fail_no_exception;
- atomic_long_t tfh_stale_on_fault;
- atomic_long_t mmu_invalidate_range;
- atomic_long_t mmu_invalidate_page;
- atomic_long_t flush_tlb;
- atomic_long_t flush_tlb_gru;
- atomic_long_t flush_tlb_gru_tgh;
- atomic_long_t flush_tlb_gru_zero_asid;
-
- atomic_long_t copy_gpa;
- atomic_long_t read_gpa;
-
- atomic_long_t mesq_receive;
- atomic_long_t mesq_receive_none;
- atomic_long_t mesq_send;
- atomic_long_t mesq_send_failed;
- atomic_long_t mesq_noop;
- atomic_long_t mesq_send_unexpected_error;
- atomic_long_t mesq_send_lb_overflow;
- atomic_long_t mesq_send_qlimit_reached;
- atomic_long_t mesq_send_amo_nacked;
- atomic_long_t mesq_send_put_nacked;
- atomic_long_t mesq_page_overflow;
- atomic_long_t mesq_qf_locked;
- atomic_long_t mesq_qf_noop_not_full;
- atomic_long_t mesq_qf_switch_head_failed;
- atomic_long_t mesq_qf_unexpected_error;
- atomic_long_t mesq_noop_unexpected_error;
- atomic_long_t mesq_noop_lb_overflow;
- atomic_long_t mesq_noop_qlimit_reached;
- atomic_long_t mesq_noop_amo_nacked;
- atomic_long_t mesq_noop_put_nacked;
- atomic_long_t mesq_noop_page_overflow;
-
-};
-
-enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync,
- cchop_deallocate, tfhop_write_only, tfhop_write_restart,
- tghop_invalidate, mcsop_last};
-
-struct mcs_op_statistic {
- atomic_long_t count;
- atomic_long_t total;
- unsigned long max;
-};
-
-extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
-
-#define OPT_DPRINT 1
-#define OPT_STATS 2
-
-
-#define IRQ_GRU 110 /* Starting IRQ number for interrupts */
-
-/* Delay in jiffies between attempts to assign a GRU context */
-#define GRU_ASSIGN_DELAY ((HZ * 20) / 1000)
-
-/*
- * If a process has it's context stolen, min delay in jiffies before trying to
- * steal a context from another process.
- */
-#define GRU_STEAL_DELAY ((HZ * 200) / 1000)
-
-#define STAT(id) do { \
- if (gru_options & OPT_STATS) \
- atomic_long_inc(&gru_stats.id); \
- } while (0)
-
-#ifdef CONFIG_SGI_GRU_DEBUG
-#define gru_dbg(dev, fmt, x...) \
- do { \
- if (gru_options & OPT_DPRINT) \
- printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\
- } while (0)
-#else
-#define gru_dbg(x...)
-#endif
-
-/*-----------------------------------------------------------------------------
- * ASID management
- */
-#define MAX_ASID 0xfffff0
-#define MIN_ASID 8
-#define ASID_INC 8 /* number of regions */
-
-/* Generate a GRU asid value from a GRU base asid & a virtual address. */
-#define VADDR_HI_BIT 64
-#define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3)
-#define GRUASID(asid, addr) ((asid) + GRUREGION(addr))
-
-/*------------------------------------------------------------------------------
- * File & VMS Tables
- */
-
-struct gru_state;
-
-/*
- * This structure is pointed to from the mmstruct via the notifier pointer.
- * There is one of these per address space.
- */
-struct gru_mm_tracker { /* pack to reduce size */
- unsigned int mt_asid_gen:24; /* ASID wrap count */
- unsigned int mt_asid:24; /* current base ASID for gru */
- unsigned short mt_ctxbitmap:16;/* bitmap of contexts using
- asid */
-} __attribute__ ((packed));
-
-struct gru_mm_struct {
- struct mmu_notifier ms_notifier;
- spinlock_t ms_asid_lock; /* protects ASID assignment */
- atomic_t ms_range_active;/* num range_invals active */
- wait_queue_head_t ms_wait_queue;
- DECLARE_BITMAP(ms_asidmap, GRU_MAX_GRUS);
- struct gru_mm_tracker ms_asids[GRU_MAX_GRUS];
-};
-
-/*
- * One of these structures is allocated when a GSEG is mmaped. The
- * structure is pointed to by the vma->vm_private_data field in the vma struct.
- */
-struct gru_vma_data {
- spinlock_t vd_lock; /* Serialize access to vma */
- struct list_head vd_head; /* head of linked list of gts */
- long vd_user_options;/* misc user option flags */
- int vd_cbr_au_count;
- int vd_dsr_au_count;
- unsigned char vd_tlb_preload_count;
-};
-
-/*
- * One of these is allocated for each thread accessing a mmaped GRU. A linked
- * list of these structure is hung off the struct gru_vma_data in the mm_struct.
- */
-struct gru_thread_state {
- struct list_head ts_next; /* list - head at vma-private */
- struct mutex ts_ctxlock; /* load/unload CTX lock */
- struct mm_struct *ts_mm; /* mm currently mapped to
- context */
- struct vm_area_struct *ts_vma; /* vma of GRU context */
- struct gru_state *ts_gru; /* GRU where the context is
- loaded */
- struct gru_mm_struct *ts_gms; /* asid & ioproc struct */
- unsigned char ts_tlb_preload_count; /* TLB preload pages */
- unsigned long ts_cbr_map; /* map of allocated CBRs */
- unsigned long ts_dsr_map; /* map of allocated DATA
- resources */
- unsigned long ts_steal_jiffies;/* jiffies when context last
- stolen */
- long ts_user_options;/* misc user option flags */
- pid_t ts_tgid_owner; /* task that is using the
- context - for migration */
- short ts_user_blade_id;/* user selected blade */
- signed char ts_user_chiplet_id;/* user selected chiplet */
- unsigned short ts_sizeavail; /* Pagesizes in use */
- int ts_tsid; /* thread that owns the
- structure */
- int ts_tlb_int_select;/* target cpu if interrupts
- enabled */
- int ts_ctxnum; /* context number where the
- context is loaded */
- refcount_t ts_refcnt; /* reference count GTS */
- unsigned char ts_dsr_au_count;/* Number of DSR resources
- required for contest */
- unsigned char ts_cbr_au_count;/* Number of CBR resources
- required for contest */
- signed char ts_cch_req_slice;/* CCH packet slice */
- signed char ts_blade; /* If >= 0, migrate context if
- ref from different blade */
- signed char ts_force_cch_reload;
- signed char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each
- allocated CB */
- int ts_data_valid; /* Indicates if ts_gdata has
- valid data */
- struct gru_gseg_statistics ustats; /* User statistics */
- unsigned long ts_gdata[]; /* save area for GRU data (CB,
- DS, CBE) */
-};
-
-/*
- * Threaded programs actually allocate an array of GSEGs when a context is
- * created. Each thread uses a separate GSEG. TSID is the index into the GSEG
- * array.
- */
-#define TSID(a, v) (((a) - (v)->vm_start) / GRU_GSEG_PAGESIZE)
-#define UGRUADDR(gts) ((gts)->ts_vma->vm_start + \
- (gts)->ts_tsid * GRU_GSEG_PAGESIZE)
-
-#define NULLCTX (-1) /* if context not loaded into GRU */
-
-/*-----------------------------------------------------------------------------
- * GRU State Tables
- */
-
-/*
- * One of these exists for each GRU chiplet.
- */
-struct gru_state {
- struct gru_blade_state *gs_blade; /* GRU state for entire
- blade */
- unsigned long gs_gru_base_paddr; /* Physical address of
- gru segments (64) */
- void *gs_gru_base_vaddr; /* Virtual address of
- gru segments (64) */
- unsigned short gs_gid; /* unique GRU number */
- unsigned short gs_blade_id; /* blade of GRU */
- unsigned char gs_chiplet_id; /* blade chiplet of GRU */
- unsigned char gs_tgh_local_shift; /* used to pick TGH for
- local flush */
- unsigned char gs_tgh_first_remote; /* starting TGH# for
- remote flush */
- spinlock_t gs_asid_lock; /* lock used for
- assigning asids */
- spinlock_t gs_lock; /* lock used for
- assigning contexts */
-
- /* -- the following are protected by the gs_asid_lock spinlock ---- */
- unsigned int gs_asid; /* Next availe ASID */
- unsigned int gs_asid_limit; /* Limit of available
- ASIDs */
- unsigned int gs_asid_gen; /* asid generation.
- Inc on wrap */
-
- /* --- the following fields are protected by the gs_lock spinlock --- */
- unsigned long gs_context_map; /* bitmap to manage
- contexts in use */
- unsigned long gs_cbr_map; /* bitmap to manage CB
- resources */
- unsigned long gs_dsr_map; /* bitmap used to manage
- DATA resources */
- unsigned int gs_reserved_cbrs; /* Number of kernel-
- reserved cbrs */
- unsigned int gs_reserved_dsr_bytes; /* Bytes of kernel-
- reserved dsrs */
- unsigned short gs_active_contexts; /* number of contexts
- in use */
- struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using
- the context */
- int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */
-};
-
-/*
- * This structure contains the GRU state for all the GRUs on a blade.
- */
-struct gru_blade_state {
- void *kernel_cb; /* First kernel
- reserved cb */
- void *kernel_dsr; /* First kernel
- reserved DSR */
- struct rw_semaphore bs_kgts_sema; /* lock for kgts */
- struct gru_thread_state *bs_kgts; /* GTS for kernel use */
-
- /* ---- the following are used for managing kernel async GRU CBRs --- */
- int bs_async_dsr_bytes; /* DSRs for async */
- int bs_async_cbrs; /* CBRs AU for async */
- struct completion *bs_async_wq;
-
- /* ---- the following are protected by the bs_lock spinlock ---- */
- spinlock_t bs_lock; /* lock used for
- stealing contexts */
- int bs_lru_ctxnum; /* STEAL - last context
- stolen */
- struct gru_state *bs_lru_gru; /* STEAL - last gru
- stolen */
-
- struct gru_state bs_grus[GRU_CHIPLETS_PER_BLADE];
-};
-
-/*-----------------------------------------------------------------------------
- * Address Primitives
- */
-#define get_tfm_for_cpu(g, c) \
- ((struct gru_tlb_fault_map *)get_tfm((g)->gs_gru_base_vaddr, (c)))
-#define get_tfh_by_index(g, i) \
- ((struct gru_tlb_fault_handle *)get_tfh((g)->gs_gru_base_vaddr, (i)))
-#define get_tgh_by_index(g, i) \
- ((struct gru_tlb_global_handle *)get_tgh((g)->gs_gru_base_vaddr, (i)))
-#define get_cbe_by_index(g, i) \
- ((struct gru_control_block_extended *)get_cbe((g)->gs_gru_base_vaddr,\
- (i)))
-
-/*-----------------------------------------------------------------------------
- * Useful Macros
- */
-
-/* Given a blade# & chiplet#, get a pointer to the GRU */
-#define get_gru(b, c) (&gru_base[b]->bs_grus[c])
-
-/* Number of bytes to save/restore when unloading/loading GRU contexts */
-#define DSR_BYTES(dsr) ((dsr) * GRU_DSR_AU_BYTES)
-#define CBR_BYTES(cbr) ((cbr) * GRU_HANDLE_BYTES * GRU_CBR_AU_SIZE * 2)
-
-/* Convert a user CB number to the actual CBRNUM */
-#define thread_cbr_number(gts, n) ((gts)->ts_cbr_idx[(n) / GRU_CBR_AU_SIZE] \
- * GRU_CBR_AU_SIZE + (n) % GRU_CBR_AU_SIZE)
-
-/* Convert a gid to a pointer to the GRU */
-#define GID_TO_GRU(gid) \
- (gru_base[(gid) / GRU_CHIPLETS_PER_BLADE] ? \
- (&gru_base[(gid) / GRU_CHIPLETS_PER_BLADE]-> \
- bs_grus[(gid) % GRU_CHIPLETS_PER_BLADE]) : \
- NULL)
-
-/* Scan all active GRUs in a GRU bitmap */
-#define for_each_gru_in_bitmap(gid, map) \
- for_each_set_bit((gid), (map), GRU_MAX_GRUS)
-
-/* Scan all active GRUs on a specific blade */
-#define for_each_gru_on_blade(gru, nid, i) \
- for ((gru) = gru_base[nid]->bs_grus, (i) = 0; \
- (i) < GRU_CHIPLETS_PER_BLADE; \
- (i)++, (gru)++)
-
-/* Scan all GRUs */
-#define foreach_gid(gid) \
- for ((gid) = 0; (gid) < gru_max_gids; (gid)++)
-
-/* Scan all active GTSs on a gru. Note: must hold ss_lock to use this macro. */
-#define for_each_gts_on_gru(gts, gru, ctxnum) \
- for ((ctxnum) = 0; (ctxnum) < GRU_NUM_CCH; (ctxnum)++) \
- if (((gts) = (gru)->gs_gts[ctxnum]))
-
-/* Scan each CBR whose bit is set in a TFM (or copy of) */
-#define for_each_cbr_in_tfm(i, map) \
- for_each_set_bit((i), (map), GRU_NUM_CBE)
-
-/* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */
-#define for_each_cbr_in_allocation_map(i, map, k) \
- for_each_set_bit((k), (map), GRU_CBR_AU) \
- for ((i) = (k)*GRU_CBR_AU_SIZE; \
- (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++)
-
-#define gseg_physical_address(gru, ctxnum) \
- ((gru)->gs_gru_base_paddr + ctxnum * GRU_GSEG_STRIDE)
-#define gseg_virtual_address(gru, ctxnum) \
- ((gru)->gs_gru_base_vaddr + ctxnum * GRU_GSEG_STRIDE)
-
-/*-----------------------------------------------------------------------------
- * Lock / Unlock GRU handles
- * Use the "delresp" bit in the handle as a "lock" bit.
- */
-
-/* Lock hierarchy checking enabled only in emulator */
-
-/* 0 = lock failed, 1 = locked */
-static inline int __trylock_handle(void *h)
-{
- return !test_and_set_bit(1, h);
-}
-
-static inline void __lock_handle(void *h)
-{
- while (test_and_set_bit(1, h))
- cpu_relax();
-}
-
-static inline void __unlock_handle(void *h)
-{
- clear_bit(1, h);
-}
-
-static inline int trylock_cch_handle(struct gru_context_configuration_handle *cch)
-{
- return __trylock_handle(cch);
-}
-
-static inline void lock_cch_handle(struct gru_context_configuration_handle *cch)
-{
- __lock_handle(cch);
-}
-
-static inline void unlock_cch_handle(struct gru_context_configuration_handle
- *cch)
-{
- __unlock_handle(cch);
-}
-
-static inline void lock_tgh_handle(struct gru_tlb_global_handle *tgh)
-{
- __lock_handle(tgh);
-}
-
-static inline void unlock_tgh_handle(struct gru_tlb_global_handle *tgh)
-{
- __unlock_handle(tgh);
-}
-
-static inline int is_kernel_context(struct gru_thread_state *gts)
-{
- return !gts->ts_mm;
-}
-
-/*
- * The following are for Nehelem-EX. A more general scheme is needed for
- * future processors.
- */
-#define UV_MAX_INT_CORES 8
-#define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1)
-#define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1)
-#define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \
- ((cpu_physical_id(p) >> 1) & 3))
-/*-----------------------------------------------------------------------------
- * Function prototypes & externs
- */
-struct gru_unload_context_req;
-
-extern const struct vm_operations_struct gru_vm_ops;
-extern struct device *grudev;
-
-extern struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma,
- int tsid);
-extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct
- *vma, int tsid);
-extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct
- *vma, int tsid);
-extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts);
-extern void gru_load_context(struct gru_thread_state *gts);
-extern void gru_steal_context(struct gru_thread_state *gts);
-extern void gru_unload_context(struct gru_thread_state *gts, int savestate);
-extern int gru_update_cch(struct gru_thread_state *gts);
-extern void gts_drop(struct gru_thread_state *gts);
-extern void gru_tgh_flush_init(struct gru_state *gru);
-extern int gru_kservices_init(void);
-extern void gru_kservices_exit(void);
-extern irqreturn_t gru0_intr(int irq, void *dev_id);
-extern irqreturn_t gru1_intr(int irq, void *dev_id);
-extern irqreturn_t gru_intr_mblade(int irq, void *dev_id);
-extern int gru_dump_chiplet_request(unsigned long arg);
-extern long gru_get_gseg_statistics(unsigned long arg);
-extern int gru_handle_user_call_os(unsigned long address);
-extern int gru_user_flush_tlb(unsigned long arg);
-extern int gru_user_unload_context(unsigned long arg);
-extern int gru_get_exception_detail(unsigned long arg);
-extern int gru_set_context_option(unsigned long address);
-extern int gru_check_context_placement(struct gru_thread_state *gts);
-extern int gru_cpu_fault_map_id(void);
-extern struct vm_area_struct *gru_find_vma(unsigned long vaddr);
-extern void gru_flush_all_tlb(struct gru_state *gru);
-extern int gru_proc_init(void);
-extern void gru_proc_exit(void);
-
-extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
- int cbr_au_count, int dsr_au_count,
- unsigned char tlb_preload_count, int options, int tsid);
-extern unsigned long gru_reserve_cb_resources(struct gru_state *gru,
- int cbr_au_count, signed char *cbmap);
-extern unsigned long gru_reserve_ds_resources(struct gru_state *gru,
- int dsr_au_count, signed char *dsmap);
-extern vm_fault_t gru_fault(struct vm_fault *vmf);
-extern struct gru_mm_struct *gru_register_mmu_notifier(void);
-extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms);
-
-extern int gru_ktest(unsigned long arg);
-extern void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start,
- unsigned long len);
-
-extern unsigned long gru_options;
-
-#endif /* __GRUTABLES_H__ */
diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c
deleted file mode 100644
index a8121d40be68..000000000000
--- a/drivers/misc/sgi-gru/grutlbpurge.c
+++ /dev/null
@@ -1,316 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * MMUOPS callbacks + TLB flushing
- *
- * This file handles emu notifier callbacks from the core kernel. The callbacks
- * are used to update the TLB in the GRU as a result of changes in the
- * state of a process address space. This file also handles TLB invalidates
- * from the GRU driver.
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/hugetlb.h>
-#include <linux/delay.h>
-#include <linux/timex.h>
-#include <linux/srcu.h>
-#include <asm/processor.h>
-#include "gru.h"
-#include "grutables.h"
-#include <asm/uv/uv_hub.h>
-
-#define gru_random() get_cycles()
-
-/* ---------------------------------- TLB Invalidation functions --------
- * get_tgh_handle
- *
- * Find a TGH to use for issuing a TLB invalidate. For GRUs that are on the
- * local blade, use a fixed TGH that is a function of the blade-local cpu
- * number. Normally, this TGH is private to the cpu & no contention occurs for
- * the TGH. For offblade GRUs, select a random TGH in the range above the
- * private TGHs. A spinlock is required to access this TGH & the lock must be
- * released when the invalidate is completes. This sucks, but it is the best we
- * can do.
- *
- * Note that the spinlock is IN the TGH handle so locking does not involve
- * additional cache lines.
- *
- */
-static inline int get_off_blade_tgh(struct gru_state *gru)
-{
- int n;
-
- n = GRU_NUM_TGH - gru->gs_tgh_first_remote;
- n = gru_random() % n;
- n += gru->gs_tgh_first_remote;
- return n;
-}
-
-static inline int get_on_blade_tgh(struct gru_state *gru)
-{
- return uv_blade_processor_id() >> gru->gs_tgh_local_shift;
-}
-
-static struct gru_tlb_global_handle *get_lock_tgh_handle(struct gru_state
- *gru)
-{
- struct gru_tlb_global_handle *tgh;
- int n;
-
- if (uv_numa_blade_id() == gru->gs_blade_id)
- n = get_on_blade_tgh(gru);
- else
- n = get_off_blade_tgh(gru);
- tgh = get_tgh_by_index(gru, n);
- lock_tgh_handle(tgh);
-
- return tgh;
-}
-
-static void get_unlock_tgh_handle(struct gru_tlb_global_handle *tgh)
-{
- unlock_tgh_handle(tgh);
-}
-
-/*
- * gru_flush_tlb_range
- *
- * General purpose TLB invalidation function. This function scans every GRU in
- * the ENTIRE system (partition) looking for GRUs where the specified MM has
- * been accessed by the GRU. For each GRU found, the TLB must be invalidated OR
- * the ASID invalidated. Invalidating an ASID causes a new ASID to be assigned
- * on the next fault. This effectively flushes the ENTIRE TLB for the MM at the
- * cost of (possibly) a large number of future TLBmisses.
- *
- * The current algorithm is optimized based on the following (somewhat true)
- * assumptions:
- * - GRU contexts are not loaded into a GRU unless a reference is made to
- * the data segment or control block (this is true, not an assumption).
- * If a DS/CB is referenced, the user will also issue instructions that
- * cause TLBmisses. It is not necessary to optimize for the case where
- * contexts are loaded but no instructions cause TLB misses. (I know
- * this will happen but I'm not optimizing for it).
- * - GRU instructions to invalidate TLB entries are SLOOOOWWW - normally
- * a few usec but in unusual cases, it could be longer. Avoid if
- * possible.
- * - intrablade process migration between cpus is not frequent but is
- * common.
- * - a GRU context is not typically migrated to a different GRU on the
- * blade because of intrablade migration
- * - interblade migration is rare. Processes migrate their GRU context to
- * the new blade.
- * - if interblade migration occurs, migration back to the original blade
- * is very very rare (ie., no optimization for this case)
- * - most GRU instruction operate on a subset of the user REGIONS. Code
- * & shared library regions are not likely targets of GRU instructions.
- *
- * To help improve the efficiency of TLB invalidation, the GMS data
- * structure is maintained for EACH address space (MM struct). The GMS is
- * also the structure that contains the pointer to the mmu callout
- * functions. This structure is linked to the mm_struct for the address space
- * using the mmu "register" function. The mmu interfaces are used to
- * provide the callbacks for TLB invalidation. The GMS contains:
- *
- * - asid[maxgrus] array. ASIDs are assigned to a GRU when a context is
- * loaded into the GRU.
- * - asidmap[maxgrus]. bitmap to make it easier to find non-zero asids in
- * the above array
- * - ctxbitmap[maxgrus]. Indicates the contexts that are currently active
- * in the GRU for the address space. This bitmap must be passed to the
- * GRU to do an invalidate.
- *
- * The current algorithm for invalidating TLBs is:
- * - scan the asidmap for GRUs where the context has been loaded, ie,
- * asid is non-zero.
- * - for each gru found:
- * - if the ctxtmap is non-zero, there are active contexts in the
- * GRU. TLB invalidate instructions must be issued to the GRU.
- * - if the ctxtmap is zero, no context is active. Set the ASID to
- * zero to force a full TLB invalidation. This is fast but will
- * cause a lot of TLB misses if the context is reloaded onto the
- * GRU
- *
- */
-
-void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start,
- unsigned long len)
-{
- struct gru_state *gru;
- struct gru_mm_tracker *asids;
- struct gru_tlb_global_handle *tgh;
- unsigned long num;
- int grupagesize, pagesize, pageshift, gid, asid;
-
- /* ZZZ TODO - handle huge pages */
- pageshift = PAGE_SHIFT;
- pagesize = (1UL << pageshift);
- grupagesize = GRU_PAGESIZE(pageshift);
- num = min(((len + pagesize - 1) >> pageshift), GRUMAXINVAL);
-
- STAT(flush_tlb);
- gru_dbg(grudev, "gms %p, start 0x%lx, len 0x%lx, asidmap 0x%lx\n", gms,
- start, len, gms->ms_asidmap[0]);
-
- spin_lock(&gms->ms_asid_lock);
- for_each_gru_in_bitmap(gid, gms->ms_asidmap) {
- STAT(flush_tlb_gru);
- gru = GID_TO_GRU(gid);
- asids = gms->ms_asids + gid;
- asid = asids->mt_asid;
- if (asids->mt_ctxbitmap && asid) {
- STAT(flush_tlb_gru_tgh);
- asid = GRUASID(asid, start);
- gru_dbg(grudev,
- " FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n",
- gid, asid, start, grupagesize, num, asids->mt_ctxbitmap);
- tgh = get_lock_tgh_handle(gru);
- tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0,
- num - 1, asids->mt_ctxbitmap);
- get_unlock_tgh_handle(tgh);
- } else {
- STAT(flush_tlb_gru_zero_asid);
- asids->mt_asid = 0;
- __clear_bit(gru->gs_gid, gms->ms_asidmap);
- gru_dbg(grudev,
- " CLEARASID gruid %d, asid 0x%x, cbtmap 0x%x, asidmap 0x%lx\n",
- gid, asid, asids->mt_ctxbitmap,
- gms->ms_asidmap[0]);
- }
- }
- spin_unlock(&gms->ms_asid_lock);
-}
-
-/*
- * Flush the entire TLB on a chiplet.
- */
-void gru_flush_all_tlb(struct gru_state *gru)
-{
- struct gru_tlb_global_handle *tgh;
-
- gru_dbg(grudev, "gid %d\n", gru->gs_gid);
- tgh = get_lock_tgh_handle(gru);
- tgh_invalidate(tgh, 0, ~0, 0, 1, 1, GRUMAXINVAL - 1, 0xffff);
- get_unlock_tgh_handle(tgh);
-}
-
-/*
- * MMUOPS notifier callout functions
- */
-static int gru_invalidate_range_start(struct mmu_notifier *mn,
- const struct mmu_notifier_range *range)
-{
- struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct,
- ms_notifier);
-
- STAT(mmu_invalidate_range);
- atomic_inc(&gms->ms_range_active);
- gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx, act %d\n", gms,
- range->start, range->end, atomic_read(&gms->ms_range_active));
- gru_flush_tlb_range(gms, range->start, range->end - range->start);
-
- return 0;
-}
-
-static void gru_invalidate_range_end(struct mmu_notifier *mn,
- const struct mmu_notifier_range *range)
-{
- struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct,
- ms_notifier);
-
- /* ..._and_test() provides needed barrier */
- (void)atomic_dec_and_test(&gms->ms_range_active);
-
- wake_up_all(&gms->ms_wait_queue);
- gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx\n",
- gms, range->start, range->end);
-}
-
-static struct mmu_notifier *gru_alloc_notifier(struct mm_struct *mm)
-{
- struct gru_mm_struct *gms;
-
- gms = kzalloc_obj(*gms);
- if (!gms)
- return ERR_PTR(-ENOMEM);
- STAT(gms_alloc);
- spin_lock_init(&gms->ms_asid_lock);
- init_waitqueue_head(&gms->ms_wait_queue);
-
- return &gms->ms_notifier;
-}
-
-static void gru_free_notifier(struct mmu_notifier *mn)
-{
- kfree(container_of(mn, struct gru_mm_struct, ms_notifier));
- STAT(gms_free);
-}
-
-static const struct mmu_notifier_ops gru_mmuops = {
- .invalidate_range_start = gru_invalidate_range_start,
- .invalidate_range_end = gru_invalidate_range_end,
- .alloc_notifier = gru_alloc_notifier,
- .free_notifier = gru_free_notifier,
-};
-
-struct gru_mm_struct *gru_register_mmu_notifier(void)
-{
- struct mmu_notifier *mn;
-
- mn = mmu_notifier_get_locked(&gru_mmuops, current->mm);
- if (IS_ERR(mn))
- return ERR_CAST(mn);
-
- return container_of(mn, struct gru_mm_struct, ms_notifier);
-}
-
-void gru_drop_mmu_notifier(struct gru_mm_struct *gms)
-{
- mmu_notifier_put(&gms->ms_notifier);
-}
-
-/*
- * Setup TGH parameters. There are:
- * - 24 TGH handles per GRU chiplet
- * - a portion (MAX_LOCAL_TGH) of the handles are reserved for
- * use by blade-local cpus
- * - the rest are used by off-blade cpus. This usage is
- * less frequent than blade-local usage.
- *
- * For now, use 16 handles for local flushes, 8 for remote flushes. If the blade
- * has less tan or equal to 16 cpus, each cpu has a unique handle that it can
- * use.
- */
-#define MAX_LOCAL_TGH 16
-
-void gru_tgh_flush_init(struct gru_state *gru)
-{
- int cpus, shift = 0, n;
-
- cpus = uv_blade_nr_possible_cpus(gru->gs_blade_id);
-
- /* n = cpus rounded up to next power of 2 */
- if (cpus) {
- n = 1 << fls(cpus - 1);
-
- /*
- * shift count for converting local cpu# to TGH index
- * 0 if cpus <= MAX_LOCAL_TGH,
- * 1 if cpus <= 2*MAX_LOCAL_TGH,
- * etc
- */
- shift = max(0, fls(n - 1) - fls(MAX_LOCAL_TGH - 1));
- }
- gru->gs_tgh_local_shift = shift;
-
- /* first starting TGH index to use for remote purges */
- gru->gs_tgh_first_remote = (cpus + (1 << shift) - 1) >> shift;
-
-}
diff --git a/drivers/misc/sgi-xp/Makefile b/drivers/misc/sgi-xp/Makefile
deleted file mode 100644
index 34c55a4045af..000000000000
--- a/drivers/misc/sgi-xp/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for SGI's XP devices.
-#
-
-obj-$(CONFIG_SGI_XP) += xp.o
-xp-y := xp_main.o xp_uv.o
-
-obj-$(CONFIG_SGI_XP) += xpc.o
-xpc-y := xpc_main.o xpc_channel.o xpc_partition.o \
- xpc_uv.o
-
-obj-$(CONFIG_SGI_XP) += xpnet.o
diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h
deleted file mode 100644
index 3185711beb07..000000000000
--- a/drivers/misc/sgi-xp/xp.h
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (C) 2004-2008 Silicon Graphics, Inc. All rights reserved.
- */
-
-/*
- * External Cross Partition (XP) structures and defines.
- */
-
-#ifndef _DRIVERS_MISC_SGIXP_XP_H
-#define _DRIVERS_MISC_SGIXP_XP_H
-
-#include <linux/mutex.h>
-
-#if defined CONFIG_X86_UV
-#include <asm/uv/uv.h>
-#endif
-
-#ifdef USE_DBUG_ON
-#define DBUG_ON(condition) BUG_ON(condition)
-#else
-#define DBUG_ON(condition)
-#endif
-
-/*
- * Define the maximum number of partitions the system can possibly support.
- * It is based on the maximum number of hardware partitionable regions. The
- * term 'region' in this context refers to the minimum number of nodes that
- * can comprise an access protection grouping. The access protection is in
- * regards to memory, IPI and IOI.
- *
- * The maximum number of hardware partitionable regions is equal to the
- * maximum number of nodes in the entire system divided by the minimum number
- * of nodes that comprise an access protection grouping.
- */
-#define XP_MAX_NPARTITIONS_SN2 64
-#define XP_MAX_NPARTITIONS_UV 256
-
-/*
- * XPC establishes channel connections between the local partition and any
- * other partition that is currently up. Over these channels, kernel-level
- * `users' can communicate with their counterparts on the other partitions.
- *
- * If the need for additional channels arises, one can simply increase
- * XPC_MAX_NCHANNELS accordingly. If the day should come where that number
- * exceeds the absolute MAXIMUM number of channels possible (eight), then one
- * will need to make changes to the XPC code to accommodate for this.
- *
- * The absolute maximum number of channels possible is limited to eight for
- * performance reasons on sn2 hardware. The internal cross partition structures
- * require sixteen bytes per channel, and eight allows all of this
- * interface-shared info to fit in one 128-byte cacheline.
- */
-#define XPC_MEM_CHANNEL 0 /* memory channel number */
-#define XPC_NET_CHANNEL 1 /* network channel number */
-
-#define XPC_MAX_NCHANNELS 2 /* max #of channels allowed */
-
-#if XPC_MAX_NCHANNELS > 8
-#error XPC_MAX_NCHANNELS exceeds absolute MAXIMUM possible.
-#endif
-
-/*
- * Define macro, XPC_MSG_SIZE(), is provided for the user
- * that wants to fit as many msg entries as possible in a given memory size
- * (e.g. a memory page).
- */
-#define XPC_MSG_MAX_SIZE 128
-#define XPC_MSG_HDR_MAX_SIZE 16
-#define XPC_MSG_PAYLOAD_MAX_SIZE (XPC_MSG_MAX_SIZE - XPC_MSG_HDR_MAX_SIZE)
-
-#define XPC_MSG_SIZE(_payload_size) \
- ALIGN(XPC_MSG_HDR_MAX_SIZE + (_payload_size), \
- is_uv_system() ? 64 : 128)
-
-
-/*
- * Define the return values and values passed to user's callout functions.
- * (It is important to add new value codes at the end just preceding
- * xpUnknownReason, which must have the highest numerical value.)
- */
-enum xp_retval {
- xpSuccess = 0,
-
- xpNotConnected, /* 1: channel is not connected */
- xpConnected, /* 2: channel connected (opened) */
- xpRETIRED1, /* 3: (formerly xpDisconnected) */
-
- xpMsgReceived, /* 4: message received */
- xpMsgDelivered, /* 5: message delivered and acknowledged */
-
- xpRETIRED2, /* 6: (formerly xpTransferFailed) */
-
- xpNoWait, /* 7: operation would require wait */
- xpRetry, /* 8: retry operation */
- xpTimeout, /* 9: timeout in xpc_allocate_msg_wait() */
- xpInterrupted, /* 10: interrupted wait */
-
- xpUnequalMsgSizes, /* 11: message size disparity between sides */
- xpInvalidAddress, /* 12: invalid address */
-
- xpNoMemory, /* 13: no memory available for XPC structures */
- xpLackOfResources, /* 14: insufficient resources for operation */
- xpUnregistered, /* 15: channel is not registered */
- xpAlreadyRegistered, /* 16: channel is already registered */
-
- xpPartitionDown, /* 17: remote partition is down */
- xpNotLoaded, /* 18: XPC module is not loaded */
- xpUnloading, /* 19: this side is unloading XPC module */
-
- xpBadMagic, /* 20: XPC MAGIC string not found */
-
- xpReactivating, /* 21: remote partition was reactivated */
-
- xpUnregistering, /* 22: this side is unregistering channel */
- xpOtherUnregistering, /* 23: other side is unregistering channel */
-
- xpCloneKThread, /* 24: cloning kernel thread */
- xpCloneKThreadFailed, /* 25: cloning kernel thread failed */
-
- xpNoHeartbeat, /* 26: remote partition has no heartbeat */
-
- xpPioReadError, /* 27: PIO read error */
- xpPhysAddrRegFailed, /* 28: registration of phys addr range failed */
-
- xpRETIRED3, /* 29: (formerly xpBteDirectoryError) */
- xpRETIRED4, /* 30: (formerly xpBtePoisonError) */
- xpRETIRED5, /* 31: (formerly xpBteWriteError) */
- xpRETIRED6, /* 32: (formerly xpBteAccessError) */
- xpRETIRED7, /* 33: (formerly xpBtePWriteError) */
- xpRETIRED8, /* 34: (formerly xpBtePReadError) */
- xpRETIRED9, /* 35: (formerly xpBteTimeOutError) */
- xpRETIRED10, /* 36: (formerly xpBteXtalkError) */
- xpRETIRED11, /* 37: (formerly xpBteNotAvailable) */
- xpRETIRED12, /* 38: (formerly xpBteUnmappedError) */
-
- xpBadVersion, /* 39: bad version number */
- xpVarsNotSet, /* 40: the XPC variables are not set up */
- xpNoRsvdPageAddr, /* 41: unable to get rsvd page's phys addr */
- xpInvalidPartid, /* 42: invalid partition ID */
- xpLocalPartid, /* 43: local partition ID */
-
- xpOtherGoingDown, /* 44: other side going down, reason unknown */
- xpSystemGoingDown, /* 45: system is going down, reason unknown */
- xpSystemHalt, /* 46: system is being halted */
- xpSystemReboot, /* 47: system is being rebooted */
- xpSystemPoweroff, /* 48: system is being powered off */
-
- xpDisconnecting, /* 49: channel disconnecting (closing) */
-
- xpOpenCloseError, /* 50: channel open/close protocol error */
-
- xpDisconnected, /* 51: channel disconnected (closed) */
-
- xpBteCopyError, /* 52: bte_copy() returned error */
- xpSalError, /* 53: sn SAL error */
- xpRsvdPageNotSet, /* 54: the reserved page is not set up */
- xpPayloadTooBig, /* 55: payload too large for message slot */
-
- xpUnsupported, /* 56: unsupported functionality or resource */
- xpNeedMoreInfo, /* 57: more info is needed by SAL */
-
- xpGruCopyError, /* 58: gru_copy_gru() returned error */
- xpGruSendMqError, /* 59: gru send message queue related error */
-
- xpBadChannelNumber, /* 60: invalid channel number */
- xpBadMsgType, /* 61: invalid message type */
- xpBiosError, /* 62: BIOS error */
-
- xpUnknownReason /* 63: unknown reason - must be last in enum */
-};
-
-/*
- * Define the callout function type used by XPC to update the user on
- * connection activity and state changes via the user function registered
- * by xpc_connect().
- *
- * Arguments:
- *
- * reason - reason code.
- * partid - partition ID associated with condition.
- * ch_number - channel # associated with condition.
- * data - pointer to optional data.
- * key - pointer to optional user-defined value provided as the "key"
- * argument to xpc_connect().
- *
- * A reason code of xpConnected indicates that a connection has been
- * established to the specified partition on the specified channel. The data
- * argument indicates the max number of entries allowed in the message queue.
- *
- * A reason code of xpMsgReceived indicates that a XPC message arrived from
- * the specified partition on the specified channel. The data argument
- * specifies the address of the message's payload. The user must call
- * xpc_received() when finished with the payload.
- *
- * All other reason codes indicate failure. The data argmument is NULL.
- * When a failure reason code is received, one can assume that the channel
- * is not connected.
- */
-typedef void (*xpc_channel_func) (enum xp_retval reason, short partid,
- int ch_number, void *data, void *key);
-
-/*
- * Define the callout function type used by XPC to notify the user of
- * messages received and delivered via the user function registered by
- * xpc_send_notify().
- *
- * Arguments:
- *
- * reason - reason code.
- * partid - partition ID associated with condition.
- * ch_number - channel # associated with condition.
- * key - pointer to optional user-defined value provided as the "key"
- * argument to xpc_send_notify().
- *
- * A reason code of xpMsgDelivered indicates that the message was delivered
- * to the intended recipient and that they have acknowledged its receipt by
- * calling xpc_received().
- *
- * All other reason codes indicate failure.
- *
- * NOTE: The user defined function must be callable by an interrupt handler
- * and thus cannot block.
- */
-typedef void (*xpc_notify_func) (enum xp_retval reason, short partid,
- int ch_number, void *key);
-
-/*
- * The following is a registration entry. There is a global array of these,
- * one per channel. It is used to record the connection registration made
- * by the users of XPC. As long as a registration entry exists, for any
- * partition that comes up, XPC will attempt to establish a connection on
- * that channel. Notification that a connection has been made will occur via
- * the xpc_channel_func function.
- *
- * The 'func' field points to the function to call when aynchronous
- * notification is required for such events as: a connection established/lost,
- * or an incoming message received, or an error condition encountered. A
- * non-NULL 'func' field indicates that there is an active registration for
- * the channel.
- */
-struct xpc_registration {
- struct mutex mutex;
- xpc_channel_func func; /* function to call */
- void *key; /* pointer to user's key */
- u16 nentries; /* #of msg entries in local msg queue */
- u16 entry_size; /* message queue's message entry size */
- u32 assigned_limit; /* limit on #of assigned kthreads */
- u32 idle_limit; /* limit on #of idle kthreads */
-} ____cacheline_aligned;
-
-#define XPC_CHANNEL_REGISTERED(_c) (xpc_registrations[_c].func != NULL)
-
-/* the following are valid xpc_send() or xpc_send_notify() flags */
-#define XPC_WAIT 0 /* wait flag */
-#define XPC_NOWAIT 1 /* no wait flag */
-
-struct xpc_interface {
- void (*connect) (int);
- void (*disconnect) (int);
- enum xp_retval (*send) (short, int, u32, void *, u16);
- enum xp_retval (*send_notify) (short, int, u32, void *, u16,
- xpc_notify_func, void *);
- void (*received) (short, int, void *);
- enum xp_retval (*partid_to_nasids) (short, void *);
-};
-
-extern struct xpc_interface xpc_interface;
-
-extern void xpc_set_interface(void (*)(int),
- void (*)(int),
- enum xp_retval (*)(short, int, u32, void *, u16),
- enum xp_retval (*)(short, int, u32, void *, u16,
- xpc_notify_func, void *),
- void (*)(short, int, void *),
- enum xp_retval (*)(short, void *));
-extern void xpc_clear_interface(void);
-
-extern enum xp_retval xpc_connect(int, xpc_channel_func, void *, u16,
- u16, u32, u32);
-extern void xpc_disconnect(int);
-
-static inline enum xp_retval
-xpc_send(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size)
-{
- if (!xpc_interface.send)
- return xpNotLoaded;
-
- return xpc_interface.send(partid, ch_number, flags, payload,
- payload_size);
-}
-
-static inline enum xp_retval
-xpc_send_notify(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size, xpc_notify_func func, void *key)
-{
- if (!xpc_interface.send_notify)
- return xpNotLoaded;
-
- return xpc_interface.send_notify(partid, ch_number, flags, payload,
- payload_size, func, key);
-}
-
-static inline void
-xpc_received(short partid, int ch_number, void *payload)
-{
- if (xpc_interface.received)
- xpc_interface.received(partid, ch_number, payload);
-}
-
-static inline enum xp_retval
-xpc_partid_to_nasids(short partid, void *nasids)
-{
- if (!xpc_interface.partid_to_nasids)
- return xpNotLoaded;
-
- return xpc_interface.partid_to_nasids(partid, nasids);
-}
-
-extern short xp_max_npartitions;
-extern short xp_partition_id;
-extern u8 xp_region_size;
-
-extern unsigned long (*xp_pa) (void *);
-extern unsigned long (*xp_socket_pa) (unsigned long);
-extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long,
- size_t);
-extern int (*xp_cpu_to_nasid) (int);
-extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long);
-extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long);
-
-extern struct device *xp;
-extern enum xp_retval xp_init_uv(void);
-extern void xp_exit_uv(void);
-
-#endif /* _DRIVERS_MISC_SGIXP_XP_H */
diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c
deleted file mode 100644
index 87d156c15f35..000000000000
--- a/drivers/misc/sgi-xp/xp_main.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition (XP) base.
- *
- * XP provides a base from which its users can interact
- * with XPC, yet not be dependent on XPC.
- *
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include "xp.h"
-
-/* define the XP debug device structures to be used with dev_dbg() et al */
-
-static struct device_driver xp_dbg_name = {
- .name = "xp"
-};
-
-static struct device xp_dbg_subname = {
- .init_name = "", /* set to "" */
- .driver = &xp_dbg_name
-};
-
-struct device *xp = &xp_dbg_subname;
-
-/* max #of partitions possible */
-short xp_max_npartitions;
-EXPORT_SYMBOL_GPL(xp_max_npartitions);
-
-short xp_partition_id;
-EXPORT_SYMBOL_GPL(xp_partition_id);
-
-u8 xp_region_size;
-EXPORT_SYMBOL_GPL(xp_region_size);
-
-unsigned long (*xp_pa) (void *addr);
-EXPORT_SYMBOL_GPL(xp_pa);
-
-unsigned long (*xp_socket_pa) (unsigned long gpa);
-EXPORT_SYMBOL_GPL(xp_socket_pa);
-
-enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa,
- const unsigned long src_gpa, size_t len);
-EXPORT_SYMBOL_GPL(xp_remote_memcpy);
-
-int (*xp_cpu_to_nasid) (int cpuid);
-EXPORT_SYMBOL_GPL(xp_cpu_to_nasid);
-
-enum xp_retval (*xp_expand_memprotect) (unsigned long phys_addr,
- unsigned long size);
-EXPORT_SYMBOL_GPL(xp_expand_memprotect);
-enum xp_retval (*xp_restrict_memprotect) (unsigned long phys_addr,
- unsigned long size);
-EXPORT_SYMBOL_GPL(xp_restrict_memprotect);
-
-/*
- * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level
- * users of XPC.
- */
-struct xpc_registration xpc_registrations[XPC_MAX_NCHANNELS];
-EXPORT_SYMBOL_GPL(xpc_registrations);
-
-/*
- * Initialize the XPC interface to NULL to indicate that XPC isn't loaded.
- */
-struct xpc_interface xpc_interface = { };
-EXPORT_SYMBOL_GPL(xpc_interface);
-
-/*
- * XPC calls this when it (the XPC module) has been loaded.
- */
-void
-xpc_set_interface(void (*connect) (int),
- void (*disconnect) (int),
- enum xp_retval (*send) (short, int, u32, void *, u16),
- enum xp_retval (*send_notify) (short, int, u32, void *, u16,
- xpc_notify_func, void *),
- void (*received) (short, int, void *),
- enum xp_retval (*partid_to_nasids) (short, void *))
-{
- xpc_interface.connect = connect;
- xpc_interface.disconnect = disconnect;
- xpc_interface.send = send;
- xpc_interface.send_notify = send_notify;
- xpc_interface.received = received;
- xpc_interface.partid_to_nasids = partid_to_nasids;
-}
-EXPORT_SYMBOL_GPL(xpc_set_interface);
-
-/*
- * XPC calls this when it (the XPC module) is being unloaded.
- */
-void
-xpc_clear_interface(void)
-{
- memset(&xpc_interface, 0, sizeof(xpc_interface));
-}
-EXPORT_SYMBOL_GPL(xpc_clear_interface);
-
-/*
- * Register for automatic establishment of a channel connection whenever
- * a partition comes up.
- *
- * Arguments:
- *
- * ch_number - channel # to register for connection.
- * func - function to call for asynchronous notification of channel
- * state changes (i.e., connection, disconnection, error) and
- * the arrival of incoming messages.
- * key - pointer to optional user-defined value that gets passed back
- * to the user on any callouts made to func.
- * payload_size - size in bytes of the XPC message's payload area which
- * contains a user-defined message. The user should make
- * this large enough to hold their largest message.
- * nentries - max #of XPC message entries a message queue can contain.
- * The actual number, which is determined when a connection
- * is established and may be less then requested, will be
- * passed to the user via the xpConnected callout.
- * assigned_limit - max number of kthreads allowed to be processing
- * messages (per connection) at any given instant.
- * idle_limit - max number of kthreads allowed to be idle at any given
- * instant.
- */
-enum xp_retval
-xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size,
- u16 nentries, u32 assigned_limit, u32 idle_limit)
-{
- struct xpc_registration *registration;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
- DBUG_ON(payload_size == 0 || nentries == 0);
- DBUG_ON(func == NULL);
- DBUG_ON(assigned_limit == 0 || idle_limit > assigned_limit);
-
- if (XPC_MSG_SIZE(payload_size) > XPC_MSG_MAX_SIZE)
- return xpPayloadTooBig;
-
- registration = &xpc_registrations[ch_number];
-
- if (mutex_lock_interruptible(&registration->mutex) != 0)
- return xpInterrupted;
-
- /* if XPC_CHANNEL_REGISTERED(ch_number) */
- if (registration->func != NULL) {
- mutex_unlock(&registration->mutex);
- return xpAlreadyRegistered;
- }
-
- /* register the channel for connection */
- registration->entry_size = XPC_MSG_SIZE(payload_size);
- registration->nentries = nentries;
- registration->assigned_limit = assigned_limit;
- registration->idle_limit = idle_limit;
- registration->key = key;
- registration->func = func;
-
- mutex_unlock(&registration->mutex);
-
- if (xpc_interface.connect)
- xpc_interface.connect(ch_number);
-
- return xpSuccess;
-}
-EXPORT_SYMBOL_GPL(xpc_connect);
-
-/*
- * Remove the registration for automatic connection of the specified channel
- * when a partition comes up.
- *
- * Before returning this xpc_disconnect() will wait for all connections on the
- * specified channel have been closed/torndown. So the caller can be assured
- * that they will not be receiving any more callouts from XPC to their
- * function registered via xpc_connect().
- *
- * Arguments:
- *
- * ch_number - channel # to unregister.
- */
-void
-xpc_disconnect(int ch_number)
-{
- struct xpc_registration *registration;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
-
- registration = &xpc_registrations[ch_number];
-
- /*
- * We've decided not to make this a down_interruptible(), since we
- * figured XPC's users will just turn around and call xpc_disconnect()
- * again anyways, so we might as well wait, if need be.
- */
- mutex_lock(&registration->mutex);
-
- /* if !XPC_CHANNEL_REGISTERED(ch_number) */
- if (registration->func == NULL) {
- mutex_unlock(&registration->mutex);
- return;
- }
-
- /* remove the connection registration for the specified channel */
- registration->func = NULL;
- registration->key = NULL;
- registration->nentries = 0;
- registration->entry_size = 0;
- registration->assigned_limit = 0;
- registration->idle_limit = 0;
-
- if (xpc_interface.disconnect)
- xpc_interface.disconnect(ch_number);
-
- mutex_unlock(&registration->mutex);
-
- return;
-}
-EXPORT_SYMBOL_GPL(xpc_disconnect);
-
-static int __init
-xp_init(void)
-{
- enum xp_retval ret;
- int ch_number;
-
- /* initialize the connection registration mutex */
- for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++)
- mutex_init(&xpc_registrations[ch_number].mutex);
-
- if (is_uv_system())
- ret = xp_init_uv();
- else
- ret = 0;
-
- if (ret != xpSuccess)
- return ret;
-
- return 0;
-}
-
-module_init(xp_init);
-
-static void __exit
-xp_exit(void)
-{
- if (is_uv_system())
- xp_exit_uv();
-}
-
-module_exit(xp_exit);
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_DESCRIPTION("Cross Partition (XP) base");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c
deleted file mode 100644
index 3faa7eadf679..000000000000
--- a/drivers/misc/sgi-xp/xp_uv.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition (XP) uv-based functions.
- *
- * Architecture specific implementation of common functions.
- *
- */
-
-#include <linux/device.h>
-#include <asm/uv/uv_hub.h>
-#if defined CONFIG_X86_64
-#include <asm/uv/bios.h>
-#endif
-#include "../sgi-gru/grukservices.h"
-#include "xp.h"
-
-/*
- * Convert a virtual memory address to a physical memory address.
- */
-static unsigned long
-xp_pa_uv(void *addr)
-{
- return uv_gpa(addr);
-}
-
-/*
- * Convert a global physical to socket physical address.
- */
-static unsigned long
-xp_socket_pa_uv(unsigned long gpa)
-{
- return uv_gpa_to_soc_phys_ram(gpa);
-}
-
-static enum xp_retval
-xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa,
- size_t len)
-{
- int ret;
- unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa));
-
- BUG_ON(!uv_gpa_in_mmr_space(src_gpa));
- BUG_ON(len != 8);
-
- ret = gru_read_gpa(dst_va, src_gpa);
- if (ret == 0)
- return xpSuccess;
-
- dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx "
- "len=%ld\n", dst_gpa, src_gpa, len);
- return xpGruCopyError;
-}
-
-
-static enum xp_retval
-xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa,
- size_t len)
-{
- int ret;
-
- if (uv_gpa_in_mmr_space(src_gpa))
- return xp_remote_mmr_read(dst_gpa, src_gpa, len);
-
- ret = gru_copy_gpa(dst_gpa, src_gpa, len);
- if (ret == 0)
- return xpSuccess;
-
- dev_err(xp, "gru_copy_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx "
- "len=%ld\n", dst_gpa, src_gpa, len);
- return xpGruCopyError;
-}
-
-static int
-xp_cpu_to_nasid_uv(int cpuid)
-{
- /* ??? Is this same as sn2 nasid in mach/part bitmaps set up by SAL? */
- return UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpuid));
-}
-
-static enum xp_retval
-xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size)
-{
- int ret;
-
-#if defined CONFIG_X86_64
- ret = uv_bios_change_memprotect(phys_addr, size, UV_MEMPROT_ALLOW_RW);
- if (ret != BIOS_STATUS_SUCCESS) {
- dev_err(xp, "uv_bios_change_memprotect(,, "
- "UV_MEMPROT_ALLOW_RW) failed, ret=%d\n", ret);
- return xpBiosError;
- }
-#else
- #error not a supported configuration
-#endif
- return xpSuccess;
-}
-
-static enum xp_retval
-xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size)
-{
- int ret;
-
-#if defined CONFIG_X86_64
- ret = uv_bios_change_memprotect(phys_addr, size,
- UV_MEMPROT_RESTRICT_ACCESS);
- if (ret != BIOS_STATUS_SUCCESS) {
- dev_err(xp, "uv_bios_change_memprotect(,, "
- "UV_MEMPROT_RESTRICT_ACCESS) failed, ret=%d\n", ret);
- return xpBiosError;
- }
-#else
- #error not a supported configuration
-#endif
- return xpSuccess;
-}
-
-enum xp_retval
-xp_init_uv(void)
-{
- WARN_ON(!is_uv_system());
- if (!is_uv_system())
- return xpUnsupported;
-
- xp_max_npartitions = XP_MAX_NPARTITIONS_UV;
-#ifdef CONFIG_X86
- xp_partition_id = sn_partition_id;
- xp_region_size = sn_region_size;
-#endif
- xp_pa = xp_pa_uv;
- xp_socket_pa = xp_socket_pa_uv;
- xp_remote_memcpy = xp_remote_memcpy_uv;
- xp_cpu_to_nasid = xp_cpu_to_nasid_uv;
- xp_expand_memprotect = xp_expand_memprotect_uv;
- xp_restrict_memprotect = xp_restrict_memprotect_uv;
-
- return xpSuccess;
-}
-
-void
-xp_exit_uv(void)
-{
- WARN_ON(!is_uv_system());
-}
diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h
deleted file mode 100644
index 225f2bb84e39..000000000000
--- a/drivers/misc/sgi-xp/xpc.h
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) structures and macros.
- */
-
-#ifndef _DRIVERS_MISC_SGIXP_XPC_H
-#define _DRIVERS_MISC_SGIXP_XPC_H
-
-#include <linux/wait.h>
-#include <linux/completion.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include "xp.h"
-
-/*
- * XPC Version numbers consist of a major and minor number. XPC can always
- * talk to versions with same major #, and never talk to versions with a
- * different major #.
- */
-#define _XPC_VERSION(_maj, _min) (((_maj) << 4) | ((_min) & 0xf))
-#define XPC_VERSION_MAJOR(_v) ((_v) >> 4)
-#define XPC_VERSION_MINOR(_v) ((_v) & 0xf)
-
-/* define frequency of the heartbeat and frequency how often it's checked */
-#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */
-#define XPC_HB_CHECK_DEFAULT_INTERVAL 20 /* check HB every x secs */
-
-/* define the process name of HB checker and the CPU it is pinned to */
-#define XPC_HB_CHECK_THREAD_NAME "xpc_hb"
-#define XPC_HB_CHECK_CPU 0
-
-/* define the process name of the discovery thread */
-#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery"
-
-/*
- * the reserved page
- *
- * SAL reserves one page of memory per partition for XPC. Though a full page
- * in length (16384 bytes), its starting address is not page aligned, but it
- * is cacheline aligned. The reserved page consists of the following:
- *
- * reserved page header
- *
- * The first two 64-byte cachelines of the reserved page contain the
- * header (struct xpc_rsvd_page). Before SAL initialization has completed,
- * SAL has set up the following fields of the reserved page header:
- * SAL_signature, SAL_version, SAL_partid, and SAL_nasids_size. The
- * other fields are set up by XPC. (xpc_rsvd_page points to the local
- * partition's reserved page.)
- *
- * part_nasids mask
- * mach_nasids mask
- *
- * SAL also sets up two bitmaps (or masks), one that reflects the actual
- * nasids in this partition (part_nasids), and the other that reflects
- * the actual nasids in the entire machine (mach_nasids). We're only
- * interested in the even numbered nasids (which contain the processors
- * and/or memory), so we only need half as many bits to represent the
- * nasids. When mapping nasid to bit in a mask (or bit to nasid) be sure
- * to either divide or multiply by 2. The part_nasids mask is located
- * starting at the first cacheline following the reserved page header. The
- * mach_nasids mask follows right after the part_nasids mask. The size in
- * bytes of each mask is reflected by the reserved page header field
- * 'SAL_nasids_size'. (Local partition's mask pointers are xpc_part_nasids
- * and xpc_mach_nasids.)
- *
- * Immediately following the mach_nasids mask are the XPC variables
- * required by other partitions. First are those that are generic to all
- * partitions (vars), followed on the next available cacheline by those
- * which are partition specific (vars part). These are setup by XPC.
- *
- * Note: Until 'ts_jiffies' is set non-zero, the partition XPC code has not been
- * initialized.
- */
-struct xpc_rsvd_page {
- u64 SAL_signature; /* SAL: unique signature */
- u64 SAL_version; /* SAL: version */
- short SAL_partid; /* SAL: partition ID */
- short max_npartitions; /* value of XPC_MAX_PARTITIONS */
- u8 version;
- u8 pad1[3]; /* align to next u64 in 1st 64-byte cacheline */
- unsigned long ts_jiffies; /* timestamp when rsvd pg was setup by XPC */
- union {
- struct {
- unsigned long heartbeat_gpa; /* phys addr */
- unsigned long activate_gru_mq_desc_gpa; /* phys addr */
- } uv;
- } sn;
- u64 pad2[9]; /* align to last u64 in 2nd 64-byte cacheline */
- u64 SAL_nasids_size; /* SAL: size of each nasid mask in bytes */
-};
-
-#define XPC_RP_VERSION _XPC_VERSION(3, 0) /* version 3.0 of the reserved page */
-
-/* the reserved page sizes and offsets */
-
-#define XPC_RP_HEADER_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page))
-
-#define XPC_RP_PART_NASIDS(_rp) ((unsigned long *)((u8 *)(_rp) + \
- XPC_RP_HEADER_SIZE))
-#define XPC_RP_MACH_NASIDS(_rp) (XPC_RP_PART_NASIDS(_rp) + \
- xpc_nasid_mask_nlongs)
-
-
-/*
- * The following structure describes the partition's heartbeat info which
- * will be periodically read by other partitions to determine whether this
- * XPC is still 'alive'.
- */
-struct xpc_heartbeat_uv {
- unsigned long value;
- unsigned long offline; /* if 0, heartbeat should be changing */
-};
-
-/*
- * Info pertinent to a GRU message queue using a watch list for irq generation.
- */
-struct xpc_gru_mq_uv {
- void *address; /* address of GRU message queue */
- unsigned int order; /* size of GRU message queue as a power of 2 */
- int irq; /* irq raised when message is received in mq */
- int mmr_blade; /* blade where watchlist was allocated from */
- unsigned long mmr_offset; /* offset of irq mmr located on mmr_blade */
- unsigned long mmr_value; /* value of irq mmr located on mmr_blade */
- int watchlist_num; /* number of watchlist allocatd by BIOS */
- void *gru_mq_desc; /* opaque structure used by the GRU driver */
-};
-
-/*
- * The activate_mq is used to send/receive GRU messages that affect XPC's
- * partition active state and channel state. This is uv only.
- */
-struct xpc_activate_mq_msghdr_uv {
- unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */
- short partid; /* sender's partid */
- u8 act_state; /* sender's act_state at time msg sent */
- u8 type; /* message's type */
- unsigned long rp_ts_jiffies; /* timestamp of sender's rp setup by XPC */
-};
-
-/* activate_mq defined message types */
-#define XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV 0
-
-#define XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV 1
-#define XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV 2
-
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV 3
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV 4
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV 5
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV 6
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV 7
-
-#define XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV 8
-#define XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV 9
-
-struct xpc_activate_mq_msg_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
-};
-
-struct xpc_activate_mq_msg_activate_req_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- unsigned long rp_gpa;
- unsigned long heartbeat_gpa;
- unsigned long activate_gru_mq_desc_gpa;
-};
-
-struct xpc_activate_mq_msg_deactivate_req_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- enum xp_retval reason;
-};
-
-struct xpc_activate_mq_msg_chctl_closerequest_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
- enum xp_retval reason;
-};
-
-struct xpc_activate_mq_msg_chctl_closereply_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
-};
-
-struct xpc_activate_mq_msg_chctl_openrequest_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
- short entry_size; /* size of notify_mq's GRU messages */
- short local_nentries; /* ??? Is this needed? What is? */
-};
-
-struct xpc_activate_mq_msg_chctl_openreply_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
- short remote_nentries; /* ??? Is this needed? What is? */
- short local_nentries; /* ??? Is this needed? What is? */
- unsigned long notify_gru_mq_desc_gpa;
-};
-
-struct xpc_activate_mq_msg_chctl_opencomplete_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
-};
-
-/*
- * Functions registered by add_timer() or called by kernel_thread() only
- * allow for a single 64-bit argument. The following macros can be used to
- * pack and unpack two (32-bit, 16-bit or 8-bit) arguments into or out from
- * the passed argument.
- */
-#define XPC_PACK_ARGS(_arg1, _arg2) \
- ((((u64)_arg1) & 0xffffffff) | \
- ((((u64)_arg2) & 0xffffffff) << 32))
-
-#define XPC_UNPACK_ARG1(_args) (((u64)_args) & 0xffffffff)
-#define XPC_UNPACK_ARG2(_args) ((((u64)_args) >> 32) & 0xffffffff)
-
-/*
- * Define a structure that contains arguments associated with opening and
- * closing a channel.
- */
-struct xpc_openclose_args {
- u16 reason; /* reason why channel is closing */
- u16 entry_size; /* sizeof each message entry */
- u16 remote_nentries; /* #of message entries in remote msg queue */
- u16 local_nentries; /* #of message entries in local msg queue */
- unsigned long local_msgqueue_pa; /* phys addr of local message queue */
-};
-
-#define XPC_OPENCLOSE_ARGS_SIZE \
- L1_CACHE_ALIGN(sizeof(struct xpc_openclose_args) * \
- XPC_MAX_NCHANNELS)
-
-
-/*
- * Structures to define a fifo singly-linked list.
- */
-
-struct xpc_fifo_entry_uv {
- struct xpc_fifo_entry_uv *next;
-};
-
-struct xpc_fifo_head_uv {
- struct xpc_fifo_entry_uv *first;
- struct xpc_fifo_entry_uv *last;
- spinlock_t lock;
- int n_entries;
-};
-
-/*
- * The format of a uv XPC notify_mq GRU message is as follows:
- *
- * A user-defined message resides in the payload area. The max size of the
- * payload is defined by the user via xpc_connect().
- *
- * The size of a message (payload and header) sent via the GRU must be either 1
- * or 2 GRU_CACHE_LINE_BYTES in length.
- */
-
-struct xpc_notify_mq_msghdr_uv {
- union {
- unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */
- struct xpc_fifo_entry_uv next; /* FOR XPC INTERNAL USE ONLY */
- } u;
- short partid; /* FOR XPC INTERNAL USE ONLY */
- u8 ch_number; /* FOR XPC INTERNAL USE ONLY */
- u8 size; /* FOR XPC INTERNAL USE ONLY */
- unsigned int msg_slot_number; /* FOR XPC INTERNAL USE ONLY */
-};
-
-struct xpc_notify_mq_msg_uv {
- struct xpc_notify_mq_msghdr_uv hdr;
- unsigned long payload;
-};
-
-/* struct xpc_notify_sn2 type of notification */
-
-#define XPC_N_CALL 0x01 /* notify function provided by user */
-
-/*
- * Define uv's version of the notify entry. It additionally is used to allocate
- * a msg slot on the remote partition into which is copied a sent message.
- */
-struct xpc_send_msg_slot_uv {
- struct xpc_fifo_entry_uv next;
- unsigned int msg_slot_number;
- xpc_notify_func func; /* user's notify function */
- void *key; /* pointer to user's key */
-};
-
-/*
- * Define the structure that manages all the stuff required by a channel. In
- * particular, they are used to manage the messages sent across the channel.
- *
- * This structure is private to a partition, and is NOT shared across the
- * partition boundary.
- *
- * There is an array of these structures for each remote partition. It is
- * allocated at the time a partition becomes active. The array contains one
- * of these structures for each potential channel connection to that partition.
- */
-
-struct xpc_channel_uv {
- void *cached_notify_gru_mq_desc; /* remote partition's notify mq's */
- /* gru mq descriptor */
-
- struct xpc_send_msg_slot_uv *send_msg_slots;
- void *recv_msg_slots; /* each slot will hold a xpc_notify_mq_msg_uv */
- /* structure plus the user's payload */
-
- struct xpc_fifo_head_uv msg_slot_free_list;
- struct xpc_fifo_head_uv recv_msg_list; /* deliverable payloads */
-};
-
-struct xpc_channel {
- short partid; /* ID of remote partition connected */
- spinlock_t lock; /* lock for updating this structure */
- unsigned int flags; /* general flags */
-
- enum xp_retval reason; /* reason why channel is disconnect'g */
- int reason_line; /* line# disconnect initiated from */
-
- u16 number; /* channel # */
-
- u16 entry_size; /* sizeof each msg entry */
- u16 local_nentries; /* #of msg entries in local msg queue */
- u16 remote_nentries; /* #of msg entries in remote msg queue */
-
- atomic_t references; /* #of external references to queues */
-
- atomic_t n_on_msg_allocate_wq; /* #on msg allocation wait queue */
- wait_queue_head_t msg_allocate_wq; /* msg allocation wait queue */
-
- u8 delayed_chctl_flags; /* chctl flags received, but delayed */
- /* action until channel disconnected */
-
- atomic_t n_to_notify; /* #of msg senders to notify */
-
- xpc_channel_func func; /* user's channel function */
- void *key; /* pointer to user's key */
-
- struct completion wdisconnect_wait; /* wait for channel disconnect */
-
- /* kthread management related fields */
-
- atomic_t kthreads_assigned; /* #of kthreads assigned to channel */
- u32 kthreads_assigned_limit; /* limit on #of kthreads assigned */
- atomic_t kthreads_idle; /* #of kthreads idle waiting for work */
- u32 kthreads_idle_limit; /* limit on #of kthreads idle */
- atomic_t kthreads_active; /* #of kthreads actively working */
-
- wait_queue_head_t idle_wq; /* idle kthread wait queue */
-
- union {
- struct xpc_channel_uv uv;
- } sn;
-
-} ____cacheline_aligned;
-
-/* struct xpc_channel flags */
-
-#define XPC_C_WASCONNECTED 0x00000001 /* channel was connected */
-
-#define XPC_C_ROPENCOMPLETE 0x00000002 /* remote open channel complete */
-#define XPC_C_OPENCOMPLETE 0x00000004 /* local open channel complete */
-#define XPC_C_ROPENREPLY 0x00000008 /* remote open channel reply */
-#define XPC_C_OPENREPLY 0x00000010 /* local open channel reply */
-#define XPC_C_ROPENREQUEST 0x00000020 /* remote open channel request */
-#define XPC_C_OPENREQUEST 0x00000040 /* local open channel request */
-
-#define XPC_C_SETUP 0x00000080 /* channel's msgqueues are alloc'd */
-#define XPC_C_CONNECTEDCALLOUT 0x00000100 /* connected callout initiated */
-#define XPC_C_CONNECTEDCALLOUT_MADE \
- 0x00000200 /* connected callout completed */
-#define XPC_C_CONNECTED 0x00000400 /* local channel is connected */
-#define XPC_C_CONNECTING 0x00000800 /* channel is being connected */
-
-#define XPC_C_RCLOSEREPLY 0x00001000 /* remote close channel reply */
-#define XPC_C_CLOSEREPLY 0x00002000 /* local close channel reply */
-#define XPC_C_RCLOSEREQUEST 0x00004000 /* remote close channel request */
-#define XPC_C_CLOSEREQUEST 0x00008000 /* local close channel request */
-
-#define XPC_C_DISCONNECTED 0x00010000 /* channel is disconnected */
-#define XPC_C_DISCONNECTING 0x00020000 /* channel is being disconnected */
-#define XPC_C_DISCONNECTINGCALLOUT \
- 0x00040000 /* disconnecting callout initiated */
-#define XPC_C_DISCONNECTINGCALLOUT_MADE \
- 0x00080000 /* disconnecting callout completed */
-#define XPC_C_WDISCONNECT 0x00100000 /* waiting for channel disconnect */
-
-/*
- * The channel control flags (chctl) union consists of a 64-bit variable which
- * is divided up into eight bytes, ordered from right to left. Byte zero
- * pertains to channel 0, byte one to channel 1, and so on. Each channel's byte
- * can have one or more of the chctl flags set in it.
- */
-
-union xpc_channel_ctl_flags {
- u64 all_flags;
- u8 flags[XPC_MAX_NCHANNELS];
-};
-
-/* chctl flags */
-#define XPC_CHCTL_CLOSEREQUEST 0x01
-#define XPC_CHCTL_CLOSEREPLY 0x02
-#define XPC_CHCTL_OPENREQUEST 0x04
-#define XPC_CHCTL_OPENREPLY 0x08
-#define XPC_CHCTL_OPENCOMPLETE 0x10
-#define XPC_CHCTL_MSGREQUEST 0x20
-
-#define XPC_OPENCLOSE_CHCTL_FLAGS \
- (XPC_CHCTL_CLOSEREQUEST | XPC_CHCTL_CLOSEREPLY | \
- XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | \
- XPC_CHCTL_OPENCOMPLETE)
-#define XPC_MSG_CHCTL_FLAGS XPC_CHCTL_MSGREQUEST
-
-static inline int
-xpc_any_openclose_chctl_flags_set(union xpc_channel_ctl_flags *chctl)
-{
- int ch_number;
-
- for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) {
- if (chctl->flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS)
- return 1;
- }
- return 0;
-}
-
-static inline int
-xpc_any_msg_chctl_flags_set(union xpc_channel_ctl_flags *chctl)
-{
- int ch_number;
-
- for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) {
- if (chctl->flags[ch_number] & XPC_MSG_CHCTL_FLAGS)
- return 1;
- }
- return 0;
-}
-
-struct xpc_partition_uv {
- unsigned long heartbeat_gpa; /* phys addr of partition's heartbeat */
- struct xpc_heartbeat_uv cached_heartbeat; /* cached copy of */
- /* partition's heartbeat */
- unsigned long activate_gru_mq_desc_gpa; /* phys addr of parititon's */
- /* activate mq's gru mq */
- /* descriptor */
- void *cached_activate_gru_mq_desc; /* cached copy of partition's */
- /* activate mq's gru mq descriptor */
- struct mutex cached_activate_gru_mq_desc_mutex;
- spinlock_t flags_lock; /* protect updating of flags */
- unsigned int flags; /* general flags */
- u8 remote_act_state; /* remote partition's act_state */
- u8 act_state_req; /* act_state request from remote partition */
- enum xp_retval reason; /* reason for deactivate act_state request */
-};
-
-/* struct xpc_partition_uv flags */
-
-#define XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV 0x00000001
-#define XPC_P_ENGAGED_UV 0x00000002
-
-/* struct xpc_partition_uv act_state change requests */
-
-#define XPC_P_ASR_ACTIVATE_UV 0x01
-#define XPC_P_ASR_REACTIVATE_UV 0x02
-#define XPC_P_ASR_DEACTIVATE_UV 0x03
-
-struct xpc_partition {
-
- /* XPC HB infrastructure */
-
- u8 remote_rp_version; /* version# of partition's rsvd pg */
- unsigned long remote_rp_ts_jiffies; /* timestamp when rsvd pg setup */
- unsigned long remote_rp_pa; /* phys addr of partition's rsvd pg */
- u64 last_heartbeat; /* HB at last read */
- u32 activate_IRQ_rcvd; /* IRQs since activation */
- spinlock_t act_lock; /* protect updating of act_state */
- u8 act_state; /* from XPC HB viewpoint */
- enum xp_retval reason; /* reason partition is deactivating */
- int reason_line; /* line# deactivation initiated from */
-
- unsigned long disengage_timeout; /* timeout in jiffies */
- struct timer_list disengage_timer;
-
- /* XPC infrastructure referencing and teardown control */
-
- u8 setup_state; /* infrastructure setup state */
- wait_queue_head_t teardown_wq; /* kthread waiting to teardown infra */
- atomic_t references; /* #of references to infrastructure */
-
- u8 nchannels; /* #of defined channels supported */
- atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */
- atomic_t nchannels_engaged; /* #of channels engaged with remote part */
- struct xpc_channel *channels; /* array of channel structures */
-
- /* fields used for managing channel avialability and activity */
-
- union xpc_channel_ctl_flags chctl; /* chctl flags yet to be processed */
- spinlock_t chctl_lock; /* chctl flags lock */
-
- void *remote_openclose_args_base; /* base address of kmalloc'd space */
- struct xpc_openclose_args *remote_openclose_args; /* copy of remote's */
- /* args */
-
- /* channel manager related fields */
-
- atomic_t channel_mgr_requests; /* #of requests to activate chan mgr */
- wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */
-
- union {
- struct xpc_partition_uv uv;
- } sn;
-
-} ____cacheline_aligned;
-
-struct xpc_arch_operations {
- int (*setup_partitions) (void);
- void (*teardown_partitions) (void);
- void (*process_activate_IRQ_rcvd) (void);
- enum xp_retval (*get_partition_rsvd_page_pa)
- (void *, u64 *, unsigned long *, size_t *);
- int (*setup_rsvd_page) (struct xpc_rsvd_page *);
-
- void (*allow_hb) (short);
- void (*disallow_hb) (short);
- void (*disallow_all_hbs) (void);
- void (*increment_heartbeat) (void);
- void (*offline_heartbeat) (void);
- void (*online_heartbeat) (void);
- void (*heartbeat_init) (void);
- void (*heartbeat_exit) (void);
- enum xp_retval (*get_remote_heartbeat) (struct xpc_partition *);
-
- void (*request_partition_activation) (struct xpc_rsvd_page *,
- unsigned long, int);
- void (*request_partition_reactivation) (struct xpc_partition *);
- void (*request_partition_deactivation) (struct xpc_partition *);
- void (*cancel_partition_deactivation_request) (struct xpc_partition *);
- enum xp_retval (*setup_ch_structures) (struct xpc_partition *);
- void (*teardown_ch_structures) (struct xpc_partition *);
-
- enum xp_retval (*make_first_contact) (struct xpc_partition *);
-
- u64 (*get_chctl_all_flags) (struct xpc_partition *);
- void (*send_chctl_closerequest) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_closereply) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_openrequest) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_openreply) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_opencomplete) (struct xpc_channel *, unsigned long *);
- void (*process_msg_chctl_flags) (struct xpc_partition *, int);
-
- enum xp_retval (*save_remote_msgqueue_pa) (struct xpc_channel *,
- unsigned long);
-
- enum xp_retval (*setup_msg_structures) (struct xpc_channel *);
- void (*teardown_msg_structures) (struct xpc_channel *);
-
- void (*indicate_partition_engaged) (struct xpc_partition *);
- void (*indicate_partition_disengaged) (struct xpc_partition *);
- void (*assume_partition_disengaged) (short);
- int (*partition_engaged) (short);
- int (*any_partition_engaged) (void);
-
- int (*n_of_deliverable_payloads) (struct xpc_channel *);
- enum xp_retval (*send_payload) (struct xpc_channel *, u32, void *,
- u16, u8, xpc_notify_func, void *);
- void *(*get_deliverable_payload) (struct xpc_channel *);
- void (*received_payload) (struct xpc_channel *, void *);
- void (*notify_senders_of_disconnect) (struct xpc_channel *);
-};
-
-/* struct xpc_partition act_state values (for XPC HB) */
-
-#define XPC_P_AS_INACTIVE 0x00 /* partition is not active */
-#define XPC_P_AS_ACTIVATION_REQ 0x01 /* created thread to activate */
-#define XPC_P_AS_ACTIVATING 0x02 /* activation thread started */
-#define XPC_P_AS_ACTIVE 0x03 /* xpc_partition_up() was called */
-#define XPC_P_AS_DEACTIVATING 0x04 /* partition deactivation initiated */
-
-#define XPC_DEACTIVATE_PARTITION(_p, _reason) \
- xpc_deactivate_partition(__LINE__, (_p), (_reason))
-
-/* struct xpc_partition setup_state values */
-
-#define XPC_P_SS_UNSET 0x00 /* infrastructure was never setup */
-#define XPC_P_SS_SETUP 0x01 /* infrastructure is setup */
-#define XPC_P_SS_WTEARDOWN 0x02 /* waiting to teardown infrastructure */
-#define XPC_P_SS_TORNDOWN 0x03 /* infrastructure is torndown */
-
-/* number of seconds to wait for other partitions to disengage */
-#define XPC_DISENGAGE_DEFAULT_TIMELIMIT 90
-
-/* interval in seconds to print 'waiting deactivation' messages */
-#define XPC_DEACTIVATE_PRINTMSG_INTERVAL 10
-
-#define XPC_PARTID(_p) ((short)((_p) - &xpc_partitions[0]))
-
-/* found in xp_main.c */
-extern struct xpc_registration xpc_registrations[];
-
-/* found in xpc_main.c */
-extern struct device *xpc_part;
-extern struct device *xpc_chan;
-extern struct xpc_arch_operations xpc_arch_ops;
-extern int xpc_disengage_timelimit;
-extern int xpc_disengage_timedout;
-extern int xpc_activate_IRQ_rcvd;
-extern spinlock_t xpc_activate_IRQ_rcvd_lock;
-extern wait_queue_head_t xpc_activate_IRQ_wq;
-extern void *xpc_kzalloc_cacheline_aligned(size_t, gfp_t, void **);
-extern void xpc_activate_partition(struct xpc_partition *);
-extern void xpc_activate_kthreads(struct xpc_channel *, int);
-extern void xpc_create_kthreads(struct xpc_channel *, int, int);
-extern void xpc_disconnect_wait(int);
-
-/* found in xpc_uv.c */
-extern int xpc_init_uv(void);
-extern void xpc_exit_uv(void);
-
-/* found in xpc_partition.c */
-extern int xpc_exiting;
-extern int xpc_nasid_mask_nlongs;
-extern struct xpc_rsvd_page *xpc_rsvd_page;
-extern unsigned long *xpc_mach_nasids;
-extern struct xpc_partition *xpc_partitions;
-extern void *xpc_kmalloc_cacheline_aligned(size_t, gfp_t, void **);
-extern int xpc_setup_rsvd_page(void);
-extern void xpc_teardown_rsvd_page(void);
-extern int xpc_identify_activate_IRQ_sender(void);
-extern int xpc_partition_disengaged(struct xpc_partition *);
-extern int xpc_partition_disengaged_from_timer(struct xpc_partition *part);
-extern enum xp_retval xpc_mark_partition_active(struct xpc_partition *);
-extern void xpc_mark_partition_inactive(struct xpc_partition *);
-extern void xpc_discovery(void);
-extern enum xp_retval xpc_get_remote_rp(int, unsigned long *,
- struct xpc_rsvd_page *,
- unsigned long *);
-extern void xpc_deactivate_partition(const int, struct xpc_partition *,
- enum xp_retval);
-extern enum xp_retval xpc_initiate_partid_to_nasids(short, void *);
-
-/* found in xpc_channel.c */
-extern void xpc_initiate_connect(int);
-extern void xpc_initiate_disconnect(int);
-extern enum xp_retval xpc_allocate_msg_wait(struct xpc_channel *);
-extern enum xp_retval xpc_initiate_send(short, int, u32, void *, u16);
-extern enum xp_retval xpc_initiate_send_notify(short, int, u32, void *, u16,
- xpc_notify_func, void *);
-extern void xpc_initiate_received(short, int, void *);
-extern void xpc_process_sent_chctl_flags(struct xpc_partition *);
-extern void xpc_connected_callout(struct xpc_channel *);
-extern void xpc_deliver_payload(struct xpc_channel *);
-extern void xpc_disconnect_channel(const int, struct xpc_channel *,
- enum xp_retval, unsigned long *);
-extern void xpc_disconnect_callout(struct xpc_channel *, enum xp_retval);
-extern void xpc_partition_going_down(struct xpc_partition *, enum xp_retval);
-
-static inline void
-xpc_wakeup_channel_mgr(struct xpc_partition *part)
-{
- if (atomic_inc_return(&part->channel_mgr_requests) == 1)
- wake_up(&part->channel_mgr_wq);
-}
-
-/*
- * These next two inlines are used to keep us from tearing down a channel's
- * msg queues while a thread may be referencing them.
- */
-static inline void
-xpc_msgqueue_ref(struct xpc_channel *ch)
-{
- atomic_inc(&ch->references);
-}
-
-static inline void
-xpc_msgqueue_deref(struct xpc_channel *ch)
-{
- s32 refs = atomic_dec_return(&ch->references);
-
- DBUG_ON(refs < 0);
- if (refs == 0)
- xpc_wakeup_channel_mgr(&xpc_partitions[ch->partid]);
-}
-
-#define XPC_DISCONNECT_CHANNEL(_ch, _reason, _irqflgs) \
- xpc_disconnect_channel(__LINE__, _ch, _reason, _irqflgs)
-
-/*
- * These two inlines are used to keep us from tearing down a partition's
- * setup infrastructure while a thread may be referencing it.
- */
-static inline void
-xpc_part_deref(struct xpc_partition *part)
-{
- s32 refs = atomic_dec_return(&part->references);
-
- DBUG_ON(refs < 0);
- if (refs == 0 && part->setup_state == XPC_P_SS_WTEARDOWN)
- wake_up(&part->teardown_wq);
-}
-
-static inline int
-xpc_part_ref(struct xpc_partition *part)
-{
- int setup;
-
- atomic_inc(&part->references);
- setup = (part->setup_state == XPC_P_SS_SETUP);
- if (!setup)
- xpc_part_deref(part);
-
- return setup;
-}
-
-/*
- * The following macro is to be used for the setting of the reason and
- * reason_line fields in both the struct xpc_channel and struct xpc_partition
- * structures.
- */
-#define XPC_SET_REASON(_p, _reason, _line) \
- { \
- (_p)->reason = _reason; \
- (_p)->reason_line = _line; \
- }
-
-#endif /* _DRIVERS_MISC_SGIXP_XPC_H */
diff --git a/drivers/misc/sgi-xp/xpc_channel.c b/drivers/misc/sgi-xp/xpc_channel.c
deleted file mode 100644
index 8e6607fc8a67..000000000000
--- a/drivers/misc/sgi-xp/xpc_channel.c
+++ /dev/null
@@ -1,1011 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) channel support.
- *
- * This is the part of XPC that manages the channels and
- * sends/receives messages across them to/from other partitions.
- *
- */
-
-#include <linux/device.h>
-#include "xpc.h"
-
-/*
- * Process a connect message from a remote partition.
- *
- * Note: xpc_process_connect() is expecting to be called with the
- * spin_lock_irqsave held and will leave it locked upon return.
- */
-static void
-xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- enum xp_retval ret;
-
- lockdep_assert_held(&ch->lock);
-
- if (!(ch->flags & XPC_C_OPENREQUEST) ||
- !(ch->flags & XPC_C_ROPENREQUEST)) {
- /* nothing more to do for now */
- return;
- }
- DBUG_ON(!(ch->flags & XPC_C_CONNECTING));
-
- if (!(ch->flags & XPC_C_SETUP)) {
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- ret = xpc_arch_ops.setup_msg_structures(ch);
- spin_lock_irqsave(&ch->lock, *irq_flags);
-
- if (ret != xpSuccess)
- XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags);
- else
- ch->flags |= XPC_C_SETUP;
-
- if (ch->flags & XPC_C_DISCONNECTING)
- return;
- }
-
- if (!(ch->flags & XPC_C_OPENREPLY)) {
- ch->flags |= XPC_C_OPENREPLY;
- xpc_arch_ops.send_chctl_openreply(ch, irq_flags);
- }
-
- if (!(ch->flags & XPC_C_ROPENREPLY))
- return;
-
- if (!(ch->flags & XPC_C_OPENCOMPLETE)) {
- ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED);
- xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags);
- }
-
- if (!(ch->flags & XPC_C_ROPENCOMPLETE))
- return;
-
- dev_info(xpc_chan, "channel %d to partition %d connected\n",
- ch->number, ch->partid);
-
- ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */
-}
-
-/*
- * spin_lock_irqsave() is expected to be held on entry.
- */
-static void
-xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_partition *part = &xpc_partitions[ch->partid];
- u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED);
-
- lockdep_assert_held(&ch->lock);
-
- if (!(ch->flags & XPC_C_DISCONNECTING))
- return;
-
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
-
- /* make sure all activity has settled down first */
-
- if (atomic_read(&ch->kthreads_assigned) > 0 ||
- atomic_read(&ch->references) > 0) {
- return;
- }
- DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
- !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE));
-
- if (part->act_state == XPC_P_AS_DEACTIVATING) {
- /* can't proceed until the other side disengages from us */
- if (xpc_arch_ops.partition_engaged(ch->partid))
- return;
-
- } else {
-
- /* as long as the other side is up do the full protocol */
-
- if (!(ch->flags & XPC_C_RCLOSEREQUEST))
- return;
-
- if (!(ch->flags & XPC_C_CLOSEREPLY)) {
- ch->flags |= XPC_C_CLOSEREPLY;
- xpc_arch_ops.send_chctl_closereply(ch, irq_flags);
- }
-
- if (!(ch->flags & XPC_C_RCLOSEREPLY))
- return;
- }
-
- /* wake those waiting for notify completion */
- if (atomic_read(&ch->n_to_notify) > 0) {
- /* we do callout while holding ch->lock, callout can't block */
- xpc_arch_ops.notify_senders_of_disconnect(ch);
- }
-
- /* both sides are disconnected now */
-
- if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) {
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- xpc_disconnect_callout(ch, xpDisconnected);
- spin_lock_irqsave(&ch->lock, *irq_flags);
- }
-
- DBUG_ON(atomic_read(&ch->n_to_notify) != 0);
-
- /* it's now safe to free the channel's message queues */
- xpc_arch_ops.teardown_msg_structures(ch);
-
- ch->func = NULL;
- ch->key = NULL;
- ch->entry_size = 0;
- ch->local_nentries = 0;
- ch->remote_nentries = 0;
- ch->kthreads_assigned_limit = 0;
- ch->kthreads_idle_limit = 0;
-
- /*
- * Mark the channel disconnected and clear all other flags, including
- * XPC_C_SETUP (because of call to
- * xpc_arch_ops.teardown_msg_structures()) but not including
- * XPC_C_WDISCONNECT (if it was set).
- */
- ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT));
-
- atomic_dec(&part->nchannels_active);
-
- if (channel_was_connected) {
- dev_info(xpc_chan, "channel %d to partition %d disconnected, "
- "reason=%d\n", ch->number, ch->partid, ch->reason);
- }
-
- if (ch->flags & XPC_C_WDISCONNECT) {
- /* we won't lose the CPU since we're holding ch->lock */
- complete(&ch->wdisconnect_wait);
- } else if (ch->delayed_chctl_flags) {
- if (part->act_state != XPC_P_AS_DEACTIVATING) {
- /* time to take action on any delayed chctl flags */
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch->number] |=
- ch->delayed_chctl_flags;
- spin_unlock(&part->chctl_lock);
- }
- ch->delayed_chctl_flags = 0;
- }
-}
-
-/*
- * Process a change in the channel's remote connection state.
- */
-static void
-xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number,
- u8 chctl_flags)
-{
- unsigned long irq_flags;
- struct xpc_openclose_args *args =
- &part->remote_openclose_args[ch_number];
- struct xpc_channel *ch = &part->channels[ch_number];
- enum xp_retval reason;
- enum xp_retval ret;
- int create_kthread = 0;
-
- spin_lock_irqsave(&ch->lock, irq_flags);
-
-again:
-
- if ((ch->flags & XPC_C_DISCONNECTED) &&
- (ch->flags & XPC_C_WDISCONNECT)) {
- /*
- * Delay processing chctl flags until thread waiting disconnect
- * has had a chance to see that the channel is disconnected.
- */
- ch->delayed_chctl_flags |= chctl_flags;
- goto out;
- }
-
- if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received "
- "from partid=%d, channel=%d\n", args->reason,
- ch->partid, ch->number);
-
- /*
- * If RCLOSEREQUEST is set, we're probably waiting for
- * RCLOSEREPLY. We should find it and a ROPENREQUEST packed
- * with this RCLOSEREQUEST in the chctl_flags.
- */
-
- if (ch->flags & XPC_C_RCLOSEREQUEST) {
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY));
- DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY);
-
- DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY));
- chctl_flags &= ~XPC_CHCTL_CLOSEREPLY;
- ch->flags |= XPC_C_RCLOSEREPLY;
-
- /* both sides have finished disconnecting */
- xpc_process_disconnect(ch, &irq_flags);
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));
- goto again;
- }
-
- if (ch->flags & XPC_C_DISCONNECTED) {
- if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) {
- if (part->chctl.flags[ch_number] &
- XPC_CHCTL_OPENREQUEST) {
-
- DBUG_ON(ch->delayed_chctl_flags != 0);
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch_number] |=
- XPC_CHCTL_CLOSEREQUEST;
- spin_unlock(&part->chctl_lock);
- }
- goto out;
- }
-
- XPC_SET_REASON(ch, 0, 0);
- ch->flags &= ~XPC_C_DISCONNECTED;
-
- atomic_inc(&part->nchannels_active);
- ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST);
- }
-
- chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY |
- XPC_CHCTL_OPENCOMPLETE);
-
- /*
- * The meaningful CLOSEREQUEST connection state fields are:
- * reason = reason connection is to be closed
- */
-
- ch->flags |= XPC_C_RCLOSEREQUEST;
-
- if (!(ch->flags & XPC_C_DISCONNECTING)) {
- reason = args->reason;
- if (reason <= xpSuccess || reason > xpUnknownReason)
- reason = xpUnknownReason;
- else if (reason == xpUnregistering)
- reason = xpOtherUnregistering;
-
- XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
-
- DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY);
- goto out;
- }
-
- xpc_process_disconnect(ch, &irq_flags);
- }
-
- if (chctl_flags & XPC_CHCTL_CLOSEREPLY) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid="
- "%d, channel=%d\n", ch->partid, ch->number);
-
- if (ch->flags & XPC_C_DISCONNECTED) {
- DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING);
- goto out;
- }
-
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
-
- if (!(ch->flags & XPC_C_RCLOSEREQUEST)) {
- if (part->chctl.flags[ch_number] &
- XPC_CHCTL_CLOSEREQUEST) {
-
- DBUG_ON(ch->delayed_chctl_flags != 0);
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch_number] |=
- XPC_CHCTL_CLOSEREPLY;
- spin_unlock(&part->chctl_lock);
- }
- goto out;
- }
-
- ch->flags |= XPC_C_RCLOSEREPLY;
-
- if (ch->flags & XPC_C_CLOSEREPLY) {
- /* both sides have finished disconnecting */
- xpc_process_disconnect(ch, &irq_flags);
- }
- }
-
- if (chctl_flags & XPC_CHCTL_OPENREQUEST) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, "
- "local_nentries=%d) received from partid=%d, "
- "channel=%d\n", args->entry_size, args->local_nentries,
- ch->partid, ch->number);
-
- if (part->act_state == XPC_P_AS_DEACTIVATING ||
- (ch->flags & XPC_C_ROPENREQUEST)) {
- goto out;
- }
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) {
- ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST;
- goto out;
- }
- DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED |
- XPC_C_OPENREQUEST)));
- DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
- XPC_C_OPENREPLY | XPC_C_CONNECTED));
-
- /*
- * The meaningful OPENREQUEST connection state fields are:
- * entry_size = size of channel's messages in bytes
- * local_nentries = remote partition's local_nentries
- */
- if (args->entry_size == 0 || args->local_nentries == 0) {
- /* assume OPENREQUEST was delayed by mistake */
- goto out;
- }
-
- ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING);
- ch->remote_nentries = args->local_nentries;
-
- if (ch->flags & XPC_C_OPENREQUEST) {
- if (args->entry_size != ch->entry_size) {
- XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
- &irq_flags);
- goto out;
- }
- } else {
- ch->entry_size = args->entry_size;
-
- XPC_SET_REASON(ch, 0, 0);
- ch->flags &= ~XPC_C_DISCONNECTED;
-
- atomic_inc(&part->nchannels_active);
- }
-
- xpc_process_connect(ch, &irq_flags);
- }
-
- if (chctl_flags & XPC_CHCTL_OPENREPLY) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa="
- "0x%lx, local_nentries=%d, remote_nentries=%d) "
- "received from partid=%d, channel=%d\n",
- args->local_msgqueue_pa, args->local_nentries,
- args->remote_nentries, ch->partid, ch->number);
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
- goto out;
-
- if (!(ch->flags & XPC_C_OPENREQUEST)) {
- XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
- &irq_flags);
- goto out;
- }
-
- DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
- DBUG_ON(ch->flags & XPC_C_CONNECTED);
-
- /*
- * The meaningful OPENREPLY connection state fields are:
- * local_msgqueue_pa = physical address of remote
- * partition's local_msgqueue
- * local_nentries = remote partition's local_nentries
- * remote_nentries = remote partition's remote_nentries
- */
- DBUG_ON(args->local_msgqueue_pa == 0);
- DBUG_ON(args->local_nentries == 0);
- DBUG_ON(args->remote_nentries == 0);
-
- ret = xpc_arch_ops.save_remote_msgqueue_pa(ch,
- args->local_msgqueue_pa);
- if (ret != xpSuccess) {
- XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags);
- goto out;
- }
- ch->flags |= XPC_C_ROPENREPLY;
-
- if (args->local_nentries < ch->remote_nentries) {
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
- "remote_nentries=%d, old remote_nentries=%d, "
- "partid=%d, channel=%d\n",
- args->local_nentries, ch->remote_nentries,
- ch->partid, ch->number);
-
- ch->remote_nentries = args->local_nentries;
- }
- if (args->remote_nentries < ch->local_nentries) {
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
- "local_nentries=%d, old local_nentries=%d, "
- "partid=%d, channel=%d\n",
- args->remote_nentries, ch->local_nentries,
- ch->partid, ch->number);
-
- ch->local_nentries = args->remote_nentries;
- }
-
- xpc_process_connect(ch, &irq_flags);
- }
-
- if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from "
- "partid=%d, channel=%d\n", ch->partid, ch->number);
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
- goto out;
-
- if (!(ch->flags & XPC_C_OPENREQUEST) ||
- !(ch->flags & XPC_C_OPENREPLY)) {
- XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
- &irq_flags);
- goto out;
- }
-
- DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
- DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY));
- DBUG_ON(!(ch->flags & XPC_C_CONNECTED));
-
- ch->flags |= XPC_C_ROPENCOMPLETE;
-
- xpc_process_connect(ch, &irq_flags);
- create_kthread = 1;
- }
-
-out:
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- if (create_kthread)
- xpc_create_kthreads(ch, 1, 0);
-}
-
-/*
- * Attempt to establish a channel connection to a remote partition.
- */
-static enum xp_retval
-xpc_connect_channel(struct xpc_channel *ch)
-{
- unsigned long irq_flags;
- struct xpc_registration *registration = &xpc_registrations[ch->number];
-
- if (mutex_trylock(&registration->mutex) == 0)
- return xpRetry;
-
- if (!XPC_CHANNEL_REGISTERED(ch->number)) {
- mutex_unlock(&registration->mutex);
- return xpUnregistered;
- }
-
- spin_lock_irqsave(&ch->lock, irq_flags);
-
- DBUG_ON(ch->flags & XPC_C_CONNECTED);
- DBUG_ON(ch->flags & XPC_C_OPENREQUEST);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- mutex_unlock(&registration->mutex);
- return ch->reason;
- }
-
- /* add info from the channel connect registration to the channel */
-
- ch->kthreads_assigned_limit = registration->assigned_limit;
- ch->kthreads_idle_limit = registration->idle_limit;
- DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
- DBUG_ON(atomic_read(&ch->kthreads_idle) != 0);
- DBUG_ON(atomic_read(&ch->kthreads_active) != 0);
-
- ch->func = registration->func;
- DBUG_ON(registration->func == NULL);
- ch->key = registration->key;
-
- ch->local_nentries = registration->nentries;
-
- if (ch->flags & XPC_C_ROPENREQUEST) {
- if (registration->entry_size != ch->entry_size) {
- /* the local and remote sides aren't the same */
-
- /*
- * Because XPC_DISCONNECT_CHANNEL() can block we're
- * forced to up the registration sema before we unlock
- * the channel lock. But that's okay here because we're
- * done with the part that required the registration
- * sema. XPC_DISCONNECT_CHANNEL() requires that the
- * channel lock be locked and will unlock and relock
- * the channel lock as needed.
- */
- mutex_unlock(&registration->mutex);
- XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
- &irq_flags);
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return xpUnequalMsgSizes;
- }
- } else {
- ch->entry_size = registration->entry_size;
-
- XPC_SET_REASON(ch, 0, 0);
- ch->flags &= ~XPC_C_DISCONNECTED;
-
- atomic_inc(&xpc_partitions[ch->partid].nchannels_active);
- }
-
- mutex_unlock(&registration->mutex);
-
- /* initiate the connection */
-
- ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING);
- xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags);
-
- xpc_process_connect(ch, &irq_flags);
-
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- return xpSuccess;
-}
-
-void
-xpc_process_sent_chctl_flags(struct xpc_partition *part)
-{
- unsigned long irq_flags;
- union xpc_channel_ctl_flags chctl;
- struct xpc_channel *ch;
- int ch_number;
- u32 ch_flags;
-
- chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part);
-
- /*
- * Initiate channel connections for registered channels.
- *
- * For each connected channel that has pending messages activate idle
- * kthreads and/or create new kthreads as needed.
- */
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch = &part->channels[ch_number];
-
- /*
- * Process any open or close related chctl flags, and then deal
- * with connecting or disconnecting the channel as required.
- */
-
- if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) {
- xpc_process_openclose_chctl_flags(part, ch_number,
- chctl.flags[ch_number]);
- }
-
- ch_flags = ch->flags; /* need an atomic snapshot of flags */
-
- if (ch_flags & XPC_C_DISCONNECTING) {
- spin_lock_irqsave(&ch->lock, irq_flags);
- xpc_process_disconnect(ch, &irq_flags);
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- continue;
- }
-
- if (part->act_state == XPC_P_AS_DEACTIVATING)
- continue;
-
- if (!(ch_flags & XPC_C_CONNECTED)) {
- if (!(ch_flags & XPC_C_OPENREQUEST)) {
- DBUG_ON(ch_flags & XPC_C_SETUP);
- (void)xpc_connect_channel(ch);
- }
- continue;
- }
-
- /*
- * Process any message related chctl flags, this may involve
- * the activation of kthreads to deliver any pending messages
- * sent from the other partition.
- */
-
- if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS)
- xpc_arch_ops.process_msg_chctl_flags(part, ch_number);
- }
-}
-
-/*
- * XPC's heartbeat code calls this function to inform XPC that a partition is
- * going down. XPC responds by tearing down the XPartition Communication
- * infrastructure used for the just downed partition.
- *
- * XPC's heartbeat code will never call this function and xpc_partition_up()
- * at the same time. Nor will it ever make multiple calls to either function
- * at the same time.
- */
-void
-xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason)
-{
- unsigned long irq_flags;
- int ch_number;
- struct xpc_channel *ch;
-
- dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n",
- XPC_PARTID(part), reason);
-
- if (!xpc_part_ref(part)) {
- /* infrastructure for this partition isn't currently set up */
- return;
- }
-
- /* disconnect channels associated with the partition going down */
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch = &part->channels[ch_number];
-
- xpc_msgqueue_ref(ch);
- spin_lock_irqsave(&ch->lock, irq_flags);
-
- XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
-
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- xpc_msgqueue_deref(ch);
- }
-
- xpc_wakeup_channel_mgr(part);
-
- xpc_part_deref(part);
-}
-
-/*
- * Called by XP at the time of channel connection registration to cause
- * XPC to establish connections to all currently active partitions.
- */
-void
-xpc_initiate_connect(int ch_number)
-{
- short partid;
- struct xpc_partition *part;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_part_ref(part)) {
- /*
- * Initiate the establishment of a connection on the
- * newly registered channel to the remote partition.
- */
- xpc_wakeup_channel_mgr(part);
- xpc_part_deref(part);
- }
- }
-}
-
-void
-xpc_connected_callout(struct xpc_channel *ch)
-{
- /* let the registerer know that a connection has been established */
-
- if (ch->func != NULL) {
- dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, "
- "partid=%d, channel=%d\n", ch->partid, ch->number);
-
- ch->func(xpConnected, ch->partid, ch->number,
- (void *)(u64)ch->local_nentries, ch->key);
-
- dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, "
- "partid=%d, channel=%d\n", ch->partid, ch->number);
- }
-}
-
-/*
- * Called by XP at the time of channel connection unregistration to cause
- * XPC to teardown all current connections for the specified channel.
- *
- * Before returning xpc_initiate_disconnect() will wait until all connections
- * on the specified channel have been closed/torndown. So the caller can be
- * assured that they will not be receiving any more callouts from XPC to the
- * function they registered via xpc_connect().
- *
- * Arguments:
- *
- * ch_number - channel # to unregister.
- */
-void
-xpc_initiate_disconnect(int ch_number)
-{
- unsigned long irq_flags;
- short partid;
- struct xpc_partition *part;
- struct xpc_channel *ch;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
-
- /* initiate the channel disconnect for every active partition */
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_part_ref(part)) {
- ch = &part->channels[ch_number];
- xpc_msgqueue_ref(ch);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
-
- if (!(ch->flags & XPC_C_DISCONNECTED)) {
- ch->flags |= XPC_C_WDISCONNECT;
-
- XPC_DISCONNECT_CHANNEL(ch, xpUnregistering,
- &irq_flags);
- }
-
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- xpc_msgqueue_deref(ch);
- xpc_part_deref(part);
- }
- }
-
- xpc_disconnect_wait(ch_number);
-}
-
-/*
- * To disconnect a channel, and reflect it back to all who may be waiting.
- *
- * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
- * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by
- * xpc_disconnect_wait().
- *
- * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
- */
-void
-xpc_disconnect_channel(const int line, struct xpc_channel *ch,
- enum xp_retval reason, unsigned long *irq_flags)
-{
- u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED);
-
- lockdep_assert_held(&ch->lock);
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
- return;
-
- DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)));
-
- dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n",
- reason, line, ch->partid, ch->number);
-
- XPC_SET_REASON(ch, reason, line);
-
- ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
- /* some of these may not have been set */
- ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |
- XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
- XPC_C_CONNECTING | XPC_C_CONNECTED);
-
- xpc_arch_ops.send_chctl_closerequest(ch, irq_flags);
-
- if (channel_was_connected)
- ch->flags |= XPC_C_WASCONNECTED;
-
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
-
- /* wake all idle kthreads so they can exit */
- if (atomic_read(&ch->kthreads_idle) > 0) {
- wake_up_all(&ch->idle_wq);
-
- } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
- !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
- /* start a kthread that will do the xpDisconnecting callout */
- xpc_create_kthreads(ch, 1, 1);
- }
-
- /* wake those waiting to allocate an entry from the local msg queue */
- if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)
- wake_up(&ch->msg_allocate_wq);
-
- spin_lock_irqsave(&ch->lock, *irq_flags);
-}
-
-void
-xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason)
-{
- /*
- * Let the channel's registerer know that the channel is being
- * disconnected. We don't want to do this if the registerer was never
- * informed of a connection being made.
- */
-
- if (ch->func != NULL) {
- dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, "
- "channel=%d\n", reason, ch->partid, ch->number);
-
- ch->func(reason, ch->partid, ch->number, NULL, ch->key);
-
- dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, "
- "channel=%d\n", reason, ch->partid, ch->number);
- }
-}
-
-/*
- * Wait for a message entry to become available for the specified channel,
- * but don't wait any longer than 1 jiffy.
- */
-enum xp_retval
-xpc_allocate_msg_wait(struct xpc_channel *ch)
-{
- enum xp_retval ret;
- DEFINE_WAIT(wait);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- DBUG_ON(ch->reason == xpInterrupted);
- return ch->reason;
- }
-
- atomic_inc(&ch->n_on_msg_allocate_wq);
- prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE);
- ret = schedule_timeout(1);
- finish_wait(&ch->msg_allocate_wq, &wait);
- atomic_dec(&ch->n_on_msg_allocate_wq);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- ret = ch->reason;
- DBUG_ON(ch->reason == xpInterrupted);
- } else if (ret == 0) {
- ret = xpTimeout;
- } else {
- ret = xpInterrupted;
- }
-
- return ret;
-}
-
-/*
- * Send a message that contains the user's payload on the specified channel
- * connected to the specified partition.
- *
- * NOTE that this routine can sleep waiting for a message entry to become
- * available. To not sleep, pass in the XPC_NOWAIT flag.
- *
- * Once sent, this routine will not wait for the message to be received, nor
- * will notification be given when it does happen.
- *
- * Arguments:
- *
- * partid - ID of partition to which the channel is connected.
- * ch_number - channel # to send message on.
- * flags - see xp.h for valid flags.
- * payload - pointer to the payload which is to be sent.
- * payload_size - size of the payload in bytes.
- */
-enum xp_retval
-xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size)
-{
- struct xpc_partition *part = &xpc_partitions[partid];
- enum xp_retval ret = xpUnknownReason;
-
- dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
- partid, ch_number);
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
- DBUG_ON(payload == NULL);
-
- if (xpc_part_ref(part)) {
- ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
- flags, payload, payload_size, 0, NULL, NULL);
- xpc_part_deref(part);
- }
-
- return ret;
-}
-
-/*
- * Send a message that contains the user's payload on the specified channel
- * connected to the specified partition.
- *
- * NOTE that this routine can sleep waiting for a message entry to become
- * available. To not sleep, pass in the XPC_NOWAIT flag.
- *
- * This routine will not wait for the message to be sent or received.
- *
- * Once the remote end of the channel has received the message, the function
- * passed as an argument to xpc_initiate_send_notify() will be called. This
- * allows the sender to free up or re-use any buffers referenced by the
- * message, but does NOT mean the message has been processed at the remote
- * end by a receiver.
- *
- * If this routine returns an error, the caller's function will NOT be called.
- *
- * Arguments:
- *
- * partid - ID of partition to which the channel is connected.
- * ch_number - channel # to send message on.
- * flags - see xp.h for valid flags.
- * payload - pointer to the payload which is to be sent.
- * payload_size - size of the payload in bytes.
- * func - function to call with asynchronous notification of message
- * receipt. THIS FUNCTION MUST BE NON-BLOCKING.
- * key - user-defined key to be passed to the function when it's called.
- */
-enum xp_retval
-xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size, xpc_notify_func func, void *key)
-{
- struct xpc_partition *part = &xpc_partitions[partid];
- enum xp_retval ret = xpUnknownReason;
-
- dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
- partid, ch_number);
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
- DBUG_ON(payload == NULL);
- DBUG_ON(func == NULL);
-
- if (xpc_part_ref(part)) {
- ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
- flags, payload, payload_size, XPC_N_CALL, func, key);
- xpc_part_deref(part);
- }
- return ret;
-}
-
-/*
- * Deliver a message's payload to its intended recipient.
- */
-void
-xpc_deliver_payload(struct xpc_channel *ch)
-{
- void *payload;
-
- payload = xpc_arch_ops.get_deliverable_payload(ch);
- if (payload != NULL) {
-
- /*
- * This ref is taken to protect the payload itself from being
- * freed before the user is finished with it, which the user
- * indicates by calling xpc_initiate_received().
- */
- xpc_msgqueue_ref(ch);
-
- atomic_inc(&ch->kthreads_active);
-
- if (ch->func != NULL) {
- dev_dbg(xpc_chan, "ch->func() called, payload=0x%p "
- "partid=%d channel=%d\n", payload, ch->partid,
- ch->number);
-
- /* deliver the message to its intended recipient */
- ch->func(xpMsgReceived, ch->partid, ch->number, payload,
- ch->key);
-
- dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p "
- "partid=%d channel=%d\n", payload, ch->partid,
- ch->number);
- }
-
- atomic_dec(&ch->kthreads_active);
- }
-}
-
-/*
- * Acknowledge receipt of a delivered message's payload.
- *
- * This function, although called by users, does not call xpc_part_ref() to
- * ensure that the partition infrastructure is in place. It relies on the
- * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload().
- *
- * Arguments:
- *
- * partid - ID of partition to which the channel is connected.
- * ch_number - channel # message received on.
- * payload - pointer to the payload area allocated via
- * xpc_initiate_send() or xpc_initiate_send_notify().
- */
-void
-xpc_initiate_received(short partid, int ch_number, void *payload)
-{
- struct xpc_partition *part = &xpc_partitions[partid];
- struct xpc_channel *ch;
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
-
- ch = &part->channels[ch_number];
- xpc_arch_ops.received_payload(ch, payload);
-
- /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */
- xpc_msgqueue_deref(ch);
-}
diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c
deleted file mode 100644
index 15b6a8e35681..000000000000
--- a/drivers/misc/sgi-xp/xpc_main.c
+++ /dev/null
@@ -1,1309 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) support - standard version.
- *
- * XPC provides a message passing capability that crosses partition
- * boundaries. This module is made up of two parts:
- *
- * partition This part detects the presence/absence of other
- * partitions. It provides a heartbeat and monitors
- * the heartbeats of other partitions.
- *
- * channel This part manages the channels and sends/receives
- * messages across them to/from other partitions.
- *
- * There are a couple of additional functions residing in XP, which
- * provide an interface to XPC for its users.
- *
- *
- * Caveats:
- *
- * . Currently on sn2, we have no way to determine which nasid an IRQ
- * came from. Thus, xpc_send_IRQ_sn2() does a remote amo write
- * followed by an IPI. The amo indicates where data is to be pulled
- * from, so after the IPI arrives, the remote partition checks the amo
- * word. The IPI can actually arrive before the amo however, so other
- * code must periodically check for this case. Also, remote amo
- * operations do not reliably time out. Thus we do a remote PIO read
- * solely to know whether the remote partition is down and whether we
- * should stop sending IPIs to it. This remote PIO read operation is
- * set up in a special nofault region so SAL knows to ignore (and
- * cleanup) any errors due to the remote amo write, PIO read, and/or
- * PIO write operations.
- *
- * If/when new hardware solves this IPI problem, we should abandon
- * the current approach.
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/sysctl.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/reboot.h>
-#include <linux/kdebug.h>
-#include <linux/kthread.h>
-#include "xpc.h"
-
-#ifdef CONFIG_X86_64
-#include <asm/traps.h>
-#endif
-
-/* define two XPC debug device structures to be used with dev_dbg() et al */
-
-static struct device_driver xpc_dbg_name = {
- .name = "xpc"
-};
-
-static struct device xpc_part_dbg_subname = {
- .init_name = "", /* set to "part" at xpc_init() time */
- .driver = &xpc_dbg_name
-};
-
-static struct device xpc_chan_dbg_subname = {
- .init_name = "", /* set to "chan" at xpc_init() time */
- .driver = &xpc_dbg_name
-};
-
-struct device *xpc_part = &xpc_part_dbg_subname;
-struct device *xpc_chan = &xpc_chan_dbg_subname;
-
-static int xpc_kdebug_ignore;
-
-/* systune related variables for /proc/sys directories */
-
-static int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL;
-static int xpc_hb_min_interval = 1;
-static int xpc_hb_max_interval = 10;
-
-static int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL;
-static int xpc_hb_check_min_interval = 10;
-static int xpc_hb_check_max_interval = 120;
-
-int xpc_disengage_timelimit = XPC_DISENGAGE_DEFAULT_TIMELIMIT;
-static int xpc_disengage_min_timelimit; /* = 0 */
-static int xpc_disengage_max_timelimit = 120;
-
-static const struct ctl_table xpc_sys_xpc_hb[] = {
- {
- .procname = "hb_interval",
- .data = &xpc_hb_interval,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &xpc_hb_min_interval,
- .extra2 = &xpc_hb_max_interval},
- {
- .procname = "hb_check_interval",
- .data = &xpc_hb_check_interval,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &xpc_hb_check_min_interval,
- .extra2 = &xpc_hb_check_max_interval},
-};
-static const struct ctl_table xpc_sys_xpc[] = {
- {
- .procname = "disengage_timelimit",
- .data = &xpc_disengage_timelimit,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &xpc_disengage_min_timelimit,
- .extra2 = &xpc_disengage_max_timelimit},
-};
-
-static struct ctl_table_header *xpc_sysctl;
-static struct ctl_table_header *xpc_sysctl_hb;
-
-/* non-zero if any remote partition disengage was timed out */
-int xpc_disengage_timedout;
-
-/* #of activate IRQs received and not yet processed */
-int xpc_activate_IRQ_rcvd;
-DEFINE_SPINLOCK(xpc_activate_IRQ_rcvd_lock);
-
-/* IRQ handler notifies this wait queue on receipt of an IRQ */
-DECLARE_WAIT_QUEUE_HEAD(xpc_activate_IRQ_wq);
-
-static unsigned long xpc_hb_check_timeout;
-static struct timer_list xpc_hb_timer;
-
-/* notification that the xpc_hb_checker thread has exited */
-static DECLARE_COMPLETION(xpc_hb_checker_exited);
-
-/* notification that the xpc_discovery thread has exited */
-static DECLARE_COMPLETION(xpc_discovery_exited);
-
-static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *);
-
-static int xpc_system_reboot(struct notifier_block *, unsigned long, void *);
-static struct notifier_block xpc_reboot_notifier = {
- .notifier_call = xpc_system_reboot,
-};
-
-static int xpc_system_die(struct notifier_block *, unsigned long, void *);
-static struct notifier_block xpc_die_notifier = {
- .notifier_call = xpc_system_die,
-};
-
-struct xpc_arch_operations xpc_arch_ops;
-
-/*
- * Timer function to enforce the timelimit on the partition disengage.
- */
-static void
-xpc_timeout_partition_disengage(struct timer_list *t)
-{
- struct xpc_partition *part = timer_container_of(part, t,
- disengage_timer);
-
- DBUG_ON(time_is_after_jiffies(part->disengage_timeout));
-
- xpc_partition_disengaged_from_timer(part);
-
- DBUG_ON(part->disengage_timeout != 0);
- DBUG_ON(xpc_arch_ops.partition_engaged(XPC_PARTID(part)));
-}
-
-/*
- * Timer to produce the heartbeat. The timer structures function is
- * already set when this is initially called. A tunable is used to
- * specify when the next timeout should occur.
- */
-static void
-xpc_hb_beater(struct timer_list *unused)
-{
- xpc_arch_ops.increment_heartbeat();
-
- if (time_is_before_eq_jiffies(xpc_hb_check_timeout))
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-
- xpc_hb_timer.expires = jiffies + (xpc_hb_interval * HZ);
- add_timer(&xpc_hb_timer);
-}
-
-static void
-xpc_start_hb_beater(void)
-{
- xpc_arch_ops.heartbeat_init();
- timer_setup(&xpc_hb_timer, xpc_hb_beater, 0);
- xpc_hb_beater(NULL);
-}
-
-static void
-xpc_stop_hb_beater(void)
-{
- timer_delete_sync(&xpc_hb_timer);
- xpc_arch_ops.heartbeat_exit();
-}
-
-/*
- * At periodic intervals, scan through all active partitions and ensure
- * their heartbeat is still active. If not, the partition is deactivated.
- */
-static void
-xpc_check_remote_hb(void)
-{
- struct xpc_partition *part;
- short partid;
- enum xp_retval ret;
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
-
- if (xpc_exiting)
- break;
-
- if (partid == xp_partition_id)
- continue;
-
- part = &xpc_partitions[partid];
-
- if (part->act_state == XPC_P_AS_INACTIVE ||
- part->act_state == XPC_P_AS_DEACTIVATING) {
- continue;
- }
-
- ret = xpc_arch_ops.get_remote_heartbeat(part);
- if (ret != xpSuccess)
- XPC_DEACTIVATE_PARTITION(part, ret);
- }
-}
-
-/*
- * This thread is responsible for nearly all of the partition
- * activation/deactivation.
- */
-static int
-xpc_hb_checker(void *ignore)
-{
- int force_IRQ = 0;
-
- /* this thread was marked active by xpc_hb_init() */
-
- set_cpus_allowed_ptr(current, cpumask_of(XPC_HB_CHECK_CPU));
-
- /* set our heartbeating to other partitions into motion */
- xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ);
- xpc_start_hb_beater();
-
- while (!xpc_exiting) {
-
- dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have "
- "been received\n",
- (int)(xpc_hb_check_timeout - jiffies),
- xpc_activate_IRQ_rcvd);
-
- /* checking of remote heartbeats is skewed by IRQ handling */
- if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) {
- xpc_hb_check_timeout = jiffies +
- (xpc_hb_check_interval * HZ);
-
- dev_dbg(xpc_part, "checking remote heartbeats\n");
- xpc_check_remote_hb();
- }
-
- /* check for outstanding IRQs */
- if (xpc_activate_IRQ_rcvd > 0 || force_IRQ != 0) {
- force_IRQ = 0;
- dev_dbg(xpc_part, "processing activate IRQs "
- "received\n");
- xpc_arch_ops.process_activate_IRQ_rcvd();
- }
-
- /* wait for IRQ or timeout */
- (void)wait_event_interruptible(xpc_activate_IRQ_wq,
- (time_is_before_eq_jiffies(
- xpc_hb_check_timeout) ||
- xpc_activate_IRQ_rcvd > 0 ||
- xpc_exiting));
- }
-
- xpc_stop_hb_beater();
-
- dev_dbg(xpc_part, "heartbeat checker is exiting\n");
-
- /* mark this thread as having exited */
- complete(&xpc_hb_checker_exited);
- return 0;
-}
-
-/*
- * This thread will attempt to discover other partitions to activate
- * based on info provided by SAL. This new thread is short lived and
- * will exit once discovery is complete.
- */
-static int
-xpc_initiate_discovery(void *ignore)
-{
- xpc_discovery();
-
- dev_dbg(xpc_part, "discovery thread is exiting\n");
-
- /* mark this thread as having exited */
- complete(&xpc_discovery_exited);
- return 0;
-}
-
-/*
- * The first kthread assigned to a newly activated partition is the one
- * created by XPC HB with which it calls xpc_activating(). XPC hangs on to
- * that kthread until the partition is brought down, at which time that kthread
- * returns back to XPC HB. (The return of that kthread will signify to XPC HB
- * that XPC has dismantled all communication infrastructure for the associated
- * partition.) This kthread becomes the channel manager for that partition.
- *
- * Each active partition has a channel manager, who, besides connecting and
- * disconnecting channels, will ensure that each of the partition's connected
- * channels has the required number of assigned kthreads to get the work done.
- */
-static void
-xpc_channel_mgr(struct xpc_partition *part)
-{
- while (part->act_state != XPC_P_AS_DEACTIVATING ||
- atomic_read(&part->nchannels_active) > 0 ||
- !xpc_partition_disengaged(part)) {
-
- xpc_process_sent_chctl_flags(part);
-
- /*
- * Wait until we've been requested to activate kthreads or
- * all of the channel's message queues have been torn down or
- * a signal is pending.
- *
- * The channel_mgr_requests is set to 1 after being awakened,
- * This is done to prevent the channel mgr from making one pass
- * through the loop for each request, since he will
- * be servicing all the requests in one pass. The reason it's
- * set to 1 instead of 0 is so that other kthreads will know
- * that the channel mgr is running and won't bother trying to
- * wake him up.
- */
- atomic_dec(&part->channel_mgr_requests);
- (void)wait_event_interruptible(part->channel_mgr_wq,
- (atomic_read(&part->channel_mgr_requests) > 0 ||
- part->chctl.all_flags != 0 ||
- (part->act_state == XPC_P_AS_DEACTIVATING &&
- atomic_read(&part->nchannels_active) == 0 &&
- xpc_partition_disengaged(part))));
- atomic_set(&part->channel_mgr_requests, 1);
- }
-}
-
-/*
- * Guarantee that the kzalloc'd memory is cacheline aligned.
- */
-void *
-xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
-{
- /* see if kzalloc will give us cachline aligned memory by default */
- *base = kzalloc(size, flags);
- if (*base == NULL)
- return NULL;
-
- if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
- return *base;
-
- kfree(*base);
-
- /* nope, we'll have to do it ourselves */
- *base = kzalloc(size + L1_CACHE_BYTES, flags);
- if (*base == NULL)
- return NULL;
-
- return (void *)L1_CACHE_ALIGN((u64)*base);
-}
-
-/*
- * Setup the channel structures necessary to support XPartition Communication
- * between the specified remote partition and the local one.
- */
-static enum xp_retval
-xpc_setup_ch_structures(struct xpc_partition *part)
-{
- enum xp_retval ret;
- int ch_number;
- struct xpc_channel *ch;
- short partid = XPC_PARTID(part);
-
- /*
- * Allocate all of the channel structures as a contiguous chunk of
- * memory.
- */
- DBUG_ON(part->channels != NULL);
- part->channels = kzalloc_objs(struct xpc_channel, XPC_MAX_NCHANNELS);
- if (part->channels == NULL) {
- dev_err(xpc_chan, "can't get memory for channels\n");
- return xpNoMemory;
- }
-
- /* allocate the remote open and close args */
-
- part->remote_openclose_args =
- xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE,
- GFP_KERNEL, &part->
- remote_openclose_args_base);
- if (part->remote_openclose_args == NULL) {
- dev_err(xpc_chan, "can't get memory for remote connect args\n");
- ret = xpNoMemory;
- goto out_1;
- }
-
- part->chctl.all_flags = 0;
- spin_lock_init(&part->chctl_lock);
-
- atomic_set(&part->channel_mgr_requests, 1);
- init_waitqueue_head(&part->channel_mgr_wq);
-
- part->nchannels = XPC_MAX_NCHANNELS;
-
- atomic_set(&part->nchannels_active, 0);
- atomic_set(&part->nchannels_engaged, 0);
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch = &part->channels[ch_number];
-
- ch->partid = partid;
- ch->number = ch_number;
- ch->flags = XPC_C_DISCONNECTED;
-
- atomic_set(&ch->kthreads_assigned, 0);
- atomic_set(&ch->kthreads_idle, 0);
- atomic_set(&ch->kthreads_active, 0);
-
- atomic_set(&ch->references, 0);
- atomic_set(&ch->n_to_notify, 0);
-
- spin_lock_init(&ch->lock);
- init_completion(&ch->wdisconnect_wait);
-
- atomic_set(&ch->n_on_msg_allocate_wq, 0);
- init_waitqueue_head(&ch->msg_allocate_wq);
- init_waitqueue_head(&ch->idle_wq);
- }
-
- ret = xpc_arch_ops.setup_ch_structures(part);
- if (ret != xpSuccess)
- goto out_2;
-
- /*
- * With the setting of the partition setup_state to XPC_P_SS_SETUP,
- * we're declaring that this partition is ready to go.
- */
- part->setup_state = XPC_P_SS_SETUP;
-
- return xpSuccess;
-
- /* setup of ch structures failed */
-out_2:
- kfree(part->remote_openclose_args_base);
- part->remote_openclose_args = NULL;
-out_1:
- kfree(part->channels);
- part->channels = NULL;
- return ret;
-}
-
-/*
- * Teardown the channel structures necessary to support XPartition Communication
- * between the specified remote partition and the local one.
- */
-static void
-xpc_teardown_ch_structures(struct xpc_partition *part)
-{
- DBUG_ON(atomic_read(&part->nchannels_engaged) != 0);
- DBUG_ON(atomic_read(&part->nchannels_active) != 0);
-
- /*
- * Make this partition inaccessible to local processes by marking it
- * as no longer setup. Then wait before proceeding with the teardown
- * until all existing references cease.
- */
- DBUG_ON(part->setup_state != XPC_P_SS_SETUP);
- part->setup_state = XPC_P_SS_WTEARDOWN;
-
- wait_event(part->teardown_wq, (atomic_read(&part->references) == 0));
-
- /* now we can begin tearing down the infrastructure */
-
- xpc_arch_ops.teardown_ch_structures(part);
-
- kfree(part->remote_openclose_args_base);
- part->remote_openclose_args = NULL;
- kfree(part->channels);
- part->channels = NULL;
-
- part->setup_state = XPC_P_SS_TORNDOWN;
-}
-
-/*
- * When XPC HB determines that a partition has come up, it will create a new
- * kthread and that kthread will call this function to attempt to set up the
- * basic infrastructure used for Cross Partition Communication with the newly
- * upped partition.
- *
- * The kthread that was created by XPC HB and which setup the XPC
- * infrastructure will remain assigned to the partition becoming the channel
- * manager for that partition until the partition is deactivating, at which
- * time the kthread will teardown the XPC infrastructure and then exit.
- */
-static int
-xpc_activating(void *__partid)
-{
- short partid = (u64)__partid;
- struct xpc_partition *part = &xpc_partitions[partid];
- unsigned long irq_flags;
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
-
- if (part->act_state == XPC_P_AS_DEACTIVATING) {
- part->act_state = XPC_P_AS_INACTIVE;
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- part->remote_rp_pa = 0;
- return 0;
- }
-
- /* indicate the thread is activating */
- DBUG_ON(part->act_state != XPC_P_AS_ACTIVATION_REQ);
- part->act_state = XPC_P_AS_ACTIVATING;
-
- XPC_SET_REASON(part, 0, 0);
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- dev_dbg(xpc_part, "activating partition %d\n", partid);
-
- xpc_arch_ops.allow_hb(partid);
-
- if (xpc_setup_ch_structures(part) == xpSuccess) {
- (void)xpc_part_ref(part); /* this will always succeed */
-
- if (xpc_arch_ops.make_first_contact(part) == xpSuccess) {
- xpc_mark_partition_active(part);
- xpc_channel_mgr(part);
- /* won't return until partition is deactivating */
- }
-
- xpc_part_deref(part);
- xpc_teardown_ch_structures(part);
- }
-
- xpc_arch_ops.disallow_hb(partid);
- xpc_mark_partition_inactive(part);
-
- if (part->reason == xpReactivating) {
- /* interrupting ourselves results in activating partition */
- xpc_arch_ops.request_partition_reactivation(part);
- }
-
- return 0;
-}
-
-void
-xpc_activate_partition(struct xpc_partition *part)
-{
- short partid = XPC_PARTID(part);
- unsigned long irq_flags;
- struct task_struct *kthread;
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
-
- DBUG_ON(part->act_state != XPC_P_AS_INACTIVE);
-
- part->act_state = XPC_P_AS_ACTIVATION_REQ;
- XPC_SET_REASON(part, xpCloneKThread, __LINE__);
-
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- kthread = kthread_run(xpc_activating, (void *)((u64)partid), "xpc%02d",
- partid);
- if (IS_ERR(kthread)) {
- spin_lock_irqsave(&part->act_lock, irq_flags);
- part->act_state = XPC_P_AS_INACTIVE;
- XPC_SET_REASON(part, xpCloneKThreadFailed, __LINE__);
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- }
-}
-
-void
-xpc_activate_kthreads(struct xpc_channel *ch, int needed)
-{
- int idle = atomic_read(&ch->kthreads_idle);
- int assigned = atomic_read(&ch->kthreads_assigned);
- int wakeup;
-
- DBUG_ON(needed <= 0);
-
- if (idle > 0) {
- wakeup = (needed > idle) ? idle : needed;
- needed -= wakeup;
-
- dev_dbg(xpc_chan, "wakeup %d idle kthreads, partid=%d, "
- "channel=%d\n", wakeup, ch->partid, ch->number);
-
- /* only wakeup the requested number of kthreads */
- wake_up_nr(&ch->idle_wq, wakeup);
- }
-
- if (needed <= 0)
- return;
-
- if (needed + assigned > ch->kthreads_assigned_limit) {
- needed = ch->kthreads_assigned_limit - assigned;
- if (needed <= 0)
- return;
- }
-
- dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n",
- needed, ch->partid, ch->number);
-
- xpc_create_kthreads(ch, needed, 0);
-}
-
-/*
- * This function is where XPC's kthreads wait for messages to deliver.
- */
-static void
-xpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch)
-{
- int (*n_of_deliverable_payloads) (struct xpc_channel *) =
- xpc_arch_ops.n_of_deliverable_payloads;
-
- do {
- /* deliver messages to their intended recipients */
-
- while (n_of_deliverable_payloads(ch) > 0 &&
- !(ch->flags & XPC_C_DISCONNECTING)) {
- xpc_deliver_payload(ch);
- }
-
- if (atomic_inc_return(&ch->kthreads_idle) >
- ch->kthreads_idle_limit) {
- /* too many idle kthreads on this channel */
- atomic_dec(&ch->kthreads_idle);
- break;
- }
-
- dev_dbg(xpc_chan, "idle kthread calling "
- "wait_event_interruptible_exclusive()\n");
-
- (void)wait_event_interruptible_exclusive(ch->idle_wq,
- (n_of_deliverable_payloads(ch) > 0 ||
- (ch->flags & XPC_C_DISCONNECTING)));
-
- atomic_dec(&ch->kthreads_idle);
-
- } while (!(ch->flags & XPC_C_DISCONNECTING));
-}
-
-static int
-xpc_kthread_start(void *args)
-{
- short partid = XPC_UNPACK_ARG1(args);
- u16 ch_number = XPC_UNPACK_ARG2(args);
- struct xpc_partition *part = &xpc_partitions[partid];
- struct xpc_channel *ch;
- int n_needed;
- unsigned long irq_flags;
- int (*n_of_deliverable_payloads) (struct xpc_channel *) =
- xpc_arch_ops.n_of_deliverable_payloads;
-
- dev_dbg(xpc_chan, "kthread starting, partid=%d, channel=%d\n",
- partid, ch_number);
-
- ch = &part->channels[ch_number];
-
- if (!(ch->flags & XPC_C_DISCONNECTING)) {
-
- /* let registerer know that connection has been established */
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if (!(ch->flags & XPC_C_CONNECTEDCALLOUT)) {
- ch->flags |= XPC_C_CONNECTEDCALLOUT;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- xpc_connected_callout(ch);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- ch->flags |= XPC_C_CONNECTEDCALLOUT_MADE;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- /*
- * It is possible that while the callout was being
- * made that the remote partition sent some messages.
- * If that is the case, we may need to activate
- * additional kthreads to help deliver them. We only
- * need one less than total #of messages to deliver.
- */
- n_needed = n_of_deliverable_payloads(ch) - 1;
- if (n_needed > 0 && !(ch->flags & XPC_C_DISCONNECTING))
- xpc_activate_kthreads(ch, n_needed);
-
- } else {
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- }
-
- xpc_kthread_waitmsgs(part, ch);
- }
-
- /* let registerer know that connection is disconnecting */
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
- !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
- ch->flags |= XPC_C_DISCONNECTINGCALLOUT;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- xpc_disconnect_callout(ch, xpDisconnecting);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE;
- }
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- if (atomic_dec_return(&ch->kthreads_assigned) == 0 &&
- atomic_dec_return(&part->nchannels_engaged) == 0) {
- xpc_arch_ops.indicate_partition_disengaged(part);
- }
-
- xpc_msgqueue_deref(ch);
-
- dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n",
- partid, ch_number);
-
- xpc_part_deref(part);
- return 0;
-}
-
-/*
- * For each partition that XPC has established communications with, there is
- * a minimum of one kernel thread assigned to perform any operation that
- * may potentially sleep or block (basically the callouts to the asynchronous
- * functions registered via xpc_connect()).
- *
- * Additional kthreads are created and destroyed by XPC as the workload
- * demands.
- *
- * A kthread is assigned to one of the active channels that exists for a given
- * partition.
- */
-void
-xpc_create_kthreads(struct xpc_channel *ch, int needed,
- int ignore_disconnecting)
-{
- unsigned long irq_flags;
- u64 args = XPC_PACK_ARGS(ch->partid, ch->number);
- struct xpc_partition *part = &xpc_partitions[ch->partid];
- struct task_struct *kthread;
- void (*indicate_partition_disengaged) (struct xpc_partition *) =
- xpc_arch_ops.indicate_partition_disengaged;
-
- while (needed-- > 0) {
-
- /*
- * The following is done on behalf of the newly created
- * kthread. That kthread is responsible for doing the
- * counterpart to the following before it exits.
- */
- if (ignore_disconnecting) {
- if (!atomic_inc_not_zero(&ch->kthreads_assigned)) {
- /* kthreads assigned had gone to zero */
- BUG_ON(!(ch->flags &
- XPC_C_DISCONNECTINGCALLOUT_MADE));
- break;
- }
-
- } else if (ch->flags & XPC_C_DISCONNECTING) {
- break;
-
- } else if (atomic_inc_return(&ch->kthreads_assigned) == 1 &&
- atomic_inc_return(&part->nchannels_engaged) == 1) {
- xpc_arch_ops.indicate_partition_engaged(part);
- }
- (void)xpc_part_ref(part);
- xpc_msgqueue_ref(ch);
-
- kthread = kthread_run(xpc_kthread_start, (void *)args,
- "xpc%02dc%d", ch->partid, ch->number);
- if (IS_ERR(kthread)) {
- /* the fork failed */
-
- /*
- * NOTE: if (ignore_disconnecting &&
- * !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) is true,
- * then we'll deadlock if all other kthreads assigned
- * to this channel are blocked in the channel's
- * registerer, because the only thing that will unblock
- * them is the xpDisconnecting callout that this
- * failed kthread_run() would have made.
- */
-
- if (atomic_dec_return(&ch->kthreads_assigned) == 0 &&
- atomic_dec_return(&part->nchannels_engaged) == 0) {
- indicate_partition_disengaged(part);
- }
- xpc_msgqueue_deref(ch);
- xpc_part_deref(part);
-
- if (atomic_read(&ch->kthreads_assigned) <
- ch->kthreads_idle_limit) {
- /*
- * Flag this as an error only if we have an
- * insufficient #of kthreads for the channel
- * to function.
- */
- spin_lock_irqsave(&ch->lock, irq_flags);
- XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources,
- &irq_flags);
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- }
- break;
- }
- }
-}
-
-void
-xpc_disconnect_wait(int ch_number)
-{
- unsigned long irq_flags;
- short partid;
- struct xpc_partition *part;
- struct xpc_channel *ch;
- int wakeup_channel_mgr;
-
- /* now wait for all callouts to the caller's function to cease */
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (!xpc_part_ref(part))
- continue;
-
- ch = &part->channels[ch_number];
-
- if (!(ch->flags & XPC_C_WDISCONNECT)) {
- xpc_part_deref(part);
- continue;
- }
-
- wait_for_completion(&ch->wdisconnect_wait);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));
- wakeup_channel_mgr = 0;
-
- if (ch->delayed_chctl_flags) {
- if (part->act_state != XPC_P_AS_DEACTIVATING) {
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch->number] |=
- ch->delayed_chctl_flags;
- spin_unlock(&part->chctl_lock);
- wakeup_channel_mgr = 1;
- }
- ch->delayed_chctl_flags = 0;
- }
-
- ch->flags &= ~XPC_C_WDISCONNECT;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- if (wakeup_channel_mgr)
- xpc_wakeup_channel_mgr(part);
-
- xpc_part_deref(part);
- }
-}
-
-static int
-xpc_setup_partitions(void)
-{
- short partid;
- struct xpc_partition *part;
-
- xpc_partitions = kzalloc_objs(struct xpc_partition, xp_max_npartitions);
- if (xpc_partitions == NULL) {
- dev_err(xpc_part, "can't get memory for partition structure\n");
- return -ENOMEM;
- }
-
- /*
- * The first few fields of each entry of xpc_partitions[] need to
- * be initialized now so that calls to xpc_connect() and
- * xpc_disconnect() can be made prior to the activation of any remote
- * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE
- * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING
- * PARTITION HAS BEEN ACTIVATED.
- */
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part));
-
- part->activate_IRQ_rcvd = 0;
- spin_lock_init(&part->act_lock);
- part->act_state = XPC_P_AS_INACTIVE;
- XPC_SET_REASON(part, 0, 0);
-
- timer_setup(&part->disengage_timer,
- xpc_timeout_partition_disengage, 0);
-
- part->setup_state = XPC_P_SS_UNSET;
- init_waitqueue_head(&part->teardown_wq);
- atomic_set(&part->references, 0);
- }
-
- return xpc_arch_ops.setup_partitions();
-}
-
-static void
-xpc_teardown_partitions(void)
-{
- xpc_arch_ops.teardown_partitions();
- kfree(xpc_partitions);
-}
-
-static void
-xpc_do_exit(enum xp_retval reason)
-{
- short partid;
- int active_part_count, printed_waiting_msg = 0;
- struct xpc_partition *part;
- unsigned long printmsg_time, disengage_timeout = 0;
-
- /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */
- DBUG_ON(xpc_exiting == 1);
-
- /*
- * Let the heartbeat checker thread and the discovery thread
- * (if one is running) know that they should exit. Also wake up
- * the heartbeat checker thread in case it's sleeping.
- */
- xpc_exiting = 1;
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-
- /* wait for the discovery thread to exit */
- wait_for_completion(&xpc_discovery_exited);
-
- /* wait for the heartbeat checker thread to exit */
- wait_for_completion(&xpc_hb_checker_exited);
-
- /* sleep for a 1/3 of a second or so */
- (void)msleep_interruptible(300);
-
- /* wait for all partitions to become inactive */
-
- printmsg_time = jiffies + (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ);
- xpc_disengage_timedout = 0;
-
- do {
- active_part_count = 0;
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_partition_disengaged(part) &&
- part->act_state == XPC_P_AS_INACTIVE) {
- continue;
- }
-
- active_part_count++;
-
- XPC_DEACTIVATE_PARTITION(part, reason);
-
- if (part->disengage_timeout > disengage_timeout)
- disengage_timeout = part->disengage_timeout;
- }
-
- if (xpc_arch_ops.any_partition_engaged()) {
- if (time_is_before_jiffies(printmsg_time)) {
- dev_info(xpc_part, "waiting for remote "
- "partitions to deactivate, timeout in "
- "%ld seconds\n", (disengage_timeout -
- jiffies) / HZ);
- printmsg_time = jiffies +
- (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ);
- printed_waiting_msg = 1;
- }
-
- } else if (active_part_count > 0) {
- if (printed_waiting_msg) {
- dev_info(xpc_part, "waiting for local partition"
- " to deactivate\n");
- printed_waiting_msg = 0;
- }
-
- } else {
- if (!xpc_disengage_timedout) {
- dev_info(xpc_part, "all partitions have "
- "deactivated\n");
- }
- break;
- }
-
- /* sleep for a 1/3 of a second or so */
- (void)msleep_interruptible(300);
-
- } while (1);
-
- DBUG_ON(xpc_arch_ops.any_partition_engaged());
-
- xpc_teardown_rsvd_page();
-
- if (reason == xpUnloading) {
- (void)unregister_die_notifier(&xpc_die_notifier);
- (void)unregister_reboot_notifier(&xpc_reboot_notifier);
- }
-
- /* clear the interface to XPC's functions */
- xpc_clear_interface();
-
- if (xpc_sysctl)
- unregister_sysctl_table(xpc_sysctl);
- if (xpc_sysctl_hb)
- unregister_sysctl_table(xpc_sysctl_hb);
-
- xpc_teardown_partitions();
-
- if (is_uv_system())
- xpc_exit_uv();
-}
-
-/*
- * This function is called when the system is being rebooted.
- */
-static int
-xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused)
-{
- enum xp_retval reason;
-
- switch (event) {
- case SYS_RESTART:
- reason = xpSystemReboot;
- break;
- case SYS_HALT:
- reason = xpSystemHalt;
- break;
- case SYS_POWER_OFF:
- reason = xpSystemPoweroff;
- break;
- default:
- reason = xpSystemGoingDown;
- }
-
- xpc_do_exit(reason);
- return NOTIFY_DONE;
-}
-
-/* Used to only allow one cpu to complete disconnect */
-static unsigned int xpc_die_disconnecting;
-
-/*
- * Notify other partitions to deactivate from us by first disengaging from all
- * references to our memory.
- */
-static void
-xpc_die_deactivate(void)
-{
- struct xpc_partition *part;
- short partid;
- int any_engaged;
- long keep_waiting;
- long wait_to_print;
-
- if (cmpxchg(&xpc_die_disconnecting, 0, 1))
- return;
-
- /* keep xpc_hb_checker thread from doing anything (just in case) */
- xpc_exiting = 1;
-
- xpc_arch_ops.disallow_all_hbs(); /*indicate we're deactivated */
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_arch_ops.partition_engaged(partid) ||
- part->act_state != XPC_P_AS_INACTIVE) {
- xpc_arch_ops.request_partition_deactivation(part);
- xpc_arch_ops.indicate_partition_disengaged(part);
- }
- }
-
- /*
- * Though we requested that all other partitions deactivate from us,
- * we only wait until they've all disengaged or we've reached the
- * defined timelimit.
- *
- * Given that one iteration through the following while-loop takes
- * approximately 200 microseconds, calculate the #of loops to take
- * before bailing and the #of loops before printing a waiting message.
- */
- keep_waiting = xpc_disengage_timelimit * 1000 * 5;
- wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5;
-
- while (1) {
- any_engaged = xpc_arch_ops.any_partition_engaged();
- if (!any_engaged) {
- dev_info(xpc_part, "all partitions have deactivated\n");
- break;
- }
-
- if (!keep_waiting--) {
- for (partid = 0; partid < xp_max_npartitions;
- partid++) {
- if (xpc_arch_ops.partition_engaged(partid)) {
- dev_info(xpc_part, "deactivate from "
- "remote partition %d timed "
- "out\n", partid);
- }
- }
- break;
- }
-
- if (!wait_to_print--) {
- dev_info(xpc_part, "waiting for remote partitions to "
- "deactivate, timeout in %ld seconds\n",
- keep_waiting / (1000 * 5));
- wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL *
- 1000 * 5;
- }
-
- udelay(200);
- }
-}
-
-/*
- * This function is called when the system is being restarted or halted due
- * to some sort of system failure. If this is the case we need to notify the
- * other partitions to disengage from all references to our memory.
- * This function can also be called when our heartbeater could be offlined
- * for a time. In this case we need to notify other partitions to not worry
- * about the lack of a heartbeat.
- */
-static int
-xpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args)
-{
- struct die_args *die_args = _die_args;
-
- switch (event) {
- case DIE_TRAP:
- if (die_args->trapnr == X86_TRAP_DF)
- xpc_die_deactivate();
-
- if (((die_args->trapnr == X86_TRAP_MF) ||
- (die_args->trapnr == X86_TRAP_XF)) &&
- !user_mode(die_args->regs))
- xpc_die_deactivate();
-
- break;
- case DIE_INT3:
- case DIE_DEBUG:
- break;
- case DIE_OOPS:
- case DIE_GPF:
- default:
- xpc_die_deactivate();
- }
-
- return NOTIFY_DONE;
-}
-
-static int __init
-xpc_init(void)
-{
- int ret;
- struct task_struct *kthread;
-
- dev_set_name(xpc_part, "part");
- dev_set_name(xpc_chan, "chan");
-
- if (is_uv_system()) {
- ret = xpc_init_uv();
-
- } else {
- ret = -ENODEV;
- }
-
- if (ret != 0)
- return ret;
-
- ret = xpc_setup_partitions();
- if (ret != 0) {
- dev_err(xpc_part, "can't get memory for partition structure\n");
- goto out_1;
- }
-
- xpc_sysctl = register_sysctl("xpc", xpc_sys_xpc);
- xpc_sysctl_hb = register_sysctl("xpc/hb", xpc_sys_xpc_hb);
-
- /*
- * Fill the partition reserved page with the information needed by
- * other partitions to discover we are alive and establish initial
- * communications.
- */
- ret = xpc_setup_rsvd_page();
- if (ret != 0) {
- dev_err(xpc_part, "can't setup our reserved page\n");
- goto out_2;
- }
-
- /* add ourselves to the reboot_notifier_list */
- ret = register_reboot_notifier(&xpc_reboot_notifier);
- if (ret != 0)
- dev_warn(xpc_part, "can't register reboot notifier\n");
-
- /* add ourselves to the die_notifier list */
- ret = register_die_notifier(&xpc_die_notifier);
- if (ret != 0)
- dev_warn(xpc_part, "can't register die notifier\n");
-
- /*
- * The real work-horse behind xpc. This processes incoming
- * interrupts and monitors remote heartbeats.
- */
- kthread = kthread_run(xpc_hb_checker, NULL, XPC_HB_CHECK_THREAD_NAME);
- if (IS_ERR(kthread)) {
- dev_err(xpc_part, "failed while forking hb check thread\n");
- ret = -EBUSY;
- goto out_3;
- }
-
- /*
- * Startup a thread that will attempt to discover other partitions to
- * activate based on info provided by SAL. This new thread is short
- * lived and will exit once discovery is complete.
- */
- kthread = kthread_run(xpc_initiate_discovery, NULL,
- XPC_DISCOVERY_THREAD_NAME);
- if (IS_ERR(kthread)) {
- dev_err(xpc_part, "failed while forking discovery thread\n");
-
- /* mark this new thread as a non-starter */
- complete(&xpc_discovery_exited);
-
- xpc_do_exit(xpUnloading);
- return -EBUSY;
- }
-
- /* set the interface to point at XPC's functions */
- xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect,
- xpc_initiate_send, xpc_initiate_send_notify,
- xpc_initiate_received, xpc_initiate_partid_to_nasids);
-
- return 0;
-
- /* initialization was not successful */
-out_3:
- xpc_teardown_rsvd_page();
-
- (void)unregister_die_notifier(&xpc_die_notifier);
- (void)unregister_reboot_notifier(&xpc_reboot_notifier);
-out_2:
- if (xpc_sysctl_hb)
- unregister_sysctl_table(xpc_sysctl_hb);
- if (xpc_sysctl)
- unregister_sysctl_table(xpc_sysctl);
-
- xpc_teardown_partitions();
-out_1:
- if (is_uv_system())
- xpc_exit_uv();
- return ret;
-}
-
-module_init(xpc_init);
-
-static void __exit
-xpc_exit(void)
-{
- xpc_do_exit(xpUnloading);
-}
-
-module_exit(xpc_exit);
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_DESCRIPTION("Cross Partition Communication (XPC) support");
-MODULE_LICENSE("GPL");
-
-module_param(xpc_hb_interval, int, 0);
-MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between "
- "heartbeat increments.");
-
-module_param(xpc_hb_check_interval, int, 0);
-MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between "
- "heartbeat checks.");
-
-module_param(xpc_disengage_timelimit, int, 0);
-MODULE_PARM_DESC(xpc_disengage_timelimit, "Number of seconds to wait "
- "for disengage to complete.");
-
-module_param(xpc_kdebug_ignore, int, 0);
-MODULE_PARM_DESC(xpc_kdebug_ignore, "Should lack of heartbeat be ignored by "
- "other partitions when dropping into kdebug.");
diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c
deleted file mode 100644
index d0467010558c..000000000000
--- a/drivers/misc/sgi-xp/xpc_partition.c
+++ /dev/null
@@ -1,545 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) partition support.
- *
- * This is the part of XPC that detects the presence/absence of
- * other partitions. It provides a heartbeat and monitors the
- * heartbeats of other partitions.
- *
- */
-
-#include <linux/device.h>
-#include <linux/hardirq.h>
-#include <linux/slab.h>
-#include "xpc.h"
-#include <asm/uv/uv_hub.h>
-
-/* XPC is exiting flag */
-int xpc_exiting;
-
-/* this partition's reserved page pointers */
-struct xpc_rsvd_page *xpc_rsvd_page;
-static unsigned long *xpc_part_nasids;
-unsigned long *xpc_mach_nasids;
-
-static int xpc_nasid_mask_nbytes; /* #of bytes in nasid mask */
-int xpc_nasid_mask_nlongs; /* #of longs in nasid mask */
-
-struct xpc_partition *xpc_partitions;
-
-/*
- * Guarantee that the kmalloc'd memory is cacheline aligned.
- */
-void *
-xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
-{
- /* see if kmalloc will give us cachline aligned memory by default */
- *base = kmalloc(size, flags);
- if (*base == NULL)
- return NULL;
-
- if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
- return *base;
-
- kfree(*base);
-
- /* nope, we'll have to do it ourselves */
- *base = kmalloc(size + L1_CACHE_BYTES, flags);
- if (*base == NULL)
- return NULL;
-
- return (void *)L1_CACHE_ALIGN((u64)*base);
-}
-
-/*
- * Given a nasid, get the physical address of the partition's reserved page
- * for that nasid. This function returns 0 on any error.
- */
-static unsigned long
-xpc_get_rsvd_page_pa(int nasid)
-{
- enum xp_retval ret;
- u64 cookie = 0;
- unsigned long rp_pa = nasid; /* seed with nasid */
- size_t len = 0;
- size_t buf_len = 0;
- void *buf = NULL;
- void *buf_base = NULL;
- enum xp_retval (*get_partition_rsvd_page_pa)
- (void *, u64 *, unsigned long *, size_t *) =
- xpc_arch_ops.get_partition_rsvd_page_pa;
-
- while (1) {
-
- /* !!! rp_pa will need to be _gpa on UV.
- * ??? So do we save it into the architecture specific parts
- * ??? of the xpc_partition structure? Do we rename this
- * ??? function or have two versions? Rename rp_pa for UV to
- * ??? rp_gpa?
- */
- ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len);
-
- dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, "
- "address=0x%016lx, len=0x%016lx\n", ret,
- (unsigned long)cookie, rp_pa, len);
-
- if (ret != xpNeedMoreInfo)
- break;
-
- if (len > buf_len) {
- kfree(buf_base);
- buf_len = L1_CACHE_ALIGN(len);
- buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
- &buf_base);
- if (buf_base == NULL) {
- dev_err(xpc_part, "unable to kmalloc "
- "len=0x%016lx\n", buf_len);
- ret = xpNoMemory;
- break;
- }
- }
-
- ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
- if (ret != xpSuccess) {
- dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
- break;
- }
- }
-
- kfree(buf_base);
-
- if (ret != xpSuccess)
- rp_pa = 0;
-
- dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
- return rp_pa;
-}
-
-/*
- * Fill the partition reserved page with the information needed by
- * other partitions to discover we are alive and establish initial
- * communications.
- */
-int
-xpc_setup_rsvd_page(void)
-{
- int ret;
- struct xpc_rsvd_page *rp;
- unsigned long rp_pa;
- unsigned long new_ts_jiffies;
-
- /* get the local reserved page's address */
-
- preempt_disable();
- rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id()));
- preempt_enable();
- if (rp_pa == 0) {
- dev_err(xpc_part, "SAL failed to locate the reserved page\n");
- return -ESRCH;
- }
- rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
-
- if (rp->SAL_version < 3) {
- /* SAL_versions < 3 had a SAL_partid defined as a u8 */
- rp->SAL_partid &= 0xff;
- }
- BUG_ON(rp->SAL_partid != xp_partition_id);
-
- if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
- dev_err(xpc_part, "the reserved page's partid of %d is outside "
- "supported range (< 0 || >= %d)\n", rp->SAL_partid,
- xp_max_npartitions);
- return -EINVAL;
- }
-
- rp->version = XPC_RP_VERSION;
- rp->max_npartitions = xp_max_npartitions;
-
- /* establish the actual sizes of the nasid masks */
- if (rp->SAL_version == 1) {
- /* SAL_version 1 didn't set the nasids_size field */
- rp->SAL_nasids_size = 128;
- }
- xpc_nasid_mask_nbytes = rp->SAL_nasids_size;
- xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size *
- BITS_PER_BYTE);
-
- /* setup the pointers to the various items in the reserved page */
- xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
- xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
-
- ret = xpc_arch_ops.setup_rsvd_page(rp);
- if (ret != 0)
- return ret;
-
- /*
- * Set timestamp of when reserved page was setup by XPC.
- * This signifies to the remote partition that our reserved
- * page is initialized.
- */
- new_ts_jiffies = jiffies;
- if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies)
- new_ts_jiffies++;
- rp->ts_jiffies = new_ts_jiffies;
-
- xpc_rsvd_page = rp;
- return 0;
-}
-
-void
-xpc_teardown_rsvd_page(void)
-{
- /* a zero timestamp indicates our rsvd page is not initialized */
- xpc_rsvd_page->ts_jiffies = 0;
-}
-
-/*
- * Get a copy of a portion of the remote partition's rsvd page.
- *
- * remote_rp points to a buffer that is cacheline aligned for BTE copies and
- * is large enough to contain a copy of their reserved page header and
- * part_nasids mask.
- */
-enum xp_retval
-xpc_get_remote_rp(int nasid, unsigned long *discovered_nasids,
- struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa)
-{
- int l;
- enum xp_retval ret;
-
- /* get the reserved page's physical address */
-
- *remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
- if (*remote_rp_pa == 0)
- return xpNoRsvdPageAddr;
-
- /* pull over the reserved page header and part_nasids mask */
- ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa,
- XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes);
- if (ret != xpSuccess)
- return ret;
-
- if (discovered_nasids != NULL) {
- unsigned long *remote_part_nasids =
- XPC_RP_PART_NASIDS(remote_rp);
-
- for (l = 0; l < xpc_nasid_mask_nlongs; l++)
- discovered_nasids[l] |= remote_part_nasids[l];
- }
-
- /* zero timestamp indicates the reserved page has not been setup */
- if (remote_rp->ts_jiffies == 0)
- return xpRsvdPageNotSet;
-
- if (XPC_VERSION_MAJOR(remote_rp->version) !=
- XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
- return xpBadVersion;
- }
-
- /* check that both remote and local partids are valid for each side */
- if (remote_rp->SAL_partid < 0 ||
- remote_rp->SAL_partid >= xp_max_npartitions ||
- remote_rp->max_npartitions <= xp_partition_id) {
- return xpInvalidPartid;
- }
-
- if (remote_rp->SAL_partid == xp_partition_id)
- return xpLocalPartid;
-
- return xpSuccess;
-}
-
-/*
- * See if the other side has responded to a partition deactivate request
- * from us. Though we requested the remote partition to deactivate with regard
- * to us, we really only need to wait for the other side to disengage from us.
- */
-static int __xpc_partition_disengaged(struct xpc_partition *part,
- bool from_timer)
-{
- short partid = XPC_PARTID(part);
- int disengaged;
-
- disengaged = !xpc_arch_ops.partition_engaged(partid);
- if (part->disengage_timeout) {
- if (!disengaged) {
- if (time_is_after_jiffies(part->disengage_timeout)) {
- /* timelimit hasn't been reached yet */
- return 0;
- }
-
- /*
- * Other side hasn't responded to our deactivate
- * request in a timely fashion, so assume it's dead.
- */
-
- dev_info(xpc_part, "deactivate request to remote "
- "partition %d timed out\n", partid);
- xpc_disengage_timedout = 1;
- xpc_arch_ops.assume_partition_disengaged(partid);
- disengaged = 1;
- }
- part->disengage_timeout = 0;
-
- /* Cancel the timer function if not called from it */
- if (!from_timer)
- timer_delete_sync(&part->disengage_timer);
-
- DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING &&
- part->act_state != XPC_P_AS_INACTIVE);
- if (part->act_state != XPC_P_AS_INACTIVE)
- xpc_wakeup_channel_mgr(part);
-
- xpc_arch_ops.cancel_partition_deactivation_request(part);
- }
- return disengaged;
-}
-
-int xpc_partition_disengaged(struct xpc_partition *part)
-{
- return __xpc_partition_disengaged(part, false);
-}
-
-int xpc_partition_disengaged_from_timer(struct xpc_partition *part)
-{
- return __xpc_partition_disengaged(part, true);
-}
-
-/*
- * Mark specified partition as active.
- */
-enum xp_retval
-xpc_mark_partition_active(struct xpc_partition *part)
-{
- unsigned long irq_flags;
- enum xp_retval ret;
-
- dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
- if (part->act_state == XPC_P_AS_ACTIVATING) {
- part->act_state = XPC_P_AS_ACTIVE;
- ret = xpSuccess;
- } else {
- DBUG_ON(part->reason == xpSuccess);
- ret = part->reason;
- }
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- return ret;
-}
-
-/*
- * Start the process of deactivating the specified partition.
- */
-void
-xpc_deactivate_partition(const int line, struct xpc_partition *part,
- enum xp_retval reason)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
-
- if (part->act_state == XPC_P_AS_INACTIVE) {
- XPC_SET_REASON(part, reason, line);
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- if (reason == xpReactivating) {
- /* we interrupt ourselves to reactivate partition */
- xpc_arch_ops.request_partition_reactivation(part);
- }
- return;
- }
- if (part->act_state == XPC_P_AS_DEACTIVATING) {
- if ((part->reason == xpUnloading && reason != xpUnloading) ||
- reason == xpReactivating) {
- XPC_SET_REASON(part, reason, line);
- }
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- return;
- }
-
- part->act_state = XPC_P_AS_DEACTIVATING;
- XPC_SET_REASON(part, reason, line);
-
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- /* ask remote partition to deactivate with regard to us */
- xpc_arch_ops.request_partition_deactivation(part);
-
- /* set a timelimit on the disengage phase of the deactivation request */
- part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ);
- part->disengage_timer.expires = part->disengage_timeout;
- add_timer(&part->disengage_timer);
-
- dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
- XPC_PARTID(part), reason);
-
- xpc_partition_going_down(part, reason);
-}
-
-/*
- * Mark specified partition as inactive.
- */
-void
-xpc_mark_partition_inactive(struct xpc_partition *part)
-{
- unsigned long irq_flags;
-
- dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
- XPC_PARTID(part));
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
- part->act_state = XPC_P_AS_INACTIVE;
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- part->remote_rp_pa = 0;
-}
-
-/*
- * SAL has provided a partition and machine mask. The partition mask
- * contains a bit for each even nasid in our partition. The machine
- * mask contains a bit for each even nasid in the entire machine.
- *
- * Using those two bit arrays, we can determine which nasids are
- * known in the machine. Each should also have a reserved page
- * initialized if they are available for partitioning.
- */
-void
-xpc_discovery(void)
-{
- void *remote_rp_base;
- struct xpc_rsvd_page *remote_rp;
- unsigned long remote_rp_pa;
- int region;
- int region_size;
- int max_regions;
- int nasid;
- unsigned long *discovered_nasids;
- enum xp_retval ret;
-
- remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
- xpc_nasid_mask_nbytes,
- GFP_KERNEL, &remote_rp_base);
- if (remote_rp == NULL)
- return;
-
- discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long),
- GFP_KERNEL);
- if (discovered_nasids == NULL) {
- kfree(remote_rp_base);
- return;
- }
-
- /*
- * The term 'region' in this context refers to the minimum number of
- * nodes that can comprise an access protection grouping. The access
- * protection is in regards to memory, IOI and IPI.
- */
- region_size = xp_region_size;
-
- if (is_uv_system())
- max_regions = 256;
- else {
- max_regions = 64;
-
- switch (region_size) {
- case 128:
- max_regions *= 2;
- fallthrough;
- case 64:
- max_regions *= 2;
- fallthrough;
- case 32:
- max_regions *= 2;
- region_size = 16;
- }
- }
-
- for (region = 0; region < max_regions; region++) {
-
- if (xpc_exiting)
- break;
-
- dev_dbg(xpc_part, "searching region %d\n", region);
-
- for (nasid = (region * region_size * 2);
- nasid < ((region + 1) * region_size * 2); nasid += 2) {
-
- if (xpc_exiting)
- break;
-
- dev_dbg(xpc_part, "checking nasid %d\n", nasid);
-
- if (test_bit(nasid / 2, xpc_part_nasids)) {
- dev_dbg(xpc_part, "PROM indicates Nasid %d is "
- "part of the local partition; skipping "
- "region\n", nasid);
- break;
- }
-
- if (!(test_bit(nasid / 2, xpc_mach_nasids))) {
- dev_dbg(xpc_part, "PROM indicates Nasid %d was "
- "not on Numa-Link network at reset\n",
- nasid);
- continue;
- }
-
- if (test_bit(nasid / 2, discovered_nasids)) {
- dev_dbg(xpc_part, "Nasid %d is part of a "
- "partition which was previously "
- "discovered\n", nasid);
- continue;
- }
-
- /* pull over the rsvd page header & part_nasids mask */
-
- ret = xpc_get_remote_rp(nasid, discovered_nasids,
- remote_rp, &remote_rp_pa);
- if (ret != xpSuccess) {
- dev_dbg(xpc_part, "unable to get reserved page "
- "from nasid %d, reason=%d\n", nasid,
- ret);
-
- if (ret == xpLocalPartid)
- break;
-
- continue;
- }
-
- xpc_arch_ops.request_partition_activation(remote_rp,
- remote_rp_pa, nasid);
- }
- }
-
- kfree(discovered_nasids);
- kfree(remote_rp_base);
-}
-
-/*
- * Given a partid, get the nasids owned by that partition from the
- * remote partition's reserved page.
- */
-enum xp_retval
-xpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
-{
- struct xpc_partition *part;
- unsigned long part_nasid_pa;
-
- part = &xpc_partitions[partid];
- if (part->remote_rp_pa == 0)
- return xpPartitionDown;
-
- memset(nasid_mask, 0, xpc_nasid_mask_nbytes);
-
- part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa);
-
- return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa,
- xpc_nasid_mask_nbytes);
-}
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
deleted file mode 100644
index 79c2f00ed4d7..000000000000
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ /dev/null
@@ -1,1727 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) uv-based functions.
- *
- * Architecture specific implementation of common functions.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/cpu.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/numa.h>
-#include <asm/uv/uv_hub.h>
-#include <asm/uv/bios.h>
-#include <asm/uv/uv_irq.h>
-#include "../sgi-gru/gru.h"
-#include "../sgi-gru/grukservices.h"
-#include "xpc.h"
-
-static struct xpc_heartbeat_uv *xpc_heartbeat_uv;
-
-#define XPC_ACTIVATE_MSG_SIZE_UV (1 * GRU_CACHE_LINE_BYTES)
-#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
- XPC_ACTIVATE_MSG_SIZE_UV)
-#define XPC_ACTIVATE_IRQ_NAME "xpc_activate"
-
-#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES)
-#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
- XPC_NOTIFY_MSG_SIZE_UV)
-#define XPC_NOTIFY_IRQ_NAME "xpc_notify"
-
-static int xpc_mq_node = NUMA_NO_NODE;
-
-static struct xpc_gru_mq_uv *xpc_activate_mq_uv;
-static struct xpc_gru_mq_uv *xpc_notify_mq_uv;
-
-static int
-xpc_setup_partitions_uv(void)
-{
- short partid;
- struct xpc_partition_uv *part_uv;
-
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part_uv = &xpc_partitions[partid].sn.uv;
-
- mutex_init(&part_uv->cached_activate_gru_mq_desc_mutex);
- spin_lock_init(&part_uv->flags_lock);
- part_uv->remote_act_state = XPC_P_AS_INACTIVE;
- }
- return 0;
-}
-
-static void
-xpc_teardown_partitions_uv(void)
-{
- short partid;
- struct xpc_partition_uv *part_uv;
- unsigned long irq_flags;
-
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part_uv = &xpc_partitions[partid].sn.uv;
-
- if (part_uv->cached_activate_gru_mq_desc != NULL) {
- mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex);
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- kfree(part_uv->cached_activate_gru_mq_desc);
- part_uv->cached_activate_gru_mq_desc = NULL;
- mutex_unlock(&part_uv->
- cached_activate_gru_mq_desc_mutex);
- }
- }
-}
-
-static int
-xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name)
-{
- int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
-
- mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset,
- UV_AFFINITY_CPU);
- if (mq->irq < 0)
- return mq->irq;
-
- mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset);
-
- return 0;
-}
-
-static void
-xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq)
-{
- uv_teardown_irq(mq->irq);
-}
-
-static int
-xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq)
-{
- int ret;
-
- ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address),
- mq->order, &mq->mmr_offset);
- if (ret < 0) {
- dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
- "ret=%d\n", ret);
- return ret;
- }
-
- mq->watchlist_num = ret;
- return 0;
-}
-
-static void
-xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq)
-{
- int ret;
- int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
-
- ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num);
- BUG_ON(ret != BIOS_STATUS_SUCCESS);
-}
-
-static struct xpc_gru_mq_uv *
-xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
- irq_handler_t irq_handler)
-{
- enum xp_retval xp_ret;
- int ret;
- int nid;
- int nasid;
- int pg_order;
- struct page *page;
- struct xpc_gru_mq_uv *mq;
- struct uv_IO_APIC_route_entry *mmr_value;
-
- mq = kmalloc_obj(struct xpc_gru_mq_uv);
- if (mq == NULL) {
- dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() "
- "a xpc_gru_mq_uv structure\n");
- ret = -ENOMEM;
- goto out_0;
- }
-
- mq->gru_mq_desc = kzalloc_obj(struct gru_message_queue_desc);
- if (mq->gru_mq_desc == NULL) {
- dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() "
- "a gru_message_queue_desc structure\n");
- ret = -ENOMEM;
- goto out_1;
- }
-
- pg_order = get_order(mq_size);
- mq->order = pg_order + PAGE_SHIFT;
- mq_size = 1UL << mq->order;
-
- mq->mmr_blade = uv_cpu_to_blade_id(cpu);
-
- nid = cpu_to_node(cpu);
- page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE,
- pg_order);
- if (page == NULL) {
- dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
- "bytes of memory on nid=%d for GRU mq\n", mq_size, nid);
- ret = -ENOMEM;
- goto out_2;
- }
- mq->address = page_address(page);
-
- /* enable generation of irq when GRU mq operation occurs to this mq */
- ret = xpc_gru_mq_watchlist_alloc_uv(mq);
- if (ret != 0)
- goto out_3;
-
- ret = xpc_get_gru_mq_irq_uv(mq, cpu, irq_name);
- if (ret != 0)
- goto out_4;
-
- ret = request_irq(mq->irq, irq_handler, 0, irq_name, NULL);
- if (ret != 0) {
- dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n",
- mq->irq, -ret);
- goto out_5;
- }
-
- nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu));
-
- mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value;
- ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size,
- nasid, mmr_value->vector, mmr_value->dest);
- if (ret != 0) {
- dev_err(xpc_part, "gru_create_message_queue() returned "
- "error=%d\n", ret);
- ret = -EINVAL;
- goto out_6;
- }
-
- /* allow other partitions to access this GRU mq */
- xp_ret = xp_expand_memprotect(xp_pa(mq->address), mq_size);
- if (xp_ret != xpSuccess) {
- ret = -EACCES;
- goto out_6;
- }
-
- return mq;
-
- /* something went wrong */
-out_6:
- free_irq(mq->irq, NULL);
-out_5:
- xpc_release_gru_mq_irq_uv(mq);
-out_4:
- xpc_gru_mq_watchlist_free_uv(mq);
-out_3:
- free_pages((unsigned long)mq->address, pg_order);
-out_2:
- kfree(mq->gru_mq_desc);
-out_1:
- kfree(mq);
-out_0:
- return ERR_PTR(ret);
-}
-
-static void
-xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq)
-{
- unsigned int mq_size;
- int pg_order;
- int ret;
-
- /* disallow other partitions to access GRU mq */
- mq_size = 1UL << mq->order;
- ret = xp_restrict_memprotect(xp_pa(mq->address), mq_size);
- BUG_ON(ret != xpSuccess);
-
- /* unregister irq handler and release mq irq/vector mapping */
- free_irq(mq->irq, NULL);
- xpc_release_gru_mq_irq_uv(mq);
-
- /* disable generation of irq when GRU mq op occurs to this mq */
- xpc_gru_mq_watchlist_free_uv(mq);
-
- pg_order = mq->order - PAGE_SHIFT;
- free_pages((unsigned long)mq->address, pg_order);
-
- kfree(mq);
-}
-
-static enum xp_retval
-xpc_send_gru_msg(struct gru_message_queue_desc *gru_mq_desc, void *msg,
- size_t msg_size)
-{
- enum xp_retval xp_ret;
- int ret;
-
- while (1) {
- ret = gru_send_message_gpa(gru_mq_desc, msg, msg_size);
- if (ret == MQE_OK) {
- xp_ret = xpSuccess;
- break;
- }
-
- if (ret == MQE_QUEUE_FULL) {
- dev_dbg(xpc_chan, "gru_send_message_gpa() returned "
- "error=MQE_QUEUE_FULL\n");
- /* !!! handle QLimit reached; delay & try again */
- /* ??? Do we add a limit to the number of retries? */
- (void)msleep_interruptible(10);
- } else if (ret == MQE_CONGESTION) {
- dev_dbg(xpc_chan, "gru_send_message_gpa() returned "
- "error=MQE_CONGESTION\n");
- /* !!! handle LB Overflow; simply try again */
- /* ??? Do we add a limit to the number of retries? */
- } else {
- /* !!! Currently this is MQE_UNEXPECTED_CB_ERR */
- dev_err(xpc_chan, "gru_send_message_gpa() returned "
- "error=%d\n", ret);
- xp_ret = xpGruSendMqError;
- break;
- }
- }
- return xp_ret;
-}
-
-static void
-xpc_process_activate_IRQ_rcvd_uv(void)
-{
- unsigned long irq_flags;
- short partid;
- struct xpc_partition *part;
- u8 act_state_req;
-
- DBUG_ON(xpc_activate_IRQ_rcvd == 0);
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part = &xpc_partitions[partid];
-
- if (part->sn.uv.act_state_req == 0)
- continue;
-
- xpc_activate_IRQ_rcvd--;
- BUG_ON(xpc_activate_IRQ_rcvd < 0);
-
- act_state_req = part->sn.uv.act_state_req;
- part->sn.uv.act_state_req = 0;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- if (act_state_req == XPC_P_ASR_ACTIVATE_UV) {
- if (part->act_state == XPC_P_AS_INACTIVE)
- xpc_activate_partition(part);
- else if (part->act_state == XPC_P_AS_DEACTIVATING)
- XPC_DEACTIVATE_PARTITION(part, xpReactivating);
-
- } else if (act_state_req == XPC_P_ASR_REACTIVATE_UV) {
- if (part->act_state == XPC_P_AS_INACTIVE)
- xpc_activate_partition(part);
- else
- XPC_DEACTIVATE_PARTITION(part, xpReactivating);
-
- } else if (act_state_req == XPC_P_ASR_DEACTIVATE_UV) {
- XPC_DEACTIVATE_PARTITION(part, part->sn.uv.reason);
-
- } else {
- BUG();
- }
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (xpc_activate_IRQ_rcvd == 0)
- break;
- }
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
-}
-
-static void
-xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
- struct xpc_activate_mq_msghdr_uv *msg_hdr,
- int part_setup,
- int *wakeup_hb_checker)
-{
- unsigned long irq_flags;
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- struct xpc_openclose_args *args;
-
- part_uv->remote_act_state = msg_hdr->act_state;
-
- switch (msg_hdr->type) {
- case XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV:
- /* syncing of remote_act_state was just done above */
- break;
-
- case XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV: {
- struct xpc_activate_mq_msg_activate_req_uv *msg;
-
- /*
- * ??? Do we deal here with ts_jiffies being different
- * ??? if act_state != XPC_P_AS_INACTIVE instead of
- * ??? below?
- */
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_activate_req_uv, hdr);
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_ACTIVATE_UV;
- part->remote_rp_pa = msg->rp_gpa; /* !!! _pa is _gpa */
- part->remote_rp_ts_jiffies = msg_hdr->rp_ts_jiffies;
- part_uv->heartbeat_gpa = msg->heartbeat_gpa;
-
- if (msg->activate_gru_mq_desc_gpa !=
- part_uv->activate_gru_mq_desc_gpa) {
- spin_lock(&part_uv->flags_lock);
- part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV;
- spin_unlock(&part_uv->flags_lock);
- part_uv->activate_gru_mq_desc_gpa =
- msg->activate_gru_mq_desc_gpa;
- }
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV: {
- struct xpc_activate_mq_msg_deactivate_req_uv *msg;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_deactivate_req_uv, hdr);
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV;
- part_uv->reason = msg->reason;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- return;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV: {
- struct xpc_activate_mq_msg_chctl_closerequest_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_closerequest_uv,
- hdr);
- args = &part->remote_openclose_args[msg->ch_number];
- args->reason = msg->reason;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREQUEST;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV: {
- struct xpc_activate_mq_msg_chctl_closereply_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_closereply_uv,
- hdr);
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREPLY;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV: {
- struct xpc_activate_mq_msg_chctl_openrequest_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_openrequest_uv,
- hdr);
- args = &part->remote_openclose_args[msg->ch_number];
- args->entry_size = msg->entry_size;
- args->local_nentries = msg->local_nentries;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREQUEST;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV: {
- struct xpc_activate_mq_msg_chctl_openreply_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_openreply_uv, hdr);
- args = &part->remote_openclose_args[msg->ch_number];
- args->remote_nentries = msg->remote_nentries;
- args->local_nentries = msg->local_nentries;
- args->local_msgqueue_pa = msg->notify_gru_mq_desc_gpa;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREPLY;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV: {
- struct xpc_activate_mq_msg_chctl_opencomplete_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_opencomplete_uv, hdr);
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENCOMPLETE;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- }
- fallthrough;
- case XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV:
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags |= XPC_P_ENGAGED_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- break;
-
- case XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV:
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags &= ~XPC_P_ENGAGED_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- break;
-
- default:
- dev_err(xpc_part, "received unknown activate_mq msg type=%d "
- "from partition=%d\n", msg_hdr->type, XPC_PARTID(part));
-
- /* get hb checker to deactivate from the remote partition */
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV;
- part_uv->reason = xpBadMsgType;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- return;
- }
-
- if (msg_hdr->rp_ts_jiffies != part->remote_rp_ts_jiffies &&
- part->remote_rp_ts_jiffies != 0) {
- /*
- * ??? Does what we do here need to be sensitive to
- * ??? act_state or remote_act_state?
- */
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_REACTIVATE_UV;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- }
-}
-
-static irqreturn_t
-xpc_handle_activate_IRQ_uv(int irq, void *dev_id)
-{
- struct xpc_activate_mq_msghdr_uv *msg_hdr;
- short partid;
- struct xpc_partition *part;
- int wakeup_hb_checker = 0;
- int part_referenced;
-
- while (1) {
- msg_hdr = gru_get_next_message(xpc_activate_mq_uv->gru_mq_desc);
- if (msg_hdr == NULL)
- break;
-
- partid = msg_hdr->partid;
- if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) {
- dev_err(xpc_part, "xpc_handle_activate_IRQ_uv() "
- "received invalid partid=0x%x in message\n",
- partid);
- } else {
- part = &xpc_partitions[partid];
-
- part_referenced = xpc_part_ref(part);
- xpc_handle_activate_mq_msg_uv(part, msg_hdr,
- part_referenced,
- &wakeup_hb_checker);
- if (part_referenced)
- xpc_part_deref(part);
- }
-
- gru_free_message(xpc_activate_mq_uv->gru_mq_desc, msg_hdr);
- }
-
- if (wakeup_hb_checker)
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-
- return IRQ_HANDLED;
-}
-
-static enum xp_retval
-xpc_cache_remote_gru_mq_desc_uv(struct gru_message_queue_desc *gru_mq_desc,
- unsigned long gru_mq_desc_gpa)
-{
- enum xp_retval ret;
-
- ret = xp_remote_memcpy(uv_gpa(gru_mq_desc), gru_mq_desc_gpa,
- sizeof(struct gru_message_queue_desc));
- if (ret == xpSuccess)
- gru_mq_desc->mq = NULL;
-
- return ret;
-}
-
-static enum xp_retval
-xpc_send_activate_IRQ_uv(struct xpc_partition *part, void *msg, size_t msg_size,
- int msg_type)
-{
- struct xpc_activate_mq_msghdr_uv *msg_hdr = msg;
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- struct gru_message_queue_desc *gru_mq_desc;
- unsigned long irq_flags;
- enum xp_retval ret;
-
- DBUG_ON(msg_size > XPC_ACTIVATE_MSG_SIZE_UV);
-
- msg_hdr->type = msg_type;
- msg_hdr->partid = xp_partition_id;
- msg_hdr->act_state = part->act_state;
- msg_hdr->rp_ts_jiffies = xpc_rsvd_page->ts_jiffies;
-
- mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex);
-again:
- if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) {
- gru_mq_desc = part_uv->cached_activate_gru_mq_desc;
- if (gru_mq_desc == NULL) {
- gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc,
- GFP_ATOMIC);
- if (gru_mq_desc == NULL) {
- ret = xpNoMemory;
- goto done;
- }
- part_uv->cached_activate_gru_mq_desc = gru_mq_desc;
- }
-
- ret = xpc_cache_remote_gru_mq_desc_uv(gru_mq_desc,
- part_uv->
- activate_gru_mq_desc_gpa);
- if (ret != xpSuccess)
- goto done;
-
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags |= XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- }
-
- /* ??? Is holding a spin_lock (ch->lock) during this call a bad idea? */
- ret = xpc_send_gru_msg(part_uv->cached_activate_gru_mq_desc, msg,
- msg_size);
- if (ret != xpSuccess) {
- smp_rmb(); /* ensure a fresh copy of part_uv->flags */
- if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV))
- goto again;
- }
-done:
- mutex_unlock(&part_uv->cached_activate_gru_mq_desc_mutex);
- return ret;
-}
-
-static void
-xpc_send_activate_IRQ_part_uv(struct xpc_partition *part, void *msg,
- size_t msg_size, int msg_type)
-{
- enum xp_retval ret;
-
- ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type);
- if (unlikely(ret != xpSuccess))
- XPC_DEACTIVATE_PARTITION(part, ret);
-}
-
-static void
-xpc_send_activate_IRQ_ch_uv(struct xpc_channel *ch, unsigned long *irq_flags,
- void *msg, size_t msg_size, int msg_type)
-{
- struct xpc_partition *part = &xpc_partitions[ch->partid];
- enum xp_retval ret;
-
- ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type);
- if (unlikely(ret != xpSuccess)) {
- if (irq_flags != NULL)
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
-
- XPC_DEACTIVATE_PARTITION(part, ret);
-
- if (irq_flags != NULL)
- spin_lock_irqsave(&ch->lock, *irq_flags);
- }
-}
-
-static void
-xpc_send_local_activate_IRQ_uv(struct xpc_partition *part, int act_state_req)
-{
- unsigned long irq_flags;
- struct xpc_partition_uv *part_uv = &part->sn.uv;
-
- /*
- * !!! Make our side think that the remote partition sent an activate
- * !!! mq message our way by doing what the activate IRQ handler would
- * !!! do had one really been sent.
- */
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = act_state_req;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-}
-
-static enum xp_retval
-xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa,
- size_t *len)
-{
- s64 status;
- enum xp_retval ret;
-
- status = uv_bios_reserved_page_pa((u64)buf, cookie, (u64 *)rp_pa,
- (u64 *)len);
- if (status == BIOS_STATUS_SUCCESS)
- ret = xpSuccess;
- else if (status == BIOS_STATUS_MORE_PASSES)
- ret = xpNeedMoreInfo;
- else
- ret = xpBiosError;
-
- return ret;
-}
-
-static int
-xpc_setup_rsvd_page_uv(struct xpc_rsvd_page *rp)
-{
- xpc_heartbeat_uv =
- &xpc_partitions[sn_partition_id].sn.uv.cached_heartbeat;
- rp->sn.uv.heartbeat_gpa = uv_gpa(xpc_heartbeat_uv);
- rp->sn.uv.activate_gru_mq_desc_gpa =
- uv_gpa(xpc_activate_mq_uv->gru_mq_desc);
- return 0;
-}
-
-static void
-xpc_allow_hb_uv(short partid)
-{
-}
-
-static void
-xpc_disallow_hb_uv(short partid)
-{
-}
-
-static void
-xpc_disallow_all_hbs_uv(void)
-{
-}
-
-static void
-xpc_increment_heartbeat_uv(void)
-{
- xpc_heartbeat_uv->value++;
-}
-
-static void
-xpc_offline_heartbeat_uv(void)
-{
- xpc_increment_heartbeat_uv();
- xpc_heartbeat_uv->offline = 1;
-}
-
-static void
-xpc_online_heartbeat_uv(void)
-{
- xpc_increment_heartbeat_uv();
- xpc_heartbeat_uv->offline = 0;
-}
-
-static void
-xpc_heartbeat_init_uv(void)
-{
- xpc_heartbeat_uv->value = 1;
- xpc_heartbeat_uv->offline = 0;
-}
-
-static void
-xpc_heartbeat_exit_uv(void)
-{
- xpc_offline_heartbeat_uv();
-}
-
-static enum xp_retval
-xpc_get_remote_heartbeat_uv(struct xpc_partition *part)
-{
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- enum xp_retval ret;
-
- ret = xp_remote_memcpy(uv_gpa(&part_uv->cached_heartbeat),
- part_uv->heartbeat_gpa,
- sizeof(struct xpc_heartbeat_uv));
- if (ret != xpSuccess)
- return ret;
-
- if (part_uv->cached_heartbeat.value == part->last_heartbeat &&
- !part_uv->cached_heartbeat.offline) {
-
- ret = xpNoHeartbeat;
- } else {
- part->last_heartbeat = part_uv->cached_heartbeat.value;
- }
- return ret;
-}
-
-static void
-xpc_request_partition_activation_uv(struct xpc_rsvd_page *remote_rp,
- unsigned long remote_rp_gpa, int nasid)
-{
- short partid = remote_rp->SAL_partid;
- struct xpc_partition *part = &xpc_partitions[partid];
- struct xpc_activate_mq_msg_activate_req_uv msg;
-
- part->remote_rp_pa = remote_rp_gpa; /* !!! _pa here is really _gpa */
- part->remote_rp_ts_jiffies = remote_rp->ts_jiffies;
- part->sn.uv.heartbeat_gpa = remote_rp->sn.uv.heartbeat_gpa;
- part->sn.uv.activate_gru_mq_desc_gpa =
- remote_rp->sn.uv.activate_gru_mq_desc_gpa;
-
- /*
- * ??? Is it a good idea to make this conditional on what is
- * ??? potentially stale state information?
- */
- if (part->sn.uv.remote_act_state == XPC_P_AS_INACTIVE) {
- msg.rp_gpa = uv_gpa(xpc_rsvd_page);
- msg.heartbeat_gpa = xpc_rsvd_page->sn.uv.heartbeat_gpa;
- msg.activate_gru_mq_desc_gpa =
- xpc_rsvd_page->sn.uv.activate_gru_mq_desc_gpa;
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV);
- }
-
- if (part->act_state == XPC_P_AS_INACTIVE)
- xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV);
-}
-
-static void
-xpc_request_partition_reactivation_uv(struct xpc_partition *part)
-{
- xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV);
-}
-
-static void
-xpc_request_partition_deactivation_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_deactivate_req_uv msg;
-
- /*
- * ??? Is it a good idea to make this conditional on what is
- * ??? potentially stale state information?
- */
- if (part->sn.uv.remote_act_state != XPC_P_AS_DEACTIVATING &&
- part->sn.uv.remote_act_state != XPC_P_AS_INACTIVE) {
-
- msg.reason = part->reason;
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV);
- }
-}
-
-static void
-xpc_cancel_partition_deactivation_request_uv(struct xpc_partition *part)
-{
- /* nothing needs to be done */
- return;
-}
-
-static void
-xpc_init_fifo_uv(struct xpc_fifo_head_uv *head)
-{
- head->first = NULL;
- head->last = NULL;
- spin_lock_init(&head->lock);
- head->n_entries = 0;
-}
-
-static void *
-xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head)
-{
- unsigned long irq_flags;
- struct xpc_fifo_entry_uv *first;
-
- spin_lock_irqsave(&head->lock, irq_flags);
- first = head->first;
- if (head->first != NULL) {
- head->first = first->next;
- if (head->first == NULL)
- head->last = NULL;
-
- head->n_entries--;
- BUG_ON(head->n_entries < 0);
-
- first->next = NULL;
- }
- spin_unlock_irqrestore(&head->lock, irq_flags);
- return first;
-}
-
-static void
-xpc_put_fifo_entry_uv(struct xpc_fifo_head_uv *head,
- struct xpc_fifo_entry_uv *last)
-{
- unsigned long irq_flags;
-
- last->next = NULL;
- spin_lock_irqsave(&head->lock, irq_flags);
- if (head->last != NULL)
- head->last->next = last;
- else
- head->first = last;
- head->last = last;
- head->n_entries++;
- spin_unlock_irqrestore(&head->lock, irq_flags);
-}
-
-static int
-xpc_n_of_fifo_entries_uv(struct xpc_fifo_head_uv *head)
-{
- return head->n_entries;
-}
-
-/*
- * Setup the channel structures that are uv specific.
- */
-static enum xp_retval
-xpc_setup_ch_structures_uv(struct xpc_partition *part)
-{
- struct xpc_channel_uv *ch_uv;
- int ch_number;
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch_uv = &part->channels[ch_number].sn.uv;
-
- xpc_init_fifo_uv(&ch_uv->msg_slot_free_list);
- xpc_init_fifo_uv(&ch_uv->recv_msg_list);
- }
-
- return xpSuccess;
-}
-
-/*
- * Teardown the channel structures that are uv specific.
- */
-static void
-xpc_teardown_ch_structures_uv(struct xpc_partition *part)
-{
- /* nothing needs to be done */
- return;
-}
-
-static enum xp_retval
-xpc_make_first_contact_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_uv msg;
-
- /*
- * We send a sync msg to get the remote partition's remote_act_state
- * updated to our current act_state which at this point should
- * be XPC_P_AS_ACTIVATING.
- */
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV);
-
- while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) ||
- (part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) {
-
- dev_dbg(xpc_part, "waiting to make first contact with "
- "partition %d\n", XPC_PARTID(part));
-
- /* wait a 1/4 of a second or so */
- (void)msleep_interruptible(250);
-
- if (part->act_state == XPC_P_AS_DEACTIVATING)
- return part->reason;
- }
-
- return xpSuccess;
-}
-
-static u64
-xpc_get_chctl_all_flags_uv(struct xpc_partition *part)
-{
- unsigned long irq_flags;
- union xpc_channel_ctl_flags chctl;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- chctl = part->chctl;
- if (chctl.all_flags != 0)
- part->chctl.all_flags = 0;
-
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
- return chctl.all_flags;
-}
-
-static enum xp_retval
-xpc_allocate_send_msg_slot_uv(struct xpc_channel *ch)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
- struct xpc_send_msg_slot_uv *msg_slot;
- unsigned long irq_flags;
- int nentries;
- int entry;
- size_t nbytes;
-
- for (nentries = ch->local_nentries; nentries > 0; nentries--) {
- nbytes = nentries * sizeof(struct xpc_send_msg_slot_uv);
- ch_uv->send_msg_slots = kzalloc(nbytes, GFP_KERNEL);
- if (ch_uv->send_msg_slots == NULL)
- continue;
-
- for (entry = 0; entry < nentries; entry++) {
- msg_slot = &ch_uv->send_msg_slots[entry];
-
- msg_slot->msg_slot_number = entry;
- xpc_put_fifo_entry_uv(&ch_uv->msg_slot_free_list,
- &msg_slot->next);
- }
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if (nentries < ch->local_nentries)
- ch->local_nentries = nentries;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return xpSuccess;
- }
-
- return xpNoMemory;
-}
-
-static enum xp_retval
-xpc_allocate_recv_msg_slot_uv(struct xpc_channel *ch)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
- struct xpc_notify_mq_msg_uv *msg_slot;
- unsigned long irq_flags;
- int nentries;
- int entry;
- size_t nbytes;
-
- for (nentries = ch->remote_nentries; nentries > 0; nentries--) {
- nbytes = nentries * ch->entry_size;
- ch_uv->recv_msg_slots = kzalloc(nbytes, GFP_KERNEL);
- if (ch_uv->recv_msg_slots == NULL)
- continue;
-
- for (entry = 0; entry < nentries; entry++) {
- msg_slot = ch_uv->recv_msg_slots +
- entry * ch->entry_size;
-
- msg_slot->hdr.msg_slot_number = entry;
- }
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if (nentries < ch->remote_nentries)
- ch->remote_nentries = nentries;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return xpSuccess;
- }
-
- return xpNoMemory;
-}
-
-/*
- * Allocate msg_slots associated with the channel.
- */
-static enum xp_retval
-xpc_setup_msg_structures_uv(struct xpc_channel *ch)
-{
- static enum xp_retval ret;
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
-
- DBUG_ON(ch->flags & XPC_C_SETUP);
-
- ch_uv->cached_notify_gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc);
- if (ch_uv->cached_notify_gru_mq_desc == NULL)
- return xpNoMemory;
-
- ret = xpc_allocate_send_msg_slot_uv(ch);
- if (ret == xpSuccess) {
-
- ret = xpc_allocate_recv_msg_slot_uv(ch);
- if (ret != xpSuccess) {
- kfree(ch_uv->send_msg_slots);
- xpc_init_fifo_uv(&ch_uv->msg_slot_free_list);
- }
- }
- return ret;
-}
-
-/*
- * Free up msg_slots and clear other stuff that were setup for the specified
- * channel.
- */
-static void
-xpc_teardown_msg_structures_uv(struct xpc_channel *ch)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
-
- lockdep_assert_held(&ch->lock);
-
- kfree(ch_uv->cached_notify_gru_mq_desc);
- ch_uv->cached_notify_gru_mq_desc = NULL;
-
- if (ch->flags & XPC_C_SETUP) {
- xpc_init_fifo_uv(&ch_uv->msg_slot_free_list);
- kfree(ch_uv->send_msg_slots);
- xpc_init_fifo_uv(&ch_uv->recv_msg_list);
- kfree(ch_uv->recv_msg_slots);
- }
-}
-
-static void
-xpc_send_chctl_closerequest_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_closerequest_uv msg;
-
- msg.ch_number = ch->number;
- msg.reason = ch->reason;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV);
-}
-
-static void
-xpc_send_chctl_closereply_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_closereply_uv msg;
-
- msg.ch_number = ch->number;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV);
-}
-
-static void
-xpc_send_chctl_openrequest_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_openrequest_uv msg;
-
- msg.ch_number = ch->number;
- msg.entry_size = ch->entry_size;
- msg.local_nentries = ch->local_nentries;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV);
-}
-
-static void
-xpc_send_chctl_openreply_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_openreply_uv msg;
-
- msg.ch_number = ch->number;
- msg.local_nentries = ch->local_nentries;
- msg.remote_nentries = ch->remote_nentries;
- msg.notify_gru_mq_desc_gpa = uv_gpa(xpc_notify_mq_uv->gru_mq_desc);
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV);
-}
-
-static void
-xpc_send_chctl_opencomplete_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_opencomplete_uv msg;
-
- msg.ch_number = ch->number;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV);
-}
-
-static void
-xpc_send_chctl_local_msgrequest_uv(struct xpc_partition *part, int ch_number)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[ch_number] |= XPC_CHCTL_MSGREQUEST;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
-}
-
-static enum xp_retval
-xpc_save_remote_msgqueue_pa_uv(struct xpc_channel *ch,
- unsigned long gru_mq_desc_gpa)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
-
- DBUG_ON(ch_uv->cached_notify_gru_mq_desc == NULL);
- return xpc_cache_remote_gru_mq_desc_uv(ch_uv->cached_notify_gru_mq_desc,
- gru_mq_desc_gpa);
-}
-
-static void
-xpc_indicate_partition_engaged_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_uv msg;
-
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV);
-}
-
-static void
-xpc_indicate_partition_disengaged_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_uv msg;
-
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV);
-}
-
-static void
-xpc_assume_partition_disengaged_uv(short partid)
-{
- struct xpc_partition_uv *part_uv = &xpc_partitions[partid].sn.uv;
- unsigned long irq_flags;
-
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags &= ~XPC_P_ENGAGED_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
-}
-
-static int
-xpc_partition_engaged_uv(short partid)
-{
- return (xpc_partitions[partid].sn.uv.flags & XPC_P_ENGAGED_UV) != 0;
-}
-
-static int
-xpc_any_partition_engaged_uv(void)
-{
- struct xpc_partition_uv *part_uv;
- short partid;
-
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part_uv = &xpc_partitions[partid].sn.uv;
- if ((part_uv->flags & XPC_P_ENGAGED_UV) != 0)
- return 1;
- }
- return 0;
-}
-
-static enum xp_retval
-xpc_allocate_msg_slot_uv(struct xpc_channel *ch, u32 flags,
- struct xpc_send_msg_slot_uv **address_of_msg_slot)
-{
- enum xp_retval ret;
- struct xpc_send_msg_slot_uv *msg_slot;
- struct xpc_fifo_entry_uv *entry;
-
- while (1) {
- entry = xpc_get_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list);
- if (entry != NULL)
- break;
-
- if (flags & XPC_NOWAIT)
- return xpNoWait;
-
- ret = xpc_allocate_msg_wait(ch);
- if (ret != xpInterrupted && ret != xpTimeout)
- return ret;
- }
-
- msg_slot = container_of(entry, struct xpc_send_msg_slot_uv, next);
- *address_of_msg_slot = msg_slot;
- return xpSuccess;
-}
-
-static void
-xpc_free_msg_slot_uv(struct xpc_channel *ch,
- struct xpc_send_msg_slot_uv *msg_slot)
-{
- xpc_put_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list, &msg_slot->next);
-
- /* wakeup anyone waiting for a free msg slot */
- if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)
- wake_up(&ch->msg_allocate_wq);
-}
-
-static void
-xpc_notify_sender_uv(struct xpc_channel *ch,
- struct xpc_send_msg_slot_uv *msg_slot,
- enum xp_retval reason)
-{
- xpc_notify_func func = msg_slot->func;
-
- if (func != NULL && cmpxchg(&msg_slot->func, func, NULL) == func) {
-
- atomic_dec(&ch->n_to_notify);
-
- dev_dbg(xpc_chan, "msg_slot->func() called, msg_slot=0x%p "
- "msg_slot_number=%d partid=%d channel=%d\n", msg_slot,
- msg_slot->msg_slot_number, ch->partid, ch->number);
-
- func(reason, ch->partid, ch->number, msg_slot->key);
-
- dev_dbg(xpc_chan, "msg_slot->func() returned, msg_slot=0x%p "
- "msg_slot_number=%d partid=%d channel=%d\n", msg_slot,
- msg_slot->msg_slot_number, ch->partid, ch->number);
- }
-}
-
-static void
-xpc_handle_notify_mq_ack_uv(struct xpc_channel *ch,
- struct xpc_notify_mq_msg_uv *msg)
-{
- struct xpc_send_msg_slot_uv *msg_slot;
- int entry = msg->hdr.msg_slot_number % ch->local_nentries;
-
- msg_slot = &ch->sn.uv.send_msg_slots[entry];
-
- BUG_ON(msg_slot->msg_slot_number != msg->hdr.msg_slot_number);
- msg_slot->msg_slot_number += ch->local_nentries;
-
- if (msg_slot->func != NULL)
- xpc_notify_sender_uv(ch, msg_slot, xpMsgDelivered);
-
- xpc_free_msg_slot_uv(ch, msg_slot);
-}
-
-static void
-xpc_handle_notify_mq_msg_uv(struct xpc_partition *part,
- struct xpc_notify_mq_msg_uv *msg)
-{
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- struct xpc_channel *ch;
- struct xpc_channel_uv *ch_uv;
- struct xpc_notify_mq_msg_uv *msg_slot;
- unsigned long irq_flags;
- int ch_number = msg->hdr.ch_number;
-
- if (unlikely(ch_number >= part->nchannels)) {
- dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received invalid "
- "channel number=0x%x in message from partid=%d\n",
- ch_number, XPC_PARTID(part));
-
- /* get hb checker to deactivate from the remote partition */
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV;
- part_uv->reason = xpBadChannelNumber;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- wake_up_interruptible(&xpc_activate_IRQ_wq);
- return;
- }
-
- ch = &part->channels[ch_number];
- xpc_msgqueue_ref(ch);
-
- if (!(ch->flags & XPC_C_CONNECTED)) {
- xpc_msgqueue_deref(ch);
- return;
- }
-
- /* see if we're really dealing with an ACK for a previously sent msg */
- if (msg->hdr.size == 0) {
- xpc_handle_notify_mq_ack_uv(ch, msg);
- xpc_msgqueue_deref(ch);
- return;
- }
-
- /* we're dealing with a normal message sent via the notify_mq */
- ch_uv = &ch->sn.uv;
-
- msg_slot = ch_uv->recv_msg_slots +
- (msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size;
-
- BUG_ON(msg_slot->hdr.size != 0);
-
- memcpy(msg_slot, msg, msg->hdr.size);
-
- xpc_put_fifo_entry_uv(&ch_uv->recv_msg_list, &msg_slot->hdr.u.next);
-
- if (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) {
- /*
- * If there is an existing idle kthread get it to deliver
- * the payload, otherwise we'll have to get the channel mgr
- * for this partition to create a kthread to do the delivery.
- */
- if (atomic_read(&ch->kthreads_idle) > 0)
- wake_up_nr(&ch->idle_wq, 1);
- else
- xpc_send_chctl_local_msgrequest_uv(part, ch->number);
- }
- xpc_msgqueue_deref(ch);
-}
-
-static irqreturn_t
-xpc_handle_notify_IRQ_uv(int irq, void *dev_id)
-{
- struct xpc_notify_mq_msg_uv *msg;
- short partid;
- struct xpc_partition *part;
-
- while ((msg = gru_get_next_message(xpc_notify_mq_uv->gru_mq_desc)) !=
- NULL) {
-
- partid = msg->hdr.partid;
- if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) {
- dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received "
- "invalid partid=0x%x in message\n", partid);
- } else {
- part = &xpc_partitions[partid];
-
- if (xpc_part_ref(part)) {
- xpc_handle_notify_mq_msg_uv(part, msg);
- xpc_part_deref(part);
- }
- }
-
- gru_free_message(xpc_notify_mq_uv->gru_mq_desc, msg);
- }
-
- return IRQ_HANDLED;
-}
-
-static int
-xpc_n_of_deliverable_payloads_uv(struct xpc_channel *ch)
-{
- return xpc_n_of_fifo_entries_uv(&ch->sn.uv.recv_msg_list);
-}
-
-static void
-xpc_process_msg_chctl_flags_uv(struct xpc_partition *part, int ch_number)
-{
- struct xpc_channel *ch = &part->channels[ch_number];
- int ndeliverable_payloads;
-
- xpc_msgqueue_ref(ch);
-
- ndeliverable_payloads = xpc_n_of_deliverable_payloads_uv(ch);
-
- if (ndeliverable_payloads > 0 &&
- (ch->flags & XPC_C_CONNECTED) &&
- (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE)) {
-
- xpc_activate_kthreads(ch, ndeliverable_payloads);
- }
-
- xpc_msgqueue_deref(ch);
-}
-
-static enum xp_retval
-xpc_send_payload_uv(struct xpc_channel *ch, u32 flags, void *payload,
- u16 payload_size, u8 notify_type, xpc_notify_func func,
- void *key)
-{
- enum xp_retval ret = xpSuccess;
- struct xpc_send_msg_slot_uv *msg_slot = NULL;
- struct xpc_notify_mq_msg_uv *msg;
- u8 msg_buffer[XPC_NOTIFY_MSG_SIZE_UV];
- size_t msg_size;
-
- DBUG_ON(notify_type != XPC_N_CALL);
-
- msg_size = sizeof(struct xpc_notify_mq_msghdr_uv) + payload_size;
- if (msg_size > ch->entry_size)
- return xpPayloadTooBig;
-
- xpc_msgqueue_ref(ch);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- ret = ch->reason;
- goto out_1;
- }
- if (!(ch->flags & XPC_C_CONNECTED)) {
- ret = xpNotConnected;
- goto out_1;
- }
-
- ret = xpc_allocate_msg_slot_uv(ch, flags, &msg_slot);
- if (ret != xpSuccess)
- goto out_1;
-
- if (func != NULL) {
- atomic_inc(&ch->n_to_notify);
-
- msg_slot->key = key;
- smp_wmb(); /* a non-NULL func must hit memory after the key */
- msg_slot->func = func;
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- ret = ch->reason;
- goto out_2;
- }
- }
-
- msg = (struct xpc_notify_mq_msg_uv *)&msg_buffer;
- msg->hdr.partid = xp_partition_id;
- msg->hdr.ch_number = ch->number;
- msg->hdr.size = msg_size;
- msg->hdr.msg_slot_number = msg_slot->msg_slot_number;
- memcpy(&msg->payload, payload, payload_size);
-
- ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg,
- msg_size);
- if (ret == xpSuccess)
- goto out_1;
-
- XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret);
-out_2:
- if (func != NULL) {
- /*
- * Try to NULL the msg_slot's func field. If we fail, then
- * xpc_notify_senders_of_disconnect_uv() beat us to it, in which
- * case we need to pretend we succeeded to send the message
- * since the user will get a callout for the disconnect error
- * by xpc_notify_senders_of_disconnect_uv(), and to also get an
- * error returned here will confuse them. Additionally, since
- * in this case the channel is being disconnected we don't need
- * to put the msg_slot back on the free list.
- */
- if (cmpxchg(&msg_slot->func, func, NULL) != func) {
- ret = xpSuccess;
- goto out_1;
- }
-
- msg_slot->key = NULL;
- atomic_dec(&ch->n_to_notify);
- }
- xpc_free_msg_slot_uv(ch, msg_slot);
-out_1:
- xpc_msgqueue_deref(ch);
- return ret;
-}
-
-/*
- * Tell the callers of xpc_send_notify() that the status of their payloads
- * is unknown because the channel is now disconnecting.
- *
- * We don't worry about putting these msg_slots on the free list since the
- * msg_slots themselves are about to be kfree'd.
- */
-static void
-xpc_notify_senders_of_disconnect_uv(struct xpc_channel *ch)
-{
- struct xpc_send_msg_slot_uv *msg_slot;
- int entry;
-
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
-
- for (entry = 0; entry < ch->local_nentries; entry++) {
-
- if (atomic_read(&ch->n_to_notify) == 0)
- break;
-
- msg_slot = &ch->sn.uv.send_msg_slots[entry];
- if (msg_slot->func != NULL)
- xpc_notify_sender_uv(ch, msg_slot, ch->reason);
- }
-}
-
-/*
- * Get the next deliverable message's payload.
- */
-static void *
-xpc_get_deliverable_payload_uv(struct xpc_channel *ch)
-{
- struct xpc_fifo_entry_uv *entry;
- struct xpc_notify_mq_msg_uv *msg;
- void *payload = NULL;
-
- if (!(ch->flags & XPC_C_DISCONNECTING)) {
- entry = xpc_get_fifo_entry_uv(&ch->sn.uv.recv_msg_list);
- if (entry != NULL) {
- msg = container_of(entry, struct xpc_notify_mq_msg_uv,
- hdr.u.next);
- payload = &msg->payload;
- }
- }
- return payload;
-}
-
-static void
-xpc_received_payload_uv(struct xpc_channel *ch, void *payload)
-{
- struct xpc_notify_mq_msg_uv *msg;
- enum xp_retval ret;
-
- msg = container_of(payload, struct xpc_notify_mq_msg_uv, payload);
-
- /* return an ACK to the sender of this message */
-
- msg->hdr.partid = xp_partition_id;
- msg->hdr.size = 0; /* size of zero indicates this is an ACK */
-
- ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg,
- sizeof(struct xpc_notify_mq_msghdr_uv));
- if (ret != xpSuccess)
- XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret);
-}
-
-static const struct xpc_arch_operations xpc_arch_ops_uv = {
- .setup_partitions = xpc_setup_partitions_uv,
- .teardown_partitions = xpc_teardown_partitions_uv,
- .process_activate_IRQ_rcvd = xpc_process_activate_IRQ_rcvd_uv,
- .get_partition_rsvd_page_pa = xpc_get_partition_rsvd_page_pa_uv,
- .setup_rsvd_page = xpc_setup_rsvd_page_uv,
-
- .allow_hb = xpc_allow_hb_uv,
- .disallow_hb = xpc_disallow_hb_uv,
- .disallow_all_hbs = xpc_disallow_all_hbs_uv,
- .increment_heartbeat = xpc_increment_heartbeat_uv,
- .offline_heartbeat = xpc_offline_heartbeat_uv,
- .online_heartbeat = xpc_online_heartbeat_uv,
- .heartbeat_init = xpc_heartbeat_init_uv,
- .heartbeat_exit = xpc_heartbeat_exit_uv,
- .get_remote_heartbeat = xpc_get_remote_heartbeat_uv,
-
- .request_partition_activation =
- xpc_request_partition_activation_uv,
- .request_partition_reactivation =
- xpc_request_partition_reactivation_uv,
- .request_partition_deactivation =
- xpc_request_partition_deactivation_uv,
- .cancel_partition_deactivation_request =
- xpc_cancel_partition_deactivation_request_uv,
-
- .setup_ch_structures = xpc_setup_ch_structures_uv,
- .teardown_ch_structures = xpc_teardown_ch_structures_uv,
-
- .make_first_contact = xpc_make_first_contact_uv,
-
- .get_chctl_all_flags = xpc_get_chctl_all_flags_uv,
- .send_chctl_closerequest = xpc_send_chctl_closerequest_uv,
- .send_chctl_closereply = xpc_send_chctl_closereply_uv,
- .send_chctl_openrequest = xpc_send_chctl_openrequest_uv,
- .send_chctl_openreply = xpc_send_chctl_openreply_uv,
- .send_chctl_opencomplete = xpc_send_chctl_opencomplete_uv,
- .process_msg_chctl_flags = xpc_process_msg_chctl_flags_uv,
-
- .save_remote_msgqueue_pa = xpc_save_remote_msgqueue_pa_uv,
-
- .setup_msg_structures = xpc_setup_msg_structures_uv,
- .teardown_msg_structures = xpc_teardown_msg_structures_uv,
-
- .indicate_partition_engaged = xpc_indicate_partition_engaged_uv,
- .indicate_partition_disengaged = xpc_indicate_partition_disengaged_uv,
- .assume_partition_disengaged = xpc_assume_partition_disengaged_uv,
- .partition_engaged = xpc_partition_engaged_uv,
- .any_partition_engaged = xpc_any_partition_engaged_uv,
-
- .n_of_deliverable_payloads = xpc_n_of_deliverable_payloads_uv,
- .send_payload = xpc_send_payload_uv,
- .get_deliverable_payload = xpc_get_deliverable_payload_uv,
- .received_payload = xpc_received_payload_uv,
- .notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_uv,
-};
-
-static int
-xpc_init_mq_node(int nid)
-{
- int cpu;
-
- cpus_read_lock();
-
- for_each_cpu(cpu, cpumask_of_node(nid)) {
- xpc_activate_mq_uv =
- xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, nid,
- XPC_ACTIVATE_IRQ_NAME,
- xpc_handle_activate_IRQ_uv);
- if (!IS_ERR(xpc_activate_mq_uv))
- break;
- }
- if (IS_ERR(xpc_activate_mq_uv)) {
- cpus_read_unlock();
- return PTR_ERR(xpc_activate_mq_uv);
- }
-
- for_each_cpu(cpu, cpumask_of_node(nid)) {
- xpc_notify_mq_uv =
- xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, nid,
- XPC_NOTIFY_IRQ_NAME,
- xpc_handle_notify_IRQ_uv);
- if (!IS_ERR(xpc_notify_mq_uv))
- break;
- }
- if (IS_ERR(xpc_notify_mq_uv)) {
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
- cpus_read_unlock();
- return PTR_ERR(xpc_notify_mq_uv);
- }
-
- cpus_read_unlock();
- return 0;
-}
-
-int
-xpc_init_uv(void)
-{
- int nid;
- int ret = 0;
-
- xpc_arch_ops = xpc_arch_ops_uv;
-
- if (sizeof(struct xpc_notify_mq_msghdr_uv) > XPC_MSG_HDR_MAX_SIZE) {
- dev_err(xpc_part, "xpc_notify_mq_msghdr_uv is larger than %d\n",
- XPC_MSG_HDR_MAX_SIZE);
- return -E2BIG;
- }
-
- if (xpc_mq_node < 0)
- for_each_online_node(nid) {
- ret = xpc_init_mq_node(nid);
-
- if (!ret)
- break;
- }
- else
- ret = xpc_init_mq_node(xpc_mq_node);
-
- if (ret < 0)
- dev_err(xpc_part, "xpc_init_mq_node() returned error=%d\n",
- -ret);
-
- return ret;
-}
-
-void
-xpc_exit_uv(void)
-{
- xpc_destroy_gru_mq_uv(xpc_notify_mq_uv);
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
-}
-
-module_param(xpc_mq_node, int, 0);
-MODULE_PARM_DESC(xpc_mq_node, "Node number on which to allocate message queues.");
diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c
deleted file mode 100644
index 1533b72d57b1..000000000000
--- a/drivers/misc/sgi-xp/xpnet.c
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved.
- */
-
-/*
- * Cross Partition Network Interface (XPNET) support
- *
- * XPNET provides a virtual network layered on top of the Cross
- * Partition communication layer.
- *
- * XPNET provides direct point-to-point and broadcast-like support
- * for an ethernet-like device. The ethernet broadcast medium is
- * replaced with a point-to-point message structure which passes
- * pointers to a DMA-capable block that a remote partition should
- * retrieve and pass to the upper level networking layer.
- *
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include "xp.h"
-
-/*
- * The message payload transferred by XPC.
- *
- * buf_pa is the physical address where the DMA should pull from.
- *
- * NOTE: for performance reasons, buf_pa should _ALWAYS_ begin on a
- * cacheline boundary. To accomplish this, we record the number of
- * bytes from the beginning of the first cacheline to the first useful
- * byte of the skb (leadin_ignore) and the number of bytes from the
- * last useful byte of the skb to the end of the last cacheline
- * (tailout_ignore).
- *
- * size is the number of bytes to transfer which includes the skb->len
- * (useful bytes of the senders skb) plus the leadin and tailout
- */
-struct xpnet_message {
- u16 version; /* Version for this message */
- u16 embedded_bytes; /* #of bytes embedded in XPC message */
- u32 magic; /* Special number indicating this is xpnet */
- unsigned long buf_pa; /* phys address of buffer to retrieve */
- u32 size; /* #of bytes in buffer */
- u8 leadin_ignore; /* #of bytes to ignore at the beginning */
- u8 tailout_ignore; /* #of bytes to ignore at the end */
- unsigned char data; /* body of small packets */
-};
-
-/*
- * Determine the size of our message, the cacheline aligned size,
- * and then the number of message will request from XPC.
- *
- * XPC expects each message to exist in an individual cacheline.
- */
-#define XPNET_MSG_SIZE XPC_MSG_PAYLOAD_MAX_SIZE
-#define XPNET_MSG_DATA_MAX \
- (XPNET_MSG_SIZE - offsetof(struct xpnet_message, data))
-#define XPNET_MSG_NENTRIES (PAGE_SIZE / XPC_MSG_MAX_SIZE)
-
-#define XPNET_MAX_KTHREADS (XPNET_MSG_NENTRIES + 1)
-#define XPNET_MAX_IDLE_KTHREADS (XPNET_MSG_NENTRIES + 1)
-
-/*
- * Version number of XPNET implementation. XPNET can always talk to versions
- * with same major #, and never talk to versions with a different version.
- */
-#define _XPNET_VERSION(_major, _minor) (((_major) << 4) | (_minor))
-#define XPNET_VERSION_MAJOR(_v) ((_v) >> 4)
-#define XPNET_VERSION_MINOR(_v) ((_v) & 0xf)
-
-#define XPNET_VERSION _XPNET_VERSION(1, 0) /* version 1.0 */
-#define XPNET_VERSION_EMBED _XPNET_VERSION(1, 1) /* version 1.1 */
-#define XPNET_MAGIC 0x88786984 /* "XNET" */
-
-#define XPNET_VALID_MSG(_m) \
- ((XPNET_VERSION_MAJOR(_m->version) == XPNET_VERSION_MAJOR(XPNET_VERSION)) \
- && (msg->magic == XPNET_MAGIC))
-
-#define XPNET_DEVICE_NAME "xp0"
-
-/*
- * When messages are queued with xpc_send_notify, a kmalloc'd buffer
- * of the following type is passed as a notification cookie. When the
- * notification function is called, we use the cookie to decide
- * whether all outstanding message sends have completed. The skb can
- * then be released.
- */
-struct xpnet_pending_msg {
- struct sk_buff *skb;
- atomic_t use_count;
-};
-
-static struct net_device *xpnet_device;
-
-/*
- * When we are notified of other partitions activating, we add them to
- * our bitmask of partitions to which we broadcast.
- */
-static unsigned long *xpnet_broadcast_partitions;
-/* protect above */
-static DEFINE_SPINLOCK(xpnet_broadcast_lock);
-
-/*
- * Since the Block Transfer Engine (BTE) is being used for the transfer
- * and it relies upon cache-line size transfers, we need to reserve at
- * least one cache-line for head and tail alignment. The BTE is
- * limited to 8MB transfers.
- *
- * Testing has shown that changing MTU to greater than 64KB has no effect
- * on TCP as the two sides negotiate a Max Segment Size that is limited
- * to 64K. Other protocols May use packets greater than this, but for
- * now, the default is 64KB.
- */
-#define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES)
-/* 68 comes from min TCP+IP+MAC header */
-#define XPNET_MIN_MTU 68
-/* 32KB has been determined to be the ideal */
-#define XPNET_DEF_MTU (0x8000UL)
-
-/*
- * The partid is encapsulated in the MAC address beginning in the following
- * octet and it consists of two octets.
- */
-#define XPNET_PARTID_OCTET 2
-
-/* Define the XPNET debug device structures to be used with dev_dbg() et al */
-
-static struct device_driver xpnet_dbg_name = {
- .name = "xpnet"
-};
-
-static struct device xpnet_dbg_subname = {
- .init_name = "", /* set to "" */
- .driver = &xpnet_dbg_name
-};
-
-static struct device *xpnet = &xpnet_dbg_subname;
-
-/*
- * Packet was recevied by XPC and forwarded to us.
- */
-static void
-xpnet_receive(short partid, int channel, struct xpnet_message *msg)
-{
- struct sk_buff *skb;
- void *dst;
- enum xp_retval ret;
-
- if (!XPNET_VALID_MSG(msg)) {
- /*
- * Packet with a different XPC version. Ignore.
- */
- xpc_received(partid, channel, (void *)msg);
-
- xpnet_device->stats.rx_errors++;
-
- return;
- }
- dev_dbg(xpnet, "received 0x%lx, %d, %d, %d\n", msg->buf_pa, msg->size,
- msg->leadin_ignore, msg->tailout_ignore);
-
- /* reserve an extra cache line */
- skb = dev_alloc_skb(msg->size + L1_CACHE_BYTES);
- if (!skb) {
- dev_err(xpnet, "failed on dev_alloc_skb(%d)\n",
- msg->size + L1_CACHE_BYTES);
-
- xpc_received(partid, channel, (void *)msg);
-
- xpnet_device->stats.rx_errors++;
-
- return;
- }
-
- /*
- * The allocated skb has some reserved space.
- * In order to use xp_remote_memcpy(), we need to get the
- * skb->data pointer moved forward.
- */
- skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data &
- (L1_CACHE_BYTES - 1)) +
- msg->leadin_ignore));
-
- /*
- * Update the tail pointer to indicate data actually
- * transferred.
- */
- skb_put(skb, (msg->size - msg->leadin_ignore - msg->tailout_ignore));
-
- /*
- * Move the data over from the other side.
- */
- if ((XPNET_VERSION_MINOR(msg->version) == 1) &&
- (msg->embedded_bytes != 0)) {
- dev_dbg(xpnet, "copying embedded message. memcpy(0x%p, 0x%p, "
- "%lu)\n", skb->data, &msg->data,
- (size_t)msg->embedded_bytes);
-
- skb_copy_to_linear_data(skb, &msg->data,
- (size_t)msg->embedded_bytes);
- } else {
- dst = (void *)((u64)skb->data & ~(L1_CACHE_BYTES - 1));
- dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t"
- "xp_remote_memcpy(0x%p, 0x%p, %u)\n", dst,
- (void *)msg->buf_pa, msg->size);
-
- ret = xp_remote_memcpy(xp_pa(dst), msg->buf_pa, msg->size);
- if (ret != xpSuccess) {
- /*
- * !!! Need better way of cleaning skb. Currently skb
- * !!! appears in_use and we can't just call
- * !!! dev_kfree_skb.
- */
- dev_err(xpnet, "xp_remote_memcpy(0x%p, 0x%p, 0x%x) "
- "returned error=0x%x\n", dst,
- (void *)msg->buf_pa, msg->size, ret);
-
- xpc_received(partid, channel, (void *)msg);
-
- xpnet_device->stats.rx_errors++;
-
- return;
- }
- }
-
- dev_dbg(xpnet, "<skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
- "skb->end=0x%p skb->len=%d\n", (void *)skb->head,
- (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
- skb->len);
-
- skb->protocol = eth_type_trans(skb, xpnet_device);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-
- dev_dbg(xpnet, "passing skb to network layer\n"
- "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p "
- "skb->end=0x%p skb->len=%d\n",
- (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb),
- skb_end_pointer(skb), skb->len);
-
- xpnet_device->stats.rx_packets++;
- xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN;
-
- netif_rx(skb);
- xpc_received(partid, channel, (void *)msg);
-}
-
-/*
- * This is the handler which XPC calls during any sort of change in
- * state or message reception on a connection.
- */
-static void
-xpnet_connection_activity(enum xp_retval reason, short partid, int channel,
- void *data, void *key)
-{
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(channel != XPC_NET_CHANNEL);
-
- switch (reason) {
- case xpMsgReceived: /* message received */
- DBUG_ON(data == NULL);
-
- xpnet_receive(partid, channel, (struct xpnet_message *)data);
- break;
-
- case xpConnected: /* connection completed to a partition */
- spin_lock_bh(&xpnet_broadcast_lock);
- __set_bit(partid, xpnet_broadcast_partitions);
- spin_unlock_bh(&xpnet_broadcast_lock);
-
- netif_carrier_on(xpnet_device);
-
- dev_dbg(xpnet, "%s connected to partition %d\n",
- xpnet_device->name, partid);
- break;
-
- default:
- spin_lock_bh(&xpnet_broadcast_lock);
- __clear_bit(partid, xpnet_broadcast_partitions);
- spin_unlock_bh(&xpnet_broadcast_lock);
-
- if (bitmap_empty(xpnet_broadcast_partitions,
- xp_max_npartitions)) {
- netif_carrier_off(xpnet_device);
- }
-
- dev_dbg(xpnet, "%s disconnected from partition %d\n",
- xpnet_device->name, partid);
- break;
- }
-}
-
-static int
-xpnet_dev_open(struct net_device *dev)
-{
- enum xp_retval ret;
-
- dev_dbg(xpnet, "calling xpc_connect(%d, 0x%p, NULL, %ld, %ld, %ld, "
- "%ld)\n", XPC_NET_CHANNEL, xpnet_connection_activity,
- (unsigned long)XPNET_MSG_SIZE,
- (unsigned long)XPNET_MSG_NENTRIES,
- (unsigned long)XPNET_MAX_KTHREADS,
- (unsigned long)XPNET_MAX_IDLE_KTHREADS);
-
- ret = xpc_connect(XPC_NET_CHANNEL, xpnet_connection_activity, NULL,
- XPNET_MSG_SIZE, XPNET_MSG_NENTRIES,
- XPNET_MAX_KTHREADS, XPNET_MAX_IDLE_KTHREADS);
- if (ret != xpSuccess) {
- dev_err(xpnet, "ifconfig up of %s failed on XPC connect, "
- "ret=%d\n", dev->name, ret);
-
- return -ENOMEM;
- }
-
- dev_dbg(xpnet, "ifconfig up of %s; XPC connected\n", dev->name);
-
- return 0;
-}
-
-static int
-xpnet_dev_stop(struct net_device *dev)
-{
- xpc_disconnect(XPC_NET_CHANNEL);
-
- dev_dbg(xpnet, "ifconfig down of %s; XPC disconnected\n", dev->name);
-
- return 0;
-}
-
-/*
- * Notification that the other end has received the message and
- * DMA'd the skb information. At this point, they are done with
- * our side. When all recipients are done processing, we
- * release the skb and then release our pending message structure.
- */
-static void
-xpnet_send_completed(enum xp_retval reason, short partid, int channel,
- void *__qm)
-{
- struct xpnet_pending_msg *queued_msg = (struct xpnet_pending_msg *)__qm;
-
- DBUG_ON(queued_msg == NULL);
-
- dev_dbg(xpnet, "message to %d notified with reason %d\n",
- partid, reason);
-
- if (atomic_dec_return(&queued_msg->use_count) == 0) {
- dev_dbg(xpnet, "all acks for skb->head=-x%p\n",
- (void *)queued_msg->skb->head);
-
- dev_kfree_skb_any(queued_msg->skb);
- kfree(queued_msg);
- }
-}
-
-static void
-xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg,
- u64 start_addr, u64 end_addr, u16 embedded_bytes, int dest_partid)
-{
- u8 msg_buffer[XPNET_MSG_SIZE];
- struct xpnet_message *msg = (struct xpnet_message *)&msg_buffer;
- u16 msg_size = sizeof(struct xpnet_message);
- enum xp_retval ret;
-
- msg->embedded_bytes = embedded_bytes;
- if (unlikely(embedded_bytes != 0)) {
- msg->version = XPNET_VERSION_EMBED;
- dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n",
- &msg->data, skb->data, (size_t)embedded_bytes);
- skb_copy_from_linear_data(skb, &msg->data,
- (size_t)embedded_bytes);
- msg_size += embedded_bytes - 1;
- } else {
- msg->version = XPNET_VERSION;
- }
- msg->magic = XPNET_MAGIC;
- msg->size = end_addr - start_addr;
- msg->leadin_ignore = (u64)skb->data - start_addr;
- msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb);
- msg->buf_pa = xp_pa((void *)start_addr);
-
- dev_dbg(xpnet, "sending XPC message to %d:%d\n"
- "msg->buf_pa=0x%lx, msg->size=%u, "
- "msg->leadin_ignore=%u, msg->tailout_ignore=%u\n",
- dest_partid, XPC_NET_CHANNEL, msg->buf_pa, msg->size,
- msg->leadin_ignore, msg->tailout_ignore);
-
- atomic_inc(&queued_msg->use_count);
-
- ret = xpc_send_notify(dest_partid, XPC_NET_CHANNEL, XPC_NOWAIT, msg,
- msg_size, xpnet_send_completed, queued_msg);
- if (unlikely(ret != xpSuccess))
- atomic_dec(&queued_msg->use_count);
-}
-
-/*
- * Network layer has formatted a packet (skb) and is ready to place it
- * "on the wire". Prepare and send an xpnet_message to all partitions
- * which have connected with us and are targets of this packet.
- *
- * MAC-NOTE: For the XPNET driver, the MAC address contains the
- * destination partid. If the destination partid octets are 0xffff,
- * this packet is to be broadcast to all connected partitions.
- */
-static netdev_tx_t
-xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct xpnet_pending_msg *queued_msg;
- u64 start_addr, end_addr;
- short dest_partid;
- u16 embedded_bytes = 0;
-
- dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
- "skb->end=0x%p skb->len=%d\n", (void *)skb->head,
- (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
- skb->len);
-
- if (skb->data[0] == 0x33) {
- dev_kfree_skb(skb);
- return NETDEV_TX_OK; /* nothing needed to be done */
- }
-
- /*
- * The xpnet_pending_msg tracks how many outstanding
- * xpc_send_notifies are relying on this skb. When none
- * remain, release the skb.
- */
- queued_msg = kmalloc_obj(struct xpnet_pending_msg, GFP_ATOMIC);
- if (queued_msg == NULL) {
- dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping "
- "packet\n", sizeof(struct xpnet_pending_msg));
-
- dev->stats.tx_errors++;
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- /* get the beginning of the first cacheline and end of last */
- start_addr = ((u64)skb->data & ~(L1_CACHE_BYTES - 1));
- end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb));
-
- /* calculate how many bytes to embed in the XPC message */
- if (unlikely(skb->len <= XPNET_MSG_DATA_MAX)) {
- /* skb->data does fit so embed */
- embedded_bytes = skb->len;
- }
-
- /*
- * Since the send occurs asynchronously, we set the count to one
- * and begin sending. Any sends that happen to complete before
- * we are done sending will not free the skb. We will be left
- * with that task during exit. This also handles the case of
- * a packet destined for a partition which is no longer up.
- */
- atomic_set(&queued_msg->use_count, 1);
- queued_msg->skb = skb;
-
- if (skb->data[0] == 0xff) {
- /* we are being asked to broadcast to all partitions */
- for_each_set_bit(dest_partid, xpnet_broadcast_partitions,
- xp_max_npartitions) {
-
- xpnet_send(skb, queued_msg, start_addr, end_addr,
- embedded_bytes, dest_partid);
- }
- } else {
- dest_partid = (short)skb->data[XPNET_PARTID_OCTET + 1];
- dest_partid |= (short)skb->data[XPNET_PARTID_OCTET + 0] << 8;
-
- if (dest_partid >= 0 &&
- dest_partid < xp_max_npartitions &&
- test_bit(dest_partid, xpnet_broadcast_partitions) != 0) {
-
- xpnet_send(skb, queued_msg, start_addr, end_addr,
- embedded_bytes, dest_partid);
- }
- }
-
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- if (atomic_dec_return(&queued_msg->use_count) == 0) {
- dev_kfree_skb(skb);
- kfree(queued_msg);
- }
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Deal with transmit timeouts coming from the network layer.
- */
-static void
-xpnet_dev_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- dev->stats.tx_errors++;
-}
-
-static const struct net_device_ops xpnet_netdev_ops = {
- .ndo_open = xpnet_dev_open,
- .ndo_stop = xpnet_dev_stop,
- .ndo_start_xmit = xpnet_dev_hard_start_xmit,
- .ndo_tx_timeout = xpnet_dev_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int __init
-xpnet_init(void)
-{
- u8 addr[ETH_ALEN];
- int result;
-
- if (!is_uv_system())
- return -ENODEV;
-
- dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME);
-
- xpnet_broadcast_partitions = bitmap_zalloc(xp_max_npartitions,
- GFP_KERNEL);
- if (xpnet_broadcast_partitions == NULL)
- return -ENOMEM;
-
- /*
- * use ether_setup() to init the majority of our device
- * structure and then override the necessary pieces.
- */
- xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, NET_NAME_UNKNOWN,
- ether_setup);
- if (xpnet_device == NULL) {
- bitmap_free(xpnet_broadcast_partitions);
- return -ENOMEM;
- }
-
- netif_carrier_off(xpnet_device);
-
- xpnet_device->netdev_ops = &xpnet_netdev_ops;
- xpnet_device->mtu = XPNET_DEF_MTU;
- xpnet_device->min_mtu = XPNET_MIN_MTU;
- xpnet_device->max_mtu = XPNET_MAX_MTU;
-
- memset(addr, 0, sizeof(addr));
- /*
- * Multicast assumes the LSB of the first octet is set for multicast
- * MAC addresses. We chose the first octet of the MAC to be unlikely
- * to collide with any vendor's officially issued MAC.
- */
- addr[0] = 0x02; /* locally administered, no OUI */
-
- addr[XPNET_PARTID_OCTET + 1] = xp_partition_id;
- addr[XPNET_PARTID_OCTET + 0] = (xp_partition_id >> 8);
- eth_hw_addr_set(xpnet_device, addr);
-
- /*
- * ether_setup() sets this to a multicast device. We are
- * really not supporting multicast at this time.
- */
- xpnet_device->flags &= ~IFF_MULTICAST;
-
- /*
- * No need to checksum as it is a DMA transfer. The BTE will
- * report an error if the data is not retrievable and the
- * packet will be dropped.
- */
- xpnet_device->features = NETIF_F_HW_CSUM;
-
- result = register_netdev(xpnet_device);
- if (result != 0) {
- free_netdev(xpnet_device);
- bitmap_free(xpnet_broadcast_partitions);
- }
-
- return result;
-}
-
-module_init(xpnet_init);
-
-static void __exit
-xpnet_exit(void)
-{
- dev_info(xpnet, "unregistering network device %s\n",
- xpnet_device[0].name);
-
- unregister_netdev(xpnet_device);
- free_netdev(xpnet_device);
- bitmap_free(xpnet_broadcast_partitions);
-}
-
-module_exit(xpnet_exit);
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_DESCRIPTION("Cross Partition Network adapter (XPNET)");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
index 1919d24c8236..c8990e83a65a 100644
--- a/drivers/misc/sram.c
+++ b/drivers/misc/sram.c
@@ -370,7 +370,7 @@ static const struct of_device_id sram_dt_ids[] = {
{ .compatible = "nvidia,tegra186-sysram", .data = &tegra_sysram_config },
{ .compatible = "nvidia,tegra194-sysram", .data = &tegra_sysram_config },
{ .compatible = "nvidia,tegra234-sysram", .data = &tegra_sysram_config },
- {}
+ { }
};
static int sram_probe(struct platform_device *pdev)
diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c
index 2aac83ec4a39..9e2d24f89457 100644
--- a/drivers/misc/ti_fpc202.c
+++ b/drivers/misc/ti_fpc202.c
@@ -718,7 +718,7 @@ static void fpc202_remove(struct i2c_client *client)
static const struct of_device_id fpc202_of_match[] = {
{ .compatible = "ti,fpc202" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, fpc202_of_match);
diff --git a/drivers/misc/tps6594-esm.c b/drivers/misc/tps6594-esm.c
index 2fbd3fbdf713..50e121669c6d 100644
--- a/drivers/misc/tps6594-esm.c
+++ b/drivers/misc/tps6594-esm.c
@@ -62,7 +62,7 @@ static int tps6594_esm_probe(struct platform_device *pdev)
tps6594_esm_isr, IRQF_ONESHOT,
pdev->resource[i].name, pdev);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request irq\n");
+ return ret;
}
ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG,
diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c
index 44fa81d6cec2..c3a0d6367f2c 100644
--- a/drivers/misc/tps6594-pfsm.c
+++ b/drivers/misc/tps6594-pfsm.c
@@ -308,7 +308,7 @@ static int tps6594_pfsm_probe(struct platform_device *pdev)
tps6594_pfsm_isr, IRQF_ONESHOT,
pdev->resource[i].name, pdev);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request irq\n");
+ return ret;
}
platform_set_drvdata(pdev, pfsm);
diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c
index 03f19eda641e..c871ea860bb5 100644
--- a/drivers/misc/tsl2550.c
+++ b/drivers/misc/tsl2550.c
@@ -420,7 +420,7 @@ static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume);
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id tsl2550_id[] = {
- { "tsl2550" },
+ { .name = "tsl2550" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tsl2550_id);
diff --git a/drivers/misc/vcpu_stall_detector.c b/drivers/misc/vcpu_stall_detector.c
index 26166357b255..1306b2c1ef99 100644
--- a/drivers/misc/vcpu_stall_detector.c
+++ b/drivers/misc/vcpu_stall_detector.c
@@ -224,8 +224,8 @@ static void vcpu_stall_detect_remove(struct platform_device *pdev)
}
static const struct of_device_id vcpu_stall_detect_of_match[] = {
- { .compatible = "qemu,vcpu-stall-detector", },
- {}
+ { .compatible = "qemu,vcpu-stall-detector" },
+ { }
};
MODULE_DEVICE_TABLE(of, vcpu_stall_detect_of_match);
diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c
index fffe068a26eb..c3ef3b98c432 100644
--- a/drivers/misc/vmw_vmci/vmci_event.c
+++ b/drivers/misc/vmw_vmci/vmci_event.c
@@ -179,16 +179,16 @@ int vmci_event_subscribe(u32 event,
}
}
+ *new_subscription_id = sub->id;
if (have_new_id) {
list_add_rcu(&sub->node, &subscriber_array[event]);
retval = VMCI_SUCCESS;
} else {
+ kfree(sub);
retval = VMCI_ERROR_NO_RESOURCES;
}
mutex_unlock(&subscriber_mutex);
-
- *new_subscription_id = sub->id;
return retval;
}
EXPORT_SYMBOL_GPL(vmci_event_subscribe);
diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c
index 476af89e751b..83355c0ec1d2 100644
--- a/drivers/misc/vmw_vmci/vmci_guest.c
+++ b/drivers/misc/vmw_vmci/vmci_guest.c
@@ -960,8 +960,8 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
}
static const struct pci_device_id vmci_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), },
- { 0 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI) },
+ { }
};
MODULE_DEVICE_TABLE(pci, vmci_ids);
diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c
index 3135ba3a58ee..a19eed39c516 100644
--- a/drivers/misc/xilinx_sdfec.c
+++ b/drivers/misc/xilinx_sdfec.c
@@ -456,10 +456,25 @@ static int xsdfec_get_turbo(struct xsdfec_dev *xsdfec, void __user *arg)
return err;
}
+static int xsdfec_ldpc_reg_addr(struct xsdfec_dev *xsdfec, u32 base, u32 high,
+ u32 offset, u32 *addr)
+{
+ if (high < base || offset > (high - base) / XSDFEC_LDPC_REG_JUMP) {
+ dev_dbg(xsdfec->dev,
+ "LDPC register offset %u outside space 0x%x-0x%x",
+ offset, base, high);
+ return -EINVAL;
+ }
+
+ *addr = base + offset * XSDFEC_LDPC_REG_JUMP;
+ return 0;
+}
+
static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize,
u32 offset)
{
u32 wdata;
+ u32 addr;
if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || psize == 0 ||
(n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) {
@@ -476,17 +491,11 @@ static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize,
k = k << XSDFEC_REG0_K_LSB;
wdata = k | n;
- if (XSDFEC_LDPC_CODE_REG0_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG0_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg0 space 0x%x",
- XSDFEC_LDPC_CODE_REG0_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG0_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG0_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG0_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -494,6 +503,7 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize,
u32 no_packing, u32 nm, u32 offset)
{
u32 wdata;
+ u32 addr;
if (psize < XSDFEC_REG1_PSIZE_MIN || psize > XSDFEC_REG1_PSIZE_MAX) {
dev_dbg(xsdfec->dev, "Psize is not in range");
@@ -510,17 +520,11 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize,
nm = (nm << XSDFEC_REG1_NM_LSB) & XSDFEC_REG1_NM_MASK;
wdata = nm | no_packing | psize;
- if (XSDFEC_LDPC_CODE_REG1_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG1_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg1 space 0x%x",
- XSDFEC_LDPC_CODE_REG1_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG1_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG1_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG1_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -529,6 +533,7 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc,
u32 max_schedule, u32 offset)
{
u32 wdata;
+ u32 addr;
if (nlayers < XSDFEC_REG2_NLAYERS_MIN ||
nlayers > XSDFEC_REG2_NLAYERS_MAX) {
@@ -563,17 +568,11 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc,
wdata = (max_schedule | no_final_parity | special_qc | norm_type |
nmqc | nlayers);
- if (XSDFEC_LDPC_CODE_REG2_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG2_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg2 space 0x%x",
- XSDFEC_LDPC_CODE_REG2_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG2_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG2_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG2_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -581,20 +580,15 @@ static int xsdfec_reg3_write(struct xsdfec_dev *xsdfec, u8 sc_off, u8 la_off,
u16 qc_off, u32 offset)
{
u32 wdata;
+ u32 addr;
wdata = ((qc_off << XSDFEC_REG3_QC_OFF_LSB) |
(la_off << XSDFEC_REG3_LA_OFF_LSB) | sc_off);
- if (XSDFEC_LDPC_CODE_REG3_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG3_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg3 space 0x%x",
- XSDFEC_LDPC_CODE_REG3_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG3_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG3_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG3_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -1390,10 +1384,8 @@ static int xsdfec_probe(struct platform_device *pdev)
err = devm_request_threaded_irq(dev, xsdfec->irq, NULL,
xsdfec_irq_thread, IRQF_ONESHOT,
"xilinx-sdfec16", xsdfec);
- if (err < 0) {
- dev_err(dev, "unable to request IRQ%d", xsdfec->irq);
+ if (err < 0)
goto err_xsdfec_dev;
- }
}
err = ida_alloc(&dev_nrs, GFP_KERNEL);
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 74ddbd0f79b0..c36c2a4c2a0b 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -28,6 +28,107 @@ source "drivers/nvmem/layouts/Kconfig"
# Devices
+if NVMEM_SYSFS
+
+config EEPROM_AT24
+ tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
+ depends on I2C && SYSFS
+ select REGMAP
+ select REGMAP_I2C
+ help
+ Enable this driver to get read/write support to most I2C EEPROMs
+ and compatible devices like FRAMs, SRAMs, ROMs etc. After you
+ configure the driver to know about each chip on your target
+ board. Use these generic chip names, instead of vendor-specific
+ ones like at24c64, 24lc02 or fm24c04:
+
+ 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
+ 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048
+
+ Unless you like data loss puzzles, always be sure that any chip
+ you configure as a 24c32 (32 kbit) or larger is NOT really a
+ 24c16 (16 kbit) or smaller, and vice versa. Marking the chip
+ as read-only won't help recover from this. Also, if your chip
+ has any software write-protect mechanism you may want to review the
+ code to make sure this driver won't turn it on by accident.
+
+ If you use this with an SMBus adapter instead of an I2C adapter,
+ full functionality is not available. Only smaller devices are
+ supported (24c16 and below, max 4 kByte).
+
+ This driver can also be built as a module. If so, the module
+ will be called at24.
+
+config EEPROM_AT25
+ tristate "SPI EEPROMs (FRAMs) from most vendors"
+ depends on SPI && SYSFS
+ select SPI_MEM
+ help
+ Enable this driver to get read/write support to most SPI EEPROMs
+ and Cypress FRAMs,
+ after you configure the board init code to know about each eeprom
+ on your target board.
+
+ This driver can also be built as a module. If so, the module
+ will be called at25.
+
+config EEPROM_93XX46
+ tristate "Microwire EEPROM 93XX46 support"
+ depends on SPI && SYSFS
+ select REGMAP
+ help
+ Driver for the microwire EEPROM chipsets 93xx46x. The driver
+ supports both read and write commands and also the command to
+ erase the whole EEPROM.
+
+ This driver can also be built as a module. If so, the module
+ will be called eeprom_93xx46.
+
+ If unsure, say N.
+
+config EEPROM_EE1004
+ tristate "SPD EEPROMs on DDR4 memory modules"
+ depends on I2C && SYSFS
+ help
+ Enable this driver to get read support to SPD EEPROMs following
+ the JEDEC EE1004 standard. These are typically found on DDR4
+ SDRAM memory modules.
+
+ This driver can also be built as a module. If so, the module
+ will be called ee1004.
+
+config EEPROM_M24LR
+ tristate "STMicroelectronics M24LR RFID/NFC EEPROM support"
+ depends on I2C && SYSFS
+ select REGMAP_I2C
+ help
+ This enables support for STMicroelectronics M24LR RFID/NFC EEPROM
+ chips. These dual-interface devices expose two I2C addresses:
+ one for EEPROM memory access and another for control and system
+ configuration (e.g. UID, password handling).
+
+ This driver provides a sysfs interface for control functions and
+ integrates with the nvmem subsystem for EEPROM access.
+
+ To compile this driver as a module, choose M here: the
+ module will be called m24lr.
+
+endif # NVMEM_SYSFS
+
+config NVMEM_AIROHA_SMC_EFUSES
+ tristate "Airoha SMC eFuse support"
+ depends on ARM64
+ depends on ARCH_AIROHA || COMPILE_TEST
+ depends on HAVE_ARM_SMCCC
+ default ARCH_AIROHA
+ help
+ Say y here to enable support for reading eFuses on Airoha AN7581
+ SoCs. These are e.g. used to store factory programmed
+ calibration data required for the PCIe or the USB-C PHY or Thermal.
+
+ This driver can also be built as a module. If so, the module will
+ be called nvmem-airoha-smc-efuses.
+
config NVMEM_AN8855_EFUSE
tristate "Airoha AN8855 eFuse support"
depends on COMPILE_TEST
@@ -138,7 +239,7 @@ config NVMEM_JZ4780_EFUSE
config NVMEM_LAN9662_OTPC
tristate "Microchip LAN9662 OTP controller support"
- depends on SOC_LAN966 || COMPILE_TEST
+ depends on SOC_LAN966 || ARCH_LAN969X || COMPILE_TEST
depends on HAS_IOMEM
help
This driver enables the OTP controller available on Microchip LAN9662
@@ -262,7 +363,7 @@ config NVMEM_S32G_OCOTP
Programmable memory pages.
config NVMEM_QCOM_QFPROM
- tristate "QCOM QFPROM Support"
+ tristate "Qualcomm QFPROM Support"
depends on ARCH_QCOM || COMPILE_TEST
depends on HAS_IOMEM
help
@@ -273,7 +374,7 @@ config NVMEM_QCOM_QFPROM
will be called nvmem_qfprom.
config NVMEM_QCOM_SEC_QFPROM
- tristate "QCOM SECURE QFPROM Support"
+ tristate "Qualcomm SECURE QFPROM Support"
depends on ARCH_QCOM || COMPILE_TEST
depends on HAS_IOMEM
depends on OF
@@ -303,7 +404,6 @@ config NVMEM_RAVE_SP_EEPROM
config NVMEM_RCAR_EFUSE
tristate "Renesas R-Car Gen4 E-FUSE support"
depends on (ARCH_RENESAS && ARM64) || COMPILE_TEST
- depends on NVMEM
help
Enable support for reading the fuses in the E-FUSE or OTP
non-volatile memory block on Renesas R-Car Gen4 SoCs.
@@ -483,4 +583,4 @@ config NVMEM_QORIQ_EFUSE
This driver can also be built as a module. If so, the module
will be called nvmem_qoriq_efuse.
-endif
+endif # NVMEM
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index 7252b8ec88d4..2bbfb9ff1885 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -10,6 +10,13 @@ nvmem_layouts-y := layouts.o
obj-y += layouts/
# Devices
+obj-$(CONFIG_EEPROM_AT24) += at24.o
+obj-$(CONFIG_EEPROM_AT25) += at25.o
+obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
+obj-$(CONFIG_EEPROM_EE1004) += ee1004.o
+obj-$(CONFIG_EEPROM_M24LR) += m24lr.o
+obj-$(CONFIG_NVMEM_AIROHA_SMC_EFUSES) += nvmem-airoha-smc-efuses.o
+nvmem-airoha-smc-efuses-y := airoha-smc-efuses.o
obj-$(CONFIG_NVMEM_AN8855_EFUSE) += nvmem-an8855-efuse.o
nvmem-an8855-efuse-y := an8855-efuse.o
obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
diff --git a/drivers/nvmem/airoha-smc-efuses.c b/drivers/nvmem/airoha-smc-efuses.c
new file mode 100644
index 000000000000..e56a99f4aa1f
--- /dev/null
+++ b/drivers/nvmem/airoha-smc-efuses.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Christian Marangi <ansuelsmth@gmail.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#define AIROHA_SMC_EFUSE_FID 0x82000001
+#define AIROHA_SMC_EFUSE_SUB_ID_READ 0x44414552
+
+#define AIROHA_EFUSE_CELLS 64
+
+struct airoha_efuse_bank_priv {
+ u32 bank_index;
+};
+
+static int airoha_efuse_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct regmap *regmap = context;
+
+ return regmap_bulk_read(regmap, offset,
+ val, bytes / sizeof(u32));
+}
+
+static int airoha_efuse_reg_read(void *context, unsigned int offset,
+ unsigned int *val)
+{
+ struct airoha_efuse_bank_priv *priv = context;
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID,
+ AIROHA_SMC_EFUSE_SUB_ID_READ,
+ priv->bank_index, offset, 0, 0, 0, 0, &res);
+
+ /* check if SMC reported an error */
+ if (res.a0)
+ return -EIO;
+
+ *val = res.a1;
+ return 0;
+}
+
+static int airoha_efuse_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ struct nvmem_config nvmem_config = {
+ .size = AIROHA_EFUSE_CELLS * sizeof(u32),
+ .stride = sizeof(u32),
+ .word_size = sizeof(u32),
+ .reg_read = airoha_efuse_read,
+ };
+ struct regmap_config regmap_config = {
+ .reg_read = airoha_efuse_reg_read,
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+ struct airoha_efuse_bank_priv *priv;
+ struct nvmem_device *nvmem;
+ struct regmap *regmap;
+ const char *name;
+ u32 bank;
+
+ ret = of_property_read_u32(child, "reg", &bank);
+ if (ret)
+ return ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "airoha-efuse-%u",
+ bank);
+ if (!name)
+ return -ENOMEM;
+
+ priv->bank_index = bank;
+
+ regmap_config.name = name;
+ regmap = devm_regmap_init(dev, NULL, priv,
+ &regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ nvmem_config.name = name;
+ nvmem_config.priv = regmap;
+ nvmem_config.dev = dev;
+ nvmem_config.id = bank;
+ nvmem_config.of_node = child;
+ nvmem = devm_nvmem_register(dev, &nvmem_config);
+ if (IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id airoha_efuse_of_match[] = {
+ { .compatible = "airoha,an7581-efuses", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, airoha_efuse_of_match);
+
+static struct platform_driver airoha_efuse_driver = {
+ .probe = airoha_efuse_probe,
+ .driver = {
+ .name = "airoha-efuse",
+ .of_match_table = airoha_efuse_of_match,
+ },
+};
+module_platform_driver(airoha_efuse_driver);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Driver for Airoha SMC eFUSEs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/at24.c b/drivers/nvmem/at24.c
index 772c4d9fa651..772c4d9fa651 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/nvmem/at24.c
diff --git a/drivers/misc/eeprom/at25.c b/drivers/nvmem/at25.c
index bc2cfb75d9bb..bc2cfb75d9bb 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/nvmem/at25.c
diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c
index aaa6537798bf..9f77aee37121 100644
--- a/drivers/nvmem/brcm_nvram.c
+++ b/drivers/nvmem/brcm_nvram.c
@@ -84,6 +84,11 @@ static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device
}
WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len);
+ if (priv->data_len < sizeof(struct brcm_nvram_header)) {
+ dev_err(priv->dev, "NVRAM data too small (%zu)\n", priv->data_len);
+ return -EINVAL;
+ }
+
priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
if (!priv->data)
return -ENOMEM;
@@ -187,9 +192,13 @@ static int brcm_nvram_parse(struct brcm_nvram *priv)
}
len = le32_to_cpu(header->len);
- if (len > priv->nvmem_size) {
- dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len,
- priv->nvmem_size);
+ if (len < sizeof(*header)) {
+ dev_err(dev, "NVRAM length (%zd) too small\n", len);
+ return -EINVAL;
+ }
+ if (len > priv->data_len) {
+ dev_err(dev, "NVRAM length (%zd) exceeds data size (%zd)\n", len,
+ priv->data_len);
return -EINVAL;
}
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 2f971b059704..0556d140170a 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -6,6 +6,7 @@
* Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
*/
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/fs.h>
@@ -13,6 +14,7 @@
#include <linux/init.h>
#include <linux/kref.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/nvmem-consumer.h>
#include <linux/nvmem-provider.h>
#include <linux/gpio/consumer.h>
@@ -55,25 +57,34 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
- if (nvmem->reg_read)
- return nvmem->reg_read(nvmem->priv, offset, val, bytes);
+ struct nvmem_operations *ops = nvmem->ops;
- return -EINVAL;
+ if (!ops->reg_read)
+ return -EOPNOTSUPP;
+
+ return ops->reg_read(nvmem->priv, offset, val, bytes);
}
static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
- int ret;
+ struct nvmem_operations *ops = nvmem->ops;
+ int ret, wr_ok;
- if (nvmem->reg_write) {
- gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
- ret = nvmem->reg_write(nvmem->priv, offset, val, bytes);
- gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
+ if (!ops->reg_write)
+ return -EOPNOTSUPP;
+
+ ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
+ if (ret)
+ return ret;
+
+ wr_ok = ops->reg_write(nvmem->priv, offset, val, bytes);
+
+ ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
+ if (ret)
return ret;
- }
- return -EINVAL;
+ return wr_ok;
}
static int nvmem_access_with_keepouts(struct nvmem_device *nvmem,
@@ -231,13 +242,12 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
count = round_down(count, nvmem->word_size);
- if (!nvmem->reg_read)
- return -EPERM;
-
rc = nvmem_reg_read(nvmem, pos, buf, count);
-
- if (rc)
+ if (rc) {
+ if (rc == -EOPNOTSUPP)
+ return -EPERM;
return rc;
+ }
return count;
}
@@ -264,19 +274,23 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
count = round_down(count, nvmem->word_size);
- if (!nvmem->reg_write || nvmem->read_only)
+ if (nvmem->read_only)
return -EPERM;
rc = nvmem_reg_write(nvmem, pos, buf, count);
-
- if (rc)
+ if (rc) {
+ if (rc == -EOPNOTSUPP)
+ return -EPERM;
return rc;
+ }
return count;
}
static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem)
{
+ struct nvmem_operations *ops = nvmem->ops;
+
umode_t mode = 0400;
if (!nvmem->root_only)
@@ -285,10 +299,10 @@ static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem)
if (!nvmem->read_only)
mode |= 0200;
- if (!nvmem->reg_write)
+ if (!ops->reg_write)
mode &= ~0200;
- if (!nvmem->reg_read)
+ if (!ops->reg_read)
mode &= ~0444;
return mode;
@@ -319,6 +333,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct nvmem_device *nvmem = to_nvmem_device(dev);
+ struct nvmem_operations *ops = nvmem->ops;
/*
* If the device has no .reg_write operation, do not allow
@@ -327,7 +342,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj,
* can be forced into read-write mode using the 'force_ro'
* attribute.
*/
- if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write)
+ if (attr == &dev_attr_force_ro.attr && !ops->reg_write)
return 0; /* Attribute not visible */
return attr->mode;
@@ -445,10 +460,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
return 0;
}
-static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
- const struct nvmem_config *config)
+static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem)
{
- if (config->compat)
+ if (nvmem->flags & FLAG_COMPAT)
device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
}
@@ -461,27 +475,23 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
const struct bin_attribute **pattrs;
struct bin_attribute *attrs;
unsigned int ncells = 0, i = 0;
- int ret = 0;
+ int ret;
- mutex_lock(&nvmem_mutex);
+ guard(mutex)(&nvmem_mutex);
if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated)
- goto unlock_mutex;
+ return 0;
/* Allocate an array of attributes with a sentinel */
ncells = list_count_nodes(&nvmem->cells);
pattrs = devm_kcalloc(&nvmem->dev, ncells + 1,
sizeof(struct bin_attribute *), GFP_KERNEL);
- if (!pattrs) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
+ if (!pattrs)
+ return -ENOMEM;
attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL);
- if (!attrs) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
+ if (!attrs)
+ return -ENOMEM;
/* Initialize each attribute to take the name and size of the cell */
list_for_each_entry(entry, &nvmem->cells, node) {
@@ -494,10 +504,8 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
attrs[i].size = entry->bytes;
attrs[i].read = &nvmem_cell_attr_read;
attrs[i].private = entry;
- if (!attrs[i].attr.name) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
+ if (!attrs[i].attr.name)
+ return -ENOMEM;
pattrs[i] = &attrs[i];
i++;
@@ -507,13 +515,10 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
ret = device_add_group(&nvmem->dev, &group);
if (ret)
- goto unlock_mutex;
+ return ret;
nvmem->sysfs_cells_populated = true;
-unlock_mutex:
- mutex_unlock(&nvmem_mutex);
-
return ret;
}
@@ -524,8 +529,7 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
{
return -ENOSYS;
}
-static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
- const struct nvmem_config *config)
+static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem)
{
}
@@ -537,6 +541,7 @@ static void nvmem_release(struct device *dev)
ida_free(&nvmem_ida, nvmem->id);
gpiod_put(nvmem->wp_gpio);
+ kfree(nvmem->ops);
kfree(nvmem);
}
@@ -551,9 +556,8 @@ static const struct bus_type nvmem_bus_type = {
static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell)
{
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
- mutex_lock(&nvmem_mutex);
- list_del(&cell->node);
- mutex_unlock(&nvmem_mutex);
+ scoped_guard(mutex, &nvmem_mutex)
+ list_del(&cell->node);
of_node_put(cell->np);
kfree_const(cell->name);
kfree(cell);
@@ -569,9 +573,8 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell)
{
- mutex_lock(&nvmem_mutex);
- list_add_tail(&cell->node, &cell->nvmem->cells);
- mutex_unlock(&nvmem_mutex);
+ scoped_guard(mutex, &nvmem_mutex)
+ list_add_tail(&cell->node, &cell->nvmem->cells);
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell);
}
@@ -721,14 +724,14 @@ nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id)
{
struct nvmem_cell_entry *iter, *cell = NULL;
- mutex_lock(&nvmem_mutex);
+ guard(mutex)(&nvmem_mutex);
+
list_for_each_entry(iter, &nvmem->cells, node) {
if (strcmp(cell_id, iter->name) == 0) {
cell = iter;
break;
}
}
- mutex_unlock(&nvmem_mutex);
return cell;
}
@@ -883,6 +886,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
struct nvmem_device *nvmem_register(const struct nvmem_config *config)
{
+ struct nvmem_operations *ops;
struct nvmem_device *nvmem;
int rval;
@@ -896,8 +900,15 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
if (!nvmem)
return ERR_PTR(-ENOMEM);
+ ops = kzalloc_obj(*ops);
+ if (!ops) {
+ kfree(nvmem);
+ return ERR_PTR(-ENOMEM);
+ }
+
rval = ida_alloc(&nvmem_ida, GFP_KERNEL);
if (rval < 0) {
+ kfree(ops);
kfree(nvmem);
return ERR_PTR(rval);
}
@@ -907,6 +918,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->dev.type = &nvmem_provider_type;
nvmem->dev.bus = &nvmem_bus_type;
nvmem->dev.parent = config->dev;
+ nvmem->ops = ops;
device_initialize(&nvmem->dev);
@@ -923,6 +935,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
INIT_LIST_HEAD(&nvmem->cells);
nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info;
+ ops->reg_read = config->reg_read;
+ ops->reg_write = config->reg_write;
+
nvmem->owner = config->owner;
if (!nvmem->owner && config->dev->driver)
nvmem->owner = config->dev->driver->owner;
@@ -932,8 +947,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->root_only = config->root_only;
nvmem->priv = config->priv;
nvmem->type = config->type;
- nvmem->reg_read = config->reg_read;
- nvmem->reg_write = config->reg_write;
nvmem->keepout = config->keepout;
nvmem->nkeepout = config->nkeepout;
if (config->of_node)
@@ -959,7 +972,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
goto err_put_device;
nvmem->read_only = device_property_present(config->dev, "read-only") ||
- config->read_only || !nvmem->reg_write;
+ config->read_only || !ops->reg_write;
#ifdef CONFIG_NVMEM_SYSFS
nvmem->dev.groups = nvmem_dev_groups;
@@ -999,6 +1012,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
if (rval)
goto err_remove_dev;
+ /* If the device has WP GPIO, default to read-only */
+ if (nvmem->wp_gpio)
+ nvmem->read_only = true;
+
#ifdef CONFIG_NVMEM_SYSFS
rval = nvmem_populate_sysfs_cells(nvmem);
if (rval)
@@ -1017,8 +1034,7 @@ err_remove_dev:
device_del(&nvmem->dev);
err_remove_cells:
nvmem_device_remove_all_cells(nvmem);
- if (config->compat)
- nvmem_sysfs_remove_compat(nvmem, config);
+ nvmem_sysfs_remove_compat(nvmem);
err_put_device:
put_device(&nvmem->dev);
@@ -1034,8 +1050,7 @@ static void nvmem_device_release(struct kref *kref)
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem);
- if (nvmem->flags & FLAG_COMPAT)
- device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+ nvmem_sysfs_remove_compat(nvmem);
nvmem_device_remove_all_cells(nvmem);
nvmem_destroy_layout(nvmem);
@@ -1088,17 +1103,17 @@ struct nvmem_device *devm_nvmem_register(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_nvmem_register);
-static struct nvmem_device *__nvmem_device_get(void *data,
+static struct nvmem_device *nvmem_device_match(void *data,
int (*match)(struct device *dev, const void *data))
{
struct nvmem_device *nvmem = NULL;
struct device *dev;
- mutex_lock(&nvmem_mutex);
- dev = bus_find_device(&nvmem_bus_type, NULL, data, match);
- if (dev)
- nvmem = to_nvmem_device(dev);
- mutex_unlock(&nvmem_mutex);
+ scoped_guard(mutex, &nvmem_mutex) {
+ dev = bus_find_device(&nvmem_bus_type, NULL, data, match);
+ if (dev)
+ nvmem = to_nvmem_device(dev);
+ }
if (!nvmem)
return ERR_PTR(-EPROBE_DEFER);
@@ -1116,13 +1131,6 @@ static struct nvmem_device *__nvmem_device_get(void *data,
return nvmem;
}
-static void __nvmem_device_put(struct nvmem_device *nvmem)
-{
- put_device(&nvmem->dev);
- module_put(nvmem->owner);
- kref_put(&nvmem->refcnt, nvmem_device_release);
-}
-
#if IS_ENABLED(CONFIG_OF)
/**
* of_nvmem_device_get() - Get nvmem device from a given id
@@ -1147,7 +1155,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
if (!nvmem_np)
return ERR_PTR(-ENOENT);
- nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
+ nvmem = nvmem_device_match(nvmem_np, device_match_of_node);
of_node_put(nvmem_np);
return nvmem;
}
@@ -1175,7 +1183,7 @@ struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name)
}
- return __nvmem_device_get((void *)dev_name, device_match_name);
+ return nvmem_device_match((void *)dev_name, device_match_name);
}
EXPORT_SYMBOL_GPL(nvmem_device_get);
@@ -1191,7 +1199,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_get);
struct nvmem_device *nvmem_device_find(void *data,
int (*match)(struct device *dev, const void *data))
{
- return __nvmem_device_get(data, match);
+ return nvmem_device_match(data, match);
}
EXPORT_SYMBOL_GPL(nvmem_device_find);
@@ -1235,7 +1243,9 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put);
*/
void nvmem_device_put(struct nvmem_device *nvmem)
{
- __nvmem_device_put(nvmem);
+ put_device(&nvmem->dev);
+ module_put(nvmem->owner);
+ kref_put(&nvmem->refcnt, nvmem_device_release);
}
EXPORT_SYMBOL_GPL(nvmem_device_put);
@@ -1308,35 +1318,32 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
dev_id = dev_name(dev);
- mutex_lock(&nvmem_lookup_mutex);
+ guard(mutex)(&nvmem_lookup_mutex);
list_for_each_entry(lookup, &nvmem_lookup_list, node) {
if ((strcmp(lookup->dev_id, dev_id) == 0) &&
(strcmp(lookup->con_id, con_id) == 0)) {
/* This is the right entry. */
- nvmem = __nvmem_device_get((void *)lookup->nvmem_name,
+ nvmem = nvmem_device_match((void *)lookup->nvmem_name,
device_match_name);
- if (IS_ERR(nvmem)) {
+ if (IS_ERR(nvmem))
/* Provider may not be registered yet. */
- cell = ERR_CAST(nvmem);
- break;
- }
+ return ERR_CAST(nvmem);
cell_entry = nvmem_find_cell_entry_by_name(nvmem,
lookup->cell_name);
if (!cell_entry) {
- __nvmem_device_put(nvmem);
+ nvmem_device_put(nvmem);
cell = ERR_PTR(-ENOENT);
} else {
cell = nvmem_create_cell(cell_entry, con_id, 0);
if (IS_ERR(cell))
- __nvmem_device_put(nvmem);
+ nvmem_device_put(nvmem);
}
break;
}
}
- mutex_unlock(&nvmem_lookup_mutex);
return cell;
}
@@ -1350,18 +1357,16 @@ static void nvmem_layout_module_put(struct nvmem_device *nvmem)
static struct nvmem_cell_entry *
nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
{
- struct nvmem_cell_entry *iter, *cell = NULL;
+ struct nvmem_cell_entry *cell;
- mutex_lock(&nvmem_mutex);
- list_for_each_entry(iter, &nvmem->cells, node) {
- if (np == iter->np) {
- cell = iter;
- break;
- }
+ guard(mutex)(&nvmem_mutex);
+
+ list_for_each_entry(cell, &nvmem->cells, node) {
+ if (np == cell->np)
+ return cell;
}
- mutex_unlock(&nvmem_mutex);
- return cell;
+ return NULL;
}
static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
@@ -1431,7 +1436,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
}
}
- nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
+ nvmem = nvmem_device_match(nvmem_np, device_match_of_node);
of_node_put(nvmem_np);
if (IS_ERR(nvmem)) {
of_node_put(cell_np);
@@ -1441,7 +1446,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
ret = nvmem_layout_module_get_optional(nvmem);
if (ret) {
of_node_put(cell_np);
- __nvmem_device_put(nvmem);
+ nvmem_device_put(nvmem);
return ERR_PTR(ret);
}
@@ -1450,14 +1455,14 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
if (!cell_entry) {
nvmem_layout_module_put(nvmem);
ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT;
- __nvmem_device_put(nvmem);
+ nvmem_device_put(nvmem);
return ERR_PTR(ret);
}
cell = nvmem_create_cell(cell_entry, id, cell_index);
if (IS_ERR(cell)) {
nvmem_layout_module_put(nvmem);
- __nvmem_device_put(nvmem);
+ nvmem_device_put(nvmem);
}
return cell;
@@ -1572,7 +1577,7 @@ void nvmem_cell_put(struct nvmem_cell *cell)
kfree(cell);
nvmem_layout_module_put(nvmem);
- __nvmem_device_put(nvmem);
+ nvmem_device_put(nvmem);
}
EXPORT_SYMBOL_GPL(nvmem_cell_put);
@@ -2094,10 +2099,10 @@ void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
{
int i;
- mutex_lock(&nvmem_lookup_mutex);
+ guard(mutex)(&nvmem_lookup_mutex);
+
for (i = 0; i < nentries; i++)
list_add_tail(&entries[i].node, &nvmem_lookup_list);
- mutex_unlock(&nvmem_lookup_mutex);
}
EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups);
@@ -2112,10 +2117,10 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
{
int i;
- mutex_lock(&nvmem_lookup_mutex);
+ guard(mutex)(&nvmem_lookup_mutex);
+
for (i = 0; i < nentries; i++)
list_del(&entries[i].node);
- mutex_unlock(&nvmem_lookup_mutex);
}
EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups);
diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/nvmem/ee1004.c
index 923f404a44c0..923f404a44c0 100644
--- a/drivers/misc/eeprom/ee1004.c
+++ b/drivers/nvmem/ee1004.c
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/nvmem/eeprom_93xx46.c
index f9c3ab52c2f9..f9c3ab52c2f9 100644
--- a/drivers/misc/eeprom/eeprom_93xx46.c
+++ b/drivers/nvmem/eeprom_93xx46.c
diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h
index f6d452b6a28f..4e610deeaa7b 100644
--- a/drivers/nvmem/internals.h
+++ b/drivers/nvmem/internals.h
@@ -7,10 +7,15 @@
#include <linux/nvmem-consumer.h>
#include <linux/nvmem-provider.h>
+/* Hold pointers to callbacks owned by the nvmem provider module. */
+struct nvmem_operations {
+ nvmem_reg_read_t reg_read;
+ nvmem_reg_write_t reg_write;
+};
+
struct nvmem_device {
struct module *owner;
struct device dev;
- struct list_head node;
int stride;
int word_size;
int id;
@@ -27,10 +32,9 @@ struct nvmem_device {
struct nvmem_cell_info *cell);
const struct nvmem_keepout *keepout;
unsigned int nkeepout;
- nvmem_reg_read_t reg_read;
- nvmem_reg_write_t reg_write;
struct gpio_desc *wp_gpio;
struct nvmem_layout *layout;
+ struct nvmem_operations *ops;
void *priv;
bool sysfs_cells_populated;
};
diff --git a/drivers/nvmem/lan9662-otpc.c b/drivers/nvmem/lan9662-otpc.c
index 56fc19f092a7..62d1d6381bf8 100644
--- a/drivers/nvmem/lan9662-otpc.c
+++ b/drivers/nvmem/lan9662-otpc.c
@@ -27,7 +27,6 @@
#define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1)
#define OTP_OTP_STATUS_OTP_BUSY BIT(0)
-#define OTP_MEM_SIZE 8192
#define OTP_SLEEP_US 10
#define OTP_TIMEOUT_US 500000
@@ -176,7 +175,6 @@ static struct nvmem_config otp_config = {
.word_size = 1,
.reg_read = lan9662_otp_read,
.reg_write = lan9662_otp_write,
- .size = OTP_MEM_SIZE,
};
static int lan9662_otp_probe(struct platform_device *pdev)
@@ -196,6 +194,7 @@ static int lan9662_otp_probe(struct platform_device *pdev)
otp_config.priv = otp;
otp_config.dev = dev;
+ otp_config.size = (uintptr_t) device_get_match_data(dev);
nvmem = devm_nvmem_register(dev, &otp_config);
@@ -203,7 +202,14 @@ static int lan9662_otp_probe(struct platform_device *pdev)
}
static const struct of_device_id lan9662_otp_match[] = {
- { .compatible = "microchip,lan9662-otpc", },
+ {
+ .compatible = "microchip,lan9662-otpc",
+ .data = (const void *) SZ_8K,
+ },
+ {
+ .compatible = "microchip,lan9691-otpc",
+ .data = (const void *) SZ_16K,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, lan9662_otp_match);
diff --git a/drivers/nvmem/layouts/u-boot-env.c b/drivers/nvmem/layouts/u-boot-env.c
index f27f387bb52a..33ec2350386f 100644
--- a/drivers/nvmem/layouts/u-boot-env.c
+++ b/drivers/nvmem/layouts/u-boot-env.c
@@ -38,9 +38,6 @@ static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, i
{
u8 mac[ETH_ALEN];
- if (bytes != MAC_ADDR_STR_LEN)
- return -EINVAL;
-
if (!mac_pton(buf, mac))
return -EINVAL;
@@ -75,7 +72,7 @@ static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem
info.offset = data_offset + value - data;
info.bytes = strlen(value);
info.np = of_get_child_by_name(dev->of_node, info.name);
- if (!strcmp(var, "ethaddr")) {
+ if (!strcmp(var, "ethaddr") && info.bytes == MAC_ADDR_STR_LEN) {
info.raw_len = strlen(value);
info.bytes = ETH_ALEN;
info.read_post_process = u_boot_env_read_post_process_ethaddr;
diff --git a/drivers/misc/eeprom/m24lr.c b/drivers/nvmem/m24lr.c
index 7a9fd45a8e46..7a9fd45a8e46 100644
--- a/drivers/misc/eeprom/m24lr.c
+++ b/drivers/nvmem/m24lr.c
diff --git a/drivers/nvmem/nintendo-otp.c b/drivers/nvmem/nintendo-otp.c
index 4440d4e5fb83..a4d986e4588e 100644
--- a/drivers/nvmem/nintendo-otp.c
+++ b/drivers/nvmem/nintendo-otp.c
@@ -16,7 +16,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/nvmem-provider.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#define HW_OTPCMD 0
@@ -73,8 +73,7 @@ MODULE_DEVICE_TABLE(of, nintendo_otp_of_table);
static int nintendo_otp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct of_device_id *of_id =
- of_match_device(nintendo_otp_of_table, dev);
+ const struct nintendo_otp_devtype_data *data;
struct nvmem_device *nvmem;
struct nintendo_otp_priv *priv;
@@ -94,8 +93,8 @@ static int nintendo_otp_probe(struct platform_device *pdev)
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
- if (of_id->data) {
- const struct nintendo_otp_devtype_data *data = of_id->data;
+ data = of_device_get_match_data(dev);
+ if (data) {
config.name = data->name;
config.size = data->num_banks * BANK_SIZE;
}
diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c
index 0ec78b5e19e7..2c0feb036f3f 100644
--- a/drivers/nvmem/rockchip-otp.c
+++ b/drivers/nvmem/rockchip-otp.c
@@ -78,9 +78,9 @@ struct rockchip_data {
struct rockchip_otp {
struct device *dev;
void __iomem *base;
- struct clk_bulk_data *clks;
struct reset_control *rst;
const struct rockchip_data *data;
+ struct clk_bulk_data clks[];
};
static int rockchip_otp_reset(struct rockchip_otp *otp)
@@ -424,7 +424,7 @@ static int rockchip_otp_probe(struct platform_device *pdev)
if (!data)
return dev_err_probe(dev, -EINVAL, "failed to get match data\n");
- otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp),
+ otp = devm_kzalloc(&pdev->dev, struct_size(otp, clks, data->num_clks),
GFP_KERNEL);
if (!otp)
return -ENOMEM;
@@ -436,11 +436,6 @@ static int rockchip_otp_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(otp->base),
"failed to ioremap resource\n");
- otp->clks = devm_kcalloc(dev, data->num_clks, sizeof(*otp->clks),
- GFP_KERNEL);
- if (!otp->clks)
- return -ENOMEM;
-
for (i = 0; i < data->num_clks; ++i)
otp->clks[i].id = data->clks[i];
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index c75abdd8ef25..bccfe75fe6e5 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2745,116 +2745,128 @@ static struct parport_pc_pci {
static const struct pci_device_id parport_pc_pci_tbl[] = {
/* Super-IO onboard chips */
- { 0x1106, 0x0686, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_686a },
- { 0x1106, 0x8231, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_8231 },
- { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_ite_8872 },
+ { PCI_DEVICE(0x1106, 0x0686),
+ .driver_data = sio_via_686a },
+ { PCI_DEVICE(0x1106, 0x8231),
+ .driver_data = sio_via_8231 },
+ { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8872),
+ .driver_data = sio_ite_8872 },
/* PCI cards */
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_20x },
- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel },
- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_a },
- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_b },
- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, boca_ioppar },
- { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
- PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_10x),
+ .driver_data = siig_1p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_10x),
+ .driver_data = siig_2p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_20x),
+ .driver_data = siig_1p_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_20x),
+ .driver_data = siig_2p_20x },
+ { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PARALLEL),
+ .driver_data = lava_parallel },
+ { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A),
+ .driver_data = lava_parallel_dual_a },
+ { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B),
+ .driver_data = lava_parallel_dual_b },
+ { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR),
+ .driver_data = boca_ioppar },
+ { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050,
+ PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014),
+ .driver_data = plx_9050 },
/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
- { 0x1409, 0x7268, 0x1409, 0x0101, 0, 0, timedia_4006a },
- { 0x1409, 0x7268, 0x1409, 0x0102, 0, 0, timedia_4014 },
- { 0x1409, 0x7268, 0x1409, 0x0103, 0, 0, timedia_4008a },
- { 0x1409, 0x7268, 0x1409, 0x0104, 0, 0, timedia_4018 },
- { 0x1409, 0x7268, 0x1409, 0x9018, 0, 0, timedia_9018a },
- { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_2P_EPP,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_2p_epp },
- { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_1P_ECP,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_1p_ecp },
- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_010L,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_010l },
+ { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0101),
+ .driver_data = timedia_4006a },
+ { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0102),
+ .driver_data = timedia_4014 },
+ { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0103),
+ .driver_data = timedia_4008a },
+ { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0104),
+ .driver_data = timedia_4018 },
+ { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x9018),
+ .driver_data = timedia_9018a },
+ { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_2P_EPP),
+ .driver_data = syba_2p_epp },
+ { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_1P_ECP),
+ .driver_data = syba_1p_ecp },
+ { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_010L),
+ .driver_data = titan_010l },
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
/* AFAVLAB_TK9902 */
- { 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p},
- { 0x14db, 0x2121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2p},
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_952 },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_954 },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_840 },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { PCI_VENDOR_ID_AKS, PCI_DEVICE_ID_AKS_ALADDINCARD,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 },
- { 0x14f2, 0x0121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, mobility_pp },
+ { PCI_DEVICE(0x14db, 0x2120),
+ .driver_data = avlab_1p },
+ { PCI_DEVICE(0x14db, 0x2121),
+ .driver_data = avlab_2p },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP),
+ .driver_data = oxsemi_952 },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP),
+ .driver_data = oxsemi_954 },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840),
+ .driver_data = oxsemi_840 },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU),
+ .driver_data = oxsemi_pcie_pport },
+ { PCI_VDEVICE(AKS, PCI_DEVICE_ID_AKS_ALADDINCARD),
+ .driver_data = aks_0100 },
+ { PCI_DEVICE(0x14f2, 0x0121),
+ .driver_data = mobility_pp },
/* NetMos communication controllers */
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
- 0xA000, 0x2000, 0, 0, netmos_9900 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
- 0xA000, 0x2000, 0, 0, netmos_9901 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
- 0xA000, 0x1000, 0, 0, netmos_9865 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
- 0xA000, 0x2000, 0, 0, netmos_9865 },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x2000),
+ .driver_data = netmos_9900 },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9705),
+ .driver_data = netmos_9705 },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9715),
+ .driver_data = netmos_9715 },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9755),
+ .driver_data = netmos_9755 },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9805),
+ .driver_data = netmos_9805 },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9815),
+ .driver_data = netmos_9815 },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9901, 0xA000, 0x2000),
+ .driver_data = netmos_9901 },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x1000),
+ .driver_data = netmos_9865 },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x2000),
+ .driver_data = netmos_9865 },
/* ASIX AX99100 PCIe to Multi I/O Controller */
- { PCI_VENDOR_ID_ASIX, PCI_DEVICE_ID_ASIX_AX99100,
- 0xA000, 0x2000, 0, 0, asix_ax99100 },
+ { PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, 0xA000, 0x2000),
+ .driver_data = asix_ax99100 },
/* Quatech SPPXP-100 Parallel port PCI ExpressCard */
- { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
+ { PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100),
+ .driver_data = quatech_sppxp100 },
/* WCH CH382L PCI-E single parallel port card */
- { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l },
+ { PCI_DEVICE_SUB(0x1c00, 0x3050, 0x1c00, 0x3050),
+ .driver_data = wch_ch382l },
/* Brainboxes IX-500/550 */
- { PCI_VENDOR_ID_INTASHIELD, 0x402a,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
+ { PCI_VDEVICE(INTASHIELD, 0x402a),
+ .driver_data = oxsemi_pcie_pport },
/* Brainboxes UC-146/UC-157 */
- { PCI_VENDOR_ID_INTASHIELD, 0x0be1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 },
- { PCI_VENDOR_ID_INTASHIELD, 0x0be2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 },
+ { PCI_VDEVICE(INTASHIELD, 0x0be1),
+ .driver_data = brainboxes_uc146 },
+ { PCI_VDEVICE(INTASHIELD, 0x0be2),
+ .driver_data = brainboxes_uc146 },
/* Brainboxes PX-146/PX-257 */
- { PCI_VENDOR_ID_INTASHIELD, 0x401c,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
+ { PCI_VDEVICE(INTASHIELD, 0x401c),
+ .driver_data = oxsemi_pcie_pport },
/* Brainboxes PX-203 */
- { PCI_VENDOR_ID_INTASHIELD, 0x4007,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px203 },
+ { PCI_VDEVICE(INTASHIELD, 0x4007),
+ .driver_data = brainboxes_px203 },
/* Brainboxes PX-475 */
- { PCI_VENDOR_ID_INTASHIELD, 0x401f,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport },
- { 0, } /* terminate list */
+ { PCI_VDEVICE(INTASHIELD, 0x401f),
+ .driver_data = oxsemi_pcie_pport },
+ { } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index 24d4f3a3ec3d..4d4140c8584d 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -170,158 +170,176 @@ static struct parport_pc_pci cards[] = {
static struct pci_device_id parport_serial_pci_tbl[] = {
/* PCI cards */
- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l },
- { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
- 0x1000, 0x0020, 0, 0, netmos_9855_2p },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
- 0x1000, 0x0022, 0, 0, netmos_9855_2p },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
- 0xA000, 0x3011, 0, 0, netmos_9900 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
- 0xA000, 0x3012, 0, 0, netmos_9900 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
- 0xA000, 0x3020, 0, 0, netmos_9900_2p },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
- 0xA000, 0x2000, 0, 0, netmos_99xx_1p },
+ { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_110L),
+ .driver_data = titan_110l },
+ { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_210L),
+ .driver_data = titan_210l },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9735),
+ .driver_data = netmos_9xx5_combo },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9745),
+ .driver_data = netmos_9xx5_combo },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9835),
+ .driver_data = netmos_9xx5_combo },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9845),
+ .driver_data = netmos_9xx5_combo },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0020),
+ .driver_data = netmos_9855_2p },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0022),
+ .driver_data = netmos_9855_2p },
+ { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9855),
+ .driver_data = netmos_9855 },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3011),
+ .driver_data = netmos_9900 },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3012),
+ .driver_data = netmos_9900 },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3020),
+ .driver_data = netmos_9900_2p },
+ { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, 0xA000, 0x2000),
+ .driver_data = netmos_99xx_1p },
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
- { PCI_VENDOR_ID_AFAVLAB, 0x2110,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2111,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2112,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2140,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2141,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2142,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2160,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2161,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
- { PCI_VENDOR_ID_AFAVLAB, 0x2162,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
- { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
+ { PCI_VDEVICE(AFAVLAB, 0x2110),
+ .driver_data = avlab_1s1p },
+ { PCI_VDEVICE(AFAVLAB, 0x2111),
+ .driver_data = avlab_1s1p },
+ { PCI_VDEVICE(AFAVLAB, 0x2112),
+ .driver_data = avlab_1s1p },
+ { PCI_VDEVICE(AFAVLAB, 0x2140),
+ .driver_data = avlab_1s2p },
+ { PCI_VDEVICE(AFAVLAB, 0x2141),
+ .driver_data = avlab_1s2p },
+ { PCI_VDEVICE(AFAVLAB, 0x2142),
+ .driver_data = avlab_1s2p },
+ { PCI_VDEVICE(AFAVLAB, 0x2160),
+ .driver_data = avlab_2s1p },
+ { PCI_VDEVICE(AFAVLAB, 0x2161),
+ .driver_data = avlab_2s1p },
+ { PCI_VDEVICE(AFAVLAB, 0x2162),
+ .driver_data = avlab_2s1p },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550),
+ .driver_data = siig_1s1p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650),
+ .driver_data = siig_1s1p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850),
+ .driver_data = siig_1s1p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550),
+ .driver_data = siig_2s1p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650),
+ .driver_data = siig_2s1p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850),
+ .driver_data = siig_2s1p_10x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550),
+ .driver_data = siig_2p1s_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650),
+ .driver_data = siig_2p1s_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850),
+ .driver_data = siig_2p1s_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550),
+ .driver_data = siig_2s1p_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650),
+ .driver_data = siig_1s1p_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850),
+ .driver_data = siig_1s1p_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550),
+ .driver_data = siig_2s1p_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650),
+ .driver_data = siig_2s1p_20x },
+ { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850),
+ .driver_data = siig_2s1p_20x },
/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
- { 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a },
- { 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h },
- { 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h },
- { 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a },
- { 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a },
- { 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a },
- { 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a },
- { 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u },
- { 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a },
- { 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u },
- { 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r },
- { 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s },
- { 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d },
- { 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e },
- { 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f },
- { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
- { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
- { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4078),
+ .driver_data = timedia_4078a },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4079),
+ .driver_data = timedia_4079h },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4085),
+ .driver_data = timedia_4085h },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4088),
+ .driver_data = timedia_4088a },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4089),
+ .driver_data = timedia_4089a },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4095),
+ .driver_data = timedia_4095a },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4096),
+ .driver_data = timedia_4096a },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5078),
+ .driver_data = timedia_4078u },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5079),
+ .driver_data = timedia_4079a },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5085),
+ .driver_data = timedia_4085u },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x6079),
+ .driver_data = timedia_4079r },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x7079),
+ .driver_data = timedia_4079s },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x8079),
+ .driver_data = timedia_4079d },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x9079),
+ .driver_data = timedia_4079e },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xa079),
+ .driver_data = timedia_4079f },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xb079),
+ .driver_data = timedia_9079a },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xc079),
+ .driver_data = timedia_9079b },
+ { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xd079),
+ .driver_data = timedia_9079c },
/* WCH CARDS */
- { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p },
- { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P,
- 0x4348, 0x3253, 0, 0, wch_ch353_2s1p },
- { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P,
- 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p },
- { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P,
- 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p },
+ { PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P),
+ .driver_data = wch_ch353_1s1p },
+ { PCI_VDEVICE_SUB(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P, 0x4348, 0x3253),
+ .driver_data = wch_ch353_2s1p },
+ { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P, 0x1c00, 0x3050),
+ .driver_data = wch_ch382_0s1p },
+ { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P, 0x1c00, 0x3250),
+ .driver_data = wch_ch382_2s1p },
/* BrainBoxes PX272/PX306 MIO card */
- { PCI_VENDOR_ID_INTASHIELD, 0x4100,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p },
+ { PCI_VDEVICE(INTASHIELD, 0x4100),
+ .driver_data = brainboxes_5s1p },
/* Sunix boards */
- { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
- 0x0100, 0, 0, sunix_4008a },
- { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
- 0x0101, 0, 0, sunix_5069a },
- { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
- 0x0102, 0, 0, sunix_5079a },
- { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
- 0x0104, 0, 0, sunix_5099a },
+ { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0100),
+ .driver_data = sunix_4008a },
+ { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0101),
+ .driver_data = sunix_5069a },
+ { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0102),
+ .driver_data = sunix_5079a },
+ { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0104),
+ .driver_data = sunix_5099a },
/* Brainboxes UC-203 */
- { PCI_VENDOR_ID_INTASHIELD, 0x0bc1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 },
- { PCI_VENDOR_ID_INTASHIELD, 0x0bc2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 },
+ { PCI_VDEVICE(INTASHIELD, 0x0bc1),
+ .driver_data = brainboxes_uc257 },
+ { PCI_VDEVICE(INTASHIELD, 0x0bc2),
+ .driver_data = brainboxes_uc257 },
/* Brainboxes UC-257 */
- { PCI_VENDOR_ID_INTASHIELD, 0x0861,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 },
- { PCI_VENDOR_ID_INTASHIELD, 0x0862,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 },
- { PCI_VENDOR_ID_INTASHIELD, 0x0863,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 },
+ { PCI_VDEVICE(INTASHIELD, 0x0861),
+ .driver_data = brainboxes_uc257 },
+ { PCI_VDEVICE(INTASHIELD, 0x0862),
+ .driver_data = brainboxes_uc257 },
+ { PCI_VDEVICE(INTASHIELD, 0x0863),
+ .driver_data = brainboxes_uc257 },
/* Brainboxes UC-414 */
- { PCI_VENDOR_ID_INTASHIELD, 0x0e61,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc414 },
+ { PCI_VDEVICE(INTASHIELD, 0x0e61),
+ .driver_data = brainboxes_uc414 },
/* Brainboxes UC-475 */
- { PCI_VENDOR_ID_INTASHIELD, 0x0981,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 },
- { PCI_VENDOR_ID_INTASHIELD, 0x0982,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 },
+ { PCI_VDEVICE(INTASHIELD, 0x0981),
+ .driver_data = brainboxes_uc257 },
+ { PCI_VDEVICE(INTASHIELD, 0x0982),
+ .driver_data = brainboxes_uc257 },
/* Brainboxes IS-300/IS-500 */
- { PCI_VENDOR_ID_INTASHIELD, 0x0da0,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_is300 },
+ { PCI_VDEVICE(INTASHIELD, 0x0da0),
+ .driver_data = brainboxes_is300 },
/* Brainboxes PX-263/PX-295 */
- { PCI_VENDOR_ID_INTASHIELD, 0x402c,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px263 },
+ { PCI_VDEVICE(INTASHIELD, 0x402c),
+ .driver_data = brainboxes_px263 },
- { 0, } /* terminate list */
+ { } /* terminate list */
};
MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
index 7f5326925ed5..8d2d9d01cfd2 100644
--- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
+++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
@@ -644,6 +644,15 @@ err_unlock:
return ret;
}
+static void pci_epf_mhi_edma_flush_async(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl);
+
+ dmaengine_synchronize(epf_mhi->dma_chan_rx);
+ dmaengine_synchronize(epf_mhi->dma_chan_tx);
+ flush_workqueue(epf_mhi->dma_wq);
+}
+
struct epf_dma_filter {
struct device *dev;
u32 dma_mask;
@@ -812,6 +821,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf)
mhi_cntrl->write_sync = pci_epf_mhi_edma_write;
mhi_cntrl->read_async = pci_epf_mhi_edma_read_async;
mhi_cntrl->write_async = pci_epf_mhi_edma_write_async;
+ mhi_cntrl->flush_async = pci_epf_mhi_edma_flush_async;
}
/* Register the MHI EP controller */
diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c
index 7cb020dd3423..9591672b0511 100644
--- a/drivers/phy/motorola/phy-cpcap-usb.c
+++ b/drivers/phy/motorola/phy-cpcap-usb.c
@@ -717,3 +717,4 @@ MODULE_ALIAS("platform:cpcap_usb");
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("CPCAP usb phy driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
index 032140bd6682..90a61d9db72c 100644
--- a/drivers/power/supply/ab8500_btemp.c
+++ b/drivers/power/supply/ab8500_btemp.c
@@ -826,3 +826,4 @@ MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
MODULE_ALIAS("platform:ab8500-btemp");
MODULE_DESCRIPTION("AB8500 battery temperature driver");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
index bc6c070a564d..ff38254d06ef 100644
--- a/drivers/power/supply/ab8500_charger.c
+++ b/drivers/power/supply/ab8500_charger.c
@@ -3748,3 +3748,4 @@ MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
MODULE_ALIAS("platform:ab8500-charger");
MODULE_DESCRIPTION("AB8500 charger management driver");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
index cfcc05dfe28d..e6b5efdf5dc9 100644
--- a/drivers/power/supply/ab8500_fg.c
+++ b/drivers/power/supply/ab8500_fg.c
@@ -3262,3 +3262,4 @@ MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
MODULE_ALIAS("platform:ab8500-fg");
MODULE_DESCRIPTION("AB8500 Fuel Gauge driver");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c
index 474661ba7cf2..d9892b351621 100644
--- a/drivers/power/supply/axp20x_ac_power.c
+++ b/drivers/power/supply/axp20x_ac_power.c
@@ -418,3 +418,4 @@ module_platform_driver(axp20x_ac_power_driver);
MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 50ca8e110085..ee8701a6e907 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -1155,3 +1155,4 @@ module_platform_driver(axp20x_batt_driver);
MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs");
MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index 29f105f9212d..e951b2b64b25 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -1077,3 +1077,4 @@ module_platform_driver(axp20x_usb_power_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
index aa04c54ff97e..68dabaf7a4f8 100644
--- a/drivers/power/supply/axp288_fuel_gauge.c
+++ b/drivers/power/supply/axp288_fuel_gauge.c
@@ -817,3 +817,4 @@ MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c
index 1d2b8bb08564..808714c6ce95 100644
--- a/drivers/power/supply/cpcap-battery.c
+++ b/drivers/power/supply/cpcap-battery.c
@@ -1202,3 +1202,4 @@ module_platform_driver(cpcap_battery_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("CPCAP PMIC Battery Driver");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c
index 76e2d2e3f5f5..34a57dd5c3cf 100644
--- a/drivers/power/supply/cpcap-charger.c
+++ b/drivers/power/supply/cpcap-charger.c
@@ -973,3 +973,4 @@ MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("CPCAP Battery Charger Interface driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:cpcap-charger");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c
index 27f36ef5b88d..58449df6068c 100644
--- a/drivers/power/supply/da9150-charger.c
+++ b/drivers/power/supply/da9150-charger.c
@@ -644,3 +644,4 @@ module_platform_driver(da9150_charger_driver);
MODULE_DESCRIPTION("Charger Driver for DA9150");
MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c
index 6913179ebb84..185f55d2fd70 100644
--- a/drivers/power/supply/generic-adc-battery.c
+++ b/drivers/power/supply/generic-adc-battery.c
@@ -298,3 +298,4 @@ module_platform_driver(gab_driver);
MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
MODULE_DESCRIPTION("generic battery driver using IIO");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c
index b111c7ce2be3..5be269f17bff 100644
--- a/drivers/power/supply/ingenic-battery.c
+++ b/drivers/power/supply/ingenic-battery.c
@@ -190,3 +190,4 @@ module_platform_driver(ingenic_battery_driver);
MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs");
MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/intel_dc_ti_battery.c b/drivers/power/supply/intel_dc_ti_battery.c
index 67a75281b0ac..cb6fa8d88b43 100644
--- a/drivers/power/supply/intel_dc_ti_battery.c
+++ b/drivers/power/supply/intel_dc_ti_battery.c
@@ -389,3 +389,4 @@ MODULE_ALIAS("platform:" DEV_NAME);
MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c
index 582644f4e8b3..1e53f11a8e2b 100644
--- a/drivers/power/supply/lego_ev3_battery.c
+++ b/drivers/power/supply/lego_ev3_battery.c
@@ -230,3 +230,4 @@ module_platform_driver(lego_ev3_battery_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c
index 1bc2c150fb91..b8b74efc9671 100644
--- a/drivers/power/supply/lp8788-charger.c
+++ b/drivers/power/supply/lp8788-charger.c
@@ -727,3 +727,4 @@ MODULE_DESCRIPTION("TI LP8788 Charger Driver");
MODULE_AUTHOR("Milo Kim");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:lp8788-charger");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c
index 41744191a363..16e42de28436 100644
--- a/drivers/power/supply/max17040_battery.c
+++ b/drivers/power/supply/max17040_battery.c
@@ -655,3 +655,4 @@ module_i2c_driver(max17040_i2c_driver);
MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/mp2629_charger.c b/drivers/power/supply/mp2629_charger.c
index 04294359ed7d..0aace0040259 100644
--- a/drivers/power/supply/mp2629_charger.c
+++ b/drivers/power/supply/mp2629_charger.c
@@ -657,3 +657,4 @@ module_platform_driver(mp2629_charger_driver);
MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
MODULE_DESCRIPTION("MP2629 Charger driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c
index 9d388bc53098..29ce34fd3804 100644
--- a/drivers/power/supply/mt6370-charger.c
+++ b/drivers/power/supply/mt6370-charger.c
@@ -928,3 +928,4 @@ module_platform_driver(mt6370_chg_driver);
MODULE_AUTHOR("ChiaEn Wu <chiaen_wu@richtek.com>");
MODULE_DESCRIPTION("MediaTek MT6370 Charger Driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c
index 7bda5cd77a75..67fdb4335338 100644
--- a/drivers/power/supply/qcom_smbx.c
+++ b/drivers/power/supply/qcom_smbx.c
@@ -1056,3 +1056,4 @@ module_platform_driver(qcom_spmi_smb);
MODULE_AUTHOR("Casey Connolly <casey.connolly@linaro.org>");
MODULE_DESCRIPTION("Qualcomm SMB2 Charger Driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c
index 5963a55341ee..84b9d6248489 100644
--- a/drivers/power/supply/rn5t618_power.c
+++ b/drivers/power/supply/rn5t618_power.c
@@ -818,3 +818,4 @@ module_platform_driver(rn5t618_power_driver);
MODULE_ALIAS("platform:rn5t618-power");
MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c
index b0220ec2d926..57266921dc8e 100644
--- a/drivers/power/supply/rx51_battery.c
+++ b/drivers/power/supply/rx51_battery.c
@@ -246,3 +246,4 @@ MODULE_ALIAS("platform:rx51-battery");
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
MODULE_DESCRIPTION("Nokia RX-51 battery driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c
index 9af0017fa88e..0999fc5bc662 100644
--- a/drivers/power/supply/sc27xx_fuel_gauge.c
+++ b/drivers/power/supply/sc27xx_fuel_gauge.c
@@ -1346,3 +1346,4 @@ module_platform_driver(sc27xx_fgu_driver);
MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c
index c46b22ab8d99..e6c9b2b1ffce 100644
--- a/drivers/power/supply/twl4030_charger.c
+++ b/drivers/power/supply/twl4030_charger.c
@@ -1146,3 +1146,4 @@ MODULE_AUTHOR("Gražvydas Ignotas");
MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:twl4030_bci");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c
index a99b3ff26929..5e04b4f0a135 100644
--- a/drivers/power/supply/twl4030_madc_battery.c
+++ b/drivers/power/supply/twl4030_madc_battery.c
@@ -235,3 +235,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>");
MODULE_DESCRIPTION("twl4030_madc battery driver");
MODULE_ALIAS("platform:twl4030_madc_battery");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/power/supply/twl6030_charger.c b/drivers/power/supply/twl6030_charger.c
index 020512d0e306..e8ef221fa0f3 100644
--- a/drivers/power/supply/twl6030_charger.c
+++ b/drivers/power/supply/twl6030_charger.c
@@ -577,3 +577,4 @@ module_platform_driver(twl6030_charger_driver);
MODULE_DESCRIPTION("TWL6030 Battery Charger Interface driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c
index 0e32a2295990..30644d2d7c54 100644
--- a/drivers/staging/iio/adc/ad7816.c
+++ b/drivers/staging/iio/adc/ad7816.c
@@ -426,9 +426,9 @@ static const struct of_device_id ad7816_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7816_of_match);
static const struct spi_device_id ad7816_id[] = {
- { "ad7816", ID_AD7816 },
- { "ad7817", ID_AD7817 },
- { "ad7818", ID_AD7818 },
+ { .name = "ad7816", .driver_data = ID_AD7816 },
+ { .name = "ad7817", .driver_data = ID_AD7817 },
+ { .name = "ad7818", .driver_data = ID_AD7818 },
{ }
};
diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c
index 1debcc36c1af..459dd7416b1d 100644
--- a/drivers/staging/iio/addac/adt7316-spi.c
+++ b/drivers/staging/iio/addac/adt7316-spi.c
@@ -126,15 +126,14 @@ static int adt7316_spi_probe(struct spi_device *spi_dev)
}
static const struct spi_device_id adt7316_spi_id[] = {
- { "adt7316", 0 },
- { "adt7317", 0 },
- { "adt7318", 0 },
- { "adt7516", 0 },
- { "adt7517", 0 },
- { "adt7519", 0 },
+ { .name = "adt7316" },
+ { .name = "adt7317" },
+ { .name = "adt7318" },
+ { .name = "adt7516" },
+ { .name = "adt7517" },
+ { .name = "adt7519" },
{ }
};
-
MODULE_DEVICE_TABLE(spi, adt7316_spi_id);
static const struct of_device_id adt7316_of_spi_match[] = {
diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c
index 59fb3bd26bc1..20f6052cc7fb 100644
--- a/drivers/staging/iio/addac/adt7316.c
+++ b/drivers/staging/iio/addac/adt7316.c
@@ -1814,11 +1814,8 @@ static int adt7316_setup_irq(struct iio_dev *indio_dev)
NULL, adt7316_event_handler,
irq_type | IRQF_ONESHOT,
indio_dev->name, indio_dev);
- if (ret) {
- dev_err(&indio_dev->dev, "failed to request irq %d\n",
- chip->bus.irq);
+ if (ret)
return ret;
- }
if (irq_type & IRQF_TRIGGER_HIGH)
chip->config1 |= ADT7316_INT_POLARITY;
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
index 60c33e10c46f..a78249812b26 100644
--- a/drivers/staging/iio/frequency/ad9832.c
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -252,22 +252,22 @@ error_ret:
* see dds.h for further information
*/
-static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9832_write, AD9832_FREQ0HM);
-static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9832_write, AD9832_FREQ1HM);
-static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9832_write, AD9832_FREQ_SYM);
+static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9832_write, AD9832_FREQ0HM);
+static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9832_write, AD9832_FREQ1HM);
+static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9832_write, AD9832_FREQ_SYM);
static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */
-static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9832_write, AD9832_PHASE0H);
-static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9832_write, AD9832_PHASE1H);
-static IIO_DEV_ATTR_PHASE(0, 2, 0200, NULL, ad9832_write, AD9832_PHASE2H);
-static IIO_DEV_ATTR_PHASE(0, 3, 0200, NULL, ad9832_write, AD9832_PHASE3H);
-static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL,
+static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9832_write, AD9832_PHASE0H);
+static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9832_write, AD9832_PHASE1H);
+static IIO_DEV_ATTR_PHASE(0200, 0, 2, NULL, ad9832_write, AD9832_PHASE2H);
+static IIO_DEV_ATTR_PHASE(0200, 0, 3, NULL, ad9832_write, AD9832_PHASE3H);
+static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL,
ad9832_write, AD9832_PHASE_SYM);
static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/
-static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL,
+static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL,
ad9832_write, AD9832_PINCTRL_EN);
-static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL,
+static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL,
ad9832_write, AD9832_OUTPUT_EN);
static struct attribute *ad9832_attributes[] = {
@@ -380,8 +380,8 @@ static const struct of_device_id ad9832_of_match[] = {
MODULE_DEVICE_TABLE(of, ad9832_of_match);
static const struct spi_device_id ad9832_id[] = {
- {"ad9832", 0},
- {"ad9835", 0},
+ { .name = "ad9832" },
+ { .name = "ad9835" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad9832_id);
diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c
index 33dfd723923c..0d20f796e0f0 100644
--- a/drivers/staging/iio/frequency/ad9834.c
+++ b/drivers/staging/iio/frequency/ad9834.c
@@ -314,21 +314,21 @@ static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, 0444,
* see dds.h for further information
*/
-static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9834_write, AD9834_REG_FREQ0);
-static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9834_write, AD9834_REG_FREQ1);
-static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9834_write, AD9834_FSEL);
+static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9834_write, AD9834_REG_FREQ0);
+static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9834_write, AD9834_REG_FREQ1);
+static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9834_write, AD9834_FSEL);
static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */
-static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9834_write, AD9834_REG_PHASE0);
-static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9834_write, AD9834_REG_PHASE1);
-static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, ad9834_write, AD9834_PSEL);
+static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9834_write, AD9834_REG_PHASE0);
+static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9834_write, AD9834_REG_PHASE1);
+static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9834_write, AD9834_PSEL);
static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/
-static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, ad9834_write, AD9834_PIN_SW);
-static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, ad9834_write, AD9834_RESET);
-static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, 0200, NULL, ad9834_write, AD9834_OPBITEN);
-static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0);
-static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1);
+static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9834_write, AD9834_PIN_SW);
+static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9834_write, AD9834_RESET);
+static IIO_DEV_ATTR_OUTY_ENABLE(0200, 0, 1, NULL, ad9834_write, AD9834_OPBITEN);
+static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 0, ad9834_store_wavetype, 0);
+static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 1, ad9834_store_wavetype, 1);
static struct attribute *ad9834_attributes[] = {
&iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr,
@@ -464,10 +464,10 @@ static int ad9834_probe(struct spi_device *spi)
}
static const struct spi_device_id ad9834_id[] = {
- {"ad9833", ID_AD9833},
- {"ad9834", ID_AD9834},
- {"ad9837", ID_AD9837},
- {"ad9838", ID_AD9838},
+ { .name = "ad9833", .driver_data = ID_AD9833 },
+ { .name = "ad9834", .driver_data = ID_AD9834 },
+ { .name = "ad9837", .driver_data = ID_AD9837 },
+ { .name = "ad9838", .driver_data = ID_AD9838 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad9834_id);
diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h
index 2ebe68eb7398..d5f0139b79f0 100644
--- a/drivers/staging/iio/frequency/dds.h
+++ b/drivers/staging/iio/frequency/dds.h
@@ -7,102 +7,102 @@
#ifndef IIO_DDS_H_
#define IIO_DDS_H_
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_frequencyY
*/
-#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \
+#define IIO_DEV_ATTR_FREQ(_mode, _channel, _num, _show, _store, _addr) \
IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num, \
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale
*/
#define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string) \
IIO_CONST_ATTR(out_altvoltage##_channel##_frequency_scale, _string)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol
*/
-#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \
+#define IIO_DEV_ATTR_FREQSYMBOL(_mode, _channel, _show, _store, _addr) \
IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol, \
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_phaseY
*/
-#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \
+#define IIO_DEV_ATTR_PHASE(_mode, _channel, _num, _show, _store, _addr) \
IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num, \
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale
*/
#define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string) \
IIO_CONST_ATTR(out_altvoltage##_channel##_phase_scale, _string)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol
*/
-#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \
+#define IIO_DEV_ATTR_PHASESYMBOL(_mode, _channel, _show, _store, _addr) \
IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol, \
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en
*/
-#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\
+#define IIO_DEV_ATTR_PINCONTROL_EN(_mode, _channel, _show, _store, _addr)\
IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en, \
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en
*/
-#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\
+#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_mode, _channel, _show, _store, _addr)\
IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en
*/
-#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\
+#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_mode, _channel, _show, _store, _addr)\
IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en, \
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_out_enable
*/
-#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \
+#define IIO_DEV_ATTR_OUT_ENABLE(_mode, _channel, _show, _store, _addr) \
IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable, \
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_outY_enable
*/
-#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \
- _mode, _show, _store, _addr) \
+#define IIO_DEV_ATTR_OUTY_ENABLE(_mode, _channel, _output, \
+ _show, _store, _addr) \
IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\
_mode, _show, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype
*/
-#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \
+#define IIO_DEV_ATTR_OUT_WAVETYPE(_mode, _channel, _output, _store, _addr)\
IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\
- 0200, NULL, _store, _addr)
+ _mode, NULL, _store, _addr)
-/**
+/*
* /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available
*/
diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c
index d1b086737bcd..af72db6299cd 100644
--- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c
+++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c
@@ -1066,3 +1066,4 @@ module_platform_driver(adc_tm5_driver);
MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
index f39ca0ddd17b..fb003ca96454 100644
--- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
@@ -904,3 +904,4 @@ module_platform_driver(qpnp_tm_driver);
MODULE_ALIAS("platform:spmi-temp-alarm");
MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c
index e25e36c99a88..7ced8f76a0ec 100644
--- a/drivers/thermal/renesas/rzg3s_thermal.c
+++ b/drivers/thermal/renesas/rzg3s_thermal.c
@@ -270,3 +270,4 @@ module_platform_driver(rzg3s_thermal_driver);
MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver");
MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c
index 7c844589b153..cfdb8e674dd2 100644
--- a/drivers/thermal/thermal-generic-adc.c
+++ b/drivers/thermal/thermal-generic-adc.c
@@ -228,3 +228,4 @@ module_platform_driver(gadc_thermal_driver);
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 1e4ade78ed84..f8fa20522660 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -150,7 +150,7 @@ static ssize_t portio_size_show(struct uio_port *port, char *buf)
static ssize_t portio_porttype_show(struct uio_port *port, char *buf)
{
- const char *porttypes[] = {"none", "x86", "gpio", "other"};
+ static const char * const porttypes[] = {"none", "x86", "gpio", "other"};
if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER))
return -EINVAL;
@@ -1057,6 +1057,11 @@ int __uio_register_device(struct module *owner,
err_request_irq:
uio_dev_del_attributes(idev);
err_uio_dev_add_attributes:
+ mutex_lock(&idev->info_lock);
+ idev->info = NULL;
+ mutex_unlock(&idev->info_lock);
+ wake_up_interruptible(&idev->wait);
+ kill_fasync(&idev->async_queue, SIGIO, POLL_HUP);
device_del(&idev->dev);
err_device_create:
uio_free_minor(idev->minor);
diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c
index 338dd2aaabc8..08cb150fb12f 100644
--- a/drivers/uio/uio_fsl_elbc_gpcm.c
+++ b/drivers/uio/uio_fsl_elbc_gpcm.c
@@ -45,8 +45,7 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/uio_driver.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
#include <asm/fsl_lbc.h>
@@ -249,19 +248,12 @@ static int check_of_data(struct fsl_elbc_gpcm *priv,
static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node,
struct resource *res, u32 *reg_br,
- u32 *reg_or, unsigned int *irq, char **name)
+ u32 *reg_or, char **name)
{
const char *dt_name;
const char *type;
int ret;
- /* get the memory resource */
- ret = of_address_to_resource(node, 0, res);
- if (ret) {
- dev_err(priv->dev, "failed to get resource\n");
- return ret;
- }
-
/* get the bank number */
ret = of_property_read_u32(node, "reg", &priv->bank);
if (ret) {
@@ -288,9 +280,6 @@ static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node,
if (of_property_read_string(node, "device_type", &type) == 0)
setup_periph(priv, type);
- /* get optional irq value */
- *irq = irq_of_parse_and_map(node, 0);
-
/* sanity check device tree data */
ret = check_of_data(priv, res, *reg_br, *reg_or);
if (ret)
@@ -312,7 +301,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
struct fsl_elbc_gpcm *priv;
struct uio_info *info;
char *uio_name = NULL;
- struct resource res;
+ struct resource *res;
+ void __iomem *base;
unsigned int irq;
u32 reg_br_cur;
u32 reg_or_cur;
@@ -323,6 +313,16 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
+ /* map the memory resource */
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* get optional irq value */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
/* allocate private data */
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -331,8 +331,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
priv->lbc = fsl_lbc_ctrl_dev->regs;
/* get device tree data */
- ret = get_of_data(priv, node, &res, &reg_br_new, &reg_or_new,
- &irq, &uio_name);
+ ret = get_of_data(priv, node, res, &reg_br_new, &reg_or_new,
+ &uio_name);
if (ret)
return ret;
@@ -349,7 +349,7 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
if ((reg_br_cur & BR_V)) {
if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM ||
(reg_br_cur & reg_or_cur & BR_BA)
- != fsl_lbc_addr(res.start)) {
+ != fsl_lbc_addr(res->start)) {
dev_err(priv->dev,
"bank in use by another peripheral\n");
return -ENODEV;
@@ -371,26 +371,18 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
/* configure the bank (force base address and GPCM) */
reg_br_new &= ~(BR_BA | BR_MSEL);
- reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V;
+ reg_br_new |= fsl_lbc_addr(res->start) | BR_MS_GPCM | BR_V;
out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new);
out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new);
- /* map the memory resource */
- info->mem[0].internal_addr = ioremap(res.start, resource_size(&res));
- if (!info->mem[0].internal_addr) {
- dev_err(priv->dev, "failed to map chip region\n");
- return -ENODEV;
- }
-
/* set all UIO data */
info->mem[0].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", node);
- if (!info->mem[0].name) {
- ret = -ENODEV;
- goto out_err3;
- }
+ if (!info->mem[0].name)
+ return -ENODEV;
- info->mem[0].addr = res.start;
- info->mem[0].size = resource_size(&res);
+ info->mem[0].internal_addr = base;
+ info->mem[0].addr = res->start;
+ info->mem[0].size = resource_size(res);
info->mem[0].memtype = UIO_MEM_PHYS;
info->priv = priv;
info->name = uio_name;
@@ -421,16 +413,13 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
dev_info(priv->dev,
"eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n",
- priv->name, (unsigned long long)res.start, priv->bank,
+ priv->name, (unsigned long long)res->start, priv->bank,
irq ? : -1);
return 0;
out_err2:
if (priv->shutdown)
priv->shutdown(info, true);
-
-out_err3:
- iounmap(info->mem[0].internal_addr);
return ret;
}
@@ -443,7 +432,6 @@ static void uio_fsl_elbc_gpcm_remove(struct platform_device *pdev)
uio_unregister_device(info);
if (priv->shutdown)
priv->shutdown(info, false);
- iounmap(info->mem[0].internal_addr);
}
diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c
index 12afc2fa1a0b..80087897f9b8 100644
--- a/drivers/uio/uio_sercos3.c
+++ b/drivers/uio/uio_sercos3.c
@@ -212,6 +212,7 @@ static const struct pci_device_id sercos3_pci_ids[] = {
},
{ 0, }
};
+MODULE_DEVICE_TABLE(pci, sercos3_pci_ids);
static struct pci_driver sercos3_pci_driver = {
.name = "sercos3",
diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c
index 0069e6f854d7..cbd11bc61274 100644
--- a/drivers/w1/masters/ds2482.c
+++ b/drivers/w1/masters/ds2482.c
@@ -310,6 +310,10 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit)
mutex_unlock(&pdev->access_lock);
+ /* On bus error, decode to 3 (no device responded) to abort the search */
+ if (status < 0)
+ status = 3 << 5;
+
/* Decode the status */
return (status >> 5);
}
@@ -545,9 +549,17 @@ static const struct i2c_device_id ds2482_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ds2482_id);
+static const struct of_device_id ds2482_of_match[] = {
+ { .compatible = "maxim,ds2482", },
+ { .compatible = "maxim,ds2484", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ds2482_of_match);
+
static struct i2c_driver ds2482_driver = {
.driver = {
.name = "ds2482",
+ .of_match_table = ds2482_of_match,
},
.probe = ds2482_probe,
.remove = ds2482_remove,
diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c
index e53bc41bde3c..b638963d4b59 100644
--- a/drivers/w1/slaves/w1_ds28e17.c
+++ b/drivers/w1/slaves/w1_ds28e17.c
@@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter,
* another simple read in that case.
*/
if (msgs[i+1].flags & I2C_M_RECV_LEN) {
+ if (msgs[i+1].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+ i = -EPROTO;
+ goto error;
+ }
result = w1_f19_i2c_read(sl, msgs[i+1].addr,
&(msgs[i+1].buf[1]), msgs[i+1].buf[0]);
if (result < 0) {
@@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter,
* another simple read in that case.
*/
if (msgs[i].flags & I2C_M_RECV_LEN) {
+ if (msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+ i = -EPROTO;
+ goto error;
+ }
result = w1_f19_i2c_read(sl,
msgs[i].addr,
&(msgs[i].buf[1]),
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 486f321eadc8..c16946642789 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -403,6 +403,11 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count,
const char *error_msg = "bad slave string format, expecting "
"ff-dddddddddddd\n";
+ if (count < 3) {
+ dev_err(dev, "%s", error_msg);
+ return -EINVAL;
+ }
+
if (buf[2] != '-') {
dev_err(dev, "%s", error_msg);
return -EINVAL;